diff --git a/src/SufiChain.SufiBlazor/Components/Forms/SbMarkEditor.razor b/src/SufiChain.SufiBlazor/Components/Forms/SbMarkEditor.razor new file mode 100644 index 0000000..0a23721 --- /dev/null +++ b/src/SufiChain.SufiBlazor/Components/Forms/SbMarkEditor.razor @@ -0,0 +1,34 @@ +@namespace SufiChain.SufiBlazor.Components.Forms +@using SufiChain.SufiBlazor.Contracts.Editors + + diff --git a/src/SufiChain.SufiBlazor/Components/Forms/SbMarkEditor.razor.cs b/src/SufiChain.SufiBlazor/Components/Forms/SbMarkEditor.razor.cs new file mode 100644 index 0000000..f482686 --- /dev/null +++ b/src/SufiChain.SufiBlazor/Components/Forms/SbMarkEditor.razor.cs @@ -0,0 +1,65 @@ +using Microsoft.AspNetCore.Components; +using SufiChain.SufiBlazor.Contracts.Editors; + +namespace SufiChain.SufiBlazor.Components.Forms; + +public partial class SbMarkEditor +{ + [Parameter] public string Value { get; set; } = string.Empty; + [Parameter] public EventCallback ValueChanged { get; set; } + [Parameter] public string? ValueHtml { get; set; } + [Parameter] public EventCallback ValueHtmlChanged { get; set; } + [Parameter] public bool ReadOnly { get; set; } + [Parameter] public bool Disabled { get; set; } + [Parameter] public string? Placeholder { get; set; } + [Parameter] public string? Class { get; set; } + [Parameter] public string? Style { get; set; } + [Parameter] public bool RightToLeft { get; set; } + [Parameter] public bool EnablePreview { get; set; } = true; + [Parameter] public bool EnableMermaid { get; set; } = true; + [Parameter] public bool EnableHighlight { get; set; } = true; + [Parameter] public string HighlightTheme { get; set; } = "github"; + [Parameter] public SbMarkEditorMode Mode { get; set; } = SbMarkEditorMode.Markdown; + [Parameter] public string? SourceLanguage { get; set; } + [Parameter] public bool UseToolbarContributors { get; set; } + [Parameter] public bool IncludeDefaultToolbarItems { get; set; } = true; + [Parameter] public bool HideToolbar { get; set; } + [Parameter] public IReadOnlyList? ToolbarItems { get; set; } + [Parameter] public EventCallback OnShortcut { get; set; } + [Parameter] public string? MinHeight { get; set; } = "200px"; + [Parameter] public string? MaxHeight { get; set; } + [Parameter] public int FallbackRows { get; set; } = 12; + [Parameter] public bool IsDiffReview { get; set; } + [Parameter] public string OriginalValue { get; set; } = string.Empty; + [Parameter] public string SuggestedValue { get; set; } = string.Empty; + [Parameter] public EventCallback SuggestedValueChanged { get; set; } + [Parameter] public EventCallback OnApplyChanges { get; set; } + [Parameter] public EventCallback OnDiscardChanges { get; set; } + [Parameter(CaptureUnmatchedValues = true)] + public Dictionary? AdditionalAttributes { get; set; } + + private SbMarkdownEditorMode ResolveEditorMode() + { + return Mode switch + { + SbMarkEditorMode.Source => SbMarkdownEditorMode.Source, + SbMarkEditorMode.Markup => SbMarkdownEditorMode.Source, + _ => SbMarkdownEditorMode.Markdown + }; + } + + private string? ResolveSourceLanguage() + { + if (!string.IsNullOrWhiteSpace(SourceLanguage)) + { + return SourceLanguage; + } + + return Mode switch + { + SbMarkEditorMode.Markup => "html", + SbMarkEditorMode.Source => "html", + _ => null + }; + } +} diff --git a/src/SufiChain.SufiBlazor/Contracts/Editors/SbMarkEditorMode.cs b/src/SufiChain.SufiBlazor/Contracts/Editors/SbMarkEditorMode.cs new file mode 100644 index 0000000..f08b60c --- /dev/null +++ b/src/SufiChain.SufiBlazor/Contracts/Editors/SbMarkEditorMode.cs @@ -0,0 +1,8 @@ +namespace SufiChain.SufiBlazor.Contracts.Editors; + +public enum SbMarkEditorMode +{ + Markdown, + Markup, + Source +} diff --git a/src/SufiChain.SufiBlazor/wwwroot/sufiblazor-markdown-editor.js b/src/SufiChain.SufiBlazor/wwwroot/sufiblazor-markdown-editor.js index b4a7465..ad47dff 100644 --- a/src/SufiChain.SufiBlazor/wwwroot/sufiblazor-markdown-editor.js +++ b/src/SufiChain.SufiBlazor/wwwroot/sufiblazor-markdown-editor.js @@ -323,6 +323,7 @@ export async function initEditor(textarea, dotNetRef, options) { easyOptions.preview = false; easyOptions.sideBySideFullscreen = false; } else { + easyOptions.sideBySideFullscreen = false; easyOptions.previewRender = buildPreviewRender(options); easyOptions.renderingConfig.markedOptions = { highlight: buildHighlightFn(options.enableHighlight, options.enableMermaid) @@ -435,6 +436,20 @@ export function insertTextAtCursor(editorId, text) { cm.focus(); } +function toggleContainerFullscreen(stored) { + const wrapper = stored.easyMDE.codemirror.getWrapperElement(); + const container = wrapper?.closest('.sb-markdown-editor'); + if (!container) { + return; + } + + container.classList.toggle('sb-markdown-editor--fullscreen'); + document.documentElement.classList.toggle( + 'sb-markdown-editor-fullscreen-active', + container.classList.contains('sb-markdown-editor--fullscreen')); + stored.easyMDE.codemirror.refresh(); +} + export function execAction(editorId, action, value) { const stored = editors.get(editorId); if (!stored) { @@ -457,7 +472,7 @@ export function execAction(editorId, action, value) { case 'image': easyMDE.drawImage(); break; case 'preview': easyMDE.togglePreview(); break; case 'side-by-side': easyMDE.toggleSideBySide(); break; - case 'fullscreen': easyMDE.toggleFullScreen(); break; + case 'fullscreen': toggleContainerFullscreen(stored); break; case 'undo': easyMDE.codemirror.undo(); break; case 'redo': easyMDE.codemirror.redo(); break; default: diff --git a/src/SufiChain.SufiBlazor/wwwroot/sufiblazor.css b/src/SufiChain.SufiBlazor/wwwroot/sufiblazor.css index 055e993..84f6c2b 100644 --- a/src/SufiChain.SufiBlazor/wwwroot/sufiblazor.css +++ b/src/SufiChain.SufiBlazor/wwwroot/sufiblazor.css @@ -8445,6 +8445,55 @@ body.sb-resizing { color: var(--sb-color-text); } +.sb-markdown-editor .EasyMDEContainer.sided--no-fullscreen { + display: flex; + flex-wrap: wrap; + width: 100%; +} + +.sb-markdown-editor .EasyMDEContainer.sided--no-fullscreen .CodeMirror-sided, +.sb-markdown-editor .EasyMDEContainer.sided--no-fullscreen .editor-preview-side { + flex: 1 1 50%; + width: 50% !important; + min-width: 20rem; + position: static !important; + inset: auto !important; + height: auto; + max-height: none; +} + +.sb-markdown-editor .EasyMDEContainer.sided--no-fullscreen .editor-preview-active-side { + display: block; +} + +.sb-markdown-editor-fullscreen-active, +.sb-markdown-editor-fullscreen-active body { + overflow: hidden; +} + +.sb-markdown-editor.sb-markdown-editor--fullscreen { + position: fixed; + inset: var(--sb-space-4); + z-index: 2000; + min-height: auto; + background: var(--sb-color-surface); + box-shadow: var(--sb-shadow-xl, 0 24px 64px rgba(15, 23, 42, 0.22)); +} + +.sb-markdown-editor.sb-markdown-editor--fullscreen .EasyMDEContainer { + flex: 1 1 auto; + min-height: 0; +} + +.sb-markdown-editor.sb-markdown-editor--fullscreen .EasyMDEContainer .CodeMirror { + height: calc(100vh - 9rem) !important; +} + +.sb-markdown-editor.sb-markdown-editor--fullscreen .EasyMDEContainer.sided--no-fullscreen .CodeMirror-sided, +.sb-markdown-editor.sb-markdown-editor--fullscreen .EasyMDEContainer.sided--no-fullscreen .editor-preview-side { + height: calc(100vh - 9rem) !important; +} + .sb-markdown-viewer pre, .sb-markdown-viewer code { font-family: var(--sb-font-family-mono, monospace);