@mt-gloss/ui 0.1.84 → 0.1.85

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.
@@ -0,0 +1,292 @@
1
+ import { jsx as i, jsxs as d, Fragment as v } from "react/jsx-runtime";
2
+ import { createContext as I, useContext as f, useReducer as S, useRef as c, useEffect as a, useMemo as p, useCallback as E, useLayoutEffect as P } from "react";
3
+ const s = I(null);
4
+ function _() {
5
+ const e = f(s);
6
+ if (!e)
7
+ throw new Error(
8
+ "[panels] usePanelContext must be used inside <PanelProvider>. See @mt-gloss/ui/composites/panels."
9
+ );
10
+ return e;
11
+ }
12
+ function w() {
13
+ return f(s);
14
+ }
15
+ const y = {
16
+ activePanelId: null,
17
+ activeTrigger: null,
18
+ bellCutoutOpen: !1,
19
+ isDirty: !1,
20
+ lockHint: null,
21
+ lastClose: null,
22
+ pendingSideEffects: []
23
+ }, h = /* @__PURE__ */ new Set(["settings"]);
24
+ function O(e, n) {
25
+ return e == null ? !1 : JSON.stringify(e) === JSON.stringify(n);
26
+ }
27
+ function m(e, n) {
28
+ switch (n.type) {
29
+ case "OPEN_PANEL": {
30
+ if (e.activePanelId && e.isDirty)
31
+ return {
32
+ ...e,
33
+ pendingSideEffects: [
34
+ ...e.pendingSideEffects,
35
+ {
36
+ type: "SET_LOCK_HINT",
37
+ hint: {
38
+ blockedTrigger: n.trigger,
39
+ activeCardId: e.activeTrigger?.cardId ?? null,
40
+ expiresAt: Date.now() + 4e3
41
+ }
42
+ }
43
+ ]
44
+ };
45
+ if (e.activePanelId === n.panelId && O(e.activeTrigger, n.trigger))
46
+ return e;
47
+ const t = e.activePanelId != null ? [
48
+ {
49
+ type: "HANDOFF_CLOSE",
50
+ reason: "mutex-handoff",
51
+ previousPanelId: e.activePanelId
52
+ }
53
+ ] : [], r = n.panelId === "notification-center" && e.bellCutoutOpen;
54
+ return {
55
+ ...e,
56
+ activePanelId: n.panelId,
57
+ activeTrigger: n.trigger,
58
+ isDirty: !1,
59
+ bellCutoutOpen: r ? !1 : e.bellCutoutOpen,
60
+ lastClose: null,
61
+ // reset on fresh open
62
+ pendingSideEffects: [...e.pendingSideEffects, ...t]
63
+ };
64
+ }
65
+ case "CLOSE_PANEL": {
66
+ if (e.activePanelId == null)
67
+ return e;
68
+ const t = e.activePanelId, r = e.isDirty ? n.reason === "apply-button" ? [{ type: "COMMIT_BUFFER" }] : [{ type: "DISCARD_BUFFER" }] : [];
69
+ return {
70
+ ...e,
71
+ activePanelId: null,
72
+ activeTrigger: null,
73
+ isDirty: !1,
74
+ lockHint: null,
75
+ // closing clears any active lockHint (dirty-buffer condition gone)
76
+ lastClose: { panelId: t, reason: n.reason },
77
+ pendingSideEffects: [...e.pendingSideEffects, ...r]
78
+ };
79
+ }
80
+ case "OPEN_BELL_CUTOUT":
81
+ return e.bellCutoutOpen ? e : e.activePanelId === "notification-center" ? {
82
+ ...e,
83
+ activePanelId: null,
84
+ activeTrigger: null,
85
+ bellCutoutOpen: !0,
86
+ pendingSideEffects: [
87
+ ...e.pendingSideEffects,
88
+ { type: "AUTO_CLOSE_NOTIF_CENTER" }
89
+ ]
90
+ } : { ...e, bellCutoutOpen: !0 };
91
+ case "CLOSE_BELL_CUTOUT":
92
+ return e.bellCutoutOpen ? { ...e, bellCutoutOpen: !1 } : e;
93
+ case "BUFFER_BECOMES_DIRTY":
94
+ return e.activePanelId == null || !h.has(e.activePanelId) ? {
95
+ ...e,
96
+ pendingSideEffects: [
97
+ ...e.pendingSideEffects,
98
+ { type: "INVALID_DISPATCH", reason: "no-buffer-panel" }
99
+ ]
100
+ } : e.isDirty ? e : { ...e, isDirty: !0 };
101
+ case "BUFFER_BECOMES_CLEAN":
102
+ return e.isDirty ? { ...e, isDirty: !1 } : e;
103
+ case "SET_LOCK_HINT":
104
+ return {
105
+ ...e,
106
+ lockHint: n.hint,
107
+ pendingSideEffects: [
108
+ ...e.pendingSideEffects,
109
+ {
110
+ type: "SCHEDULE_TIMER",
111
+ ms: Math.max(0, n.hint.expiresAt - Date.now()),
112
+ action: { type: "CLEAR_LOCK_HINT" }
113
+ }
114
+ ]
115
+ };
116
+ case "CLEAR_LOCK_HINT":
117
+ return e.lockHint == null ? e : { ...e, lockHint: null };
118
+ case "DRAIN_SIDE_EFFECTS":
119
+ return e.pendingSideEffects.length === 0 ? e : {
120
+ ...e,
121
+ pendingSideEffects: []
122
+ };
123
+ default:
124
+ return e;
125
+ }
126
+ }
127
+ function L(e) {
128
+ 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";
129
+ }
130
+ function D() {
131
+ const [e, n] = S(m, y), t = c([]);
132
+ c(null), a(() => {
133
+ if (e.pendingSideEffects.length !== 0) {
134
+ for (const l of e.pendingSideEffects)
135
+ l.type === "SCHEDULE_TIMER" ? window.setTimeout(() => n(l.action), Math.max(0, l.ms)) : l.type === "INVALID_DISPATCH" && process.env.NODE_ENV !== "production" && console.warn("[panels] INVALID_DISPATCH:", l.reason);
136
+ n({ type: "DRAIN_SIDE_EFFECTS" });
137
+ }
138
+ }, [e.pendingSideEffects]), a(() => {
139
+ if (!e.lastClose) return;
140
+ const l = {
141
+ panelId: e.lastClose.panelId,
142
+ reason: e.lastClose.reason,
143
+ at: Date.now(),
144
+ note: e.lastClose.reason === "nav-escape-hatch" ? "F21: backdrop-click OR page-nav escape" : void 0
145
+ };
146
+ t.current = [
147
+ ...t.current.slice(-99),
148
+ l
149
+ ];
150
+ }, [e.lastClose]);
151
+ const r = p(
152
+ () => L(e),
153
+ // eslint-disable-next-line react-hooks/exhaustive-deps
154
+ [e.activePanelId]
155
+ ), o = E(() => t.current.slice(), []);
156
+ return { state: e, surfaceState: r, dispatch: n, getCloseLog: o };
157
+ }
158
+ function F({ children: e }) {
159
+ const n = D(), t = p(
160
+ () => n,
161
+ // Re-publish only when one of the four coordinator fields changes identity.
162
+ // dispatch + getCloseLog are stable. state + surfaceState change on dispatch.
163
+ [n.state, n.surfaceState, n.dispatch, n.getCloseLog]
164
+ );
165
+ return /* @__PURE__ */ i(s.Provider, { value: t, children: e });
166
+ }
167
+ const N = {
168
+ position: "relative",
169
+ pointerEvents: "auto"
170
+ };
171
+ function b({ children: e, onClose: n, isOpen: t = !0 }) {
172
+ return /* @__PURE__ */ d(
173
+ "div",
174
+ {
175
+ "data-chrome": "panel",
176
+ "data-open": t ? "true" : "false",
177
+ role: "dialog",
178
+ "aria-modal": "true",
179
+ className: "gloss-panel-chrome",
180
+ style: N,
181
+ children: [
182
+ /* @__PURE__ */ i("div", { className: "gloss-panel-chrome__header", children: /* @__PURE__ */ i(
183
+ "button",
184
+ {
185
+ id: "panel-close",
186
+ type: "button",
187
+ "aria-label": "Close panel",
188
+ className: "gloss-panel-chrome__close",
189
+ onClick: n,
190
+ children: "×"
191
+ }
192
+ ) }),
193
+ /* @__PURE__ */ i("div", { className: "gloss-panel-chrome__body", children: e })
194
+ ]
195
+ }
196
+ );
197
+ }
198
+ const T = {
199
+ position: "fixed",
200
+ top: 0,
201
+ left: 0,
202
+ right: 0,
203
+ height: "var(--tb-h, 60px)",
204
+ perspective: "1200px",
205
+ perspectiveOrigin: "center bottom",
206
+ overflow: "visible",
207
+ pointerEvents: "none",
208
+ zIndex: 55
209
+ }, x = {
210
+ position: "fixed",
211
+ top: "var(--tb-h, 60px)",
212
+ left: 0,
213
+ right: 0,
214
+ bottom: 0,
215
+ zIndex: 1,
216
+ pointerEvents: "auto",
217
+ background: "rgba(0, 0, 0, 0.04)"
218
+ };
219
+ function R({ panelComponents: e }) {
220
+ const { state: n, dispatch: t, getCloseLog: r } = _(), o = n.activePanelId ? e[n.activePanelId] : null;
221
+ n.activePanelId && !o && process.env.NODE_ENV !== "production" && console.error(
222
+ "[panels] No panel component registered for activePanelId:",
223
+ n.activePanelId
224
+ ), a(() => {
225
+ if (n.activePanelId == null) return;
226
+ function u(C) {
227
+ C.key === "Escape" && t({ type: "CLOSE_PANEL", reason: "escape-key" });
228
+ }
229
+ return document.addEventListener("keydown", u), () => {
230
+ document.removeEventListener("keydown", u);
231
+ };
232
+ }, [n.activePanelId, t]);
233
+ const l = c(n);
234
+ P(() => {
235
+ l.current = n;
236
+ }), a(() => {
237
+ if (process.env.NODE_ENV !== "production")
238
+ return window.__panelsHostHandle = {
239
+ dispatch: t,
240
+ getState: () => l.current,
241
+ getCloseLog: r
242
+ }, () => {
243
+ delete window.__panelsHostHandle;
244
+ };
245
+ }, [t, r]);
246
+ const g = E(() => {
247
+ t({ type: "CLOSE_PANEL", reason: "nav-escape-hatch" });
248
+ }, [t]);
249
+ return /* @__PURE__ */ i("div", { "data-panel-host": "true", style: T, children: n.activePanelId != null && o != null && /* @__PURE__ */ d(v, { children: [
250
+ /* @__PURE__ */ i(
251
+ "div",
252
+ {
253
+ "data-panel-scrim": "true",
254
+ style: x,
255
+ onClick: g
256
+ }
257
+ ),
258
+ /* @__PURE__ */ i(
259
+ b,
260
+ {
261
+ isOpen: !0,
262
+ onClose: () => t({ type: "CLOSE_PANEL", reason: "header-x" }),
263
+ children: /* @__PURE__ */ i(o, { isOpen: !0 })
264
+ }
265
+ )
266
+ ] }) });
267
+ }
268
+ const k = {
269
+ "apply-button": !0,
270
+ // the ONLY commit reason
271
+ "cancel-button": !1,
272
+ "header-x": !1,
273
+ "escape-key": !1,
274
+ "page-nav": !1,
275
+ "mutex-handoff": !1,
276
+ "ghost-anchor": !1,
277
+ "viewport-collapse": !1,
278
+ unmount: !1,
279
+ "nav-escape-hatch": !1
280
+ };
281
+ export {
282
+ k as C,
283
+ F as P,
284
+ w as a,
285
+ R as b,
286
+ b as c,
287
+ D as d,
288
+ L as e,
289
+ y as i,
290
+ m as r,
291
+ _ as u
292
+ };