aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsilverwind <me@silverwind.io>2024-03-29 04:00:07 +0100
committerGitHub <noreply@github.com>2024-03-29 04:00:07 +0100
commitdd8dde2be89921b2b1497c6cc5eafdde213429cb (patch)
tree7c67156c6f51db443393955df3e3e2dc4ac2e1c0
parent62b073e6f31645e446c7e8d6b5a506f61b47924e (diff)
downloadgitea-dd8dde2be89921b2b1497c6cc5eafdde213429cb.tar.gz
gitea-dd8dde2be89921b2b1497c6cc5eafdde213429cb.zip
replace jquery-minicolors with coloris (#30055)
Get rid of one more jQuery dependant and have a nicer color picker as well. Now there is only a single global color picker init because that is all that's necessary because the elements are present on the page when the init code runs. The init is slightly weird because the module only takes a selector instead of DOM elements directly. The label modals now also perform form validation because previously it was possible to trigger a 500 error `Color cannot be empty.` by clearing out the color value on labels. <img width="867" alt="Screenshot 2024-03-25 at 00 21 05" src="https://github.com/go-gitea/gitea/assets/115237/71215c39-abb1-4881-b5c1-9954b4a89adb"> <img width="860" alt="Screenshot 2024-03-25 at 00 20 48" src="https://github.com/go-gitea/gitea/assets/115237/a12cb68f-c38b-4433-ba05-53bbb4b1023e">
-rw-r--r--.dockerignore1
-rw-r--r--.gitignore1
-rw-r--r--Makefile2
-rw-r--r--package-lock.json15
-rw-r--r--package.json2
-rw-r--r--templates/projects/view.tmpl8
-rw-r--r--templates/repo/issue/labels/edit_delete_label.tmpl4
-rw-r--r--templates/repo/issue/labels/label_new.tmpl4
-rw-r--r--web_src/css/base.css5
-rw-r--r--web_src/css/features/colorpicker.css164
-rw-r--r--web_src/css/features/projects.css23
-rw-r--r--web_src/css/repo.css18
-rw-r--r--web_src/js/features/colorpicker.js35
-rw-r--r--web_src/js/features/common-global.js6
-rw-r--r--web_src/js/features/comp/ColorPicker.js16
-rw-r--r--web_src/js/features/comp/LabelEdit.js17
-rw-r--r--web_src/js/index.js2
-rw-r--r--webpack.config.js7
18 files changed, 224 insertions, 106 deletions
diff --git a/.dockerignore b/.dockerignore
index 7143c039fd..b299c7313d 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 abf9565cff..501fef7dcf 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 b4fa62e05e..8489520920 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 fa4f80fbe8..25fe14e1a6 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 b5bfda9dc6..d5a1d46056 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 b45174b086..33dd758c79 100644
--- a/templates/projects/view.tmpl
+++ b/templates/projects/view.tmpl
@@ -42,8 +42,8 @@
<div class="field color-field">
<label for="new_project_column_color_picker">{{ctx.Locale.Tr "repo.projects.column.color"}}</label>
- <div class="color picker column">
- <input class="color-picker" maxlength="7" placeholder="#c320f6" id="new_project_column_color_picker" name="color">
+ <div class="js-color-picker-input column">
+ <input maxlength="7" placeholder="#c320f6" id="new_project_column_color_picker" name="color">
{{template "repo/issue/label_precolors"}}
</div>
</div>
@@ -114,8 +114,8 @@
<div class="field color-field">
<label for="new_project_column_color">{{ctx.Locale.Tr "repo.projects.column.color"}}</label>
- <div class="color picker column">
- <input class="color-picker" maxlength="7" placeholder="#c320f6" id="new_project_column_color" name="color" value="{{.Color}}">
+ <div class="js-color-picker-input column">
+ <input maxlength="7" placeholder="#c320f6" id="new_project_column_color" name="color" value="{{.Color}}">
{{template "repo/issue/label_precolors"}}
</div>
</div>
diff --git a/templates/repo/issue/labels/edit_delete_label.tmpl b/templates/repo/issue/labels/edit_delete_label.tmpl
index 98e0f47020..fcf69217ea 100644
--- a/templates/repo/issue/labels/edit_delete_label.tmpl
+++ b/templates/repo/issue/labels/edit_delete_label.tmpl
@@ -52,8 +52,8 @@
</div>
<div class="field color-field">
<label for="color">{{ctx.Locale.Tr "repo.issues.label_color"}}</label>
- <div class="color picker column">
- <input class="color-picker" name="color" value="#70c24a" required maxlength="7">
+ <div class="column js-color-picker-input">
+ <input name="color" value="#70c24a"placeholder="#c320f6" required maxlength="7">
{{template "repo/issue/label_precolors"}}
</div>
</div>
diff --git a/templates/repo/issue/labels/label_new.tmpl b/templates/repo/issue/labels/label_new.tmpl
index 2b2b2336c4..32fd8e76d7 100644
--- a/templates/repo/issue/labels/label_new.tmpl
+++ b/templates/repo/issue/labels/label_new.tmpl
@@ -27,8 +27,8 @@
</div>
<div class="field color-field">
<label for="color">{{ctx.Locale.Tr "repo.issues.label_color"}}</label>
- <div class="color picker column">
- <input class="color-picker" name="color" value="#70c24a" required maxlength="7">
+ <div class="js-color-picker-input column">
+ <input name="color" value="#70c24a" placeholder="#c320f6" required maxlength="7">
{{template "repo/issue/label_precolors"}}
</div>
</div>
diff --git a/web_src/css/base.css b/web_src/css/base.css
index 368bc56126..21090f67ba 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 0000000000..0c651cfeb3
--- /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 30df994c38..cec5e6fc64 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 18f28dc4a6..780093fb7f 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 df0353376d..f342598e66 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 18849ba7c1..ce702f041f 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 d7e7038803..0000000000
--- 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 843657a6b6..2cc75cc6b0 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 4c707486bd..fc2f6b9b0b 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 0b0e7403e8..fdf80a5313 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: [