twiddle-panel 0.7.1 → 0.8.0
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/README.md +15 -3
- package/bin/init.js +118 -0
- package/dist/twiddle.js +60 -45
- package/dist/twiddle.mjs +1122 -1037
- package/package.json +5 -1
package/dist/twiddle.mjs
CHANGED
|
@@ -1,148 +1,148 @@
|
|
|
1
|
-
const
|
|
2
|
-
let M =
|
|
3
|
-
function
|
|
4
|
-
const
|
|
5
|
-
if (!
|
|
1
|
+
const gt = /* @__PURE__ */ new Set();
|
|
2
|
+
let M = sa(), Q = [], ke = [], qe = null, an = 0;
|
|
3
|
+
function sa() {
|
|
4
|
+
const e = new URLSearchParams(location.search).get("twd");
|
|
5
|
+
if (!e) return {};
|
|
6
6
|
try {
|
|
7
|
-
return JSON.parse(
|
|
7
|
+
return JSON.parse(e);
|
|
8
8
|
} catch {
|
|
9
9
|
return {};
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
-
function
|
|
13
|
-
const
|
|
14
|
-
Object.keys(M).length ?
|
|
12
|
+
function ia() {
|
|
13
|
+
const e = new URL(location.href);
|
|
14
|
+
Object.keys(M).length ? e.searchParams.set("twd", JSON.stringify(M)) : e.searchParams.delete("twd"), history.replaceState(history.state, "", e);
|
|
15
15
|
}
|
|
16
|
-
function
|
|
17
|
-
|
|
16
|
+
function Et() {
|
|
17
|
+
ia(), gt.forEach((e) => e(M));
|
|
18
18
|
}
|
|
19
|
-
function
|
|
20
|
-
if (JSON.stringify(
|
|
19
|
+
function pe(e, t) {
|
|
20
|
+
if (JSON.stringify(e) === JSON.stringify(M)) return;
|
|
21
21
|
const n = Date.now();
|
|
22
|
-
|
|
22
|
+
t && t === qe && n - an < 400 || (Q.push(M), Q.length > 100 && Q.shift()), ke = [], qe = t, an = n, M = e, Et();
|
|
23
23
|
}
|
|
24
|
-
function
|
|
25
|
-
const n = { ...
|
|
24
|
+
function Ve(e, t) {
|
|
25
|
+
const n = { ...e, ...t };
|
|
26
26
|
return Object.keys(n).forEach((a) => {
|
|
27
27
|
n[a] == null && delete n[a];
|
|
28
28
|
}), Object.keys(n).length ? n : null;
|
|
29
29
|
}
|
|
30
|
-
function
|
|
31
|
-
const a = { ...
|
|
32
|
-
return n == null ? delete a[
|
|
30
|
+
function B(e, t, n) {
|
|
31
|
+
const a = { ...e };
|
|
32
|
+
return n == null ? delete a[t] : a[t] = n, a;
|
|
33
33
|
}
|
|
34
|
-
function
|
|
34
|
+
function mt() {
|
|
35
35
|
return JSON.parse(JSON.stringify(M));
|
|
36
36
|
}
|
|
37
|
-
function
|
|
38
|
-
|
|
37
|
+
function la(e, t) {
|
|
38
|
+
pe(B(M, e, Ve(M[e], t)), `${e}:${Object.keys(t)}`);
|
|
39
39
|
}
|
|
40
|
-
function
|
|
41
|
-
const n = Object.entries(
|
|
42
|
-
|
|
40
|
+
function da(e, t = `multi:${Object.keys(e)}`) {
|
|
41
|
+
const n = Object.entries(e).reduce((a, [o, s]) => B(a, o, Ve(a[o], s)), M);
|
|
42
|
+
pe(n, t);
|
|
43
43
|
}
|
|
44
|
-
function
|
|
45
|
-
const a =
|
|
46
|
-
|
|
44
|
+
function Mn(e, t, n) {
|
|
45
|
+
const a = B(M.scopes?.[e], t, Ve(M.scopes?.[e]?.[t], n)), o = B(M.scopes, e, Object.keys(a).length ? a : null);
|
|
46
|
+
pe(B(M, "scopes", Object.keys(o).length ? o : null), `${e}|${t}:${Object.keys(n)}`);
|
|
47
47
|
}
|
|
48
|
-
function
|
|
49
|
-
if (
|
|
50
|
-
Object.keys(M.scopes?.[
|
|
48
|
+
function ca(e, t) {
|
|
49
|
+
if (t) return Mn(e, t, Object.fromEntries(
|
|
50
|
+
Object.keys(M.scopes?.[e]?.[t] || {}).map((a) => [a, null])
|
|
51
51
|
));
|
|
52
|
-
const n =
|
|
53
|
-
|
|
52
|
+
const n = B(M.scopes, e, null);
|
|
53
|
+
pe(B(M, "scopes", Object.keys(n).length ? n : null));
|
|
54
54
|
}
|
|
55
|
-
function
|
|
56
|
-
const n = M.scopes?.[
|
|
57
|
-
if (!n ||
|
|
58
|
-
let a = { ...M.scopes[
|
|
55
|
+
function pa(e, t) {
|
|
56
|
+
const n = M.scopes?.[e];
|
|
57
|
+
if (!n || e === t) return;
|
|
58
|
+
let a = { ...M.scopes[t] };
|
|
59
59
|
Object.entries(n).forEach(([s, r]) => {
|
|
60
|
-
a =
|
|
60
|
+
a = B(a, s, Ve(a[s], r));
|
|
61
61
|
});
|
|
62
|
-
const o =
|
|
63
|
-
|
|
62
|
+
const o = B(B(M.scopes, e, null), t, a);
|
|
63
|
+
pe(B(M, "scopes", o));
|
|
64
64
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
65
|
+
function Tn() {
|
|
66
|
+
pe({});
|
|
67
67
|
}
|
|
68
|
-
function
|
|
69
|
-
|
|
68
|
+
function An() {
|
|
69
|
+
Q.length && (ke.push(M), M = Q.pop(), qe = null, Et());
|
|
70
70
|
}
|
|
71
|
-
function
|
|
72
|
-
|
|
71
|
+
function Ln() {
|
|
72
|
+
ke.length && (Q.push(M), M = ke.pop(), qe = null, Et());
|
|
73
73
|
}
|
|
74
|
-
const
|
|
75
|
-
function
|
|
76
|
-
return
|
|
74
|
+
const ua = () => Q.length > 0, ha = () => ke.length > 0;
|
|
75
|
+
function Pn(e) {
|
|
76
|
+
return gt.add(e), () => gt.delete(e);
|
|
77
77
|
}
|
|
78
|
-
const
|
|
78
|
+
const fa = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
79
79
|
__proto__: null,
|
|
80
|
-
canRedo:
|
|
81
|
-
canUndo:
|
|
82
|
-
getState:
|
|
83
|
-
moveScope:
|
|
84
|
-
onChange:
|
|
85
|
-
redo:
|
|
86
|
-
removeScope:
|
|
87
|
-
resetState:
|
|
88
|
-
setScope:
|
|
89
|
-
setSection:
|
|
90
|
-
setSections:
|
|
91
|
-
undo:
|
|
92
|
-
}, Symbol.toStringTag, { value: "Module" })),
|
|
93
|
-
let
|
|
94
|
-
function
|
|
95
|
-
typeof
|
|
96
|
-
}
|
|
97
|
-
const
|
|
80
|
+
canRedo: ha,
|
|
81
|
+
canUndo: ua,
|
|
82
|
+
getState: mt,
|
|
83
|
+
moveScope: pa,
|
|
84
|
+
onChange: Pn,
|
|
85
|
+
redo: Ln,
|
|
86
|
+
removeScope: ca,
|
|
87
|
+
resetState: Tn,
|
|
88
|
+
setScope: Mn,
|
|
89
|
+
setSection: la,
|
|
90
|
+
setSections: da,
|
|
91
|
+
undo: An
|
|
92
|
+
}, Symbol.toStringTag, { value: "Module" })), On = [];
|
|
93
|
+
let bt = [], Ct = {}, R = null, de = !1, Fn = {};
|
|
94
|
+
function ga(e) {
|
|
95
|
+
typeof e == "function" && On.push(e);
|
|
96
|
+
}
|
|
97
|
+
const on = (e) => Object.assign({}, ...On.map((t) => t(e) || {})), xt = (e) => /^(\.[\w-]+|\[data-twd="[^"]+"\])$/.test(e), ma = (e) => {
|
|
98
98
|
try {
|
|
99
|
-
return document.querySelector(
|
|
99
|
+
return document.querySelector(e), !0;
|
|
100
100
|
} catch {
|
|
101
101
|
return !1;
|
|
102
102
|
}
|
|
103
103
|
};
|
|
104
|
-
function
|
|
104
|
+
function ba() {
|
|
105
105
|
return (!R || !R.isConnected) && (R = document.createElement("style"), R.id = "twiddle-scoped", document.head.appendChild(R)), R;
|
|
106
106
|
}
|
|
107
|
-
function
|
|
108
|
-
const t = document.documentElement;
|
|
109
|
-
ft.forEach((e) => t.style.removeProperty(e)), ft = [], St = {};
|
|
110
|
-
}
|
|
111
|
-
function mt(t) {
|
|
112
|
-
if (An = t, se) return;
|
|
107
|
+
function Nn() {
|
|
113
108
|
const e = document.documentElement;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
109
|
+
bt.forEach((t) => e.style.removeProperty(t)), bt = [], Ct = {};
|
|
110
|
+
}
|
|
111
|
+
function wt(e) {
|
|
112
|
+
if (Fn = e, de) return;
|
|
113
|
+
const t = document.documentElement;
|
|
114
|
+
Nn();
|
|
115
|
+
const n = on(e), a = Object.entries(e.scopes || {}).filter(([s]) => ma(s)).sort(([s], [r]) => xt(r) - xt(s)).map(([s, r]) => [s, on(r)]);
|
|
116
|
+
Object.entries(n).forEach(([s, r]) => t.style.setProperty(s, r)), bt = Object.keys(n), Ct = n;
|
|
117
117
|
const o = a.filter(([, s]) => Object.keys(s).length).map(([s, r]) => `${s} {
|
|
118
118
|
${Object.entries(r).map(([i, l]) => ` ${i}: ${l} !important;`).join(`
|
|
119
119
|
`)}
|
|
120
120
|
}`).join(`
|
|
121
121
|
`);
|
|
122
|
-
(o || R) && (
|
|
122
|
+
(o || R) && (ba().textContent = o);
|
|
123
123
|
}
|
|
124
|
-
function
|
|
125
|
-
const
|
|
126
|
-
Object.keys(a).forEach((o) =>
|
|
124
|
+
function jn(e) {
|
|
125
|
+
const t = document.documentElement, n = de, a = { ...Ct };
|
|
126
|
+
Object.keys(a).forEach((o) => t.style.removeProperty(o)), R && (R.disabled = !0);
|
|
127
127
|
try {
|
|
128
|
-
return
|
|
128
|
+
return e();
|
|
129
129
|
} finally {
|
|
130
|
-
n || (Object.entries(a).forEach(([o, s]) =>
|
|
130
|
+
n || (Object.entries(a).forEach(([o, s]) => t.style.setProperty(o, s)), R && (R.disabled = !1));
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
|
-
function
|
|
134
|
-
return
|
|
135
|
-
const n = getComputedStyle(
|
|
136
|
-
return Object.fromEntries(
|
|
133
|
+
function xa(e, t = document.documentElement) {
|
|
134
|
+
return jn(() => {
|
|
135
|
+
const n = getComputedStyle(t);
|
|
136
|
+
return Object.fromEntries(e.map((a) => [a, n.getPropertyValue(a).trim()]));
|
|
137
137
|
});
|
|
138
138
|
}
|
|
139
|
-
function
|
|
140
|
-
|
|
139
|
+
function wa() {
|
|
140
|
+
de || (Nn(), R && (R.disabled = !0), de = !0);
|
|
141
141
|
}
|
|
142
|
-
function
|
|
143
|
-
|
|
142
|
+
function ya() {
|
|
143
|
+
de && (de = !1, R && (R.disabled = !1), wt(Fn));
|
|
144
144
|
}
|
|
145
|
-
const
|
|
145
|
+
const va = `
|
|
146
146
|
:host { all: initial; } /* block fonts/colours inherited from the page */
|
|
147
147
|
* { box-sizing: border-box; }
|
|
148
148
|
|
|
@@ -160,6 +160,10 @@ const ma = `
|
|
|
160
160
|
position: fixed;
|
|
161
161
|
right: 16px;
|
|
162
162
|
bottom: 16px;
|
|
163
|
+
/* Where it sits is set inline by position.js (drag / nudge, per site);
|
|
164
|
+
--off-x / --off-y = distance from the two nearest edges. */
|
|
165
|
+
--off-x: 16px;
|
|
166
|
+
--off-y: 16px;
|
|
163
167
|
z-index: 2147483647;
|
|
164
168
|
font: 13px/1.4 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
165
169
|
color: var(--p-fg);
|
|
@@ -202,6 +206,13 @@ const ma = `
|
|
|
202
206
|
padding 6 + half a tab (~49px) − half the launcher (22). */
|
|
203
207
|
.launcher { transition: margin-bottom .2s cubic-bezier(0.2, 0.8, 0.2, 1); }
|
|
204
208
|
.wrap.is-open .launcher { margin-bottom: 10px; }
|
|
209
|
+
/* Left corners: the panel opens to the right of the launcher. Top corners: tops aligned. */
|
|
210
|
+
.wrap.at-left { flex-direction: row-reverse; }
|
|
211
|
+
.wrap.at-top { align-items: flex-start; }
|
|
212
|
+
.wrap.at-top.is-open .launcher { margin-bottom: 0; }
|
|
213
|
+
.launcher { touch-action: none; }
|
|
214
|
+
.wrap.dragging .launcher { cursor: grabbing; }
|
|
215
|
+
.wrap.dragging .panel { pointer-events: none; }
|
|
205
216
|
@media (prefers-reduced-motion: reduce) { .launcher { transition: none; } }
|
|
206
217
|
.mark { display: block; overflow: visible; }
|
|
207
218
|
.mark-disc { fill: #16161a; }
|
|
@@ -219,12 +230,12 @@ const ma = `
|
|
|
219
230
|
/* Panel */
|
|
220
231
|
.panel {
|
|
221
232
|
width: 360px; /* tabbed layout (Figma 254:323); was 300 */
|
|
222
|
-
/*
|
|
223
|
-
max-width: calc(100vw -
|
|
233
|
+
/* 70px = 16 room on the far side + 44 launcher + 10 gap */
|
|
234
|
+
max-width: calc(100vw - var(--off-x) - 70px);
|
|
224
235
|
/* As tall as its content; scrolls only when the window is shorter.
|
|
225
236
|
32px = 16 margin below + 16 room at the top. */
|
|
226
|
-
max-height: calc(100vh -
|
|
227
|
-
max-height: calc(100dvh -
|
|
237
|
+
max-height: calc(100vh - var(--off-y) - 16px);
|
|
238
|
+
max-height: calc(100dvh - var(--off-y) - 16px);
|
|
228
239
|
background: var(--p-bg);
|
|
229
240
|
border: 1px solid var(--p-line);
|
|
230
241
|
border-radius: 14px;
|
|
@@ -451,9 +462,7 @@ const ma = `
|
|
|
451
462
|
padding: 1px 4px;
|
|
452
463
|
}
|
|
453
464
|
|
|
454
|
-
/* Closed: beside the launcher (
|
|
455
|
-
the panel takes that spot, so the toast floats just above the tab bar
|
|
456
|
-
(63px tall), inside the panel's right edge. */
|
|
465
|
+
/* Closed: beside the launcher (on the panel's side of it, centred on it). */
|
|
457
466
|
.toast {
|
|
458
467
|
position: absolute; bottom: 22px; right: 54px; transform: translateY(50%);
|
|
459
468
|
background: var(--p-accent); color: var(--p-on-accent);
|
|
@@ -462,7 +471,13 @@ const ma = `
|
|
|
462
471
|
opacity: 0; transition: opacity .15s ease; pointer-events: none;
|
|
463
472
|
}
|
|
464
473
|
.toast.show { opacity: 1; }
|
|
465
|
-
.wrap.
|
|
474
|
+
.wrap.at-left .toast { right: auto; left: 54px; }
|
|
475
|
+
.wrap.at-top .toast { bottom: auto; top: 22px; transform: translateY(-50%); }
|
|
476
|
+
/* Open or minimised: just above the panel, right-aligned with it, so it
|
|
477
|
+
never covers a row (Ravi, 2026-10-16; Figma 269:1037). Top corners: below it. */
|
|
478
|
+
.wrap.is-open .toast { bottom: calc(100% + 8px); right: 54px; left: auto; top: auto; transform: none; z-index: 3; }
|
|
479
|
+
.wrap.is-open.at-left .toast { right: auto; left: 54px; }
|
|
480
|
+
.wrap.is-open.at-top .toast { bottom: auto; top: calc(100% + 8px); }
|
|
466
481
|
|
|
467
482
|
/* ---------- Proposals below: not yet designed in Figma ---------- */
|
|
468
483
|
|
|
@@ -886,219 +901,288 @@ const ma = `
|
|
|
886
901
|
.summary-swatch { width: 14px; height: 14px; border-radius: 4px; flex: none; }
|
|
887
902
|
.summary-swatch[hidden] { display: none; }
|
|
888
903
|
.summary-row .chev { display: grid; color: var(--p-muted); }
|
|
889
|
-
`,
|
|
890
|
-
dial:
|
|
891
|
-
link:
|
|
892
|
-
reset:
|
|
893
|
-
close:
|
|
894
|
-
chevron:
|
|
904
|
+
`, P = (e, t, n) => `<svg width="${e}" height="${e}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="${t}" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${n}</svg>`, A = {
|
|
905
|
+
dial: P(20, 1.8, '<circle cx="12" cy="12" r="8"/><path d="M12 12l4-4"/><path d="M12 2v2M12 20v2M2 12h2M20 12h2"/>'),
|
|
906
|
+
link: P(15, 2, '<path d="M10 13a5 5 0 0 0 7.07 0l3-3a5 5 0 0 0-7.07-7.07l-1 1"/><path d="M14 11a5 5 0 0 0-7.07 0l-3 3a5 5 0 0 0 7.07 7.07l1-1"/>'),
|
|
907
|
+
reset: P(15, 2, '<path d="M3 12a9 9 0 1 0 3-6.7"/><path d="M3 3v6h6"/>'),
|
|
908
|
+
close: P(15, 2, '<path d="M18 6L6 18M6 6l12 12"/>'),
|
|
909
|
+
chevron: P(12, 2.4, '<path d="M9 6l6 6-6 6"/>'),
|
|
895
910
|
// Proposals: not yet in the Figma Icon/* set.
|
|
896
|
-
pick:
|
|
897
|
-
compare:
|
|
898
|
-
undo:
|
|
899
|
-
redo:
|
|
900
|
-
back:
|
|
901
|
-
down:
|
|
902
|
-
minimise:
|
|
903
|
-
copy:
|
|
904
|
-
warn:
|
|
911
|
+
pick: P(15, 2, '<circle cx="12" cy="12" r="7"/><path d="M12 2v4M12 18v4M2 12h4M18 12h4"/>'),
|
|
912
|
+
compare: P(15, 2, '<circle cx="12" cy="12" r="9"/><path d="M12 3v18" /><path d="M12 3a9 9 0 0 0 0 18z" fill="currentColor" stroke="none"/>'),
|
|
913
|
+
undo: P(15, 2, '<path d="M9 14L4 9l5-5"/><path d="M4 9h11a5 5 0 0 1 0 10h-3"/>'),
|
|
914
|
+
redo: P(15, 2, '<path d="M15 14l5-5-5-5"/><path d="M20 9H9a5 5 0 0 0 0 10h3"/>'),
|
|
915
|
+
back: P(15, 2, '<path d="M15 18l-6-6 6-6"/>'),
|
|
916
|
+
down: P(12, 2.4, '<path d="M6 9l6 6 6-6"/>'),
|
|
917
|
+
minimise: P(15, 2, '<path d="M5 12h14"/>'),
|
|
918
|
+
copy: P(13, 2, '<rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/>'),
|
|
919
|
+
warn: P(13, 2, '<path d="M12 3l9.5 17h-19z"/><path d="M12 10v4M12 17.5v.01"/>'),
|
|
905
920
|
// Launcher: the Panel product mark (Twiddle brand; Figma Logo page › Panel mark; also site/favicon.svg).
|
|
906
921
|
// A switch knocked out of a disc; knob + two-dot trail. Classes let the Toggle animation move them.
|
|
907
922
|
panelMark: '<svg class="mark" width="44" height="44" viewBox="0 0 120 120" aria-hidden="true"><path class="mark-disc" fill-rule="evenodd" clip-rule="evenodd" d="M60 120C93.1371 120 120 93.1371 120 60C120 26.8629 93.1371 0 60 0C26.8629 0 0 26.8629 0 60C0 93.1371 26.8629 120 60 120ZM86 76C86 90.3594 74.3594 102 60 102C45.6406 102 34 90.3594 34 76V44C34 29.6406 45.6406 18 60 18C74.3594 18 86 29.6406 86 44V76ZM60 22C72.1503 22 82 31.8497 82 44V76C82 88.1503 72.1503 98 60 98C47.8497 98 38 88.1503 38 76V44C38 31.8497 47.8497 22 60 22Z"/><circle class="mark-t2" cx="60" cy="80.46" r="4.28"/><circle class="mark-t1" cx="60" cy="66.04" r="7.14"/><circle class="mark-knob" cx="60" cy="44" r="11.9"/></svg>',
|
|
908
923
|
// Tab bar (Figma: Panel — tabbed layout (proposal), Icon/Tab * components)
|
|
909
|
-
tabOverview:
|
|
910
|
-
tabColour:
|
|
911
|
-
tabType:
|
|
912
|
-
tabSpacing:
|
|
913
|
-
tabShape:
|
|
914
|
-
tabShadows:
|
|
915
|
-
sliders:
|
|
924
|
+
tabOverview: P(18, 1.8, '<circle cx="12" cy="12" r="8"/><path d="M12 12l4-4"/><path d="M12 2v2M12 20v2M2 12h2M20 12h2"/>'),
|
|
925
|
+
tabColour: P(18, 2, '<path d="M12 3c-3.5 4.5-6 7.8-6 11a6 6 0 0 0 12 0c0-3.2-2.5-6.5-6-11z"/>'),
|
|
926
|
+
tabType: P(18, 2, '<path d="M5 7V5h14v2"/><path d="M12 5v14"/><path d="M9 19h6"/>'),
|
|
927
|
+
tabSpacing: P(18, 2, '<path d="M4 5v14M20 5v14"/><path d="M8 12h8"/><path d="M10 9l-3 3 3 3M14 9l3 3-3 3"/>'),
|
|
928
|
+
tabShape: P(18, 2, '<path d="M20 4h-9a7 7 0 0 0-7 7v9"/><path d="M20 10h-5a5 5 0 0 0-5 5v5"/>'),
|
|
929
|
+
tabShadows: P(18, 2, '<rect x="4" y="4" width="12" height="12" rx="2"/><path d="M20 8v10a2 2 0 0 1-2 2H8"/>'),
|
|
930
|
+
sliders: P(15, 2, '<path d="M4 7h10M18 7h2M4 17h4M12 17h8"/><circle cx="16" cy="7" r="2"/><circle cx="10" cy="17" r="2"/>')
|
|
916
931
|
// Figma: Icon/Sliders (proposal)
|
|
917
|
-
},
|
|
918
|
-
function
|
|
932
|
+
}, Rn = "twiddle:";
|
|
933
|
+
function Z(e) {
|
|
919
934
|
try {
|
|
920
|
-
return localStorage.getItem(
|
|
935
|
+
return localStorage.getItem(Rn + e);
|
|
921
936
|
} catch {
|
|
922
937
|
return null;
|
|
923
938
|
}
|
|
924
939
|
}
|
|
925
|
-
function
|
|
940
|
+
function oe(e, t) {
|
|
926
941
|
try {
|
|
927
|
-
localStorage.setItem(
|
|
942
|
+
localStorage.setItem(Rn + e, t);
|
|
928
943
|
} catch {
|
|
929
944
|
}
|
|
930
945
|
}
|
|
931
|
-
|
|
946
|
+
const re = 44, ae = 8, ka = 4, zn = { corner: "br", x: 16, y: 16 };
|
|
947
|
+
function Sa() {
|
|
948
|
+
try {
|
|
949
|
+
const e = JSON.parse(Z("position"));
|
|
950
|
+
if (e && /^[tb][lr]$/.test(e.corner) && isFinite(e.x) && isFinite(e.y)) return e;
|
|
951
|
+
} catch {
|
|
952
|
+
}
|
|
953
|
+
return { ...zn };
|
|
954
|
+
}
|
|
955
|
+
function rn(e) {
|
|
956
|
+
const t = Math.max(ae, innerWidth - re - ae), n = Math.max(ae, innerHeight - re - ae);
|
|
957
|
+
return { corner: e.corner, x: Math.round(Math.min(Math.max(e.x, ae), t)), y: Math.round(Math.min(Math.max(e.y, ae), n)) };
|
|
958
|
+
}
|
|
959
|
+
function $a({ wrapEl: e, launcherEl: t }) {
|
|
960
|
+
let n = Sa();
|
|
961
|
+
function a() {
|
|
962
|
+
const d = rn(n), p = d.corner[1] === "l", u = d.corner[0] === "t";
|
|
963
|
+
Object.assign(e.style, {
|
|
964
|
+
left: p ? `${d.x}px` : "auto",
|
|
965
|
+
right: p ? "auto" : `${d.x}px`,
|
|
966
|
+
top: u ? `${d.y}px` : "auto",
|
|
967
|
+
bottom: u ? "auto" : `${d.y}px`
|
|
968
|
+
}), e.style.setProperty("--off-x", `${d.x}px`), e.style.setProperty("--off-y", `${d.y}px`), e.classList.toggle("at-left", p), e.classList.toggle("at-top", u);
|
|
969
|
+
}
|
|
970
|
+
function o(d, p = !0) {
|
|
971
|
+
n = rn(d), a(), p && oe("position", JSON.stringify(n));
|
|
972
|
+
}
|
|
973
|
+
function s(d, p) {
|
|
974
|
+
const u = d + re / 2 < innerWidth / 2, v = p + re / 2 < innerHeight / 2, f = !v && e.classList.contains("is-open") ? 10 : 0;
|
|
975
|
+
return {
|
|
976
|
+
corner: (v ? "t" : "b") + (u ? "l" : "r"),
|
|
977
|
+
x: u ? d : innerWidth - d - re,
|
|
978
|
+
y: v ? p : innerHeight - p - re - f
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
let r = null, i = !1;
|
|
982
|
+
t.addEventListener("pointerdown", (d) => {
|
|
983
|
+
if (d.button !== 0) return;
|
|
984
|
+
const p = t.getBoundingClientRect();
|
|
985
|
+
r = { id: d.pointerId, sx: d.clientX, sy: d.clientY, dx: d.clientX - p.left, dy: d.clientY - p.top, moving: !1 };
|
|
986
|
+
}), t.addEventListener("pointermove", (d) => {
|
|
987
|
+
if (!(!r || d.pointerId !== r.id)) {
|
|
988
|
+
if (!r.moving) {
|
|
989
|
+
if (Math.hypot(d.clientX - r.sx, d.clientY - r.sy) < ka) return;
|
|
990
|
+
r.moving = !0, t.setPointerCapture(d.pointerId), e.classList.add("dragging");
|
|
991
|
+
}
|
|
992
|
+
o(s(d.clientX - r.dx, d.clientY - r.dy), !1);
|
|
993
|
+
}
|
|
994
|
+
});
|
|
995
|
+
const l = (d) => {
|
|
996
|
+
!r || d.pointerId !== r.id || (r.moving && (i = !0, e.classList.remove("dragging"), o(n)), r = null);
|
|
997
|
+
};
|
|
998
|
+
return t.addEventListener("pointerup", l), t.addEventListener("pointercancel", l), t.addEventListener("keydown", (d) => {
|
|
999
|
+
const p = { ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [0, -1], ArrowDown: [0, 1] }[d.key];
|
|
1000
|
+
if (!p || d.altKey || d.metaKey || d.ctrlKey) return;
|
|
1001
|
+
d.preventDefault();
|
|
1002
|
+
const u = d.shiftKey ? 10 : 1, v = n.corner[1] === "l" ? 1 : -1, f = n.corner[0] === "t" ? 1 : -1;
|
|
1003
|
+
o({ corner: n.corner, x: n.x + p[0] * u * v, y: n.y + p[1] * u * f });
|
|
1004
|
+
}), window.addEventListener("resize", a), a(), {
|
|
1005
|
+
/** Call from the launcher's click handler: true = ignore this click (it ended a drag). */
|
|
1006
|
+
consumeClick() {
|
|
1007
|
+
const d = i;
|
|
1008
|
+
return i = !1, d;
|
|
1009
|
+
},
|
|
1010
|
+
reset() {
|
|
1011
|
+
o({ ...zn });
|
|
1012
|
+
}
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
function ce({ label: e, options: t, onSelect: n }) {
|
|
932
1016
|
const a = document.createElement("div");
|
|
933
|
-
a.className = "seg", a.setAttribute("role", "radiogroup"), a.setAttribute("aria-label",
|
|
934
|
-
const o =
|
|
935
|
-
const
|
|
936
|
-
return
|
|
1017
|
+
a.className = "seg", a.setAttribute("role", "radiogroup"), a.setAttribute("aria-label", e);
|
|
1018
|
+
const o = t.map((l) => {
|
|
1019
|
+
const d = document.createElement("button");
|
|
1020
|
+
return d.type = "button", d.setAttribute("role", "radio"), d.dataset.value = l.value, d.textContent = l.label, l.title && (d.title = l.title), d.addEventListener("click", () => i(l.value)), d;
|
|
937
1021
|
});
|
|
938
1022
|
a.append(...o);
|
|
939
1023
|
let s = null;
|
|
940
1024
|
function r(l) {
|
|
941
|
-
s = l, o.forEach((
|
|
942
|
-
const
|
|
943
|
-
o.forEach((
|
|
944
|
-
|
|
1025
|
+
s = l, o.forEach((p) => p.setAttribute("aria-checked", String(p.dataset.value === l)));
|
|
1026
|
+
const d = Math.max(0, o.findIndex((p) => p.dataset.value === l));
|
|
1027
|
+
o.forEach((p, u) => {
|
|
1028
|
+
p.tabIndex = u === d ? 0 : -1;
|
|
945
1029
|
});
|
|
946
1030
|
}
|
|
947
|
-
function i(l,
|
|
948
|
-
l !== s && (r(l), n(l)),
|
|
1031
|
+
function i(l, d) {
|
|
1032
|
+
l !== s && (r(l), n(l)), d && o.find((p) => p.dataset.value === l).focus();
|
|
949
1033
|
}
|
|
950
1034
|
return a.addEventListener("keydown", (l) => {
|
|
951
|
-
const
|
|
952
|
-
if (
|
|
953
|
-
const
|
|
954
|
-
let
|
|
955
|
-
if (
|
|
956
|
-
else if (l.key === "Home")
|
|
957
|
-
else if (l.key === "End")
|
|
1035
|
+
const d = o.indexOf(l.target);
|
|
1036
|
+
if (d < 0) return;
|
|
1037
|
+
const p = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[l.key];
|
|
1038
|
+
let u;
|
|
1039
|
+
if (p) u = (d + p + o.length) % o.length;
|
|
1040
|
+
else if (l.key === "Home") u = 0;
|
|
1041
|
+
else if (l.key === "End") u = o.length - 1;
|
|
958
1042
|
else return;
|
|
959
|
-
l.preventDefault(), i(o[
|
|
1043
|
+
l.preventDefault(), i(o[u].dataset.value, !0);
|
|
960
1044
|
}), r(null), { el: a, setValue: r };
|
|
961
1045
|
}
|
|
962
|
-
const
|
|
963
|
-
function*
|
|
964
|
-
for (const
|
|
965
|
-
|
|
1046
|
+
const In = /var\(\s*(--twd-[\w-]+)/g, Ea = /::?[\w-]+(\([^)]*\))?/g;
|
|
1047
|
+
function* _e(e) {
|
|
1048
|
+
for (const t of e)
|
|
1049
|
+
t.selectorText != null && (yield t), t.cssRules && (yield* _e(t.cssRules));
|
|
966
1050
|
}
|
|
967
|
-
function
|
|
968
|
-
const
|
|
969
|
-
for (const
|
|
970
|
-
if (
|
|
1051
|
+
function Ca() {
|
|
1052
|
+
const e = [];
|
|
1053
|
+
for (const t of document.styleSheets) {
|
|
1054
|
+
if (t.ownerNode?.id === "twiddle-scoped") continue;
|
|
971
1055
|
let n;
|
|
972
1056
|
try {
|
|
973
|
-
n =
|
|
1057
|
+
n = t.cssRules;
|
|
974
1058
|
} catch {
|
|
975
1059
|
continue;
|
|
976
1060
|
}
|
|
977
|
-
for (const a of
|
|
978
|
-
const o = [...a.style.cssText.matchAll(
|
|
979
|
-
o.length &&
|
|
1061
|
+
for (const a of _e(n)) {
|
|
1062
|
+
const o = [...a.style.cssText.matchAll(In)].map((s) => s[1]);
|
|
1063
|
+
o.length && e.push({ selectors: a.selectorText.split(","), vars: o });
|
|
980
1064
|
}
|
|
981
1065
|
}
|
|
982
|
-
return
|
|
1066
|
+
return e;
|
|
983
1067
|
}
|
|
984
|
-
function
|
|
985
|
-
const n =
|
|
1068
|
+
function Ma(e, t) {
|
|
1069
|
+
const n = t.replace(Ea, "").trim() || "*";
|
|
986
1070
|
try {
|
|
987
|
-
return
|
|
1071
|
+
return e.matches(n);
|
|
988
1072
|
} catch {
|
|
989
1073
|
return !1;
|
|
990
1074
|
}
|
|
991
1075
|
}
|
|
992
|
-
function
|
|
993
|
-
const
|
|
994
|
-
return
|
|
995
|
-
const s = a.find((r) =>
|
|
996
|
-
s && (o.forEach((r) =>
|
|
997
|
-
}), [...(
|
|
1076
|
+
function Dn(e) {
|
|
1077
|
+
const t = /* @__PURE__ */ new Set(), n = [];
|
|
1078
|
+
return Ca().forEach(({ selectors: a, vars: o }) => {
|
|
1079
|
+
const s = a.find((r) => Ma(e, r));
|
|
1080
|
+
s && (o.forEach((r) => t.add(r)), n.push(s.trim()));
|
|
1081
|
+
}), [...(e.getAttribute("style") || "").matchAll(In)].forEach((a) => t.add(a[1])), { names: t, selectors: n };
|
|
998
1082
|
}
|
|
999
|
-
const
|
|
1000
|
-
function
|
|
1001
|
-
const
|
|
1002
|
-
let n =
|
|
1083
|
+
const yt = (e) => window.CSS && CSS.escape ? CSS.escape(e) : e;
|
|
1084
|
+
function Ta(e) {
|
|
1085
|
+
const t = [];
|
|
1086
|
+
let n = e;
|
|
1003
1087
|
for (; n && n.nodeType === 1 && n !== document.body && n !== document.documentElement; ) {
|
|
1004
|
-
if (n.id && document.querySelectorAll(`#${
|
|
1005
|
-
return
|
|
1088
|
+
if (n.id && document.querySelectorAll(`#${yt(n.id)}`).length === 1)
|
|
1089
|
+
return t.unshift(`#${yt(n.id)}`), t.join(" > ");
|
|
1006
1090
|
const a = n.localName, o = [...n.parentElement.children].filter((s) => s.localName === a);
|
|
1007
|
-
|
|
1091
|
+
t.unshift(o.length > 1 ? `${a}:nth-of-type(${o.indexOf(n) + 1})` : a), n = n.parentElement;
|
|
1008
1092
|
}
|
|
1009
|
-
return ["body", ...
|
|
1093
|
+
return ["body", ...t].join(" > ");
|
|
1010
1094
|
}
|
|
1011
|
-
function
|
|
1012
|
-
const n =
|
|
1095
|
+
function Aa(e, t = Dn(e).selectors) {
|
|
1096
|
+
const n = e.getAttribute("data-twd");
|
|
1013
1097
|
if (n) return `[data-twd="${n.replace(/"/g, "")}"]`;
|
|
1014
|
-
const a = [...
|
|
1015
|
-
return s ? `.${
|
|
1098
|
+
const a = [...e.classList], s = a.find((r) => t.some((i) => new RegExp(`\\.${r.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\w-])`).test(i))) || a[0];
|
|
1099
|
+
return s ? `.${yt(s)}` : null;
|
|
1016
1100
|
}
|
|
1017
|
-
function
|
|
1018
|
-
const
|
|
1101
|
+
function La(e) {
|
|
1102
|
+
const t = Dn(e), n = Aa(e, t.selectors), a = (e.getAttribute("aria-label") || e.placeholder || e.textContent || "").trim().replace(/\s+/g, " ");
|
|
1019
1103
|
return {
|
|
1020
|
-
el:
|
|
1021
|
-
tag:
|
|
1022
|
-
classes: [...
|
|
1104
|
+
el: e,
|
|
1105
|
+
tag: e.localName,
|
|
1106
|
+
classes: [...e.classList],
|
|
1023
1107
|
text: a.length > 40 ? `${a.slice(0, 40)}…` : a,
|
|
1024
|
-
tokens:
|
|
1025
|
-
elementSelector:
|
|
1108
|
+
tokens: t.names,
|
|
1109
|
+
elementSelector: Ta(e),
|
|
1026
1110
|
classSelector: n,
|
|
1027
1111
|
classCount: n ? document.querySelectorAll(n).length : 0
|
|
1028
1112
|
};
|
|
1029
1113
|
}
|
|
1030
|
-
const
|
|
1031
|
-
function
|
|
1032
|
-
const a =
|
|
1114
|
+
const vt = (e) => e.localName + [...e.classList].map((t) => `.${t}`).join(""), Pa = { radius: "px", intensity: "%", base: "px" }, Un = (e) => e.charAt(0).toUpperCase() + e.slice(1), Oa = (e, t) => typeof t == "number" || /^\d+$/.test(t) ? `${t}${Pa[e] || ""}` : Un(String(t));
|
|
1115
|
+
function sn(e, t, n) {
|
|
1116
|
+
const a = jn(() => e.tokens({ [e.id]: t }) || {}), o = Object.keys(a);
|
|
1033
1117
|
let s = {};
|
|
1034
1118
|
if (o.length) {
|
|
1035
1119
|
const r = n.selector ? document.querySelector(n.selector) : void 0;
|
|
1036
|
-
s =
|
|
1120
|
+
s = xa(o, r || void 0);
|
|
1037
1121
|
}
|
|
1038
1122
|
return {
|
|
1039
1123
|
...n,
|
|
1040
|
-
sectionId:
|
|
1041
|
-
title:
|
|
1042
|
-
settings: Object.entries(
|
|
1124
|
+
sectionId: e.id,
|
|
1125
|
+
title: e.title,
|
|
1126
|
+
settings: Object.entries(t).map(([r, i]) => ({ key: r, label: Un(r), value: Oa(r, i), ...e.describe?.(r, i) || {} })),
|
|
1043
1127
|
tokens: o.map((r) => ({ name: r, from: s[r] || "", to: a[r] }))
|
|
1044
1128
|
};
|
|
1045
1129
|
}
|
|
1046
|
-
function
|
|
1130
|
+
function ln(e, t) {
|
|
1047
1131
|
const n = [];
|
|
1048
|
-
return
|
|
1049
|
-
|
|
1050
|
-
}), Object.entries(
|
|
1051
|
-
const s =
|
|
1052
|
-
|
|
1053
|
-
o[r.id] && n.push(
|
|
1132
|
+
return t.forEach((a) => {
|
|
1133
|
+
e[a.id] && n.push(sn(a, e[a.id], { kind: "global", selector: null }));
|
|
1134
|
+
}), Object.entries(e.scopes || {}).forEach(([a, o]) => {
|
|
1135
|
+
const s = xt(a) ? "class" : "element";
|
|
1136
|
+
t.forEach((r) => {
|
|
1137
|
+
o[r.id] && n.push(sn(r, o[r.id], { kind: s, selector: a }));
|
|
1054
1138
|
});
|
|
1055
1139
|
}), n;
|
|
1056
1140
|
}
|
|
1057
|
-
function
|
|
1058
|
-
const
|
|
1059
|
-
return
|
|
1141
|
+
function Fa(e) {
|
|
1142
|
+
const t = (o) => Object.values(o || {}).reduce((s, r) => s + Object.keys(r || {}).length, 0), { scopes: n, ...a } = e;
|
|
1143
|
+
return t(a) + Object.values(n || {}).reduce((o, s) => o + t(s), 0);
|
|
1060
1144
|
}
|
|
1061
|
-
const
|
|
1062
|
-
function
|
|
1063
|
-
let
|
|
1145
|
+
const le = (e) => `\`${e}\``, et = (e) => e.map((t) => ` - ${le(t.name)}: ${t.from ? `${le(t.from)} → ` : ""}${le(t.to)}`), tt = (e) => `${e.title} › ${e.settings.map((t) => `${t.label}: ${t.value}`).join(", ")}`;
|
|
1146
|
+
function Na(e) {
|
|
1147
|
+
let t = null;
|
|
1064
1148
|
try {
|
|
1065
|
-
|
|
1149
|
+
t = document.querySelector(e);
|
|
1066
1150
|
} catch {
|
|
1067
1151
|
}
|
|
1068
|
-
if (!
|
|
1069
|
-
const n = (
|
|
1070
|
-
return `${
|
|
1152
|
+
if (!t) return "not found on the current page";
|
|
1153
|
+
const n = (t.getAttribute("aria-label") || t.placeholder || t.textContent || "").trim().replace(/\s+/g, " ").slice(0, 60);
|
|
1154
|
+
return `${le(vt(t))}${n ? ` with text “${n}”` : ""}`;
|
|
1071
1155
|
}
|
|
1072
|
-
function
|
|
1156
|
+
function dn(e, t) {
|
|
1073
1157
|
const n = /* @__PURE__ */ new Map();
|
|
1074
|
-
return
|
|
1075
|
-
const o = a[
|
|
1158
|
+
return e.forEach((a) => {
|
|
1159
|
+
const o = a[t];
|
|
1076
1160
|
n.has(o) || n.set(o, []), n.get(o).push(a);
|
|
1077
1161
|
}), [...n];
|
|
1078
1162
|
}
|
|
1079
|
-
function
|
|
1080
|
-
if (!
|
|
1081
|
-
const a =
|
|
1163
|
+
function ja(e, { link: t = location.href, warnings: n = [] } = {}) {
|
|
1164
|
+
if (!e.length) return "";
|
|
1165
|
+
const a = e.filter((i) => i.kind === "global"), o = e.filter((i) => i.kind === "class"), s = e.filter((i) => i.kind === "element"), r = [];
|
|
1082
1166
|
if (r.push(
|
|
1083
1167
|
"# Apply design tweaks from Panel",
|
|
1084
1168
|
"",
|
|
1085
1169
|
"I explored design changes to this prototype with Panel (by Twiddle), a tweak panel that overrides the prototype's `--twd-*` CSS custom properties (design tokens) at runtime. Make these changes permanent in the source code.",
|
|
1086
1170
|
"",
|
|
1087
|
-
`Preview of the tweaked version: ${
|
|
1171
|
+
`Preview of the tweaked version: ${t}`
|
|
1088
1172
|
), a.length && (r.push(
|
|
1089
1173
|
"",
|
|
1090
1174
|
"## 1. Global token changes",
|
|
1091
1175
|
"",
|
|
1092
1176
|
"Update the default value of each token where it is defined (usually a `:root` rule or a `tokens.css` / `twiddle-tokens.css` file). Change the definition, not the places that use it.",
|
|
1093
1177
|
""
|
|
1094
|
-
), a.forEach((i) => r.push(`- **${
|
|
1178
|
+
), a.forEach((i) => r.push(`- **${tt(i)}**`, ...et(i.tokens)))), o.length && (r.push(
|
|
1095
1179
|
"",
|
|
1096
1180
|
`## ${a.length ? 2 : 1}. Component-wide changes`,
|
|
1097
1181
|
"",
|
|
1098
1182
|
"These apply to every element matching the selector. In the CSS rule for that component, redeclare the token, e.g. `.input { --twd-radius-control: 999px; }`. Don't replace the `var(--twd-…)` usages with fixed values.",
|
|
1099
1183
|
""
|
|
1100
|
-
),
|
|
1101
|
-
r.push(`- ${
|
|
1184
|
+
), dn(o, "selector").forEach(([i, l]) => {
|
|
1185
|
+
r.push(`- ${le(i)} (${document.querySelectorAll(i).length} on this page)`), l.forEach((d) => r.push(` - **${tt(d)}**`, ...et(d.tokens).map((p) => ` ${p}`)));
|
|
1102
1186
|
})), s.length) {
|
|
1103
1187
|
const i = 1 + (a.length ? 1 : 0) + (o.length ? 1 : 0);
|
|
1104
1188
|
r.push(
|
|
@@ -1107,8 +1191,8 @@ function Ma(t, { link: e = location.href, warnings: n = [] } = {}) {
|
|
|
1107
1191
|
"",
|
|
1108
1192
|
"These apply to one element only. Add a modifier class to that element (e.g. `input--pill`) and redeclare the token in a rule for that class. The selectors below are runtime paths to help you find the element; don't copy them into the stylesheet.",
|
|
1109
1193
|
""
|
|
1110
|
-
),
|
|
1111
|
-
r.push(`- ${
|
|
1194
|
+
), dn(s, "selector").forEach(([l, d]) => {
|
|
1195
|
+
r.push(`- ${Na(l)}, found at ${le(l)}`), d.forEach((p) => r.push(` - **${tt(p)}**`, ...et(p.tokens).map((u) => ` ${u}`)));
|
|
1112
1196
|
});
|
|
1113
1197
|
}
|
|
1114
1198
|
return n.length && r.push(
|
|
@@ -1129,85 +1213,85 @@ function Ma(t, { link: e = location.href, warnings: n = [] } = {}) {
|
|
|
1129
1213
|
), r.join(`
|
|
1130
1214
|
`);
|
|
1131
1215
|
}
|
|
1132
|
-
const
|
|
1216
|
+
const Ue = 4.5, Ra = 3, za = [
|
|
1133
1217
|
["--twd-text", "--twd-bg", "Text on background"],
|
|
1134
1218
|
["--twd-text", "--twd-surface", "Text on surface"],
|
|
1135
1219
|
["--twd-text-muted", "--twd-surface", "Muted text on surface"],
|
|
1136
1220
|
["--twd-on-primary", "--twd-primary", "Text on brand"]
|
|
1137
1221
|
];
|
|
1138
1222
|
let Y;
|
|
1139
|
-
function
|
|
1140
|
-
if (!
|
|
1141
|
-
Y.clearRect(0, 0, 1, 1), Y.fillStyle = "#000", Y.fillStyle =
|
|
1142
|
-
const [
|
|
1143
|
-
return [
|
|
1144
|
-
}
|
|
1145
|
-
function
|
|
1146
|
-
const
|
|
1147
|
-
|
|
1148
|
-
const n = getComputedStyle(
|
|
1149
|
-
return
|
|
1150
|
-
}
|
|
1151
|
-
const
|
|
1223
|
+
function Mt(e) {
|
|
1224
|
+
if (!e || (Y ||= Object.assign(document.createElement("canvas"), { width: 1, height: 1 }).getContext("2d", { willReadFrequently: !0 }), !Y)) return null;
|
|
1225
|
+
Y.clearRect(0, 0, 1, 1), Y.fillStyle = "#000", Y.fillStyle = e, Y.fillRect(0, 0, 1, 1);
|
|
1226
|
+
const [t, n, a, o] = Y.getImageData(0, 0, 1, 1).data;
|
|
1227
|
+
return [t, n, a, o / 255];
|
|
1228
|
+
}
|
|
1229
|
+
function cn(e) {
|
|
1230
|
+
const t = document.createElement("i");
|
|
1231
|
+
t.style.cssText = `position:absolute;visibility:hidden;color:var(${e})`, document.documentElement.appendChild(t);
|
|
1232
|
+
const n = getComputedStyle(t).color;
|
|
1233
|
+
return t.remove(), Mt(n);
|
|
1234
|
+
}
|
|
1235
|
+
const pn = ([e, t, n]) => {
|
|
1152
1236
|
const a = (o) => (o /= 255, o <= 0.03928 ? o / 12.92 : ((o + 0.055) / 1.055) ** 2.4);
|
|
1153
|
-
return 0.2126 * a(
|
|
1154
|
-
},
|
|
1155
|
-
function
|
|
1156
|
-
const n =
|
|
1237
|
+
return 0.2126 * a(e) + 0.7152 * a(t) + 0.0722 * a(n);
|
|
1238
|
+
}, Bn = (e, t) => e.slice(0, 3).map((n, a) => n * e[3] + t[a] * (1 - e[3])).concat(1);
|
|
1239
|
+
function qn(e, t) {
|
|
1240
|
+
const n = pn(e[3] < 1 ? Bn(e, t) : e), a = pn(t);
|
|
1157
1241
|
return (Math.max(n, a) + 0.05) / (Math.min(n, a) + 0.05);
|
|
1158
1242
|
}
|
|
1159
|
-
const
|
|
1160
|
-
function
|
|
1161
|
-
const
|
|
1162
|
-
return
|
|
1163
|
-
const o =
|
|
1243
|
+
const Hn = (e) => `${e.toFixed(1)}:1`;
|
|
1244
|
+
function nt() {
|
|
1245
|
+
const e = [];
|
|
1246
|
+
return za.forEach(([t, n, a]) => {
|
|
1247
|
+
const o = cn(t), s = cn(n);
|
|
1164
1248
|
if (!o || !s || s[3] === 0) return;
|
|
1165
|
-
const r =
|
|
1166
|
-
r <
|
|
1167
|
-
}),
|
|
1249
|
+
const r = qn(o, s);
|
|
1250
|
+
r < Ue && e.push({ label: a, ratio: r, needed: Ue, text: `${a}: ${Hn(r)} · AA needs ${Ue}` });
|
|
1251
|
+
}), e;
|
|
1168
1252
|
}
|
|
1169
|
-
function
|
|
1170
|
-
const
|
|
1171
|
-
for (let n =
|
|
1172
|
-
const a =
|
|
1173
|
-
if (a && a[3] > 0 && (
|
|
1253
|
+
function Ia(e) {
|
|
1254
|
+
const t = [];
|
|
1255
|
+
for (let n = e; n && n.nodeType === 1; n = n.parentElement) {
|
|
1256
|
+
const a = Mt(getComputedStyle(n).backgroundColor);
|
|
1257
|
+
if (a && a[3] > 0 && (t.push(a), a[3] >= 1))
|
|
1174
1258
|
break;
|
|
1175
1259
|
}
|
|
1176
|
-
return
|
|
1260
|
+
return t.reduceRight((n, a) => Bn(a, n), [255, 255, 255, 1]);
|
|
1177
1261
|
}
|
|
1178
|
-
function
|
|
1179
|
-
if (!
|
|
1180
|
-
const
|
|
1262
|
+
function Da(e) {
|
|
1263
|
+
if (!e.textContent.trim() && !e.placeholder) return null;
|
|
1264
|
+
const t = getComputedStyle(e), n = Mt(t.color);
|
|
1181
1265
|
if (!n) return null;
|
|
1182
|
-
const a = parseFloat(
|
|
1183
|
-
return r < s ? { label: "This element", ratio: r, needed: s, text: `This element’s text: ${
|
|
1184
|
-
}
|
|
1185
|
-
const
|
|
1186
|
-
function
|
|
1187
|
-
return
|
|
1188
|
-
const n =
|
|
1189
|
-
return n && !n.startsWith("--") &&
|
|
1266
|
+
const a = parseFloat(t.fontSize), s = a >= 24 || a >= 18.66 && parseInt(t.fontWeight, 10) >= 700 ? Ra : Ue, r = qn(n, Ia(e));
|
|
1267
|
+
return r < s ? { label: "This element", ratio: r, needed: s, text: `This element’s text: ${Hn(r)} · AA needs ${s}` } : null;
|
|
1268
|
+
}
|
|
1269
|
+
const un = "twiddle-panel", Ua = "0.8.0", hn = "https://panel.twiddle.in", Ba = "--twd-radius-control", qa = /var\(\s*--twd-/;
|
|
1270
|
+
function Ha(e) {
|
|
1271
|
+
return e.style.cssText.split(";").some((t) => {
|
|
1272
|
+
const n = t.slice(0, t.indexOf(":")).trim();
|
|
1273
|
+
return n && !n.startsWith("--") && qa.test(t);
|
|
1190
1274
|
});
|
|
1191
1275
|
}
|
|
1192
|
-
function
|
|
1193
|
-
if (!getComputedStyle(document.documentElement).getPropertyValue(
|
|
1194
|
-
let
|
|
1195
|
-
for (const
|
|
1196
|
-
if (
|
|
1276
|
+
function Va() {
|
|
1277
|
+
if (!getComputedStyle(document.documentElement).getPropertyValue(Ba).trim()) return "no-tokens";
|
|
1278
|
+
let e = !1;
|
|
1279
|
+
for (const t of document.styleSheets) {
|
|
1280
|
+
if (t.ownerNode?.id === "twiddle-scoped") continue;
|
|
1197
1281
|
let n;
|
|
1198
1282
|
try {
|
|
1199
|
-
n =
|
|
1283
|
+
n = t.cssRules;
|
|
1200
1284
|
} catch {
|
|
1201
|
-
|
|
1285
|
+
e = !0;
|
|
1202
1286
|
continue;
|
|
1203
1287
|
}
|
|
1204
|
-
for (const a of
|
|
1288
|
+
for (const a of _e(n)) if (Ha(a)) return "ok";
|
|
1205
1289
|
}
|
|
1206
|
-
return document.querySelector('[style*="var(--twd-"]') ? "ok" :
|
|
1290
|
+
return document.querySelector('[style*="var(--twd-"]') ? "ok" : e ? "unknown" : "unused";
|
|
1207
1291
|
}
|
|
1208
|
-
function
|
|
1209
|
-
const
|
|
1210
|
-
for (const a of n.matchAll(/var\(\s*(--twd-[\w-]+)/g))
|
|
1292
|
+
function _a() {
|
|
1293
|
+
const e = /* @__PURE__ */ new Set(), t = (n) => {
|
|
1294
|
+
for (const a of n.matchAll(/var\(\s*(--twd-[\w-]+)/g)) e.add(a[1]);
|
|
1211
1295
|
};
|
|
1212
1296
|
for (const n of document.styleSheets) {
|
|
1213
1297
|
if (n.ownerNode?.id === "twiddle-scoped") continue;
|
|
@@ -1217,19 +1301,19 @@ function za() {
|
|
|
1217
1301
|
} catch {
|
|
1218
1302
|
continue;
|
|
1219
1303
|
}
|
|
1220
|
-
for (const o of
|
|
1304
|
+
for (const o of _e(a))
|
|
1221
1305
|
o.style.cssText.split(";").forEach((s) => {
|
|
1222
1306
|
const r = s.slice(0, s.indexOf(":")).trim();
|
|
1223
|
-
r && !r.startsWith("--") &&
|
|
1307
|
+
r && !r.startsWith("--") && t(s);
|
|
1224
1308
|
});
|
|
1225
1309
|
}
|
|
1226
|
-
return document.querySelectorAll('[style*="var(--twd-"]').forEach((n) =>
|
|
1310
|
+
return document.querySelectorAll('[style*="var(--twd-"]').forEach((n) => t(n.getAttribute("style"))), e;
|
|
1227
1311
|
}
|
|
1228
|
-
const
|
|
1229
|
-
{ label: "With a build tool, in your entry file", code: `import '${
|
|
1312
|
+
const fn = [
|
|
1313
|
+
{ label: "With a build tool, in your entry file", code: `import '${un}/tokens.css';` },
|
|
1230
1314
|
// Pinned to this build's version: unversioned jsDelivr links can lag a release by days, and unpkg once failed for a fresh one.
|
|
1231
|
-
{ label: "Plain HTML, in <head>", code: `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/${
|
|
1232
|
-
],
|
|
1315
|
+
{ label: "Plain HTML, in <head>", code: `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/${un}@${Ua}/dist/twiddle-tokens.css">` }
|
|
1316
|
+
], Ka = `Convert this prototype's styles to use the Panel design tokens (the --twd-* CSS custom properties from twiddle-panel), so the panel can tweak them live.
|
|
1233
1317
|
|
|
1234
1318
|
1. Keep the current look. In a :root rule loaded after twiddle-tokens.css, set each token below to the value this project already uses (for example, if buttons use #0f766e, set --twd-primary: #0f766e). Only set the tokens the project actually needs.
|
|
1235
1319
|
2. Replace fixed values in component styles with var(--twd-…). Map each value to the closest token by role:
|
|
@@ -1247,52 +1331,52 @@ const cn = [
|
|
|
1247
1331
|
3. Leave values that don't match a role as they are (layout widths, one-off illustrations, icon sizes). Don't invent new --twd-* tokens.
|
|
1248
1332
|
4. Don't change the layout or how the page looks; after the change it should look the same until someone uses the panel.
|
|
1249
1333
|
5. When done, list the files you changed and any values you left fixed, with the reason.`;
|
|
1250
|
-
function
|
|
1251
|
-
const
|
|
1252
|
-
|
|
1334
|
+
function U(e) {
|
|
1335
|
+
const t = document.createElement("div");
|
|
1336
|
+
t.className = "field";
|
|
1253
1337
|
const n = document.createElement("div");
|
|
1254
1338
|
n.className = "field-head";
|
|
1255
1339
|
const a = document.createElement("span");
|
|
1256
|
-
a.className = "field-label", a.textContent =
|
|
1340
|
+
a.className = "field-label", a.textContent = e;
|
|
1257
1341
|
const o = document.createElement("span");
|
|
1258
|
-
return o.className = "field-value", o.hidden = !0, n.append(a, o),
|
|
1259
|
-
el:
|
|
1342
|
+
return o.className = "field-value", o.hidden = !0, n.append(a, o), t.append(n), {
|
|
1343
|
+
el: t,
|
|
1260
1344
|
setValue(s) {
|
|
1261
1345
|
o.textContent = s || "", o.hidden = !s;
|
|
1262
1346
|
},
|
|
1263
1347
|
append(...s) {
|
|
1264
|
-
|
|
1348
|
+
t.append(...s);
|
|
1265
1349
|
}
|
|
1266
1350
|
};
|
|
1267
1351
|
}
|
|
1268
|
-
function
|
|
1352
|
+
function Wa({ label: e, ends: t, cols: n = 9, rows: a = 5, onInput: o }) {
|
|
1269
1353
|
const s = document.createElement("div");
|
|
1270
1354
|
s.className = "pad", s.tabIndex = 0, s.setAttribute("role", "group"), s.setAttribute("aria-roledescription", "2D slider");
|
|
1271
|
-
const r = (b,
|
|
1272
|
-
const
|
|
1273
|
-
return
|
|
1355
|
+
const r = (b, h) => {
|
|
1356
|
+
const y = document.createElement("span");
|
|
1357
|
+
return y.className = `pad-edge ${b}`, y.textContent = h, y.setAttribute("aria-hidden", "true"), y;
|
|
1274
1358
|
};
|
|
1275
|
-
s.append(r("top",
|
|
1359
|
+
s.append(r("top", t.top), r("bottom", t.bottom), r("left", t.left), r("right", t.right));
|
|
1276
1360
|
const i = document.createElement("div");
|
|
1277
1361
|
i.className = "pad-area";
|
|
1278
1362
|
for (let b = 0; b < a; b++)
|
|
1279
|
-
for (let
|
|
1280
|
-
const
|
|
1281
|
-
|
|
1363
|
+
for (let h = 0; h < n; h++) {
|
|
1364
|
+
const y = document.createElement("i");
|
|
1365
|
+
y.className = "pad-dot", y.style.left = `${h / (n - 1) * 100}%`, y.style.top = `${b / (a - 1) * 100}%`, i.append(y);
|
|
1282
1366
|
}
|
|
1283
1367
|
const l = document.createElement("span");
|
|
1284
1368
|
l.className = "pad-knob", i.append(l), s.append(i);
|
|
1285
|
-
let
|
|
1286
|
-
const
|
|
1287
|
-
function
|
|
1288
|
-
l.style.left = `${
|
|
1369
|
+
let d = 0.5, p = 0.5;
|
|
1370
|
+
const u = (b, h) => Math.round(Math.min(1, Math.max(0, b)) * (h - 1)) / (h - 1);
|
|
1371
|
+
function v() {
|
|
1372
|
+
l.style.left = `${d * 100}%`, l.style.top = `${p * 100}%`;
|
|
1289
1373
|
}
|
|
1290
|
-
function f(b,
|
|
1291
|
-
b =
|
|
1374
|
+
function f(b, h, y) {
|
|
1375
|
+
b = y === "x" ? d : u(b, n), h = y === "y" ? p : u(h, a), !(b === d && h === p) && (d = b, p = h, v(), o(d, p));
|
|
1292
1376
|
}
|
|
1293
1377
|
const m = (b) => {
|
|
1294
|
-
const
|
|
1295
|
-
f((b.clientX -
|
|
1378
|
+
const h = i.getBoundingClientRect();
|
|
1379
|
+
f((b.clientX - h.left) / h.width, (b.clientY - h.top) / h.height);
|
|
1296
1380
|
};
|
|
1297
1381
|
s.addEventListener("pointerdown", (b) => {
|
|
1298
1382
|
s.setPointerCapture(b.pointerId), s.classList.add("dragging"), m(b);
|
|
@@ -1303,149 +1387,149 @@ function Da({ label: t, ends: e, cols: n = 9, rows: a = 5, onInput: o }) {
|
|
|
1303
1387
|
s.hasPointerCapture(b.pointerId) && s.releasePointerCapture(b.pointerId), s.classList.remove("dragging");
|
|
1304
1388
|
};
|
|
1305
1389
|
return s.addEventListener("pointerup", x), s.addEventListener("pointercancel", x), s.addEventListener("keydown", (b) => {
|
|
1306
|
-
const
|
|
1307
|
-
|
|
1390
|
+
const h = { ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [0, -1], ArrowDown: [0, 1] }[b.key];
|
|
1391
|
+
h && (b.preventDefault(), h[0] ? f(u(d, n) + h[0] / (n - 1), p, "y") : f(d, u(p, a) + h[1] / (a - 1), "x"));
|
|
1308
1392
|
}), {
|
|
1309
1393
|
el: s,
|
|
1310
1394
|
/** Show a position without telling anyone (used to sync). Off-grid values are shown as they are. */
|
|
1311
|
-
setValue(b,
|
|
1312
|
-
|
|
1395
|
+
setValue(b, h, y) {
|
|
1396
|
+
d = Math.min(1, Math.max(0, b)), p = Math.min(1, Math.max(0, h)), v(), s.setAttribute("aria-label", y ? `${e}: ${y}` : e);
|
|
1313
1397
|
}
|
|
1314
1398
|
};
|
|
1315
1399
|
}
|
|
1316
|
-
function
|
|
1400
|
+
function Vn({ label: e, min: t, max: n, step: a = 1, format: o = String, onInput: s }) {
|
|
1317
1401
|
const r = document.createElement("div");
|
|
1318
1402
|
r.className = "slider";
|
|
1319
1403
|
const i = document.createElement("input");
|
|
1320
|
-
i.type = "range", i.min = String(
|
|
1404
|
+
i.type = "range", i.min = String(t), i.max = String(n), i.step = String(a), i.setAttribute("aria-label", e);
|
|
1321
1405
|
const l = document.createElement("span");
|
|
1322
1406
|
l.className = "slider-value", l.setAttribute("aria-hidden", "true"), r.append(i, l);
|
|
1323
|
-
const
|
|
1324
|
-
const
|
|
1325
|
-
i.style.setProperty("--fill", `${
|
|
1407
|
+
const d = (p) => {
|
|
1408
|
+
const u = (Number(i.value) - t) / (n - t) * 100;
|
|
1409
|
+
i.style.setProperty("--fill", `${u}%`), l.textContent = p ?? o(Number(i.value)), i.setAttribute("aria-valuetext", l.textContent);
|
|
1326
1410
|
};
|
|
1327
1411
|
return i.addEventListener("input", () => {
|
|
1328
|
-
|
|
1412
|
+
d(), s(Number(i.value));
|
|
1329
1413
|
}), {
|
|
1330
1414
|
el: r,
|
|
1331
1415
|
/** Show a value without reporting it (sync after reset / URL load). */
|
|
1332
|
-
setValue(
|
|
1333
|
-
i.value = String(
|
|
1416
|
+
setValue(p, u) {
|
|
1417
|
+
i.value = String(p), d(u);
|
|
1334
1418
|
}
|
|
1335
1419
|
};
|
|
1336
1420
|
}
|
|
1337
|
-
function
|
|
1421
|
+
function Ga({ label: e, options: t, columns: n = 3, className: a = "", onSelect: o }) {
|
|
1338
1422
|
const s = document.createElement("div");
|
|
1339
|
-
s.className = `tiles ${a}`.trim(), s.style.setProperty("--cols", n), s.setAttribute("role", "radiogroup"), s.setAttribute("aria-label",
|
|
1340
|
-
const r =
|
|
1341
|
-
const
|
|
1342
|
-
return
|
|
1423
|
+
s.className = `tiles ${a}`.trim(), s.style.setProperty("--cols", n), s.setAttribute("role", "radiogroup"), s.setAttribute("aria-label", e);
|
|
1424
|
+
const r = t.map((p) => {
|
|
1425
|
+
const u = document.createElement("button");
|
|
1426
|
+
return u.type = "button", u.className = "tile", p.wide && u.classList.add("wide"), u.setAttribute("role", "radio"), u.dataset.value = p.value, p.title && (u.title = p.title), u.innerHTML = `${p.preview ? `<span class="tile-preview" aria-hidden="true">${p.preview}</span>` : ""}<span class="tile-text"><span class="tile-label"></span>${p.sub ? '<span class="tile-sub"></span>' : ""}</span>`, u.querySelector(".tile-label").textContent = p.label, p.sub && (u.querySelector(".tile-sub").textContent = p.sub), u.addEventListener("click", () => d(p.value)), u;
|
|
1343
1427
|
});
|
|
1344
1428
|
s.append(...r);
|
|
1345
1429
|
let i = null;
|
|
1346
|
-
function l(
|
|
1347
|
-
i =
|
|
1348
|
-
const
|
|
1349
|
-
r.forEach((
|
|
1350
|
-
|
|
1430
|
+
function l(p) {
|
|
1431
|
+
i = p, r.forEach((v) => v.setAttribute("aria-checked", String(v.dataset.value === p)));
|
|
1432
|
+
const u = Math.max(0, r.findIndex((v) => v.dataset.value === p));
|
|
1433
|
+
r.forEach((v, f) => {
|
|
1434
|
+
v.tabIndex = f === u ? 0 : -1;
|
|
1351
1435
|
});
|
|
1352
1436
|
}
|
|
1353
|
-
function p
|
|
1354
|
-
|
|
1437
|
+
function d(p, u) {
|
|
1438
|
+
p !== i && (l(p), o(p)), u && r.find((v) => v.dataset.value === p).focus();
|
|
1355
1439
|
}
|
|
1356
|
-
return s.addEventListener("keydown", (
|
|
1357
|
-
const
|
|
1358
|
-
if (
|
|
1359
|
-
const
|
|
1440
|
+
return s.addEventListener("keydown", (p) => {
|
|
1441
|
+
const u = r.indexOf(p.target.closest(".tile"));
|
|
1442
|
+
if (u < 0) return;
|
|
1443
|
+
const v = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[p.key];
|
|
1360
1444
|
let f;
|
|
1361
|
-
if (
|
|
1362
|
-
else if (
|
|
1363
|
-
else if (
|
|
1445
|
+
if (v) f = (u + v + r.length) % r.length;
|
|
1446
|
+
else if (p.key === "Home") f = 0;
|
|
1447
|
+
else if (p.key === "End") f = r.length - 1;
|
|
1364
1448
|
else return;
|
|
1365
|
-
|
|
1449
|
+
p.preventDefault(), d(r[f].dataset.value, !0);
|
|
1366
1450
|
}), l(null), { el: s, setValue: l };
|
|
1367
1451
|
}
|
|
1368
|
-
const F = (
|
|
1369
|
-
function
|
|
1370
|
-
const n = /^(-?\d*\.?\d+)([a-z%]*)$/i.exec(String(
|
|
1452
|
+
const F = (e) => getComputedStyle(document.documentElement).getPropertyValue(e).trim();
|
|
1453
|
+
function Se(e, t) {
|
|
1454
|
+
const n = /^(-?\d*\.?\d+)([a-z%]*)$/i.exec(String(e).trim());
|
|
1371
1455
|
if (!n) return null;
|
|
1372
|
-
const a = parseFloat(n[1]) *
|
|
1456
|
+
const a = parseFloat(n[1]) * t, o = n[2] || "px";
|
|
1373
1457
|
return o === "px" ? `${Math.round(a)}px` : `${+a.toFixed(3)}${o}`;
|
|
1374
1458
|
}
|
|
1375
|
-
const
|
|
1376
|
-
function
|
|
1377
|
-
const
|
|
1459
|
+
const Ya = 999;
|
|
1460
|
+
function _n({ id: e, title: t, hint: n, token: a, uses: o = [], presets: s, max: r, extra: i = [], extraTokens: l = () => ({}), shape: d = "card" }) {
|
|
1461
|
+
const p = (f) => s.find((m) => m.value === f), u = (f) => f >= Ya ? "full" : `${f}px`, v = (f) => {
|
|
1378
1462
|
if (typeof f.radius == "number" && isFinite(f.radius))
|
|
1379
1463
|
return { [a]: `${Math.min(Math.max(f.radius, 0), r)}px` };
|
|
1380
|
-
const m =
|
|
1464
|
+
const m = p(f.corners);
|
|
1381
1465
|
return m ? { [a]: `${m.radius}px` } : {};
|
|
1382
1466
|
};
|
|
1383
1467
|
return {
|
|
1384
|
-
id:
|
|
1385
|
-
title:
|
|
1468
|
+
id: e,
|
|
1469
|
+
title: t,
|
|
1386
1470
|
hint: n,
|
|
1387
1471
|
presets: s,
|
|
1388
1472
|
uses: [a, ...o],
|
|
1389
1473
|
tokens(f) {
|
|
1390
|
-
const m = f[
|
|
1391
|
-
return { ...
|
|
1474
|
+
const m = f[e] || {};
|
|
1475
|
+
return { ...v(m), ...l(m) };
|
|
1392
1476
|
},
|
|
1393
1477
|
// One render function per knob, so a layout can place them separately.
|
|
1394
1478
|
// Each takes an optional label (e.g. "Buttons" on the Corners page).
|
|
1395
1479
|
parts: {
|
|
1396
1480
|
corners(f, m, { label: x = "Corners" } = {}) {
|
|
1397
|
-
const b =
|
|
1398
|
-
label: `${
|
|
1399
|
-
options: s.map((E) => ({ value: E.value, label: E.label, title:
|
|
1400
|
-
onSelect: (E) => m.setSection(
|
|
1401
|
-
}),
|
|
1402
|
-
label: `${
|
|
1481
|
+
const b = U(x), h = ce({
|
|
1482
|
+
label: `${t} corners`,
|
|
1483
|
+
options: s.map((E) => ({ value: E.value, label: E.label, title: u(E.radius) })),
|
|
1484
|
+
onSelect: (E) => m.setSection(e, { corners: E, radius: null })
|
|
1485
|
+
}), y = Vn({
|
|
1486
|
+
label: `${t} corner radius`,
|
|
1403
1487
|
min: 0,
|
|
1404
1488
|
max: r,
|
|
1405
|
-
format:
|
|
1406
|
-
onInput: (E) => m.setSection(
|
|
1489
|
+
format: u,
|
|
1490
|
+
onInput: (E) => m.setSection(e, { radius: E, corners: null })
|
|
1407
1491
|
});
|
|
1408
|
-
b.append(
|
|
1492
|
+
b.append(h.el, y.el), f.append(b.el);
|
|
1409
1493
|
const L = () => {
|
|
1410
|
-
const E = parseFloat((m.readToken || F)(a)),
|
|
1411
|
-
if (
|
|
1412
|
-
|
|
1494
|
+
const E = parseFloat((m.readToken || F)(a)), ee = m.getState()[e] || {}, K = p(ee.corners) || s.find((W) => W.radius === E);
|
|
1495
|
+
if (h.setValue(K ? K.value : null), isNaN(E)) {
|
|
1496
|
+
y.setValue(0, "—"), b.setValue("");
|
|
1413
1497
|
return;
|
|
1414
1498
|
}
|
|
1415
|
-
|
|
1499
|
+
y.setValue(Math.min(E, r), u(E)), b.setValue(u(E));
|
|
1416
1500
|
};
|
|
1417
1501
|
L(), m.onChange(L);
|
|
1418
1502
|
},
|
|
1419
|
-
...Object.fromEntries(i.map((f) => [f.key, (m, x, { label: b } = {}) => m.append(
|
|
1503
|
+
...Object.fromEntries(i.map((f) => [f.key, (m, x, { label: b } = {}) => m.append($e(x, { section: e, ...f, label: b || f.label }))])),
|
|
1420
1504
|
// Tabbed layout: presets as tiles showing the real rounding.
|
|
1421
1505
|
cornerTiles(f, m, { label: x = "Corners" } = {}) {
|
|
1422
|
-
const b = (
|
|
1506
|
+
const b = (h) => `<span class="shape shape-${d}" style="border-radius:${Math.min(h, d === "button" ? 12 : 16)}px"></span>`;
|
|
1423
1507
|
f.append(V(m, {
|
|
1424
|
-
section:
|
|
1508
|
+
section: e,
|
|
1425
1509
|
key: "corners",
|
|
1426
1510
|
label: x,
|
|
1427
1511
|
columns: s.length,
|
|
1428
|
-
options: s.map((
|
|
1429
|
-
current: (
|
|
1430
|
-
const
|
|
1431
|
-
if (
|
|
1512
|
+
options: s.map((h) => ({ value: h.value, label: h.label, title: u(h.radius), preview: b(h.radius) })),
|
|
1513
|
+
current: (h) => {
|
|
1514
|
+
const y = h[e] || {};
|
|
1515
|
+
if (y.radius != null) return null;
|
|
1432
1516
|
const L = parseFloat((m.readToken || F)(a));
|
|
1433
|
-
return (
|
|
1517
|
+
return (p(y.corners) || s.find((E) => E.radius === L))?.value || null;
|
|
1434
1518
|
},
|
|
1435
|
-
select: (
|
|
1519
|
+
select: (h) => m.setSection(e, { corners: h, radius: null }),
|
|
1436
1520
|
valueText: () => {
|
|
1437
|
-
const
|
|
1438
|
-
return isNaN(
|
|
1521
|
+
const h = parseFloat((m.readToken || F)(a));
|
|
1522
|
+
return isNaN(h) ? "" : u(h);
|
|
1439
1523
|
}
|
|
1440
1524
|
}));
|
|
1441
1525
|
},
|
|
1442
1526
|
...Object.fromEntries(i.filter((f) => f.preview).map((f) => [`${f.key}Tiles`, (m, x, { label: b } = {}) => m.append(V(x, {
|
|
1443
|
-
section:
|
|
1527
|
+
section: e,
|
|
1444
1528
|
key: f.key,
|
|
1445
1529
|
label: b || f.label,
|
|
1446
1530
|
fallback: f.fallback,
|
|
1447
1531
|
columns: f.columns || f.options.length,
|
|
1448
|
-
options: f.options.map((
|
|
1532
|
+
options: f.options.map((h) => ({ ...h, preview: f.preview(h.value) }))
|
|
1449
1533
|
}))]))
|
|
1450
1534
|
},
|
|
1451
1535
|
render(f, m) {
|
|
@@ -1453,50 +1537,50 @@ function Bn({ id: t, title: e, hint: n, token: a, uses: o = [], presets: s, max:
|
|
|
1453
1537
|
}
|
|
1454
1538
|
};
|
|
1455
1539
|
}
|
|
1456
|
-
function
|
|
1457
|
-
const i =
|
|
1540
|
+
function $e(e, { section: t, key: n, label: a, options: o, fallback: s, valueText: r }) {
|
|
1541
|
+
const i = U(a), l = ce({
|
|
1458
1542
|
label: a,
|
|
1459
1543
|
options: o,
|
|
1460
|
-
onSelect: (
|
|
1544
|
+
onSelect: (p) => e.setSection(t, { [n]: p })
|
|
1461
1545
|
});
|
|
1462
1546
|
i.append(l.el);
|
|
1463
|
-
const
|
|
1464
|
-
const
|
|
1465
|
-
l.setValue(
|
|
1547
|
+
const d = () => {
|
|
1548
|
+
const p = (e.getState()[t] || {})[n] || s;
|
|
1549
|
+
l.setValue(p), i.setValue(r ? r(p) : "");
|
|
1466
1550
|
};
|
|
1467
|
-
return
|
|
1468
|
-
}
|
|
1469
|
-
function
|
|
1470
|
-
const l =
|
|
1471
|
-
l.append(
|
|
1472
|
-
const
|
|
1473
|
-
const
|
|
1474
|
-
|
|
1551
|
+
return d(), e.onChange(d), i.el;
|
|
1552
|
+
}
|
|
1553
|
+
function Xa(e, { section: t, key: n, label: a, min: o, max: s, fallback: r, format: i }) {
|
|
1554
|
+
const l = U(a), d = Vn({ label: a, min: o, max: s, format: i, onInput: (u) => e.setSection(t, { [n]: u }) });
|
|
1555
|
+
l.append(d.el);
|
|
1556
|
+
const p = () => {
|
|
1557
|
+
const u = (e.getState()[t] || {})[n];
|
|
1558
|
+
d.setValue(typeof u == "number" ? u : r);
|
|
1475
1559
|
};
|
|
1476
|
-
return
|
|
1560
|
+
return p(), e.onChange(p), l.el;
|
|
1477
1561
|
}
|
|
1478
|
-
function V(
|
|
1479
|
-
const
|
|
1562
|
+
function V(e, { section: t, key: n, label: a, options: o, columns: s = 3, className: r = "", fallback: i, valueText: l, current: d, select: p }) {
|
|
1563
|
+
const u = U(a), v = Ga({
|
|
1480
1564
|
label: a,
|
|
1481
1565
|
options: o,
|
|
1482
1566
|
columns: s,
|
|
1483
1567
|
className: r,
|
|
1484
|
-
onSelect: (m) =>
|
|
1568
|
+
onSelect: (m) => p ? p(m) : e.setSection(t, { [n]: m })
|
|
1485
1569
|
});
|
|
1486
|
-
|
|
1570
|
+
u.append(v.el);
|
|
1487
1571
|
const f = () => {
|
|
1488
|
-
const m =
|
|
1489
|
-
|
|
1572
|
+
const m = e.getState(), x = d ? d(m) : (m[t] || {})[n] || i || null;
|
|
1573
|
+
v.setValue(x), u.setValue(l ? l(x, m) : "");
|
|
1490
1574
|
};
|
|
1491
|
-
return f(),
|
|
1575
|
+
return f(), e.onChange(f), u.el;
|
|
1492
1576
|
}
|
|
1493
|
-
const
|
|
1577
|
+
const gn = (e, t) => t.map((n) => `${e}${n}`), Kn = {
|
|
1494
1578
|
fonts: { label: "Tokens", tokens: ["font-heading", "font-body"], kind: "text" },
|
|
1495
|
-
type: { label: "Tokens", tokens:
|
|
1579
|
+
type: { label: "Tokens", tokens: gn("text-", ["xs", "sm", "base", "lg", "xl", "2xl", "3xl"]), kind: "length" },
|
|
1496
1580
|
leading: { label: "Line height", tokens: ["leading-tight", "leading-normal"], kind: "number" },
|
|
1497
1581
|
weights: { label: "Weights", tokens: ["weight-heading", "weight-strong"], kind: "number" },
|
|
1498
1582
|
colours: { label: "Tokens", tokens: ["bg", "surface", "surface-raised", "text", "text-muted", "border", "primary", "primary-hover", "on-primary", "focus", "success", "warning", "danger"], kind: "colour" },
|
|
1499
|
-
spacing: { label: "Steps", tokens:
|
|
1583
|
+
spacing: { label: "Steps", tokens: gn("space-", [1, 2, 3, 4, 5, 6, 7, 8]), kind: "length", bar: 64 },
|
|
1500
1584
|
controls: { label: "Controls", tokens: ["control-height", "control-pad-x"], kind: "length" },
|
|
1501
1585
|
corners: { label: "Tokens", tokens: ["radius-control", "radius-container"], kind: "length" },
|
|
1502
1586
|
borders: { label: "Tokens", tokens: ["border-width", "border-width-container"], kind: "length" },
|
|
@@ -1504,124 +1588,124 @@ const pn = (t, e) => e.map((n) => `${t}${n}`), Un = {
|
|
|
1504
1588
|
buttonStyle: { label: "Button", tokens: ["button-bg", "button-fg", "button-border", "button-hover"], kind: "colour" },
|
|
1505
1589
|
cardStyle: { label: "Card", tokens: ["card-bg", "card-border"], kind: "colour" },
|
|
1506
1590
|
cardShadow: { label: "Card shadow", tokens: ["card-shadow"], kind: "text" }
|
|
1507
|
-
}, _ = (
|
|
1508
|
-
function
|
|
1509
|
-
const n = /^(-?\d*\.?\d+)(px|rem)?$/.exec(String(
|
|
1510
|
-
return !n ||
|
|
1591
|
+
}, _ = (e) => `--twd-${e}`, at = (e, t) => Object.prototype.hasOwnProperty.call(e.getState().custom || {}, t), mn = (e, t) => Kn[t].tokens.some((n) => (e.custom || {})[n] != null);
|
|
1592
|
+
function Za(e, t) {
|
|
1593
|
+
const n = /^(-?\d*\.?\d+)(px|rem)?$/.exec(String(e).trim());
|
|
1594
|
+
return !n || t === "text" || t === "colour" ? null : n[2] === "rem" ? { n: +(parseFloat(n[1]) * 16).toFixed(1), unit: "px" } : { n: +parseFloat(n[1]).toFixed(3), unit: n[2] || (t === "length" ? "px" : "") };
|
|
1511
1595
|
}
|
|
1512
|
-
function
|
|
1513
|
-
const n =
|
|
1514
|
-
return n ?
|
|
1596
|
+
function Ja(e, t) {
|
|
1597
|
+
const n = e.trim();
|
|
1598
|
+
return n ? t === "length" && /^-?\d*\.?\d+$/.test(n) ? `${n}px` : n : null;
|
|
1515
1599
|
}
|
|
1516
|
-
function
|
|
1600
|
+
function Qa(e, t, n, a) {
|
|
1517
1601
|
const o = document.createElement("div");
|
|
1518
1602
|
o.className = `token-row kind-${n}`, o.innerHTML = `
|
|
1519
|
-
<code class="token-name" title="${_(
|
|
1603
|
+
<code class="token-name" title="${_(t)}">${t}</code>
|
|
1520
1604
|
${a ? '<span class="token-bar"><span></span></span>' : '<span class="token-gap"></span>'}
|
|
1521
1605
|
<span class="custom-tag" hidden>Custom</span>
|
|
1522
1606
|
<label class="token-value">
|
|
1523
1607
|
${n === "colour" ? '<span class="token-swatch"></span>' : ""}
|
|
1524
|
-
<input type="text" spellcheck="false" aria-label="${_(
|
|
1608
|
+
<input type="text" spellcheck="false" aria-label="${_(t)}" ${n === "length" || n === "number" ? 'inputmode="decimal"' : ""}>
|
|
1525
1609
|
<span class="token-unit"></span>
|
|
1526
1610
|
</label>
|
|
1527
|
-
<button class="icon-btn token-reset" title="Back to the page’s value" aria-label="Reset ${_(
|
|
1611
|
+
<button class="icon-btn token-reset" title="Back to the page’s value" aria-label="Reset ${_(t)}" hidden>${A.reset}</button>
|
|
1528
1612
|
`;
|
|
1529
|
-
const s = o.querySelector("input"), r = o.querySelector(".token-unit"), i = o.querySelector(".token-bar span"), l = o.querySelector(".token-swatch"),
|
|
1530
|
-
let
|
|
1531
|
-
const
|
|
1613
|
+
const s = o.querySelector("input"), r = o.querySelector(".token-unit"), i = o.querySelector(".token-bar span"), l = o.querySelector(".token-swatch"), d = o.querySelector(".custom-tag"), p = o.querySelector(".token-reset");
|
|
1614
|
+
let u = "";
|
|
1615
|
+
const v = () => {
|
|
1532
1616
|
let m = s.value;
|
|
1533
|
-
|
|
1534
|
-
const x =
|
|
1617
|
+
u && /^-?\d*\.?\d+$/.test(m.trim()) && (m = `${m.trim()}${u}`);
|
|
1618
|
+
const x = Ja(m, n), b = F(_(t));
|
|
1535
1619
|
if (x === null) {
|
|
1536
|
-
|
|
1620
|
+
at(e, t) ? e.setSection("custom", { [t]: null }) : f();
|
|
1537
1621
|
return;
|
|
1538
1622
|
}
|
|
1539
|
-
if (x === b && !
|
|
1540
|
-
|
|
1623
|
+
if (x === b && !at(e, t)) return f();
|
|
1624
|
+
e.setSection("custom", { [t]: x });
|
|
1541
1625
|
};
|
|
1542
|
-
s.addEventListener("change",
|
|
1626
|
+
s.addEventListener("change", v), s.addEventListener("blur", () => f()), s.addEventListener("keydown", (m) => {
|
|
1543
1627
|
m.key === "Enter" && (m.preventDefault(), s.blur()), m.key === "Escape" && (m.stopPropagation(), f(), s.blur());
|
|
1544
|
-
}),
|
|
1628
|
+
}), p.addEventListener("click", () => e.setSection("custom", { [t]: null }));
|
|
1545
1629
|
function f() {
|
|
1546
|
-
const m = F(_(
|
|
1547
|
-
|
|
1630
|
+
const m = F(_(t)), x = at(e, t), b = Za(m, n);
|
|
1631
|
+
u = b ? b.unit : "", document.activeElement !== s && o.getRootNode().activeElement !== s && (s.value = b ? String(b.n) : m), s.title = m, r.textContent = u, r.hidden = !u, i && (i.style.width = `${Math.max(2, Math.min(100, (b ? b.n : 0) / a * 100))}%`), l && (l.style.background = m), o.classList.toggle("edited", x), d.hidden = !x, p.hidden = !x;
|
|
1548
1632
|
}
|
|
1549
|
-
return f(),
|
|
1633
|
+
return f(), e.onChange(f), o;
|
|
1550
1634
|
}
|
|
1551
|
-
const
|
|
1635
|
+
const eo = {
|
|
1552
1636
|
id: "custom",
|
|
1553
1637
|
title: "Custom tokens",
|
|
1554
1638
|
hint: "Any token, set to a fixed value",
|
|
1555
1639
|
uses: [],
|
|
1556
1640
|
// not offered for a single picked element (yet)
|
|
1557
|
-
tokens(
|
|
1558
|
-
return Object.fromEntries(Object.entries(
|
|
1641
|
+
tokens(e) {
|
|
1642
|
+
return Object.fromEntries(Object.entries(e.custom || {}).map(([t, n]) => [_(t), n]));
|
|
1559
1643
|
},
|
|
1560
|
-
describe: (
|
|
1644
|
+
describe: (e, t) => ({ label: _(e), value: String(t) }),
|
|
1561
1645
|
// One part per Edit page group, e.g. parts.spacing → rows for --twd-space-1 … 8.
|
|
1562
|
-
parts: Object.fromEntries(Object.entries(
|
|
1563
|
-
const s =
|
|
1646
|
+
parts: Object.fromEntries(Object.entries(Kn).map(([e, t]) => [e, (n, a, { label: o = t.label } = {}) => {
|
|
1647
|
+
const s = U(o);
|
|
1564
1648
|
s.el.classList.add("token-field");
|
|
1565
|
-
const r =
|
|
1566
|
-
s.setValue(
|
|
1649
|
+
const r = t.tokens[0], i = t.tokens[t.tokens.length - 1];
|
|
1650
|
+
s.setValue(t.tokens.length > 2 && /-\w+$/.test(r) ? `${_(r)} … ${i.split("-").pop()}` : "");
|
|
1567
1651
|
const l = document.createElement("div");
|
|
1568
|
-
l.className = "token-rows",
|
|
1652
|
+
l.className = "token-rows", t.tokens.forEach((d) => l.append(Qa(a, d, t.kind, t.bar))), s.append(l), n.append(s.el);
|
|
1569
1653
|
}]))
|
|
1570
|
-
},
|
|
1571
|
-
function
|
|
1572
|
-
return typeof
|
|
1654
|
+
}, ve = { compact: 0.75, regular: 1, comfy: 1.25 }, kt = [0.75, 1.25], bn = [1, 2, 3, 4, 5, 6, 7, 8].map((e) => `--twd-space-${e}`);
|
|
1655
|
+
function se(e = {}) {
|
|
1656
|
+
return typeof e.factor == "number" && isFinite(e.factor) ? Math.min(kt[1], Math.max(kt[0], e.factor)) : ve[e.spacing] || 1;
|
|
1573
1657
|
}
|
|
1574
|
-
const
|
|
1658
|
+
const ot = (e) => `${Math.round(e * 100)}%`, to = {
|
|
1575
1659
|
id: "density",
|
|
1576
1660
|
title: "Density",
|
|
1577
1661
|
hint: "Compact · regular · comfortable",
|
|
1578
|
-
uses:
|
|
1579
|
-
tokens(
|
|
1580
|
-
const
|
|
1581
|
-
if (
|
|
1662
|
+
uses: bn,
|
|
1663
|
+
tokens(e) {
|
|
1664
|
+
const t = se(e.density);
|
|
1665
|
+
if (t === 1) return {};
|
|
1582
1666
|
const n = {};
|
|
1583
|
-
return
|
|
1584
|
-
const o =
|
|
1667
|
+
return bn.forEach((a) => {
|
|
1668
|
+
const o = Se(F(a), t);
|
|
1585
1669
|
o && (n[a] = o);
|
|
1586
1670
|
}), n;
|
|
1587
1671
|
},
|
|
1588
1672
|
/** How the Changes list and prompt describe a setting. */
|
|
1589
|
-
describe: (
|
|
1673
|
+
describe: (e, t) => e === "factor" ? { label: "Spacing", value: ot(t) } : null,
|
|
1590
1674
|
parts: {
|
|
1591
1675
|
// Tabbed layout: presets as tiles with a tiny layout sketch.
|
|
1592
|
-
spacingTiles(
|
|
1676
|
+
spacingTiles(e, t, { label: n = "Density" } = {}) {
|
|
1593
1677
|
const a = (o, s) => `<span class="sketch" style="padding:${s}px;gap:${o}px"><i></i><i></i><i></i></span>`;
|
|
1594
|
-
|
|
1678
|
+
e.append(V(t, {
|
|
1595
1679
|
section: "density",
|
|
1596
1680
|
key: "spacing",
|
|
1597
1681
|
label: n,
|
|
1598
1682
|
options: [{ value: "compact", label: "Compact", preview: a(2, 5) }, { value: "regular", label: "Regular", preview: a(4, 8) }, { value: "comfy", label: "Comfy", preview: a(6, 11) }],
|
|
1599
1683
|
current: (o) => {
|
|
1600
|
-
const s =
|
|
1601
|
-
return Object.keys(
|
|
1684
|
+
const s = se(o.density);
|
|
1685
|
+
return Object.keys(ve).find((r) => ve[r] === s) || null;
|
|
1602
1686
|
},
|
|
1603
|
-
select: (o) =>
|
|
1604
|
-
valueText: (o, s) =>
|
|
1687
|
+
select: (o) => t.setSection("density", { spacing: o, factor: null }),
|
|
1688
|
+
valueText: (o, s) => mn(s, "spacing") ? "Custom" : o ? "" : ot(se(s.density))
|
|
1605
1689
|
}));
|
|
1606
1690
|
},
|
|
1607
|
-
spacing(
|
|
1608
|
-
const a =
|
|
1691
|
+
spacing(e, t, { label: n = "Spacing" } = {}) {
|
|
1692
|
+
const a = U(n), o = ce({
|
|
1609
1693
|
label: n,
|
|
1610
1694
|
options: [{ value: "compact", label: "Compact" }, { value: "regular", label: "Regular" }, { value: "comfy", label: "Comfy" }],
|
|
1611
|
-
onSelect: (r) =>
|
|
1695
|
+
onSelect: (r) => t.setSection("density", { spacing: r, factor: null })
|
|
1612
1696
|
});
|
|
1613
1697
|
a.append(o.el);
|
|
1614
1698
|
const s = () => {
|
|
1615
|
-
const r =
|
|
1616
|
-
o.setValue(l || null), a.setValue(
|
|
1699
|
+
const r = t.getState().density || {}, i = se(r), l = Object.keys(ve).find((d) => ve[d] === i);
|
|
1700
|
+
o.setValue(l || null), a.setValue(mn(t.getState(), "spacing") ? "Custom" : l ? "" : ot(i));
|
|
1617
1701
|
};
|
|
1618
|
-
s(),
|
|
1702
|
+
s(), t.onChange(s), e.append(a.el);
|
|
1619
1703
|
}
|
|
1620
1704
|
},
|
|
1621
|
-
render(
|
|
1622
|
-
Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(
|
|
1705
|
+
render(e, t) {
|
|
1706
|
+
Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(e, t));
|
|
1623
1707
|
}
|
|
1624
|
-
},
|
|
1708
|
+
}, no = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 6l4 4M4 20l1-4 9.5-9.5a2.1 2.1 0 013 3L8 19l-4 1z"/></svg>', ao = [
|
|
1625
1709
|
[50, "white", 10],
|
|
1626
1710
|
[100, "white", 20],
|
|
1627
1711
|
[200, "white", 36],
|
|
@@ -1633,11 +1717,11 @@ const tt = (t) => `${Math.round(t * 100)}%`, Ga = {
|
|
|
1633
1717
|
[800, "black", 58],
|
|
1634
1718
|
[900, "black", 45],
|
|
1635
1719
|
[950, "black", 30]
|
|
1636
|
-
],
|
|
1637
|
-
let
|
|
1638
|
-
return /^[0-9a-f]{3}$/i.test(
|
|
1720
|
+
], rt = (e) => {
|
|
1721
|
+
let t = String(e).trim().replace(/^#/, "");
|
|
1722
|
+
return /^[0-9a-f]{3}$/i.test(t) && (t = t.split("").map((n) => n + n).join("")), /^[0-9a-f]{6}$/i.test(t) ? `#${t.toUpperCase()}` : null;
|
|
1639
1723
|
};
|
|
1640
|
-
function
|
|
1724
|
+
function oo({ label: e, onInput: t }) {
|
|
1641
1725
|
const n = document.createElement("div");
|
|
1642
1726
|
n.className = "colour";
|
|
1643
1727
|
const a = document.createElement("div");
|
|
@@ -1645,21 +1729,21 @@ function Za({ label: t, onInput: e }) {
|
|
|
1645
1729
|
const o = document.createElement("input");
|
|
1646
1730
|
o.type = "color", o.className = "colour-picker", o.tabIndex = -1, o.setAttribute("aria-hidden", "true");
|
|
1647
1731
|
const s = document.createElement("button");
|
|
1648
|
-
s.type = "button", s.className = "colour-swatch", s.setAttribute("aria-label", `${
|
|
1732
|
+
s.type = "button", s.className = "colour-swatch", s.setAttribute("aria-label", `${e}: open colour picker`);
|
|
1649
1733
|
const r = document.createElement("input");
|
|
1650
|
-
r.type = "text", r.className = "colour-hex", r.spellcheck = !1, r.maxLength = 7, r.setAttribute("aria-label", `${
|
|
1734
|
+
r.type = "text", r.className = "colour-hex", r.spellcheck = !1, r.maxLength = 7, r.setAttribute("aria-label", `${e} hex value`);
|
|
1651
1735
|
const i = document.createElement("button");
|
|
1652
|
-
i.type = "button", i.className = "colour-pipette", i.innerHTML =
|
|
1736
|
+
i.type = "button", i.className = "colour-pipette", i.innerHTML = no, i.title = "Pick a colour from the screen", i.setAttribute("aria-label", "Pick a colour from the screen"), a.append(o, s, r, i);
|
|
1653
1737
|
const l = document.createElement("div");
|
|
1654
|
-
l.className = "ramp", l.setAttribute("aria-hidden", "true"), l.innerHTML =
|
|
1738
|
+
l.className = "ramp", l.setAttribute("aria-hidden", "true"), l.innerHTML = ao.map(([f, m, x]) => {
|
|
1655
1739
|
const b = m ? `color-mix(in oklch, var(--brand) ${x}%, ${m})` : "var(--brand)";
|
|
1656
1740
|
return `<span title="${f}" style="background:${b}"></span>`;
|
|
1657
1741
|
}).join(""), n.append(a, l);
|
|
1658
|
-
let
|
|
1659
|
-
const
|
|
1660
|
-
|
|
1661
|
-
},
|
|
1662
|
-
f !==
|
|
1742
|
+
let d = "#000000";
|
|
1743
|
+
const p = (f) => {
|
|
1744
|
+
d = f, o.value = f.toLowerCase(), s.style.background = f, n.getRootNode().activeElement !== r && (r.value = f), n.style.setProperty("--brand", f);
|
|
1745
|
+
}, u = (f) => {
|
|
1746
|
+
f !== d && (p(f), t(f));
|
|
1663
1747
|
};
|
|
1664
1748
|
s.addEventListener("click", () => {
|
|
1665
1749
|
if (o.showPicker)
|
|
@@ -1669,64 +1753,64 @@ function Za({ label: t, onInput: e }) {
|
|
|
1669
1753
|
} catch {
|
|
1670
1754
|
}
|
|
1671
1755
|
o.click();
|
|
1672
|
-
}), o.addEventListener("input", () =>
|
|
1673
|
-
const f =
|
|
1674
|
-
f &&
|
|
1756
|
+
}), o.addEventListener("input", () => u(o.value.toUpperCase())), r.addEventListener("input", () => {
|
|
1757
|
+
const f = rt(r.value);
|
|
1758
|
+
f && u(f);
|
|
1675
1759
|
});
|
|
1676
|
-
const
|
|
1677
|
-
r.value =
|
|
1760
|
+
const v = () => {
|
|
1761
|
+
r.value = d;
|
|
1678
1762
|
};
|
|
1679
|
-
return r.addEventListener("blur",
|
|
1680
|
-
f.key === "Enter" && (
|
|
1763
|
+
return r.addEventListener("blur", v), r.addEventListener("keydown", (f) => {
|
|
1764
|
+
f.key === "Enter" && (v(), r.select());
|
|
1681
1765
|
}), i.addEventListener("click", async () => {
|
|
1682
1766
|
if ("EyeDropper" in window)
|
|
1683
1767
|
try {
|
|
1684
|
-
const { sRGBHex: f } = await new window.EyeDropper().open(), m =
|
|
1685
|
-
m &&
|
|
1768
|
+
const { sRGBHex: f } = await new window.EyeDropper().open(), m = rt(f);
|
|
1769
|
+
m && u(m);
|
|
1686
1770
|
} catch {
|
|
1687
1771
|
}
|
|
1688
1772
|
else
|
|
1689
1773
|
s.click();
|
|
1690
|
-
}), { el: n, setValue: (f) =>
|
|
1774
|
+
}), { el: n, setValue: (f) => p(rt(f) || d) };
|
|
1691
1775
|
}
|
|
1692
|
-
function
|
|
1776
|
+
function ro({ label: e, options: t, onSelect: n }) {
|
|
1693
1777
|
const a = document.createElement("div");
|
|
1694
|
-
a.className = "swatches", a.setAttribute("role", "radiogroup"), a.setAttribute("aria-label",
|
|
1695
|
-
const o =
|
|
1696
|
-
const
|
|
1697
|
-
return
|
|
1778
|
+
a.className = "swatches", a.setAttribute("role", "radiogroup"), a.setAttribute("aria-label", e);
|
|
1779
|
+
const o = t.map((l) => {
|
|
1780
|
+
const d = document.createElement("button");
|
|
1781
|
+
return d.type = "button", d.className = "swatch", d.setAttribute("role", "radio"), d.dataset.value = l.value, d.innerHTML = `<span class="swatch-ring"><span class="swatch-dot" style="background:${l.colour}"></span></span><span class="swatch-label">${l.label}</span>`, d.addEventListener("click", () => i(l.value)), d;
|
|
1698
1782
|
});
|
|
1699
1783
|
a.append(...o);
|
|
1700
1784
|
let s = null;
|
|
1701
1785
|
function r(l) {
|
|
1702
|
-
s = l, o.forEach((
|
|
1703
|
-
const
|
|
1704
|
-
o.forEach((
|
|
1705
|
-
|
|
1786
|
+
s = l, o.forEach((p) => p.setAttribute("aria-checked", String(p.dataset.value === l)));
|
|
1787
|
+
const d = Math.max(0, o.findIndex((p) => p.dataset.value === l));
|
|
1788
|
+
o.forEach((p, u) => {
|
|
1789
|
+
p.tabIndex = u === d ? 0 : -1;
|
|
1706
1790
|
});
|
|
1707
1791
|
}
|
|
1708
|
-
function i(l,
|
|
1709
|
-
l !== s && (r(l), n(l)),
|
|
1792
|
+
function i(l, d) {
|
|
1793
|
+
l !== s && (r(l), n(l)), d && o.find((p) => p.dataset.value === l).focus();
|
|
1710
1794
|
}
|
|
1711
1795
|
return a.addEventListener("keydown", (l) => {
|
|
1712
|
-
const
|
|
1713
|
-
if (
|
|
1714
|
-
const
|
|
1715
|
-
let
|
|
1716
|
-
if (
|
|
1717
|
-
else if (l.key === "Home")
|
|
1718
|
-
else if (l.key === "End")
|
|
1796
|
+
const d = o.indexOf(l.target.closest(".swatch"));
|
|
1797
|
+
if (d < 0) return;
|
|
1798
|
+
const p = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[l.key];
|
|
1799
|
+
let u;
|
|
1800
|
+
if (p) u = (d + p + o.length) % o.length;
|
|
1801
|
+
else if (l.key === "Home") u = 0;
|
|
1802
|
+
else if (l.key === "End") u = o.length - 1;
|
|
1719
1803
|
else return;
|
|
1720
|
-
l.preventDefault(), i(o[
|
|
1804
|
+
l.preventDefault(), i(o[u].dataset.value, !0);
|
|
1721
1805
|
}), r(null), { el: a, setValue: r };
|
|
1722
1806
|
}
|
|
1723
|
-
const
|
|
1724
|
-
slate:
|
|
1725
|
-
gray:
|
|
1726
|
-
zinc:
|
|
1727
|
-
neutral:
|
|
1728
|
-
stone:
|
|
1729
|
-
}, N = (
|
|
1807
|
+
const so = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950], xe = (e) => Object.fromEntries(e.split(" ").map((t, n) => [so[n], `#${t}`])), st = {
|
|
1808
|
+
slate: xe("f8fafc f1f5f9 e2e8f0 cbd5e1 94a3b8 64748b 475569 334155 1e293b 0f172a 020617"),
|
|
1809
|
+
gray: xe("f9fafb f3f4f6 e5e7eb d1d5db 9ca3af 6b7280 4b5563 374151 1f2937 111827 030712"),
|
|
1810
|
+
zinc: xe("fafafa f4f4f5 e4e4e7 d4d4d8 a1a1aa 71717a 52525b 3f3f46 27272a 18181b 09090b"),
|
|
1811
|
+
neutral: xe("fafafa f5f5f5 e5e5e5 d4d4d4 a3a3a3 737373 525252 404040 262626 171717 0a0a0a"),
|
|
1812
|
+
stone: xe("fafaf9 f5f5f4 e7e5e4 d6d3d1 a8a29e 78716c 57534e 44403c 292524 1c1917 0c0a09")
|
|
1813
|
+
}, N = (e) => Object.fromEntries(e.split(" ").map((t, n) => [400 + 100 * n, `#${t}`])), it = {
|
|
1730
1814
|
red: N("f87171 ef4444 dc2626 b91c1c 991b1b"),
|
|
1731
1815
|
orange: N("fb923c f97316 ea580c c2410c 9a3412"),
|
|
1732
1816
|
amber: N("fbbf24 f59e0b d97706 b45309 92400e"),
|
|
@@ -1744,17 +1828,17 @@ const Ja = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950], me = (t) => O
|
|
|
1744
1828
|
fuchsia: N("e879f9 d946ef c026d3 a21caf 86198f"),
|
|
1745
1829
|
pink: N("f472b6 ec4899 db2777 be185d 9d174d"),
|
|
1746
1830
|
rose: N("fb7185 f43f5e e11d48 be123c 9f1239")
|
|
1747
|
-
},
|
|
1831
|
+
}, Be = [
|
|
1748
1832
|
{ value: "indigo-zinc", label: "Indigo · Zinc", primary: "indigo", neutral: "zinc" },
|
|
1749
1833
|
{ value: "emerald-stone", label: "Emerald · Stone", primary: "emerald", neutral: "stone" },
|
|
1750
1834
|
{ value: "rose-slate", label: "Rose · Slate", primary: "rose", neutral: "slate" },
|
|
1751
1835
|
{ value: "amber-neutral", label: "Amber · Neutral", primary: "amber", neutral: "neutral" }
|
|
1752
|
-
],
|
|
1836
|
+
], xn = "#4F46E5", io = "#A0743C", lo = [
|
|
1753
1837
|
// Figma Swatch row sample colours
|
|
1754
1838
|
{ value: "pure", label: "Pure", colour: "#8E8E93" },
|
|
1755
1839
|
{ value: "subtle", label: "Subtle", colour: "#8A89A6" },
|
|
1756
1840
|
{ value: "warm", label: "Warm", colour: "#9C9389" }
|
|
1757
|
-
],
|
|
1841
|
+
], wn = ["bg", "surface", "surface-raised", "text", "text-muted", "border"], co = {
|
|
1758
1842
|
// proposal
|
|
1759
1843
|
bg: "#0F0F11",
|
|
1760
1844
|
surface: "#18181B",
|
|
@@ -1762,56 +1846,56 @@ const Ja = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950], me = (t) => O
|
|
|
1762
1846
|
text: "#F4F4F5",
|
|
1763
1847
|
"text-muted": "#A1A1AA",
|
|
1764
1848
|
border: "#2E2E33"
|
|
1765
|
-
},
|
|
1766
|
-
function
|
|
1767
|
-
const
|
|
1849
|
+
}, po = { bg: 4, surface: 2, "surface-raised": 2, text: 5, "text-muted": 12, border: 10 }, X = (e) => /^#[0-9a-f]{6}$/i.test(e || ""), lt = (e) => [500, 600, 700].includes(Number(e.shade)) ? Number(e.shade) : 600, dt = (e) => e[0].toUpperCase() + e.slice(1);
|
|
1850
|
+
function yn(e) {
|
|
1851
|
+
const t = (l) => (l /= 255, l <= 0.03928 ? l / 12.92 : ((l + 0.055) / 1.055) ** 2.4), [n, a, o] = [1, 3, 5].map((l) => t(parseInt(e.slice(l, l + 2), 16))), s = 0.2126 * n + 0.7152 * a + 0.0722 * o, r = 1.05 / (s + 0.05), i = (s + 0.05) / (98e-4 + 0.05);
|
|
1768
1852
|
return r >= i ? "#FFFFFF" : "#18181B";
|
|
1769
1853
|
}
|
|
1770
|
-
const
|
|
1854
|
+
const He = {
|
|
1771
1855
|
id: "colour",
|
|
1772
1856
|
title: "Colour",
|
|
1773
1857
|
hint: "Brand colour · neutral tint · mode",
|
|
1774
|
-
uses: ["--twd-primary", "--twd-primary-hover", "--twd-on-primary", "--twd-focus", ...
|
|
1775
|
-
tokens(
|
|
1776
|
-
const
|
|
1777
|
-
if (!
|
|
1778
|
-
const i =
|
|
1779
|
-
n["--twd-primary"] = o[i], n["--twd-primary-hover"] = o[a ? i - 100 : i + 100], n["--twd-on-primary"] =
|
|
1858
|
+
uses: ["--twd-primary", "--twd-primary-hover", "--twd-on-primary", "--twd-focus", ...wn.map((e) => `--twd-${e}`)],
|
|
1859
|
+
tokens(e) {
|
|
1860
|
+
const t = e.colour || {}, n = {}, a = t.mode === "dark", o = it[t.primary];
|
|
1861
|
+
if (!X(t.brand) && o) {
|
|
1862
|
+
const i = lt(t);
|
|
1863
|
+
n["--twd-primary"] = o[i], n["--twd-primary-hover"] = o[a ? i - 100 : i + 100], n["--twd-on-primary"] = yn(o[i]), n["--twd-focus"] = o[500];
|
|
1780
1864
|
}
|
|
1781
|
-
|
|
1782
|
-
const s =
|
|
1865
|
+
X(t.brand) && (n["--twd-primary"] = t.brand, n["--twd-primary-hover"] = a ? `color-mix(in oklch, ${t.brand} 85%, white)` : `color-mix(in oklch, ${t.brand} 86%, black)`, n["--twd-on-primary"] = yn(t.brand), n["--twd-focus"] = t.brand);
|
|
1866
|
+
const s = t.tint === "subtle" ? X(t.brand) ? t.brand : F("--twd-primary") || xn : t.tint === "warm" ? io : null, r = st[t.neutral];
|
|
1783
1867
|
if (r) {
|
|
1784
1868
|
const i = a ? { bg: r[950], surface: r[900], "surface-raised": r[800], text: r[100], "text-muted": r[400], border: r[800] } : { bg: r[50], surface: "#ffffff", "surface-raised": "#ffffff", text: r[900], "text-muted": r[500], border: r[200] };
|
|
1785
|
-
Object.entries(i).forEach(([l,
|
|
1786
|
-
n[`--twd-${l}`] =
|
|
1869
|
+
Object.entries(i).forEach(([l, d]) => {
|
|
1870
|
+
n[`--twd-${l}`] = d;
|
|
1787
1871
|
});
|
|
1788
|
-
} else (a || s) &&
|
|
1789
|
-
const l = a ?
|
|
1790
|
-
l && (n[`--twd-${i}`] = s ? `color-mix(in oklch, ${l} ${100 -
|
|
1872
|
+
} else (a || s) && wn.forEach((i) => {
|
|
1873
|
+
const l = a ? co[i] : F(`--twd-${i}`);
|
|
1874
|
+
l && (n[`--twd-${i}`] = s ? `color-mix(in oklch, ${l} ${100 - po[i]}%, ${s})` : l);
|
|
1791
1875
|
});
|
|
1792
1876
|
return n;
|
|
1793
1877
|
},
|
|
1794
1878
|
parts: {
|
|
1795
1879
|
// ---- Tabbed layout (Colour tab): themes, then Tailwind fine-tuning ----
|
|
1796
|
-
themeTiles(
|
|
1880
|
+
themeTiles(e, t, { label: n = "Theme" } = {}) {
|
|
1797
1881
|
const a = (o) => {
|
|
1798
|
-
const s =
|
|
1882
|
+
const s = st[o.neutral], r = it[o.primary];
|
|
1799
1883
|
return `<span class="theme" style="--bg:${s[50]};--line:${s[200]};--ink:${s[900]};--soft:${s[500]};--pri:${r[600]}"><i><b></b><s></s><u></u></i></span><span class="palette">${[r[400], r[500], r[600], r[700], r[800], s[200], s[500], s[900]].map((i) => `<i style="background:${i}"></i>`).join("")}</span>`;
|
|
1800
1884
|
};
|
|
1801
|
-
|
|
1885
|
+
e.append(V(t, {
|
|
1802
1886
|
section: "colour",
|
|
1803
1887
|
key: "theme",
|
|
1804
1888
|
label: n,
|
|
1805
1889
|
columns: 2,
|
|
1806
1890
|
className: "tiles-themes",
|
|
1807
|
-
options:
|
|
1891
|
+
options: Be.map((o) => ({ value: o.value, label: o.label, preview: a(o) })),
|
|
1808
1892
|
current: (o) => {
|
|
1809
1893
|
const s = o.colour || {};
|
|
1810
|
-
return
|
|
1894
|
+
return X(s.brand) ? null : Be.find((r) => r.primary === s.primary && r.neutral === s.neutral)?.value || null;
|
|
1811
1895
|
},
|
|
1812
1896
|
select: (o) => {
|
|
1813
|
-
const s =
|
|
1814
|
-
|
|
1897
|
+
const s = Be.find((r) => r.value === o);
|
|
1898
|
+
t.setSection("colour", { primary: s.primary, neutral: s.neutral, shade: null, brand: null, tint: null });
|
|
1815
1899
|
},
|
|
1816
1900
|
valueText: (o, s) => {
|
|
1817
1901
|
const r = s.colour || {};
|
|
@@ -1819,61 +1903,61 @@ const Be = {
|
|
|
1819
1903
|
}
|
|
1820
1904
|
}));
|
|
1821
1905
|
},
|
|
1822
|
-
primary(
|
|
1823
|
-
const a =
|
|
1906
|
+
primary(e, t, { label: n = "Primary" } = {}) {
|
|
1907
|
+
const a = U(n), o = V(t, {
|
|
1824
1908
|
section: "colour",
|
|
1825
1909
|
key: "primary",
|
|
1826
1910
|
label: `${n} colour family`,
|
|
1827
1911
|
columns: 9,
|
|
1828
1912
|
className: "tiles-swatches",
|
|
1829
|
-
options: Object.entries(
|
|
1913
|
+
options: Object.entries(it).map(([i, l]) => ({ value: i, label: dt(i), title: dt(i), preview: `<span class="dot" style="background:${l[500]}"></span>` })),
|
|
1830
1914
|
current: (i) => {
|
|
1831
1915
|
const l = i.colour || {};
|
|
1832
|
-
return
|
|
1916
|
+
return X(l.brand) ? null : l.primary || null;
|
|
1833
1917
|
},
|
|
1834
|
-
select: (i) =>
|
|
1918
|
+
select: (i) => t.setSection("colour", { primary: i, brand: null })
|
|
1835
1919
|
});
|
|
1836
1920
|
o.querySelector(".field-head").remove();
|
|
1837
|
-
const s =
|
|
1921
|
+
const s = ce({ label: "Shade", options: [500, 600, 700].map((i) => ({ value: String(i), label: String(i) })), onSelect: (i) => t.setSection("colour", { shade: Number(i) }) });
|
|
1838
1922
|
a.append(o, s.el);
|
|
1839
1923
|
const r = () => {
|
|
1840
|
-
const i =
|
|
1841
|
-
s.setValue(String(
|
|
1924
|
+
const i = t.getState().colour || {};
|
|
1925
|
+
s.setValue(String(lt(i))), a.setValue(X(i.brand) ? i.brand.toLowerCase() : i.primary ? `${i.primary}-${lt(i)}` : "");
|
|
1842
1926
|
};
|
|
1843
|
-
r(),
|
|
1927
|
+
r(), t.onChange(r), e.append(a.el);
|
|
1844
1928
|
},
|
|
1845
|
-
neutralTiles(
|
|
1846
|
-
|
|
1929
|
+
neutralTiles(e, t, { label: n = "Neutral" } = {}) {
|
|
1930
|
+
e.append(V(t, {
|
|
1847
1931
|
section: "colour",
|
|
1848
1932
|
key: "neutral",
|
|
1849
1933
|
label: n,
|
|
1850
1934
|
columns: 5,
|
|
1851
1935
|
className: "tiles-greys",
|
|
1852
|
-
options: Object.entries(
|
|
1853
|
-
select: (a) =>
|
|
1936
|
+
options: Object.entries(st).map(([a, o]) => ({ value: a, label: dt(a), preview: `<span class="greys">${[o[50], o[200], o[500], o[900]].map((s) => `<i style="background:${s}"></i>`).join("")}</span>` })),
|
|
1937
|
+
select: (a) => t.setSection("colour", { neutral: a, tint: null }),
|
|
1854
1938
|
valueText: (a) => a || ""
|
|
1855
1939
|
}));
|
|
1856
1940
|
},
|
|
1857
|
-
custom(
|
|
1941
|
+
custom(e, t) {
|
|
1858
1942
|
const n = document.createElement("details");
|
|
1859
1943
|
n.className = "more more-custom", n.innerHTML = '<summary><span class="rainbow" aria-hidden="true"></span><span>Custom colour…</span><span class="muted">any hex</span></summary>';
|
|
1860
1944
|
const a = document.createElement("div");
|
|
1861
|
-
a.className = "more-body", n.append(a),
|
|
1945
|
+
a.className = "more-body", n.append(a), He.parts.brand(a, t, { label: "Brand" }), X((t.getState().colour || {}).brand) && (n.open = !0), e.append(n);
|
|
1862
1946
|
},
|
|
1863
|
-
brand(
|
|
1864
|
-
const a =
|
|
1947
|
+
brand(e, t, { label: n = "Brand" } = {}) {
|
|
1948
|
+
const a = U(n), o = oo({ label: "Brand colour", onInput: (r) => t.setSection("colour", { brand: r }) });
|
|
1865
1949
|
a.append(o.el);
|
|
1866
|
-
const s = () => o.setValue((
|
|
1867
|
-
s(),
|
|
1950
|
+
const s = () => o.setValue((t.getState().colour || {}).brand || (t.readToken || F)("--twd-primary") || xn);
|
|
1951
|
+
s(), t.onChange(s), e.append(a.el);
|
|
1868
1952
|
},
|
|
1869
|
-
neutral(
|
|
1870
|
-
const a =
|
|
1953
|
+
neutral(e, t, { label: n = "Neutral" } = {}) {
|
|
1954
|
+
const a = U(n), o = ro({ label: "Neutral tint", options: lo, onSelect: (r) => t.setSection("colour", { tint: r }) });
|
|
1871
1955
|
a.append(o.el);
|
|
1872
|
-
const s = () => o.setValue((
|
|
1873
|
-
s(),
|
|
1956
|
+
const s = () => o.setValue((t.getState().colour || {}).tint || "pure");
|
|
1957
|
+
s(), t.onChange(s), e.append(a.el);
|
|
1874
1958
|
},
|
|
1875
|
-
mode(
|
|
1876
|
-
|
|
1959
|
+
mode(e, t, { label: n = "Mode" } = {}) {
|
|
1960
|
+
e.append($e(t, {
|
|
1877
1961
|
section: "colour",
|
|
1878
1962
|
key: "mode",
|
|
1879
1963
|
label: n,
|
|
@@ -1882,15 +1966,15 @@ const Be = {
|
|
|
1882
1966
|
}));
|
|
1883
1967
|
}
|
|
1884
1968
|
},
|
|
1885
|
-
render(
|
|
1886
|
-
["brand", "neutral", "mode"].forEach((n) => this.parts[n](
|
|
1969
|
+
render(e, t) {
|
|
1970
|
+
["brand", "neutral", "mode"].forEach((n) => this.parts[n](e, t));
|
|
1887
1971
|
}
|
|
1888
|
-
},
|
|
1972
|
+
}, Ne = (e, t = "var(--twd-surface)") => `color-mix(in oklch, var(--twd-primary) ${e}%, ${t})`, uo = {
|
|
1889
1973
|
// proposal recipes; Solid leaves tokens.css defaults
|
|
1890
|
-
soft: { bg:
|
|
1891
|
-
outline: { bg: "transparent", fg: "var(--twd-primary)", border: "var(--twd-primary)", hover:
|
|
1892
|
-
ghost: { bg: "transparent", fg: "var(--twd-primary)", border: "transparent", hover:
|
|
1893
|
-
},
|
|
1974
|
+
soft: { bg: Ne(14), fg: "var(--twd-primary)", border: "transparent", hover: Ne(22) },
|
|
1975
|
+
outline: { bg: "transparent", fg: "var(--twd-primary)", border: "var(--twd-primary)", hover: Ne(8, "transparent") },
|
|
1976
|
+
ghost: { bg: "transparent", fg: "var(--twd-primary)", border: "transparent", hover: Ne(10, "transparent") }
|
|
1977
|
+
}, ho = { small: { height: 0.8, pad: 0.75 }, large: { height: 1.2, pad: 1.25 } }, Wn = _n({
|
|
1894
1978
|
id: "buttons",
|
|
1895
1979
|
title: "Buttons",
|
|
1896
1980
|
hint: "Style · corners · size",
|
|
@@ -1909,15 +1993,15 @@ const Be = {
|
|
|
1909
1993
|
// Figma: "8px"
|
|
1910
1994
|
{ value: "pill", label: "Pill", radius: 999 }
|
|
1911
1995
|
],
|
|
1912
|
-
extraTokens(
|
|
1913
|
-
const
|
|
1996
|
+
extraTokens(e) {
|
|
1997
|
+
const t = {}, n = uo[e.style];
|
|
1914
1998
|
n && Object.entries(n).forEach(([r, i]) => {
|
|
1915
|
-
|
|
1999
|
+
t[`--twd-button-${r}`] = i;
|
|
1916
2000
|
});
|
|
1917
|
-
const a =
|
|
1918
|
-
if (!a) return
|
|
1919
|
-
const o =
|
|
1920
|
-
return o && (
|
|
2001
|
+
const a = ho[e.size];
|
|
2002
|
+
if (!a) return t;
|
|
2003
|
+
const o = Se(F("--twd-control-height"), a.height), s = Se(F("--twd-control-pad-x"), a.pad);
|
|
2004
|
+
return o && (t["--twd-control-height"] = o), s && (t["--twd-control-pad-x"] = s), t;
|
|
1921
2005
|
},
|
|
1922
2006
|
extra: [
|
|
1923
2007
|
{
|
|
@@ -1925,23 +2009,23 @@ const Be = {
|
|
|
1925
2009
|
label: "Style",
|
|
1926
2010
|
fallback: "solid",
|
|
1927
2011
|
columns: 4,
|
|
1928
|
-
preview: (
|
|
2012
|
+
preview: (e) => `<span class="shape shape-button style-${e}"></span>`,
|
|
1929
2013
|
options: [{ value: "solid", label: "Solid" }, { value: "soft", label: "Soft" }, { value: "outline", label: "Outline" }, { value: "ghost", label: "Ghost" }]
|
|
1930
2014
|
},
|
|
1931
2015
|
{
|
|
1932
2016
|
key: "size",
|
|
1933
2017
|
label: "Size",
|
|
1934
2018
|
fallback: "medium",
|
|
1935
|
-
preview: (
|
|
2019
|
+
preview: (e) => `<span class="shape shape-button size-${e}"></span>`,
|
|
1936
2020
|
options: [{ value: "small", label: "Small" }, { value: "medium", label: "Medium" }, { value: "large", label: "Large" }]
|
|
1937
2021
|
}
|
|
1938
2022
|
]
|
|
1939
|
-
}),
|
|
2023
|
+
}), fo = {
|
|
1940
2024
|
// proposal recipes; Filled leaves tokens.css defaults
|
|
1941
2025
|
outlined: { bg: "transparent", border: "color-mix(in oklch, var(--twd-border) 60%, var(--twd-text))", shadow: "none" },
|
|
1942
2026
|
elevated: { bg: "var(--twd-surface-raised)", border: "transparent", shadow: "var(--twd-shadow-md)" },
|
|
1943
2027
|
tinted: { bg: "color-mix(in oklch, var(--twd-primary) 8%, var(--twd-surface))", border: "color-mix(in oklch, var(--twd-primary) 25%, var(--twd-surface))", shadow: "none" }
|
|
1944
|
-
},
|
|
2028
|
+
}, St = _n({
|
|
1945
2029
|
id: "cards",
|
|
1946
2030
|
title: "Cards",
|
|
1947
2031
|
hint: "Style · corners · border",
|
|
@@ -1960,15 +2044,15 @@ const Be = {
|
|
|
1960
2044
|
{ value: "large", label: "Large", radius: 20 }
|
|
1961
2045
|
// proposal (cards don't go fully pill)
|
|
1962
2046
|
],
|
|
1963
|
-
extraTokens(
|
|
1964
|
-
const
|
|
2047
|
+
extraTokens(e) {
|
|
2048
|
+
const t = {}, n = fo[e.style];
|
|
1965
2049
|
if (n && Object.entries(n).forEach(([a, o]) => {
|
|
1966
|
-
|
|
1967
|
-
}),
|
|
1968
|
-
const a =
|
|
1969
|
-
a && (
|
|
2050
|
+
t[`--twd-card-${a}`] = o;
|
|
2051
|
+
}), e.border === "none" && (t["--twd-border-width-container"] = "0px"), e.border === "thick") {
|
|
2052
|
+
const a = Se(F("--twd-border-width-container"), 2);
|
|
2053
|
+
a && (t["--twd-border-width-container"] = a);
|
|
1970
2054
|
}
|
|
1971
|
-
return
|
|
2055
|
+
return t;
|
|
1972
2056
|
},
|
|
1973
2057
|
extra: [
|
|
1974
2058
|
{
|
|
@@ -1976,49 +2060,49 @@ const Be = {
|
|
|
1976
2060
|
label: "Style",
|
|
1977
2061
|
fallback: "filled",
|
|
1978
2062
|
columns: 4,
|
|
1979
|
-
preview: (
|
|
2063
|
+
preview: (e) => `<span class="shape shape-card style-${e}"></span>`,
|
|
1980
2064
|
options: [{ value: "filled", label: "Filled" }, { value: "outlined", label: "Outlined" }, { value: "elevated", label: "Elevated" }, { value: "tinted", label: "Tinted" }]
|
|
1981
2065
|
},
|
|
1982
2066
|
{
|
|
1983
2067
|
key: "border",
|
|
1984
2068
|
label: "Border",
|
|
1985
2069
|
fallback: "thin",
|
|
1986
|
-
preview: (
|
|
2070
|
+
preview: (e) => `<span class="shape shape-card border-${e}"></span>`,
|
|
1987
2071
|
options: [{ value: "none", label: "None" }, { value: "thin", label: "Thin" }, { value: "thick", label: "Thick" }]
|
|
1988
2072
|
}
|
|
1989
2073
|
]
|
|
1990
|
-
}),
|
|
2074
|
+
}), ie = 60, ct = {
|
|
1991
2075
|
soft: { sm: [0, 1, 2, 0, 0.06], md: [0, 4, 12, 0, 0.08], lg: [0, 12, 32, 0, 0.12] },
|
|
1992
2076
|
// = tokens.css
|
|
1993
2077
|
crisp: { sm: [0, 1, 1, 0, 0.12], md: [0, 2, 4, 0, 0.14], lg: [0, 4, 8, 0, 0.16] },
|
|
1994
2078
|
bold: { sm: [2, 2, 0, 0, 0.85], md: [3, 3, 0, 0, 0.85], lg: [5, 5, 0, 0, 0.85] }
|
|
1995
|
-
},
|
|
2079
|
+
}, je = ([e, t, n, a, o], s) => {
|
|
1996
2080
|
const r = Math.min(1, +(o * s).toFixed(3));
|
|
1997
|
-
return r <= 0 ? "none" : `${
|
|
1998
|
-
},
|
|
2081
|
+
return r <= 0 ? "none" : `${e}px ${t}px ${n}px ${a}px rgb(0 0 0 / ${r})`;
|
|
2082
|
+
}, $t = {
|
|
1999
2083
|
id: "depth",
|
|
2000
2084
|
title: "Depth",
|
|
2001
2085
|
hint: "Flat · soft · crisp · bold",
|
|
2002
2086
|
uses: ["--twd-shadow-sm", "--twd-shadow-md", "--twd-shadow-lg"],
|
|
2003
|
-
tokens(
|
|
2004
|
-
const
|
|
2087
|
+
tokens(e) {
|
|
2088
|
+
const t = e.depth || {}, n = t.style || "soft", a = typeof t.intensity == "number" ? Math.min(100, Math.max(0, t.intensity)) : ie;
|
|
2005
2089
|
if (n === "flat")
|
|
2006
2090
|
return { "--twd-shadow-sm": "none", "--twd-shadow-md": "none", "--twd-shadow-lg": "none" };
|
|
2007
|
-
const o =
|
|
2091
|
+
const o = ct[n];
|
|
2008
2092
|
if (!o) return {};
|
|
2009
|
-
if (n === "soft" && a ===
|
|
2010
|
-
const s = a /
|
|
2093
|
+
if (n === "soft" && a === ie) return {};
|
|
2094
|
+
const s = a / ie;
|
|
2011
2095
|
return {
|
|
2012
|
-
"--twd-shadow-sm":
|
|
2013
|
-
"--twd-shadow-md":
|
|
2014
|
-
"--twd-shadow-lg":
|
|
2096
|
+
"--twd-shadow-sm": je(o.sm, s),
|
|
2097
|
+
"--twd-shadow-md": je(o.md, s),
|
|
2098
|
+
"--twd-shadow-lg": je(o.lg, s)
|
|
2015
2099
|
};
|
|
2016
2100
|
},
|
|
2017
2101
|
parts: {
|
|
2018
2102
|
// Tabbed layout: each style as a small card wearing that shadow.
|
|
2019
|
-
styleTiles(
|
|
2020
|
-
const a = (o) => `<span class="shape shape-card shadow-${o}" style="box-shadow:${
|
|
2021
|
-
|
|
2103
|
+
styleTiles(e, t, { label: n = "Style" } = {}) {
|
|
2104
|
+
const a = (o) => `<span class="shape shape-card shadow-${o}" style="box-shadow:${ct[o] ? je(ct[o].md, 1) : "none"}"></span>`;
|
|
2105
|
+
e.append(V(t, {
|
|
2022
2106
|
section: "depth",
|
|
2023
2107
|
key: "style",
|
|
2024
2108
|
label: n,
|
|
@@ -2027,8 +2111,8 @@ const Be = {
|
|
|
2027
2111
|
options: ["flat", "soft", "crisp", "bold"].map((o) => ({ value: o, label: o[0].toUpperCase() + o.slice(1), preview: a(o) }))
|
|
2028
2112
|
}));
|
|
2029
2113
|
},
|
|
2030
|
-
style(
|
|
2031
|
-
|
|
2114
|
+
style(e, t, { label: n = "Style" } = {}) {
|
|
2115
|
+
e.append($e(t, {
|
|
2032
2116
|
section: "depth",
|
|
2033
2117
|
key: "style",
|
|
2034
2118
|
label: n,
|
|
@@ -2036,160 +2120,160 @@ const Be = {
|
|
|
2036
2120
|
options: [{ value: "flat", label: "Flat" }, { value: "soft", label: "Soft" }, { value: "crisp", label: "Crisp" }, { value: "bold", label: "Bold" }]
|
|
2037
2121
|
}));
|
|
2038
2122
|
},
|
|
2039
|
-
intensity(
|
|
2040
|
-
|
|
2123
|
+
intensity(e, t, { label: n = "Intensity" } = {}) {
|
|
2124
|
+
e.append(Xa(t, { section: "depth", key: "intensity", label: n, min: 0, max: 100, fallback: ie, format: (a) => `${a}%` }));
|
|
2041
2125
|
}
|
|
2042
2126
|
},
|
|
2043
|
-
render(
|
|
2044
|
-
Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(
|
|
2127
|
+
render(e, t) {
|
|
2128
|
+
Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(e, t));
|
|
2045
2129
|
}
|
|
2046
|
-
},
|
|
2047
|
-
function
|
|
2048
|
-
return typeof
|
|
2130
|
+
}, go = (e) => Math.min(1, Math.max(0, e)), vn = (e) => Math.round(e / 50) * 50, pt = (e) => `${+e.toFixed(1)}%`;
|
|
2131
|
+
function Tt(e = {}) {
|
|
2132
|
+
return typeof e.bold == "number" && isFinite(e.bold) && e.bold !== 0.5 ? go(e.bold) : null;
|
|
2049
2133
|
}
|
|
2050
|
-
const
|
|
2134
|
+
const Re = (e, t, n) => (e.tokens(t) || {})[n] || F(n), mo = {
|
|
2051
2135
|
id: "character",
|
|
2052
2136
|
title: "Character",
|
|
2053
2137
|
hint: "Subtle ↔ Bold",
|
|
2054
2138
|
uses: [],
|
|
2055
2139
|
// set from the Simple page only
|
|
2056
|
-
tokens(
|
|
2057
|
-
const
|
|
2058
|
-
if (
|
|
2059
|
-
const n = {}, a = 2 *
|
|
2060
|
-
n["--twd-weight-heading"] = String(
|
|
2061
|
-
const r =
|
|
2062
|
-
if (s && (n["--twd-border"] = `color-mix(in oklch, ${r} ${
|
|
2063
|
-
n["--twd-border"] = `color-mix(in oklch, ${r} ${
|
|
2064
|
-
const l =
|
|
2065
|
-
n["--twd-text-muted"] = `color-mix(in oklch, ${l} ${
|
|
2066
|
-
const
|
|
2067
|
-
isNaN(
|
|
2068
|
-
}), [[
|
|
2069
|
-
const
|
|
2070
|
-
!isNaN(
|
|
2140
|
+
tokens(e) {
|
|
2141
|
+
const t = Tt(e.character);
|
|
2142
|
+
if (t === null) return {};
|
|
2143
|
+
const n = {}, a = 2 * t - 1, o = Math.max(0, a), s = Math.max(0, -a);
|
|
2144
|
+
n["--twd-weight-heading"] = String(vn(600 + 200 * a)), n["--twd-weight-strong"] = String(vn(600 + 100 * a));
|
|
2145
|
+
const r = Re(He, e, "--twd-border");
|
|
2146
|
+
if (s && (n["--twd-border"] = `color-mix(in oklch, ${r} ${pt(100 - 50 * s)}, var(--twd-bg))`), o) {
|
|
2147
|
+
n["--twd-border"] = `color-mix(in oklch, ${r} ${pt(100 - 90 * o)}, var(--twd-text))`;
|
|
2148
|
+
const l = Re(He, e, "--twd-text-muted");
|
|
2149
|
+
n["--twd-text-muted"] = `color-mix(in oklch, ${l} ${pt(100 - 35 * o)}, var(--twd-text))`, ["--twd-border-width", "--twd-border-width-container"].forEach((d) => {
|
|
2150
|
+
const p = d === "--twd-border-width-container" ? Re(St, e, d) : F(d), u = parseFloat(p);
|
|
2151
|
+
isNaN(u) || (n[d] = `${Math.round(u * (1 + o))}px`);
|
|
2152
|
+
}), [[Wn, "--twd-radius-control"], [St, "--twd-radius-container"]].forEach(([d, p]) => {
|
|
2153
|
+
const u = parseFloat(Re(d, e, p));
|
|
2154
|
+
!isNaN(u) && u < 999 && (n[p] = Se(`${u}px`, 1 - 0.5 * o));
|
|
2071
2155
|
});
|
|
2072
2156
|
}
|
|
2073
|
-
const i =
|
|
2157
|
+
const i = e.depth || {};
|
|
2074
2158
|
if (!i.style) {
|
|
2075
|
-
const l = typeof i.intensity == "number" ? i.intensity :
|
|
2076
|
-
Object.assign(n,
|
|
2159
|
+
const l = typeof i.intensity == "number" ? i.intensity : ie, d = s ? $t.tokens({ depth: { style: "soft", intensity: l * (1 - s) } }) : $t.tokens({ depth: { style: "bold", intensity: ie * o } });
|
|
2160
|
+
Object.assign(n, d);
|
|
2077
2161
|
}
|
|
2078
2162
|
return n;
|
|
2079
2163
|
},
|
|
2080
|
-
describe: (
|
|
2081
|
-
},
|
|
2082
|
-
let
|
|
2083
|
-
function
|
|
2084
|
-
const s = `twd-fonts-${++
|
|
2164
|
+
describe: (e, t) => e === "bold" ? { label: "Subtle ↔ Bold", value: `${Math.round(t * 100)}%` } : null
|
|
2165
|
+
}, bo = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>', xo = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>';
|
|
2166
|
+
let wo = 0;
|
|
2167
|
+
function yo({ label: e, options: t, onSelect: n, placeholder: a, onOpen: o }) {
|
|
2168
|
+
const s = `twd-fonts-${++wo}`, r = document.createElement("div");
|
|
2085
2169
|
r.className = "font-select";
|
|
2086
2170
|
const i = document.createElement("button");
|
|
2087
2171
|
i.type = "button", i.className = "font-trigger", i.setAttribute("aria-haspopup", "listbox"), i.setAttribute("aria-expanded", "false"), i.setAttribute("aria-controls", s), i.innerHTML = `
|
|
2088
2172
|
<span class="font-aa" aria-hidden="true">Aa</span>
|
|
2089
2173
|
<span class="font-name"></span>
|
|
2090
2174
|
<span class="font-fonts"></span>
|
|
2091
|
-
<span class="font-chevron">${
|
|
2175
|
+
<span class="font-chevron">${bo}</span>`;
|
|
2092
2176
|
const l = document.createElement("div");
|
|
2093
|
-
l.className = "font-menu", l.id = s, l.setAttribute("role", "listbox"), l.setAttribute("aria-label",
|
|
2094
|
-
const
|
|
2095
|
-
const
|
|
2096
|
-
return
|
|
2097
|
-
<span class="font-aa" style="font-family:${
|
|
2098
|
-
<span class="font-text"><span class="font-name">${
|
|
2099
|
-
<span class="font-check">${
|
|
2177
|
+
l.className = "font-menu", l.id = s, l.setAttribute("role", "listbox"), l.setAttribute("aria-label", e), l.hidden = !0;
|
|
2178
|
+
const d = t.map((h) => {
|
|
2179
|
+
const y = document.createElement("div");
|
|
2180
|
+
return y.className = "font-option", y.setAttribute("role", "option"), y.tabIndex = -1, y.dataset.value = h.value, y.innerHTML = `
|
|
2181
|
+
<span class="font-aa" style="font-family:${h.preview}" aria-hidden="true">Aa</span>
|
|
2182
|
+
<span class="font-text"><span class="font-name">${h.name}</span><span class="font-fonts">${h.fonts}</span></span>
|
|
2183
|
+
<span class="font-check">${xo}</span>`, y.addEventListener("click", () => b(h.value)), y;
|
|
2100
2184
|
});
|
|
2101
|
-
l.append(...
|
|
2102
|
-
let
|
|
2103
|
-
function h
|
|
2104
|
-
const
|
|
2105
|
-
|
|
2185
|
+
l.append(...d), r.append(i, l);
|
|
2186
|
+
let p = null;
|
|
2187
|
+
function u(h) {
|
|
2188
|
+
const y = t.find((E) => E.value === h), L = y || a || t[0];
|
|
2189
|
+
p = y ? y.value : a ? null : t[0].value, i.querySelector(".font-name").textContent = L.name, i.querySelector(".font-fonts").textContent = L.fonts, i.querySelector(".font-aa").style.fontFamily = L.preview, i.setAttribute("aria-label", `${e}: ${L.name}, ${L.fonts}`), d.forEach((E) => E.setAttribute("aria-selected", String(E.dataset.value === p)));
|
|
2106
2190
|
}
|
|
2107
|
-
function
|
|
2108
|
-
const
|
|
2109
|
-
l.style.top = `${E}px`, l.style.left = `${
|
|
2191
|
+
function v() {
|
|
2192
|
+
const h = i.getBoundingClientRect(), y = l.offsetHeight, E = window.innerHeight - h.bottom - 8 >= y ? h.bottom + 4 : Math.max(8, h.top - 4 - y);
|
|
2193
|
+
l.style.top = `${E}px`, l.style.left = `${h.left}px`, l.style.width = `${h.width}px`;
|
|
2110
2194
|
}
|
|
2111
2195
|
function f() {
|
|
2112
|
-
l.hidden && (l.hidden = !1, i.setAttribute("aria-expanded", "true"), o && o(),
|
|
2196
|
+
l.hidden && (l.hidden = !1, i.setAttribute("aria-expanded", "true"), o && o(), v(), (d.find((h) => h.dataset.value === p) || d[0]).focus(), setTimeout(() => document.addEventListener("pointerdown", x, !0)), window.addEventListener("resize", m), r.closest(".body")?.addEventListener("scroll", m, { once: !0 }));
|
|
2113
2197
|
}
|
|
2114
|
-
function m(
|
|
2115
|
-
l.hidden || (l.hidden = !0, i.setAttribute("aria-expanded", "false"), document.removeEventListener("pointerdown", x, !0), window.removeEventListener("resize", m),
|
|
2198
|
+
function m(h) {
|
|
2199
|
+
l.hidden || (l.hidden = !0, i.setAttribute("aria-expanded", "false"), document.removeEventListener("pointerdown", x, !0), window.removeEventListener("resize", m), h === !0 && i.focus());
|
|
2116
2200
|
}
|
|
2117
|
-
function x(
|
|
2118
|
-
|
|
2201
|
+
function x(h) {
|
|
2202
|
+
h.composedPath().includes(r) || m();
|
|
2119
2203
|
}
|
|
2120
|
-
function b(
|
|
2121
|
-
|
|
2204
|
+
function b(h) {
|
|
2205
|
+
h !== p && (u(h), n(h)), m(!0);
|
|
2122
2206
|
}
|
|
2123
|
-
return i.addEventListener("click", () => l.hidden ? f() : m(!0)), i.addEventListener("keydown", (
|
|
2124
|
-
(
|
|
2125
|
-
}), l.addEventListener("keydown", (
|
|
2126
|
-
const
|
|
2207
|
+
return i.addEventListener("click", () => l.hidden ? f() : m(!0)), i.addEventListener("keydown", (h) => {
|
|
2208
|
+
(h.key === "ArrowDown" || h.key === "ArrowUp") && (h.preventDefault(), f());
|
|
2209
|
+
}), l.addEventListener("keydown", (h) => {
|
|
2210
|
+
const y = d.indexOf(h.target);
|
|
2127
2211
|
let L = null;
|
|
2128
|
-
if (
|
|
2129
|
-
else if (
|
|
2130
|
-
else if (
|
|
2131
|
-
else if (
|
|
2132
|
-
else if (
|
|
2133
|
-
|
|
2212
|
+
if (h.key === "ArrowDown") L = Math.min(d.length - 1, y + 1);
|
|
2213
|
+
else if (h.key === "ArrowUp") L = Math.max(0, y - 1);
|
|
2214
|
+
else if (h.key === "Home") L = 0;
|
|
2215
|
+
else if (h.key === "End") L = d.length - 1;
|
|
2216
|
+
else if (h.key === "Enter" || h.key === " ") {
|
|
2217
|
+
h.preventDefault(), b(h.target.dataset.value);
|
|
2134
2218
|
return;
|
|
2135
|
-
} else if (
|
|
2136
|
-
|
|
2219
|
+
} else if (h.key === "Escape") {
|
|
2220
|
+
h.preventDefault(), h.stopPropagation(), m(!0);
|
|
2137
2221
|
return;
|
|
2138
|
-
} else if (
|
|
2222
|
+
} else if (h.key === "Tab") {
|
|
2139
2223
|
m();
|
|
2140
2224
|
return;
|
|
2141
2225
|
}
|
|
2142
|
-
L !== null && (
|
|
2143
|
-
}),
|
|
2226
|
+
L !== null && (h.preventDefault(), d[L].focus());
|
|
2227
|
+
}), u(a ? null : t[0].value), { el: r, setValue: u };
|
|
2144
2228
|
}
|
|
2145
|
-
const I = 'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif',
|
|
2229
|
+
const I = 'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif', ze = 'Georgia, "Times New Roman", serif', J = [
|
|
2146
2230
|
{ value: "neutral", name: "Neutral", fonts: "Inter · Inter", heading: `"Inter", ${I}`, body: `"Inter", ${I}`, load: ["Inter:wght@400;500;600;700"] },
|
|
2147
2231
|
{ value: "modern", name: "Modern", fonts: "Geist · Geist", heading: `"Geist", ${I}`, body: `"Geist", ${I}`, load: ["Geist:wght@400;500;600;700"] },
|
|
2148
|
-
{ value: "editorial", name: "Editorial", fonts: "Fraunces · Inter", heading: `"Fraunces", ${
|
|
2232
|
+
{ value: "editorial", name: "Editorial", fonts: "Fraunces · Inter", heading: `"Fraunces", ${ze}`, body: `"Inter", ${I}`, load: ["Fraunces:wght@400;600;700", "Inter:wght@400;500;600;700"] },
|
|
2149
2233
|
{ value: "technical", name: "Technical", fonts: "Space Grotesk · DM Sans", heading: `"Space Grotesk", ${I}`, body: `"DM Sans", ${I}`, load: ["Space+Grotesk:wght@500;600;700", "DM+Sans:wght@400;500;600;700"] },
|
|
2150
|
-
{ value: "classic", name: "Classic", fonts: "Playfair · DM Sans", heading: `"Playfair Display", ${
|
|
2151
|
-
{ value: "plex", name: "Plex", fonts: "Plex Serif · Plex Sans", heading: `"IBM Plex Serif", ${
|
|
2152
|
-
{ value: "elegant", name: "Elegant", fonts: "Instrument Serif · Inter", heading: `"Instrument Serif", ${
|
|
2234
|
+
{ value: "classic", name: "Classic", fonts: "Playfair · DM Sans", heading: `"Playfair Display", ${ze}`, body: `"DM Sans", ${I}`, load: ["Playfair+Display:wght@600;700", "DM+Sans:wght@400;500;600;700"] },
|
|
2235
|
+
{ value: "plex", name: "Plex", fonts: "Plex Serif · Plex Sans", heading: `"IBM Plex Serif", ${ze}`, body: `"IBM Plex Sans", ${I}`, load: ["IBM+Plex+Serif:wght@500;600;700", "IBM+Plex+Sans:wght@400;500;600"] },
|
|
2236
|
+
{ value: "elegant", name: "Elegant", fonts: "Instrument Serif · Inter", heading: `"Instrument Serif", ${ze}`, body: `"Inter", ${I}`, load: ["Instrument+Serif", "Inter:wght@400;500;600;700"] },
|
|
2153
2237
|
{ value: "friendly", name: "Friendly", fonts: "Manrope · Manrope", heading: `"Manrope", ${I}`, body: `"Manrope", ${I}`, load: ["Manrope:wght@400;500;600;700"] }
|
|
2154
|
-
],
|
|
2155
|
-
function
|
|
2156
|
-
const
|
|
2157
|
-
if (!
|
|
2158
|
-
|
|
2238
|
+
], we = { compact: 1.125, balanced: 1.2, expressive: 1.333 }, kn = { xs: -2, sm: -1, base: 0, lg: 1, xl: 2, "2xl": 3, "3xl": 4 }, Sn = /* @__PURE__ */ new Set();
|
|
2239
|
+
function ut(e) {
|
|
2240
|
+
const t = e.filter((a) => !Sn.has(a));
|
|
2241
|
+
if (!t.length) return;
|
|
2242
|
+
t.forEach((a) => Sn.add(a));
|
|
2159
2243
|
const n = document.createElement("link");
|
|
2160
|
-
n.rel = "stylesheet", n.href = `https://fonts.googleapis.com/css2?${
|
|
2244
|
+
n.rel = "stylesheet", n.href = `https://fonts.googleapis.com/css2?${t.map((a) => `family=${a}`).join("&")}&display=swap`, n.dataset.twiddle = "fonts", document.head.appendChild(n);
|
|
2161
2245
|
}
|
|
2162
|
-
const
|
|
2246
|
+
const vo = {
|
|
2163
2247
|
id: "typography",
|
|
2164
2248
|
title: "Typography",
|
|
2165
2249
|
hint: "Font pairing · base size · scale",
|
|
2166
|
-
uses: ["--twd-font-heading", "--twd-font-body", ...Object.keys(
|
|
2167
|
-
tokens(
|
|
2168
|
-
const
|
|
2169
|
-
if (a && (
|
|
2170
|
-
const o = Number(
|
|
2171
|
-
Object.entries(
|
|
2250
|
+
uses: ["--twd-font-heading", "--twd-font-body", ...Object.keys(kn).map((e) => `--twd-text-${e}`)],
|
|
2251
|
+
tokens(e) {
|
|
2252
|
+
const t = e.typography || {}, n = {}, a = J.find((o) => o.value === t.font);
|
|
2253
|
+
if (a && (ut(a.load), n["--twd-font-heading"] = a.heading, n["--twd-font-body"] = a.body), t.base || t.scale) {
|
|
2254
|
+
const o = Number(t.base) || 16, s = we[t.scale] || we.balanced;
|
|
2255
|
+
Object.entries(kn).forEach(([r, i]) => {
|
|
2172
2256
|
n[`--twd-text-${r}`] = `${+(o * s ** i / 16).toFixed(3)}rem`;
|
|
2173
2257
|
});
|
|
2174
2258
|
}
|
|
2175
2259
|
return n;
|
|
2176
2260
|
},
|
|
2177
2261
|
parts: {
|
|
2178
|
-
fonts(
|
|
2179
|
-
const a =
|
|
2262
|
+
fonts(e, t, { label: n = "Fonts" } = {}) {
|
|
2263
|
+
const a = U(n), o = yo({
|
|
2180
2264
|
label: "Font pairing",
|
|
2181
|
-
options:
|
|
2265
|
+
options: J.map((r) => ({ value: r.value, name: r.name, fonts: r.fonts, preview: r.heading })),
|
|
2182
2266
|
placeholder: { name: "Default", fonts: "Prototype fonts", preview: I },
|
|
2183
2267
|
// Load every pairing's fonts when the list opens, so the "Aa" previews are real.
|
|
2184
|
-
onOpen: () =>
|
|
2185
|
-
onSelect: (r) =>
|
|
2268
|
+
onOpen: () => ut([...new Set(J.flatMap((r) => r.load))]),
|
|
2269
|
+
onSelect: (r) => t.setSection("typography", { font: r })
|
|
2186
2270
|
});
|
|
2187
2271
|
a.append(o.el);
|
|
2188
|
-
const s = () => o.setValue((
|
|
2189
|
-
s(),
|
|
2272
|
+
const s = () => o.setValue((t.getState().typography || {}).font || null);
|
|
2273
|
+
s(), t.onChange(s), e.append(a.el);
|
|
2190
2274
|
},
|
|
2191
|
-
base(
|
|
2192
|
-
|
|
2275
|
+
base(e, t, { label: n = "Base size" } = {}) {
|
|
2276
|
+
e.append($e(t, {
|
|
2193
2277
|
section: "typography",
|
|
2194
2278
|
key: "base",
|
|
2195
2279
|
label: n,
|
|
@@ -2200,8 +2284,8 @@ const ho = {
|
|
|
2200
2284
|
},
|
|
2201
2285
|
// Tabbed layout: pairings as tiles, each "Aa" in its own heading font.
|
|
2202
2286
|
// The fonts load (from Google Fonts) only once the tiles are on screen.
|
|
2203
|
-
fontTiles(
|
|
2204
|
-
const a = (i) => i.replace(/"/g, """), o = (i) => `<span class="tile-aa" style="font-family:${a(i)}">Aa</span>`, s = V(
|
|
2287
|
+
fontTiles(e, t, { label: n = "Fonts" } = {}) {
|
|
2288
|
+
const a = (i) => i.replace(/"/g, """), o = (i) => `<span class="tile-aa" style="font-family:${a(i)}">Aa</span>`, s = V(t, {
|
|
2205
2289
|
section: "typography",
|
|
2206
2290
|
key: "font",
|
|
2207
2291
|
label: n,
|
|
@@ -2209,165 +2293,165 @@ const ho = {
|
|
|
2209
2293
|
className: "tiles-fonts",
|
|
2210
2294
|
options: [
|
|
2211
2295
|
{ value: "default", label: "Prototype fonts", sub: "Default", preview: o(I), wide: !0 },
|
|
2212
|
-
...
|
|
2296
|
+
...J.map((i) => ({ value: i.value, label: i.name, sub: i.fonts, preview: o(i.heading) }))
|
|
2213
2297
|
],
|
|
2214
2298
|
current: (i) => (i.typography || {}).font || "default",
|
|
2215
|
-
select: (i) =>
|
|
2216
|
-
valueText: (i) => i === "default" ? "Default" :
|
|
2299
|
+
select: (i) => t.setSection("typography", { font: i === "default" ? null : i }),
|
|
2300
|
+
valueText: (i) => i === "default" ? "Default" : J.find((l) => l.value === i)?.name || ""
|
|
2217
2301
|
}), r = new IntersectionObserver((i) => {
|
|
2218
|
-
i.some((l) => l.isIntersecting) && (
|
|
2302
|
+
i.some((l) => l.isIntersecting) && (ut([...new Set(J.flatMap((l) => l.load))]), r.disconnect());
|
|
2219
2303
|
});
|
|
2220
|
-
r.observe(s),
|
|
2304
|
+
r.observe(s), e.append(s);
|
|
2221
2305
|
},
|
|
2222
|
-
scaleTiles(
|
|
2306
|
+
scaleTiles(e, t, { label: n = "Scale" } = {}) {
|
|
2223
2307
|
const a = (o) => `<span class="ladder">${[-1, 0, 1, 2].map((s) => `<b style="font-size:${Math.round(13 * o ** s)}px">A</b>`).join("")}</span>`;
|
|
2224
|
-
|
|
2308
|
+
e.append(V(t, {
|
|
2225
2309
|
section: "typography",
|
|
2226
2310
|
key: "scale",
|
|
2227
2311
|
label: n,
|
|
2228
2312
|
fallback: "balanced",
|
|
2229
|
-
options: Object.entries(
|
|
2230
|
-
valueText: (o) => String(
|
|
2313
|
+
options: Object.entries(we).map(([o, s]) => ({ value: o, label: o[0].toUpperCase() + o.slice(1), preview: a(s) })),
|
|
2314
|
+
valueText: (o) => String(we[o])
|
|
2231
2315
|
}));
|
|
2232
2316
|
},
|
|
2233
|
-
scale(
|
|
2234
|
-
|
|
2317
|
+
scale(e, t, { label: n = "Scale" } = {}) {
|
|
2318
|
+
e.append($e(t, {
|
|
2235
2319
|
section: "typography",
|
|
2236
2320
|
key: "scale",
|
|
2237
2321
|
label: n,
|
|
2238
2322
|
fallback: "balanced",
|
|
2239
2323
|
options: [{ value: "compact", label: "Compact" }, { value: "balanced", label: "Balanced" }, { value: "expressive", label: "Expressive" }],
|
|
2240
|
-
valueText: (a) => String(
|
|
2324
|
+
valueText: (a) => String(we[a])
|
|
2241
2325
|
}));
|
|
2242
2326
|
}
|
|
2243
2327
|
},
|
|
2244
|
-
render(
|
|
2245
|
-
Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(
|
|
2328
|
+
render(e, t) {
|
|
2329
|
+
Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(e, t));
|
|
2246
2330
|
}
|
|
2247
|
-
}, [
|
|
2248
|
-
function
|
|
2249
|
-
return
|
|
2331
|
+
}, [$n, Ie] = kt;
|
|
2332
|
+
function ko(e) {
|
|
2333
|
+
return e > 0.56 ? "Bold" : e < 0.44 ? "Subtle" : "Balanced";
|
|
2250
2334
|
}
|
|
2251
|
-
function
|
|
2252
|
-
return
|
|
2335
|
+
function Gn(e) {
|
|
2336
|
+
return e > 1.06 ? "Comfy" : e < 0.94 ? "Compact" : "Regular";
|
|
2253
2337
|
}
|
|
2254
|
-
const
|
|
2255
|
-
["colour", "Colour", (
|
|
2256
|
-
const
|
|
2257
|
-
return { text: `${
|
|
2338
|
+
const De = (e) => e && e[0].toUpperCase() + e.slice(1), En = [
|
|
2339
|
+
["colour", "Colour", (e) => {
|
|
2340
|
+
const t = e.colour || {}, n = Be.find((o) => o.primary === t.primary && o.neutral === t.neutral);
|
|
2341
|
+
return { text: `${t.brand ? t.brand.toUpperCase() : n && !t.shade ? n.label : t.primary ? `${De(t.primary)}${t.neutral ? ` · ${De(t.neutral)}` : ""}` : "Prototype colours"}${t.mode === "dark" ? " · Dark" : ""}`, swatch: F("--twd-primary") };
|
|
2258
2342
|
}],
|
|
2259
|
-
["type", "Type", (
|
|
2260
|
-
const
|
|
2261
|
-
return { text: `${
|
|
2343
|
+
["type", "Type", (e) => {
|
|
2344
|
+
const t = e.typography || {};
|
|
2345
|
+
return { text: `${J.find((n) => n.value === t.font)?.name || "Prototype fonts"} · ${t.base || 16}px` };
|
|
2262
2346
|
}],
|
|
2263
|
-
["spacing", "Spacing", (
|
|
2264
|
-
["shape", "Shape", (
|
|
2265
|
-
["shadows", "Shadows", (
|
|
2266
|
-
],
|
|
2267
|
-
summary(
|
|
2268
|
-
const o =
|
|
2269
|
-
s.className = "summary",
|
|
2270
|
-
const
|
|
2271
|
-
|
|
2347
|
+
["spacing", "Spacing", (e) => ({ text: Gn(se(e.density)) })],
|
|
2348
|
+
["shape", "Shape", (e) => ({ text: `${De((e.buttons || {}).corners) || "Default"} corners` })],
|
|
2349
|
+
["shadows", "Shadows", (e) => ({ text: De((e.depth || {}).style) || (Tt(e.character) !== null ? "From the pad" : "Default") })]
|
|
2350
|
+
], So = {
|
|
2351
|
+
summary(e, t, { label: n = "Current look", goTab: a } = {}) {
|
|
2352
|
+
const o = U(n), s = document.createElement("div");
|
|
2353
|
+
s.className = "summary", En.forEach(([i, l]) => {
|
|
2354
|
+
const d = document.createElement("button");
|
|
2355
|
+
d.type = "button", d.className = "summary-row", d.dataset.summary = i, d.innerHTML = `<span class="summary-name"></span><span class="summary-swatch" hidden></span><span class="summary-value"></span><span class="chev" aria-hidden="true">${A.chevron}</span>`, d.querySelector(".summary-name").textContent = l, d.addEventListener("click", () => a && a(i)), s.append(d);
|
|
2272
2356
|
}), o.append(s);
|
|
2273
2357
|
const r = () => {
|
|
2274
|
-
const i =
|
|
2275
|
-
|
|
2276
|
-
const
|
|
2277
|
-
|
|
2278
|
-
const
|
|
2279
|
-
|
|
2358
|
+
const i = t.getState();
|
|
2359
|
+
En.forEach(([l, , d]) => {
|
|
2360
|
+
const p = s.querySelector(`[data-summary="${l}"]`), u = d(i);
|
|
2361
|
+
p.querySelector(".summary-value").textContent = u.text;
|
|
2362
|
+
const v = p.querySelector(".summary-swatch");
|
|
2363
|
+
v.hidden = !u.swatch, u.swatch && (v.style.background = u.swatch);
|
|
2280
2364
|
});
|
|
2281
2365
|
};
|
|
2282
|
-
r(),
|
|
2366
|
+
r(), t.onChange(r), e.append(o.el);
|
|
2283
2367
|
},
|
|
2284
|
-
character(
|
|
2285
|
-
const a =
|
|
2368
|
+
character(e, t, { label: n = "Character" } = {}) {
|
|
2369
|
+
const a = U(n), o = Wa({
|
|
2286
2370
|
label: n,
|
|
2287
2371
|
ends: { top: "Spacious", bottom: "Compact", left: "Subtle", right: "Bold" },
|
|
2288
2372
|
onInput: (r, i) => {
|
|
2289
|
-
const l = +(
|
|
2290
|
-
|
|
2373
|
+
const l = +(Ie - i * (Ie - $n)).toFixed(3), d = +r.toFixed(3);
|
|
2374
|
+
t.setSections({
|
|
2291
2375
|
density: { factor: l === 1 ? null : l, spacing: null },
|
|
2292
|
-
character: { bold:
|
|
2376
|
+
character: { bold: d === 0.5 ? null : d }
|
|
2293
2377
|
}, "character");
|
|
2294
2378
|
}
|
|
2295
2379
|
});
|
|
2296
2380
|
a.append(o.el);
|
|
2297
2381
|
const s = () => {
|
|
2298
|
-
const r =
|
|
2299
|
-
o.setValue(
|
|
2382
|
+
const r = t.getState(), i = se(r.density), l = Tt(r.character), d = l === null ? 0.5 : l, p = `${Gn(i)} · ${ko(d)}`;
|
|
2383
|
+
o.setValue(d, (Ie - i) / (Ie - $n), p), a.setValue(p);
|
|
2300
2384
|
};
|
|
2301
|
-
s(),
|
|
2385
|
+
s(), t.onChange(s), e.append(a.el);
|
|
2302
2386
|
}
|
|
2303
|
-
},
|
|
2387
|
+
}, ht = [
|
|
2304
2388
|
{ key: "page-pad", label: "Page padding", step: 5 },
|
|
2305
2389
|
{ key: "section-gap", label: "Section gap", step: 6 },
|
|
2306
2390
|
{ key: "card-pad", label: "Card padding", step: 5 },
|
|
2307
2391
|
{ key: "card-gap", label: "Card gap", step: 2 },
|
|
2308
2392
|
{ key: "control-gap", label: "Control gap", step: 3 },
|
|
2309
2393
|
{ key: "control-pad-x", label: "Control padding", step: null }
|
|
2310
|
-
],
|
|
2311
|
-
const
|
|
2312
|
-
return
|
|
2394
|
+
], $o = [1, 2, 3, 4, 5, 6, 7, 8], Eo = /^var\(\s*--twd-space-(\d)\s*\)$/, ft = (e) => {
|
|
2395
|
+
const t = /^(-?\d*\.?\d+)(px|rem)?$/.exec(String(e).trim());
|
|
2396
|
+
return t ? +(parseFloat(t[1]) * (t[2] === "rem" ? 16 : 1)).toFixed(1) : null;
|
|
2313
2397
|
};
|
|
2314
|
-
function
|
|
2315
|
-
const a = `--twd-${
|
|
2398
|
+
function Co(e, t, n) {
|
|
2399
|
+
const a = `--twd-${t.key}`, o = document.createElement("div");
|
|
2316
2400
|
o.className = "role-row", n || o.classList.add("unused"), o.innerHTML = `
|
|
2317
|
-
<span class="role-label" title="${a}${n ? "" : " (not used on this page)"}">${
|
|
2318
|
-
<label class="token-value role-fixed" hidden><input type="text" inputmode="decimal" spellcheck="false" aria-label="${
|
|
2401
|
+
<span class="role-label" title="${a}${n ? "" : " (not used on this page)"}">${t.label}</span>
|
|
2402
|
+
<label class="token-value role-fixed" hidden><input type="text" inputmode="decimal" spellcheck="false" aria-label="${t.label}, fixed value"><span class="token-unit">px</span></label>
|
|
2319
2403
|
<span class="step-select">
|
|
2320
|
-
<select aria-label="${
|
|
2404
|
+
<select aria-label="${t.label}"></select>
|
|
2321
2405
|
<span class="step-chevron" aria-hidden="true">${A.down}</span>
|
|
2322
2406
|
</span>
|
|
2323
|
-
<button class="icon-btn token-reset" title="Back to the default" aria-label="Reset ${
|
|
2407
|
+
<button class="icon-btn token-reset" title="Back to the default" aria-label="Reset ${t.label}" hidden>${A.reset}</button>
|
|
2324
2408
|
`;
|
|
2325
|
-
const s = o.querySelector("select"), r = o.querySelector(".role-fixed"), i = r.querySelector("input"), l = o.querySelector(".token-reset"),
|
|
2326
|
-
const x =
|
|
2327
|
-
if (x == null) return
|
|
2328
|
-
const b =
|
|
2409
|
+
const s = o.querySelector("select"), r = o.querySelector(".role-fixed"), i = r.querySelector("input"), l = o.querySelector(".token-reset"), d = () => (e.getState().custom || {})[t.key], p = () => {
|
|
2410
|
+
const x = d();
|
|
2411
|
+
if (x == null) return t.step ? { step: t.step } : { fixed: F(a) };
|
|
2412
|
+
const b = Eo.exec(x);
|
|
2329
2413
|
return b ? { step: +b[1] } : { fixed: x };
|
|
2330
|
-
},
|
|
2414
|
+
}, u = (x) => e.setSection("custom", { [t.key]: x }), v = (x) => t.step ? x === `var(--twd-space-${t.step})` : !1;
|
|
2331
2415
|
s.addEventListener("change", () => {
|
|
2332
2416
|
if (s.value === "fixed") {
|
|
2333
|
-
const b =
|
|
2334
|
-
|
|
2417
|
+
const b = ft(F(a));
|
|
2418
|
+
u(b != null ? `${b}px` : F(a)), i.focus(), i.select();
|
|
2335
2419
|
return;
|
|
2336
2420
|
}
|
|
2337
2421
|
const x = `var(--twd-space-${s.value})`;
|
|
2338
|
-
|
|
2422
|
+
u(v(x) ? null : x);
|
|
2339
2423
|
});
|
|
2340
2424
|
const f = () => {
|
|
2341
2425
|
const x = i.value.trim();
|
|
2342
|
-
if (!x) return
|
|
2343
|
-
|
|
2426
|
+
if (!x) return u(null);
|
|
2427
|
+
u(/^-?\d*\.?\d+$/.test(x) ? `${x}px` : x);
|
|
2344
2428
|
};
|
|
2345
2429
|
i.addEventListener("change", f), i.addEventListener("blur", () => m()), i.addEventListener("keydown", (x) => {
|
|
2346
2430
|
x.key === "Enter" && (x.preventDefault(), i.blur()), x.key === "Escape" && (x.stopPropagation(), m(), i.blur());
|
|
2347
|
-
}), l.addEventListener("click", () =>
|
|
2431
|
+
}), l.addEventListener("click", () => u(null));
|
|
2348
2432
|
function m() {
|
|
2349
|
-
const x =
|
|
2350
|
-
if (s.innerHTML =
|
|
2351
|
-
const
|
|
2352
|
-
i.value =
|
|
2433
|
+
const x = p();
|
|
2434
|
+
if (s.innerHTML = $o.map((h) => `<option value="${h}">space-${h} · ${ft(F(`--twd-space-${h}`)) ?? "?"}</option>`).join("") + '<option value="fixed">Fixed</option>', s.value = x.step ? String(x.step) : "fixed", r.hidden = !x.fixed, o.classList.toggle("is-fixed", !!x.fixed), x.fixed && o.getRootNode().activeElement !== i) {
|
|
2435
|
+
const h = ft(x.fixed);
|
|
2436
|
+
i.value = h != null ? String(h) : x.fixed;
|
|
2353
2437
|
}
|
|
2354
|
-
const b =
|
|
2438
|
+
const b = d() != null;
|
|
2355
2439
|
o.classList.toggle("edited", b), l.hidden = !b;
|
|
2356
2440
|
}
|
|
2357
|
-
return m(),
|
|
2441
|
+
return m(), e.onChange(m), o;
|
|
2358
2442
|
}
|
|
2359
|
-
const
|
|
2360
|
-
spacing(
|
|
2361
|
-
const a =
|
|
2443
|
+
const Mo = {
|
|
2444
|
+
spacing(e, t, { label: n = "Roles" } = {}) {
|
|
2445
|
+
const a = _a(), o = U(n);
|
|
2362
2446
|
o.el.classList.add("token-field");
|
|
2363
|
-
const s =
|
|
2364
|
-
o.setValue(s.length ===
|
|
2447
|
+
const s = ht.filter((l) => !a.has(`--twd-${l.key}`));
|
|
2448
|
+
o.setValue(s.length === ht.length ? "not used on this page" : "");
|
|
2365
2449
|
const r = document.createElement("div");
|
|
2366
|
-
r.className = "token-rows",
|
|
2450
|
+
r.className = "token-rows", ht.forEach((l) => r.append(Co(t, l, a.has(`--twd-${l.key}`)))), o.append(r), e.append(o.el);
|
|
2367
2451
|
const i = document.createElement("p");
|
|
2368
|
-
i.className = "role-note", i.textContent = s.length ? `Only elements styled with these tokens change. Not used on this page: ${s.map((l) => l.label).join(", ")}.` : "Each role points at a spacing step, so Density still scales it. Pick another step, or a fixed value.",
|
|
2452
|
+
i.className = "role-note", i.textContent = s.length ? `Only elements styled with these tokens change. Not used on this page: ${s.map((l) => l.label).join(", ")}.` : "Each role points at a spacing step, so Density still scales it. Pick another step, or a fixed value.", e.append(i);
|
|
2369
2453
|
}
|
|
2370
|
-
},
|
|
2454
|
+
}, ye = [
|
|
2371
2455
|
{ id: "overview", title: "Overview", icon: "tabOverview", parts: [["simple", "character"], ["simple", "summary"]] },
|
|
2372
2456
|
{ id: "colour", title: "Colour", icon: "tabColour", parts: [
|
|
2373
2457
|
["colour", "themeTiles"],
|
|
@@ -2407,18 +2491,18 @@ const wo = {
|
|
|
2407
2491
|
["depth", "intensity"],
|
|
2408
2492
|
{ more: "Shadow tokens", parts: [["custom", "shadows"]] }
|
|
2409
2493
|
] }
|
|
2410
|
-
],
|
|
2411
|
-
function
|
|
2494
|
+
], O = (e) => String(e).replace(/[&<>"]/g, (t) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[t]);
|
|
2495
|
+
function To({ sections: e, store: t, view: n }) {
|
|
2412
2496
|
const a = document.createElement("div");
|
|
2413
2497
|
a.id = "twiddle-root", document.body.appendChild(a);
|
|
2414
|
-
const o = a.attachShadow({ mode: "open" }), s = (
|
|
2498
|
+
const o = a.attachShadow({ mode: "open" }), s = (c) => c.map((g) => `
|
|
2415
2499
|
<details data-section="${g.id}">
|
|
2416
2500
|
<summary>${g.title}<span class="chev">${A.chevron}</span></summary>
|
|
2417
2501
|
<div class="section-body"><div class="placeholder">${g.hint}</div></div>
|
|
2418
2502
|
</details>
|
|
2419
2503
|
`).join("");
|
|
2420
2504
|
o.innerHTML = `
|
|
2421
|
-
<style>${
|
|
2505
|
+
<style>${va}</style>
|
|
2422
2506
|
<div class="pick-box" hidden><span class="pick-tag"></span></div>
|
|
2423
2507
|
<div class="wrap">
|
|
2424
2508
|
<div class="toast" role="status"></div>
|
|
@@ -2448,15 +2532,15 @@ function vo({ sections: t, store: e, view: n }) {
|
|
|
2448
2532
|
</div>
|
|
2449
2533
|
<div class="original-pill" hidden>Showing original</div>
|
|
2450
2534
|
<div class="body" data-view="global"><div class="setup-hint" role="status" hidden></div>
|
|
2451
|
-
${
|
|
2535
|
+
${ye.map((c) => `<div class="page page-fields" data-page="${c.id}" hidden></div>`).join("")}
|
|
2452
2536
|
</div>
|
|
2453
2537
|
<div class="body" data-view="local" hidden></div>
|
|
2454
2538
|
<div class="body" data-view="changes" hidden></div>
|
|
2455
2539
|
<nav class="tabbar" aria-label="Areas">
|
|
2456
|
-
${
|
|
2540
|
+
${ye.map((c) => `<button class="tab" data-tab="${c.id}" aria-pressed="false">${A[c.icon]}<span>${c.title}</span></button>`).join("")}
|
|
2457
2541
|
</nav>
|
|
2458
2542
|
</div>
|
|
2459
|
-
<button class="launcher" aria-label="Open Panel" aria-expanded="false">${A.panelMark}</button>
|
|
2543
|
+
<button class="launcher" aria-label="Open Panel" aria-expanded="false" title="Panel · drag to move, double-click to reset">${A.panelMark}</button>
|
|
2460
2544
|
</div>
|
|
2461
2545
|
<div class="dialog-backdrop" hidden>
|
|
2462
2546
|
<div class="dialog" role="dialog" aria-modal="true" aria-labelledby="twd-dialog-title">
|
|
@@ -2472,168 +2556,168 @@ function vo({ sections: t, store: e, view: n }) {
|
|
|
2472
2556
|
</div>
|
|
2473
2557
|
</div>
|
|
2474
2558
|
`;
|
|
2475
|
-
const r = (
|
|
2476
|
-
function m(
|
|
2559
|
+
const r = (c) => o.querySelector(c), i = r(".panel"), l = r(".launcher"), d = r(".toast"), p = { global: r('[data-view="global"]'), local: r('[data-view="local"]'), changes: r('[data-view="changes"]') }, u = r(".pick-box"), v = r(".pick-tag"), f = { getState: t.getState, setSection: t.setSection, setSections: t.setSections, onChange: t.onChange };
|
|
2560
|
+
function m(c, g, w) {
|
|
2477
2561
|
g.forEach((k) => {
|
|
2478
2562
|
if (typeof k.render != "function") return;
|
|
2479
|
-
const C =
|
|
2563
|
+
const C = c.querySelector(`[data-section="${k.id}"] .section-body`);
|
|
2480
2564
|
C.innerHTML = "", k.render(C, w);
|
|
2481
2565
|
});
|
|
2482
|
-
const $ = [...
|
|
2566
|
+
const $ = [...c.querySelectorAll("details")];
|
|
2483
2567
|
return $.forEach((k) => k.addEventListener("toggle", () => {
|
|
2484
2568
|
k.open && $.forEach((C) => {
|
|
2485
2569
|
C !== k && (C.open = !1);
|
|
2486
2570
|
});
|
|
2487
2571
|
})), $;
|
|
2488
2572
|
}
|
|
2489
|
-
const x = Object.fromEntries(
|
|
2490
|
-
function b(
|
|
2573
|
+
const x = Object.fromEntries(e.map((c) => [c.id, c]));
|
|
2574
|
+
function b(c, g) {
|
|
2491
2575
|
g.forEach((w) => {
|
|
2492
2576
|
if (w === "warnings") {
|
|
2493
2577
|
const S = document.createElement("div");
|
|
2494
|
-
S.className = "warnings page-warnings",
|
|
2578
|
+
S.className = "warnings page-warnings", c.append(S);
|
|
2495
2579
|
return;
|
|
2496
2580
|
}
|
|
2497
2581
|
if (w.heading) {
|
|
2498
2582
|
const S = document.createElement("div");
|
|
2499
|
-
S.className = "page-heading", S.innerHTML = `<span></span>${w.note ? '<span class="muted"></span>' : ""}`, S.firstChild.textContent = w.heading, w.note && (S.lastChild.textContent = w.note),
|
|
2583
|
+
S.className = "page-heading", S.innerHTML = `<span></span>${w.note ? '<span class="muted"></span>' : ""}`, S.firstChild.textContent = w.heading, w.note && (S.lastChild.textContent = w.note), c.append(S);
|
|
2500
2584
|
return;
|
|
2501
2585
|
}
|
|
2502
2586
|
if (w.more) {
|
|
2503
2587
|
const S = document.createElement("details");
|
|
2504
|
-
S.className = "more", S.innerHTML = `<summary><span class="chev" aria-hidden="true">${A.chevron}</span><span></span></summary><div class="more-body"></div>`, S.querySelector("summary span:last-child").textContent = w.more, b(S.querySelector(".more-body"), w.parts),
|
|
2588
|
+
S.className = "more", S.innerHTML = `<summary><span class="chev" aria-hidden="true">${A.chevron}</span><span></span></summary><div class="more-body"></div>`, S.querySelector("summary span:last-child").textContent = w.more, b(S.querySelector(".more-body"), w.parts), c.append(S);
|
|
2505
2589
|
return;
|
|
2506
2590
|
}
|
|
2507
2591
|
if (w.tabs) {
|
|
2508
2592
|
const S = w.tabs.map((j) => {
|
|
2509
2593
|
const z = document.createElement("div");
|
|
2510
2594
|
return z.className = "tab-pane", b(z, j.parts), z;
|
|
2511
|
-
}),
|
|
2595
|
+
}), be = ce({
|
|
2512
2596
|
label: "View",
|
|
2513
2597
|
options: w.tabs.map((j, z) => ({ value: String(z), label: j.label })),
|
|
2514
2598
|
onSelect: (j) => {
|
|
2515
|
-
|
|
2516
|
-
z.hidden = String(
|
|
2599
|
+
be.setValue(j), S.forEach((z, ra) => {
|
|
2600
|
+
z.hidden = String(ra) !== j;
|
|
2517
2601
|
});
|
|
2518
2602
|
}
|
|
2519
2603
|
});
|
|
2520
|
-
|
|
2604
|
+
be.setValue("0"), S.forEach((j, z) => {
|
|
2521
2605
|
j.hidden = z !== 0;
|
|
2522
|
-
}),
|
|
2606
|
+
}), c.append(be.el, ...S);
|
|
2523
2607
|
return;
|
|
2524
2608
|
}
|
|
2525
|
-
const [$, k, C = {}] = w, q = $ === "simple" ?
|
|
2526
|
-
q && q(
|
|
2527
|
-
|
|
2609
|
+
const [$, k, C = {}] = w, q = $ === "simple" ? So[k] : $ === "roles" ? Mo[k] : x[$]?.parts?.[k];
|
|
2610
|
+
q && q(c, f, { ...C, buttons: x.buttons, cards: x.cards, goTab: (S) => {
|
|
2611
|
+
W("global"), E(S);
|
|
2528
2612
|
} });
|
|
2529
2613
|
});
|
|
2530
2614
|
}
|
|
2531
|
-
|
|
2532
|
-
const
|
|
2533
|
-
let
|
|
2615
|
+
ye.forEach((c) => b(p.global.querySelector(`[data-page="${c.id}"]`), c.parts));
|
|
2616
|
+
const h = [...o.querySelectorAll(".tab")];
|
|
2617
|
+
let y = ye.some((c) => c.id === Z("tab")) ? Z("tab") : ye[0].id;
|
|
2534
2618
|
function L() {
|
|
2535
|
-
|
|
2619
|
+
h.forEach((c) => c.setAttribute("aria-pressed", String(K === "global" && c.dataset.tab === y)));
|
|
2536
2620
|
}
|
|
2537
|
-
function E(
|
|
2538
|
-
|
|
2539
|
-
g.hidden = g.dataset.page !==
|
|
2540
|
-
}),
|
|
2621
|
+
function E(c) {
|
|
2622
|
+
y = c, oe("tab", c), p.global.querySelectorAll(".page").forEach((g) => {
|
|
2623
|
+
g.hidden = g.dataset.page !== c;
|
|
2624
|
+
}), p.global.scrollTop = 0, L();
|
|
2541
2625
|
}
|
|
2542
|
-
const
|
|
2626
|
+
const ee = (c) => c.map((g) => `<div class="warning">${A.warn}<span>${O(g.text)}</span></div>`).join("");
|
|
2543
2627
|
let K = "global";
|
|
2544
|
-
function
|
|
2545
|
-
K =
|
|
2546
|
-
w.hidden = g !==
|
|
2547
|
-
}), r('[data-action="changes"]').setAttribute("aria-pressed", String(
|
|
2548
|
-
}
|
|
2549
|
-
E(
|
|
2550
|
-
const
|
|
2551
|
-
function
|
|
2628
|
+
function W(c) {
|
|
2629
|
+
K = c, Object.entries(p).forEach(([g, w]) => {
|
|
2630
|
+
w.hidden = g !== c;
|
|
2631
|
+
}), r('[data-action="changes"]').setAttribute("aria-pressed", String(c === "changes")), c !== "local" && Qn(), c === "changes" && Wt(), L();
|
|
2632
|
+
}
|
|
2633
|
+
E(y);
|
|
2634
|
+
const Ke = "cubic-bezier(0.3, 0.7, 0.2, 1)", We = "cubic-bezier(0.2, 0.8, 0.2, 1)", Ee = 640, ue = 100, Ge = 50, Yn = () => l.querySelector(".mark-knob"), Xn = () => [l.querySelector(".mark-t1"), l.querySelector(".mark-t2")];
|
|
2635
|
+
function At(c) {
|
|
2552
2636
|
if (matchMedia("(prefers-reduced-motion: reduce)").matches || !l.animate) return;
|
|
2553
|
-
const g =
|
|
2554
|
-
if (
|
|
2555
|
-
g.animate([{ transform: k(32) }, { transform: k(-6), offset: 0.78 }, { transform: k(0) }], { duration:
|
|
2637
|
+
const g = Yn(), [w, $] = Xn(), k = (C) => `translateY(${C}px)`;
|
|
2638
|
+
if (c) {
|
|
2639
|
+
g.animate([{ transform: k(32) }, { transform: k(-6), offset: 0.78 }, { transform: k(0) }], { duration: Ee, easing: Ke });
|
|
2556
2640
|
const C = [{ transform: "scale(0.4)", opacity: 0 }, { transform: "scale(1)", opacity: 1 }];
|
|
2557
|
-
$.animate(C, { duration:
|
|
2641
|
+
$.animate(C, { duration: ue * 2, delay: Ee * 0.12, easing: Ke, fill: "backwards" }), w.animate(C, { duration: ue * 2, delay: Ee * 0.32 + Ge, easing: Ke, fill: "backwards" });
|
|
2558
2642
|
} else {
|
|
2559
2643
|
const C = [{ opacity: 1 }, { opacity: 0 }];
|
|
2560
|
-
w.animate(C, { duration:
|
|
2644
|
+
w.animate(C, { duration: ue, easing: We }), $.animate(C, { duration: ue, delay: Ge / 2, easing: We, fill: "backwards" }), g.animate([{ transform: k(0) }, { transform: k(34), offset: 0.8 }, { transform: k(32) }], { duration: Ee * 0.7, delay: ue + Ge / 2, easing: We, fill: "backwards" });
|
|
2561
2645
|
}
|
|
2562
2646
|
}
|
|
2563
|
-
const
|
|
2564
|
-
let
|
|
2565
|
-
function
|
|
2566
|
-
|
|
2647
|
+
const Ye = r(".wrap"), Lt = $a({ wrapEl: Ye, launcherEl: l });
|
|
2648
|
+
let Pt = !1;
|
|
2649
|
+
function Ce(c) {
|
|
2650
|
+
Pt = c, i.classList.toggle("minimised", c), i.hidden || oe("panel", c ? "tabs" : "open");
|
|
2567
2651
|
}
|
|
2568
|
-
function
|
|
2569
|
-
i.hidden &&
|
|
2652
|
+
function G({ animate: c = !0 } = {}) {
|
|
2653
|
+
i.hidden && c && At(!0), i.hidden = !1, Ye.classList.add("is-open"), l.setAttribute("aria-expanded", "true"), l.setAttribute("aria-label", "Close Panel"), Ce(!1);
|
|
2570
2654
|
}
|
|
2571
|
-
function
|
|
2572
|
-
i.hidden &&
|
|
2655
|
+
function Zn() {
|
|
2656
|
+
i.hidden && G(), Ce(!0);
|
|
2573
2657
|
}
|
|
2574
|
-
function
|
|
2575
|
-
i.hidden ||
|
|
2658
|
+
function Xe() {
|
|
2659
|
+
i.hidden || At(!1), i.hidden = !0, Ye.classList.remove("is-open"), l.setAttribute("aria-expanded", "false"), l.setAttribute("aria-label", "Open Panel"), oe("panel", "closed"), Te();
|
|
2576
2660
|
}
|
|
2577
|
-
function
|
|
2578
|
-
i.hidden ?
|
|
2661
|
+
function Me() {
|
|
2662
|
+
i.hidden ? G() : Xe();
|
|
2579
2663
|
}
|
|
2580
|
-
let
|
|
2581
|
-
function H(
|
|
2582
|
-
|
|
2664
|
+
let Ot;
|
|
2665
|
+
function H(c) {
|
|
2666
|
+
d.textContent = c, d.classList.add("show"), clearTimeout(Ot), Ot = setTimeout(() => d.classList.remove("show"), 1600);
|
|
2583
2667
|
}
|
|
2584
|
-
function
|
|
2585
|
-
navigator.clipboard.writeText(
|
|
2668
|
+
function he(c, g) {
|
|
2669
|
+
navigator.clipboard.writeText(c).then(() => H(g)).catch(() => H("Couldn’t copy — select the text and copy it instead"));
|
|
2586
2670
|
}
|
|
2587
|
-
let
|
|
2588
|
-
function
|
|
2589
|
-
if (
|
|
2590
|
-
|
|
2671
|
+
let Ze = null;
|
|
2672
|
+
function te(c, g) {
|
|
2673
|
+
if (Ze = c, !c) {
|
|
2674
|
+
u.hidden = !0;
|
|
2591
2675
|
return;
|
|
2592
2676
|
}
|
|
2593
|
-
const w =
|
|
2594
|
-
Object.assign(
|
|
2677
|
+
const w = c.getBoundingClientRect();
|
|
2678
|
+
Object.assign(u.style, { top: `${w.top}px`, left: `${w.left}px`, width: `${w.width}px`, height: `${w.height}px` }), v.textContent = g || vt(c), u.hidden = !1;
|
|
2595
2679
|
}
|
|
2596
|
-
const
|
|
2597
|
-
|
|
2680
|
+
const Ft = () => {
|
|
2681
|
+
Ze && !u.hidden && te(Ze, v.textContent);
|
|
2598
2682
|
};
|
|
2599
|
-
window.addEventListener("scroll",
|
|
2600
|
-
let
|
|
2601
|
-
const
|
|
2602
|
-
function
|
|
2603
|
-
if (
|
|
2604
|
-
const g =
|
|
2605
|
-
g && g.nodeType === 1 && g !== document.documentElement && g !== document.body &&
|
|
2606
|
-
}
|
|
2607
|
-
function
|
|
2608
|
-
if (
|
|
2609
|
-
const g =
|
|
2610
|
-
!g || g === document.documentElement || g === document.body || (
|
|
2611
|
-
}
|
|
2612
|
-
function
|
|
2613
|
-
|
|
2614
|
-
}
|
|
2615
|
-
const
|
|
2616
|
-
function
|
|
2617
|
-
if (!
|
|
2618
|
-
if (
|
|
2619
|
-
const
|
|
2620
|
-
|
|
2683
|
+
window.addEventListener("scroll", Ft, !0), window.addEventListener("resize", Ft);
|
|
2684
|
+
let fe = !1;
|
|
2685
|
+
const Nt = r('[data-action="pick"]'), jt = (c) => c.composedPath().includes(a);
|
|
2686
|
+
function Rt(c) {
|
|
2687
|
+
if (jt(c)) return;
|
|
2688
|
+
const g = c.target;
|
|
2689
|
+
g && g.nodeType === 1 && g !== document.documentElement && g !== document.body && te(g);
|
|
2690
|
+
}
|
|
2691
|
+
function zt(c) {
|
|
2692
|
+
if (jt(c) || (c.preventDefault(), c.stopPropagation(), c.type !== "click")) return;
|
|
2693
|
+
const g = c.target;
|
|
2694
|
+
!g || g === document.documentElement || g === document.body || (Te(), Bt(g));
|
|
2695
|
+
}
|
|
2696
|
+
function It(c) {
|
|
2697
|
+
c.key === "Escape" && (c.preventDefault(), Te(), T && te(T.el));
|
|
2698
|
+
}
|
|
2699
|
+
const Dt = ["pointerdown", "mousedown", "mouseup", "click"];
|
|
2700
|
+
function Ut() {
|
|
2701
|
+
if (!fe) {
|
|
2702
|
+
if (fe = !0, !document.getElementById("twiddle-picking")) {
|
|
2703
|
+
const c = document.createElement("style");
|
|
2704
|
+
c.id = "twiddle-picking", c.textContent = "html.twd-picking, html.twd-picking * { cursor: crosshair !important; }", document.head.appendChild(c);
|
|
2621
2705
|
}
|
|
2622
|
-
|
|
2706
|
+
G(), Nt.setAttribute("aria-pressed", "true"), document.documentElement.classList.add("twd-picking"), document.addEventListener("pointermove", Rt, !0), Dt.forEach((c) => document.addEventListener(c, zt, !0)), document.addEventListener("keydown", It, !0), H("Click an element · Esc to cancel");
|
|
2623
2707
|
}
|
|
2624
2708
|
}
|
|
2625
|
-
function
|
|
2626
|
-
|
|
2709
|
+
function Te() {
|
|
2710
|
+
fe && (fe = !1, Nt.setAttribute("aria-pressed", "false"), document.documentElement.classList.remove("twd-picking"), document.removeEventListener("pointermove", Rt, !0), Dt.forEach((c) => document.removeEventListener(c, zt, !0)), document.removeEventListener("keydown", It, !0), te(T ? T.el : null));
|
|
2627
2711
|
}
|
|
2628
2712
|
let T = null;
|
|
2629
|
-
function
|
|
2713
|
+
function Jn(c, g) {
|
|
2630
2714
|
const w = [];
|
|
2631
2715
|
return {
|
|
2632
2716
|
ctx: {
|
|
2633
|
-
getState: () => (
|
|
2634
|
-
setSection: ($, k) =>
|
|
2717
|
+
getState: () => (t.getState().scopes || {})[c] || {},
|
|
2718
|
+
setSection: ($, k) => t.setScope(c, $, k),
|
|
2635
2719
|
onChange: ($) => {
|
|
2636
|
-
const k =
|
|
2720
|
+
const k = t.onChange($);
|
|
2637
2721
|
return w.push(k), k;
|
|
2638
2722
|
},
|
|
2639
2723
|
readToken: ($) => getComputedStyle(g).getPropertyValue($).trim()
|
|
@@ -2641,262 +2725,263 @@ function vo({ sections: t, store: e, view: n }) {
|
|
|
2641
2725
|
dispose: () => w.forEach(($) => $())
|
|
2642
2726
|
};
|
|
2643
2727
|
}
|
|
2644
|
-
function
|
|
2645
|
-
const w =
|
|
2646
|
-
g || (g = !$[w.elementSelector] && w.classSelector && $[w.classSelector] ? "class" : "element"), T && T.dispose(), T = { el:
|
|
2647
|
-
} },
|
|
2728
|
+
function Bt(c, g) {
|
|
2729
|
+
const w = La(c), $ = t.getState().scopes || {};
|
|
2730
|
+
g || (g = !$[w.elementSelector] && w.classSelector && $[w.classSelector] ? "class" : "element"), T && T.dispose(), T = { el: c, info: w, target: g, dispose: () => {
|
|
2731
|
+
} }, Je(), W("local"), G(), te(c);
|
|
2648
2732
|
}
|
|
2649
|
-
const
|
|
2650
|
-
function
|
|
2733
|
+
const qt = () => T.target === "class" ? T.info.classSelector : T.info.elementSelector;
|
|
2734
|
+
function Je() {
|
|
2651
2735
|
T.dispose();
|
|
2652
|
-
const { info:
|
|
2736
|
+
const { info: c } = T, g = e.filter((j) => (j.uses || []).some((z) => c.tokens.has(z))), w = p.local;
|
|
2653
2737
|
if (w.innerHTML = `
|
|
2654
2738
|
<div class="scope-bar">
|
|
2655
2739
|
<div class="scope-head">
|
|
2656
2740
|
<button class="icon-btn" data-action="back" title="Back to the whole page" aria-label="Back to the whole page">${A.back}</button>
|
|
2657
2741
|
<div class="scope-name">
|
|
2658
|
-
<code>${
|
|
2659
|
-
${
|
|
2742
|
+
<code>${O(vt(c.el))}</code>
|
|
2743
|
+
${c.text ? `<span class="muted">“${O(c.text)}”</span>` : ""}
|
|
2660
2744
|
</div>
|
|
2661
2745
|
</div>
|
|
2662
2746
|
<div class="scope-target"></div>
|
|
2663
2747
|
<div class="warnings"></div>
|
|
2664
2748
|
</div>
|
|
2665
2749
|
${g.length ? s(g) : '<div class="empty">This element doesn’t use Panel tokens yet, so there’s nothing to tweak here. Style it with <code>var(--twd-…)</code> to make it tweakable.</div>'}
|
|
2666
|
-
`,
|
|
2667
|
-
const j =
|
|
2750
|
+
`, c.classSelector) {
|
|
2751
|
+
const j = ce({
|
|
2668
2752
|
label: "Apply to",
|
|
2669
2753
|
options: [
|
|
2670
2754
|
{ value: "element", label: "This element" },
|
|
2671
|
-
{ value: "class", label: `All ${
|
|
2755
|
+
{ value: "class", label: `All ${c.classSelector} (${c.classCount})`, title: `Every element matching ${c.classSelector}` }
|
|
2672
2756
|
],
|
|
2673
2757
|
onSelect: (z) => {
|
|
2674
|
-
z === "class" &&
|
|
2758
|
+
z === "class" && t.moveScope(c.elementSelector, c.classSelector), T.target = z, Je();
|
|
2675
2759
|
}
|
|
2676
2760
|
});
|
|
2677
2761
|
j.setValue(T.target), w.querySelector(".scope-target").append(j.el);
|
|
2678
2762
|
}
|
|
2679
|
-
const { ctx: $, dispose: k } =
|
|
2763
|
+
const { ctx: $, dispose: k } = Jn(qt(), c.el), C = m(w, g, $);
|
|
2680
2764
|
C[0] && (C[0].open = !0);
|
|
2681
2765
|
const q = w.querySelector(".warnings"), S = () => {
|
|
2682
|
-
const j =
|
|
2683
|
-
q.innerHTML = j ?
|
|
2766
|
+
const j = c.tokens.size ? Da(c.el) : null;
|
|
2767
|
+
q.innerHTML = j ? ee([j]) : "";
|
|
2684
2768
|
};
|
|
2685
2769
|
S();
|
|
2686
|
-
const
|
|
2770
|
+
const be = t.onChange(S);
|
|
2687
2771
|
T.dispose = () => {
|
|
2688
|
-
k(),
|
|
2772
|
+
k(), be();
|
|
2689
2773
|
};
|
|
2690
2774
|
}
|
|
2691
|
-
function
|
|
2775
|
+
function Ht() {
|
|
2692
2776
|
if (!T || !T.info.classSelector) return;
|
|
2693
|
-
const
|
|
2694
|
-
!
|
|
2777
|
+
const c = t.getState().scopes || {}, g = T.target === "class" ? "element" : "class", w = g === "class" ? T.info.classSelector : T.info.elementSelector;
|
|
2778
|
+
!c[qt()] && c[w] && (T.target = g, Je());
|
|
2695
2779
|
}
|
|
2696
|
-
const
|
|
2697
|
-
|
|
2698
|
-
},
|
|
2699
|
-
|
|
2780
|
+
const Vt = () => {
|
|
2781
|
+
t.undo(), Ht();
|
|
2782
|
+
}, _t = () => {
|
|
2783
|
+
t.redo(), Ht();
|
|
2700
2784
|
};
|
|
2701
|
-
function
|
|
2702
|
-
T && (T.dispose(), T = null,
|
|
2703
|
-
}
|
|
2704
|
-
const
|
|
2705
|
-
function
|
|
2706
|
-
const
|
|
2707
|
-
if (!
|
|
2708
|
-
w.innerHTML = `<div class="empty">No changes yet. Tweak a section, or pick an element to change it on its own.</div>${
|
|
2785
|
+
function Qn() {
|
|
2786
|
+
T && (T.dispose(), T = null, p.local.innerHTML = "", te(null));
|
|
2787
|
+
}
|
|
2788
|
+
const Kt = (c) => c.kind === "global" ? "Whole page" : c.kind === "class" ? `All ${c.selector}` : "One element";
|
|
2789
|
+
function Wt() {
|
|
2790
|
+
const c = ln(t.getState(), e), g = nt(), w = p.changes;
|
|
2791
|
+
if (!c.length) {
|
|
2792
|
+
w.innerHTML = `<div class="empty">No changes yet. Tweak a section, or pick an element to change it on its own.</div>${ee(g)}`;
|
|
2709
2793
|
return;
|
|
2710
2794
|
}
|
|
2711
2795
|
const $ = /* @__PURE__ */ new Map();
|
|
2712
|
-
|
|
2796
|
+
c.forEach((k) => {
|
|
2713
2797
|
const C = k.selector || "";
|
|
2714
2798
|
$.has(C) || $.set(C, []), $.get(C).push(k);
|
|
2715
|
-
}), w.innerHTML = (g.length ? `<div class="warnings">${
|
|
2799
|
+
}), w.innerHTML = (g.length ? `<div class="warnings">${ee(g)}</div>` : "") + [...$].map(([k, C]) => `
|
|
2716
2800
|
<div class="change-group">
|
|
2717
2801
|
<div class="change-scope">
|
|
2718
|
-
${k ? `<button class="link-btn" data-select="${
|
|
2802
|
+
${k ? `<button class="link-btn" data-select="${O(k)}" title="Select it on the page">${O(Kt(C[0]))}</button><code>${O(k)}</code>` : `<span>${Kt(C[0])}</span>`}
|
|
2719
2803
|
</div>
|
|
2720
2804
|
${C.map((q) => `
|
|
2721
2805
|
<div class="change-row">
|
|
2722
2806
|
<div class="change-main">
|
|
2723
|
-
<div class="change-title">${
|
|
2724
|
-
${q.tokens.map((S) => `<div class="change-token"><code>${
|
|
2807
|
+
<div class="change-title">${O(q.title)} <span class="muted">›</span> ${q.settings.map((S) => `${O(S.label)}: <b>${O(S.value)}</b>`).join(", ")}</div>
|
|
2808
|
+
${q.tokens.map((S) => `<div class="change-token"><code>${O(S.name)}</code> ${S.from ? `${O(S.from)} → ` : ""}${O(S.to)}</div>`).join("")}
|
|
2725
2809
|
</div>
|
|
2726
|
-
<button class="icon-btn" data-remove="${
|
|
2810
|
+
<button class="icon-btn" data-remove="${O(q.sectionId)}" data-remove-scope="${O(k)}" aria-label="Remove ${O(q.title)} change">${A.close}</button>
|
|
2727
2811
|
</div>`).join("")}
|
|
2728
2812
|
</div>`).join("");
|
|
2729
2813
|
}
|
|
2730
|
-
function
|
|
2731
|
-
if (g) return
|
|
2732
|
-
const w =
|
|
2733
|
-
|
|
2814
|
+
function ea(c, g) {
|
|
2815
|
+
if (g) return t.removeScope(g, c);
|
|
2816
|
+
const w = t.getState()[c] || {};
|
|
2817
|
+
t.setSection(c, Object.fromEntries(Object.keys(w).map(($) => [$, null])));
|
|
2734
2818
|
}
|
|
2735
|
-
const
|
|
2736
|
-
function
|
|
2737
|
-
|
|
2819
|
+
const ge = r(".apply-menu"), Gt = r('[data-action="apply-menu"]'), Yt = () => Z("apply-mode") === "copy" ? "copy" : "preview";
|
|
2820
|
+
function ta() {
|
|
2821
|
+
ge.querySelectorAll("[data-apply-mode]").forEach((c) => c.setAttribute("aria-checked", String(c.dataset.applyMode === Yt())));
|
|
2738
2822
|
}
|
|
2739
|
-
function
|
|
2740
|
-
|
|
2823
|
+
function Ae(c) {
|
|
2824
|
+
ge.hidden = !c, Gt.setAttribute("aria-expanded", String(c)), c && ta();
|
|
2741
2825
|
}
|
|
2742
|
-
function
|
|
2743
|
-
return
|
|
2826
|
+
function Xt() {
|
|
2827
|
+
return ja(ln(t.getState(), e), { warnings: nt() });
|
|
2744
2828
|
}
|
|
2745
|
-
const
|
|
2746
|
-
function
|
|
2747
|
-
const
|
|
2748
|
-
if (!
|
|
2749
|
-
if (
|
|
2750
|
-
|
|
2829
|
+
const Le = r(".dialog-backdrop"), Zt = r(".prompt-text");
|
|
2830
|
+
function na() {
|
|
2831
|
+
const c = Xt();
|
|
2832
|
+
if (!c) return H("No changes to copy yet");
|
|
2833
|
+
if (Yt() === "copy") return he(c, "Prompt copied · paste it into your agent");
|
|
2834
|
+
Zt.value = c, Le.hidden = !1, r('[data-action="dialog-copy"]').focus();
|
|
2751
2835
|
}
|
|
2752
|
-
function
|
|
2753
|
-
|
|
2836
|
+
function Qe() {
|
|
2837
|
+
Le.hidden = !0, r('[data-action="apply"]').focus();
|
|
2754
2838
|
}
|
|
2755
|
-
const
|
|
2756
|
-
function
|
|
2757
|
-
|
|
2839
|
+
const ne = r('[data-action="compare"]'), aa = r(".original-pill");
|
|
2840
|
+
function Pe(c) {
|
|
2841
|
+
c ? n.suspend() : n.resume(), ne.setAttribute("aria-pressed", String(c)), aa.hidden = !c;
|
|
2758
2842
|
}
|
|
2759
|
-
let
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2843
|
+
let Jt = null, me = !1;
|
|
2844
|
+
ne.addEventListener("pointerdown", () => {
|
|
2845
|
+
me = !1, Jt = setTimeout(() => {
|
|
2846
|
+
me = !0, Pe(!0);
|
|
2763
2847
|
}, 180);
|
|
2764
2848
|
});
|
|
2765
|
-
const
|
|
2766
|
-
clearTimeout(
|
|
2849
|
+
const Qt = () => {
|
|
2850
|
+
clearTimeout(Jt), me && (Pe(!1), me = !1);
|
|
2767
2851
|
};
|
|
2768
|
-
|
|
2769
|
-
const
|
|
2852
|
+
ne.addEventListener("pointerup", Qt), ne.addEventListener("pointerleave", Qt);
|
|
2853
|
+
const Oe = r(".setup-hint"), en = `setup-hint-dismissed:${location.pathname}`, oa = {
|
|
2770
2854
|
"no-tokens": {
|
|
2771
2855
|
title: "The Panel tokens aren’t loaded",
|
|
2772
2856
|
text: "The knobs change the --twd-* variables, but this page doesn’t load them. Add the token stylesheet before your own CSS:",
|
|
2773
|
-
snippets:
|
|
2774
|
-
link: `${
|
|
2857
|
+
snippets: fn,
|
|
2858
|
+
link: `${hn}/#install`
|
|
2775
2859
|
},
|
|
2776
2860
|
unused: {
|
|
2777
2861
|
title: "Nothing here uses the Panel tokens yet",
|
|
2778
2862
|
text: "The tokens are loaded, but no style on this page uses var(--twd-…), so the knobs won’t change anything. Ask your coding agent to convert the styles:",
|
|
2779
2863
|
copy: "Copy prompt for your agent",
|
|
2780
2864
|
copied: "Prompt copied",
|
|
2781
|
-
text2copy:
|
|
2782
|
-
link: `${
|
|
2865
|
+
text2copy: Ka,
|
|
2866
|
+
link: `${hn}/#tokens`
|
|
2783
2867
|
}
|
|
2784
2868
|
};
|
|
2785
2869
|
let D = null;
|
|
2786
|
-
function
|
|
2787
|
-
const
|
|
2788
|
-
D =
|
|
2789
|
-
<div class="setup-head">${A.warn}<b>${
|
|
2870
|
+
function Fe() {
|
|
2871
|
+
const c = Va();
|
|
2872
|
+
D = Z(en) === c ? null : oa[c] || null, l.toggleAttribute("data-hint", !!D), Oe.hidden = !D, D && (Oe.innerHTML = `
|
|
2873
|
+
<div class="setup-head">${A.warn}<b>${O(D.title)}</b>
|
|
2790
2874
|
<button class="icon-btn" data-action="setup-dismiss" title="Hide this message" aria-label="Hide this message">${A.close}</button>
|
|
2791
2875
|
</div>
|
|
2792
|
-
<p>${
|
|
2876
|
+
<p>${O(D.text)}</p>
|
|
2793
2877
|
${(D.snippets || []).map((g, w) => `
|
|
2794
2878
|
<div class="setup-snippet">
|
|
2795
|
-
<span>${
|
|
2796
|
-
<div class="setup-code"><code>${
|
|
2879
|
+
<span>${O(g.label)}</span>
|
|
2880
|
+
<div class="setup-code"><code>${O(g.code)}</code><button class="text-btn" data-setup-snippet="${w}">Copy</button></div>
|
|
2797
2881
|
</div>`).join("")}
|
|
2798
2882
|
<div class="setup-actions">
|
|
2799
|
-
${D.copy ? `<button class="apply-btn" data-action="setup-copy">${
|
|
2883
|
+
${D.copy ? `<button class="apply-btn" data-action="setup-copy">${O(D.copy)}</button>` : ""}
|
|
2800
2884
|
<a href="${D.link}" target="_blank" rel="noopener">Setup guide</a>
|
|
2801
|
-
</div>`,
|
|
2802
|
-
}
|
|
2803
|
-
|
|
2804
|
-
const
|
|
2805
|
-
function
|
|
2806
|
-
r('[data-action="undo"]').disabled = !
|
|
2807
|
-
const
|
|
2808
|
-
|
|
2809
|
-
const g =
|
|
2810
|
-
|
|
2885
|
+
</div>`, Oe.dataset.status = c);
|
|
2886
|
+
}
|
|
2887
|
+
Fe(), document.readyState !== "complete" && window.addEventListener("load", Fe, { once: !0 }), setTimeout(Fe, 1500);
|
|
2888
|
+
const tn = r(".badge");
|
|
2889
|
+
function nn() {
|
|
2890
|
+
r('[data-action="undo"]').disabled = !t.canUndo(), r('[data-action="redo"]').disabled = !t.canRedo();
|
|
2891
|
+
const c = Fa(t.getState());
|
|
2892
|
+
tn.textContent = c, tn.hidden = !c;
|
|
2893
|
+
const g = ee(nt());
|
|
2894
|
+
p.global.querySelectorAll(".page-warnings").forEach((w) => {
|
|
2811
2895
|
w.innerHTML = g;
|
|
2812
|
-
}), K === "changes" &&
|
|
2896
|
+
}), K === "changes" && Wt();
|
|
2813
2897
|
}
|
|
2814
|
-
return
|
|
2815
|
-
if (
|
|
2816
|
-
const g =
|
|
2898
|
+
return t.onChange(nn), nn(), o.addEventListener("click", (c) => {
|
|
2899
|
+
if (c.target === Le) return Qe();
|
|
2900
|
+
const g = c.target.closest("button");
|
|
2817
2901
|
if (g) {
|
|
2818
|
-
if (g !==
|
|
2902
|
+
if (g !== Gt && !ge.contains(g) && Ae(!1), g === l)
|
|
2903
|
+
return Lt.consumeClick() ? void 0 : c.detail === 2 ? (Me(), Lt.reset(), H("Panel moved back to the corner")) : Me();
|
|
2819
2904
|
if (g.dataset.select) {
|
|
2820
2905
|
const w = document.querySelector(g.dataset.select);
|
|
2821
|
-
return w ?
|
|
2906
|
+
return w ? Bt(w) : H("That element isn’t on this page");
|
|
2822
2907
|
}
|
|
2823
|
-
if (g.dataset.setupSnippet) return
|
|
2908
|
+
if (g.dataset.setupSnippet) return he(fn[g.dataset.setupSnippet].code, "Copied");
|
|
2824
2909
|
if (g.dataset.tab)
|
|
2825
|
-
return
|
|
2826
|
-
if (g.dataset.remove) return
|
|
2910
|
+
return W("global"), Pt && Ce(!1), E(g.dataset.tab);
|
|
2911
|
+
if (g.dataset.remove) return ea(g.dataset.remove, g.dataset.removeScope);
|
|
2827
2912
|
if (g.dataset.applyMode)
|
|
2828
|
-
return
|
|
2913
|
+
return oe("apply-mode", g.dataset.applyMode), Ae(!1), H(g.dataset.applyMode === "copy" ? "Copy prompt copies straight away" : "Copy prompt shows a preview first");
|
|
2829
2914
|
switch (g.dataset.action) {
|
|
2830
2915
|
case "close":
|
|
2831
|
-
return
|
|
2916
|
+
return Xe();
|
|
2832
2917
|
case "minimise":
|
|
2833
|
-
return
|
|
2918
|
+
return Zn(), (o.querySelector('.tab[aria-pressed="true"]') || h[0]).focus();
|
|
2834
2919
|
case "reset":
|
|
2835
|
-
return
|
|
2920
|
+
return t.resetState(), H("Reset to defaults · ⌘Z to undo");
|
|
2836
2921
|
case "copy":
|
|
2837
|
-
return
|
|
2922
|
+
return he(location.href, "Link copied");
|
|
2838
2923
|
case "pick":
|
|
2839
|
-
return
|
|
2924
|
+
return fe ? Te() : Ut();
|
|
2840
2925
|
case "compare":
|
|
2841
|
-
|
|
2926
|
+
me || Pe(ne.getAttribute("aria-pressed") !== "true");
|
|
2842
2927
|
return;
|
|
2843
2928
|
case "undo":
|
|
2844
|
-
return
|
|
2929
|
+
return Vt();
|
|
2845
2930
|
case "redo":
|
|
2846
|
-
return
|
|
2931
|
+
return _t();
|
|
2847
2932
|
case "back":
|
|
2848
|
-
return
|
|
2933
|
+
return W("global");
|
|
2849
2934
|
case "changes":
|
|
2850
|
-
return
|
|
2935
|
+
return W(K === "changes" ? "global" : "changes");
|
|
2851
2936
|
case "apply":
|
|
2852
|
-
return
|
|
2937
|
+
return na();
|
|
2853
2938
|
case "apply-menu":
|
|
2854
|
-
return
|
|
2939
|
+
return Ae(ge.hidden);
|
|
2855
2940
|
case "dialog-close":
|
|
2856
|
-
return
|
|
2941
|
+
return Qe();
|
|
2857
2942
|
case "dialog-copy":
|
|
2858
|
-
return
|
|
2943
|
+
return he(Zt.value, "Prompt copied · paste it into your agent");
|
|
2859
2944
|
case "setup-copy":
|
|
2860
|
-
return D &&
|
|
2945
|
+
return D && he(D.text2copy, D.copied);
|
|
2861
2946
|
case "setup-dismiss":
|
|
2862
|
-
return
|
|
2947
|
+
return oe(en, Oe.dataset.status), Fe();
|
|
2863
2948
|
}
|
|
2864
2949
|
}
|
|
2865
|
-
}), o.addEventListener("keydown", (
|
|
2866
|
-
if (
|
|
2867
|
-
if (!
|
|
2868
|
-
if (!
|
|
2950
|
+
}), o.addEventListener("keydown", (c) => {
|
|
2951
|
+
if (c.key === "Escape") {
|
|
2952
|
+
if (!Le.hidden) return Qe();
|
|
2953
|
+
if (!ge.hidden) return Ae(!1);
|
|
2869
2954
|
}
|
|
2870
|
-
!(
|
|
2871
|
-
}), document.addEventListener("keydown", (
|
|
2872
|
-
|
|
2873
|
-
}),
|
|
2955
|
+
!(c.metaKey || c.ctrlKey) || c.key.toLowerCase() !== "z" || c.target.matches?.('input[type="text"], textarea') || (c.preventDefault(), c.shiftKey ? _t() : Vt());
|
|
2956
|
+
}), document.addEventListener("keydown", (c) => {
|
|
2957
|
+
c.altKey && (c.code === "KeyT" && (c.preventDefault(), Me()), c.code === "KeyC" && (c.preventDefault(), Pe(ne.getAttribute("aria-pressed") !== "true")));
|
|
2958
|
+
}), Z("panel") === "open" ? G({ animate: !1 }) : Z("panel") === "tabs" && (G({ animate: !1 }), Ce(!0)), { open: G, close: Xe, toggle: Me, toast: H, pick: Ut, prompt: Xt };
|
|
2874
2959
|
}
|
|
2875
|
-
const
|
|
2876
|
-
function
|
|
2960
|
+
const Cn = [vo, He, Wn, St, to, $t, mo, eo], Ao = ':root{--twd-font-heading: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;--twd-font-body: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;--twd-text-xs: .694rem;--twd-text-sm: .833rem;--twd-text-base: 1rem;--twd-text-lg: 1.2rem;--twd-text-xl: 1.44rem;--twd-text-2xl: 1.728rem;--twd-text-3xl: 2.074rem;--twd-leading-tight: 1.2;--twd-leading-normal: 1.5;--twd-weight-heading: 600;--twd-weight-strong: 600;--twd-bg: #fafafa;--twd-surface: #ffffff;--twd-surface-raised: #ffffff;--twd-text: #18181b;--twd-text-muted: #71717a;--twd-border: #e4e4e7;--twd-primary: #4f46e5;--twd-primary-hover: #4338ca;--twd-on-primary: #ffffff;--twd-focus: #6366f1;--twd-success: #16a34a;--twd-warning: #d97706;--twd-danger: #dc2626;--twd-radius-control: 8px;--twd-radius-container: 12px;--twd-space-1: 4px;--twd-space-2: 8px;--twd-space-3: 12px;--twd-space-4: 16px;--twd-space-5: 24px;--twd-space-6: 32px;--twd-space-7: 48px;--twd-space-8: 64px;--twd-control-height: 40px;--twd-control-pad-x: 16px;--twd-page-pad: var(--twd-space-5);--twd-section-gap: var(--twd-space-6);--twd-card-pad: var(--twd-space-5);--twd-card-gap: var(--twd-space-2);--twd-control-gap: var(--twd-space-3);--twd-border-width: 1px;--twd-border-width-container: var(--twd-border-width);--twd-shadow-sm: 0 1px 2px rgb(0 0 0 / .06);--twd-shadow-md: 0 4px 12px rgb(0 0 0 / .08);--twd-shadow-lg: 0 12px 32px rgb(0 0 0 / .12);--twd-button-bg: var(--twd-primary);--twd-button-fg: var(--twd-on-primary);--twd-button-border: transparent;--twd-button-hover: var(--twd-primary-hover);--twd-card-bg: var(--twd-surface);--twd-card-border: var(--twd-border);--twd-card-shadow: var(--twd-shadow-sm)}';
|
|
2961
|
+
function Lo() {
|
|
2877
2962
|
if (getComputedStyle(document.documentElement).getPropertyValue("--twd-radius-control").trim() || document.getElementById("twiddle-tokens")) return;
|
|
2878
|
-
const
|
|
2879
|
-
|
|
2963
|
+
const t = document.createElement("style");
|
|
2964
|
+
t.id = "twiddle-tokens", t.textContent = Ao, document.head.prepend(t);
|
|
2880
2965
|
}
|
|
2881
2966
|
if (!window.Twiddle) {
|
|
2882
|
-
|
|
2883
|
-
let
|
|
2884
|
-
const
|
|
2885
|
-
mt(
|
|
2967
|
+
Lo(), Cn.forEach((n) => ga(n.tokens)), Pn(wt);
|
|
2968
|
+
let e;
|
|
2969
|
+
const t = () => {
|
|
2970
|
+
wt(mt()), e = To({ sections: Cn, store: fa, view: { suspend: wa, resume: ya } });
|
|
2886
2971
|
};
|
|
2887
|
-
document.readyState === "loading" ? document.addEventListener("DOMContentLoaded",
|
|
2888
|
-
open: () =>
|
|
2889
|
-
close: () =>
|
|
2890
|
-
toggle: () =>
|
|
2891
|
-
reset: () =>
|
|
2892
|
-
undo: () =>
|
|
2893
|
-
redo: () =>
|
|
2894
|
-
pick: () =>
|
|
2895
|
-
prompt: () =>
|
|
2896
|
-
getState:
|
|
2972
|
+
document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", t) : t(), window.Twiddle = {
|
|
2973
|
+
open: () => e && e.open(),
|
|
2974
|
+
close: () => e && e.close(),
|
|
2975
|
+
toggle: () => e && e.toggle(),
|
|
2976
|
+
reset: () => Tn(),
|
|
2977
|
+
undo: () => An(),
|
|
2978
|
+
redo: () => Ln(),
|
|
2979
|
+
pick: () => e && e.pick(),
|
|
2980
|
+
prompt: () => e ? e.prompt() : "",
|
|
2981
|
+
getState: mt
|
|
2897
2982
|
};
|
|
2898
2983
|
}
|
|
2899
|
-
const
|
|
2984
|
+
const Po = window.Twiddle;
|
|
2900
2985
|
export {
|
|
2901
|
-
|
|
2986
|
+
Po as Twiddle
|
|
2902
2987
|
};
|