@weasel-js/ui 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/chunks/{Callout-CvTKW1sd.js → Callout-Dt0kBIls.js} +1 -1
  2. package/dist/chunks/{Callout-CvTKW1sd.js.map → Callout-Dt0kBIls.js.map} +1 -1
  3. package/dist/chunks/{Checkbox-BeWFZr1M.js → Checkbox-CtK8H_OQ.js} +1 -1
  4. package/dist/chunks/{Checkbox-BeWFZr1M.js.map → Checkbox-CtK8H_OQ.js.map} +1 -1
  5. package/dist/chunks/{ColorField-B7vHaU5G.js → ColorField-sGLnThpB.js} +1 -1
  6. package/dist/chunks/{ColorField-B7vHaU5G.js.map → ColorField-sGLnThpB.js.map} +1 -1
  7. package/dist/chunks/{ComboBox-yfrxG0_I.js → ComboBox-pH8aDEL2.js} +2 -2
  8. package/dist/chunks/{ComboBox-yfrxG0_I.js.map → ComboBox-pH8aDEL2.js.map} +1 -1
  9. package/dist/chunks/{CurveEditor-BEQtef4P.js → CurveEditor-DrORWShU.js} +1 -1
  10. package/dist/chunks/{CurveEditor-BEQtef4P.js.map → CurveEditor-DrORWShU.js.map} +1 -1
  11. package/dist/chunks/{Dialog-tAi14bY3.js → Dialog-CQymDCAl.js} +1 -1
  12. package/dist/chunks/{Dialog-tAi14bY3.js.map → Dialog-CQymDCAl.js.map} +1 -1
  13. package/dist/chunks/{Field-4cagmYlT.js → Field-BHSu2F3E.js} +1 -1
  14. package/dist/chunks/{Field-4cagmYlT.js.map → Field-BHSu2F3E.js.map} +1 -1
  15. package/dist/chunks/{Input-DRei6KJr.js → Input-D_Xzgr2b.js} +2 -2
  16. package/dist/chunks/{Input-DRei6KJr.js.map → Input-D_Xzgr2b.js.map} +1 -1
  17. package/dist/chunks/{NumberField-BAmJwxw0.js → NumberField-BQmELz32.js} +2 -2
  18. package/dist/chunks/{NumberField-BAmJwxw0.js.map → NumberField-BQmELz32.js.map} +1 -1
  19. package/dist/chunks/{PointPlotter-Dvh5cyOC.js → PointPlotter-D528sBdT.js} +2 -2
  20. package/dist/chunks/{PointPlotter-Dvh5cyOC.js.map → PointPlotter-D528sBdT.js.map} +1 -1
  21. package/dist/chunks/{Powerline-W_yqAVMc.js → Powerline-C8rddbbK.js} +1 -1
  22. package/dist/chunks/{Powerline-W_yqAVMc.js.map → Powerline-C8rddbbK.js.map} +1 -1
  23. package/dist/chunks/{Prefs-NOoGuUHy.js → Prefs-snBejOzK.js} +10 -10
  24. package/dist/chunks/{Prefs-NOoGuUHy.js.map → Prefs-snBejOzK.js.map} +1 -1
  25. package/dist/chunks/{RadioGroup-DUlTy95a.js → RadioGroup-Ci-I9NEx.js} +2 -2
  26. package/dist/chunks/{RadioGroup-DUlTy95a.js.map → RadioGroup-Ci-I9NEx.js.map} +1 -1
  27. package/dist/chunks/{RangeSlider-Bks9GQuO.js → RangeSlider-CW-edtNx.js} +1 -1
  28. package/dist/chunks/{RangeSlider-Bks9GQuO.js.map → RangeSlider-CW-edtNx.js.map} +1 -1
  29. package/dist/chunks/{Select-BV70VU5O.js → Select-l7pAkVEG.js} +2 -2
  30. package/dist/chunks/{Select-BV70VU5O.js.map → Select-l7pAkVEG.js.map} +1 -1
  31. package/dist/chunks/{SelectionPanel-D_U7GKup.js → SelectionPanel-CMglTgEf.js} +92 -72
  32. package/dist/chunks/SelectionPanel-CMglTgEf.js.map +1 -0
  33. package/dist/chunks/{Sidebar-Bk9_TPhX.js → Sidebar-BFcBx8QV.js} +1 -1
  34. package/dist/chunks/{Sidebar-Bk9_TPhX.js.map → Sidebar-BFcBx8QV.js.map} +1 -1
  35. package/dist/chunks/{SidebarPanel-Nd9Od5WD.js → SidebarPanel-CRnSlvee.js} +1 -1
  36. package/dist/chunks/{SidebarPanel-Nd9Od5WD.js.map → SidebarPanel-CRnSlvee.js.map} +1 -1
  37. package/dist/chunks/{Switch-iUmlnP_b.js → Switch-DxJLMYki.js} +1 -1
  38. package/dist/chunks/{Switch-iUmlnP_b.js.map → Switch-DxJLMYki.js.map} +1 -1
  39. package/dist/chunks/{Tabs-DCs-Ww2f.js → Tabs-DelDecks.js} +1 -1
  40. package/dist/chunks/{Tabs-DCs-Ww2f.js.map → Tabs-DelDecks.js.map} +1 -1
  41. package/dist/chunks/{Toast-NdSJo2vj.js → Toast-Bw3XQxmV.js} +1 -1
  42. package/dist/chunks/{Toast-NdSJo2vj.js.map → Toast-Bw3XQxmV.js.map} +1 -1
  43. package/dist/chunks/{ToggleBar-BfcvLaOr.js → ToggleBar-Defj6Asn.js} +31 -22
  44. package/dist/chunks/ToggleBar-Defj6Asn.js.map +1 -0
  45. package/dist/chunks/{ToolButton-GAzBOvcR.js → ToolButton-Dk_uJf7N.js} +1 -1
  46. package/dist/chunks/{ToolButton-GAzBOvcR.js.map → ToolButton-Dk_uJf7N.js.map} +1 -1
  47. package/dist/chunks/{ToolGroup-Btg47vJF.js → ToolGroup-D8-qdtuw.js} +1 -1
  48. package/dist/chunks/{ToolGroup-Btg47vJF.js.map → ToolGroup-D8-qdtuw.js.map} +1 -1
  49. package/dist/chunks/ToolOptionsBar-ChiL-HVA.js +27 -0
  50. package/dist/chunks/ToolOptionsBar-ChiL-HVA.js.map +1 -0
  51. package/dist/chunks/{ToolPalette-C8V-I-uX.js → ToolPalette-xKHzPKYf.js} +3 -3
  52. package/dist/chunks/{ToolPalette-C8V-I-uX.js.map → ToolPalette-xKHzPKYf.js.map} +1 -1
  53. package/dist/components/Callout/index.js +1 -1
  54. package/dist/components/Checkbox/index.js +1 -1
  55. package/dist/components/ColorField/index.js +1 -1
  56. package/dist/components/ComboBox/index.js +1 -1
  57. package/dist/components/CurveEditor/index.js +1 -1
  58. package/dist/components/Dialog/index.js +1 -1
  59. package/dist/components/Field/index.js +1 -1
  60. package/dist/components/Input/index.js +1 -1
  61. package/dist/components/NumberField/index.js +1 -1
  62. package/dist/components/PointPlotter/index.js +1 -1
  63. package/dist/components/Powerline/index.js +1 -1
  64. package/dist/components/Prefs/index.js +1 -1
  65. package/dist/components/RadioGroup/index.js +1 -1
  66. package/dist/components/RangeSlider/index.js +1 -1
  67. package/dist/components/Select/index.js +1 -1
  68. package/dist/components/SelectionPanel/SelectionPanel.d.ts.map +1 -1
  69. package/dist/components/SelectionPanel/index.d.ts +1 -1
  70. package/dist/components/SelectionPanel/index.d.ts.map +1 -1
  71. package/dist/components/SelectionPanel/index.js +2 -2
  72. package/dist/components/SelectionPanel/model.d.ts +13 -11
  73. package/dist/components/SelectionPanel/model.d.ts.map +1 -1
  74. package/dist/components/Sidebar/index.js +1 -1
  75. package/dist/components/SidebarPanel/index.js +1 -1
  76. package/dist/components/Switch/index.js +1 -1
  77. package/dist/components/Tabs/index.js +1 -1
  78. package/dist/components/Toast/index.js +1 -1
  79. package/dist/components/ToggleBar/ToggleBar.d.ts +17 -0
  80. package/dist/components/ToggleBar/ToggleBar.d.ts.map +1 -1
  81. package/dist/components/ToggleBar/index.js +1 -1
  82. package/dist/components/ToolButton/index.js +1 -1
  83. package/dist/components/ToolGroup/index.js +1 -1
  84. package/dist/components/ToolOptionsBar/ToolOptionsBar.d.ts +44 -0
  85. package/dist/components/ToolOptionsBar/ToolOptionsBar.d.ts.map +1 -0
  86. package/dist/components/ToolOptionsBar/index.d.ts +3 -0
  87. package/dist/components/ToolOptionsBar/index.d.ts.map +1 -0
  88. package/dist/components/ToolOptionsBar/index.js +2 -0
  89. package/dist/components/ToolPalette/index.js +1 -1
  90. package/dist/index.d.ts +1 -0
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js +37 -36
  93. package/dist/index.js.map +1 -1
  94. package/dist/style.css +1 -1
  95. package/package.json +2 -2
  96. package/dist/chunks/SelectionPanel-D_U7GKup.js.map +0 -1
  97. package/dist/chunks/ToggleBar-BfcvLaOr.js.map +0 -1
@@ -1,13 +1,12 @@
1
- import { t as e } from "./ColorField-B7vHaU5G.js";
2
- import { t } from "./Input-DRei6KJr.js";
3
- import { t as n } from "./NumberField-BAmJwxw0.js";
4
- import { t as r } from "./Select-BV70VU5O.js";
5
- import { t as i } from "./Switch-iUmlnP_b.js";
1
+ import { t as e } from "./ColorField-sGLnThpB.js";
2
+ import { t } from "./Input-D_Xzgr2b.js";
3
+ import { t as n } from "./NumberField-BQmELz32.js";
4
+ import { t as r } from "./Select-l7pAkVEG.js";
5
+ import { t as i } from "./Switch-DxJLMYki.js";
6
6
  import { Fragment as a, useState as o, useSyncExternalStore as s } from "react";
7
- import { asNodeId as c } from "@weasel-js/core";
8
- import { Fragment as l, jsx as u, jsxs as d } from "react/jsx-runtime";
7
+ import { MIXED as c, asNodeId as l } from "@weasel-js/core";
8
+ import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
9
9
  //#region src/components/SelectionPanel/model.ts
10
- var f = Symbol("weasel-ui:mixed");
11
10
  function p(e, t) {
12
11
  if (e.kind === "container") return "group";
13
12
  for (let n of t) if (n.matches(e.data)) return n.name;
@@ -67,25 +66,34 @@ function _(e, t) {
67
66
  })).filter((e) => e.leaves.length > 0)
68
67
  })).filter((e) => e.rows.length > 0);
69
68
  }
