From ad204f9c5aa9659abb35aebca2aead296175a018 Mon Sep 17 00:00:00 2001 From: Kerwin Bryant <kerwin612@qq.com> Date: Tue, 4 Mar 2025 04:30:55 +0800 Subject: [PATCH] Refactor buttons to use new init framework (#33774) Make buttons to use new init framework * "js-toggle-commit-body" * "show-panel/hide-panel/show-modal" * "copy-content" --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> --- templates/repo/commits_list.tmpl | 2 +- templates/repo/latest_commit.tmpl | 2 +- templates/repo/view_file.tmpl | 2 +- web_src/js/features/common-button.ts | 25 +++++++++++++++---------- web_src/js/features/copycontent.ts | 12 +++++------- web_src/js/features/repo-commit.ts | 15 +++++++-------- 6 files changed, 30 insertions(+), 28 deletions(-) diff --git a/templates/repo/commits_list.tmpl b/templates/repo/commits_list.tmpl index 329dc45149..c8405ca748 100644 --- a/templates/repo/commits_list.tmpl +++ b/templates/repo/commits_list.tmpl @@ -48,7 +48,7 @@ {{end}} </span> {{if IsMultilineCommitMessage .Message}} - <button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button> + <button class="ui button ellipsis-button" aria-expanded="false" data-global-click="onRepoEllipsisButtonClick">...</button> {{end}} {{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}} {{if IsMultilineCommitMessage .Message}} diff --git a/templates/repo/latest_commit.tmpl b/templates/repo/latest_commit.tmpl index c62efc8e88..0341d60eb2 100644 --- a/templates/repo/latest_commit.tmpl +++ b/templates/repo/latest_commit.tmpl @@ -23,7 +23,7 @@ {{$commitLink:= printf "%s/commit/%s" .RepoLink (PathEscape .LatestCommit.ID.String)}} <span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span class="message-wrapper">{{ctx.RenderUtils.RenderCommitMessageLinkSubject .LatestCommit.Message $commitLink ($.Repository.ComposeMetas ctx)}}</span> {{if IsMultilineCommitMessage .LatestCommit.Message}} - <button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button> + <button class="ui button ellipsis-button" aria-expanded="false" data-global-click="onRepoEllipsisButtonClick">...</button> <pre class="commit-body tw-hidden">{{ctx.RenderUtils.RenderCommitBody .LatestCommit.Message ($.Repository.ComposeMetas ctx)}}</pre> {{end}} </span> diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl index 9f1b2a5e8f..51a8f7d501 100644 --- a/templates/repo/view_file.tmpl +++ b/templates/repo/view_file.tmpl @@ -55,7 +55,7 @@ {{end}} </div> <a download class="btn-octicon" data-tooltip-content="{{ctx.Locale.Tr "repo.download_file"}}" href="{{$.RawFileLink}}">{{svg "octicon-download"}}</a> - <a id="copy-content" class="btn-octicon {{if not .CanCopyContent}} disabled{{end}}"{{if or .IsImageFile (and .HasSourceRenderedToggle (not .IsDisplayingSource))}} data-link="{{$.RawFileLink}}"{{end}} data-tooltip-content="{{if .CanCopyContent}}{{ctx.Locale.Tr "copy_content"}}{{else}}{{ctx.Locale.Tr "copy_type_unsupported"}}{{end}}">{{svg "octicon-copy"}}</a> + <a class="btn-octicon {{if not .CanCopyContent}} disabled{{end}}" data-global-click="onCopyContentButtonClick" {{if or .IsImageFile (and .HasSourceRenderedToggle (not .IsDisplayingSource))}} data-link="{{$.RawFileLink}}"{{end}} data-tooltip-content="{{if .CanCopyContent}}{{ctx.Locale.Tr "copy_content"}}{{else}}{{ctx.Locale.Tr "copy_type_unsupported"}}{{end}}">{{svg "octicon-copy"}}</a> {{if .EnableFeed}} <a class="btn-octicon" href="{{$.RepoLink}}/rss/{{$.RefTypeNameSubURL}}/{{PathEscapeSegments .TreePath}}" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}"> {{svg "octicon-rss"}} diff --git a/web_src/js/features/common-button.ts b/web_src/js/features/common-button.ts index 7aebdd8dd5..003bfbce5d 100644 --- a/web_src/js/features/common-button.ts +++ b/web_src/js/features/common-button.ts @@ -1,5 +1,5 @@ import {POST} from '../modules/fetch.ts'; -import {addDelegatedEventListener, hideElem, queryElems, showElem, toggleElem} from '../utils/dom.ts'; +import {addDelegatedEventListener, hideElem, showElem, toggleElem} from '../utils/dom.ts'; import {fomanticQuery} from '../modules/fomantic/base.ts'; import {camelize} from 'vue'; @@ -74,10 +74,9 @@ export function initGlobalDeleteButton(): void { } } -function onShowPanelClick(e: MouseEvent) { +function onShowPanelClick(el: HTMLElement, e: MouseEvent) { // a '.show-panel' element can show a panel, by `data-panel="selector"` // if it has "toggle" class, it toggles the panel - const el = e.currentTarget as HTMLElement; e.preventDefault(); const sel = el.getAttribute('data-panel'); if (el.classList.contains('toggle')) { @@ -87,9 +86,8 @@ function onShowPanelClick(e: MouseEvent) { } } -function onHidePanelClick(e: MouseEvent) { +function onHidePanelClick(el: HTMLElement, e: MouseEvent) { // a `.hide-panel` element can hide a panel, by `data-panel="selector"` or `data-panel-closest="selector"` - const el = e.currentTarget as HTMLElement; e.preventDefault(); let sel = el.getAttribute('data-panel'); if (sel) { @@ -104,7 +102,7 @@ function onHidePanelClick(e: MouseEvent) { throw new Error('no panel to hide'); // should never happen, otherwise there is a bug in code } -function onShowModalClick(e: MouseEvent) { +function onShowModalClick(el: HTMLElement, e: MouseEvent) { // A ".show-modal" button will show a modal dialog defined by its "data-modal" attribute. // Each "data-modal-{target}" attribute will be filled to target element's value or text-content. // * First, try to query '#target' @@ -112,7 +110,6 @@ function onShowModalClick(e: MouseEvent) { // * Then, try to query '.target' // * Then, try to query 'target' as HTML tag // If there is a ".{attr}" part like "data-modal-form.action", then the form's "action" attribute will be set. - const el = e.currentTarget as HTMLElement; e.preventDefault(); const modalSelector = el.getAttribute('data-modal'); const elModal = document.querySelector(modalSelector); @@ -160,7 +157,15 @@ export function initGlobalButtons(): void { // There are a few cancel buttons in non-modal forms, and there are some dynamically created forms (eg: the "Edit Issue Content") addDelegatedEventListener(document, 'click', 'form button.ui.cancel.button', (_ /* el */, e) => e.preventDefault()); - queryElems(document, '.show-panel', (el) => el.addEventListener('click', onShowPanelClick)); - queryElems(document, '.hide-panel', (el) => el.addEventListener('click', onHidePanelClick)); - queryElems(document, '.show-modal', (el) => el.addEventListener('click', onShowModalClick)); + // Ideally these "button" events should be handled by registerGlobalEventFunc + // Refactoring would involve too many changes, so at the moment, just use the global event listener. + addDelegatedEventListener(document, 'click', '.show-panel, .hide-panel, .show-modal', (el, e: MouseEvent) => { + if (el.classList.contains('show-panel')) { + onShowPanelClick(el, e); + } else if (el.classList.contains('hide-panel')) { + onHidePanelClick(el, e); + } else if (el.classList.contains('show-modal')) { + onShowModalClick(el, e); + } + }); } diff --git a/web_src/js/features/copycontent.ts b/web_src/js/features/copycontent.ts index 4bc9281a35..3961a8d7ac 100644 --- a/web_src/js/features/copycontent.ts +++ b/web_src/js/features/copycontent.ts @@ -2,21 +2,19 @@ import {clippie} from 'clippie'; import {showTemporaryTooltip} from '../modules/tippy.ts'; import {convertImage} from '../utils.ts'; import {GET} from '../modules/fetch.ts'; +import {registerGlobalEventFunc} from '../modules/observer.ts'; const {i18n} = window.config; export function initCopyContent() { - const btn = document.querySelector('#copy-content'); - if (!btn || btn.classList.contains('disabled')) return; - - btn.addEventListener('click', async () => { - if (btn.classList.contains('is-loading')) return; + registerGlobalEventFunc('click', 'onCopyContentButtonClick', async (btn: HTMLInputElement) => { + if (btn.classList.contains('disabled') || btn.classList.contains('is-loading')) return; let content; let isRasterImage = false; const link = btn.getAttribute('data-link'); // when data-link is present, we perform a fetch. this is either because - // the text to copy is not in the DOM or it is an image which should be + // the text to copy is not in the DOM, or it is an image which should be // fetched to copy in full resolution if (link) { btn.classList.add('is-loading', 'loading-icon-2px'); @@ -40,7 +38,7 @@ export function initCopyContent() { content = Array.from(lineEls, (el) => el.textContent).join(''); } - // try copy original first, if that fails and it's an image, convert it to png + // try copy original first, if that fails, and it's an image, convert it to png const success = await clippie(content); if (success) { showTemporaryTooltip(btn, i18n.copy_success); diff --git a/web_src/js/features/repo-commit.ts b/web_src/js/features/repo-commit.ts index 8994a57f4a..e6d1112778 100644 --- a/web_src/js/features/repo-commit.ts +++ b/web_src/js/features/repo-commit.ts @@ -1,15 +1,14 @@ import {createTippy} from '../modules/tippy.ts'; import {toggleElem} from '../utils/dom.ts'; +import {registerGlobalEventFunc} from '../modules/observer.ts'; export function initRepoEllipsisButton() { - for (const button of document.querySelectorAll<HTMLButtonElement>('.js-toggle-commit-body')) { - button.addEventListener('click', function (e) { - e.preventDefault(); - const expanded = this.getAttribute('aria-expanded') === 'true'; - toggleElem(this.parentElement.querySelector('.commit-body')); - this.setAttribute('aria-expanded', String(!expanded)); - }); - } + registerGlobalEventFunc('click', 'onRepoEllipsisButtonClick', async (el: HTMLInputElement, e: Event) => { + e.preventDefault(); + const expanded = el.getAttribute('aria-expanded') === 'true'; + toggleElem(el.parentElement.querySelector('.commit-body')); + el.setAttribute('aria-expanded', String(!expanded)); + }); } export function initCommitStatuses() {