color-elements
Version:
A set of web components for working with color. A Color.js project.
214 lines (182 loc) • 4.47 kB
JavaScript
import ColorElement from "../common/color-element.js";
const Self = class SpacePicker extends ColorElement {
static tagName = "space-picker";
static url = import.meta.url;
static styles = "./space-picker.css";
static shadowTemplate = `<select id="picker" part="base"></select>`;
constructor () {
super();
this._el = {};
this._el.picker = this.shadowRoot.querySelector("#picker");
}
connectedCallback () {
super.connectedCallback?.();
this._el.picker.addEventListener("input", this);
}
disconnectedCallback () {
super.disconnectedCallback?.();
this._el.picker.removeEventListener("input", this);
}
handleEvent (event) {
if (event.target === this._el.picker && event.target.value !== this.value) {
this.value = event.target.value;
}
}
propChangedCallback ({name, prop, detail: change}) {
if (name === "spaces") {
if (!this.groups) {
this._el.picker.innerHTML = Object.entries(this.spaces)
.map(([id, space]) => `<option value="${ id }">${ this.getSpaceLabel(space) }</option>`)
.join("\n");
}
else {
let groups = this.groups;
// Remove empty groups
groups = Object.entries(groups).filter(([type, spaces]) => {
if (Object.keys(spaces).length === 0) {
console.warn(`Removed empty group of color spaces with the label "${type}."`);
return false;
}
return true;
});
if (!groups.length) {
console.warn("All provided groups of color spaces are empty. Falling back to default grouping.");
groups = [["All spaces", this.spaces]];
}
this._el.picker.innerHTML = groups.map(([type, spaces]) => `
<optgroup label="${type}">
${Object.entries(spaces)
.map(([id, space]) => `<option value="${ id }">${ this.getSpaceLabel(space) }</option>`)
.join("\n")}
</optgroup>
`).join("\n");
}
this._el.picker.value = this.value;
}
if (name === "value") {
let value = this.value;
if (value) {
if (!(value in this.spaces)) {
let spaces = Object.keys(this.spaces);
let firstSpace = spaces[0];
let currentSpace = this._el.picker.value;
let fallback = spaces.includes(currentSpace) ? currentSpace : firstSpace;
console.warn(`No color space found with id = "${ value }". Choose one of the following: ${ spaces.join(", ") }. Falling back to "${ fallback }".`);
this.value = value = fallback;
}
if (this._el.picker.value !== value) {
this._el.picker.value = value;
}
}
}
}
static props = {
value: {
default () {
if (this.groups) {
let groups = this.groups;
let firstGroup = Object.values(groups)[0];
return firstGroup && Object.keys(firstGroup)[0];
}
else {
return Object.keys(this.spaces)[0];
}
},
},
selectedSpace: {
get () {
let value = this.value;
if (value === undefined || value === null) {
return;
}
return this.spaces[value];
},
},
spaces: {
type: {
is: Object,
get values () {
return Self.Color.Space;
},
defaultValue: (id, index) => {
try {
return Self.Color.Space.get(id);
}
catch (e) {
console.error(e);
}
},
},
default: () => Self.Color.spaces,
convert (value) {
// Replace non-existing spaces with { id, name: id }
for (let id in value) {
if (!value[id]) {
value[id] = { id, name: id };
}
}
return value;
},
stringify (value) {
return Object.entries(value).map(([id, space]) => id).join(", ");
},
},
groupBy: {
type: {
is: Function,
arguments: ["space"],
},
reflect: false,
},
groups: {
get () {
if (!this.groupBy) {
return;
}
let ret = {};
for (let [id, space] of Object.entries(this.spaces)) {
let group = this.groupBy(space);
if (group) {
(ret[group] ??= {})[id] = space;
}
}
return ret;
},
},
getSpaceLabel: {
type: {
is: Function,
arguments: ["space"],
},
default () {
return space => space.name;
},
reflect: false,
},
};
static events = {
change: {
from () {
return this._el.picker;
},
},
input: {
from () {
return this._el.picker;
},
},
valuechange: {
propchange: "value",
},
spacechange: {
propchange: "selectedSpace",
},
};
static formAssociated = {
like: el => el._el.picker,
role: "combobox",
changeEvent: "change",
};
};
Self.define();
export default Self;