From 37205039fc83707edc8079d0884d4c12953e80b1 Mon Sep 17 00:00:00 2001 From: silverwind Date: Sun, 30 May 2021 21:15:57 +0200 Subject: [PATCH] Replace clipboard.js with async clipboard api (#15899) Use async clipboard api [1] over this dependency, saving around 10kB bundle size before minify while delivering the same functionality. The issue comment button works but does not have a popup indication. We could add some toast-style notifications in the future to fix that but I think it's out of scope of this PR. [1] https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText --- package-lock.json | 67 ------------------- package.json | 1 - templates/repo/clone_buttons.tmpl | 2 +- .../repo/issue/view_content/context_menu.tmpl | 2 +- web_src/js/features/clipboard.js | 52 +++++++++----- 5 files changed, 36 insertions(+), 88 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0ec6f4345..43f1b6f45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,6 @@ "@claviska/jquery-minicolors": "2.3.5", "@primer/octicons": "13.0.0", "add-asset-webpack-plugin": "2.0.1", - "clipboard": "2.0.8", "codemirror": "5.61.0", "css-loader": "5.2.4", "dropzone": "5.9.2", @@ -2649,16 +2648,6 @@ "node": ">=4" } }, - "node_modules/clipboard": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.8.tgz", - "integrity": "sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==", - "dependencies": { - "good-listener": "^1.2.2", - "select": "^1.1.2", - "tiny-emitter": "^2.0.0" - } - }, "node_modules/cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -3481,11 +3470,6 @@ "node": ">=0.4.0" } }, - "node_modules/delegate": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", - "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" - }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -5567,14 +5551,6 @@ "node": ">=0.6.0" } }, - "node_modules/good-listener": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", - "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", - "dependencies": { - "delegate": "^3.1.2" - } - }, "node_modules/graceful-fs": { "version": "4.2.6", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", @@ -10856,11 +10832,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/select": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", - "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=" - }, "node_modules/semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -12256,11 +12227,6 @@ "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "dev": true }, - "node_modules/tiny-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" - }, "node_modules/tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", @@ -15708,16 +15674,6 @@ "escape-string-regexp": "^1.0.5" } }, - "clipboard": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.8.tgz", - "integrity": "sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==", - "requires": { - "good-listener": "^1.2.2", - "select": "^1.1.2", - "tiny-emitter": "^2.0.0" - } - }, "cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -16412,11 +16368,6 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, - "delegate": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", - "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" - }, "detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -18044,14 +17995,6 @@ "minimist": "^1.2.5" } }, - "good-listener": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", - "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", - "requires": { - "delegate": "^3.1.2" - } - }, "graceful-fs": { "version": "4.2.6", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", @@ -22064,11 +22007,6 @@ "ajv-keywords": "^3.5.2" } }, - "select": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", - "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=" - }, "semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -23188,11 +23126,6 @@ "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "dev": true }, - "tiny-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" - }, "tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", diff --git a/package.json b/package.json index 5da3dfab2..bbd565803 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "@claviska/jquery-minicolors": "2.3.5", "@primer/octicons": "13.0.0", "add-asset-webpack-plugin": "2.0.1", - "clipboard": "2.0.8", "codemirror": "5.61.0", "css-loader": "5.2.4", "dropzone": "5.9.2", diff --git a/templates/repo/clone_buttons.tmpl b/templates/repo/clone_buttons.tmpl index b0377b5bc..8fc1ba12c 100644 --- a/templates/repo/clone_buttons.tmpl +++ b/templates/repo/clone_buttons.tmpl @@ -14,7 +14,7 @@ {{end}} {{if or (not $.DisableHTTP) (and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH))}} - {{end}} diff --git a/templates/repo/issue/view_content/context_menu.tmpl b/templates/repo/issue/view_content/context_menu.tmpl index bbffd5346..e3001cddc 100644 --- a/templates/repo/issue/view_content/context_menu.tmpl +++ b/templates/repo/issue/view_content/context_menu.tmpl @@ -10,7 +10,7 @@ {{ else }} {{ $referenceUrl = Printf "%s%s/pulls/%d/files#%s" AppUrl .ctx.Repository.FullName .ctx.Issue.Index .item.HashTag }} {{ end }} -
{{.ctx.i18n.Tr "repo.issues.context.copy_link"}}
+
{{.ctx.i18n.Tr "repo.issues.context.copy_link"}}
{{.ctx.i18n.Tr "repo.issues.context.quote_reply"}}
{{if not .ctx.UnitIssuesGlobalDisabled}}
{{.ctx.i18n.Tr "repo.issues.context.reference_issue"}}
diff --git a/web_src/js/features/clipboard.js b/web_src/js/features/clipboard.js index 32eff981b..12486a208 100644 --- a/web_src/js/features/clipboard.js +++ b/web_src/js/features/clipboard.js @@ -1,22 +1,38 @@ -export default async function initClipboard() { - const els = document.querySelectorAll('.clipboard'); - if (!els || !els.length) return; +const selector = '[data-clipboard-target], [data-clipboard-text]'; - const {default: ClipboardJS} = await import(/* webpackChunkName: "clipboard" */'clipboard'); +// TODO: replace these with toast-style notifications +function onSuccess(btn) { + if (!btn.dataset.content) return; + $(btn).popup('destroy'); + btn.dataset.content = btn.dataset.success; + $(btn).popup('show'); + btn.dataset.content = btn.dataset.original; +} +function onError(btn) { + if (!btn.dataset.content) return; + $(btn).popup('destroy'); + btn.dataset.content = btn.dataset.error; + $(btn).popup('show'); + btn.dataset.content = btn.dataset.original; +} - const clipboard = new ClipboardJS(els); - clipboard.on('success', (e) => { - e.clearSelection(); - $(e.trigger).popup('destroy'); - e.trigger.dataset.content = e.trigger.dataset.success; - $(e.trigger).popup('show'); - e.trigger.dataset.content = e.trigger.dataset.original; - }); +export default async function initClipboard() { + for (const btn of document.querySelectorAll(selector) || []) { + btn.addEventListener('click', async () => { + let text; + if (btn.dataset.clipboardText) { + text = btn.dataset.clipboardText; + } else if (btn.dataset.clipboardTarget) { + text = document.querySelector(btn.dataset.clipboardTarget)?.value; + } + if (!text) return; - clipboard.on('error', (e) => { - $(e.trigger).popup('destroy'); - e.trigger.dataset.content = e.trigger.dataset.error; - $(e.trigger).popup('show'); - e.trigger.dataset.content = e.trigger.dataset.original; - }); + try { + await navigator.clipboard.writeText(text); + onSuccess(btn); + } catch { + onError(btn); + } + }); + } }