@mt-gloss/ui 0.1.81 → 0.1.83

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,219 @@
1
+ import { jsx as a, jsxs as u, Fragment as I } from "react/jsx-runtime";
2
+ import { createContext as h, useContext as p, useReducer as g, useRef as i, useEffect as r, useMemo as f, useCallback as v, useLayoutEffect as C } from "react";
3
+ const c = h(null);
4
+ function m() {
5
+ const e = p(c);
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 O() {
13
+ return p(c);
14
+ }
15
+ const S = {
16
+ activePanelId: null,
17
+ lastClose: null,
18
+ pendingSideEffects: []
19
+ };
20
+ function _(e, n) {
21
+ switch (n.type) {
22
+ case "OPEN_PANEL":
23
+ return n.panelId === "notification-center" && e.activePanelId === null ? {
24
+ activePanelId: "notification-center",
25
+ lastClose: null,
26
+ // reset lastClose on fresh open
27
+ pendingSideEffects: e.pendingSideEffects
28
+ // preserve any draining queue
29
+ } : n.panelId === "notification-center" && e.activePanelId === "notification-center" ? e : {
30
+ ...e,
31
+ pendingSideEffects: [
32
+ ...e.pendingSideEffects,
33
+ {
34
+ type: "INVALID_DISPATCH",
35
+ reason: `panel-not-supported-in-P1:${n.panelId}`
36
+ }
37
+ ]
38
+ };
39
+ case "CLOSE_PANEL":
40
+ return e.activePanelId === null ? e : {
41
+ activePanelId: null,
42
+ lastClose: { panelId: e.activePanelId, reason: n.reason },
43
+ pendingSideEffects: e.pendingSideEffects
44
+ };
45
+ case "DRAIN_SIDE_EFFECTS":
46
+ return e.pendingSideEffects.length === 0 ? e : {
47
+ ...e,
48
+ pendingSideEffects: []
49
+ };
50
+ default:
51
+ return e;
52
+ }
53
+ }
54
+ function y(e) {
55
+ return e.activePanelId === "notification-center" ? "notif-center" : (e.activePanelId != null, "idle");
56
+ }
57
+ function N() {
58
+ const [e, n] = g(_, S), t = i([]);
59
+ i(null), r(() => {
60
+ if (e.pendingSideEffects.length !== 0) {
61
+ for (const o of e.pendingSideEffects)
62
+ o.type === "SCHEDULE_TIMER" ? window.setTimeout(() => n(o.action), Math.max(0, o.ms)) : o.type === "INVALID_DISPATCH" && process.env.NODE_ENV !== "production" && console.warn("[panels] INVALID_DISPATCH:", o.reason);
63
+ n({ type: "DRAIN_SIDE_EFFECTS" });
64
+ }
65
+ }, [e.pendingSideEffects]), r(() => {
66
+ if (!e.lastClose) return;
67
+ const o = {
68
+ panelId: e.lastClose.panelId,
69
+ reason: e.lastClose.reason,
70
+ at: Date.now(),
71
+ note: e.lastClose.reason === "nav-escape-hatch" ? "F21: backdrop-click OR page-nav escape" : void 0
72
+ };
73
+ t.current = [
74
+ ...t.current.slice(-99),
75
+ o
76
+ ];
77
+ }, [e.lastClose]);
78
+ const l = f(
79
+ () => y(e),
80
+ // eslint-disable-next-line react-hooks/exhaustive-deps
81
+ [e.activePanelId]
82
+ ), s = v(() => t.current.slice(), []);
83
+ return { state: e, surfaceState: l, dispatch: n, getCloseLog: s };
84
+ }
85
+ function k({ children: e }) {
86
+ const n = N(), t = f(
87
+ () => n,
88
+ // Re-publish only when one of the four coordinator fields changes identity.
89
+ // dispatch + getCloseLog are stable. state + surfaceState change on dispatch.
90
+ [n.state, n.surfaceState, n.dispatch, n.getCloseLog]
91
+ );
92
+ return /* @__PURE__ */ a(c.Provider, { value: t, children: e });
93
+ }
94
+ const x = {
95
+ position: "relative",
96
+ pointerEvents: "auto"
97
+ };
98
+ function L({ children: e, onClose: n, isOpen: t = !0 }) {
99
+ return /* @__PURE__ */ u(
100
+ "div",
101
+ {
102
+ "data-chrome": "panel",
103
+ "data-open": t ? "true" : "false",
104
+ role: "dialog",
105
+ "aria-modal": "true",
106
+ className: "gloss-panel-chrome",
107
+ style: x,
108
+ children: [
109
+ /* @__PURE__ */ a("div", { className: "gloss-panel-chrome__header", children: /* @__PURE__ */ a(
110
+ "button",
111
+ {
112
+ id: "panel-close",
113
+ type: "button",
114
+ "aria-label": "Close panel",
115
+ className: "gloss-panel-chrome__close",
116
+ onClick: n,
117
+ children: "×"
118
+ }
119
+ ) }),
120
+ /* @__PURE__ */ a("div", { className: "gloss-panel-chrome__body", children: e })
121
+ ]
122
+ }
123
+ );
124
+ }
125
+ const b = {
126
+ position: "fixed",
127
+ top: 0,
128
+ left: 0,
129
+ right: 0,
130
+ height: "var(--tb-h, 60px)",
131
+ perspective: "1200px",
132
+ perspectiveOrigin: "center bottom",
133
+ overflow: "visible",
134
+ pointerEvents: "none",
135
+ zIndex: 55
136
+ }, w = {
137
+ position: "fixed",
138
+ top: "var(--tb-h, 60px)",
139
+ left: 0,
140
+ right: 0,
141
+ bottom: 0,
142
+ zIndex: 1,
143
+ pointerEvents: "auto",
144
+ background: "rgba(0, 0, 0, 0.04)"
145
+ };
146
+ function H({ panelComponents: e }) {
147
+ const { state: n, dispatch: t, getCloseLog: l } = m(), s = n.activePanelId ? e[n.activePanelId] : null;
148
+ n.activePanelId && !s && process.env.NODE_ENV !== "production" && console.error(
149
+ "[panels] No panel component registered for activePanelId:",
150
+ n.activePanelId
151
+ ), r(() => {
152
+ if (n.activePanelId == null) return;
153
+ function d(P) {
154
+ P.key === "Escape" && t({ type: "CLOSE_PANEL", reason: "escape-key" });
155
+ }
156
+ return document.addEventListener("keydown", d), () => {
157
+ document.removeEventListener("keydown", d);
158
+ };
159
+ }, [n.activePanelId, t]);
160
+ const o = i(n);
161
+ C(() => {
162
+ o.current = n;
163
+ }), r(() => {
164
+ if (process.env.NODE_ENV !== "production")
165
+ return window.__panelsHostHandle = {
166
+ dispatch: t,
167
+ getState: () => o.current,
168
+ getCloseLog: l
169
+ }, () => {
170
+ delete window.__panelsHostHandle;
171
+ };
172
+ }, [t, l]);
173
+ const E = v(() => {
174
+ t({ type: "CLOSE_PANEL", reason: "nav-escape-hatch" });
175
+ }, [t]);
176
+ return /* @__PURE__ */ a("div", { "data-panel-host": "true", style: b, children: n.activePanelId != null && s != null && /* @__PURE__ */ u(I, { children: [
177
+ /* @__PURE__ */ a(
178
+ "div",
179
+ {
180
+ "data-panel-scrim": "true",
181
+ style: w,
182
+ onClick: E
183
+ }
184
+ ),
185
+ /* @__PURE__ */ a(
186
+ L,
187
+ {
188
+ isOpen: !0,
189
+ onClose: () => t({ type: "CLOSE_PANEL", reason: "header-x" }),
190
+ children: /* @__PURE__ */ a(s, { isOpen: !0 })
191
+ }
192
+ )
193
+ ] }) });
194
+ }
195
+ const R = {
196
+ "apply-button": !0,
197
+ // the ONLY commit reason
198
+ "cancel-button": !1,
199
+ "header-x": !1,
200
+ "escape-key": !1,
201
+ "page-nav": !1,
202
+ "mutex-handoff": !1,
203
+ "ghost-anchor": !1,
204
+ "viewport-collapse": !1,
205
+ unmount: !1,
206
+ "nav-escape-hatch": !1
207
+ };
208
+ export {
209
+ R as C,
210
+ k as P,
211
+ O as a,
212
+ H as b,
213
+ L as c,
214
+ N as d,
215
+ y as e,
216
+ S as i,
217
+ _ as r,
218
+ m as u
219
+ };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Composites/panels entry point — flat-root mirror for the subpath export
3
+ * '@mt-gloss/ui/composites/panels'.
4
+ *
5
+ * Mirrors the catalog.ts / internals.ts shape so that vite emits
6
+ * `dist/libs/gloss-ui/composites-panels.{js,d.ts}` at the package root,
7
+ * matching the package.json exports map (F12).
8
+ *
9
+ * Usage:
10
+ * import { PanelProvider, PanelHost } from '@mt-gloss/ui/composites/panels';
11
+ */
12
+ export * from './lib/composites/panels/index';
@@ -0,0 +1,82 @@
1
+ import { u as c, a as u, c as d } from "./COMMITS-or4m2o8k.js";
2
+ import { C as L, b as O, P as _, e as k, i as N, r as w, d as g } from "./COMMITS-or4m2o8k.js";
3
+ import { jsx as r } from "react/jsx-runtime";
4
+ import { useEffect as l, useRef as p, useLayoutEffect as f, useCallback as P } from "react";
5
+ const v = {
6
+ position: "fixed",
7
+ inset: 0,
8
+ pointerEvents: "none",
9
+ zIndex: 55
10
+ }, E = {
11
+ position: "absolute",
12
+ inset: 0,
13
+ zIndex: 1,
14
+ pointerEvents: "auto",
15
+ background: "rgba(0, 0, 0, 0.04)"
16
+ };
17
+ function S() {
18
+ const { state: n, dispatch: e, getCloseLog: t } = c();
19
+ l(() => {
20
+ if (n.activePanelId == null) return;
21
+ function s(i) {
22
+ i.key === "Escape" && e({ type: "CLOSE_PANEL", reason: "escape-key" });
23
+ }
24
+ return document.addEventListener("keydown", s), () => document.removeEventListener("keydown", s);
25
+ }, [n.activePanelId, e]);
26
+ const o = p(n);
27
+ f(() => {
28
+ o.current = n;
29
+ }), l(() => {
30
+ if (process.env.NODE_ENV !== "production")
31
+ return window.__panelsHostHandle = {
32
+ dispatch: e,
33
+ getState: () => o.current,
34
+ getCloseLog: t
35
+ }, () => {
36
+ delete window.__panelsHostHandle;
37
+ };
38
+ }, [e, t]);
39
+ const a = P(() => {
40
+ e({ type: "CLOSE_PANEL", reason: "nav-escape-hatch" });
41
+ }, [e]);
42
+ return /* @__PURE__ */ r("div", { "data-panel-host": "true", style: v, children: n.activePanelId != null && /* @__PURE__ */ r(
43
+ "div",
44
+ {
45
+ "data-panel-scrim": "true",
46
+ style: E,
47
+ onClick: a
48
+ }
49
+ ) });
50
+ }
51
+ function h({ panelComponents: n }) {
52
+ const e = u();
53
+ if (e == null) return null;
54
+ const { state: t, dispatch: o } = e;
55
+ if (t.activePanelId == null) return null;
56
+ const a = n[t.activePanelId];
57
+ return a == null ? (process.env.NODE_ENV !== "production" && console.error(
58
+ "[panels] No panel component registered for activePanelId:",
59
+ t.activePanelId
60
+ ), null) : /* @__PURE__ */ r(
61
+ d,
62
+ {
63
+ isOpen: !0,
64
+ onClose: () => o({ type: "CLOSE_PANEL", reason: "header-x" }),
65
+ children: /* @__PURE__ */ r(a, { isOpen: !0 })
66
+ }
67
+ );
68
+ }
69
+ export {
70
+ L as COMMITS,
71
+ d as PanelChrome,
72
+ O as PanelHost,
73
+ S as PanelHostShell,
74
+ _ as PanelProvider,
75
+ h as PanelSlot,
76
+ k as deriveSurfaceState,
77
+ N as initialState,
78
+ w as reducer,
79
+ c as usePanelContext,
80
+ u as usePanelContextOptional,
81
+ g as usePanelCoordinator
82
+ };
package/index.d.ts CHANGED
@@ -366,3 +366,5 @@ export { UIContext, UIContextProvider, useUIContext, ContainerCard, ContainerCar
366
366
  export type { UIContextValue, UIRegion, UIDensity, UIContextProviderProps, ContainerCardProps, ContainerCardVariant, ContainerDialogProps, ContainerDialogHeaderProps, ContainerDialogBodyProps, ContainerDialogActionsProps, ContainerPanelProps, ContainerToolbarProps, ContainerFormSectionProps, ContainerTableProps, } from './lib/containers';
367
367
  export { mergeRefs, mergeHandlers, Actionable, Selectable, Dismissible, Sortable, Expandable, } from './lib/wrappers';
368
368
  export type { ActionableProps, SelectableProps, DismissibleProps, SortableProps, ExpandableProps, } from './lib/wrappers';
369
+ export { PanelProvider, usePanelContext, usePanelContextOptional, PanelHost, PanelChrome, usePanelCoordinator, reducer as panelsReducer, initialState as panelsInitialState, deriveSurfaceState as panelsDeriveSurfaceState, COMMITS as PANEL_COMMITS, } from './lib/composites/panels';
370
+ export type { PanelProviderProps, PanelHostProps, PanelChromeProps, PanelShellProps, UsePanelCoordinatorReturn, PanelId, PanelState, PanelAction, CloseReason, SurfaceState, TriggerContext, SideEffect, CloseLogEntry, LockHint, } from './lib/composites/panels';
package/index.js CHANGED
@@ -6,8 +6,9 @@ import F, { useRef as P, useEffect as A, useState as E, useCallback as C, useId
6
6
  import { motion as G, useMotionValue as tt, AnimatePresence as at, useDragControls as nt } from "framer-motion";
7
7
  import { B as rt, a as X, b as ce, U as fe } from "./UIContext-DP6JGCto.js";
8
8
  import { c as Wo, u as Vo } from "./UIContext-DP6JGCto.js";
9
+ import { C as Uo, c as Yo, b as Zo, P as qo, e as Qo, i as Xo, r as Jo, u as el, a as tl, d as al } from "./COMMITS-or4m2o8k.js";
9
10
  import { createPortal as st } from "react-dom";
10
- import { DEFAULT_MARKET_PRESETS as Uo, DEFAULT_PINNED as Yo, PERIOD_OPTIONS as Zo, ROLLING_OPTIONS as qo, TODAY_PRESET as Qo, findPreset as Xo, formatDateRange as Jo, formatMarketDates as el, formatTrend as tl, formatValue as al, generateMarketId as nl, getAllPresets as rl, getDateRangeForPreset as sl, getPresetLabel as il, isInRange as ol, isMarketActive as ll, isSameDay as cl, normalizePresetId as dl, toUppercasePresetId as ul } from "@mt-gloss/utils";
11
+ import { DEFAULT_MARKET_PRESETS as rl, DEFAULT_PINNED as sl, PERIOD_OPTIONS as il, ROLLING_OPTIONS as ol, TODAY_PRESET as ll, findPreset as cl, formatDateRange as dl, formatMarketDates as ul, formatTrend as ml, formatValue as gl, generateMarketId as pl, getAllPresets as hl, getDateRangeForPreset as fl, getPresetLabel as bl, isInRange as vl, isMarketActive as yl, isSameDay as wl, normalizePresetId as _l, toUppercasePresetId as Cl } from "@mt-gloss/utils";
11
12
  import it from "@mui/icons-material/AccessTime";
12
13
  import ot from "@mui/icons-material/Tune";
13
14
  import lt from "@mui/icons-material/ViewColumn";
@@ -4686,9 +4687,9 @@ export {
4686
4687
  hs as CreditCardForm,
4687
4688
  fs as DEFAULT_FOOTER_IDS,
4688
4689
  U as DEFAULT_GRID_INTERACTION_PHYSICS,
4689
- Uo as DEFAULT_MARKET_PRESETS,
4690
+ rl as DEFAULT_MARKET_PRESETS,
4690
4691
  bs as DEFAULT_OVERFLOW_IDS,
4691
- Yo as DEFAULT_PINNED,
4692
+ sl as DEFAULT_PINNED,
4692
4693
  _t as DEFAULT_RESIZE_PHYSICS,
4693
4694
  Ie as DEFAULT_RESIZE_PILL_PHYSICS,
4694
4695
  vs as DataTable,
@@ -4760,11 +4761,15 @@ export {
4760
4761
  mi as NumberInput,
4761
4762
  gi as ObjectArrow,
4762
4763
  pi as OrderFilterBar,
4763
- Zo as PERIOD_OPTIONS,
4764
+ Uo as PANEL_COMMITS,
4765
+ il as PERIOD_OPTIONS,
4764
4766
  or as PaceCard,
4765
4767
  lr as PaceTilePreview,
4766
4768
  Et as PageChip,
4767
4769
  Oe as PageDropdown,
4770
+ Yo as PanelChrome,
4771
+ Zo as PanelHost,
4772
+ qo as PanelProvider,
4768
4773
  hi as PasswordInput,
4769
4774
  fi as PaymentForm,
4770
4775
  bi as PaymentMethodSelect,
@@ -4775,7 +4780,7 @@ export {
4775
4780
  _i as ProgressCard,
4776
4781
  Ci as QuickConfigPanel,
4777
4782
  Bt as RECONFIG_BACKSIDE_LABELS,
4778
- qo as ROLLING_OPTIONS,
4783
+ ol as ROLLING_OPTIONS,
4779
4784
  Ni as Radio,
4780
4785
  Si as RadioGroup,
4781
4786
  ki as RangeSlider,
@@ -4824,7 +4829,7 @@ export {
4824
4829
  yt as StatusSlotChipMenu,
4825
4830
  Ui as Stepper,
4826
4831
  De as SwingReveal,
4827
- Qo as TODAY_PRESET,
4832
+ ll as TODAY_PRESET,
4828
4833
  V as TUNING_DEFAULTS,
4829
4834
  Be as TUNING_STORAGE_KEY,
4830
4835
  Yi as TabGroup,
@@ -4879,26 +4884,26 @@ export {
4879
4884
  Tt as errorFixture,
4880
4885
  yo as evaluateTintCondition,
4881
4886
  Rn as filteredState,
4882
- Xo as findPreset,
4883
- Jo as formatDateRange,
4884
- el as formatMarketDates,
4885
- tl as formatTrend,
4886
- al as formatValue,
4887
+ cl as findPreset,
4888
+ dl as formatDateRange,
4889
+ ul as formatMarketDates,
4890
+ ml as formatTrend,
4891
+ gl as formatValue,
4887
4892
  Hn as fullLayout,
4888
- nl as generateMarketId,
4893
+ pl as generateMarketId,
4889
4894
  wo as generateTabId,
4890
- rl as getAllPresets,
4891
- sl as getDateRangeForPreset,
4895
+ hl as getAllPresets,
4896
+ fl as getDateRangeForPreset,
4892
4897
  _o as getMenuActions,
4893
4898
  Ke as getMotionMode,
4894
- il as getPresetLabel,
4899
+ bl as getPresetLabel,
4895
4900
  Co as getTransitionStyle,
4896
4901
  Bn as gridCards,
4897
4902
  cr as gridChannels,
4898
4903
  No as interpolate,
4899
- ol as isInRange,
4900
- ll as isMarketActive,
4901
- cl as isSameDay,
4904
+ vl as isInRange,
4905
+ yl as isMarketActive,
4906
+ wl as isSameDay,
4902
4907
  Tn as itemColumns,
4903
4908
  Dn as itemRows,
4904
4909
  xt as loadingFixture,
@@ -4910,7 +4915,7 @@ export {
4910
4915
  xo as mergeRefs,
4911
4916
  sn as metricTabGroup,
4912
4917
  Vn as mixedTabs,
4913
- dl as normalizePresetId,
4918
+ _l as normalizePresetId,
4914
4919
  zt as numericActions,
4915
4920
  xn as orderColumns,
4916
4921
  $t as orderIdActions,
@@ -4918,6 +4923,9 @@ export {
4918
4923
  Wn as ordersTabs,
4919
4924
  Me as paletteHex,
4920
4925
  j as paletteVars,
4926
+ Qo as panelsDeriveSurfaceState,
4927
+ Xo as panelsInitialState,
4928
+ Jo as panelsReducer,
4921
4929
  To as parseNumericValue,
4922
4930
  Ro as paymentFormMetadata,
4923
4931
  Mo as paymentSchema,
@@ -4950,7 +4958,7 @@ export {
4950
4958
  gn as tableSamples,
4951
4959
  dn as textSamples,
4952
4960
  ln as timeFrameTabGroup,
4953
- ul as toUppercasePresetId,
4961
+ Cl as toUppercasePresetId,
4954
4962
  Ze as useAffordanceVisibility,
4955
4963
  nn as useArrowNavigation,
4956
4964
  Ao as useCheckboxGroupContext,
@@ -4962,6 +4970,9 @@ export {
4962
4970
  zo as useFocusTrap,
4963
4971
  Bo as useFormContext,
4964
4972
  Ko as useGestureState,
4973
+ el as usePanelContext,
4974
+ tl as usePanelContextOptional,
4975
+ al as usePanelCoordinator,
4965
4976
  Go as useRadioGroupContext,
4966
4977
  yr as useResizeChoreography,
4967
4978
  fn as useResizePill,
@@ -0,0 +1,7 @@
1
+ import { ReactNode } from 'react';
2
+ export interface PanelChromeProps {
3
+ children: ReactNode;
4
+ onClose: () => void;
5
+ isOpen?: boolean;
6
+ }
7
+ export declare function PanelChrome({ children, onClose, isOpen }: PanelChromeProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,19 @@
1
+ import { ComponentType } from 'react';
2
+ import { PanelId } from './coordinator/types';
3
+ /**
4
+ * Shape contract for panel shell components (F10).
5
+ * Receives `isOpen` so future plans may always-mount panels and rely on this
6
+ * prop for animation gating; P1 mounts only the active panel.
7
+ */
8
+ export interface PanelShellProps {
9
+ isOpen: boolean;
10
+ }
11
+ export interface PanelHostProps {
12
+ /**
13
+ * Map of PanelId → shell component. Only the active panel mounts in P1.
14
+ * F8 — callers SHOULD declare this at module scope as a `const` for
15
+ * referential stability.
16
+ */
17
+ panelComponents: Partial<Record<PanelId, ComponentType<PanelShellProps>>>;
18
+ }
19
+ export declare function PanelHost({ panelComponents }: PanelHostProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ export declare function PanelHostShell(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { ReactNode } from 'react';
2
+ export interface PanelProviderProps {
3
+ children: ReactNode;
4
+ }
5
+ export declare function PanelProvider({ children }: PanelProviderProps): import("react/jsx-runtime").JSX.Element;
6
+ export { usePanelContext, usePanelContextOptional } from './hooks/usePanelContext';
@@ -0,0 +1,7 @@
1
+ import { ComponentType } from 'react';
2
+ import { PanelId } from './coordinator/types';
3
+ import { PanelShellProps } from './PanelHost';
4
+ export interface PanelSlotProps {
5
+ panelComponents: Partial<Record<PanelId, ComponentType<PanelShellProps>>>;
6
+ }
7
+ export declare function PanelSlot({ panelComponents }: PanelSlotProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,2 @@
1
+ import { CloseReason } from './types';
2
+ export declare const COMMITS: Record<CloseReason, boolean>;
@@ -0,0 +1,3 @@
1
+ import { PanelAction, PanelState } from './types';
2
+ export declare const initialState: PanelState;
3
+ export declare function reducer(state: PanelState, action: PanelAction): PanelState;
@@ -0,0 +1,2 @@
1
+ import { PanelState, SurfaceState } from './types';
2
+ export declare function deriveSurfaceState(state: PanelState): SurfaceState;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Panel system — type contract.
3
+ *
4
+ * Authority: `.planning/spikes/toolbar/panels/SPIKE-DELIVERABLES.md` §1.2 (PanelId),
5
+ * §1.3 (SurfaceState), §1.10/§1.15 (CloseReason taxonomy), §1.17 (TriggerContext).
6
+ *
7
+ * P1 reach: reducer only OPENs/CLOSEs `'notification-center'`; other PanelIds are
8
+ * present in the union but emit INVALID_DISPATCH per F15 branch 3. Surface-state
9
+ * derivation in P1 reaches `'idle'` + `'notif-center'` only; other branches return
10
+ * `'idle'` with a TODO until P2 extends the reducer to full 7-state.
11
+ *
12
+ * Plan refs:
13
+ * F1 — exhaustive types ship Day 1
14
+ * F4 — `lastClose` carries `{ panelId, reason }` so closeLog pump can append
15
+ * F6 — `SCHEDULE_TIMER` is a SideEffect, NOT a PanelAction (external callers
16
+ * cannot dispatch it). Only `OPEN_PANEL | CLOSE_PANEL | DRAIN_SIDE_EFFECTS`
17
+ * are dispatchable.
18
+ * F9 + F21 — `NAV_ESCAPE_HATCH` carries dual P1 meaning: backdrop-click AND
19
+ * page-nav escape; spike enum has no `BACKDROP_CLICK` literal.
20
+ */
21
+ export type PanelId = 'settings' | 'catalog-add' | 'catalog-replace' | 'page-management' | 'notification-center';
22
+ export type CloseReason = 'cancel-button' | 'apply-button' | 'header-x' | 'escape-key' | 'page-nav' | 'mutex-handoff' | 'ghost-anchor' | 'viewport-collapse' | 'unmount' | 'nav-escape-hatch';
23
+ export type SurfaceState = 'idle' | 'bell-cutout-only' | 'panel-clean' | 'panel-dirty' | 'panel-clean+cutout' | 'panel-dirty+cutout' | 'notif-center';
24
+ export type TriggerContext = {
25
+ kind: 'bell';
26
+ } | {
27
+ kind: 'card-overflow';
28
+ cardId: string;
29
+ sectionIndex: number;
30
+ action: 'configure' | 'replace';
31
+ } | {
32
+ kind: 'empty-cell';
33
+ sectionIndex: number;
34
+ cellPosition: {
35
+ row: number;
36
+ col: number;
37
+ };
38
+ } | {
39
+ kind: 'manage-pages-action';
40
+ global: true;
41
+ align: 'left';
42
+ };
43
+ export interface CloseLogEntry {
44
+ panelId: PanelId;
45
+ reason: CloseReason;
46
+ at: number;
47
+ /**
48
+ * F21 — When `reason === 'nav-escape-hatch'`, the same enum literal fires for
49
+ * both backdrop-click AND page-nav escape. Future analytics MUST inspect this
50
+ * note before attributing UX intent.
51
+ */
52
+ note?: string;
53
+ }
54
+ export type SideEffect = {
55
+ type: 'SCHEDULE_TIMER';
56
+ ms: number;
57
+ action: PanelAction;
58
+ } | {
59
+ type: 'INVALID_DISPATCH';
60
+ reason: string;
61
+ };
62
+ export interface PanelState {
63
+ activePanelId: PanelId | null;
64
+ lastClose: {
65
+ panelId: PanelId;
66
+ reason: CloseReason;
67
+ } | null;
68
+ pendingSideEffects: SideEffect[];
69
+ }
70
+ export type PanelAction = {
71
+ type: 'OPEN_PANEL';
72
+ panelId: PanelId;
73
+ trigger: TriggerContext;
74
+ } | {
75
+ type: 'CLOSE_PANEL';
76
+ reason: CloseReason;
77
+ } | {
78
+ type: 'DRAIN_SIDE_EFFECTS';
79
+ };
80
+ export interface LockHint {
81
+ blockedTrigger: TriggerContext;
82
+ activeCardId: string | null;
83
+ expiresAt: number;
84
+ }
@@ -0,0 +1,4 @@
1
+ import { UsePanelCoordinatorReturn } from './usePanelCoordinator';
2
+ export declare const PanelContext: import('react').Context<UsePanelCoordinatorReturn | null>;
3
+ export declare function usePanelContext(): UsePanelCoordinatorReturn;
4
+ export declare function usePanelContextOptional(): UsePanelCoordinatorReturn | null;
@@ -0,0 +1,8 @@
1
+ import { CloseLogEntry, PanelAction, PanelState, SurfaceState } from '../coordinator/types';
2
+ export interface UsePanelCoordinatorReturn {
3
+ state: PanelState;
4
+ surfaceState: SurfaceState;
5
+ dispatch: React.Dispatch<PanelAction>;
6
+ getCloseLog: () => CloseLogEntry[];
7
+ }
8
+ export declare function usePanelCoordinator(): UsePanelCoordinatorReturn;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @mt-gloss/ui/composites/panels — panel-system foundation.
3
+ *
4
+ * Consumers (reptime-cloud P1) import:
5
+ * import { PanelProvider, PanelHost } from '@mt-gloss/ui/composites/panels';
6
+ *
7
+ * Subpath export wired in package.json (F12); flat package-root artifact
8
+ * `composites-panels.js` matching existing `internals` + `catalog` pattern.
9
+ */
10
+ export { PanelProvider, usePanelContext, usePanelContextOptional } from './PanelProvider';
11
+ export { PanelHost } from './PanelHost';
12
+ export { PanelHostShell } from './PanelHostShell';
13
+ export { PanelSlot } from './PanelSlot';
14
+ export { PanelChrome } from './PanelChrome';
15
+ export type { PanelHostProps, PanelShellProps } from './PanelHost';
16
+ export type { PanelSlotProps } from './PanelSlot';
17
+ export type { PanelProviderProps } from './PanelProvider';
18
+ export type { PanelChromeProps } from './PanelChrome';
19
+ export { usePanelCoordinator } from './hooks/usePanelCoordinator';
20
+ export type { UsePanelCoordinatorReturn } from './hooks/usePanelCoordinator';
21
+ export { reducer, initialState } from './coordinator/coordinator';
22
+ export { deriveSurfaceState } from './coordinator/deriveSurfaceState';
23
+ export { COMMITS } from './coordinator/COMMITS';
24
+ export type { PanelId, PanelState, PanelAction, CloseReason, SurfaceState, TriggerContext, SideEffect, CloseLogEntry, LockHint, } from './coordinator/types';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mt-gloss/ui",
3
- "version": "0.1.81",
3
+ "version": "0.1.83",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
@@ -38,6 +38,17 @@
38
38
  },
39
39
  "default": "./catalog.js"
40
40
  },
41
+ "./composites/panels": {
42
+ "import": {
43
+ "types": "./composites-panels.d.ts",
44
+ "default": "./composites-panels.js"
45
+ },
46
+ "require": {
47
+ "types": "./composites-panels.d.ts",
48
+ "default": "./composites-panels.js"
49
+ },
50
+ "default": "./composites-panels.js"
51
+ },
41
52
  "./styles.css": "./ui.css"
42
53
  },
43
54
  "files": [