color-elements 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +30 -0
- package/.editorconfig +8 -0
- package/.prettierrc +17 -0
- package/.vscode/settings.json +7 -0
- package/README.md +27 -18
- package/_build/copy-config.js +15 -14
- package/_build/copy-config.json +4 -9
- package/_build/eleventy.js +8 -8
- package/_includes/component.njk +9 -14
- package/_includes/plain.njk +5 -0
- package/_redirects +1 -1
- package/assets/css/style.css +4 -4
- package/debug.html +46 -0
- package/package.json +19 -8
- package/src/channel-picker/channel-picker.css +4 -4
- package/src/channel-picker/channel-picker.js +15 -11
- package/src/channel-slider/channel-slider.css +14 -7
- package/src/channel-slider/channel-slider.js +13 -5
- package/src/color-chart/README.md +36 -5
- package/src/color-chart/color-chart-global.css +13 -12
- package/src/color-chart/color-chart-shadow.css +123 -0
- package/src/color-chart/color-chart.css +2 -112
- package/src/color-chart/color-chart.js +307 -103
- package/src/color-inline/color-inline.css +21 -16
- package/src/color-inline/color-inline.js +2 -3
- package/src/color-inline/style.css +1 -1
- package/src/color-picker/color-picker.css +3 -3
- package/src/color-picker/color-picker.js +14 -7
- package/src/color-scale/README.md +42 -2
- package/src/color-scale/color-scale.css +1 -1
- package/src/color-scale/color-scale.js +12 -9
- package/src/color-slider/README.md +17 -3
- package/src/color-slider/color-slider.css +54 -33
- package/src/color-slider/color-slider.js +9 -7
- package/src/color-swatch/color-swatch.css +41 -32
- package/src/color-swatch/color-swatch.js +17 -9
- package/src/common/color-element.js +34 -76
- package/src/common/dom.js +4 -2
- package/src/common/util.js +1 -1
- package/src/gamut-badge/gamut-badge.css +33 -13
- package/src/gamut-badge/gamut-badge.js +9 -7
- package/src/space-picker/space-picker.css +3 -3
- package/src/space-picker/space-picker.js +28 -10
- package/src/src.json +1 -1
|
@@ -5,7 +5,7 @@ import ColorElement from "../common/color-element.js";
|
|
|
5
5
|
const Self = class ColorChart extends ColorElement {
|
|
6
6
|
static tagName = "color-chart";
|
|
7
7
|
static url = import.meta.url;
|
|
8
|
-
static styles = "./color-chart.css";
|
|
8
|
+
static styles = "./color-chart-shadow.css";
|
|
9
9
|
static globalStyles = "./color-chart-global.css";
|
|
10
10
|
static shadowTemplate = `
|
|
11
11
|
<slot name="color-channel">
|
|
@@ -30,9 +30,9 @@ const Self = class ColorChart extends ColorElement {
|
|
|
30
30
|
super();
|
|
31
31
|
|
|
32
32
|
this._el = {
|
|
33
|
-
slot:
|
|
33
|
+
slot: this.shadowRoot.querySelector("slot:not([name])"),
|
|
34
34
|
channel_picker: this.shadowRoot.getElementById("channel_picker"),
|
|
35
|
-
chart:
|
|
35
|
+
chart: this.shadowRoot.getElementById("chart"),
|
|
36
36
|
xTicks: this.shadowRoot.querySelector("#x_axis .ticks"),
|
|
37
37
|
yTicks: this.shadowRoot.querySelector("#y_axis .ticks"),
|
|
38
38
|
xLabel: this.shadowRoot.querySelector("#x_axis .label"),
|
|
@@ -46,77 +46,159 @@ const Self = class ColorChart extends ColorElement {
|
|
|
46
46
|
|
|
47
47
|
connectedCallback () {
|
|
48
48
|
super.connectedCallback();
|
|
49
|
-
this._el.chart.addEventListener("colorschange", this, {capture: true});
|
|
49
|
+
this._el.chart.addEventListener("colorschange", this, { capture: true });
|
|
50
50
|
this._slots.color_channel.addEventListener("input", this);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
disconnectedCallback () {
|
|
54
|
-
this._el.chart.removeEventListener("colorschange", this, {capture: true});
|
|
54
|
+
this._el.chart.removeEventListener("colorschange", this, { capture: true });
|
|
55
55
|
this._slots.color_channel.removeEventListener("input", this);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
handleEvent (evt) {
|
|
59
59
|
let source = evt.target;
|
|
60
|
+
|
|
60
61
|
if (source.tagName === "COLOR-SCALE" && evt.name === "computedColors") {
|
|
62
|
+
// TODO check if changed
|
|
61
63
|
this.render(evt);
|
|
62
64
|
}
|
|
63
65
|
|
|
64
|
-
if (
|
|
66
|
+
if (
|
|
67
|
+
this._el.channel_picker === source ||
|
|
68
|
+
this._slots.color_channel.assignedElements().includes(source)
|
|
69
|
+
) {
|
|
65
70
|
this.y = source.value;
|
|
66
71
|
}
|
|
67
72
|
}
|
|
68
73
|
|
|
69
74
|
series = new WeakMap();
|
|
70
75
|
|
|
76
|
+
bounds = { x: { min: Infinity, max: -Infinity }, y: { min: Infinity, max: -Infinity } };
|
|
77
|
+
|
|
78
|
+
// Follow the provided min/max values, if any
|
|
79
|
+
get force () {
|
|
80
|
+
const ctx = this;
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
x: {
|
|
84
|
+
get min () {
|
|
85
|
+
return !isNaN(ctx.xMin);
|
|
86
|
+
},
|
|
87
|
+
get max () {
|
|
88
|
+
return !isNaN(ctx.xMax);
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
y: {
|
|
92
|
+
get min () {
|
|
93
|
+
return !isNaN(ctx.yMin);
|
|
94
|
+
},
|
|
95
|
+
get max () {
|
|
96
|
+
return !isNaN(ctx.yMax);
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
71
102
|
render (evt) {
|
|
72
|
-
|
|
103
|
+
this.renderScales(evt);
|
|
104
|
+
this.renderAxis("x");
|
|
105
|
+
this.renderAxis("y");
|
|
106
|
+
}
|
|
73
107
|
|
|
74
|
-
|
|
75
|
-
|
|
108
|
+
/**
|
|
109
|
+
* (Re)render one of the axes
|
|
110
|
+
* @param {string} axis "x" or "y"
|
|
111
|
+
*/
|
|
112
|
+
renderAxis (axis) {
|
|
113
|
+
axis = axis.toLowerCase();
|
|
114
|
+
|
|
115
|
+
let min = this[`${axis}MinAsNumber`];
|
|
116
|
+
if (isNaN(min) || !isFinite(min)) {
|
|
117
|
+
// auto, undefined, etc
|
|
118
|
+
min = this.bounds[axis].min;
|
|
76
119
|
}
|
|
77
120
|
|
|
78
|
-
let
|
|
79
|
-
|
|
121
|
+
let max = this[`${axis}MaxAsNumber`];
|
|
122
|
+
if (isNaN(max) || !isFinite(max)) {
|
|
123
|
+
// auto, undefined, etc
|
|
124
|
+
max = this.bounds[axis].max;
|
|
125
|
+
}
|
|
80
126
|
|
|
81
|
-
|
|
82
|
-
|
|
127
|
+
if (isFinite(min) && isFinite(max)) {
|
|
128
|
+
const axisData = getAxis({
|
|
129
|
+
min,
|
|
130
|
+
max,
|
|
131
|
+
initialSteps: 10,
|
|
132
|
+
force: this.force[axis],
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
this._el.chart.style.setProperty(`--min-${axis}`, axisData.min);
|
|
136
|
+
this._el.chart.style.setProperty(`--max-${axis}`, axisData.max);
|
|
137
|
+
this._el.chart.style.setProperty(`--steps-${axis}`, axisData.steps);
|
|
138
|
+
|
|
139
|
+
const tickElements = Array(axisData.steps)
|
|
140
|
+
.fill()
|
|
141
|
+
.map(
|
|
142
|
+
(_, i) =>
|
|
143
|
+
`<div part='${axis} tick'>${+(axisData.min + i * axisData.step).toPrecision(15)}</div>`,
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
let ticksEl = this._el[`${axis}Ticks`];
|
|
147
|
+
ticksEl.innerHTML =
|
|
148
|
+
axis === "y" ? tickElements.toReversed().join("\n") : tickElements.join("\n");
|
|
149
|
+
}
|
|
83
150
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
151
|
+
let resolved = this[`${axis}Resolved`];
|
|
152
|
+
let labelEl = this._el[`${axis}Label`];
|
|
153
|
+
// Set axis label if we have a custom coordinate
|
|
154
|
+
labelEl.textContent = resolved ? this.space.name + " " + resolved.name : "";
|
|
155
|
+
}
|
|
87
156
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
157
|
+
/** (Re)render all scales */
|
|
158
|
+
renderScales (evt) {
|
|
159
|
+
let colorScales = this.querySelectorAll("color-scale");
|
|
91
160
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
161
|
+
if (colorScales.length === 0 || evt.name === "computedColors") {
|
|
162
|
+
this.bounds = {
|
|
163
|
+
x: { min: Infinity, max: -Infinity },
|
|
164
|
+
y: { min: Infinity, max: -Infinity },
|
|
165
|
+
};
|
|
97
166
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
this._el.chart.style.setProperty("--max-x", xAxis.max);
|
|
102
|
-
this._el.chart.style.setProperty("--steps-x", xAxis.steps);
|
|
103
|
-
this._el.xTicks.innerHTML = Array(xAxis.steps).fill().map((_, i) => "<div part='x tick'>" + +(xAxis.min + i * xAxis.step).toPrecision(15) + "</div>").join("\n");
|
|
167
|
+
if (colorScales.length === 0) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
104
170
|
}
|
|
105
171
|
|
|
106
|
-
|
|
107
|
-
|
|
172
|
+
for (let colorScale of colorScales) {
|
|
173
|
+
let scale = this.series.get(colorScale);
|
|
108
174
|
|
|
109
|
-
|
|
110
|
-
|
|
175
|
+
if (
|
|
176
|
+
!scale ||
|
|
177
|
+
!evt ||
|
|
178
|
+
evt.target === colorScale ||
|
|
179
|
+
evt.target.nodeName !== "COLOR-SCALE"
|
|
180
|
+
) {
|
|
181
|
+
scale = this.renderScale(colorScale);
|
|
111
182
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
183
|
+
if (scale) {
|
|
184
|
+
if (scale.x.min < this.bounds.x.min) {
|
|
185
|
+
this.bounds.x.min = scale.x.min;
|
|
186
|
+
}
|
|
187
|
+
if (scale.x.max > this.bounds.x.max) {
|
|
188
|
+
this.bounds.x.max = scale.x.max;
|
|
189
|
+
}
|
|
190
|
+
if (scale.y.min < this.bounds.y.min) {
|
|
191
|
+
this.bounds.y.min = scale.y.min;
|
|
192
|
+
}
|
|
193
|
+
if (scale.y.max > this.bounds.y.max) {
|
|
194
|
+
this.bounds.y.max = scale.y.max;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
117
198
|
}
|
|
118
199
|
}
|
|
119
200
|
|
|
201
|
+
/** (Re)render one scale */
|
|
120
202
|
renderScale (colorScale) {
|
|
121
203
|
if (!colorScale.computedColors) {
|
|
122
204
|
// Not yet initialized
|
|
@@ -126,56 +208,82 @@ const Self = class ColorChart extends ColorElement {
|
|
|
126
208
|
let ret = {
|
|
127
209
|
element: colorScale,
|
|
128
210
|
swatches: new WeakMap(),
|
|
129
|
-
x: {min: Infinity, max: -Infinity, values: new WeakMap() },
|
|
130
|
-
y: {min: Infinity, max: -Infinity, values: new WeakMap()},
|
|
211
|
+
x: { min: Infinity, max: -Infinity, values: new WeakMap() },
|
|
212
|
+
y: { min: Infinity, max: -Infinity, values: new WeakMap() },
|
|
131
213
|
colors: colorScale.computedColors?.slice() ?? [],
|
|
132
214
|
};
|
|
133
215
|
|
|
134
216
|
colorScale.style.setProperty("--color-count", ret.colors.length);
|
|
135
217
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
218
|
+
// Helper function to calculate coordinates for an axis
|
|
219
|
+
const calculateAxisCoords = axis => {
|
|
220
|
+
const resolved = this[`${axis}Resolved`];
|
|
221
|
+
if (!resolved) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
139
224
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
225
|
+
const coords = ret.colors.map(({ color }) => color.to(this.space).get(this[axis]));
|
|
226
|
+
const min = this[`${axis}MinAsNumber`];
|
|
227
|
+
const max = this[`${axis}MaxAsNumber`];
|
|
228
|
+
|
|
229
|
+
if (resolved.type === "angle") {
|
|
230
|
+
// First, normalize
|
|
231
|
+
return { coords: normalizeAngles(coords), min, max };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return { coords, min, max };
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const yAxis = calculateAxisCoords("y");
|
|
238
|
+
const xAxis = calculateAxisCoords("x");
|
|
144
239
|
|
|
145
240
|
let index = 0;
|
|
146
|
-
for (let {name, color} of ret.colors) {
|
|
241
|
+
for (let { name, color } of ret.colors) {
|
|
147
242
|
let swatch = colorScale._el.swatches.children[index];
|
|
148
243
|
ret.colors[index] = color = color.to(this.space);
|
|
149
244
|
ret.swatches.set(color, swatch);
|
|
150
245
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
// or separated from the previous text with a space (e.g., Red 50 or Red / 50).
|
|
156
|
-
let x = name.match(/(?:^|\s)-?\d*\.?\d+$/)?.[0];
|
|
157
|
-
if (x !== undefined) {
|
|
158
|
-
// Transform `Label / X-coord` to `Label`
|
|
159
|
-
// (there should be at least one space before and after the slash so the number is treated as an X-coord)
|
|
160
|
-
let label = name.slice(0, -x.length).trim();
|
|
161
|
-
if (label.endsWith("/")) {
|
|
162
|
-
name = label.slice(0, -1).trim();
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
swatch.label = name;
|
|
166
|
-
|
|
167
|
-
x = Number(x);
|
|
246
|
+
let x;
|
|
247
|
+
if (xAxis) {
|
|
248
|
+
// Use the calculated X coordinate from the specified coordinate
|
|
249
|
+
x = xAxis.coords[index];
|
|
168
250
|
}
|
|
169
251
|
else {
|
|
170
|
-
|
|
252
|
+
// It's not always possible to use the last number in the color label as the X-coord;
|
|
253
|
+
// for example, the number "9" can't be interpreted as the X-coord in the "#90caf9" label.
|
|
254
|
+
// It might cause bugs with color order (see https://github.com/color-js/elements/issues/103).
|
|
255
|
+
// We expect the valid X-coord to be the only number in the color label (e.g., 50)
|
|
256
|
+
// or separated from the previous text with a space (e.g., Red 50 or Red / 50).
|
|
257
|
+
x = name.match(/(?:^|\s)-?\d*\.?\d+$/)?.[0];
|
|
258
|
+
if (x !== undefined) {
|
|
259
|
+
// Transform `Label / X-coord` to `Label`
|
|
260
|
+
// (there should be at least one space before and after the slash so the number is treated as an X-coord)
|
|
261
|
+
let label = name.slice(0, -x.length).trim();
|
|
262
|
+
if (label.endsWith("/")) {
|
|
263
|
+
name = label.slice(0, -1).trim();
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
swatch.label = name;
|
|
267
|
+
|
|
268
|
+
x = Number(x);
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
x = index;
|
|
272
|
+
}
|
|
171
273
|
}
|
|
172
274
|
|
|
173
|
-
let y =
|
|
275
|
+
let y = yAxis.coords[index];
|
|
174
276
|
|
|
175
277
|
ret.x.values.set(color, x);
|
|
176
278
|
ret.y.values.set(color, y);
|
|
177
279
|
|
|
178
|
-
|
|
280
|
+
const yOutOfRange =
|
|
281
|
+
(isFinite(yAxis.min) && y < yAxis.min) || (isFinite(yAxis.max) && y > yAxis.max);
|
|
282
|
+
const xOutOfRange = xAxis
|
|
283
|
+
? (isFinite(xAxis.min) && x < xAxis.min) || (isFinite(xAxis.max) && x > xAxis.max)
|
|
284
|
+
: false;
|
|
285
|
+
const outOfRange = yOutOfRange || xOutOfRange;
|
|
286
|
+
|
|
179
287
|
if (!outOfRange) {
|
|
180
288
|
// Only swatches that are in range participate in the min/max calculation
|
|
181
289
|
ret.x.min = Math.min(ret.x.min, x);
|
|
@@ -188,7 +296,10 @@ const Self = class ColorChart extends ColorElement {
|
|
|
188
296
|
swatch.style.setProperty("--y", y);
|
|
189
297
|
swatch.style.setProperty("--index", index);
|
|
190
298
|
|
|
191
|
-
if (
|
|
299
|
+
if (
|
|
300
|
+
HTMLElement.prototype.hasOwnProperty("popover") &&
|
|
301
|
+
!swatch._el.wrapper.hasAttribute("popover")
|
|
302
|
+
) {
|
|
192
303
|
// The Popover API is supported
|
|
193
304
|
let popover = swatch._el.wrapper;
|
|
194
305
|
popover.setAttribute("popover", "");
|
|
@@ -218,7 +329,10 @@ const Self = class ColorChart extends ColorElement {
|
|
|
218
329
|
let swatch = ret.swatches.get(color);
|
|
219
330
|
|
|
220
331
|
if (prevColor !== undefined) {
|
|
221
|
-
prevColor.style.setProperty(
|
|
332
|
+
prevColor.style.setProperty(
|
|
333
|
+
"--next-color",
|
|
334
|
+
swatch.style.getPropertyValue("--color"),
|
|
335
|
+
);
|
|
222
336
|
prevColor.style.setProperty("--next-x", ret.x.values.get(color));
|
|
223
337
|
prevColor.style.setProperty("--next-y", ret.y.values.get(color));
|
|
224
338
|
}
|
|
@@ -229,7 +343,8 @@ const Self = class ColorChart extends ColorElement {
|
|
|
229
343
|
if (prevColor !== undefined) {
|
|
230
344
|
// When we update colors, and we have fewer colors than before,
|
|
231
345
|
// we need to make sure the last swatch is not connected to the non-existent next swatch
|
|
232
|
-
["--next-color", "--next-x", "--next-y"].forEach(prop =>
|
|
346
|
+
["--next-color", "--next-x", "--next-y"].forEach(prop =>
|
|
347
|
+
prevColor.style.removeProperty(prop));
|
|
233
348
|
}
|
|
234
349
|
|
|
235
350
|
this.series.set(colorScale, ret);
|
|
@@ -238,10 +353,15 @@ const Self = class ColorChart extends ColorElement {
|
|
|
238
353
|
}
|
|
239
354
|
|
|
240
355
|
propChangedCallback (evt) {
|
|
241
|
-
let {name, prop, detail: change} = evt;
|
|
356
|
+
let { name, prop, detail: change } = evt;
|
|
357
|
+
|
|
358
|
+
if (name.startsWith("x") || name.startsWith("y")) {
|
|
359
|
+
let axis = name[0];
|
|
360
|
+
|
|
361
|
+
if (!/^[xy](?:Resolved|(?:Min|Max)AsNumber)$/.test(name)) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
242
364
|
|
|
243
|
-
if (["yResolved", "yMinAsNumber", "yMaxAsNumber"].includes(name)) {
|
|
244
|
-
// Re-render swatches
|
|
245
365
|
this.render(evt);
|
|
246
366
|
}
|
|
247
367
|
|
|
@@ -255,6 +375,9 @@ const Self = class ColorChart extends ColorElement {
|
|
|
255
375
|
static props = {
|
|
256
376
|
y: {
|
|
257
377
|
default: "oklch.l",
|
|
378
|
+
changed (change) {
|
|
379
|
+
this.bounds.y = { min: Infinity, max: -Infinity };
|
|
380
|
+
},
|
|
258
381
|
convert (value) {
|
|
259
382
|
// Try setting the value to the channel picker. The picker will handle possible erroneous values.
|
|
260
383
|
this._el.channel_picker.value = value;
|
|
@@ -273,7 +396,6 @@ const Self = class ColorChart extends ColorElement {
|
|
|
273
396
|
get () {
|
|
274
397
|
return Self.Color.Space.resolveCoord(this.y, "oklch");
|
|
275
398
|
},
|
|
276
|
-
// rawProp: "coord",
|
|
277
399
|
},
|
|
278
400
|
|
|
279
401
|
yMin: {
|
|
@@ -282,8 +404,7 @@ const Self = class ColorChart extends ColorElement {
|
|
|
282
404
|
let { value } = change;
|
|
283
405
|
|
|
284
406
|
if (value === "auto") {
|
|
285
|
-
|
|
286
|
-
this._el.chart.style.removeProperty("--min-y");
|
|
407
|
+
this.bounds.y.min = Infinity;
|
|
287
408
|
}
|
|
288
409
|
},
|
|
289
410
|
reflect: {
|
|
@@ -298,14 +419,7 @@ const Self = class ColorChart extends ColorElement {
|
|
|
298
419
|
return range[0];
|
|
299
420
|
}
|
|
300
421
|
else if (this.yMin === "auto") {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
if (minY !== "") {
|
|
304
|
-
return Number(minY);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Intermediate state (the chart is not rendered yet)
|
|
308
|
-
return;
|
|
422
|
+
return this.bounds.y.min;
|
|
309
423
|
}
|
|
310
424
|
|
|
311
425
|
return Number(this.yMin);
|
|
@@ -319,7 +433,6 @@ const Self = class ColorChart extends ColorElement {
|
|
|
319
433
|
else {
|
|
320
434
|
this.yMin = value.toString();
|
|
321
435
|
}
|
|
322
|
-
|
|
323
436
|
},
|
|
324
437
|
},
|
|
325
438
|
|
|
@@ -329,8 +442,7 @@ const Self = class ColorChart extends ColorElement {
|
|
|
329
442
|
let { value } = change;
|
|
330
443
|
|
|
331
444
|
if (value === "auto") {
|
|
332
|
-
|
|
333
|
-
this._el.chart.style.removeProperty("--max-y");
|
|
445
|
+
this.bounds.y.max = -Infinity;
|
|
334
446
|
}
|
|
335
447
|
},
|
|
336
448
|
reflect: {
|
|
@@ -345,18 +457,10 @@ const Self = class ColorChart extends ColorElement {
|
|
|
345
457
|
return range[1];
|
|
346
458
|
}
|
|
347
459
|
else if (this.yMax === "auto") {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
if (maxY !== "") {
|
|
351
|
-
return Number(maxY);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Intermediate state (the chart is not rendered yet)
|
|
355
|
-
return;
|
|
460
|
+
return this.bounds.y.max;
|
|
356
461
|
}
|
|
357
462
|
|
|
358
463
|
return Number(this.yMax);
|
|
359
|
-
|
|
360
464
|
},
|
|
361
465
|
set (value) {
|
|
362
466
|
value = Number(value);
|
|
@@ -367,7 +471,99 @@ const Self = class ColorChart extends ColorElement {
|
|
|
367
471
|
else {
|
|
368
472
|
this.yMax = value.toString();
|
|
369
473
|
}
|
|
474
|
+
},
|
|
475
|
+
},
|
|
476
|
+
|
|
477
|
+
x: {
|
|
478
|
+
default: null,
|
|
479
|
+
changed (change) {
|
|
480
|
+
this.bounds.x = { min: Infinity, max: -Infinity };
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
|
|
484
|
+
xResolved: {
|
|
485
|
+
get () {
|
|
486
|
+
return this.x ? Self.Color.Space.resolveCoord(this.x, "oklch") : null;
|
|
487
|
+
},
|
|
488
|
+
},
|
|
489
|
+
|
|
490
|
+
xMin: {
|
|
491
|
+
default: "auto",
|
|
492
|
+
changed (change) {
|
|
493
|
+
let { value } = change;
|
|
494
|
+
|
|
495
|
+
if (value === "auto") {
|
|
496
|
+
this.bounds.x.min = Infinity;
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
reflect: {
|
|
500
|
+
from: "xmin",
|
|
501
|
+
},
|
|
502
|
+
},
|
|
503
|
+
|
|
504
|
+
xMinAsNumber: {
|
|
505
|
+
get () {
|
|
506
|
+
let force = this.force;
|
|
507
|
+
|
|
508
|
+
if (this.x && this.xMin === "coord") {
|
|
509
|
+
let range = this.xResolved?.refRange ?? this.xResolved?.range ?? [0, 100];
|
|
510
|
+
return range[0];
|
|
511
|
+
}
|
|
512
|
+
else if ((!this.x && !force.x.min) || this.xMin === "auto") {
|
|
513
|
+
return this.bounds.x.min;
|
|
514
|
+
}
|
|
370
515
|
|
|
516
|
+
return Number(this.xMin);
|
|
517
|
+
},
|
|
518
|
+
set (value) {
|
|
519
|
+
value = Number(value);
|
|
520
|
+
|
|
521
|
+
if (Number.isNaN(value)) {
|
|
522
|
+
this.xMin = "auto";
|
|
523
|
+
}
|
|
524
|
+
else {
|
|
525
|
+
this.xMin = value.toString();
|
|
526
|
+
}
|
|
527
|
+
},
|
|
528
|
+
},
|
|
529
|
+
|
|
530
|
+
xMax: {
|
|
531
|
+
default: "auto",
|
|
532
|
+
changed (change) {
|
|
533
|
+
let { value } = change;
|
|
534
|
+
|
|
535
|
+
if (value === "auto") {
|
|
536
|
+
this.bounds.x.max = -Infinity;
|
|
537
|
+
}
|
|
538
|
+
},
|
|
539
|
+
reflect: {
|
|
540
|
+
from: "xmax",
|
|
541
|
+
},
|
|
542
|
+
},
|
|
543
|
+
|
|
544
|
+
xMaxAsNumber: {
|
|
545
|
+
get () {
|
|
546
|
+
let force = this.force;
|
|
547
|
+
|
|
548
|
+
if (this.x && this.xMax === "coord") {
|
|
549
|
+
let range = this.xResolved?.refRange ?? this.xResolved?.range ?? [0, 100];
|
|
550
|
+
return range[1];
|
|
551
|
+
}
|
|
552
|
+
else if ((!this.x && !force.x.max) || this.xMax === "auto") {
|
|
553
|
+
return this.bounds.x.max;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
return Number(this.xMax);
|
|
557
|
+
},
|
|
558
|
+
set (value) {
|
|
559
|
+
value = Number(value);
|
|
560
|
+
|
|
561
|
+
if (Number.isNaN(value)) {
|
|
562
|
+
this.xMax = "auto";
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
this.xMax = value.toString();
|
|
566
|
+
}
|
|
371
567
|
},
|
|
372
568
|
},
|
|
373
569
|
|
|
@@ -386,7 +582,7 @@ Self.define();
|
|
|
386
582
|
|
|
387
583
|
export default Self;
|
|
388
584
|
|
|
389
|
-
function getAxis (min, max, initialSteps) {
|
|
585
|
+
function getAxis ({ min, max, initialSteps, force = { min: false, max: false } }) {
|
|
390
586
|
let range = max - min;
|
|
391
587
|
let step = range / initialSteps;
|
|
392
588
|
let magnitude = Math.floor(Math.log10(step));
|
|
@@ -400,11 +596,15 @@ function getAxis (min, max, initialSteps) {
|
|
|
400
596
|
}
|
|
401
597
|
}
|
|
402
598
|
|
|
403
|
-
|
|
404
|
-
|
|
599
|
+
if (force === true) {
|
|
600
|
+
force = { min: true, max: true };
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
let start = force.min ? min : Math.floor(min / step) * step;
|
|
604
|
+
let end = force.max ? max : Math.ceil(max / step) * step;
|
|
405
605
|
let steps = Math.round((end - start) / step);
|
|
406
606
|
|
|
407
|
-
let ret = {min: start, max: end, step, steps};
|
|
607
|
+
let ret = { min: start, max: end, step, steps };
|
|
408
608
|
for (let prop in ret) {
|
|
409
609
|
ret[prop] = +ret[prop].toPrecision(15);
|
|
410
610
|
}
|
|
@@ -416,7 +616,11 @@ function normalizeAngles (angles) {
|
|
|
416
616
|
angles = angles.map(h => ((h % 360) + 360) % 360);
|
|
417
617
|
|
|
418
618
|
// Remove top and bottom 25% and find average
|
|
419
|
-
let averageHue =
|
|
619
|
+
let averageHue =
|
|
620
|
+
angles
|
|
621
|
+
.toSorted((a, b) => a - b)
|
|
622
|
+
.slice(angles.length / 4, -angles.length / 4)
|
|
623
|
+
.reduce((a, b) => a + b, 0) / angles.length;
|
|
420
624
|
|
|
421
625
|
for (let i = 0; i < angles.length; i++) {
|
|
422
626
|
let h = angles[i];
|
|
@@ -1,39 +1,44 @@
|
|
|
1
1
|
[part="swatch-wrapper"] {
|
|
2
2
|
display: inline-flex;
|
|
3
3
|
align-items: baseline;
|
|
4
|
-
gap: .2em;
|
|
5
|
-
margin-inline: .1em;
|
|
4
|
+
gap: 0.2em;
|
|
5
|
+
margin-inline: 0.1em;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
#swatch {
|
|
9
|
-
--_transparency-cell-size: var(--transparency-cell-size, clamp(6px, .5em, 30px));
|
|
9
|
+
--_transparency-cell-size: var(--transparency-cell-size, clamp(6px, 0.5em, 30px));
|
|
10
10
|
--_transparency-background: var(--transparency-background, transparent);
|
|
11
11
|
--_transparency-darkness: var(--transparency-darkness, 5%);
|
|
12
|
-
--_transparency-grid: var(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
--_transparency-grid: var(
|
|
13
|
+
--transparency-grid,
|
|
14
|
+
repeating-conic-gradient(
|
|
15
|
+
transparent 0 25%,
|
|
16
|
+
rgb(0 0 0 / var(--_transparency-darkness)) 0 50%
|
|
17
|
+
)
|
|
18
|
+
0 0 / calc(2 * var(--_transparency-cell-size)) calc(2 * var(--_transparency-cell-size))
|
|
19
|
+
content-box border-box var(--_transparency-background)
|
|
16
20
|
);
|
|
17
21
|
|
|
18
22
|
--color-image: linear-gradient(var(--color), var(--color));
|
|
19
|
-
--border-width: clamp(2px, .15em, 16px);
|
|
20
|
-
--box-shadow-blur: clamp(2px, .1em, 5px);
|
|
21
|
-
--box-shadow-color: rgb(0 0 0 / .3);
|
|
23
|
+
--border-width: clamp(2px, 0.15em, 16px);
|
|
24
|
+
--box-shadow-blur: clamp(2px, 0.1em, 5px);
|
|
25
|
+
--box-shadow-color: rgb(0 0 0 / 0.3);
|
|
22
26
|
|
|
23
27
|
position: relative;
|
|
24
|
-
bottom: calc(-1 * var(--border-width) - .1em);
|
|
28
|
+
bottom: calc(-1 * var(--border-width) - 0.1em);
|
|
25
29
|
width: 1.2em;
|
|
26
30
|
height: 1.2em;
|
|
27
31
|
border: var(--border-width) solid white;
|
|
28
32
|
box-sizing: border-box;
|
|
29
33
|
background: var(--color-image, 0), var(--_transparency-grid, canvas);
|
|
30
|
-
box-shadow: calc(var(--box-shadow-blur) * .2) calc(var(--box-shadow-blur) * .2)
|
|
31
|
-
|
|
34
|
+
box-shadow: calc(var(--box-shadow-blur) * 0.2) calc(var(--box-shadow-blur) * 0.2)
|
|
35
|
+
var(--box-shadow-blur) var(--box-shadow-color);
|
|
36
|
+
border-radius: clamp(1px, 0.1em, 10px);
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
#swatch:hover {
|
|
35
40
|
transform: scale(1.5);
|
|
36
|
-
transition: .4s;
|
|
41
|
+
transition: 0.4s;
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
#swatch.invalid {
|
|
@@ -43,6 +48,6 @@
|
|
|
43
48
|
|
|
44
49
|
@media (prefers-color-scheme: dark) {
|
|
45
50
|
#swatch {
|
|
46
|
-
--box-shadow-color: rgb(0 0 0 / .8);
|
|
51
|
+
--box-shadow-color: rgb(0 0 0 / 0.8);
|
|
47
52
|
}
|
|
48
|
-
}
|
|
53
|
+
}
|
|
@@ -19,10 +19,10 @@ const Self = class ColorInline extends ColorElement {
|
|
|
19
19
|
|
|
20
20
|
connectedCallback () {
|
|
21
21
|
super.connectedCallback?.();
|
|
22
|
-
Self.#mo.observe(this, {childList: true, subtree: true, characterData: true});
|
|
22
|
+
Self.#mo.observe(this, { childList: true, subtree: true, characterData: true });
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
propChangedCallback ({name, prop, detail: change}) {
|
|
25
|
+
propChangedCallback ({ name, prop, detail: change }) {
|
|
26
26
|
if (name === "color") {
|
|
27
27
|
let isValid = this.color !== null;
|
|
28
28
|
this._el.swatch.classList.toggle("invalid", !isValid);
|
|
@@ -79,7 +79,6 @@ const Self = class ColorInline extends ColorElement {
|
|
|
79
79
|
};
|
|
80
80
|
};
|
|
81
81
|
|
|
82
|
-
|
|
83
82
|
Self.define();
|
|
84
83
|
|
|
85
84
|
export default Self;
|