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.
Files changed (44) hide show
  1. package/.claude/settings.local.json +30 -0
  2. package/.editorconfig +8 -0
  3. package/.prettierrc +17 -0
  4. package/.vscode/settings.json +7 -0
  5. package/README.md +27 -18
  6. package/_build/copy-config.js +15 -14
  7. package/_build/copy-config.json +4 -9
  8. package/_build/eleventy.js +8 -8
  9. package/_includes/component.njk +9 -14
  10. package/_includes/plain.njk +5 -0
  11. package/_redirects +1 -1
  12. package/assets/css/style.css +4 -4
  13. package/debug.html +46 -0
  14. package/package.json +19 -8
  15. package/src/channel-picker/channel-picker.css +4 -4
  16. package/src/channel-picker/channel-picker.js +15 -11
  17. package/src/channel-slider/channel-slider.css +14 -7
  18. package/src/channel-slider/channel-slider.js +13 -5
  19. package/src/color-chart/README.md +36 -5
  20. package/src/color-chart/color-chart-global.css +13 -12
  21. package/src/color-chart/color-chart-shadow.css +123 -0
  22. package/src/color-chart/color-chart.css +2 -112
  23. package/src/color-chart/color-chart.js +307 -103
  24. package/src/color-inline/color-inline.css +21 -16
  25. package/src/color-inline/color-inline.js +2 -3
  26. package/src/color-inline/style.css +1 -1
  27. package/src/color-picker/color-picker.css +3 -3
  28. package/src/color-picker/color-picker.js +14 -7
  29. package/src/color-scale/README.md +42 -2
  30. package/src/color-scale/color-scale.css +1 -1
  31. package/src/color-scale/color-scale.js +12 -9
  32. package/src/color-slider/README.md +17 -3
  33. package/src/color-slider/color-slider.css +54 -33
  34. package/src/color-slider/color-slider.js +9 -7
  35. package/src/color-swatch/color-swatch.css +41 -32
  36. package/src/color-swatch/color-swatch.js +17 -9
  37. package/src/common/color-element.js +34 -76
  38. package/src/common/dom.js +4 -2
  39. package/src/common/util.js +1 -1
  40. package/src/gamut-badge/gamut-badge.css +33 -13
  41. package/src/gamut-badge/gamut-badge.js +9 -7
  42. package/src/space-picker/space-picker.css +3 -3
  43. package/src/space-picker/space-picker.js +28 -10
  44. 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: this.shadowRoot.querySelector("slot:not([name])"),
33
+ slot: this.shadowRoot.querySelector("slot:not([name])"),
34
34
  channel_picker: this.shadowRoot.getElementById("channel_picker"),
35
- chart: this.shadowRoot.getElementById("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 (this._el.channel_picker === source || this._slots.color_channel.assignedElements().includes(source)) {
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
- let colorScales = this.querySelectorAll("color-scale");
103
+ this.renderScales(evt);
104
+ this.renderAxis("x");
105
+ this.renderAxis("y");
106
+ }
73
107
 
74
- if (colorScales.length === 0) {
75
- return;
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 minX = Infinity, maxX = -Infinity;
79
- let minY = Infinity, maxY = -Infinity;
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
- for (let colorScale of colorScales) {
82
- let scale = this.series.get(colorScale);
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
- if (!scale || !evt || evt.target === colorScale || evt.target.nodeName !== "COLOR-SCALE") {
85
- scale = this.renderScale(colorScale);
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
- if (!scale) {
89
- continue;
90
- }
157
+ /** (Re)render all scales */
158
+ renderScales (evt) {
159
+ let colorScales = this.querySelectorAll("color-scale");
91
160
 
92
- minX = Math.min(scale.x.min, minX);
93
- maxX = Math.max(scale.x.max, maxX);
94
- minY = Math.min(scale.y.min, minY);
95
- maxY = Math.max(scale.y.max, maxY);
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
- if (isFinite(minX) && isFinite(maxX)) {
99
- let xAxis = getAxis(minX, maxX, 10);
100
- this._el.chart.style.setProperty("--min-x", xAxis.min);
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
- minY = this.yMin === "auto" || this.yMinAsNumber === undefined || Number.isNaN(this.yMinAsNumber) ? minY : this.yMinAsNumber;
107
- maxY = this.yMax === "auto" || this.yMaxAsNumber === undefined || Number.isNaN(this.yMaxAsNumber) ? maxY : this.yMaxAsNumber;
172
+ for (let colorScale of colorScales) {
173
+ let scale = this.series.get(colorScale);
108
174
 
109
- if (isFinite(minY) && isFinite(maxY)) {
110
- let yAxis = getAxis(minY, maxY, 10);
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
- this._el.chart.style.setProperty("--min-y", yAxis.min);
113
- this._el.chart.style.setProperty("--max-y", yAxis.max);
114
- this._el.chart.style.setProperty("--steps-y", yAxis.steps);
115
- this._el.yTicks.innerHTML = Array(yAxis.steps).fill().map((_, i) => "<div part='y tick'>" + +(yAxis.min + i * yAxis.step).toPrecision(15) + "</div>").reverse().join("\n");
116
- this._el.yLabel.textContent = this.space.name + " " + this.yResolved.name;
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
- let yAll = ret.colors.map(({color}) => color.to(this.space).get(this.y));
137
- let yMin = this.yMin === "auto" || this.yMinAsNumber === undefined || Number.isNaN(this.yMinAsNumber) ? -Infinity : this.yMinAsNumber;
138
- let yMax = this.yMax === "auto" || this.yMaxAsNumber === undefined || Number.isNaN(this.yMaxAsNumber) ? Infinity : this.yMaxAsNumber;
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
- if (this.yResolved.type === "angle") {
141
- // First, normalize
142
- yAll = normalizeAngles(yAll);
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
- // It's not always possible to use the last number in the color label as the X-coord;
152
- // for example, the number “9” can't be interpreted as the X-coord in the “#90caf9” label.
153
- // It might cause bugs with color order (see https://github.com/color-js/elements/issues/103).
154
- // We expect the valid X-coord to be the only number in the color label (e.g., 50)
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
- x = index;
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 = yAll[index];
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
- let outOfRange = (isFinite(yMin) && y < yMin) || (isFinite(yMax) && y > yMax);
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 (HTMLElement.prototype.hasOwnProperty("popover") && !swatch._el.wrapper.hasAttribute("popover")) {
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("--next-color", swatch.style.getPropertyValue("--color"));
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 => prevColor.style.removeProperty(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
- // `this.yMinAsNumber` will become `undefined` (i.e., get a new value), and the chart will be re-rendered
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
- let minY = this._el.chart.style.getPropertyValue("--min-y");
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
- // `this.yMaxAsNumber` will become `undefined` (i.e., get a new value), and the chart will be re-rendered
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
- let maxY = this._el.chart.style.getPropertyValue("--max-y");
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
- let start = Math.floor(min / step) * step;
404
- let end = Math.ceil(max / step) * step;
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 = angles.toSorted((a, b) => a - b).slice(angles.length / 4, -angles.length / 4).reduce((a, b) => a + b, 0) / angles.length;
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(--transparency-grid,
13
- repeating-conic-gradient(transparent 0 25%, rgb(0 0 0 / var(--_transparency-darkness)) 0 50%)
14
- 0 0 / calc(2 * var(--_transparency-cell-size)) calc(2 * var(--_transparency-cell-size))
15
- content-box border-box var(--_transparency-background)
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) var(--box-shadow-blur) var(--box-shadow-color);
31
- border-radius: clamp(1px, .1em, 10px);
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;