color-elements 0.0.3 → 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/partials/_nav-links.njk +1 -0
- package/_includes/plain.njk +5 -0
- package/_redirects +1 -1
- package/assets/css/style.css +4 -4
- package/debug.html +38 -439
- package/package.json +24 -8
- package/src/channel-picker/channel-picker.css +4 -4
- package/src/channel-picker/channel-picker.js +16 -12
- package/src/channel-slider/channel-slider.css +14 -7
- package/src/channel-slider/channel-slider.js +14 -6
- package/src/color-chart/README.md +36 -5
- package/src/color-chart/color-chart-global.css +19 -14
- 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 +309 -107
- package/src/color-inline/color-inline.css +21 -16
- package/src/color-inline/color-inline.js +3 -4
- package/src/color-inline/style.css +1 -1
- package/src/color-picker/color-picker.css +3 -3
- package/src/color-picker/color-picker.js +15 -8
- package/src/color-scale/README.md +42 -2
- package/src/color-scale/color-scale.css +8 -13
- package/src/color-scale/color-scale.js +14 -11
- package/src/color-slider/README.md +17 -3
- package/src/color-slider/color-slider.css +54 -33
- package/src/color-slider/color-slider.js +10 -8
- package/src/color-swatch/color-swatch.css +52 -34
- package/src/color-swatch/color-swatch.js +20 -10
- package/src/common/color-element.js +63 -51
- package/src/common/dom.js +4 -2
- package/src/common/util.js +66 -1
- package/src/gamut-badge/gamut-badge.css +33 -13
- package/src/gamut-badge/gamut-badge.js +10 -8
- package/src/space-picker/space-picker.css +3 -3
- package/src/space-picker/space-picker.js +29 -11
- package/src/src.json +1 -1
|
@@ -1,24 +1,28 @@
|
|
|
1
1
|
:host {
|
|
2
|
-
--_transparency-cell-size: var(--transparency-cell-size, .5em);
|
|
2
|
+
--_transparency-cell-size: var(--transparency-cell-size, 0.5em);
|
|
3
3
|
--_transparency-background: var(--transparency-background, transparent);
|
|
4
4
|
--_transparency-darkness: var(--transparency-darkness, 5%);
|
|
5
|
-
--_transparency-grid: var(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
--_transparency-grid: var(
|
|
6
|
+
--transparency-grid,
|
|
7
|
+
repeating-conic-gradient(
|
|
8
|
+
transparent 0 25%,
|
|
9
|
+
rgb(0 0 0 / var(--_transparency-darkness)) 0 50%
|
|
10
|
+
)
|
|
11
|
+
0 0 / calc(2 * var(--_transparency-cell-size)) calc(2 * var(--_transparency-cell-size))
|
|
12
|
+
content-box border-box var(--_transparency-background)
|
|
9
13
|
);
|
|
10
14
|
|
|
11
15
|
position: relative;
|
|
12
16
|
display: inline-flex;
|
|
13
|
-
gap: .3em;
|
|
14
|
-
width: fit-content;
|
|
15
|
-
margin: .3em;
|
|
16
|
-
border-radius: .2rem;
|
|
17
|
+
gap: 0.3em;
|
|
18
|
+
width: var(--color-swatch-width, fit-content);
|
|
19
|
+
margin: 0.3em;
|
|
20
|
+
border-radius: var(--color-swatch-radius, 0.2rem);
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
:host([size="large"]) {
|
|
20
24
|
flex-flow: column;
|
|
21
|
-
inline-size: 11em;
|
|
25
|
+
inline-size: min(11em, 100%);
|
|
22
26
|
min-block-size: 6em;
|
|
23
27
|
contain: inline-size;
|
|
24
28
|
container-name: color-swatch;
|
|
@@ -37,7 +41,7 @@ slot {
|
|
|
37
41
|
position: absolute;
|
|
38
42
|
top: 0;
|
|
39
43
|
right: 0;
|
|
40
|
-
margin: .5rem;
|
|
44
|
+
margin: 0.5rem;
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
&:not(:host([size="large"]) *) {
|
|
@@ -46,7 +50,7 @@ slot {
|
|
|
46
50
|
font-size: 50%;
|
|
47
51
|
top: 0;
|
|
48
52
|
right: 0;
|
|
49
|
-
margin: .2rem;
|
|
53
|
+
margin: 0.2rem;
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
&:is(.static *) {
|
|
@@ -54,8 +58,6 @@ slot {
|
|
|
54
58
|
}
|
|
55
59
|
}
|
|
56
60
|
|
|
57
|
-
|
|
58
|
-
|
|
59
61
|
&[style*="--gamut-level: 0"] {
|
|
60
62
|
display: none;
|
|
61
63
|
}
|
|
@@ -65,12 +67,12 @@ slot {
|
|
|
65
67
|
margin: 0;
|
|
66
68
|
display: inline-flex;
|
|
67
69
|
display: none;
|
|
68
|
-
gap: .5em;
|
|
70
|
+
gap: 0.5em;
|
|
69
71
|
|
|
70
72
|
&:is(:host([size="large"]) &) {
|
|
71
73
|
display: grid;
|
|
72
74
|
grid-template-columns: max-content auto;
|
|
73
|
-
gap: .1em .2em;
|
|
75
|
+
gap: 0.1em 0.2em;
|
|
74
76
|
font-size: max(9px, 80%);
|
|
75
77
|
justify-content: start;
|
|
76
78
|
|
|
@@ -81,7 +83,7 @@ slot {
|
|
|
81
83
|
|
|
82
84
|
.coord {
|
|
83
85
|
display: flex;
|
|
84
|
-
gap: .2em;
|
|
86
|
+
gap: 0.2em;
|
|
85
87
|
|
|
86
88
|
dd {
|
|
87
89
|
margin: 0;
|
|
@@ -96,9 +98,12 @@ slot {
|
|
|
96
98
|
flex-flow: inherit;
|
|
97
99
|
gap: inherit;
|
|
98
100
|
|
|
101
|
+
/* Prevent flex items from overflowing */
|
|
102
|
+
min-inline-size: 0;
|
|
103
|
+
|
|
99
104
|
&.static {
|
|
100
105
|
&:is(:host([size="large"]) *) {
|
|
101
|
-
background:
|
|
106
|
+
background: canvas;
|
|
102
107
|
}
|
|
103
108
|
}
|
|
104
109
|
|
|
@@ -111,8 +116,11 @@ slot {
|
|
|
111
116
|
}
|
|
112
117
|
|
|
113
118
|
@container style(--details-style: compact) {
|
|
114
|
-
--_border-color: var(
|
|
115
|
-
|
|
119
|
+
--_border-color: var(
|
|
120
|
+
--border-color,
|
|
121
|
+
color-mix(in oklab, buttonborder 20%, oklab(none none none / 0%))
|
|
122
|
+
);
|
|
123
|
+
--_pointer-height: var(--pointer-height, 0.5em);
|
|
116
124
|
--_transition-duration: var(--transition-duration, 400ms);
|
|
117
125
|
--_details-popup-width: var(--details-popup-width, max-content);
|
|
118
126
|
|
|
@@ -121,13 +129,13 @@ slot {
|
|
|
121
129
|
z-index: 2;
|
|
122
130
|
translate: -50% 0;
|
|
123
131
|
bottom: 100%;
|
|
124
|
-
margin-bottom: calc(var(--_pointer-height) * .8);
|
|
132
|
+
margin-bottom: calc(var(--_pointer-height) * 0.8);
|
|
125
133
|
width: var(--_details-popup-width);
|
|
126
134
|
background: canvas;
|
|
127
135
|
border: 1px solid var(--_border-color);
|
|
128
|
-
padding: .6em 1em;
|
|
129
|
-
border-radius: .2rem;
|
|
130
|
-
box-shadow: 0 .05em 1em
|
|
136
|
+
padding: 0.6em 1em;
|
|
137
|
+
border-radius: 0.2rem;
|
|
138
|
+
box-shadow: 0 0.05em 1em -0.7em black;
|
|
131
139
|
transition: var(--_transition-duration) allow-discrete;
|
|
132
140
|
transition-property: all, display;
|
|
133
141
|
transition-delay: 0s, var(--_transition-duration);
|
|
@@ -162,8 +170,14 @@ slot {
|
|
|
162
170
|
clip-path: polygon(0 0, 0 100%, 100% 100%);
|
|
163
171
|
}
|
|
164
172
|
|
|
165
|
-
|
|
166
|
-
|
|
173
|
+
/*
|
|
174
|
+
More straightforward selector:
|
|
175
|
+
&:not(:is(:host(:hover), :host(:focus-within), :host(:active), :host(:target), :host([open])) *)
|
|
176
|
+
doesn't work in Safari!
|
|
177
|
+
See https://bugs.webkit.org/show_bug.cgi?id=296577
|
|
178
|
+
*/
|
|
179
|
+
&:is(:host(:not(:hover):not(:focus-within):not(:active):not(:target):not([open])) *),
|
|
180
|
+
&:is(:host([open="false"]) *) {
|
|
167
181
|
display: none;
|
|
168
182
|
opacity: 0;
|
|
169
183
|
scale: 0;
|
|
@@ -173,7 +187,13 @@ slot {
|
|
|
173
187
|
|
|
174
188
|
[part="color"] {
|
|
175
189
|
display: flex;
|
|
176
|
-
gap: .2em;
|
|
190
|
+
gap: 0.2em;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
[part="label"] {
|
|
194
|
+
overflow: hidden;
|
|
195
|
+
white-space: nowrap;
|
|
196
|
+
text-overflow: ellipsis;
|
|
177
197
|
}
|
|
178
198
|
|
|
179
199
|
slot:not([name]) {
|
|
@@ -190,25 +210,23 @@ slot:not([name]) {
|
|
|
190
210
|
}
|
|
191
211
|
|
|
192
212
|
[part="color-wrapper"],
|
|
193
|
-
slot[name=swatch]::slotted(*),
|
|
213
|
+
slot[name="swatch"]::slotted(*),
|
|
194
214
|
#swatch {
|
|
195
215
|
border-radius: inherit;
|
|
196
216
|
}
|
|
197
217
|
|
|
198
|
-
slot[name=swatch]::slotted(*),
|
|
218
|
+
slot[name="swatch"]::slotted(*),
|
|
199
219
|
#swatch {
|
|
200
220
|
flex: 1;
|
|
201
221
|
display: flex;
|
|
202
222
|
flex-flow: column;
|
|
203
223
|
align-items: center;
|
|
204
224
|
justify-content: center;
|
|
205
|
-
padding: .5em;
|
|
225
|
+
padding: 0.5em;
|
|
206
226
|
display: flex;
|
|
207
227
|
flex-flow: column;
|
|
208
228
|
flex: 1;
|
|
209
|
-
background:
|
|
210
|
-
linear-gradient(var(--color) 0 100%),
|
|
211
|
-
var(--_transparency-grid);
|
|
229
|
+
background: linear-gradient(var(--color) 0 100%), var(--_transparency-grid);
|
|
212
230
|
|
|
213
231
|
&:is(:host([size="large"]) *) {
|
|
214
232
|
min-block-size: 3em;
|
|
@@ -223,7 +241,7 @@ slot[name=swatch]::slotted(*),
|
|
|
223
241
|
}
|
|
224
242
|
}
|
|
225
243
|
|
|
226
|
-
slot[name=swatch-content] {
|
|
244
|
+
slot[name="swatch-content"] {
|
|
227
245
|
/* See https://lea.verou.me/blog/2024/contrast-color/ */
|
|
228
246
|
--l: clamp(0, (var(--l-threshold, 0.7) / l - 1) * infinity, 1);
|
|
229
247
|
color: oklch(from var(--color) var(--l) 0 h);
|
|
@@ -7,7 +7,7 @@ const Self = class ColorSwatch extends ColorElement {
|
|
|
7
7
|
static tagName = "color-swatch";
|
|
8
8
|
static url = import.meta.url;
|
|
9
9
|
static dependencies = new Set(["gamut-badge"]);
|
|
10
|
-
static
|
|
10
|
+
static styles = "./color-swatch.css";
|
|
11
11
|
static shadowTemplate = `
|
|
12
12
|
<slot name="swatch">
|
|
13
13
|
<div id="swatch" part="swatch">
|
|
@@ -42,7 +42,7 @@ const Self = class ColorSwatch extends ColorElement {
|
|
|
42
42
|
|
|
43
43
|
#updateStatic () {
|
|
44
44
|
let previousInput = this._el.input;
|
|
45
|
-
let input = this._el.input = this.querySelector("input");
|
|
45
|
+
let input = (this._el.input = this.querySelector("input"));
|
|
46
46
|
|
|
47
47
|
this.static = !input;
|
|
48
48
|
|
|
@@ -50,7 +50,9 @@ const Self = class ColorSwatch extends ColorElement {
|
|
|
50
50
|
this._el.wrapper.classList.toggle("static", this.static);
|
|
51
51
|
|
|
52
52
|
if (input && input !== previousInput) {
|
|
53
|
-
importIncrementable ??= import("https://incrementable.verou.me/incrementable.mjs").then(
|
|
53
|
+
importIncrementable ??= import("https://incrementable.verou.me/incrementable.mjs").then(
|
|
54
|
+
m => m.default,
|
|
55
|
+
);
|
|
54
56
|
importIncrementable?.then(Incrementable => new Incrementable(input));
|
|
55
57
|
|
|
56
58
|
input.addEventListener("input", evt => {
|
|
@@ -65,10 +67,14 @@ const Self = class ColorSwatch extends ColorElement {
|
|
|
65
67
|
|
|
66
68
|
get swatchTextContent () {
|
|
67
69
|
// Children that are not assigned to another slot
|
|
68
|
-
return [...this.childNodes]
|
|
70
|
+
return [...this.childNodes]
|
|
71
|
+
.filter(n => !n.slot)
|
|
72
|
+
.map(n => n.textContent)
|
|
73
|
+
.join("")
|
|
74
|
+
.trim();
|
|
69
75
|
}
|
|
70
76
|
|
|
71
|
-
propChangedCallback ({name, prop, detail: change}) {
|
|
77
|
+
propChangedCallback ({ name, prop, detail: change }) {
|
|
72
78
|
let input = this._el.input;
|
|
73
79
|
|
|
74
80
|
if (name === "gamuts") {
|
|
@@ -91,9 +97,11 @@ const Self = class ColorSwatch extends ColorElement {
|
|
|
91
97
|
this._el.gamutIndicator.addEventListener("gamutchange", evt => {
|
|
92
98
|
let gamut = this._el.gamutIndicator.gamut;
|
|
93
99
|
this.setAttribute("gamut", gamut);
|
|
94
|
-
this.dispatchEvent(
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
this.dispatchEvent(
|
|
101
|
+
new CustomEvent("gamutchange", {
|
|
102
|
+
detail: gamut,
|
|
103
|
+
}),
|
|
104
|
+
);
|
|
97
105
|
});
|
|
98
106
|
}
|
|
99
107
|
else {
|
|
@@ -111,9 +119,11 @@ const Self = class ColorSwatch extends ColorElement {
|
|
|
111
119
|
if (name === "label") {
|
|
112
120
|
if (this.label.length && this.label !== this.swatchTextContent) {
|
|
113
121
|
this._el.label.textContent = this.label;
|
|
122
|
+
this._el.label.title = this.label;
|
|
114
123
|
}
|
|
115
124
|
else {
|
|
116
125
|
this._el.label.textContent = "";
|
|
126
|
+
this._el.label.title = "";
|
|
117
127
|
}
|
|
118
128
|
}
|
|
119
129
|
|
|
@@ -135,7 +145,7 @@ const Self = class ColorSwatch extends ColorElement {
|
|
|
135
145
|
return;
|
|
136
146
|
}
|
|
137
147
|
|
|
138
|
-
this._el.info ??= Object.assign(document.createElement("dl"), {part: "info"});
|
|
148
|
+
this._el.info ??= Object.assign(document.createElement("dl"), { part: "info" });
|
|
139
149
|
if (!this._el.info.parentNode) {
|
|
140
150
|
this._el.colorWrapper.after(this._el.info);
|
|
141
151
|
}
|
|
@@ -151,7 +161,7 @@ const Self = class ColorSwatch extends ColorElement {
|
|
|
151
161
|
|
|
152
162
|
value = typeof value === "number" ? Number(value.toPrecision(4)) : value;
|
|
153
163
|
|
|
154
|
-
info.push(`<div class="coord"><dt>${
|
|
164
|
+
info.push(`<div class="coord"><dt>${label}</dt><dd>${value}</dd></div>`);
|
|
155
165
|
}
|
|
156
166
|
|
|
157
167
|
this._el.info.innerHTML = info.join("\n");
|
|
@@ -1,85 +1,97 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import Color from "colorjs.io";
|
|
2
|
+
import NudeElement from "nude-element";
|
|
3
|
+
import { states } from "nude-element/plugins";
|
|
4
|
+
import { defer, wait, dynamicAll, noOpTemplateTag as css } from "./util.js";
|
|
5
|
+
|
|
6
|
+
const baseGlobalStyles = css`
|
|
7
|
+
@keyframes fade-in {
|
|
8
|
+
from {
|
|
9
|
+
opacity: 0;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
:state(color-element) {
|
|
14
|
+
&:state(loading) {
|
|
15
|
+
content-visibility: hidden;
|
|
16
|
+
opacity: 0;
|
|
17
|
+
|
|
18
|
+
&,
|
|
19
|
+
* {
|
|
20
|
+
transition-property: opacity !important;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&:not(:state(loading)) {
|
|
25
|
+
animation: fade-in 300ms both;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
`;
|
|
3
29
|
|
|
4
30
|
const Self = class ColorElement extends NudeElement {
|
|
31
|
+
static url = import.meta.url;
|
|
5
32
|
// TODO make lazy
|
|
6
33
|
static Color;
|
|
7
34
|
static all = {};
|
|
8
35
|
static dependencies = new Set();
|
|
9
36
|
|
|
37
|
+
static globalStyles = [{ css: baseGlobalStyles }];
|
|
38
|
+
static plugins = [states];
|
|
39
|
+
|
|
10
40
|
constructor () {
|
|
11
41
|
super();
|
|
12
42
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
43
|
+
let Self = this.constructor;
|
|
44
|
+
|
|
45
|
+
if (Self.shadowTemplate !== undefined) {
|
|
46
|
+
this.attachShadow({ mode: "open" });
|
|
47
|
+
this.shadowRoot.innerHTML = Self.shadowTemplate;
|
|
16
48
|
}
|
|
49
|
+
|
|
50
|
+
this.toggleState("color-element", true);
|
|
51
|
+
this.toggleState("loading", true);
|
|
52
|
+
Self.whenReady.then(() => {
|
|
53
|
+
this.toggleState("loading");
|
|
54
|
+
});
|
|
17
55
|
}
|
|
18
56
|
|
|
57
|
+
static ready = [defer()];
|
|
58
|
+
static whenReady = dynamicAll(this.ready);
|
|
59
|
+
|
|
19
60
|
static async define () {
|
|
61
|
+
// Overwrite static properties, otherwise they will be shared across subclasses
|
|
62
|
+
this.ready = [defer()];
|
|
63
|
+
this.whenReady = dynamicAll(this.ready);
|
|
64
|
+
|
|
65
|
+
if (!Object.hasOwn(this, "dependencies")) {
|
|
66
|
+
this.dependencies = new Set();
|
|
67
|
+
}
|
|
68
|
+
|
|
20
69
|
Self.all[this.tagName] = this;
|
|
21
70
|
let colorTags = Object.keys(Self.all);
|
|
22
71
|
|
|
23
72
|
if (this.shadowTemplate) {
|
|
24
73
|
// TODO find dependencies
|
|
25
|
-
let colorTagRegex = RegExp(`(?<=</)(${
|
|
74
|
+
let colorTagRegex = RegExp(`(?<=</)(${colorTags.join("|")})(?=>)`, "g");
|
|
26
75
|
(this.shadowTemplate.match(colorTagRegex) ?? []).forEach(tag => {
|
|
27
76
|
this.dependencies ??= new Set();
|
|
28
77
|
this.dependencies.add(tag);
|
|
29
78
|
});
|
|
30
|
-
|
|
31
|
-
if (this.shadowStyle) {
|
|
32
|
-
let url = this.shadowStyle;
|
|
33
|
-
url = url === true ? `./${this.tagName}.css` : url;
|
|
34
|
-
url = new URL(url, this.url);
|
|
35
|
-
this.shadowTemplate = `<style>@import url("${ url }")</style>` + "\n" + this.shadowTemplate;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Hide elements before they are defined
|
|
40
|
-
let style = document.getElementById("color-element-styles")
|
|
41
|
-
?? Object.assign(document.createElement("style"), {id: "color-element-styles"});
|
|
42
|
-
style.textContent = `:is(${ colorTags.join(", ") }):not(:defined) {display: none}`;
|
|
43
|
-
if (!style.parentNode) {
|
|
44
|
-
document.head.append(style);
|
|
45
79
|
}
|
|
46
80
|
|
|
47
81
|
if (this.dependencies.size > 0) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// Give other code a chance to overwrite Self.Color
|
|
52
|
-
await wait();
|
|
82
|
+
let whenDefined = [...this.dependencies].map(tag =>
|
|
83
|
+
customElements.whenDefined(tag).then(Class => Class.whenReady));
|
|
84
|
+
this.ready.push(...whenDefined);
|
|
53
85
|
}
|
|
54
86
|
|
|
55
|
-
|
|
56
|
-
|
|
87
|
+
// Give other code a chance to overwrite Self.Color
|
|
88
|
+
await wait();
|
|
57
89
|
|
|
58
|
-
|
|
59
|
-
// Is already loaded? (e.g. via an import map, or if we're in Node)
|
|
60
|
-
import.meta.resolve("colorjs.io");
|
|
61
|
-
specifier = "colorjs.io";
|
|
62
|
-
}
|
|
63
|
-
catch (e) {
|
|
64
|
-
// specifier = "../../node_modules/colorjs.io/dist/color.js";
|
|
65
|
-
specifier = "https://colorjs.io/dist/color.js";
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
Self.Color = import(specifier).then(module => module.default);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// We can't just use top level await, see https://bugs.webkit.org/show_bug.cgi?id=242740
|
|
72
|
-
if (getType(Self.Color) === "Promise") {
|
|
73
|
-
let ColorPending = Self.Color;
|
|
74
|
-
let Color = await ColorPending;
|
|
75
|
-
|
|
76
|
-
if (Self.Color === ColorPending) {
|
|
77
|
-
// Hasn't changed
|
|
78
|
-
Self.Color = Color;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
90
|
+
Self.Color ??= Color;
|
|
81
91
|
|
|
82
92
|
customElements.define(this.tagName, this);
|
|
93
|
+
|
|
94
|
+
this.ready[0].resolve();
|
|
83
95
|
}
|
|
84
96
|
};
|
|
85
97
|
|
package/src/common/dom.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export function named (host, attributes = ["id", "part"]) {
|
|
2
2
|
let ret = {};
|
|
3
|
-
let selector = attributes.map(attr => `[${
|
|
3
|
+
let selector = attributes.map(attr => `[${attr}]`).join(", ");
|
|
4
4
|
|
|
5
5
|
for (let el of host.shadowRoot.querySelectorAll(selector)) {
|
|
6
6
|
// Get the value of the first attribute in attributes that has a value
|
|
@@ -27,7 +27,9 @@ export function slots (host) {
|
|
|
27
27
|
|
|
28
28
|
export function toSlots ({
|
|
29
29
|
slots = this._slots,
|
|
30
|
-
slotElements = slots
|
|
30
|
+
slotElements = slots
|
|
31
|
+
? Object.values(slots)
|
|
32
|
+
: Array.from(this.shadowRoot.querySelectorAll("slot")),
|
|
31
33
|
}) {
|
|
32
34
|
let children = this.childNodes;
|
|
33
35
|
let assignments = new WeakMap();
|
package/src/common/util.js
CHANGED
|
@@ -6,13 +6,68 @@ export async function wait (ms) {
|
|
|
6
6
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
export function defer (executor) {
|
|
10
|
+
let res, rej;
|
|
11
|
+
|
|
12
|
+
let promise = new Promise((resolve, reject) => {
|
|
13
|
+
res = resolve;
|
|
14
|
+
rej = reject;
|
|
15
|
+
|
|
16
|
+
executor?.(res, rej);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
promise.resolve = res;
|
|
20
|
+
promise.reject = rej;
|
|
21
|
+
|
|
22
|
+
return promise;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Wait for all promises to resolve. Supports dynamically adding promises to the list after the initial call.
|
|
27
|
+
* @param {Promise[] | Set<Promise>} promises
|
|
28
|
+
* @returns {Promise}
|
|
29
|
+
*/
|
|
30
|
+
export async function dynamicAll (promises) {
|
|
31
|
+
let all = new Set([...promises]);
|
|
32
|
+
let unresolved = new Set();
|
|
33
|
+
|
|
34
|
+
for (let promise of promises) {
|
|
35
|
+
if (promise?.then) {
|
|
36
|
+
unresolved.add(promise);
|
|
37
|
+
promise.then(() => {
|
|
38
|
+
// Remove the promise from the list
|
|
39
|
+
unresolved.delete(promise);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return Promise.all(unresolved).then(resolved => {
|
|
45
|
+
// Check if the array has new items
|
|
46
|
+
for (let promise of promises) {
|
|
47
|
+
if (!all.has(promise)) {
|
|
48
|
+
all.add(promise);
|
|
49
|
+
|
|
50
|
+
if (promise?.then) {
|
|
51
|
+
unresolved.add(promise);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (unresolved.size > 0) {
|
|
57
|
+
return dynamicAll(unresolved).then(r => [...resolved, ...r]);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return resolved;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
9
64
|
/**
|
|
10
65
|
* Compute the ideal step for a range, to be used as a default in spinners and sliders
|
|
11
66
|
* @param {number} min
|
|
12
67
|
* @param {number} max
|
|
13
68
|
* @param {options} options
|
|
14
69
|
*/
|
|
15
|
-
export function getStep (min, max, {minSteps = 100, maxStep = 1} = {}) {
|
|
70
|
+
export function getStep (min, max, { minSteps = 100, maxStep = 1 } = {}) {
|
|
16
71
|
let range = Math.abs(max - min);
|
|
17
72
|
let step = range / minSteps;
|
|
18
73
|
|
|
@@ -53,3 +108,13 @@ export function getType (value) {
|
|
|
53
108
|
|
|
54
109
|
return Object.prototype.toString.call(value).slice(8, -1);
|
|
55
110
|
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Template tag that does nothing. Useful for importing under different names (e.g. `css`) for syntax highlighting.
|
|
114
|
+
* @param {string[]} strings
|
|
115
|
+
* @param {...any} values
|
|
116
|
+
* @returns {string}
|
|
117
|
+
*/
|
|
118
|
+
export function noOpTemplateTag (strings, ...values) {
|
|
119
|
+
return strings.reduce((acc, string, i) => acc + string + (values[i] ?? ""), "");
|
|
120
|
+
}
|
|
@@ -7,27 +7,47 @@
|
|
|
7
7
|
--_color-invalid: var(--color-invalid, hsl(220 10% 60%));
|
|
8
8
|
|
|
9
9
|
/* Low-level scales, i.e. between pairs of our core colors */
|
|
10
|
-
--color-scale-1: color-mix(
|
|
11
|
-
|
|
10
|
+
--color-scale-1: color-mix(
|
|
11
|
+
in oklch,
|
|
12
|
+
var(--_color-green),
|
|
13
|
+
var(--_color-yellow) var(--progress-1)
|
|
14
|
+
);
|
|
15
|
+
--color-scale-2: color-mix(
|
|
16
|
+
in oklch,
|
|
17
|
+
var(--_color-yellow),
|
|
18
|
+
var(--_color-orange) var(--progress-2)
|
|
19
|
+
);
|
|
12
20
|
--color-scale-3: color-mix(in oklch, var(--_color-orange), var(--_color-red) var(--progress-3));
|
|
13
21
|
|
|
14
22
|
/* Recursive scales: their only purpose is to select one of the low-level scales, and will only ever have 0%/100% positions
|
|
15
23
|
For N colors there are N-2 + N-3 + ... + 1 = (N-1)(N-2) / 2 of these
|
|
16
24
|
*/
|
|
17
|
-
--color-scale-12: color-mix(
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
--color-scale-12: color-mix(
|
|
26
|
+
in oklch,
|
|
27
|
+
var(--color-scale-1),
|
|
28
|
+
var(--color-scale-2) var(--progress-12)
|
|
29
|
+
);
|
|
30
|
+
--color-scale-23: color-mix(
|
|
31
|
+
in oklch,
|
|
32
|
+
var(--color-scale-2),
|
|
33
|
+
var(--color-scale-3) var(--progress-23)
|
|
34
|
+
);
|
|
35
|
+
--color-scale-123: color-mix(
|
|
36
|
+
in oklch,
|
|
37
|
+
var(--color-scale-12),
|
|
38
|
+
var(--color-scale-23) var(--progress-123)
|
|
39
|
+
);
|
|
20
40
|
|
|
21
41
|
--gamut-progress: calc(var(--gamut-level) / (var(--gamut-count) - 1));
|
|
22
42
|
--progress-ext: calc(var(--gamut-progress) * 300%);
|
|
23
43
|
|
|
24
|
-
--progress-123: clamp(0%, (var(--progress-ext) - 150%)
|
|
25
|
-
--progress-12:
|
|
26
|
-
--progress-23:
|
|
44
|
+
--progress-123: clamp(0%, (var(--progress-ext) - 150%) * infinity, 100%);
|
|
45
|
+
--progress-12: clamp(0%, (var(--progress-ext) - 150% + 75%) * infinity, 100%);
|
|
46
|
+
--progress-23: clamp(0%, (var(--progress-ext) - 150% - 75%) * infinity, 100%);
|
|
27
47
|
|
|
28
|
-
--progress-1:
|
|
29
|
-
--progress-2:
|
|
30
|
-
--progress-3:
|
|
48
|
+
--progress-1: clamp(0%, var(--progress-ext), 100%);
|
|
49
|
+
--progress-2: calc(clamp(100%, var(--progress-ext), 200%) - 100%);
|
|
50
|
+
--progress-3: calc(clamp(200%, var(--progress-ext), 300%) - 200%);
|
|
31
51
|
|
|
32
52
|
--color-level-infinity: var(--_color-red-dark);
|
|
33
53
|
--color: var(--color-scale-123, var(--_color-invalid));
|
|
@@ -35,11 +55,11 @@
|
|
|
35
55
|
display: inline-flex;
|
|
36
56
|
align-items: center;
|
|
37
57
|
justify-content: center;
|
|
38
|
-
border-radius: .2em;
|
|
58
|
+
border-radius: 0.2em;
|
|
39
59
|
color: white;
|
|
40
60
|
background-color: var(--color);
|
|
41
61
|
font-weight: bold;
|
|
42
|
-
padding-inline: .4em;
|
|
62
|
+
padding-inline: 0.4em;
|
|
43
63
|
line-height: 1.4;
|
|
44
64
|
|
|
45
65
|
/* See https://lea.verou.me/blog/2024/contrast-color/ */
|
|
@@ -3,7 +3,7 @@ import ColorElement from "../common/color-element.js";
|
|
|
3
3
|
const Self = class GamutBadge extends ColorElement {
|
|
4
4
|
static tagName = "gamut-badge";
|
|
5
5
|
static url = import.meta.url;
|
|
6
|
-
static
|
|
6
|
+
static styles = "./gamut-badge.css";
|
|
7
7
|
static shadowTemplate = `
|
|
8
8
|
<slot>
|
|
9
9
|
<span id="label" part="label"></span>
|
|
@@ -23,7 +23,7 @@ const Self = class GamutBadge extends ColorElement {
|
|
|
23
23
|
return this.gamutInfo?.label ?? "";
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
propChangedCallback ({name, prop, detail: change}) {
|
|
26
|
+
propChangedCallback ({ name, prop, detail: change }) {
|
|
27
27
|
if (name === "gamuts") {
|
|
28
28
|
this.style.setProperty("--gamut-count", this.gamuts.length - 1);
|
|
29
29
|
}
|
|
@@ -31,8 +31,8 @@ const Self = class GamutBadge extends ColorElement {
|
|
|
31
31
|
if (name === "gamutInfo") {
|
|
32
32
|
if (this.gamutInfo) {
|
|
33
33
|
this.style.setProperty("--gamut-level", this.gamutInfo.level);
|
|
34
|
-
this.style.setProperty("--gamut-label", `"${
|
|
35
|
-
this.style.setProperty("--gamut-id", `"${
|
|
34
|
+
this.style.setProperty("--gamut-label", `"${this.gamutInfo.label}"`);
|
|
35
|
+
this.style.setProperty("--gamut-id", `"${this.gamutInfo.id}"`);
|
|
36
36
|
}
|
|
37
37
|
else {
|
|
38
38
|
this.style.removeProperty("--gamut-level");
|
|
@@ -61,7 +61,7 @@ const Self = class GamutBadge extends ColorElement {
|
|
|
61
61
|
}
|
|
62
62
|
else if (!Array.isArray(gamuts) && typeof gamuts === "object") {
|
|
63
63
|
// Object
|
|
64
|
-
return Object.entries(gamuts).map(([id, label]) => ({id, label}));
|
|
64
|
+
return Object.entries(gamuts).map(([id, label]) => ({ id, label }));
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
let ret = gamuts.map((gamut, level) => {
|
|
@@ -73,7 +73,7 @@ const Self = class GamutBadge extends ColorElement {
|
|
|
73
73
|
gamut = gamut.trim().split(/\s*:\s*/);
|
|
74
74
|
let id = gamut[0];
|
|
75
75
|
let label = gamut[1] ?? Self.Color.spaces[id]?.name ?? id;
|
|
76
|
-
return {id, label, level};
|
|
76
|
+
return { id, label, level };
|
|
77
77
|
});
|
|
78
78
|
|
|
79
79
|
if (!ret.find(gamut => gamut.id === "none")) {
|
|
@@ -89,7 +89,7 @@ const Self = class GamutBadge extends ColorElement {
|
|
|
89
89
|
return ret;
|
|
90
90
|
},
|
|
91
91
|
stringify (gamuts) {
|
|
92
|
-
return gamuts.map(({id, label}) => `${
|
|
92
|
+
return gamuts.map(({ id, label }) => `${id}: ${label}`).join(", ");
|
|
93
93
|
},
|
|
94
94
|
},
|
|
95
95
|
gamutInfo: {
|
|
@@ -98,7 +98,9 @@ const Self = class GamutBadge extends ColorElement {
|
|
|
98
98
|
return null;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
return this.gamuts?.find(
|
|
101
|
+
return this.gamuts?.find(
|
|
102
|
+
gamut => gamut.id === "none" || this.color?.inGamut(gamut.id),
|
|
103
|
+
);
|
|
102
104
|
},
|
|
103
105
|
},
|
|
104
106
|
gamut: {
|