Improve project & label color picker and image scroll (#34971)

Fix #34609
Fix #34967
This commit is contained in:
wxiaoguang 2025-07-06 23:37:26 +08:00 committed by GitHub
parent e0745eb14d
commit 9dafcc5c9e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 76 additions and 57 deletions

View File

@ -134,7 +134,7 @@
</div> </div>
<div class="field"> <div class="field">
<label class="project-column-color-label" for="project-column-color-input">color</label> <label class="project-column-color-label" for="project-column-color-input">color</label>
<div class="js-color-picker-input column"> <div class="color-picker-combo" data-global-init="initColorPicker">
<input maxlength="7" placeholder="#c320f6" id="project-column-color-input" name="color"> <input maxlength="7" placeholder="#c320f6" id="project-column-color-input" name="color">
{{template "repo/issue/label_precolors"}} {{template "repo/issue/label_precolors"}}
</div> </div>

View File

@ -1,4 +1,8 @@
<div class="precolors"> <div class="precolors">
<button type="button" class="ui button generate-random-color">
{{svg "octicon-sync"}}
</button>
<div>
<div class="tw-flex"> <div class="tw-flex">
<a class="color" style="background-color:#e11d21" data-color-hex="#e11d21"></a> <a class="color" style="background-color:#e11d21" data-color-hex="#e11d21"></a>
<a class="color" style="background-color:#eb6420" data-color-hex="#eb6420"></a> <a class="color" style="background-color:#eb6420" data-color-hex="#eb6420"></a>
@ -19,4 +23,5 @@
<a class="color" style="background-color:#bfd4f2" data-color-hex="#bfd4f2"></a> <a class="color" style="background-color:#bfd4f2" data-color-hex="#bfd4f2"></a>
<a class="color" style="background-color:#d4c5f9" data-color-hex="#d4c5f9"></a> <a class="color" style="background-color:#d4c5f9" data-color-hex="#d4c5f9"></a>
</div> </div>
</div>
</div> </div>

View File

@ -49,7 +49,7 @@
</div> </div>
<div class="field"> <div class="field">
<label for="color">{{ctx.Locale.Tr "repo.issues.label_color"}}</label> <label for="color">{{ctx.Locale.Tr "repo.issues.label_color"}}</label>
<div class="column js-color-picker-input"> <div class="color-picker-combo" data-global-init="initColorPicker">
<!-- the "#" is optional because backend NormalizeColor is able to handle it, API also accepts both formats, and it is easier for users to directly copy-paste a hex value --> <!-- the "#" is optional because backend NormalizeColor is able to handle it, API also accepts both formats, and it is easier for users to directly copy-paste a hex value -->
<input name="color" value="#70c24a" placeholder="#c320f6" required pattern="^#?([\dA-Fa-f]{3}|[\dA-Fa-f]{6})$" maxlength="7"> <input name="color" value="#70c24a" placeholder="#c320f6" required pattern="^#?([\dA-Fa-f]{3}|[\dA-Fa-f]{6})$" maxlength="7">
{{template "repo/issue/label_precolors"}} {{template "repo/issue/label_precolors"}}

View File

@ -1031,19 +1031,6 @@ table th[data-sortt-desc] .svg {
min-height: 0; min-height: 0;
} }
.precolors {
display: flex;
flex-direction: column;
justify-content: center;
margin-left: 1em;
}
.precolors .color {
display: inline-block;
width: 15px;
height: 15px;
}
.ui.dropdown:not(.button) { .ui.dropdown:not(.button) {
line-height: var(--line-height-default); /* the dropdown doesn't have default line-height, use this to make the dropdown icon align with plain dropdown */ line-height: var(--line-height-default); /* the dropdown doesn't have default line-height, use this to make the dropdown icon align with plain dropdown */
} }

View File

