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