diff --git a/.dockerignore b/.dockerignore index 7143c039fd2df..b299c7313d05d 100644 --- a/.dockerignore +++ b/.dockerignore @@ -78,7 +78,6 @@ cpu.out /public/assets/css /public/assets/fonts /public/assets/img/avatar -/public/assets/img/webpack /vendor /web_src/fomantic/node_modules /web_src/fomantic/build/* diff --git a/.gitignore b/.gitignore index abf9565cff3b8..501fef7dcf888 100644 --- a/.gitignore +++ b/.gitignore @@ -77,7 +77,6 @@ cpu.out /public/assets/css /public/assets/fonts /public/assets/licenses.txt -/public/assets/img/webpack /vendor /web_src/fomantic/node_modules /web_src/fomantic/build/* diff --git a/Makefile b/Makefile index b4fa62e05e821..8489520920c39 100644 --- a/Makefile +++ b/Makefile @@ -119,7 +119,7 @@ FOMANTIC_WORK_DIR := web_src/fomantic WEBPACK_SOURCES := $(shell find web_src/js web_src/css -type f) WEBPACK_CONFIGS := webpack.config.js tailwind.config.js WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css -WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts public/assets/img/webpack +WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST)) diff --git a/package-lock.json b/package-lock.json index fa4f80fbe8fe9..25fe14e1a6c99 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,11 @@ "@citation-js/plugin-bibtex": "0.7.9", "@citation-js/plugin-csl": "0.7.9", "@citation-js/plugin-software-formats": "0.6.1", - "@claviska/jquery-minicolors": "2.3.6", "@github/markdown-toolbar-element": "2.2.3", "@github/relative-time-element": "4.4.0", "@github/text-expander-element": "2.6.1", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", + "@melloware/coloris": "0.23.0", "@primer/octicons": "19.9.0", "add-asset-webpack-plugin": "2.0.1", "ansi_up": "6.0.2", @@ -394,14 +394,6 @@ "node": ">=14.0.0" } }, - "node_modules/@claviska/jquery-minicolors": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/@claviska/jquery-minicolors/-/jquery-minicolors-2.3.6.tgz", - "integrity": "sha512-8Ro6D4GCrmOl41+6w4NFhEOpx8vjxwVRI69bulXsFDt49uVRKhLU5TnzEV7AmOJrylkVq+ugnYNMiGHBieeKUQ==", - "peerDependencies": { - "jquery": ">= 1.7.x" - } - }, "node_modules/@csstools/css-parser-algorithms": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.6.1.tgz", @@ -1297,6 +1289,11 @@ "@mcaptcha/core-glue": "^0.1.0-alpha-5" } }, + "node_modules/@melloware/coloris": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@melloware/coloris/-/coloris-0.23.0.tgz", + "integrity": "sha512-VGIjI9+IQwg6BHjIE10yl0K2ARYz5bsjn6BgFEs1y1ErPAQymgdoxwVcSVL4Ai5t9OVs8xaCB7JKHqFu2N96Ow==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", diff --git a/package.json b/package.json index b5bfda9dc6e38..d5a1d460564bc 100644 --- a/package.json +++ b/package.json @@ -8,11 +8,11 @@ "@citation-js/plugin-bibtex": "0.7.9", "@citation-js/plugin-csl": "0.7.9", "@citation-js/plugin-software-formats": "0.6.1", - "@claviska/jquery-minicolors": "2.3.6", "@github/markdown-toolbar-element": "2.2.3", "@github/relative-time-element": "4.4.0", "@github/text-expander-element": "2.6.1", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", + "@melloware/coloris": "0.23.0", "@primer/octicons": "19.9.0", "add-asset-webpack-plugin": "2.0.1", "ansi_up": "6.0.2", diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl index b45174b086a7c..33dd758c79ee1 100644 --- a/templates/projects/view.tmpl +++ b/templates/projects/view.tmpl @@ -42,8 +42,8 @@
-
- +
+ {{template "repo/issue/label_precolors"}}
@@ -114,8 +114,8 @@
-
- +
+ {{template "repo/issue/label_precolors"}}
diff --git a/templates/repo/issue/labels/edit_delete_label.tmpl b/templates/repo/issue/labels/edit_delete_label.tmpl index 98e0f47020325..fcf69217ea7e9 100644 --- a/templates/repo/issue/labels/edit_delete_label.tmpl +++ b/templates/repo/issue/labels/edit_delete_label.tmpl @@ -52,8 +52,8 @@
-
- +
+ {{template "repo/issue/label_precolors"}}
diff --git a/templates/repo/issue/labels/label_new.tmpl b/templates/repo/issue/labels/label_new.tmpl index 2b2b2336c4338..32fd8e76d7cc5 100644 --- a/templates/repo/issue/labels/label_new.tmpl +++ b/templates/repo/issue/labels/label_new.tmpl @@ -27,8 +27,8 @@
-
- +
+ {{template "repo/issue/label_precolors"}}
diff --git a/web_src/css/base.css b/web_src/css/base.css index 368bc56126a9e..21090f67ba3f7 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -1436,11 +1436,6 @@ table th[data-sortt-desc] .svg { vertical-align: -0.15em; } -/* for the jquery.minicolors plugin */ -.minicolors-panel { - background: var(--color-secondary-dark-1) !important; -} - .ui.tabular.menu { border-color: var(--color-secondary); } diff --git a/web_src/css/features/colorpicker.css b/web_src/css/features/colorpicker.css new file mode 100644 index 0000000000000..0c651cfeb3b3e --- /dev/null +++ b/web_src/css/features/colorpicker.css @@ -0,0 +1,164 @@ +/* This is a stripped-down version of coloris's CSS tailored to our needs. It does only include + opaqua colors, and if more features like opacity are needed, the CSS needs to be extended + based on upstream: https://github.com/mdbassit/Coloris/blob/main/src/coloris.css. */ + +.js-color-picker-input { + display: flex; + flex-wrap: wrap; +} + +.js-color-picker-input input { + padding-top: 8px !important; + padding-bottom: 8px !important; + padding-left: 32px !important; +} + +.clr-picker { + display: none; + flex-wrap: wrap; + position: absolute; + width: 200px; + z-index: 1002; /* above .ui.modal which has 1001 */ + border-radius: var(--border-radius); + background-color: var(--color-menu); + justify-content: flex-end; + direction: ltr; + box-shadow: 0 5px 20px var(--color-shadow); + user-select: none; +} + +.clr-picker.clr-open { + display: flex; +} + +.clr-gradient { + position: relative; + width: 100%; + height: 100px; + border-radius: 3px 3px 0 0; + background: linear-gradient(rgba(0,0,0,0), #000), linear-gradient(90deg, #fff, currentcolor); /* stylelint-disable-line scale-unlimited/declaration-strict-value */ + cursor: pointer; +} + +.clr-marker { + position: absolute; + width: 12px; + height: 12px; + margin: -6px 0 0 -6px; + border: 1px solid var(--color-white); + border-radius: 50%; + background-color: currentcolor; + cursor: pointer; +} + +.clr-picker input[type="range"]::-webkit-slider-runnable-track { + width: 100%; + height: 16px; +} + +.clr-picker input[type="range"]::-webkit-slider-thumb { + width: 16px; + height: 16px; + -webkit-appearance: none; +} + +.clr-picker input[type="range"]::-moz-range-track { + width: 100%; + height: 16px; + border: 0; +} + +.clr-picker input[type="range"]::-moz-range-thumb { + width: 16px; + height: 16px; + border: 0; +} + +.clr-hue { + background: linear-gradient(to right, #f00 0%, #ff0 16.66%, #0f0 33.33%, #0ff 50%, #00f 66.66%, #f0f 83.33%, #f00 100%); /* stylelint-disable-line scale-unlimited/declaration-strict-value */ + position: relative; + width: calc(100% - 40px); + height: 10px; + margin: 10px 20px; + border-radius: 4px; +} + +.clr-hue input[type="range"] { + position: absolute; + width: calc(100% + 32px); + margin: 0; + background-color: transparent; + opacity: 0; + cursor: pointer; + appearance: none; +} + +.clr-hue div { + position: absolute; + width: 16px; + height: 16px; + left: 0; + top: 50%; + transform: translate(-50%, -50%); + border: 2px solid var(--color-white); + border-radius: 50%; + background-color: currentcolor; + box-shadow: 0 0 1px var(--color-shadow); + pointer-events: none; +} + +.clr-field { + flex: 1; + position: relative; + color: transparent; +} + +.clr-field button { + position: absolute; + aspect-ratio: 1; + height: 16px; + left: 10px; + top: 50%; + transform: translateY(-50%); + margin: 0; + padding: 0; + border: 0; + color: inherit; + pointer-events: none; + border-radius: 2px; + background: repeating-linear-gradient(45deg, #aaa 25%, transparent 25%, transparent 75%, #aaa 75%, #aaa), repeating-linear-gradient(45deg, #aaa 25%, #fff 25%, #fff 75%, #aaa 75%, #aaa); /* stylelint-disable-line scale-unlimited/declaration-strict-value */ + background-position: 0 0, 4px 4px; + background-size: 8px 8px; +} + +.clr-field button::after { + content: ""; + display: block; + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + border-radius: inherit; + background-color: currentcolor; +} + +.clr-marker:focus { + outline: none; +} + +.clr-keyboard-nav .clr-marker:focus, +.clr-keyboard-nav .clr-hue input:focus + div, +.clr-keyboard-nav .clr-alpha input:focus + div { + outline: none; + box-shadow: 0 0 2px 2px var(--color-white); +} + +.clr-picker .clr-preview, +.clr-picker .clr-clear, +.clr-picker .clr-swatches, +.clr-picker .clr-format, +.clr-picker .clr-alpha, +.clr-picker .clr-color { + display: none; +} diff --git a/web_src/css/features/projects.css b/web_src/css/features/projects.css index 30df994c38a60..cec5e6fc6433e 100644 --- a/web_src/css/features/projects.css +++ b/web_src/css/features/projects.css @@ -102,26 +102,3 @@ .card-ghost * { opacity: 0; } - -.color-field .minicolors.minicolors-theme-default { - display: block; -} - -.color-field .minicolors.minicolors-theme-default .minicolors-input { - height: 38px; - padding-left: 2rem; -} - -.color-field .minicolors.minicolors-theme-default .minicolors-swatch { - top: 10px; -} - -.edit-project-column-modal .color.picker.column, -.new-project-column-modal .color.picker.column { - display: flex; -} - -.edit-project-column-modal .color.picker.column .minicolors, -.new-project-column-modal .color.picker.column .minicolors { - flex: 1; -} diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 18f28dc4a6e0d..780093fb7fc55 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -2260,24 +2260,6 @@ padding-top: 15px; } -.edit-label.modal .form .color.picker.column, -.new-label.modal .form .color.picker.column { - display: flex; -} - -.edit-label.modal .form .color.picker.column .minicolors, -.new-label.modal .form .color.picker.column .minicolors { - flex: 1; -} - -.edit-label.modal .form .minicolors-swatch.minicolors-sprite, -.new-label.modal .form .minicolors-swatch.minicolors-sprite { - top: 10px; - left: 10px; - width: 15px; - height: 15px; -} - .tab-size-1 { tab-size: 1 !important; -moz-tab-size: 1 !important; diff --git a/web_src/js/features/colorpicker.js b/web_src/js/features/colorpicker.js index df0353376d756..f342598e664a5 100644 --- a/web_src/js/features/colorpicker.js +++ b/web_src/js/features/colorpicker.js @@ -1,12 +1,31 @@ -import $ from 'jquery'; +export async function initColorPickers(selector = '.js-color-picker-input input', opts = {}) { + const inputEls = document.querySelectorAll(selector); + if (!inputEls.length) return; -export async function createColorPicker(els) { - if (!els.length) return; - - await Promise.all([ - import(/* webpackChunkName: "minicolors" */'@claviska/jquery-minicolors'), - import(/* webpackChunkName: "minicolors" */'@claviska/jquery-minicolors/jquery.minicolors.css'), + const [{coloris, init}] = await Promise.all([ + import(/* webpackChunkName: "colorpicker" */'@melloware/coloris'), + import(/* webpackChunkName: "colorpicker" */'../../css/features/colorpicker.css'), ]); - return $(els).minicolors(); + init(); + coloris({ + el: selector, + alpha: false, + focusInput: true, + selectInput: false, + ...opts, + }); + + for (const inputEl of inputEls) { + const parent = inputEl.closest('.js-color-picker-input'); + // prevent tabbing on the color preview `button` inside the input + parent.querySelector('button').tabIndex = -1; + // init precolors + for (const el of parent.querySelectorAll('.precolors .color')) { + el.addEventListener('click', (e) => { + inputEl.value = e.target.getAttribute('data-color-hex'); + inputEl.dispatchEvent(new Event('input', {bubbles: true})); + }); + } + } } diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index 18849ba7c18ca..ce702f041fb0c 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -2,7 +2,6 @@ import $ from 'jquery'; import '../vendor/jquery.are-you-sure.js'; import {clippie} from 'clippie'; import {createDropzone} from './dropzone.js'; -import {initCompColorPicker} from './comp/ColorPicker.js'; import {showGlobalErrorMessage} from '../bootstrap.js'; import {handleGlobalEnterQuickSubmit} from './comp/QuickSubmit.js'; import {svg} from '../svg.js'; @@ -379,10 +378,7 @@ function initGlobalShowModal() { $attrTarget.text(attrib.value); // FIXME: it should be more strict here, only handle div/span/p } } - const $colorPickers = $modal.find('.color-picker'); - if ($colorPickers.length > 0) { - initCompColorPicker(); // FIXME: this might cause duplicate init - } + $modal.modal('setting', { onApprove: () => { // "form-fetch-action" can handle network errors gracefully, diff --git a/web_src/js/features/comp/ColorPicker.js b/web_src/js/features/comp/ColorPicker.js deleted file mode 100644 index d7e703880377a..0000000000000 --- a/web_src/js/features/comp/ColorPicker.js +++ /dev/null @@ -1,16 +0,0 @@ -import $ from 'jquery'; -import {createColorPicker} from '../colorpicker.js'; - -export function initCompColorPicker() { - (async () => { - await createColorPicker(document.querySelectorAll('.color-picker')); - - for (const el of document.querySelectorAll('.precolors .color')) { - el.addEventListener('click', (e) => { - const color = e.target.getAttribute('data-color-hex'); - const parent = e.target.closest('.color.picker'); - $(parent.querySelector('.color-picker')).minicolors('value', color); - }); - } - })(); -} diff --git a/web_src/js/features/comp/LabelEdit.js b/web_src/js/features/comp/LabelEdit.js index 843657a6b63d0..2cc75cc6b0831 100644 --- a/web_src/js/features/comp/LabelEdit.js +++ b/web_src/js/features/comp/LabelEdit.js @@ -1,5 +1,4 @@ import $ from 'jquery'; -import {initCompColorPicker} from './ColorPicker.js'; function isExclusiveScopeName(name) { return /.*[^/]\/[^/].*/.test(name); @@ -28,13 +27,17 @@ function updateExclusiveLabelEdit(form) { export function initCompLabelEdit(selector) { if (!$(selector).length) return; - initCompColorPicker(); // Create label $('.new-label.button').on('click', () => { updateExclusiveLabelEdit('.new-label'); $('.new-label.modal').modal({ onApprove() { + const form = document.querySelector('.new-label.form'); + if (!form.checkValidity()) { + form.reportValidity(); + return false; + } $('.new-label.form').trigger('submit'); }, }).modal('show'); @@ -60,10 +63,18 @@ export function initCompLabelEdit(selector) { updateExclusiveLabelEdit('.edit-label'); $('.edit-label .label-desc-input').val(this.getAttribute('data-description')); - $('.edit-label .color-picker').minicolors('value', this.getAttribute('data-color')); + + const colorInput = document.querySelector('.edit-label .js-color-picker-input input'); + colorInput.value = this.getAttribute('data-color'); + colorInput.dispatchEvent(new Event('input', {bubbles: true})); $('.edit-label.modal').modal({ onApprove() { + const form = document.querySelector('.edit-label.form'); + if (!form.checkValidity()) { + form.reportValidity(); + return false; + } $('.edit-label.form').trigger('submit'); }, }).modal('show'); diff --git a/web_src/js/index.js b/web_src/js/index.js index 4c707486bda97..fc2f6b9b0b919 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -86,6 +86,7 @@ import {initRepoRecentCommits} from './features/recent-commits.js'; import {initRepoDiffCommitBranchesAndTags} from './features/repo-diff-commit.js'; import {initDirAuto} from './modules/dirauto.js'; import {initRepositorySearch} from './features/repo-search.js'; +import {initColorPickers} from './features/colorpicker.js'; // Init Gitea's Fomantic settings initGiteaFomantic(); @@ -188,4 +189,5 @@ onDomReady(() => { initRepoDiffView(); initPdfViewer(); initScopedAccessTokenCategories(); + initColorPickers(); }); diff --git a/webpack.config.js b/webpack.config.js index 0b0e7403e87ab..fdf80a5313f82 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -192,13 +192,6 @@ export default { filename: 'fonts/[name].[contenthash:8][ext]', }, }, - { - test: /\.png$/i, - type: 'asset/resource', - generator: { - filename: 'img/webpack/[name].[contenthash:8][ext]', - }, - }, ], }, plugins: [