@ -1,15 +1,13 @@
.js-color-picker-input { .color-picker-combo {
display: flex; display: flex;
position: relative; position: relative; /* to position the preview square */
} }
.js-color-picker-input input { .color-picker-combo input {
padding-top: 8px !important;
padding-bottom: 8px !important;
padding-left: 32px !important; padding-left: 32px !important;
} }
.js-color-picker-input .preview-square { .color-picker-combo .preview-square {
position: absolute; position: absolute;
aspect-ratio: 1; aspect-ratio: 1;
height: 16px; height: 16px;
@ -22,7 +20,7 @@
background-size: 8px 8px; background-size: 8px 8px;
} }
.js-color-picker-input .preview-square::after { .color-picker-combo .preview-square::after {
content: ""; content: "";
position: absolute; position: absolute;
width: 100%; width: 100%;
@ -31,6 +29,26 @@
background-color: currentcolor; background-color: currentcolor;
} }
.color-picker-combo .precolors {
display: flex;
margin-left: 1em;
align-items: center;
gap: 0.125em;
}
.color-picker-combo .precolors .generate-random-color {
padding: 0;
width: 30px;
height: 30px;
min-height: 0;
}
.color-picker-combo .precolors .color {
display: inline-block;
width: 15px;
height: 15px;
}
hex-color-picker { hex-color-picker {
width: 180px; width: 180px;
height: 120px; height: 120px;

View File

@ -71,7 +71,7 @@
.card-attachment-images { .card-attachment-images {
display: inline-block; display: inline-block;
white-space: nowrap; white-space: nowrap;
overflow: scroll; overflow: auto;
cursor: default; cursor: default;
scroll-snap-type: x mandatory; scroll-snap-type: x mandatory;
text-align: center; text-align: center;
@ -85,6 +85,7 @@
scroll-snap-align: center; scroll-snap-align: center;
margin-right: 2px; margin-right: 2px;
aspect-ratio: 1; aspect-ratio: 1;
object-fit: contain;
} }
.card-attachment-images img:only-child { .card-attachment-images img:only-child {

View File

@ -1,18 +1,19 @@
import {createTippy} from '../modules/tippy.ts'; import {createTippy} from '../modules/tippy.ts';
import type {DOMEvent} from '../utils/dom.ts'; import type {DOMEvent} from '../utils/dom.ts';
import {registerGlobalInitFunc} from '../modules/observer.ts';
export async function initColorPickers() { export async function initColorPickers() {
const els = document.querySelectorAll<HTMLElement>('.js-color-picker-input'); let imported = false;
if (!els.length) return; registerGlobalInitFunc('initColorPicker', async (el) => {
if (!imported) {
await Promise.all([ await Promise.all([
import(/* webpackChunkName: "colorpicker" */'vanilla-colorful/hex-color-picker.js'), import(/* webpackChunkName: "colorpicker" */'vanilla-colorful/hex-color-picker.js'),
import(/* webpackChunkName: "colorpicker" */'../../css/features/colorpicker.css'), import(/* webpackChunkName: "colorpicker" */'../../css/features/colorpicker.css'),
]); ]);
imported = true;
for (const el of els) {
initPicker(el);
} }
initPicker(el);
});
} }
function updateSquare(el: HTMLElement, newValue: string): void { function updateSquare(el: HTMLElement, newValue: string): void {
@ -55,13 +56,20 @@ function initPicker(el: HTMLElement): void {
}, },
}); });
// init precolors // init random color & precolors
const setSelectedColor = (color: string) => {
input.value = color;
input.dispatchEvent(new Event('input', {bubbles: true}));
updateSquare(square, color);
};
el.querySelector('.generate-random-color').addEventListener('click', () => {
const newValue = `#${Math.floor(Math.random() * 0xFFFFFF).toString(16).padStart(6, '0')}`;
setSelectedColor(newValue);
});
for (const colorEl of el.querySelectorAll<HTMLElement>('.precolors .color')) { for (const colorEl of el.querySelectorAll<HTMLElement>('.precolors .color')) {
colorEl.addEventListener('click', (e: DOMEvent<MouseEvent, HTMLAnchorElement>) => { colorEl.addEventListener('click', (e: DOMEvent<MouseEvent, HTMLAnchorElement>) => {
const newValue = e.target.getAttribute('data-color-hex'); const newValue = e.target.getAttribute('data-color-hex');
input.value = newValue; setSelectedColor(newValue);
input.dispatchEvent(new Event('input', {bubbles: true}));
updateSquare(square, newValue);
}); });
} }
} }

View File

@ -24,7 +24,7 @@ export function initCompLabelEdit(pageSelector: string) {
const elIsArchivedField = elModal.querySelector('.label-is-archived-input-field'); const elIsArchivedField = elModal.querySelector('.label-is-archived-input-field');
const elIsArchivedInput = elModal.querySelector<HTMLInputElement>('.label-is-archived-input'); const elIsArchivedInput = elModal.querySelector<HTMLInputElement>('.label-is-archived-input');
const elDescInput = elModal.querySelector<HTMLInputElement>('.label-desc-input'); const elDescInput = elModal.querySelector<HTMLInputElement>('.label-desc-input');
const elColorInput = elModal.querySelector<HTMLInputElement>('.js-color-picker-input input'); const elColorInput = elModal.querySelector<HTMLInputElement>('.color-picker-combo input');
const syncModalUi = () => { const syncModalUi = () => {
const hasScope = nameHasScope(elNameInput.value); const hasScope = nameHasScope(elNameInput.value);