ColorPicker
composantThe colour well of the browser, its hexadecimal value and swatches to choose from. The well is the field: it works alone without a script.
Aperçu
Code
<twig:ColorPicker label="Accent" name="accent" value="#336699" :swatches="[{value: '#336699', label: 'Blue'}, {value: '#993366', label: 'Plum'}]" />
Code
<twig:ColorPicker label="Accent" name="accent" value="#336699" :swatches="[{value: '#336699', label: 'Blue'}, {value: '#993366', label: 'Plum'}]" />
Essayer
Changez les propriétés ; le serveur rend l’exemple de nouveau.
<twig:ColorPicker label="Accent" name="accent" value="#336699" :swatches="[{value: '#336699', label: 'Blue'}, {value: '#993366', label: 'Plum'}]" />
<twig:ColorPicker label="Accent" name="accent" value="#336699" :swatches="[{value: '#336699', label: 'Blue'}, {value: '#993366', label: 'Plum'}]" />
Variantes
disabled
Whether the field cannot be changed for now.
Installation
php bin/console ux:install color-picker --kit=github.com/parler-haut-interagir-librement/ux-kit
Propriétés
| Propriété | Type | Par défaut | Description |
|---|---|---|---|
id |
string|null |
null |
The identifier of the field. |
name |
string|null |
null |
The name of the field. |
value |
string |
'#000000' |
The colour, as #rrggbb; anything else is black. |
swatches |
array |
[] |
The colours to choose from: #rrggbb, or maps of value and label. |
valueLabel |
string |
'Hexadecimal value' |
The name of the text field. |
swatchesLabel |
string |
'Suggested colours' |
The name of the group of swatches. |
disabled |
bool |
false |
Whether the field cannot be changed for now. |
Accessibility
Each swatch is a button with a name and aria-pressed; the name of a swatch says the colour in words, which its look alone would not. What is typed and is not a colour is refused, with aria-invalid, and the server validates in any case.
Fichiers à copier
assets/controllers/color_picker_controller.js
import { Controller } from '@hotwired/stimulus';
/** A colour as `#rrggbb`, from three or six hexadecimal digits with or without `#`; null for anything else. */
export function normalize(text) {
const digits = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(text.trim())?.[1].toLowerCase();
if (digits === undefined) {
return null;
}
return `#${digits.length === 3 ? [...digits].map((digit) => digit + digit).join('') : digits}`;
}
/*
* Keeps the colour well, its text and its swatches in step. The well stays the field of the form; the text refuses
* what is not a colour, and says so with `aria-invalid`.
*/
export default class extends Controller {
static targets = ['well', 'text', 'swatch'];
connect() {
this.textTarget.hidden = false;
for (const swatch of this.swatchTargets) {
swatch.hidden = false;
swatch.style.backgroundColor = normalize(this.#colour(swatch)) ?? 'transparent';
}
this.#mark();
}
pick() {
this.textTarget.value = this.wellTarget.value;
this.textTarget.removeAttribute('aria-invalid');
this.#mark();
}
type() {
const colour = normalize(this.textTarget.value);
if (colour === null) {
this.textTarget.setAttribute('aria-invalid', 'true');
} else {
this.#set(colour);
}
}
choose(event) {
const colour = normalize(event.params.color);
if (colour !== null) {
this.#set(colour);
}
}
#set(colour) {
this.wellTarget.value = colour;
this.wellTarget.dispatchEvent(new Event('input', { bubbles: true }));
this.wellTarget.dispatchEvent(new Event('change', { bubbles: true }));
}
#mark() {
for (const swatch of this.swatchTargets) {
swatch.setAttribute('aria-pressed', String(normalize(this.#colour(swatch)) === this.wellTarget.value));
}
}
#colour(swatch) {
return swatch.getAttribute(`data-${this.identifier}-color-param`) ?? '';
}
}
templates/components/ColorPicker.html.twig
{#-
# The colour well of the browser, the hexadecimal value beside it and swatches to choose from. The well is the field:
# it works alone without a script, which only adds the rest.
#}
{%- props
## string The name the colour well is read by.
label,
## string|null The identifier of the field.
id = null,
## string|null The name of the field.
name = null,
## string The colour, as `#rrggbb`; anything else is black.
value = '#000000',
## array The colours to choose from: `#rrggbb`, or maps of `value` and `label`.
swatches = [],
## string The name of the text field.
valueLabel = 'Hexadecimal value',
## string The name of the group of swatches.
swatchesLabel = 'Suggested colours',
## bool Whether the field cannot be changed for now.
disabled = false
-%}
{%- set _value = value matches '/^#[0-9a-fA-F]{6}$/' ? value|lower : '#000000' -%}
<div{{ attributes.defaults({class: 'isColorPicker', 'data-controller': 'color-picker'}) }}>
<input class="isColorPickerWell" type="color" value="{{ _value }}" aria-label="{{ label }}"{% if id is not null %} id="{{ id }}"{% endif %}{% if name is not null %} name="{{ name }}"{% endif %}{% if disabled %} disabled{% endif %} data-color-picker-target="well" data-action="input->color-picker#pick">
<input class="isColorPickerValue" type="text" hidden value="{{ _value }}" aria-label="{{ valueLabel }}" maxlength="7" spellcheck="false" autocomplete="off"{% if disabled %} disabled{% endif %} data-color-picker-target="text" data-action="change->color-picker#type">
{%- if swatches is not empty -%}
<div class="isColorPickerSwatches" role="group" aria-label="{{ swatchesLabel }}">
{%- for swatch in swatches -%}
{%- set _swatch = swatch is iterable ? swatch : {value: swatch, label: swatch} -%}
<button type="button" class="isColorPickerSwatch" hidden aria-label="{{ _swatch.label }}" title="{{ _swatch.label }}" aria-pressed="false"{% if disabled %} disabled{% endif %} data-color-picker-target="swatch" data-color-picker-color-param="{{ _swatch.value }}" data-action="color-picker#choose"></button>
{%- endfor -%}
</div>
{%- endif -%}
</div>