diff options
author | wxiaoguang <wxiaoguang@gmail.com> | 2024-12-04 10:11:34 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-12-04 02:11:34 +0000 |
commit | c9e582c6b6cbc7beae66d24b05be6e1d338aa81b (patch) | |
tree | e2212df009376c6bde41296232e414b2388b8ae5 | |
parent | 2f43536c3eb6ba20e43521f5539df8372f225652 (diff) | |
download | gitea-c9e582c6b6cbc7beae66d24b05be6e1d338aa81b.tar.gz gitea-c9e582c6b6cbc7beae66d24b05be6e1d338aa81b.zip |
Refactor markdown editor and use it for milestone description editor (#32688)
Refactor markdown editor to clarify its "preview" behavior and remove
jQuery code.
Close #15045
---------
Co-authored-by: silverwind <me@silverwind.io>
29 files changed, 147 insertions, 116 deletions
diff --git a/modules/web/route.go b/modules/web/route.go index 77c411a97b..787521dfb0 100644 --- a/modules/web/route.go +++ b/modules/web/route.go @@ -9,6 +9,7 @@ import ( "reflect" "strings" + "code.gitea.io/gitea/modules/htmlutil" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web/middleware" @@ -214,7 +215,9 @@ func (r *Router) normalizeRequestPath(resp http.ResponseWriter, req *http.Reques normalizedPath = "/" } else if !strings.HasPrefix(normalizedPath+"/", "/v2/") { // do not respond to other requests, to simulate a real sub-path environment - http.Error(resp, "404 page not found, sub-path is: "+setting.AppSubURL, http.StatusNotFound) + resp.Header().Add("Content-Type", "text/html; charset=utf-8") + resp.WriteHeader(http.StatusNotFound) + _, _ = resp.Write([]byte(htmlutil.HTMLFormat(`404 page not found, sub-path is: <a href="%s">%s</a>`, setting.AppSubURL, setting.AppSubURL))) return } normalized = true diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 8da7412ade..57d2d89c5a 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2590,7 +2590,6 @@ diff.generated = generated diff.vendored = vendored diff.comment.add_line_comment = Add line comment diff.comment.placeholder = Leave a comment -diff.comment.markdown_info = Styling with markdown is supported. diff.comment.add_single_comment = Add single comment diff.comment.add_review_comment = Add comment diff.comment.start_review = Start review diff --git a/routers/web/web.go b/routers/web/web.go index 5ed046a983..e89d069449 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -485,6 +485,8 @@ func registerRoutes(m *web.Router) { m.Methods("GET, HEAD", "/*", public.FileHandlerFunc()) }, optionsCorsHandler()) + m.Post("/-/markup", reqSignIn, web.Bind(structs.MarkupOption{}), misc.Markup) + m.Group("/explore", func() { m.Get("", func(ctx *context.Context) { ctx.Redirect(setting.AppSubURL + "/explore/repos") diff --git a/templates/devtest/devtest-footer.tmpl b/templates/devtest/devtest-footer.tmpl index 1c755508a5..a1b3b86e5c 100644 --- a/templates/devtest/devtest-footer.tmpl +++ b/templates/devtest/devtest-footer.tmpl @@ -1,3 +1,3 @@ {{/* TODO: the devtest.js is isolated from index.js, so no module is shared and many index.js functions do not work in devtest.ts */}} <script src="{{AssetUrlPrefix}}/js/devtest.js?v={{AssetVersion}}"></script> -{{template "base/footer" dict}} +{{template "base/footer" ctx.RootData}} diff --git a/templates/devtest/devtest-header.tmpl b/templates/devtest/devtest-header.tmpl index a5910b96e6..ee08545640 100644 --- a/templates/devtest/devtest-header.tmpl +++ b/templates/devtest/devtest-header.tmpl @@ -1,2 +1,2 @@ -{{template "base/head" dict}} +{{template "base/head" ctx.RootData}} <link rel="stylesheet" href="{{AssetUrlPrefix}}/css/devtest.css?v={{AssetVersion}}"> diff --git a/templates/devtest/gitea-ui.tmpl b/templates/devtest/gitea-ui.tmpl index 303421fe13..5b40268761 100644 --- a/templates/devtest/gitea-ui.tmpl +++ b/templates/devtest/gitea-ui.tmpl @@ -183,8 +183,7 @@ <div> <h1>ComboMarkdownEditor</h1> - <div>ps: no JS code attached, so just a layout</div> - {{template "shared/combomarkdowneditor" .}} + {{template "shared/combomarkdowneditor" dict "MarkdownPreviewContext" "/owner/path"}} </div> <h1>Tailwind CSS Demo</h1> diff --git a/templates/org/settings/options.tmpl b/templates/org/settings/options.tmpl index 62debfc0ae..3b817d068b 100644 --- a/templates/org/settings/options.tmpl +++ b/templates/org/settings/options.tmpl @@ -23,6 +23,7 @@ <input id="email" name="email" type="email" value="{{.Org.Email}}" maxlength="255"> </div> <div class="field {{if .Err_Description}}error{{end}}"> + {{/* it is rendered as markdown, but the length is limited, so at the moment we do not use the markdown editor here */}} <label for="description">{{ctx.Locale.Tr "org.org_desc"}}</label> <textarea id="description" name="description" rows="2" maxlength="255">{{.Org.Description}}</textarea> </div> diff --git a/templates/projects/new.tmpl b/templates/projects/new.tmpl index bd173b54bc..a936079c46 100644 --- a/templates/projects/new.tmpl +++ b/templates/projects/new.tmpl @@ -18,7 +18,16 @@ </div> <div class="field"> <label>{{ctx.Locale.Tr "repo.projects.description"}}</label> - <textarea name="content" placeholder="{{ctx.Locale.Tr "repo.projects.description_placeholder"}}">{{.content}}</textarea> + {{/* TODO: repo-level project and org-level project have different behaviros to render */}} + {{/* the "Repository" is nil when the project is org-level */}} + {{template "shared/combomarkdowneditor" (dict + "MarkdownPreviewInRepo" $.Repository + "MarkdownPreviewContext" (Iif $.Repository "" .HomeLink) + "MarkdownPreviewMode" (Iif $.Repository "comment") + "TextareaName" "content" + "TextareaContent" .content + "TextareaPlaceholder" (ctx.Locale.Tr "repo.projects.description_placeholder") + )}} </div> {{if not .PageIsEditProjects}} diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl index 20e0c9db66..0f1458bfbf 100644 --- a/templates/repo/diff/box.tmpl +++ b/templates/repo/diff/box.tmpl @@ -240,8 +240,9 @@ <template id="issue-comment-editor-template"> <div class="ui form comment"> {{template "shared/combomarkdowneditor" (dict - "MarkdownPreviewUrl" (print $.Repository.Link "/markup") - "MarkdownPreviewContext" $.RepoLink + "CustomInit" true + "MarkdownPreviewInRepo" $.Repository + "MarkdownPreviewMode" "comment" "TextareaName" "content" "DropzoneParentContainer" ".ui.form" )}} diff --git a/templates/repo/diff/comment_form.tmpl b/templates/repo/diff/comment_form.tmpl index 856b3da01a..964dc2adc7 100644 --- a/templates/repo/diff/comment_form.tmpl +++ b/templates/repo/diff/comment_form.tmpl @@ -9,24 +9,24 @@ <input type="hidden" name="diff_start_cid"> <input type="hidden" name="diff_end_cid"> <input type="hidden" name="diff_base_cid"> - + <div class="field"> {{template "shared/combomarkdowneditor" (dict - "MarkdownPreviewUrl" (print $.root.Repository.Link "/markup") - "MarkdownPreviewContext" $.root.RepoLink + "CustomInit" true + "MarkdownPreviewInRepo" $.root.Repository + "MarkdownPreviewMode" "comment" "TextareaName" "content" "TextareaPlaceholder" (ctx.Locale.Tr "repo.diff.comment.placeholder") "DropzoneParentContainer" "form" "DisableAutosize" "true" )}} - + </div> {{if $.root.IsAttachmentEnabled}} <div class="field"> {{template "repo/upload" $.root}} </div> {{end}} - <div class="field footer tw-mx-2"> - <span class="markup-info">{{svg "octicon-markdown"}} {{ctx.Locale.Tr "repo.diff.comment.markdown_info"}}</span> + <div class="field footer"> <div class="tw-text-right"> {{if $.reply}} <button class="ui submit primary tiny button btn-reply" type="submit">{{ctx.Locale.Tr "repo.diff.comment.reply"}}</button> diff --git a/templates/repo/diff/new_review.tmpl b/templates/repo/diff/new_review.tmpl index 1b74a230f4..2febc6303a 100644 --- a/templates/repo/diff/new_review.tmpl +++ b/templates/repo/diff/new_review.tmpl @@ -16,8 +16,8 @@ </div> <div class="field"> {{template "shared/combomarkdowneditor" (dict - "MarkdownPreviewUrl" (print .Repository.Link "/markup") - "MarkdownPreviewContext" .RepoLink + "MarkdownPreviewInRepo" $.Repository + "MarkdownPreviewMode" "comment" "TextareaName" "content" "TextareaPlaceholder" (ctx.Locale.Tr "repo.diff.review.placeholder") "DropzoneParentContainer" "form" diff --git a/templates/repo/issue/comment_tab.tmpl b/templates/repo/issue/comment_tab.tmpl index 4197ea4f65..a4626dd89e 100644 --- a/templates/repo/issue/comment_tab.tmpl +++ b/templates/repo/issue/comment_tab.tmpl @@ -5,11 +5,12 @@ <div class="field"> {{template "shared/combomarkdowneditor" (dict - "MarkdownPreviewUrl" (print .Repository.Link "/markup") - "MarkdownPreviewContext" .RepoLink + "CustomInit" true + "MarkdownPreviewInRepo" $.Repository + "MarkdownPreviewMode" "comment" "TextareaName" "content" "TextareaContent" $textareaContent - "TextareaPlaceholder" (ctx.Locale.Tr "repo.diff.comment.placeholder") + "TextareaPlaceholder" (ctx.Locale.Tr "repo.diff.comment.placeholder") "DropzoneParentContainer" "form, .ui.form" )}} </div> diff --git a/templates/repo/issue/fields/textarea.tmpl b/templates/repo/issue/fields/textarea.tmpl index 3ad69e1220..722b67015f 100644 --- a/templates/repo/issue/fields/textarea.tmpl +++ b/templates/repo/issue/fields/textarea.tmpl @@ -7,11 +7,12 @@ {{if $useMarkdownEditor}} {{template "shared/combomarkdowneditor" (dict + "CustomInit" true "ContainerClasses" "tw-hidden" - "MarkdownPreviewUrl" (print .root.RepoLink "/markup") - "MarkdownPreviewContext" .root.RepoLink + "MarkdownPreviewInRepo" $.root.Repository + "MarkdownPreviewMode" "comment" "TextareaContent" .item.Attributes.value - "TextareaPlaceholder" .item.Attributes.placeholder + "TextareaPlaceholder" .item.Attributes.placeholder "DropzoneParentContainer" ".combo-editor-dropzone" )}} diff --git a/templates/repo/issue/milestone_new.tmpl b/templates/repo/issue/milestone_new.tmpl index 96a3879b80..4809149a21 100644 --- a/templates/repo/issue/milestone_new.tmpl +++ b/templates/repo/issue/milestone_new.tmpl @@ -36,9 +36,14 @@ </div> <div class="field"> <label>{{ctx.Locale.Tr "repo.milestones.desc"}}</label> - <textarea name="content">{{.content}}</textarea> + {{template "shared/combomarkdowneditor" (dict + "MarkdownPreviewInRepo" $.Repository + "MarkdownPreviewMode" "comment" + "TextareaName" "content" + "TextareaContent" .content + "TextareaPlaceholder" (ctx.Locale.Tr "repo.milestones.desc") + )}} </div> - <div class="divider"></div> <div class="tw-text-right"> {{if .PageIsEditMilestone}} <a class="ui primary basic button" href="{{.RepoLink}}/milestones"> diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl index 3f7b2dc78f..69b5a11a14 100644 --- a/templates/repo/issue/view_content.tmpl +++ b/templates/repo/issue/view_content.tmpl @@ -142,8 +142,9 @@ <div class="ui form comment"> <div class="field"> {{template "shared/combomarkdowneditor" (dict - "MarkdownPreviewUrl" (print .Repository.Link "/markup") - "MarkdownPreviewContext" .RepoLink + "CustomInit" true + "MarkdownPreviewInRepo" $.Repository + "MarkdownPreviewMode" "comment" "TextareaName" "content" "DropzoneParentContainer" ".ui.form" )}} diff --git a/templates/repo/release/new.tmpl b/templates/repo/release/new.tmpl index c01f9a421b..574b0d0311 100644 --- a/templates/repo/release/new.tmpl +++ b/templates/repo/release/new.tmpl @@ -50,12 +50,11 @@ </div> <div class="field"> {{template "shared/combomarkdowneditor" (dict - "MarkdownPreviewUrl" (print .Repository.Link "/markup") - "MarkdownPreviewContext" .RepoLink + "MarkdownPreviewInRepo" $.Repository + "MarkdownPreviewMode" "comment" "TextareaName" "content" "TextareaContent" .content "TextareaPlaceholder" (ctx.Locale.Tr "repo.release.message") - "TextareaAriaLabel" (ctx.Locale.Tr "repo.release.message") "DropzoneParentContainer" "form" )}} </div> diff --git a/templates/repo/wiki/new.tmpl b/templates/repo/wiki/new.tmpl index 0f10e60c4f..ea2913c0fd 100644 --- a/templates/repo/wiki/new.tmpl +++ b/templates/repo/wiki/new.tmpl @@ -23,12 +23,12 @@ {{$content = ctx.Locale.Tr "repo.wiki.welcome"}} {{end}} {{template "shared/combomarkdowneditor" (dict - "MarkdownPreviewUrl" (print .Repository.Link "/markup") - "MarkdownPreviewContext" .RepoLink + "CustomInit" true + "MarkdownPreviewInRepo" $.Repository + "MarkdownPreviewMode" "wiki" "TextareaName" "content" - "TextareaPlaceholder" (ctx.Locale.Tr "repo.wiki.page_content") - "TextareaAriaLabel" (ctx.Locale.Tr "repo.wiki.page_content") "TextareaContent" $content + "TextareaPlaceholder" (ctx.Locale.Tr "repo.wiki.page_content") )}} <div class="field tw-mt-4"> diff --git a/templates/shared/combomarkdowneditor.tmpl b/templates/shared/combomarkdowneditor.tmpl index 6ee989d1d6..3191346f59 100644 --- a/templates/shared/combomarkdowneditor.tmpl +++ b/templates/shared/combomarkdowneditor.tmpl @@ -1,23 +1,39 @@ {{/* Template Attributes: +* CustomInit: do not initialize the editor automatically * ContainerId: id attribute for the container element * ContainerClasses: additional classes for the container element -* MarkdownPreviewUrl: preview url for the preview tab -* MarkdownPreviewContext: preview context for the preview tab +* MarkdownPreviewInRepo: the repo to preview markdown +* MarkdownPreviewContext: preview context (the related url path when rendering) for the preview tab, eg: repo link or user home link +* MarkdownPreviewMode: content mode for the editor, eg: wiki, comment or default * TextareaName: name attribute for the textarea * TextareaContent: content for the textarea +* TextareaMaxLength: maxlength attribute for the textarea * TextareaPlaceholder: placeholder attribute for the textarea * TextareaAriaLabel: aria-label attribute for the textarea * DropzoneParentContainer: container for file upload (leave it empty if no upload) * DisableAutosize: whether to disable automatic height resizing */}} -<div {{if .ContainerId}}id="{{.ContainerId}}"{{end}} class="combo-markdown-editor {{.ContainerClasses}}" data-dropzone-parent-container="{{.DropzoneParentContainer}}"> - {{if .MarkdownPreviewUrl}} +{{$ariaLabel := or .TextareaAriaLabel .TextareaPlaceholder}} +{{$repo := .MarkdownPreviewInRepo}} +{{$previewContext := .MarkdownPreviewContext}} +{{$previewMode := .MarkdownPreviewMode}} +{{$previewUrl := print AppSubUrl "/-/markup"}} +{{if $repo}} + {{$previewUrl = print $repo.Link "/markup"}} +{{end}} +{{$supportEasyMDE := or (eq $previewMode "comment") (eq $previewMode "wiki")}} +<div {{if .ContainerId}}id="{{.ContainerId}}"{{end}} class="combo-markdown-editor {{if .CustomInit}}custom-init{{end}} {{.ContainerClasses}}" + data-dropzone-parent-container="{{.DropzoneParentContainer}}" + data-content-mode="{{$previewMode}}" + data-support-easy-mde="{{$supportEasyMDE}}" + data-preview-url="{{$previewUrl}}" + data-preview-context="{{$previewContext}}" +> <div class="ui top tabular menu"> <a class="active item" data-tab-for="markdown-writer">{{template "shared/misc/tabtitle" (ctx.Locale.Tr "write")}}</a> - <a class="item" data-tab-for="markdown-previewer" data-preview-url="{{.MarkdownPreviewUrl}}" data-preview-context="{{.MarkdownPreviewContext}}">{{template "shared/misc/tabtitle" (ctx.Locale.Tr "preview")}}</a> + <a class="item" data-tab-for="markdown-previewer">{{template "shared/misc/tabtitle" (ctx.Locale.Tr "preview")}}</a> </div> - {{end}} <div class="ui tab active" data-tab-panel="markdown-writer"> <markdown-toolbar> <div class="markdown-toolbar-group"> @@ -40,17 +56,25 @@ Template Attributes: <md-task-list class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.list.task.tooltip"}}">{{svg "octicon-tasklist"}}</md-task-list> <button class="markdown-toolbar-button markdown-button-table-add" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.table.add.tooltip"}}">{{svg "octicon-table"}}</button> </div> + {{if eq $previewMode "comment"}} <div class="markdown-toolbar-group"> <md-mention class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.mention.tooltip"}}">{{svg "octicon-mention"}}</md-mention> <md-ref class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.ref.tooltip"}}">{{svg "octicon-cross-reference"}}</md-ref> </div> + {{end}} <div class="markdown-toolbar-group"> <button class="markdown-toolbar-button markdown-switch-monospace" role="switch" data-enable-text="{{ctx.Locale.Tr "editor.buttons.enable_monospace_font"}}" data-disable-text="{{ctx.Locale.Tr "editor.buttons.disable_monospace_font"}}">{{svg "octicon-typography"}}</button> + {{if $supportEasyMDE}} <button class="markdown-toolbar-button markdown-switch-easymde" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.switch_to_legacy.tooltip"}}">{{svg "octicon-arrow-switch"}}</button> + {{end}} </div> </markdown-toolbar> <text-expander keys=": @ #" multiword="#" suffix=""> - <textarea class="markdown-text-editor"{{if .TextareaName}} name="{{.TextareaName}}"{{end}}{{if .TextareaPlaceholder}} placeholder="{{.TextareaPlaceholder}}"{{end}}{{if .TextareaAriaLabel}} aria-label="{{.TextareaAriaLabel}}"{{end}}{{if .DisableAutosize}} data-disable-autosize="{{.DisableAutosize}}"{{end}}>{{.TextareaContent}}</textarea> + <textarea class="markdown-text-editor" + {{if .TextareaName}}name="{{.TextareaName}}"{{end}} {{if .TextareaMaxLength}}maxlength="{{.TextareaMaxLength}}"{{end}} + {{if .TextareaPlaceholder}}placeholder="{{.TextareaPlaceholder}}"{{end}} {{if $ariaLabel}}aria-label="{{$ariaLabel}}"{{end}} + {{if .DisableAutosize}}data-disable-autosize="{{.DisableAutosize}}"{{end}} + >{{.TextareaContent}}</textarea> </text-expander> <script> if (localStorage?.getItem('markdown-editor-monospace') === 'true') { diff --git a/templates/user/settings/profile.tmpl b/templates/user/settings/profile.tmpl index f879587c71..197763425c 100644 --- a/templates/user/settings/profile.tmpl +++ b/templates/user/settings/profile.tmpl @@ -29,6 +29,7 @@ <p id="signed-user-email">{{.SignedUser.Email}}</p> </div> <div class="field {{if .Err_Description}}error{{end}}"> + {{/* it is rendered as markdown, but the length is limited, so at the moment we do not use the markdown editor here */}} <label for="description">{{ctx.Locale.Tr "user.user_bio"}}</label> <textarea id="description" name="description" rows="2" placeholder="{{ctx.Locale.Tr "settings.biography_placeholder"}}" maxlength="255">{{.SignedUser.Description}}</textarea> </div> diff --git a/web_src/css/editor/combomarkdowneditor.css b/web_src/css/editor/combomarkdowneditor.css index 97a8b70227..835286b795 100644 --- a/web_src/css/editor/combomarkdowneditor.css +++ b/web_src/css/editor/combomarkdowneditor.css @@ -96,6 +96,11 @@ font-size: 0.85em; } +.combo-markdown-editor .ui.tab.markup[data-tab-panel="markdown-previewer"] { + border-bottom: 1px solid var(--color-secondary); + padding-bottom: 1rem; +} + text-expander { display: block; position: relative; diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 12cdc4657b..b40859975c 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -1005,7 +1005,7 @@ td .commit-summary { } .repository.view.issue .comment-list .code-comment .comment-content { - margin-left: 36px; + margin-left: 24px; } .repository.view.issue .comment-list .comment > .avatar { diff --git a/web_src/css/review.css b/web_src/css/review.css index 0d69e36681..036ad017f8 100644 --- a/web_src/css/review.css +++ b/web_src/css/review.css @@ -102,19 +102,11 @@ cursor: pointer; } -.comment-code-cloud .ui.active.tab { - padding: 0.5em; -} - .comment-code-cloud .ui.active.tab.markup { padding: 1em; min-height: 168px; } -.comment-code-cloud .ui.tabular.menu { - margin: 0.5em; -} - .comment-code-cloud .editor-statusbar { display: none; } @@ -123,23 +115,6 @@ padding: 10px 0; } -.comment-code-cloud .footer .markup-info { - display: inline-block; - margin: 5px 0; - font-size: 12px; - color: var(--color-text-light); -} - -.comment-code-cloud .footer .ui.right.floated { - padding-top: 6px; -} - -.comment-code-cloud .footer::after { - clear: both; - content: ""; - display: block; -} - .diff-file-body .comment-form { margin: 0 0 0 3em; } diff --git a/web_src/js/features/common-form.ts b/web_src/js/features/common-form.ts index 1aca93169d..86323e352e 100644 --- a/web_src/js/features/common-form.ts +++ b/web_src/js/features/common-form.ts @@ -1,5 +1,7 @@ import {applyAreYouSure, initAreYouSure} from '../vendor/jquery.are-you-sure.ts'; import {handleGlobalEnterQuickSubmit} from './comp/QuickSubmit.ts'; +import {queryElems} from '../utils/dom.ts'; +import {initComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts'; export function initGlobalFormDirtyLeaveConfirm() { initAreYouSure(window.jQuery); @@ -11,7 +13,7 @@ export function initGlobalFormDirtyLeaveConfirm() { } export function initGlobalEnterQuickSubmit() { - document.addEventListener('keydown', (e) => { + document.addEventListener('keydown', (e: KeyboardEvent & {target: HTMLElement}) => { if (e.key !== 'Enter') return; const hasCtrlOrMeta = ((e.ctrlKey || e.metaKey) && !e.altKey); if (hasCtrlOrMeta && e.target.matches('textarea')) { @@ -27,3 +29,7 @@ export function initGlobalEnterQuickSubmit() { } }); } + +export function initGlobalComboMarkdownEditor() { + queryElems<HTMLElement>(document, '.combo-markdown-editor:not(.custom-init)', (el) => initComboMarkdownEditor(el)); +} diff --git a/web_src/js/features/comp/ComboMarkdownEditor.ts b/web_src/js/features/comp/ComboMarkdownEditor.ts index 7117952fa3..80eabaa37a 100644 --- a/web_src/js/features/comp/ComboMarkdownEditor.ts +++ b/web_src/js/features/comp/ComboMarkdownEditor.ts @@ -1,6 +1,5 @@ import '@github/markdown-toolbar-element'; import '@github/text-expander-element'; -import $ from 'jquery'; import {attachTribute} from '../tribute.ts'; import {hideElem, showElem, autosize, isElemVisible} from '../../utils/dom.ts'; import { @@ -23,6 +22,8 @@ import { } from './EditorMarkdown.ts'; import {DropzoneCustomEventReloadFiles, initDropzone} from '../dropzone.ts'; import {createTippy} from '../../modules/tippy.ts'; +import {fomanticQuery} from '../../modules/fomantic/base.ts'; +import type EasyMDE from 'easymde'; let elementIdCounter = 0; @@ -48,18 +49,23 @@ export function validateTextareaNonEmpty(textarea) { return true; } +type ComboMarkdownEditorOptions = { + editorHeights?: {minHeight?: string, height?: string, maxHeight?: string}, + easyMDEOptions?: EasyMDE.Options, +}; + export class ComboMarkdownEditor { static EventEditorContentChanged = EventEditorContentChanged; static EventUploadStateChanged = EventUploadStateChanged; public container : HTMLElement; - // TODO: use correct types to replace these "any" types - options: any; + options: ComboMarkdownEditorOptions; tabEditor: HTMLElement; tabPreviewer: HTMLElement; + supportEasyMDE: boolean; easyMDE: any; easyMDEToolbarActions: any; easyMDEToolbarDefault: any; @@ -71,11 +77,12 @@ export class ComboMarkdownEditor { dropzone: HTMLElement; attachedDropzoneInst: any; + previewMode: string; previewUrl: string; previewContext: string; - previewMode: string; - constructor(container, options = {}) { + constructor(container, options:ComboMarkdownEditorOptions = {}) { + if (container._giteaComboMarkdownEditor) throw new Error('ComboMarkdownEditor already initialized'); container._giteaComboMarkdownEditor = this; this.options = options; this.container = container; @@ -99,6 +106,10 @@ export class ComboMarkdownEditor { } setupContainer() { + this.supportEasyMDE = this.container.getAttribute('data-support-easy-mde') === 'true'; + this.previewMode = this.container.getAttribute('data-content-mode'); + this.previewUrl = this.container.getAttribute('data-preview-url'); + this.previewContext = this.container.getAttribute('data-preview-context'); initTextExpander(this.container.querySelector('text-expander')); } @@ -137,12 +148,14 @@ export class ComboMarkdownEditor { monospaceButton.setAttribute('aria-checked', String(enabled)); }); - const easymdeButton = this.container.querySelector('.markdown-switch-easymde'); - easymdeButton.addEventListener('click', async (e) => { - e.preventDefault(); - this.userPreferredEditor = 'easymde'; - await this.switchToEasyMDE(); - }); + if (this.supportEasyMDE) { + const easymdeButton = this.container.querySelector('.markdown-switch-easymde'); + easymdeButton.addEventListener('click', async (e) => { + e.preventDefault(); + this.userPreferredEditor = 'easymde'; + await this.switchToEasyMDE(); + }); + } this.initMarkdownButtonTableAdd(); @@ -187,6 +200,7 @@ export class ComboMarkdownEditor { setupTab() { const tabs = this.container.querySelectorAll<HTMLElement>('.tabular.menu > .item'); + if (!tabs.length) return; // Fomantic Tab requires the "data-tab" to be globally unique. // So here it uses our defined "data-tab-for" and "data-tab-panel" to generate the "data-tab" attribute for Fomantic. @@ -207,11 +221,8 @@ export class ComboMarkdownEditor { }); }); - $(tabs).tab(); + fomanticQuery(tabs).tab(); - this.previewUrl = this.tabPreviewer.getAttribute('data-preview-url'); - this.previewContext = this.tabPreviewer.getAttribute('data-preview-context'); - this.previewMode = this.options.previewMode ?? 'comment'; this.tabPreviewer.addEventListener('click', async () => { const formData = new FormData(); formData.append('mode', this.previewMode); @@ -219,7 +230,7 @@ export class ComboMarkdownEditor { formData.append('text', this.value()); const response = await POST(this.previewUrl, {data: formData}); const data = await response.text(); - renderPreviewPanelContent($(panelPreviewer), data); + renderPreviewPanelContent(panelPreviewer, data); }); } @@ -284,7 +295,7 @@ export class ComboMarkdownEditor { } async switchToUserPreference() { - if (this.userPreferredEditor === 'easymde') { + if (this.userPreferredEditor === 'easymde' && this.supportEasyMDE) { await this.switchToEasyMDE(); } else { this.switchToTextarea(); @@ -304,7 +315,7 @@ export class ComboMarkdownEditor { if (this.easyMDE) return; // EasyMDE's CSS should be loaded via webpack config, otherwise our own styles can not overwrite the default styles. const {default: EasyMDE} = await import(/* webpackChunkName: "easymde" */'easymde'); - const easyMDEOpt = { + const easyMDEOpt: EasyMDE.Options = { autoDownloadFontAwesome: false, element: this.textarea, forceSync: true, @@ -384,19 +395,20 @@ export class ComboMarkdownEditor { } get userPreferredEditor() { - return window.localStorage.getItem(`markdown-editor-${this.options.useScene ?? 'default'}`); + return window.localStorage.getItem(`markdown-editor-${this.previewMode ?? 'default'}`); } set userPreferredEditor(s) { - window.localStorage.setItem(`markdown-editor-${this.options.useScene ?? 'default'}`, s); + window.localStorage.setItem(`markdown-editor-${this.previewMode ?? 'default'}`, s); } } export function getComboMarkdownEditor(el) { - if (el instanceof $) el = el[0]; - return el?._giteaComboMarkdownEditor; + if (!el) return null; + if (el.length) el = el[0]; + return el._giteaComboMarkdownEditor; } -export async function initComboMarkdownEditor(container: HTMLElement, options = {}) { +export async function initComboMarkdownEditor(container: HTMLElement, options:ComboMarkdownEditorOptions = {}) { if (!container) { throw new Error('initComboMarkdownEditor: container is null'); } diff --git a/web_src/js/features/repo-editor.ts b/web_src/js/features/repo-editor.ts index 6ea9347eba..adae55f25c 100644 --- a/web_src/js/features/repo-editor.ts +++ b/web_src/js/features/repo-editor.ts @@ -201,10 +201,8 @@ export function initRepoEditor() { })(); } -export function renderPreviewPanelContent($previewPanel, data) { - $previewPanel.html(data); +export function renderPreviewPanelContent(previewPanel: Element, content: string) { + previewPanel.innerHTML = content; initMarkupContent(); - - const $refIssues = $previewPanel.find('p .ref-issue'); - attachRefIssueContextPopup($refIssues); + attachRefIssueContextPopup(previewPanel.querySelectorAll('p .ref-issue')); } diff --git a/web_src/js/features/repo-issue.ts b/web_src/js/features/repo-issue.ts index 9cc478712b..a59e4319ab 100644 --- a/web_src/js/features/repo-issue.ts +++ b/web_src/js/features/repo-issue.ts @@ -414,11 +414,6 @@ export function initRepoPullRequestReview() { await handleReply(this); }); - const elReviewBox = document.querySelector('.review-box-panel'); - if (elReviewBox) { - initComboMarkdownEditor(elReviewBox.querySelector('.combo-markdown-editor')); - } - // The following part is only for diff views if (!$('.repository.pull.diff').length) return; diff --git a/web_src/js/features/repo-release.ts b/web_src/js/features/repo-release.ts index 7589c77136..c59ab1f323 100644 --- a/web_src/js/features/repo-release.ts +++ b/web_src/js/features/repo-release.ts @@ -1,5 +1,4 @@ import {hideElem, showElem} from '../utils/dom.ts'; -import {initComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts'; export function initRepoRelease() { document.addEventListener('click', (e) => { @@ -16,7 +15,6 @@ export function initRepoReleaseNew() { if (!document.querySelector('.repository.new.release')) return; initTagNameEditor(); - initRepoReleaseEditor(); } function initTagNameEditor() { @@ -48,11 +46,3 @@ function initTagNameEditor() { hideTargetInput(e.target); }); } - -function initRepoReleaseEditor() { - const editor = document.querySelector<HTMLElement>('.repository.new.release .combo-markdown-editor'); - if (!editor) { - return; - } - initComboMarkdownEditor(editor); -} diff --git a/web_src/js/features/repo-wiki.ts b/web_src/js/features/repo-wiki.ts index 0e72b87109..69188d6ae2 100644 --- a/web_src/js/features/repo-wiki.ts +++ b/web_src/js/features/repo-wiki.ts @@ -2,6 +2,7 @@ import {initMarkupContent} from '../markup/content.ts'; import {validateTextareaNonEmpty, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts'; import {fomanticMobileScreen} from '../modules/fomantic.ts'; import {POST} from '../modules/fetch.ts'; +import type {ComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts'; async function initRepoWikiFormEditor() { const editArea = document.querySelector<HTMLTextAreaElement>('.repository.wiki .combo-markdown-editor textarea'); @@ -9,7 +10,7 @@ async function initRepoWikiFormEditor() { const form = document.querySelector('.repository.wiki.new .ui.form'); const editorContainer = form.querySelector<HTMLElement>('.combo-markdown-editor'); - let editor; + let editor: ComboMarkdownEditor; let renderRequesting = false; let lastContent; @@ -45,12 +46,10 @@ async function initRepoWikiFormEditor() { renderEasyMDEPreview(); editor = await initComboMarkdownEditor(editorContainer, { - useScene: 'wiki', // EasyMDE has some problems of height definition, it has inline style height 300px by default, so we also use inline styles to override it. // And another benefit is that we only need to write the style once for both editors. // TODO: Move height style to CSS after EasyMDE removal. editorHeights: {minHeight: '300px', height: 'calc(100vh - 600px)'}, - previewMode: 'wiki', easyMDEOptions: { previewRender: (_content, previewTarget) => previewTarget.innerHTML, // disable builtin preview render toolbar: ['bold', 'italic', 'strikethrough', '|', @@ -59,7 +58,7 @@ async function initRepoWikiFormEditor() { 'unordered-list', 'ordered-list', '|', 'link', 'image', 'table', 'horizontal-rule', '|', 'preview', 'fullscreen', 'side-by-side', '|', 'gitea-switch-to-textarea', - ], + ] as any, // to use custom toolbar buttons }, }); diff --git a/web_src/js/index.ts b/web_src/js/index.ts index 90e2d29225..48c4b76cee 100644 --- a/web_src/js/index.ts +++ b/web_src/js/index.ts @@ -83,7 +83,11 @@ import { initGlobalButtons, initGlobalDeleteButton, } from './features/common-button.ts'; -import {initGlobalEnterQuickSubmit, initGlobalFormDirtyLeaveConfirm} from './features/common-form.ts'; +import { + initGlobalComboMarkdownEditor, + initGlobalEnterQuickSubmit, + initGlobalFormDirtyLeaveConfirm, +} from './features/common-form.ts'; initGiteaFomantic(); initDirAuto(); @@ -127,6 +131,7 @@ onDomReady(() => { initGlobalCopyToClipboardListener, initGlobalEnterQuickSubmit, initGlobalFormDirtyLeaveConfirm, + initGlobalComboMarkdownEditor, initGlobalDeleteButton, initCommonOrganization, |