@mt-gloss/ui 0.1.114 → 0.1.116

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.
@@ -1,542 +0,0 @@
1
- import { jsx as c, jsxs as E } from "react/jsx-runtime";
2
- import { motion as L } from "framer-motion";
3
- import { useSyncExternalStore as O, createContext as T, useContext as y, useReducer as N, useRef as C, useEffect as d, useMemo as S, useCallback as I, useLayoutEffect as _, useState as w } from "react";
4
- import { z as f } from "zod";
5
- const x = "(prefers-reduced-motion: reduce)";
6
- function b() {
7
- return typeof window > "u" || typeof window.matchMedia != "function" ? null : window.matchMedia(x);
8
- }
9
- function H(e) {
10
- const n = b();
11
- return n ? (n.addEventListener("change", e), () => {
12
- n.removeEventListener("change", e);
13
- }) : () => {
14
- };
15
- }
16
- function A() {
17
- const e = b();
18
- return e ? e.matches : !1;
19
- }
20
- function F() {
21
- return !1;
22
- }
23
- function P() {
24
- return O(H, A, F);
25
- }
26
- const R = {
27
- threshold: "Threshold",
28
- timeframe: "Timeframe",
29
- slots: "Slots",
30
- color: "Color"
31
- };
32
- function B({
33
- dimensions: e,
34
- activeTab: n,
35
- onSelect: t,
36
- reduced: r,
37
- labels: l,
38
- ariaLabel: a = "Settings dimensions"
39
- }) {
40
- const s = P(), i = r ?? s;
41
- if (e.length < 2) return null;
42
- const u = (o) => l?.[o] ?? R[o], h = (o, p) => {
43
- o.key === "ArrowRight" ? (o.preventDefault(), t(e[(p + 1) % e.length])) : o.key === "ArrowLeft" ? (o.preventDefault(), t(e[(p - 1 + e.length) % e.length])) : o.key === "Home" ? (o.preventDefault(), t(e[0])) : o.key === "End" && (o.preventDefault(), t(e[e.length - 1]));
44
- };
45
- return /* @__PURE__ */ c(
46
- "div",
47
- {
48
- role: "tablist",
49
- "aria-label": a,
50
- className: "settings-tab-strip",
51
- children: e.map((o, p) => {
52
- const g = n === o;
53
- return /* @__PURE__ */ E(
54
- "button",
55
- {
56
- role: "tab",
57
- type: "button",
58
- id: `settings-tab-${o}`,
59
- "aria-selected": g,
60
- "aria-controls": `settings-tabpanel-${o}`,
61
- tabIndex: g ? 0 : -1,
62
- className: `settings-tab-strip__tab${g ? " settings-tab-strip__tab--active" : ""}`,
63
- onClick: () => t(o),
64
- onKeyDown: (D) => h(D, p),
65
- children: [
66
- /* @__PURE__ */ c("span", { className: "settings-tab-strip__label", children: u(o) }),
67
- g && !i && /* @__PURE__ */ c(
68
- L.span,
69
- {
70
- layoutId: "settings-active-tab-indicator",
71
- "data-layout-id": "settings-active-tab-indicator",
72
- className: "settings-tab-strip__indicator",
73
- "aria-hidden": "true",
74
- transition: { type: "spring", stiffness: 380, damping: 30 }
75
- }
76
- )
77
- ]
78
- },
79
- o
80
- );
81
- })
82
- }
83
- );
84
- }
85
- B.displayName = "SettingsTabStrip";
86
- const m = T(null);
87
- function k() {
88
- const e = y(m);
89
- if (!e)
90
- throw new Error(
91
- "[panels] usePanelContext must be used inside <PanelProvider>. See @mt-gloss/ui/composites/panels."
92
- );
93
- return e;
94
- }
95
- function oe() {
96
- return y(m);
97
- }
98
- const M = f.number().min(0).max(100), U = f.number().min(7).max(90), V = f.enum(["#1d6fd8", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6"]), K = f.array(f.string().min(1)).max(3), j = f.boolean();
99
- function q(e, n) {
100
- if (e === "threshold") {
101
- const t = M.safeParse(n);
102
- return t.success ? t.data : 80;
103
- }
104
- if (e === "timeframe") {
105
- const t = U.safeParse(n);
106
- return t.success ? t.data : 30;
107
- }
108
- if (e === "accent") {
109
- const t = V.safeParse(n);
110
- return t.success ? t.data : null;
111
- }
112
- if (e === "slots") {
113
- const t = K.safeParse(n);
114
- return t.success ? t.data : [];
115
- }
116
- if (e === "thresholdEnabled" || e === "accentEnabled") {
117
- const t = j.safeParse(n);
118
- return t.success ? t.data : !1;
119
- }
120
- return n;
121
- }
122
- const z = {
123
- activePanelId: null,
124
- activeTrigger: null,
125
- bellCutoutOpen: !1,
126
- isDirty: !1,
127
- dirtySessionCount: 0,
128
- lockHint: null,
129
- bufferByCard: {},
130
- lastClose: null,
131
- pendingSideEffects: []
132
- }, v = /* @__PURE__ */ new Set(["settings"]);
133
- function Y(e, n) {
134
- if (e == null || e.kind !== n.kind) return !1;
135
- switch (e.kind) {
136
- case "bell":
137
- return !0;
138
- case "card-overflow":
139
- return e.cardId === n.cardId && e.sectionIndex === n.sectionIndex && e.action === n.action;
140
- case "empty-cell":
141
- return e.sectionIndex === n.sectionIndex && e.cellPosition.row === n.cellPosition.row && e.cellPosition.col === n.cellPosition.col;
142
- case "manage-pages-action":
143
- return e.global === n.global && e.align === n.align;
144
- case "layout-action-blocked":
145
- return e.action === n.action && e.blockedCardId === n.blockedCardId && e.activeCardId === n.activeCardId;
146
- }
147
- }
148
- function $(e, n) {
149
- switch (n.type) {
150
- case "OPEN_PANEL": {
151
- if (e.activePanelId && e.isDirty)
152
- return {
153
- ...e,
154
- pendingSideEffects: [
155
- ...e.pendingSideEffects,
156
- {
157
- type: "SET_LOCK_HINT",
158
- hint: {
159
- blockedTrigger: n.trigger,
160
- activeCardId: e.activeTrigger?.cardId ?? null,
161
- expiresAt: Date.now() + 4e3
162
- }
163
- }
164
- ]
165
- };
166
- if (e.activePanelId === n.panelId && Y(e.activeTrigger, n.trigger))
167
- return e;
168
- const t = e.activePanelId != null ? [
169
- {
170
- type: "HANDOFF_CLOSE",
171
- reason: "mutex-handoff",
172
- previousPanelId: e.activePanelId
173
- }
174
- ] : [], r = n.panelId === "notification-center" && e.bellCutoutOpen;
175
- return {
176
- ...e,
177
- activePanelId: n.panelId,
178
- activeTrigger: n.trigger,
179
- isDirty: !1,
180
- dirtySessionCount: 0,
181
- // Phase 16 P2 CC-17 — fresh open starts a clean session
182
- bellCutoutOpen: r ? !1 : e.bellCutoutOpen,
183
- lastClose: null,
184
- // reset on fresh open
185
- pendingSideEffects: [...e.pendingSideEffects, ...t]
186
- };
187
- }
188
- case "CLOSE_PANEL": {
189
- if (e.activePanelId == null)
190
- return e;
191
- const t = e.activePanelId, r = e.activeTrigger?.cardId ?? null, l = r != null ? e.bufferByCard[r] : void 0, a = e.isDirty ? n.reason === "apply-button" ? r != null && l != null ? [
192
- {
193
- type: "COMMIT_BUFFER",
194
- payload: { cardId: r, delta: l }
195
- }
196
- ] : [{ type: "COMMIT_BUFFER" }] : [{ type: "DISCARD_BUFFER" }] : [], s = r != null && l != null ? (() => {
197
- const i = { ...e.bufferByCard };
198
- return delete i[r], i;
199
- })() : e.bufferByCard;
200
- return {
201
- ...e,
202
- activePanelId: null,
203
- activeTrigger: null,
204
- isDirty: !1,
205
- dirtySessionCount: 0,
206
- // Phase 16 P2 CC-17 — session ends on panel close (apply or discard)
207
- lockHint: null,
208
- // closing clears any active lockHint (dirty-buffer condition gone)
209
- bufferByCard: s,
210
- lastClose: { panelId: t, reason: n.reason },
211
- pendingSideEffects: [...e.pendingSideEffects, ...a]
212
- };
213
- }
214
- case "OPEN_BELL_CUTOUT":
215
- return e.bellCutoutOpen ? e : e.activePanelId === "notification-center" ? {
216
- ...e,
217
- activePanelId: null,
218
- activeTrigger: null,
219
- bellCutoutOpen: !0,
220
- pendingSideEffects: [
221
- ...e.pendingSideEffects,
222
- { type: "AUTO_CLOSE_NOTIF_CENTER" }
223
- ]
224
- } : { ...e, bellCutoutOpen: !0 };
225
- case "CLOSE_BELL_CUTOUT":
226
- return e.bellCutoutOpen ? { ...e, bellCutoutOpen: !1 } : e;
227
- case "SET_BUFFER_VALUE": {
228
- if (e.activePanelId == null || !v.has(e.activePanelId))
229
- return {
230
- ...e,
231
- pendingSideEffects: [
232
- ...e.pendingSideEffects,
233
- { type: "INVALID_DISPATCH", reason: "no-buffer-panel" }
234
- ]
235
- };
236
- const t = e.bufferByCard[n.cardId], r = t == null, l = q(n.key, n.value);
237
- if (!r && t.changes[n.key] === l)
238
- return e;
239
- const s = { kind: "settings", changes: {
240
- ...t?.kind === "settings" ? t.changes : {},
241
- [n.key]: l
242
- } }, i = { ...e.bufferByCard, [n.cardId]: s }, u = r ? [
243
- {
244
- type: "SCHEDULE_TIMER",
245
- ms: 0,
246
- action: { type: "BUFFER_BECOMES_DIRTY" }
247
- }
248
- ] : [];
249
- return {
250
- ...e,
251
- bufferByCard: i,
252
- pendingSideEffects: [...e.pendingSideEffects, ...u]
253
- };
254
- }
255
- case "CLEAR_BUFFER": {
256
- if (e.bufferByCard[n.cardId] == null)
257
- return e;
258
- const r = Object.keys(e.bufferByCard).length === 1, l = { ...e.bufferByCard };
259
- delete l[n.cardId];
260
- const a = r && e.isDirty ? [
261
- {
262
- type: "SCHEDULE_TIMER",
263
- ms: 0,
264
- action: { type: "BUFFER_BECOMES_CLEAN" }
265
- }
266
- ] : [];
267
- return {
268
- ...e,
269
- bufferByCard: l,
270
- pendingSideEffects: [...e.pendingSideEffects, ...a]
271
- };
272
- }
273
- case "BUFFER_BECOMES_DIRTY":
274
- return e.activePanelId == null || !v.has(e.activePanelId) ? {
275
- ...e,
276
- pendingSideEffects: [
277
- ...e.pendingSideEffects,
278
- { type: "INVALID_DISPATCH", reason: "no-buffer-panel" }
279
- ]
280
- } : e.isDirty ? e : { ...e, isDirty: !0, dirtySessionCount: e.dirtySessionCount + 1 };
281
- case "BUFFER_BECOMES_CLEAN":
282
- return e.isDirty ? { ...e, isDirty: !1, dirtySessionCount: 0 } : e;
283
- case "SET_LOCK_HINT":
284
- return {
285
- ...e,
286
- lockHint: n.hint,
287
- pendingSideEffects: [
288
- ...e.pendingSideEffects,
289
- {
290
- type: "SCHEDULE_TIMER",
291
- ms: Math.max(0, n.hint.expiresAt - Date.now()),
292
- action: { type: "CLEAR_LOCK_HINT" }
293
- }
294
- ]
295
- };
296
- case "CLEAR_LOCK_HINT":
297
- return e.lockHint == null ? e : { ...e, lockHint: null };
298
- case "DRAIN_SIDE_EFFECTS":
299
- return e.pendingSideEffects.length === 0 ? e : typeof n.count == "number" && n.count > 0 ? n.count >= e.pendingSideEffects.length ? { ...e, pendingSideEffects: [] } : {
300
- ...e,
301
- pendingSideEffects: e.pendingSideEffects.slice(n.count)
302
- } : {
303
- ...e,
304
- pendingSideEffects: []
305
- };
306
- default:
307
- return e;
308
- }
309
- }
310
- function Q(e) {
311
- return e.activePanelId === "notification-center" ? "notif-center" : e.activePanelId != null && e.bellCutoutOpen ? e.isDirty ? "panel-dirty+cutout" : "panel-clean+cutout" : e.activePanelId != null ? e.isDirty ? "panel-dirty" : "panel-clean" : e.bellCutoutOpen ? "bell-cutout-only" : "idle";
312
- }
313
- function W() {
314
- const [e, n] = N($, z), t = C([]), r = C(null);
315
- d(() => () => {
316
- r.current != null && (window.clearTimeout(r.current), r.current = null);
317
- }, []), d(() => {
318
- const s = e.pendingSideEffects.length;
319
- if (s !== 0) {
320
- for (const i of e.pendingSideEffects)
321
- i.type === "SCHEDULE_TIMER" ? (r.current != null && window.clearTimeout(r.current), r.current = window.setTimeout(() => {
322
- r.current = null, n(i.action);
323
- }, Math.max(0, i.ms))) : i.type === "SET_LOCK_HINT" ? n({ type: "SET_LOCK_HINT", hint: i.hint }) : i.type === "HANDOFF_CLOSE" ? t.current = [
324
- ...t.current.slice(-99),
325
- {
326
- panelId: i.previousPanelId,
327
- reason: "mutex-handoff",
328
- at: Date.now(),
329
- note: "F22: mutex-handoff synthetic close-log entry"
330
- }
331
- ] : i.type === "INVALID_DISPATCH" && process.env.NODE_ENV !== "production" && console.warn("[panels] INVALID_DISPATCH:", i.reason);
332
- n({ type: "DRAIN_SIDE_EFFECTS", count: s });
333
- }
334
- }, [e.pendingSideEffects, n]), d(() => {
335
- if (!e.lastClose) return;
336
- const s = {
337
- panelId: e.lastClose.panelId,
338
- reason: e.lastClose.reason,
339
- at: Date.now(),
340
- note: e.lastClose.reason === "nav-escape-hatch" ? "F21: backdrop-click OR page-nav escape" : void 0
341
- };
342
- t.current = [
343
- ...t.current.slice(-99),
344
- s
345
- ];
346
- }, [e.lastClose]);
347
- const l = S(
348
- () => Q(e),
349
- [e.activePanelId, e.bellCutoutOpen, e.isDirty]
350
- ), a = I(() => t.current.slice(), []);
351
- return { state: e, surfaceState: l, dispatch: n, getCloseLog: a };
352
- }
353
- function ae({ children: e }) {
354
- const n = W(), t = S(
355
- () => n,
356
- // Re-publish only when one of the four coordinator fields changes identity.
357
- // dispatch + getCloseLog are stable. state + surfaceState change on dispatch.
358
- [n.state, n.surfaceState, n.dispatch, n.getCloseLog]
359
- );
360
- return /* @__PURE__ */ c(m.Provider, { value: t, children: e });
361
- }
362
- const G = {
363
- position: "relative",
364
- pointerEvents: "auto"
365
- };
366
- function J({ children: e, onClose: n, footer: t, title: r, variant: l = "default" }) {
367
- return /* @__PURE__ */ E(
368
- "div",
369
- {
370
- "data-chrome": "panel",
371
- "data-chrome-variant": l,
372
- role: "dialog",
373
- "aria-modal": "true",
374
- className: l === "merged-stage" ? "gloss-panel-chrome gloss-panel-chrome--merged-stage" : "gloss-panel-chrome",
375
- style: G,
376
- children: [
377
- /* @__PURE__ */ E("div", { className: "gloss-panel-chrome__header", children: [
378
- r != null && /* @__PURE__ */ c("span", { className: "gloss-panel-chrome__title", children: r }),
379
- /* @__PURE__ */ c(
380
- "button",
381
- {
382
- id: "panel-close",
383
- type: "button",
384
- "aria-label": "Close panel",
385
- className: "gloss-panel-chrome__close",
386
- onClick: n,
387
- children: "×"
388
- }
389
- )
390
- ] }),
391
- /* @__PURE__ */ c("div", { className: "gloss-panel-chrome__body", children: e }),
392
- t != null && /* @__PURE__ */ c("div", { className: "gloss-panel-chrome__footer", children: t })
393
- ]
394
- }
395
- );
396
- }
397
- function X(e) {
398
- if (process.env.NODE_ENV === "production" || !e || !e.parentElement) return;
399
- const n = e.parentElement, t = window.getComputedStyle(n);
400
- t.perspective === "none" && console.warn(
401
- "[PanelMount] §2.7 violation: parent of .gloss-panel-mount has perspective:none. Rotated mount will render flat. Parent:",
402
- n
403
- ), (t.overflow === "hidden" || t.overflowY === "hidden") && console.warn(
404
- "[PanelMount] §2.9 violation: parent of .gloss-panel-mount has overflow:hidden. Rising panel will be clipped. Parent:",
405
- n
406
- );
407
- }
408
- function Z({ isOpen: e, children: n }) {
409
- const t = P(), r = C(null);
410
- _(() => {
411
- X(r.current);
412
- }, []);
413
- const [l, a] = w(!1);
414
- d(() => {
415
- if (!e) {
416
- a(!1);
417
- return;
418
- }
419
- const i = requestAnimationFrame(() => a(!0));
420
- return () => cancelAnimationFrame(i);
421
- }, [e]);
422
- const s = [
423
- "gloss-panel-mount",
424
- l ? "has-panel" : "",
425
- t ? "reduced-motion" : ""
426
- ].filter(Boolean).join(" ");
427
- return /* @__PURE__ */ c(
428
- "div",
429
- {
430
- ref: r,
431
- className: s,
432
- "data-mount": "panel",
433
- "data-open": e ? "true" : "false",
434
- children: n
435
- }
436
- );
437
- }
438
- const ee = {
439
- position: "fixed",
440
- top: 0,
441
- left: 0,
442
- right: 0,
443
- height: "var(--tb-h, 60px)",
444
- perspective: "var(--panel-perspective, 1500px)",
445
- perspectiveOrigin: "center bottom",
446
- overflow: "visible",
447
- pointerEvents: "none",
448
- zIndex: 55
449
- }, ne = {
450
- position: "fixed",
451
- top: "var(--tb-h, 60px)",
452
- left: 0,
453
- right: 0,
454
- bottom: 0,
455
- zIndex: 1,
456
- // CR-03 fix: pass-through tint — scrim is visual-only and must not intercept
457
- // grid card clicks. Click-outside / scrim-dismiss is handled via the onClick
458
- // on the div below, which still fires because pointer events on the element
459
- // itself are honoured when explicitly set; using 'none' prevents the invisible
460
- // overlay from swallowing clicks on underlying controls (same class as the
461
- // panel-wrapper-covers-grid-cards bug fixed at V2FeedbackSurface).
462
- pointerEvents: "none",
463
- background: "rgba(0, 0, 0, 0.04)"
464
- };
465
- function se({ panelComponents: e }) {
466
- const { state: n, dispatch: t, getCloseLog: r } = k(), l = n.activePanelId ? e[n.activePanelId] : null;
467
- n.activePanelId && !l && process.env.NODE_ENV !== "production" && console.error(
468
- "[panels] No panel component registered for activePanelId:",
469
- n.activePanelId
470
- ), d(() => {
471
- if (n.activePanelId == null) return;
472
- function u(h) {
473
- h.key === "Escape" && t({ type: "CLOSE_PANEL", reason: "escape-key" });
474
- }
475
- return document.addEventListener("keydown", u), () => {
476
- document.removeEventListener("keydown", u);
477
- };
478
- }, [n.activePanelId, t]);
479
- const a = C(n);
480
- _(() => {
481
- a.current = n;
482
- }), d(() => {
483
- if (process.env.NODE_ENV === "production") return;
484
- const u = window;
485
- return u.__panelsHostHandle && console.warn("[panels] multiple PanelHost/PanelHostShell instances mounted; dev handle is unreliable"), u.__panelsHostHandle = {
486
- dispatch: t,
487
- getState: () => a.current,
488
- getCloseLog: r
489
- }, () => {
490
- u.__panelsHostHandle?.dispatch === t && delete u.__panelsHostHandle;
491
- };
492
- }, [t, r]);
493
- const s = I(() => {
494
- t({ type: "CLOSE_PANEL", reason: "nav-escape-hatch" });
495
- }, [t]), i = n.activePanelId != null && l != null;
496
- return /* @__PURE__ */ E("div", { "data-panel-host": "true", style: ee, children: [
497
- i && /* @__PURE__ */ c(
498
- "div",
499
- {
500
- "data-panel-scrim": "true",
501
- style: ne,
502
- onClick: s
503
- }
504
- ),
505
- /* @__PURE__ */ c(Z, { isOpen: i, children: i && /* @__PURE__ */ c(
506
- J,
507
- {
508
- isOpen: !0,
509
- onClose: () => t({ type: "CLOSE_PANEL", reason: "header-x" }),
510
- children: /* @__PURE__ */ c(l, { isOpen: !0 })
511
- }
512
- ) })
513
- ] });
514
- }
515
- const ce = {
516
- "apply-button": !0,
517
- // the ONLY commit reason
518
- "cancel-button": !1,
519
- "header-x": !1,
520
- "escape-key": !1,
521
- "page-nav": !1,
522
- "mutex-handoff": !1,
523
- "ghost-anchor": !1,
524
- "viewport-collapse": !1,
525
- unmount: !1,
526
- "nav-escape-hatch": !1
527
- };
528
- export {
529
- ce as C,
530
- ae as P,
531
- B as S,
532
- k as a,
533
- se as b,
534
- J as c,
535
- W as d,
536
- Q as e,
537
- P as f,
538
- Z as g,
539
- z as i,
540
- $ as r,
541
- oe as u
542
- };