70
- function v(e) {
71
- let t = e.indexOf(".");
72
- return t < 0 ? null : {
73
- head: e.slice(0, t),
74
- key: e.slice(t + 1)
75
- };
69
+ function v(e, t) {
70
+ let n = t.split(".");
71
+ if (n.length < 2) return;
72
+ let r = n[0], i = r === "pose" ? e.pose : r === "data" ? e.data : void 0;
73
+ for (let e = 1; e < n.length; e++) {
74
+ if (typeof i != "object" || !i) return;
75
+ i = i[n[e]];
76
+ }
77
+ return i;
76
78
  }
77
- function y(e, t) {
78
- let n = v(t);
79
- if (n === null) return;
80
- let { head: r, key: i } = n, a = r === "pose" ? e.pose : r === "data" ? e.data : void 0;
81
- if (!(typeof a != "object" || !a)) return a[i];
79
+ function y(e, t, n) {
80
+ let [r, ...i] = t;
81
+ if (i.length === 0) return Array.isArray(e) ? Object.assign([...e], { [r]: n }) : {
82
+ ...e,
83
+ [r]: n
84
+ };
85
+ let a = e[r], o = y(Array.isArray(a) ? [...a] : typeof a == "object" && a ? { ...a } : {}, i, n);
86
+ return Array.isArray(e) ? Object.assign([...e], { [r]: o }) : {
87
+ ...e,
88
+ [r]: o
89
+ };
82
90
  }
83
91
  function b(e, t) {
84
92
  let n, r = !0;
85
93
  for (let i of e) {
86
- let e = y(i, t);
94
+ let e = v(i, t);
87
95
  if (r) n = e, r = !1;
88
- else if (!Object.is(e, n)) return f;
96
+ else if (!Object.is(e, n)) return c;
89
97
  }
90
98
  return n;
91
99
  }
@@ -111,43 +119,37 @@ var S = {
111
119
  unrenderable: "_unrenderable_nbai7_83"
112
120
  }, C = (e) => e.length === 0 ? e : e[0].toUpperCase() + e.slice(1);
113
121
  function w(e) {
114
- let { scene: t, selection: n, properties: r, routing: i, renderers: o, kindLabel: f = C, emptyState: m = null, className: h } = e;
122
+ let { scene: t, selection: n, properties: r, routing: i, renderers: o, kindLabel: c = C, emptyState: m = null, className: h } = e;
115
123
  s(t.subscribe, t.getVersion, t.getVersion);
116
- let g = n.current.map((e) => t.get(c(e))).filter((e) => e != null), y = g.map((e) => p(e, i)), b = _(y, r);
117
- if (g.length === 0) return /* @__PURE__ */ u("div", {
124
+ let g = n.current.map((e) => t.get(l(e))).filter((e) => e != null), v = g.map((e) => p(e, i)), b = _(v, r);
125
+ if (g.length === 0) return /* @__PURE__ */ d("div", {
118
126
  className: [S.root, h].filter(Boolean).join(" "),
119
127
  children: m
120
128
  });
121
129
  let w = (e, r) => {
122
- let i = v(e.path);
123
- if (i === null) return;
124
- let { head: a, key: o } = i, s = n.current.map(c);
130
+ let [i, ...a] = e.path.split(".");
131
+ if (a.length === 0) return;
132
+ let o = n.current.map(l);
125
133
  t.batch(`Edit ${e.leaf.name}`, () => {
126
- for (let e of s) {
134
+ for (let e of o) {
127
135
  let n = t.get(e);
128
- n && (a === "pose" ? t.setPose(e, {
129
- ...n.pose,
130
- [o]: r
131
- }) : a === "data" && t.update(e, { data: {
132
- ...n.data,
133
- [o]: r
134
- } }));
136
+ n && (i === "pose" ? t.setPose(e, y(n.pose, a, r)) : i === "data" && t.update(e, { data: y(n.data, a, r) }));
135
137
  }
136
138
  });
137
139
  };
138
- return /* @__PURE__ */ d("div", {
140
+ return /* @__PURE__ */ f("div", {
139
141
  className: [S.root, h].filter(Boolean).join(" "),
140
- children: [/* @__PURE__ */ u("header", {
142
+ children: [/* @__PURE__ */ d("header", {
141
143
  className: S.header,
142
- children: g.length === 1 ? /* @__PURE__ */ u("span", {
144
+ children: g.length === 1 ? /* @__PURE__ */ d("span", {
143
145
  className: S.kind,
144
- children: f(y[0])
145
- }) : /* @__PURE__ */ d(l, { children: [/* @__PURE__ */ d("span", {
146
+ children: c(v[0])
147
+ }) : /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ f("span", {
146
148
  className: S.kind,
147
149
  children: [g.length, " selected"]
148
- }), /* @__PURE__ */ u("span", {
150
+ }), /* @__PURE__ */ d("span", {
149
151
  className: S.breakdown,
150
- children: x(y)
152
+ children: x(v)
151
153
  })] })
152
154
  }), b.map((e) => {
153
155
  let t = e.rows.map((e) => ({
@@ -157,20 +159,20 @@ function w(e) {
157
159
  content: T(t, e.leaves.length > 1 ? `${e.label} ${t.leaf.name}` : t.leaf.name, g, o, w)
158
160
  })).filter((e) => e.content != null)
159
161
  })).filter(({ controls: e }) => e.length > 0);
160
- return t.length === 0 ? null : /* @__PURE__ */ d("section", {
162
+ return t.length === 0 ? null : /* @__PURE__ */ f("section", {
161
163
  className: S.section,
162
- children: [e.name !== "" && /* @__PURE__ */ u("h3", {
164
+ children: [e.name !== "" && /* @__PURE__ */ d("h3", {
163
165
  className: S.sectionTitle,
164
166
  children: e.name
165
- }), t.map(({ row: e, controls: t }) => /* @__PURE__ */ d("div", {
167
+ }), t.map(({ row: e, controls: t }) => /* @__PURE__ */ f("div", {
166
168
  className: S.row,
167
- children: [/* @__PURE__ */ u("span", {
169
+ children: [/* @__PURE__ */ d("span", {
168
170
  className: S.rowLabel,
169
171
  title: e.leaves[0].leaf.description,
170
172
  children: e.label
171
- }), /* @__PURE__ */ u("span", {
173
+ }), /* @__PURE__ */ d("span", {
172
174
  className: S.rowControls,
173
- children: t.map(({ panelLeaf: e, content: t }) => /* @__PURE__ */ u(a, { children: t }, e.path))
175
+ children: t.map(({ panelLeaf: e, content: t }) => /* @__PURE__ */ d(a, { children: t }, e.path))
174
176
  })]
175
177
  }, e.leaves[0].path))]
176
178
  }, e.key);
@@ -178,20 +180,25 @@ function w(e) {
178
180
  });
179
181
  }
180
182
  function T(e, t, n, r, i) {
181
- let { path: a, leaf: o } = e, s = b(n, a), c = s === f, l = {
183
+ let { path: a, leaf: o } = e, s = b(n, a), l = s === c, u = {
182
184
  path: a,
183
185
  pref: o,
184
- value: c ? void 0 : s,
185
- mixed: c,
186
+ value: l ? void 0 : s,
187
+ mixed: l,
186
188
  setValue: (t) => i(e, t)
187
- }, u = r?.[a] ?? r?.[o.kind];
188
- return u ? u(l) : E(l, t);
189
+ }, d = r?.[a] ?? r?.[o.kind];
190
+ return d ? d(u) : D(u, t);
189
191
  }
190
- function E(t, a) {
191
- let { pref: o, value: s, mixed: c, setValue: f } = t;
192
+ function E(e) {
193
+ if (typeof e != "object" || !e) return;
194
+ let t = e;
195
+ if (!(t.fill !== void 0 && t.fill !== "solid")) return typeof t.color == "string" ? t.color : void 0;
196
+ }
197
+ function D(t, a) {
198
+ let { pref: o, value: s, mixed: c, setValue: l } = t;
192
199
  switch (o.kind) {
193
200
  case "number": {
194
- let e = o, t = typeof s == "number" && Number.isFinite(s) ? s : void 0, r = t === void 0 ? NaN : e.unit ? e.unit.toDisplay(t) : t, i = /* @__PURE__ */ u(n, {
201
+ let e = o, t = typeof s == "number" && Number.isFinite(s) ? s : void 0, r = t === void 0 ? NaN : e.unit ? e.unit.toDisplay(t) : t, i = /* @__PURE__ */ d(n, {
195
202
  className: S.number,
196
203
  value: c || t === void 0 ? NaN : r,
197
204
  placeholder: c ? "Mixed" : void 0,
@@ -201,28 +208,28 @@ function E(t, a) {
201
208
  hideSteppers: !0,
202
209
  "aria-label": a,
203
210
  onChange: (t) => {
204
- Number.isNaN(t) || f(e.unit ? e.unit.fromDisplay(t) : t);
211
+ Number.isNaN(t) || l(e.unit ? e.unit.fromDisplay(t) : t);
205
212
  }
206
213
  });
207
- return e.unit?.suffix === void 0 ? i : /* @__PURE__ */ d(l, { children: [i, /* @__PURE__ */ u("span", {
214
+ return e.unit?.suffix === void 0 ? i : /* @__PURE__ */ f(u, { children: [i, /* @__PURE__ */ d("span", {
208
215
  className: S.unitSuffix,
209
216
  "aria-hidden": "true",
210
217
  children: e.unit.suffix
211
218
  })] });
212
219
  }
213
- case "string": return /* @__PURE__ */ u(D, {
220
+ case "string": return /* @__PURE__ */ d(O, {
214
221
  text: c ? void 0 : typeof s == "string" ? s : "",
215
222
  placeholder: c ? "Mixed" : void 0,
216
223
  ariaLabel: a,
217
- onCommit: f
224
+ onCommit: l
218
225
  });
219
226
  case "boolean": {
220
- let e = /* @__PURE__ */ u(i, {
227
+ let e = /* @__PURE__ */ d(i, {
221
228
  isSelected: !!s,
222
- onChange: f,
229
+ onChange: l,
223
230
  "aria-label": a
224
231
  });
225
- return c ? /* @__PURE__ */ u("span", {
232
+ return c ? /* @__PURE__ */ d("span", {
226
233
  className: S.mixedSwitch,
227
234
  title: "Mixed",
228
235
  children: e
@@ -230,7 +237,7 @@ function E(t, a) {
230
237
  }
231
238
  case "enum": {
232
239
  let e = o;
233
- return /* @__PURE__ */ u(r, {
240
+ return /* @__PURE__ */ d(r, {
234
241
  className: S.select,
235
242
  options: e.options.map((e) => ({
236
243
  value: e.value,
@@ -238,21 +245,34 @@ function E(t, a) {
238
245
  })),
239
246
  selectedKey: c ? null : typeof s == "string" ? s : e.default,
240
247
  placeholder: c ? "Mixed" : void 0,
241
- onSelectionChange: f,
248
+ onSelectionChange: l,
242
249
  "aria-label": a
243
250
  });
244
251
  }
245
252
  case "color": {
246
253
  let t = o;
247
- return /* @__PURE__ */ u(e, {
254
+ return /* @__PURE__ */ d(e, {
248
255
  value: c ? void 0 : typeof s == "string" ? s : t.default,
249
256
  mixed: c,
250
257
  alpha: t.alpha,
251
- onChange: f,
258
+ onChange: l,
259
+ "aria-label": a
260
+ });
261
+ }
262
+ case "paint": {
263
+ let t = o, n = E(s) ?? (c ? void 0 : E(t.default));
264
+ return /* @__PURE__ */ d(e, {
265
+ value: c ? void 0 : n,
266
+ mixed: c || s !== void 0 && E(s) === void 0,
267
+ alpha: t.alpha,
268
+ onChange: (e) => l({
269
+ fill: "solid",
270
+ color: e
271
+ }),
252
272
  "aria-label": a
253
273
  });
254
274
  }
255
- default: return /* @__PURE__ */ d("span", {
275
+ default: return /* @__PURE__ */ f("span", {
256
276
  className: S.unrenderable,
257
277
  children: [
258
278
  "(",
@@ -262,9 +282,9 @@ function E(t, a) {
262
282
  });
263
283
  }
264
284
  }
265
- function D({ text: e, placeholder: n, ariaLabel: r, onCommit: i }) {
285
+ function O({ text: e, placeholder: n, ariaLabel: r, onCommit: i }) {
266
286
  let [a, s] = o(null);
267
- return /* @__PURE__ */ u(t, {
287
+ return /* @__PURE__ */ d(t, {
268
288
  value: a ?? e ?? "",
269
289
  placeholder: n,
270
290
  "aria-label": r,
@@ -278,6 +298,6 @@ function D({ text: e, placeholder: n, ariaLabel: r, onCommit: i }) {
278
298
  });
279
299
  }
280
300
  //#endregion
281
- export { _ as a, p as i, f as n, x as o, b as r, v as s, w as t };
301
+ export { _ as a, y as c, p as i, c as n, x as o, b as r, v as s, w as t };
282
302
 
283
- //# sourceMappingURL=SelectionPanel-D_U7GKup.js.map
303
+ //# sourceMappingURL=SelectionPanel-CMglTgEf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SelectionPanel-CMglTgEf.js","names":[],"sources":["../../src/components/SelectionPanel/model.ts","../../src/components/SelectionPanel/SelectionPanel.module.css","../../src/components/SelectionPanel/SelectionPanel.tsx"],"sourcesContent":["// Pure selection→panel derivations for SelectionPanel. Kept free of\n// React so intersection/aggregation semantics are unit-testable.\n//\n// Path convention (see core `NodePropertiesEntry`): a leaf's OWN KEY in\n// the schema is its node path — a dotted path of any depth rooted at\n// `pose` or `data` (`pose.x`, `data.fill`, `data.style.fontSize`). Group\n// keys are organizational only.\n\nimport {\n MIXED,\n type Mixed,\n type NodePropertiesEntry,\n type NodeRoutingEntry,\n type SceneNode,\n type ToolPrefGroup,\n type ToolPrefLeaf,\n} from '@weasel-js/core';\n\n// Re-exported under its existing public name here: the kit-wide \"these\n// values disagree\" sentinel (`@weasel-js/core`'s `MIXED`) also covers\n// multi-node aggregation, so this panel doesn't get its own symbol.\nexport { MIXED };\nexport type { Mixed };\n\nexport type AnyNode = SceneNode<unknown, string, unknown>;\n\nexport interface PanelLeaf {\n /** Dotted node path — the leaf's key in the schema. */\n path: string;\n leaf: ToolPrefLeaf;\n}\n\nexport interface PanelRow {\n label: string;\n leaves: PanelLeaf[];\n}\n\nexport interface PanelSection {\n key: string;\n name: string;\n rows: PanelRow[];\n}\n\n/** Derive a node's kind: containers are `'group'`; leaves classify their\n * `data` through the routing entries (first match wins, `'unknown'`\n * otherwise) — same semantics as core's `NodeRouting.classify`. */\nexport function classifyKind(\n node: AnyNode,\n routing: readonly NodeRoutingEntry[],\n): string {\n if (node.kind === 'container') return 'group';\n for (const entry of routing) {\n if (entry.matches(node.data)) return entry.name;\n }\n return 'unknown';\n}\n\nfunction isGroup(n: ToolPrefLeaf | ToolPrefGroup): n is ToolPrefGroup {\n return !('kind' in n);\n}\n\n/** Flatten a schema to sections of leaves. Nested groups fold into their\n * top-level section; top-level leaves get an untitled leading section. */\nfunction flatten(schema: ToolPrefGroup): PanelSection[] {\n const sections: PanelSection[] = [];\n const untitled: PanelLeaf[] = [];\n\n const collect = (group: ToolPrefGroup, into: PanelLeaf[]): void => {\n for (const [key, child] of Object.entries(group.children)) {\n if (isGroup(child)) collect(child, into);\n else into.push({ path: key, leaf: child });\n }\n };\n\n for (const [key, child] of Object.entries(schema.children)) {\n if (isGroup(child)) {\n const leaves: PanelLeaf[] = [];\n collect(child, leaves);\n sections.push({ key, name: child.name, rows: pairRows(leaves) });\n } else {\n untitled.push({ path: key, leaf: child });\n }\n }\n if (untitled.length > 0) {\n sections.unshift({ key: '', name: '', rows: pairRows(untitled) });\n }\n return sections;\n}\n\n/** Merge consecutive leaves sharing a `pair` id into one labeled row. */\nfunction pairRows(leaves: readonly PanelLeaf[]): PanelRow[] {\n const rows: PanelRow[] = [];\n for (const item of leaves) {\n const pair = item.leaf.pair;\n const prev = rows[rows.length - 1];\n if (pair !== undefined && prev !== undefined && prev.label === pair) {\n prev.leaves.push(item);\n } else {\n rows.push({ label: pair ?? item.leaf.name, leaves: [item] });\n }\n }\n return rows;\n}\n\n/**\n * The schema the panel shows for a set of kinds: one kind → its full\n * schema; several → the intersection by (path, leaf kind), shaped by the\n * first kind's section/row layout. Kinds without a registered schema\n * contribute nothing, so their presence collapses the intersection.\n */\nexport function effectiveSections(\n kinds: readonly string[],\n entries: readonly NodePropertiesEntry[],\n): PanelSection[] {\n const uniq = [...new Set(kinds)];\n if (uniq.length === 0) return [];\n const byName = new Map(entries.map((e) => [e.name, e]));\n const schemas = uniq.map((k) => byName.get(k)?.schema);\n if (schemas.some((s) => s === undefined)) return [];\n const [first, ...rest] = schemas as ToolPrefGroup[];\n\n const flatFirst = flatten(first);\n if (rest.length === 0) return flatFirst;\n\n // '\\0' delimiter: path and kind strings are unconstrained, so a\n // printable separator could collide with real content.\n const leafKey = (l: PanelLeaf): string => `${l.path}\\0${l.leaf.kind}`;\n const restKeys = rest.map(\n (schema) =>\n new Set(\n flatten(schema)\n .flatMap((s) => s.rows)\n .flatMap((r) => r.leaves)\n .map(leafKey),\n ),\n );\n const keep = (l: PanelLeaf): boolean =>\n restKeys.every((set) => set.has(leafKey(l)));\n\n return flatFirst\n .map((section) => ({\n ...section,\n rows: section.rows\n .map((row) => ({ ...row, leaves: row.leaves.filter(keep) }))\n .filter((row) => row.leaves.length > 0),\n }))\n .filter((section) => section.rows.length > 0);\n}\n\n/** Read a node value at a dotted path of any depth (`pose.x`,\n * `data.style.fontSize`, `data.style.fill.color`). Returns `undefined` if\n * the path is dotless (mirrors `commit`'s no-op on a malformed schema\n * key) or if any intermediate segment is missing or not an object. */\nexport function nodeValueAt(node: AnyNode, path: string): unknown {\n const segments = path.split('.');\n if (segments.length < 2) return undefined;\n const head = segments[0];\n let cursor: unknown = head === 'pose' ? node.pose : head === 'data' ? node.data : undefined;\n for (let i = 1; i < segments.length; i++) {\n if (cursor == null || typeof cursor !== 'object') return undefined;\n cursor = (cursor as Record<string, unknown>)[segments[i]];\n }\n return cursor;\n}\n\n/** Immutably set `value` at a dotted path within `root`, cloning each level\n * on the way down so React sees new object identities. Arrays stay arrays\n * (an array intermediate is shallow-copied, not flattened into `{0: ...}`)\n * — this is the write-side twin of `nodeValueAt`, so a schema author who\n * gets a working read gets a working write. */\nexport function setAtPath(root: object, segments: readonly string[], value: unknown): object {\n const [head, ...rest] = segments;\n if (rest.length === 0) {\n return Array.isArray(root)\n ? Object.assign([...root], { [head]: value })\n : { ...root, [head]: value };\n }\n const child = (root as Record<string, unknown>)[head];\n const childObj = Array.isArray(child)\n ? [...child]\n : child != null && typeof child === 'object'\n ? { ...child }\n : {};\n const nested = setAtPath(childObj, rest, value);\n return Array.isArray(root)\n ? Object.assign([...root], { [head]: nested })\n : { ...root, [head]: nested };\n}\n\n/** Aggregate a path across nodes: the shared value, or `MIXED`. */\nexport function aggregateValue(\n nodes: readonly AnyNode[],\n path: string,\n): unknown | Mixed {\n let value: unknown;\n let first = true;\n for (const node of nodes) {\n const v = nodeValueAt(node, path);\n if (first) {\n value = v;\n first = false;\n } else if (!Object.is(v, value)) {\n return MIXED;\n }\n }\n return value;\n}\n\n/** `'rect ×2 · text'` — kinds in first-seen order with counts. */\nexport function kindBreakdown(kinds: readonly string[]): string {\n const counts = new Map<string, number>();\n for (const k of kinds) counts.set(k, (counts.get(k) ?? 0) + 1);\n return [...counts.entries()]\n .map(([k, n]) => (n > 1 ? `${k} ×${n}` : k))\n .join(' · ');\n}\n",".root {\n display: flex;\n flex-direction: column;\n font-size: 12px;\n color: var(--wzl-text);\n}\n\n.header {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 8px 10px;\n border-block-end: 1px solid var(--wzl-border);\n}\n\n.kind {\n font-weight: 600;\n font-size: 13px;\n}\n\n.breakdown {\n font-size: 11px;\n opacity: 0.6;\n}\n\n.section {\n padding: 6px 10px 8px;\n border-block-end: 1px solid var(--wzl-border);\n}\n\n.section:last-child {\n border-block-end: none;\n}\n\n.sectionTitle {\n margin: 0 0 6px;\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n opacity: 0.55;\n}\n\n.row {\n display: grid;\n grid-template-columns: 64px 1fr;\n gap: 6px;\n align-items: center;\n margin-block-end: 5px;\n}\n\n.rowLabel {\n opacity: 0.75;\n}\n\n.rowControls {\n display: flex;\n gap: 6px;\n align-items: center;\n min-inline-size: 0;\n}\n\n.rowControls > * {\n flex: 1;\n min-inline-size: 0;\n}\n\n.number :global(input),\n.select {\n font-size: 11px;\n}\n\n.unitSuffix {\n flex: none;\n font-size: 11px;\n opacity: 0.6;\n}\n\n.mixedSwitch {\n opacity: 0.55;\n}\n\n.unrenderable {\n font-size: 11px;\n opacity: 0.5;\n font-style: italic;\n}\n","import { Fragment, useState, useSyncExternalStore, type ReactNode } from 'react';\nimport {\n asNodeId,\n type NodePropertiesEntry,\n type NodeRoutingEntry,\n type Scene,\n type SelectionApi,\n type ToolPrefColor,\n type ToolPrefEnum,\n type ToolPrefLeaf,\n type ToolPrefNumber,\n type ToolPrefPaint,\n} from '@weasel-js/core';\nimport { ColorField } from '../ColorField';\nimport { Input } from '../Input';\nimport { NumberField } from '../NumberField';\nimport { Select } from '../Select';\nimport { Switch } from '../Switch';\nimport {\n MIXED,\n aggregateValue,\n classifyKind,\n effectiveSections,\n kindBreakdown,\n setAtPath,\n type AnyNode,\n type PanelLeaf,\n} from './model';\nimport s from './SelectionPanel.module.css';\n\nexport interface PropertyRenderContext {\n /** Dotted node path of the leaf (`pose.x`, `data.fill`). */\n path: string;\n /** The schema leaf. App renderers narrow it to their own kind shape. */\n pref: ToolPrefLeaf;\n /** Aggregated value across the selection; `undefined` when mixed. */\n value: unknown;\n /** True when selected nodes disagree at this path. */\n mixed: boolean;\n /** Commit a value — fans out to every selected node in one undo step. */\n setValue: (value: unknown) => void;\n}\n\nexport type PropertyRenderer = (ctx: PropertyRenderContext) => ReactNode;\n\nexport interface SelectionPanelProps<TData, TLayer extends string, TPose> {\n /** The scene handle (`useScene`). The panel subscribes itself, so it\n * re-renders on scene mutations regardless of parent renders. */\n scene: Scene<TData, TLayer, TPose>;\n /** Selection handle (`useSelection`). Only `current` is read. */\n selection: Pick<SelectionApi, 'current'>;\n /** Properties-trait entries, e.g. core's `defaultNodeProperties` /\n * `inferredNodeProperties` (+ consumer extras). Memoize or hoist. */\n properties: readonly NodePropertiesEntry[];\n /** Routing-trait classifiers used to derive each node's kind — pass\n * the same list the canvas uses. Memoize or hoist. */\n routing: readonly NodeRoutingEntry[];\n /** Control overrides / app-defined kinds (PrefsForm-style). Keys are\n * leaf paths (`data.fill`, checked first) or leaf kinds (`color`).\n * A renderer returning `null` collapses its leaf (and the row, when\n * every leaf in it collapses). */\n renderers?: Record<string, PropertyRenderer>;\n /** Kind → header label. Default: capitalized kind name. */\n kindLabel?: (kind: string) => string;\n /** Rendered when the selection is empty. */\n emptyState?: ReactNode;\n className?: string;\n}\n\nconst defaultKindLabel = (kind: string): string =>\n kind.length === 0 ? kind : kind[0].toUpperCase() + kind.slice(1);\n\n/**\n * Pre-baked selection properties panel. Shows the selected nodes' kind\n * and the properties-trait schema for that kind; multi-selections show\n * the intersection of the kinds' schemas with per-field Mixed state.\n * Edits commit as one labeled `scene.batch` fan-out per gesture.\n */\nexport function SelectionPanel<TData, TLayer extends string, TPose>(\n props: SelectionPanelProps<TData, TLayer, TPose>,\n) {\n const {\n scene,\n selection,\n properties,\n routing,\n renderers,\n kindLabel = defaultKindLabel,\n emptyState = null,\n className,\n } = props;\n\n useSyncExternalStore(scene.subscribe, scene.getVersion, scene.getVersion);\n\n const nodes = selection.current\n .map((id) => scene.get(asNodeId(id)))\n .filter((n): n is NonNullable<typeof n> => n != null) as readonly AnyNode[];\n\n const kinds = nodes.map((n) => classifyKind(n, routing));\n const sections = effectiveSections(kinds, properties);\n\n if (nodes.length === 0) {\n return <div className={[s.root, className].filter(Boolean).join(' ')}>{emptyState}</div>;\n }\n\n const commit = (leaf: PanelLeaf, value: unknown): void => {\n const [head, ...rest] = leaf.path.split('.');\n if (rest.length === 0) return;\n const ids = selection.current.map(asNodeId);\n scene.batch(`Edit ${leaf.leaf.name}`, () => {\n for (const id of ids) {\n const node = scene.get(id);\n if (!node) continue;\n // `setAtPath` spreads plain objects/arrays down the schema path;\n // consumer `pose`/`data` are assumed plain-object-shaped along\n // that path (a class instance's prototype would be dropped by\n // the spread) — the `as TPose`/`as TData` casts below rely on it.\n if (head === 'pose') {\n scene.setPose(id, setAtPath(node.pose as object, rest, value) as TPose);\n } else if (head === 'data') {\n scene.update(id, { data: setAtPath(node.data as object, rest, value) as TData });\n }\n }\n });\n };\n\n return (\n <div className={[s.root, className].filter(Boolean).join(' ')}>\n <header className={s.header}>\n {nodes.length === 1 ? (\n <span className={s.kind}>{kindLabel(kinds[0])}</span>\n ) : (\n <>\n <span className={s.kind}>{nodes.length} selected</span>\n <span className={s.breakdown}>{kindBreakdown(kinds)}</span>\n </>\n )}\n </header>\n {sections.map((section) => {\n // Render controls before emitting row chrome so a null-rendering\n // leaf (custom renderer opting out) collapses its cell — and the\n // whole row / section when nothing survives. PrefsForm precedent.\n const rows = section.rows\n .map((row) => {\n const controls = row.leaves\n .map((panelLeaf) => ({\n panelLeaf,\n content: renderLeafControl(\n panelLeaf,\n row.leaves.length > 1\n ? `${row.label} ${panelLeaf.leaf.name}`\n : panelLeaf.leaf.name,\n nodes,\n renderers,\n commit,\n ),\n }))\n .filter((c) => c.content != null);\n return { row, controls };\n })\n .filter(({ controls }) => controls.length > 0);\n if (rows.length === 0) return null;\n return (\n <section key={section.key} className={s.section}>\n {section.name !== '' && <h3 className={s.sectionTitle}>{section.name}</h3>}\n {rows.map(({ row, controls }) => (\n <div key={row.leaves[0].path} className={s.row}>\n <span className={s.rowLabel} title={row.leaves[0].leaf.description}>\n {row.label}\n </span>\n <span className={s.rowControls}>\n {controls.map(({ panelLeaf, content }) => (\n <Fragment key={panelLeaf.path}>{content}</Fragment>\n ))}\n </span>\n </div>\n ))}\n </section>\n );\n })}\n </div>\n );\n}\n\nfunction renderLeafControl(\n panelLeaf: PanelLeaf,\n ariaLabel: string,\n nodes: readonly AnyNode[],\n renderers: Record<string, PropertyRenderer> | undefined,\n commit: (leaf: PanelLeaf, value: unknown) => void,\n): ReactNode {\n const { path, leaf } = panelLeaf;\n const aggregated = aggregateValue(nodes, path);\n const mixed = aggregated === MIXED;\n const value = mixed ? undefined : aggregated;\n\n const ctx: PropertyRenderContext = {\n path,\n pref: leaf,\n value,\n mixed,\n setValue: (v) => commit(panelLeaf, v),\n };\n\n const custom = renderers?.[path] ?? renderers?.[leaf.kind];\n if (custom) return custom(ctx);\n return renderBuiltin(ctx, ariaLabel);\n}\n\n/** The color of a solid `FillStyle`, or `undefined` for anything else —\n * including a value that isn't a paint at all. `fill` is optional on the\n * solid member of the union, so the tag alone can't decide it. */\nfunction solidColorOf(value: unknown): string | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const paint = value as { fill?: string; color?: unknown };\n if (paint.fill !== undefined && paint.fill !== 'solid') return undefined;\n return typeof paint.color === 'string' ? paint.color : undefined;\n}\n\nfunction renderBuiltin(ctx: PropertyRenderContext, ariaLabel: string): ReactNode {\n const { pref, value, mixed, setValue } = ctx;\n switch (pref.kind) {\n case 'number': {\n const p = pref as ToolPrefNumber;\n const stored = typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n const display = stored !== undefined ? (p.unit ? p.unit.toDisplay(stored) : stored) : NaN;\n const field = (\n <NumberField\n className={s.number}\n value={mixed || stored === undefined ? NaN : display}\n placeholder={mixed ? 'Mixed' : undefined}\n minValue={p.min}\n maxValue={p.max}\n step={p.step ?? 1}\n hideSteppers\n aria-label={ariaLabel}\n onChange={(n) => {\n if (Number.isNaN(n)) return;\n setValue(p.unit ? p.unit.fromDisplay(n) : n);\n }}\n />\n );\n if (p.unit?.suffix === undefined) return field;\n return (\n <>\n {field}\n <span className={s.unitSuffix} aria-hidden=\"true\">\n {p.unit.suffix}\n </span>\n </>\n );\n }\n case 'string': {\n return (\n <DraftInput\n text={mixed ? undefined : typeof value === 'string' ? value : ''}\n placeholder={mixed ? 'Mixed' : undefined}\n ariaLabel={ariaLabel}\n onCommit={setValue}\n />\n );\n }\n case 'boolean': {\n const control = (\n <Switch isSelected={Boolean(value)} onChange={setValue} aria-label={ariaLabel} />\n );\n // Switch has no indeterminate state; a reduced-opacity wrapper with\n // a title is the cheap honest cue for a mixed selection.\n if (!mixed) return control;\n return (\n <span className={s.mixedSwitch} title=\"Mixed\">\n {control}\n </span>\n );\n }\n case 'enum': {\n const p = pref as ToolPrefEnum;\n return (\n <Select<string>\n className={s.select}\n options={p.options.map((o) => ({ value: o.value, label: o.label }))}\n selectedKey={mixed ? null : typeof value === 'string' ? value : p.default}\n placeholder={mixed ? 'Mixed' : undefined}\n onSelectionChange={setValue}\n aria-label={ariaLabel}\n />\n );\n }\n case 'color': {\n const p = pref as ToolPrefColor;\n return (\n <ColorField\n value={mixed ? undefined : typeof value === 'string' ? value : p.default}\n mixed={mixed}\n alpha={p.alpha}\n onChange={setValue}\n aria-label={ariaLabel}\n />\n );\n }\n case 'paint': {\n // The value is a whole `FillStyle`. A solid one has a color to show; a\n // pattern or gradient does not, and showing the control's default there\n // would claim a color the shape doesn't have. It gets the same\n // indeterminate chip a genuinely mixed selection gets — in both cases\n // the honest statement is \"there is no single color here\".\n const p = pref as ToolPrefPaint;\n const solid = solidColorOf(value) ?? (mixed ? undefined : solidColorOf(p.default));\n return (\n <ColorField\n value={mixed ? undefined : solid}\n mixed={mixed || (value !== undefined && solidColorOf(value) === undefined)}\n alpha={p.alpha}\n // Write the whole union member. Setting a `color` key on the\n // existing paint would leave a `{ fill: 'linear-gradient', stops,\n // color }` hybrid that every structural `'color' in paint` check\n // downstream reads as solid.\n onChange={(color) => setValue({ fill: 'solid', color })}\n aria-label={ariaLabel}\n />\n );\n }\n default:\n return <span className={s.unrenderable}>({pref.kind}: no renderer)</span>;\n }\n}\n\n/** Text input with commit-on-blur/Enter semantics — live-per-keystroke\n * writes would emit one undo step per character. */\nfunction DraftInput({\n text,\n placeholder,\n ariaLabel,\n onCommit,\n}: {\n text: string | undefined;\n placeholder?: string;\n ariaLabel: string;\n onCommit: (v: string) => void;\n}) {\n const [draft, setDraft] = useState<string | null>(null);\n return (\n <Input\n value={draft ?? text ?? ''}\n placeholder={placeholder}\n aria-label={ariaLabel}\n onChange={setDraft}\n onBlur={() => {\n if (draft !== null && draft !== text) onCommit(draft);\n setDraft(null);\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter') (e.target as HTMLInputElement).blur();\n }}\n />\n );\n}\n"],"mappings":"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"}
@@ -23,4 +23,4 @@ function n(n) {
23
23
  //#endregion
24
24
  export { n as t };
25
25
 
26
- //# sourceMappingURL=Sidebar-Bk9_TPhX.js.map
26
+ //# sourceMappingURL=Sidebar-BFcBx8QV.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Sidebar-Bk9_TPhX.js","names":[],"sources":["../../src/components/Sidebar/Sidebar.module.css","../../src/components/Sidebar/Sidebar.tsx"],"sourcesContent":[".sidebar {\n display: flex;\n flex-direction: column;\n gap: 4px;\n background: var(--wzl-panel-bg);\n color: var(--wzl-text);\n}\n\n.sidebar.left {\n border-right: 1px solid var(--wzl-panel-border);\n}\n.sidebar.right {\n border-left: 1px solid var(--wzl-panel-border);\n}\n","import type { CSSProperties, ReactNode } from 'react';\nimport s from './Sidebar.module.css';\n\nexport interface SidebarProps {\n /**\n * Edge the sidebar docks to. Adds a class hook (`s.left` / `s.right`)\n * so consumers can target either edge for borders, shadows, etc.\n * Purely cosmetic — layout is the parent's job.\n */\n side?: 'left' | 'right';\n /**\n * Accessible label — rendered onto the `<aside>`. Without it, screen\n * readers fall back to \"complementary landmark,\" which is fine for\n * apps with a single sidebar but ambiguous when there's both a left\n * and a right.\n */\n ariaLabel?: string;\n children?: ReactNode;\n className?: string;\n /** Inline style — useful for width-as-custom-property handles or any\n * other one-off CSS property the consumer wants to set dynamically. */\n style?: CSSProperties;\n}\n\n/**\n * Generic sidebar shell — `<aside>` with column layout and token-driven\n * surface treatment. Width, sticky positioning, and resize handles stay\n * the consumer's concern (every app has its own layout grid); the kit\n * just provides the chrome.\n *\n * Pair with `<SidebarPanel>` for collapsible sections.\n */\nexport function Sidebar(props: SidebarProps) {\n const { side, ariaLabel, children, className, style } = props;\n const cls = [s.sidebar, side === 'left' && s.left, side === 'right' && s.right, className]\n .filter(Boolean).join(' ');\n return (\n <aside className={cls} aria-label={ariaLabel} style={style}>\n {children}\n </aside>\n );\n}\n"],"mappings":";;;;;;;;ACgCA,SAAgB,EAAQ,GAAqB;CAC3C,IAAM,EAAE,SAAM,cAAW,aAAU,cAAW,aAAU;CAGxD,OACE,kBAAC,SAAD;EAAO,WAHG;GAAC,EAAE;GAAS,MAAS,UAAU,EAAE;GAAM,MAAS,WAAW,EAAE;GAAO;GAAU,CACvF,OAAO,QAAQ,CAAC,KAAK,IAEJ;EAAK,cAAY;EAAkB;EAClD;EACK,CAAA"}
1
+ {"version":3,"file":"Sidebar-BFcBx8QV.js","names":[],"sources":["../../src/components/Sidebar/Sidebar.module.css","../../src/components/Sidebar/Sidebar.tsx"],"sourcesContent":[".sidebar {\n display: flex;\n flex-direction: column;\n gap: 4px;\n background: var(--wzl-panel-bg);\n color: var(--wzl-text);\n}\n\n.sidebar.left {\n border-right: 1px solid var(--wzl-panel-border);\n}\n.sidebar.right {\n border-left: 1px solid var(--wzl-panel-border);\n}\n","import type { CSSProperties, ReactNode } from 'react';\nimport s from './Sidebar.module.css';\n\nexport interface SidebarProps {\n /**\n * Edge the sidebar docks to. Adds a class hook (`s.left` / `s.right`)\n * so consumers can target either edge for borders, shadows, etc.\n * Purely cosmetic — layout is the parent's job.\n */\n side?: 'left' | 'right';\n /**\n * Accessible label — rendered onto the `<aside>`. Without it, screen\n * readers fall back to \"complementary landmark,\" which is fine for\n * apps with a single sidebar but ambiguous when there's both a left\n * and a right.\n */\n ariaLabel?: string;\n children?: ReactNode;\n className?: string;\n /** Inline style — useful for width-as-custom-property handles or any\n * other one-off CSS property the consumer wants to set dynamically. */\n style?: CSSProperties;\n}\n\n/**\n * Generic sidebar shell — `<aside>` with column layout and token-driven\n * surface treatment. Width, sticky positioning, and resize handles stay\n * the consumer's concern (every app has its own layout grid); the kit\n * just provides the chrome.\n *\n * Pair with `<SidebarPanel>` for collapsible sections.\n */\nexport function Sidebar(props: SidebarProps) {\n const { side, ariaLabel, children, className, style } = props;\n const cls = [s.sidebar, side === 'left' && s.left, side === 'right' && s.right, className]\n .filter(Boolean).join(' ');\n return (\n <aside className={cls} aria-label={ariaLabel} style={style}>\n {children}\n </aside>\n );\n}\n"],"mappings":";;;;;;;;ACgCA,SAAgB,EAAQ,GAAqB;CAC3C,IAAM,EAAE,SAAM,cAAW,aAAU,cAAW,aAAU;CAGxD,OACE,kBAAC,SAAD;EAAO,WAHG;GAAC,EAAE;GAAS,MAAS,UAAU,EAAE;GAAM,MAAS,WAAW,EAAE;GAAO;GAAU,CACvF,OAAO,QAAQ,CAAC,KAAK,IAEJ;EAAK,cAAY;EAAkB;EAClD;EACK,CAAA"}
@@ -56,4 +56,4 @@ function o(o) {
56
56
  //#endregion
57
57
  export { o as t };
58
58
 
59
- //# sourceMappingURL=SidebarPanel-Nd9Od5WD.js.map
59
+ //# sourceMappingURL=SidebarPanel-CRnSlvee.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SidebarPanel-Nd9Od5WD.js","names":[],"sources":["../../src/components/SidebarPanel/SidebarPanel.module.css","../../src/components/SidebarPanel/SidebarPanel.tsx"],"sourcesContent":[".panel {\n padding: 8px;\n background: var(--wzl-panel-bg);\n color: var(--wzl-text);\n}\n\n.title {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--wzl-text-muted);\n}\n\n.titleRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 4px;\n padding: 0;\n}\n\n.titleButton {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n flex: 1 1 auto;\n background: transparent;\n border: 0;\n padding: 4px 8px;\n cursor: pointer;\n color: inherit;\n font: inherit;\n text-align: left;\n}\n.titleButton:hover {\n color: var(--wzl-text);\n}\n\n.chevron {\n display: inline-block;\n font-size: 10px;\n line-height: 1;\n color: var(--wzl-text-muted);\n transition: transform var(--wzl-motion-fast) ease;\n}\n.chevronCollapsed {\n transform: rotate(-90deg);\n}\n\n.hideButton {\n background: transparent;\n border: 0;\n padding: 0 6px;\n font-size: 14px;\n line-height: 1;\n color: var(--wzl-text-muted);\n cursor: pointer;\n}\n.hideButton:hover {\n color: var(--wzl-text);\n}\n\n.panel.collapsed {\n padding-bottom: 4px;\n}\n\n.body {\n padding: 0 4px;\n}\n","import type { ReactNode } from 'react';\nimport { Focusable } from 'react-aria-components';\nimport { Tooltip, TooltipTrigger } from '../Tooltip';\nimport s from './SidebarPanel.module.css';\n\nexport interface SidebarPanelProps {\n /** Title rendered in the panel's header row. */\n title?: ReactNode;\n /**\n * Collapsed state — when true, only the header paints; the body is\n * omitted. Caller owns the state (typically via prefs) and flips it\n * back via `onToggleCollapse`.\n */\n collapsed?: boolean;\n /**\n * Click handler for the title row. When provided, the header renders\n * as a button (chevron + label) and acts as the collapse toggle.\n * When omitted, the title is static text and the panel can't collapse.\n */\n onToggleCollapse?(): void;\n /**\n * Optional close handler — when provided, the header gains a `×`\n * button on the trailing edge. Caller flips its \"hidden\" state and\n * conditionally unmounts the panel from the parent.\n */\n onHide?(): void;\n children?: ReactNode;\n className?: string;\n}\n\n/**\n * Generic collapsible panel section for sidebars. Pure presentation —\n * caller manages `collapsed`/`hidden` state externally (the pattern is\n * to drive it from app prefs so the Preferences modal and inline\n * chevrons toggle the same map).\n *\n * Pair with `<Sidebar>` for the docked column layout.\n */\nexport function SidebarPanel(props: SidebarPanelProps) {\n const { title, collapsed, onToggleCollapse, onHide, children, className } = props;\n const cls = [s.panel, collapsed && s.collapsed, className].filter(Boolean).join(' ');\n const headerInteractive = onToggleCollapse !== undefined;\n return (\n <div className={cls}>\n {title !== undefined && (\n <div className={s.titleRow}>\n {headerInteractive ? (\n <button\n type=\"button\"\n className={s.titleButton}\n onClick={onToggleCollapse}\n aria-expanded={!collapsed}\n >\n <span className={[s.chevron, collapsed && s.chevronCollapsed].filter(Boolean).join(' ')} aria-hidden=\"true\">▾</span>\n <span className={s.title}>{title}</span>\n </button>\n ) : (\n <span className={s.title}>{title}</span>\n )}\n {onHide !== undefined && (\n <TooltipTrigger>\n <Focusable>\n <button\n type=\"button\"\n className={s.hideButton}\n onClick={onHide}\n aria-label=\"Hide panel\"\n >\n ×\n </button>\n </Focusable>\n <Tooltip>Hide panel</Tooltip>\n </TooltipTrigger>\n )}\n </div>\n )}\n {!collapsed && <div className={s.body}>{children}</div>}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;ACsCA,SAAgB,EAAa,GAA0B;CACrD,IAAM,EAAE,UAAO,cAAW,qBAAkB,WAAQ,aAAU,iBAAc,GACtE,IAAM;EAAC,EAAE;EAAO,KAAa,EAAE;EAAW;EAAU,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI,EAC9E,IAAoB,MAAqB,KAAA;CAC/C,OACE,kBAAC,OAAD;EAAK,WAAW;YAAhB,CACG,MAAU,KAAA,KACT,kBAAC,OAAD;GAAK,WAAW,EAAE;aAAlB,CACG,IACC,kBAAC,UAAD;IACE,MAAK;IACL,WAAW,EAAE;IACb,SAAS;IACT,iBAAe,CAAC;cAJlB,CAME,kBAAC,QAAD;KAAM,WAAW,CAAC,EAAE,SAAS,KAAa,EAAE,iBAAiB,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;KAAE,eAAY;eAAO;KAAQ,CAAA,EACpH,kBAAC,QAAD;KAAM,WAAW,EAAE;eAAQ;KAAa,CAAA,CACjC;QAET,kBAAC,QAAD;IAAM,WAAW,EAAE;cAAQ;IAAa,CAAA,EAEzC,MAAW,KAAA,KACV,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,UAAD;IACE,MAAK;IACL,WAAW,EAAE;IACb,SAAS;IACT,cAAW;cACZ;IAEQ,CAAA,EACC,CAAA,EACZ,kBAAC,GAAD,EAAA,UAAS,cAAoB,CAAA,CACd,EAAA,CAAA,CAEf;MAEP,CAAC,KAAa,kBAAC,OAAD;GAAK,WAAW,EAAE;GAAO;GAAe,CAAA,CACnD"}
1
+ {"version":3,"file":"SidebarPanel-CRnSlvee.js","names":[],"sources":["../../src/components/SidebarPanel/SidebarPanel.module.css","../../src/components/SidebarPanel/SidebarPanel.tsx"],"sourcesContent":[".panel {\n padding: 8px;\n background: var(--wzl-panel-bg);\n color: var(--wzl-text);\n}\n\n.title {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--wzl-text-muted);\n}\n\n.titleRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 4px;\n padding: 0;\n}\n\n.titleButton {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n flex: 1 1 auto;\n background: transparent;\n border: 0;\n padding: 4px 8px;\n cursor: pointer;\n color: inherit;\n font: inherit;\n text-align: left;\n}\n.titleButton:hover {\n color: var(--wzl-text);\n}\n\n.chevron {\n display: inline-block;\n font-size: 10px;\n line-height: 1;\n color: var(--wzl-text-muted);\n transition: transform var(--wzl-motion-fast) ease;\n}\n.chevronCollapsed {\n transform: rotate(-90deg);\n}\n\n.hideButton {\n background: transparent;\n border: 0;\n padding: 0 6px;\n font-size: 14px;\n line-height: 1;\n color: var(--wzl-text-muted);\n cursor: pointer;\n}\n.hideButton:hover {\n color: var(--wzl-text);\n}\n\n.panel.collapsed {\n padding-bottom: 4px;\n}\n\n.body {\n padding: 0 4px;\n}\n","import type { ReactNode } from 'react';\nimport { Focusable } from 'react-aria-components';\nimport { Tooltip, TooltipTrigger } from '../Tooltip';\nimport s from './SidebarPanel.module.css';\n\nexport interface SidebarPanelProps {\n /** Title rendered in the panel's header row. */\n title?: ReactNode;\n /**\n * Collapsed state — when true, only the header paints; the body is\n * omitted. Caller owns the state (typically via prefs) and flips it\n * back via `onToggleCollapse`.\n */\n collapsed?: boolean;\n /**\n * Click handler for the title row. When provided, the header renders\n * as a button (chevron + label) and acts as the collapse toggle.\n * When omitted, the title is static text and the panel can't collapse.\n */\n onToggleCollapse?(): void;\n /**\n * Optional close handler — when provided, the header gains a `×`\n * button on the trailing edge. Caller flips its \"hidden\" state and\n * conditionally unmounts the panel from the parent.\n */\n onHide?(): void;\n children?: ReactNode;\n className?: string;\n}\n\n/**\n * Generic collapsible panel section for sidebars. Pure presentation —\n * caller manages `collapsed`/`hidden` state externally (the pattern is\n * to drive it from app prefs so the Preferences modal and inline\n * chevrons toggle the same map).\n *\n * Pair with `<Sidebar>` for the docked column layout.\n */\nexport function SidebarPanel(props: SidebarPanelProps) {\n const { title, collapsed, onToggleCollapse, onHide, children, className } = props;\n const cls = [s.panel, collapsed && s.collapsed, className].filter(Boolean).join(' ');\n const headerInteractive = onToggleCollapse !== undefined;\n return (\n <div className={cls}>\n {title !== undefined && (\n <div className={s.titleRow}>\n {headerInteractive ? (\n <button\n type=\"button\"\n className={s.titleButton}\n onClick={onToggleCollapse}\n aria-expanded={!collapsed}\n >\n <span className={[s.chevron, collapsed && s.chevronCollapsed].filter(Boolean).join(' ')} aria-hidden=\"true\">▾</span>\n <span className={s.title}>{title}</span>\n </button>\n ) : (\n <span className={s.title}>{title}</span>\n )}\n {onHide !== undefined && (\n <TooltipTrigger>\n <Focusable>\n <button\n type=\"button\"\n className={s.hideButton}\n onClick={onHide}\n aria-label=\"Hide panel\"\n >\n ×\n </button>\n </Focusable>\n <Tooltip>Hide panel</Tooltip>\n </TooltipTrigger>\n )}\n </div>\n )}\n {!collapsed && <div className={s.body}>{children}</div>}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;ACsCA,SAAgB,EAAa,GAA0B;CACrD,IAAM,EAAE,UAAO,cAAW,qBAAkB,WAAQ,aAAU,iBAAc,GACtE,IAAM;EAAC,EAAE;EAAO,KAAa,EAAE;EAAW;EAAU,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI,EAC9E,IAAoB,MAAqB,KAAA;CAC/C,OACE,kBAAC,OAAD;EAAK,WAAW;YAAhB,CACG,MAAU,KAAA,KACT,kBAAC,OAAD;GAAK,WAAW,EAAE;aAAlB,CACG,IACC,kBAAC,UAAD;IACE,MAAK;IACL,WAAW,EAAE;IACb,SAAS;IACT,iBAAe,CAAC;cAJlB,CAME,kBAAC,QAAD;KAAM,WAAW,CAAC,EAAE,SAAS,KAAa,EAAE,iBAAiB,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;KAAE,eAAY;eAAO;KAAQ,CAAA,EACpH,kBAAC,QAAD;KAAM,WAAW,EAAE;eAAQ;KAAa,CAAA,CACjC;QAET,kBAAC,QAAD;IAAM,WAAW,EAAE;cAAQ;IAAa,CAAA,EAEzC,MAAW,KAAA,KACV,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,UAAD;IACE,MAAK;IACL,WAAW,EAAE;IACb,SAAS;IACT,cAAW;cACZ;IAEQ,CAAA,EACC,CAAA,EACZ,kBAAC,GAAD,EAAA,UAAS,cAAoB,CAAA,CACd,EAAA,CAAA,CAEf;MAEP,CAAC,KAAa,kBAAC,OAAD;GAAK,WAAW,EAAE;GAAO;GAAe,CAAA,CACnD"}
@@ -24,4 +24,4 @@ var i = {
24
24
  //#endregion
25
25
  export { a as t };
26
26
 
27
- //# sourceMappingURL=Switch-iUmlnP_b.js.map
27
+ //# sourceMappingURL=Switch-DxJLMYki.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Switch-iUmlnP_b.js","names":[],"sources":["../../src/components/Switch/Switch.module.css","../../src/components/Switch/Switch.tsx"],"sourcesContent":[".switch {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n font-family: var(--wzl-font-ui);\n font-size: 13px;\n color: var(--wzl-fg);\n user-select: none;\n -webkit-user-select: none;\n}\n\n.switch[data-disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.track {\n position: relative;\n flex: 0 0 auto;\n width: 28px;\n height: 16px;\n background: var(--wzl-input-bg);\n border: 1px solid var(--wzl-border-strong);\n border-radius: 8px;\n transition: background-color var(--wzl-motion-fast), border-color var(--wzl-motion-fast), box-shadow var(--wzl-motion-fast);\n}\n\n.switch[data-focus-visible] .track {\n box-shadow: 0 0 0 2px var(--wzl-focus-ring);\n}\n\n.switch[data-selected] .track {\n background: var(--wzl-accent-strong);\n border-color: var(--wzl-accent-strong);\n}\n\n.thumb {\n position: absolute;\n top: 1px;\n left: 1px;\n width: 12px;\n height: 12px;\n background: var(--wzl-fg);\n border-radius: 50%;\n transition: transform var(--wzl-motion-fast);\n}\n\n.switch[data-selected] .thumb {\n transform: translateX(12px);\n background: var(--wzl-fg-on-accent);\n}\n\n.label {\n line-height: 1.2;\n}\n","import { forwardRef, type ReactNode, type Ref } from 'react';\nimport {\n Switch as RACSwitch,\n type SwitchProps as RACSwitchProps,\n} from 'react-aria-components';\nimport s from './Switch.module.css';\n\nexport type SwitchProps = Omit<RACSwitchProps, 'children' | 'className'> & {\n children?: ReactNode;\n className?: string;\n};\n\nexport const Switch = forwardRef(function Switch(\n props: SwitchProps,\n ref: Ref<HTMLLabelElement>,\n) {\n const { children, className, ...rest } = props;\n return (\n <RACSwitch\n {...rest}\n ref={ref}\n className={[s.switch, className].filter(Boolean).join(' ')}\n >\n <span className={s.track}>\n <span className={s.thumb} />\n </span>\n {children !== undefined && <span className={s.label}>{children}</span>}\n </RACSwitch>\n );\n});\n"],"mappings":";;;;;;;;GCYa,IAAS,EAAW,SAC/B,GACA,GACA;CACA,IAAM,EAAE,aAAU,cAAW,GAAG,MAAS;CACzC,OACE,kBAAC,GAAD;EACE,GAAI;EACC;EACL,WAAW,CAAC,EAAE,QAAQ,EAAU,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;YAH5D,CAKE,kBAAC,QAAD;GAAM,WAAW,EAAE;aACjB,kBAAC,QAAD,EAAM,WAAW,EAAE,OAAS,CAAA;GACvB,CAAA,EACN,MAAa,KAAA,KAAa,kBAAC,QAAD;GAAM,WAAW,EAAE;GAAQ;GAAgB,CAAA,CAC5D;;EAEd"}
1
+ {"version":3,"file":"Switch-DxJLMYki.js","names":[],"sources":["../../src/components/Switch/Switch.module.css","../../src/components/Switch/Switch.tsx"],"sourcesContent":[".switch {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n font-family: var(--wzl-font-ui);\n font-size: 13px;\n color: var(--wzl-fg);\n user-select: none;\n -webkit-user-select: none;\n}\n\n.switch[data-disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.track {\n position: relative;\n flex: 0 0 auto;\n width: 28px;\n height: 16px;\n background: var(--wzl-input-bg);\n border: 1px solid var(--wzl-border-strong);\n border-radius: 8px;\n transition: background-color var(--wzl-motion-fast), border-color var(--wzl-motion-fast), box-shadow var(--wzl-motion-fast);\n}\n\n.switch[data-focus-visible] .track {\n box-shadow: 0 0 0 2px var(--wzl-focus-ring);\n}\n\n.switch[data-selected] .track {\n background: var(--wzl-accent-strong);\n border-color: var(--wzl-accent-strong);\n}\n\n.thumb {\n position: absolute;\n top: 1px;\n left: 1px;\n width: 12px;\n height: 12px;\n background: var(--wzl-fg);\n border-radius: 50%;\n transition: transform var(--wzl-motion-fast);\n}\n\n.switch[data-selected] .thumb {\n transform: translateX(12px);\n background: var(--wzl-fg-on-accent);\n}\n\n.label {\n line-height: 1.2;\n}\n","import { forwardRef, type ReactNode, type Ref } from 'react';\nimport {\n Switch as RACSwitch,\n type SwitchProps as RACSwitchProps,\n} from 'react-aria-components';\nimport s from './Switch.module.css';\n\nexport type SwitchProps = Omit<RACSwitchProps, 'children' | 'className'> & {\n children?: ReactNode;\n className?: string;\n};\n\nexport const Switch = forwardRef(function Switch(\n props: SwitchProps,\n ref: Ref<HTMLLabelElement>,\n) {\n const { children, className, ...rest } = props;\n return (\n <RACSwitch\n {...rest}\n ref={ref}\n className={[s.switch, className].filter(Boolean).join(' ')}\n >\n <span className={s.track}>\n <span className={s.thumb} />\n </span>\n {children !== undefined && <span className={s.label}>{children}</span>}\n </RACSwitch>\n );\n});\n"],"mappings":";;;;;;;;GCYa,IAAS,EAAW,SAC/B,GACA,GACA;CACA,IAAM,EAAE,aAAU,cAAW,GAAG,MAAS;CACzC,OACE,kBAAC,GAAD;EACE,GAAI;EACC;EACL,WAAW,CAAC,EAAE,QAAQ,EAAU,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;YAH5D,CAKE,kBAAC,QAAD;GAAM,WAAW,EAAE;aACjB,kBAAC,QAAD,EAAM,WAAW,EAAE,OAAS,CAAA;GACvB,CAAA,EACN,MAAa,KAAA,KAAa,kBAAC,QAAD;GAAM,WAAW,EAAE;GAAQ;GAAgB,CAAA,CAC5D;;EAEd"}
@@ -38,4 +38,4 @@ function l({ children: e, className: t, ...r }) {
38
38
  //#endregion
39
39
  export { o as i, s as n, l as r, c as t };
40
40
 
41
- //# sourceMappingURL=Tabs-DCs-Ww2f.js.map
41
+ //# sourceMappingURL=Tabs-DelDecks.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Tabs-DCs-Ww2f.js","names":[],"sources":["../../src/components/Tabs/Tabs.module.css","../../src/components/Tabs/Tabs.tsx"],"sourcesContent":[".tabs {\n display: flex;\n flex-direction: column;\n gap: 0;\n font-family: var(--wzl-font-ui);\n color: var(--wzl-fg);\n}\n\n.tabs[data-orientation=\"horizontal\"] {\n flex-direction: column;\n}\n\n.tabs[data-orientation=\"vertical\"] {\n flex-direction: row;\n gap: 12px;\n}\n\n.list {\n display: flex;\n gap: 2px;\n border-bottom: 1px solid var(--wzl-border);\n}\n\n.tabs[data-orientation=\"vertical\"] .list {\n flex-direction: column;\n border-bottom: none;\n border-right: 1px solid var(--wzl-border);\n padding-right: 8px;\n}\n\n.tab {\n position: relative;\n padding: 6px 10px;\n background: transparent;\n border: none;\n color: var(--wzl-fg-muted);\n font: inherit;\n font-size: 12px;\n cursor: pointer;\n outline: none;\n transition: color var(--wzl-motion-fast);\n}\n\n.tab[data-hovered]:not([data-disabled]) {\n color: var(--wzl-fg);\n}\n\n.tab[data-selected] {\n color: var(--wzl-fg);\n}\n\n.tab[data-selected]::after {\n content: '';\n position: absolute;\n left: 0;\n right: 0;\n bottom: -1px;\n height: 2px;\n background: var(--wzl-accent-strong);\n}\n\n.tabs[data-orientation=\"vertical\"] .tab[data-selected]::after {\n left: auto;\n right: -9px;\n top: 0;\n bottom: 0;\n width: 2px;\n height: auto;\n}\n\n.tab[data-focus-visible] {\n box-shadow: inset 0 0 0 2px var(--wzl-focus-ring);\n border-radius: var(--wzl-radius-sm);\n}\n\n.tab[data-disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.panel {\n padding: 12px 0;\n outline: none;\n}\n\n.panel[data-focus-visible] {\n box-shadow: inset 0 0 0 2px var(--wzl-focus-ring);\n border-radius: var(--wzl-radius-sm);\n}\n","import type { ReactNode } from 'react';\nimport {\n Tabs as RACTabs,\n TabList as RACTabList,\n Tab as RACTab,\n TabPanel as RACTabPanel,\n type TabsProps as RACTabsProps,\n type TabListProps as RACTabListProps,\n type TabProps as RACTabProps,\n type TabPanelProps as RACTabPanelProps,\n} from 'react-aria-components';\nimport s from './Tabs.module.css';\n\nexport type TabsProps = Omit<RACTabsProps, 'className' | 'children'> & {\n children?: ReactNode;\n className?: string;\n};\n\nexport function Tabs({ children, className, ...rest }: TabsProps) {\n return (\n <RACTabs {...rest} className={[s.tabs, className].filter(Boolean).join(' ')}>\n {children}\n </RACTabs>\n );\n}\n\nexport type TabListProps<T extends object> = Omit<RACTabListProps<T>, 'className'> & {\n className?: string;\n};\n\nexport function TabList<T extends object>({ className, ...rest }: TabListProps<T>) {\n return <RACTabList<T> {...rest} className={[s.list, className].filter(Boolean).join(' ')} />;\n}\n\nexport type TabProps = Omit<RACTabProps, 'className' | 'children'> & {\n children?: ReactNode;\n className?: string;\n};\n\nexport function Tab({ children, className, ...rest }: TabProps) {\n return (\n <RACTab {...rest} className={[s.tab, className].filter(Boolean).join(' ')}>\n {children}\n </RACTab>\n );\n}\n\nexport type TabPanelProps = Omit<RACTabPanelProps, 'className' | 'children'> & {\n children?: ReactNode;\n className?: string;\n};\n\nexport function TabPanel({ children, className, ...rest }: TabPanelProps) {\n return (\n <RACTabPanel {...rest} className={[s.panel, className].filter(Boolean).join(' ')}>\n {children}\n </RACTabPanel>\n );\n}\n"],"mappings":";;;;;;;;;;ACkBA,SAAgB,EAAK,EAAE,aAAU,cAAW,GAAG,KAAmB;CAChE,OACE,kBAAC,GAAD;EAAS,GAAI;EAAM,WAAW,CAAC,EAAE,MAAM,EAAU,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;EACxE;EACO,CAAA;;AAQd,SAAgB,EAA0B,EAAE,cAAW,GAAG,KAAyB;CACjF,OAAO,kBAAC,GAAD;EAAe,GAAI;EAAM,WAAW,CAAC,EAAE,MAAM,EAAU,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;EAAI,CAAA;;AAQ9F,SAAgB,EAAI,EAAE,aAAU,cAAW,GAAG,KAAkB;CAC9D,OACE,kBAAC,GAAD;EAAQ,GAAI;EAAM,WAAW,CAAC,EAAE,KAAK,EAAU,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;EACtE;EACM,CAAA;;AASb,SAAgB,EAAS,EAAE,aAAU,cAAW,GAAG,KAAuB;CACxE,OACE,kBAAC,GAAD;EAAa,GAAI;EAAM,WAAW,CAAC,EAAE,OAAO,EAAU,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;EAC7E;EACW,CAAA"}
1
+ {"version":3,"file":"Tabs-DelDecks.js","names":[],"sources":["../../src/components/Tabs/Tabs.module.css","../../src/components/Tabs/Tabs.tsx"],"sourcesContent":[".tabs {\n display: flex;\n flex-direction: column;\n gap: 0;\n font-family: var(--wzl-font-ui);\n color: var(--wzl-fg);\n}\n\n.tabs[data-orientation=\"horizontal\"] {\n flex-direction: column;\n}\n\n.tabs[data-orientation=\"vertical\"] {\n flex-direction: row;\n gap: 12px;\n}\n\n.list {\n display: flex;\n gap: 2px;\n border-bottom: 1px solid var(--wzl-border);\n}\n\n.tabs[data-orientation=\"vertical\"] .list {\n flex-direction: column;\n border-bottom: none;\n border-right: 1px solid var(--wzl-border);\n padding-right: 8px;\n}\n\n.tab {\n position: relative;\n padding: 6px 10px;\n background: transparent;\n border: none;\n color: var(--wzl-fg-muted);\n font: inherit;\n font-size: 12px;\n cursor: pointer;\n outline: none;\n transition: color var(--wzl-motion-fast);\n}\n\n.tab[data-hovered]:not([data-disabled]) {\n color: var(--wzl-fg);\n}\n\n.tab[data-selected] {\n color: var(--wzl-fg);\n}\n\n.tab[data-selected]::after {\n content: '';\n position: absolute;\n left: 0;\n right: 0;\n bottom: -1px;\n height: 2px;\n background: var(--wzl-accent-strong);\n}\n\n.tabs[data-orientation=\"vertical\"] .tab[data-selected]::after {\n left: auto;\n right: -9px;\n top: 0;\n bottom: 0;\n width: 2px;\n height: auto;\n}\n\n.tab[data-focus-visible] {\n box-shadow: inset 0 0 0 2px var(--wzl-focus-ring);\n border-radius: var(--wzl-radius-sm);\n}\n\n.tab[data-disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.panel {\n padding: 12px 0;\n outline: none;\n}\n\n.panel[data-focus-visible] {\n box-shadow: inset 0 0 0 2px var(--wzl-focus-ring);\n border-radius: var(--wzl-radius-sm);\n}\n","import type { ReactNode } from 'react';\nimport {\n Tabs as RACTabs,\n TabList as RACTabList,\n Tab as RACTab,\n TabPanel as RACTabPanel,\n type TabsProps as RACTabsProps,\n type TabListProps as RACTabListProps,\n type TabProps as RACTabProps,\n type TabPanelProps as RACTabPanelProps,\n} from 'react-aria-components';\nimport s from './Tabs.module.css';\n\nexport type TabsProps = Omit<RACTabsProps, 'className' | 'children'> & {\n children?: ReactNode;\n className?: string;\n};\n\nexport function Tabs({ children, className, ...rest }: TabsProps) {\n return (\n <RACTabs {...rest} className={[s.tabs, className].filter(Boolean).join(' ')}>\n {children}\n </RACTabs>\n );\n}\n\nexport type TabListProps<T extends object> = Omit<RACTabListProps<T>, 'className'> & {\n className?: string;\n};\n\nexport function TabList<T extends object>({ className, ...rest }: TabListProps<T>) {\n return <RACTabList<T> {...rest} className={[s.list, className].filter(Boolean).join(' ')} />;\n}\n\nexport type TabProps = Omit<RACTabProps, 'className' | 'children'> & {\n children?: ReactNode;\n className?: string;\n};\n\nexport function Tab({ children, className, ...rest }: TabProps) {\n return (\n <RACTab {...rest} className={[s.tab, className].filter(Boolean).join(' ')}>\n {children}\n </RACTab>\n );\n}\n\nexport type TabPanelProps = Omit<RACTabPanelProps, 'className' | 'children'> & {\n children?: ReactNode;\n className?: string;\n};\n\nexport function TabPanel({ children, className, ...rest }: TabPanelProps) {\n return (\n <RACTabPanel {...rest} className={[s.panel, className].filter(Boolean).join(' ')}>\n {children}\n </RACTabPanel>\n );\n}\n"],"mappings":";;;;;;;;;;ACkBA,SAAgB,EAAK,EAAE,aAAU,cAAW,GAAG,KAAmB;CAChE,OACE,kBAAC,GAAD;EAAS,GAAI;EAAM,WAAW,CAAC,EAAE,MAAM,EAAU,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;EACxE;EACO,CAAA;;AAQd,SAAgB,EAA0B,EAAE,cAAW,GAAG,KAAyB;CACjF,OAAO,kBAAC,GAAD;EAAe,GAAI;EAAM,WAAW,CAAC,EAAE,MAAM,EAAU,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;EAAI,CAAA;;AAQ9F,SAAgB,EAAI,EAAE,aAAU,cAAW,GAAG,KAAkB;CAC9D,OACE,kBAAC,GAAD;EAAQ,GAAI;EAAM,WAAW,CAAC,EAAE,KAAK,EAAU,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;EACtE;EACM,CAAA;;AASb,SAAgB,EAAS,EAAE,aAAU,cAAW,GAAG,KAAuB;CACxE,OACE,kBAAC,GAAD;EAAa,GAAI;EAAM,WAAW,CAAC,EAAE,OAAO,EAAU,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;EAC7E;EACW,CAAA"}
@@ -125,4 +125,4 @@ function S(i) {
125
125
  //#endregion
126
126
  export { v as a, g as i, p as n, m as r, S as t };
127
127
 
128
- //# sourceMappingURL=Toast-NdSJo2vj.js.map
128
+ //# sourceMappingURL=Toast-Bw3XQxmV.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Toast-NdSJo2vj.js","names":[],"sources":["../../src/components/Toast/queue.ts","../../src/components/Toast/Toast.module.css","../../src/components/Toast/Toast.tsx"],"sourcesContent":["import { UNSTABLE_ToastQueue } from 'react-aria-components';\nimport { flushSync } from 'react-dom';\n\nexport type ToastTone = 'info' | 'success' | 'warning' | 'error';\n\n/** Kit-owned payload rendered inside each toast. */\nexport interface ToastContent {\n title: string;\n description?: string;\n tone: ToastTone;\n}\n\nexport interface ToastOptions {\n description?: string;\n /**\n * Auto-dismiss delay in ms. Default 8000. `null` — sticky until dismissed.\n * Must be > 0; RAC treats 0 as no timer (sticky).\n */\n ttlMs?: number | null;\n /** Stable identity: re-adding with the same id replaces the earlier toast. */\n id?: string;\n}\n\nconst DEFAULT_TTL_MS = 8000;\n\n// Tracks overlapping toast-triggered view transitions so the `:root`\n// marker class survives until the *last* one settles.\nlet activeToastTransitions = 0;\n\n/**\n * RAC's sanctioned toast-animation hook: wrap every queue mutation in a\n * view transition so entry, exit, and the surviving stack's reflow all\n * animate (choreography lives in `toastViewTransitions.css`). Falls back\n * to a plain synchronous update when the browser lacks the API or the\n * user prefers reduced motion — i.e. exactly the pre-animation behavior.\n * The jsdom test environment always takes the fallback path.\n */\nfunction viewTransitionWrapUpdate(fn: () => void): void {\n const reduceMotion =\n typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;\n if (typeof document === 'undefined' || !('startViewTransition' in document) || reduceMotion) {\n fn();\n return;\n }\n // The marker class scopes the CSS that un-names `:root` (keeps the rest\n // of the page live and interactive during the transition) to\n // toast-owned transitions only.\n const root = document.documentElement;\n activeToastTransitions++;\n root.classList.add('wzl-toast-vt');\n // flushSync: the DOM must reflect the queue change inside the\n // transition callback, before the browser snapshots the new state.\n const vt = document.startViewTransition(() => flushSync(fn));\n // `ready` rejects when the browser skips the transition — routine under\n // a rapid burst of toasts, where each new transition supersedes the\n // in-flight one. The DOM update has still applied by then, but an\n // untouched `ready` surfaces the rejection as a console error, so\n // consume it.\n vt.ready.catch(() => {});\n void vt.finished.finally(() => {\n if (--activeToastTransitions === 0) root.classList.remove('wzl-toast-vt');\n });\n}\n\n// The wrapped RAC queue lives in a module-scoped WeakMap so the public\n// class surface carries no RAC (UNSTABLE_) types — see the design spec's\n// containment rule. `racQueueOf` below is the folder-internal accessor.\nconst RAC_QUEUES = new WeakMap<ToastQueue, UNSTABLE_ToastQueue<ToastContent>>();\n\n/**\n * Kit-owned toast queue. Create isolated instances with\n * `createToastQueue()` (tests, secondary roots); most apps use the\n * module-level `defaultToastQueue` via `toast()`.\n *\n * At most 5 toasts are visible at once; adding a 6th shows immediately\n * and hides the oldest (it returns when a slot frees, with its ttl\n * restarted).\n */\nexport class ToastQueue {\n private keysById = new Map<string, string>();\n\n constructor() {\n RAC_QUEUES.set(\n this,\n new UNSTABLE_ToastQueue<ToastContent>({\n maxVisibleToasts: 5,\n wrapUpdate: viewTransitionWrapUpdate,\n }),\n );\n }\n\n add(tone: ToastTone, title: string, options: ToastOptions = {}): void {\n const { description, ttlMs = DEFAULT_TTL_MS, id } = options;\n const rac = RAC_QUEUES.get(this)!;\n if (id !== undefined) {\n const existing = this.keysById.get(id);\n if (existing !== undefined) rac.close(existing);\n }\n let key: string;\n const racOptions: { timeout?: number; onClose?: () => void } =\n ttlMs === null ? {} : { timeout: ttlMs };\n if (id !== undefined) {\n racOptions.onClose = () => {\n if (this.keysById.get(id) === key) this.keysById.delete(id);\n };\n }\n key = rac.add({ title, description, tone }, racOptions);\n if (id !== undefined) this.keysById.set(id, key);\n }\n\n /** Dismiss the toast previously added with this `id`. No-op if none is live. */\n dismiss(id: string): void {\n const key = this.keysById.get(id);\n if (key !== undefined) RAC_QUEUES.get(this)!.close(key);\n }\n\n /** Dismiss every queued toast. */\n clear(): void {\n RAC_QUEUES.get(this)!.clear();\n this.keysById.clear();\n }\n}\n\nexport function createToastQueue(): ToastQueue {\n return new ToastQueue();\n}\n\n/**\n * Folder-internal bridge to the wrapped RAC queue for `ToastRegion`.\n * Deliberately NOT exported from the Toast folder's `index.ts` — the\n * RAC toast surface is unstable and must not leak past this folder.\n */\nexport function racQueueOf(queue: ToastQueue): UNSTABLE_ToastQueue<ToastContent> {\n return RAC_QUEUES.get(queue)!;\n}\n\n/** Module-level default queue used by `toast()` and `<ToastRegion>`. */\nexport const defaultToastQueue = createToastQueue();\n\ntype ToastFn = {\n (title: string, options?: ToastOptions): void;\n info(title: string, options?: ToastOptions): void;\n success(title: string, options?: ToastOptions): void;\n warning(title: string, options?: ToastOptions): void;\n error(title: string, options?: ToastOptions): void;\n};\n\nfunction tonedAdd(tone: ToastTone) {\n return (title: string, options?: ToastOptions) => defaultToastQueue.add(tone, title, options);\n}\n\n/** Imperative convenience over `defaultToastQueue`. Bare `toast(...)` is `info`. */\nexport const toast: ToastFn = Object.assign(tonedAdd('info'), {\n info: tonedAdd('info'),\n success: tonedAdd('success'),\n warning: tonedAdd('warning'),\n error: tonedAdd('error'),\n});\n",".region {\n position: fixed;\n z-index: 1100;\n display: flex;\n /* RAC unshifts new toasts to the front of the DOM (newest-first), and the\n * ol/li wrappers are display:contents, so the flex direction alone\n * decides which end of the stack the newest toast lands on. Base\n * (bottom corners): column-reverse puts DOM-first (newest) at the\n * bottom, hugging the corner. Top corners override back to column so\n * DOM-first still hugs the top edge. */\n flex-direction: column-reverse;\n gap: 8px;\n max-width: 360px;\n outline: none;\n}\n\n.bottomRight { bottom: 16px; right: 16px; }\n.bottomLeft { bottom: 16px; left: 16px; }\n.topRight { top: 16px; right: 16px; flex-direction: column; }\n.topLeft { top: 16px; left: 16px; flex-direction: column; }\n\n.toast {\n --toast-accent: var(--wzl-accent-strong);\n /* Groups every toast under one ident so toastViewTransitions.css can\n * target `::view-transition-new/old(.wzl-toast)` without knowing the\n * per-toast view-transition-name (set inline in Toast.tsx). */\n view-transition-class: wzl-toast;\n display: flex;\n align-items: flex-start;\n gap: 8px;\n background: var(--wzl-surface);\n border: 1px solid var(--wzl-border);\n border-left: 3px solid var(--toast-accent);\n border-radius: var(--wzl-radius-md);\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);\n color: var(--wzl-fg);\n font-family: var(--wzl-font-ui);\n padding: 10px 12px;\n outline: none;\n}\n\n.region[data-focus-visible],\n.toast[data-focus-visible] {\n outline: 2px solid var(--wzl-focus-ring);\n outline-offset: 1px;\n}\n\n.toneInfo { /* default accent — uses --toast-accent fallback */ }\n.toneSuccess { --toast-accent: var(--wzl-success, #2ec27e); }\n.toneWarning { --toast-accent: var(--wzl-warning, #d99a3f); }\n.toneError { --toast-accent: var(--wzl-danger, #d94a3f); }\n\n.content {\n flex: 1;\n min-width: 0;\n}\n\n.title {\n display: block;\n font-size: 13px;\n font-weight: 600;\n}\n\n.description {\n display: block;\n margin-top: 2px;\n font-size: 12px;\n color: var(--wzl-fg-muted);\n}\n\n.close {\n flex: 0 0 auto;\n background: none;\n border: none;\n color: var(--wzl-fg-muted);\n font-size: 16px;\n line-height: 1;\n padding: 2px 4px;\n cursor: pointer;\n border-radius: var(--wzl-radius-sm);\n}\n\n.close:hover {\n color: var(--wzl-fg);\n}\n","import {\n UNSTABLE_ToastRegion as RACToastRegion,\n UNSTABLE_Toast as RACToast,\n UNSTABLE_ToastContent as RACToastContent,\n Text,\n Button,\n} from 'react-aria-components';\nimport { defaultToastQueue, racQueueOf, type ToastQueue, type ToastTone } from './queue';\nimport s from './Toast.module.css';\nimport './toastViewTransitions.css';\n\nexport type ToastPlacement = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\n\nexport interface ToastRegionProps {\n /** Queue to render. Defaults to the module-level `defaultToastQueue`. */\n queue?: ToastQueue;\n /** Screen corner for the stack. Default `bottom-right`. */\n placement?: ToastPlacement;\n className?: string;\n}\n\nconst placementClass: Record<ToastPlacement, string> = {\n 'bottom-right': s.bottomRight,\n 'bottom-left': s.bottomLeft,\n 'top-right': s.topRight,\n 'top-left': s.topLeft,\n};\n\nconst toneClass: Record<ToastTone, string> = {\n info: s.toneInfo,\n success: s.toneSuccess,\n warning: s.toneWarning,\n error: s.toneError,\n};\n\n/**\n * Renders the toast stack for a queue. Mount once, near the app root.\n * From the underlying React Aria region: landmark semantics (keyboard /\n * F6 reachable), screen-reader announcements, and hover pausing the\n * auto-dismiss timers.\n */\nexport function ToastRegion(props: ToastRegionProps) {\n const { queue = defaultToastQueue, placement = 'bottom-right', className } = props;\n return (\n <RACToastRegion\n queue={racQueueOf(queue)}\n aria-label=\"Notifications\"\n className={[s.region, placementClass[placement], className].filter(Boolean).join(' ')}\n >\n {({ toast: t }) => (\n <RACToast\n toast={t}\n className={[s.toast, toneClass[t.content.tone]].filter(Boolean).join(' ')}\n // Inline style is the one sanctioned exception here: each toast\n // needs a *unique* view-transition-name (so enter/exit/reflow\n // track per-toast), and a class can't mint unique idents. The\n // prefix keeps the ident valid (RAC keys can start with digits).\n style={{ viewTransitionName: `wzl-toast-${t.key.replace(/[^a-zA-Z0-9_-]/g, '_')}` }}\n >\n <RACToastContent className={s.content}>\n <Text slot=\"title\" className={s.title}>{t.content.title}</Text>\n {t.content.description !== undefined && (\n <Text slot=\"description\" className={s.description}>{t.content.description}</Text>\n )}\n </RACToastContent>\n <Button slot=\"close\" className={s.close} aria-label=\"Dismiss notification\">\n ×\n </Button>\n </RACToast>\n )}\n </RACToastRegion>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Toast-Bw3XQxmV.js","names":[],"sources":["../../src/components/Toast/queue.ts","../../src/components/Toast/Toast.module.css","../../src/components/Toast/Toast.tsx"],"sourcesContent":["import { UNSTABLE_ToastQueue } from 'react-aria-components';\nimport { flushSync } from 'react-dom';\n\nexport type ToastTone = 'info' | 'success' | 'warning' | 'error';\n\n/** Kit-owned payload rendered inside each toast. */\nexport interface ToastContent {\n title: string;\n description?: string;\n tone: ToastTone;\n}\n\nexport interface ToastOptions {\n description?: string;\n /**\n * Auto-dismiss delay in ms. Default 8000. `null` — sticky until dismissed.\n * Must be > 0; RAC treats 0 as no timer (sticky).\n */\n ttlMs?: number | null;\n /** Stable identity: re-adding with the same id replaces the earlier toast. */\n id?: string;\n}\n\nconst DEFAULT_TTL_MS = 8000;\n\n// Tracks overlapping toast-triggered view transitions so the `:root`\n// marker class survives until the *last* one settles.\nlet activeToastTransitions = 0;\n\n/**\n * RAC's sanctioned toast-animation hook: wrap every queue mutation in a\n * view transition so entry, exit, and the surviving stack's reflow all\n * animate (choreography lives in `toastViewTransitions.css`). Falls back\n * to a plain synchronous update when the browser lacks the API or the\n * user prefers reduced motion — i.e. exactly the pre-animation behavior.\n * The jsdom test environment always takes the fallback path.\n */\nfunction viewTransitionWrapUpdate(fn: () => void): void {\n const reduceMotion =\n typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;\n if (typeof document === 'undefined' || !('startViewTransition' in document) || reduceMotion) {\n fn();\n return;\n }\n // The marker class scopes the CSS that un-names `:root` (keeps the rest\n // of the page live and interactive during the transition) to\n // toast-owned transitions only.\n const root = document.documentElement;\n activeToastTransitions++;\n root.classList.add('wzl-toast-vt');\n // flushSync: the DOM must reflect the queue change inside the\n // transition callback, before the browser snapshots the new state.\n const vt = document.startViewTransition(() => flushSync(fn));\n // `ready` rejects when the browser skips the transition — routine under\n // a rapid burst of toasts, where each new transition supersedes the\n // in-flight one. The DOM update has still applied by then, but an\n // untouched `ready` surfaces the rejection as a console error, so\n // consume it.\n vt.ready.catch(() => {});\n void vt.finished.finally(() => {\n if (--activeToastTransitions === 0) root.classList.remove('wzl-toast-vt');\n });\n}\n\n// The wrapped RAC queue lives in a module-scoped WeakMap so the public\n// class surface carries no RAC (UNSTABLE_) types — see the design spec's\n// containment rule. `racQueueOf` below is the folder-internal accessor.\nconst RAC_QUEUES = new WeakMap<ToastQueue, UNSTABLE_ToastQueue<ToastContent>>();\n\n/**\n * Kit-owned toast queue. Create isolated instances with\n * `createToastQueue()` (tests, secondary roots); most apps use the\n * module-level `defaultToastQueue` via `toast()`.\n *\n * At most 5 toasts are visible at once; adding a 6th shows immediately\n * and hides the oldest (it returns when a slot frees, with its ttl\n * restarted).\n */\nexport class ToastQueue {\n private keysById = new Map<string, string>();\n\n constructor() {\n RAC_QUEUES.set(\n this,\n new UNSTABLE_ToastQueue<ToastContent>({\n maxVisibleToasts: 5,\n wrapUpdate: viewTransitionWrapUpdate,\n }),\n );\n }\n\n add(tone: ToastTone, title: string, options: ToastOptions = {}): void {\n const { description, ttlMs = DEFAULT_TTL_MS, id } = options;\n const rac = RAC_QUEUES.get(this)!;\n if (id !== undefined) {\n const existing = this.keysById.get(id);\n if (existing !== undefined) rac.close(existing);\n }\n let key: string;\n const racOptions: { timeout?: number; onClose?: () => void } =\n ttlMs === null ? {} : { timeout: ttlMs };\n if (id !== undefined) {\n racOptions.onClose = () => {\n if (this.keysById.get(id) === key) this.keysById.delete(id);\n };\n }\n key = rac.add({ title, description, tone }, racOptions);\n if (id !== undefined) this.keysById.set(id, key);\n }\n\n /** Dismiss the toast previously added with this `id`. No-op if none is live. */\n dismiss(id: string): void {\n const key = this.keysById.get(id);\n if (key !== undefined) RAC_QUEUES.get(this)!.close(key);\n }\n\n /** Dismiss every queued toast. */\n clear(): void {\n RAC_QUEUES.get(this)!.clear();\n this.keysById.clear();\n }\n}\n\nexport function createToastQueue(): ToastQueue {\n return new ToastQueue();\n}\n\n/**\n * Folder-internal bridge to the wrapped RAC queue for `ToastRegion`.\n * Deliberately NOT exported from the Toast folder's `index.ts` — the\n * RAC toast surface is unstable and must not leak past this folder.\n */\nexport function racQueueOf(queue: ToastQueue): UNSTABLE_ToastQueue<ToastContent> {\n return RAC_QUEUES.get(queue)!;\n}\n\n/** Module-level default queue used by `toast()` and `<ToastRegion>`. */\nexport const defaultToastQueue = createToastQueue();\n\ntype ToastFn = {\n (title: string, options?: ToastOptions): void;\n info(title: string, options?: ToastOptions): void;\n success(title: string, options?: ToastOptions): void;\n warning(title: string, options?: ToastOptions): void;\n error(title: string, options?: ToastOptions): void;\n};\n\nfunction tonedAdd(tone: ToastTone) {\n return (title: string, options?: ToastOptions) => defaultToastQueue.add(tone, title, options);\n}\n\n/** Imperative convenience over `defaultToastQueue`. Bare `toast(...)` is `info`. */\nexport const toast: ToastFn = Object.assign(tonedAdd('info'), {\n info: tonedAdd('info'),\n success: tonedAdd('success'),\n warning: tonedAdd('warning'),\n error: tonedAdd('error'),\n});\n",".region {\n position: fixed;\n z-index: 1100;\n display: flex;\n /* RAC unshifts new toasts to the front of the DOM (newest-first), and the\n * ol/li wrappers are display:contents, so the flex direction alone\n * decides which end of the stack the newest toast lands on. Base\n * (bottom corners): column-reverse puts DOM-first (newest) at the\n * bottom, hugging the corner. Top corners override back to column so\n * DOM-first still hugs the top edge. */\n flex-direction: column-reverse;\n gap: 8px;\n max-width: 360px;\n outline: none;\n}\n\n.bottomRight { bottom: 16px; right: 16px; }\n.bottomLeft { bottom: 16px; left: 16px; }\n.topRight { top: 16px; right: 16px; flex-direction: column; }\n.topLeft { top: 16px; left: 16px; flex-direction: column; }\n\n.toast {\n --toast-accent: var(--wzl-accent-strong);\n /* Groups every toast under one ident so toastViewTransitions.css can\n * target `::view-transition-new/old(.wzl-toast)` without knowing the\n * per-toast view-transition-name (set inline in Toast.tsx). */\n view-transition-class: wzl-toast;\n display: flex;\n align-items: flex-start;\n gap: 8px;\n background: var(--wzl-surface);\n border: 1px solid var(--wzl-border);\n border-left: 3px solid var(--toast-accent);\n border-radius: var(--wzl-radius-md);\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);\n color: var(--wzl-fg);\n font-family: var(--wzl-font-ui);\n padding: 10px 12px;\n outline: none;\n}\n\n.region[data-focus-visible],\n.toast[data-focus-visible] {\n outline: 2px solid var(--wzl-focus-ring);\n outline-offset: 1px;\n}\n\n.toneInfo { /* default accent — uses --toast-accent fallback */ }\n.toneSuccess { --toast-accent: var(--wzl-success, #2ec27e); }\n.toneWarning { --toast-accent: var(--wzl-warning, #d99a3f); }\n.toneError { --toast-accent: var(--wzl-danger, #d94a3f); }\n\n.content {\n flex: 1;\n min-width: 0;\n}\n\n.title {\n display: block;\n font-size: 13px;\n font-weight: 600;\n}\n\n.description {\n display: block;\n margin-top: 2px;\n font-size: 12px;\n color: var(--wzl-fg-muted);\n}\n\n.close {\n flex: 0 0 auto;\n background: none;\n border: none;\n color: var(--wzl-fg-muted);\n font-size: 16px;\n line-height: 1;\n padding: 2px 4px;\n cursor: pointer;\n border-radius: var(--wzl-radius-sm);\n}\n\n.close:hover {\n color: var(--wzl-fg);\n}\n","import {\n UNSTABLE_ToastRegion as RACToastRegion,\n UNSTABLE_Toast as RACToast,\n UNSTABLE_ToastContent as RACToastContent,\n Text,\n Button,\n} from 'react-aria-components';\nimport { defaultToastQueue, racQueueOf, type ToastQueue, type ToastTone } from './queue';\nimport s from './Toast.module.css';\nimport './toastViewTransitions.css';\n\nexport type ToastPlacement = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\n\nexport interface ToastRegionProps {\n /** Queue to render. Defaults to the module-level `defaultToastQueue`. */\n queue?: ToastQueue;\n /** Screen corner for the stack. Default `bottom-right`. */\n placement?: ToastPlacement;\n className?: string;\n}\n\nconst placementClass: Record<ToastPlacement, string> = {\n 'bottom-right': s.bottomRight,\n 'bottom-left': s.bottomLeft,\n 'top-right': s.topRight,\n 'top-left': s.topLeft,\n};\n\nconst toneClass: Record<ToastTone, string> = {\n info: s.toneInfo,\n success: s.toneSuccess,\n warning: s.toneWarning,\n error: s.toneError,\n};\n\n/**\n * Renders the toast stack for a queue. Mount once, near the app root.\n * From the underlying React Aria region: landmark semantics (keyboard /\n * F6 reachable), screen-reader announcements, and hover pausing the\n * auto-dismiss timers.\n */\nexport function ToastRegion(props: ToastRegionProps) {\n const { queue = defaultToastQueue, placement = 'bottom-right', className } = props;\n return (\n <RACToastRegion\n queue={racQueueOf(queue)}\n aria-label=\"Notifications\"\n className={[s.region, placementClass[placement], className].filter(Boolean).join(' ')}\n >\n {({ toast: t }) => (\n <RACToast\n toast={t}\n className={[s.toast, toneClass[t.content.tone]].filter(Boolean).join(' ')}\n // Inline style is the one sanctioned exception here: each toast\n // needs a *unique* view-transition-name (so enter/exit/reflow\n // track per-toast), and a class can't mint unique idents. The\n // prefix keeps the ident valid (RAC keys can start with digits).\n style={{ viewTransitionName: `wzl-toast-${t.key.replace(/[^a-zA-Z0-9_-]/g, '_')}` }}\n >\n <RACToastContent className={s.content}>\n <Text slot=\"title\" className={s.title}>{t.content.title}</Text>\n {t.content.description !== undefined && (\n <Text slot=\"description\" className={s.description}>{t.content.description}</Text>\n )}\n </RACToastContent>\n <Button slot=\"close\" className={s.close} aria-label=\"Dismiss notification\">\n ×\n </Button>\n </RACToast>\n )}\n </RACToastRegion>\n );\n}\n"],"mappings":"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"}