@lattice-php/lattice 0.56.3 → 0.57.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 (56) hide show
  1. package/dist/fragments/fragment.js +1 -1
  2. package/dist/fragments/fragment.js.map +1 -1
  3. package/dist/index.d.ts +3 -3
  4. package/dist/index.js +3 -3
  5. package/dist/inertia-navigation.d.ts +10 -0
  6. package/dist/inertia-navigation.js +18 -1
  7. package/dist/inertia-navigation.js.map +1 -1
  8. package/dist/layout/components/menu-item.js +3 -3
  9. package/dist/layout/components/menu-item.js.map +1 -1
  10. package/dist/layout/components/outlet.js +3 -4
  11. package/dist/layout/components/outlet.js.map +1 -1
  12. package/dist/layout/components/popover.js +4 -4
  13. package/dist/layout/components/popover.js.map +1 -1
  14. package/dist/layout/components/schema-layout.js +3 -3
  15. package/dist/layout/components/schema-layout.js.map +1 -1
  16. package/dist/layout/components/sidebar.js +4 -4
  17. package/dist/layout/components/sidebar.js.map +1 -1
  18. package/dist/layout/hooks/context.d.ts +5 -3
  19. package/dist/layout/hooks/context.js +13 -4
  20. package/dist/layout/hooks/context.js.map +1 -1
  21. package/dist/layout/index.d.ts +1 -1
  22. package/dist/layout/index.js +2 -2
  23. package/dist/notifications/components/notification-list.js +1 -1
  24. package/dist/notifications/components/notification-list.js.map +1 -1
  25. package/dist/notifications/components/notifications-sheet.d.ts +3 -0
  26. package/dist/notifications/components/notifications-sheet.js +59 -0
  27. package/dist/notifications/components/notifications-sheet.js.map +1 -0
  28. package/dist/notifications/components/notifications.js +23 -19
  29. package/dist/notifications/components/notifications.js.map +1 -1
  30. package/dist/notifications/context.d.ts +17 -0
  31. package/dist/notifications/context.js +36 -0
  32. package/dist/notifications/context.js.map +1 -0
  33. package/dist/notifications/store.d.ts +1 -1
  34. package/dist/notifications/store.js.map +1 -1
  35. package/dist/provider-base.js +9 -7
  36. package/dist/provider-base.js.map +1 -1
  37. package/dist/runtime.d.ts +7 -3
  38. package/dist/runtime.js +8 -5
  39. package/dist/types/generated.d.ts +1 -0
  40. package/dist-standalone/chunks/{chart-view-QdsrTt3D.js → chart-view-eWW-pC-B.js} +6 -6
  41. package/dist-standalone/chunks/{date-picker-field-D_QaDI9c.js → date-picker-field-FY2OOgtu.js} +1 -1
  42. package/dist-standalone/chunks/{field-DE3_bVGA.js → field-DMXnzaJF.js} +1 -1
  43. package/dist-standalone/chunks/floating-ui.dom-CAMzcZKV.js +1 -0
  44. package/dist-standalone/chunks/{input-C-Wm1w0g.js → input-0lNoXsuO.js} +1 -1
  45. package/dist-standalone/chunks/registry-context-gXwNT8GJ.js +1 -0
  46. package/dist-standalone/chunks/{rich-editor-field-B8gIiZoQ.js → rich-editor-field-B0XwZGpp.js} +4 -4
  47. package/dist-standalone/chunks/runtime-BjRIrbqh.js +162 -0
  48. package/dist-standalone/chunks/{subscriptions-C_vxf4VT.js → subscriptions-CSh5RtT7.js} +1 -1
  49. package/dist-standalone/lattice.css +1 -1
  50. package/dist-standalone/lattice.js +1 -1
  51. package/dist-standalone/manifest.json +1 -1
  52. package/dist-standalone/runtime.js +1 -1
  53. package/package.json +6 -6
  54. package/dist-standalone/chunks/floating-ui.dom-DRSJZ5ii.js +0 -1
  55. package/dist-standalone/chunks/registry-context-Q5KtSmdi.js +0 -1
  56. package/dist-standalone/chunks/runtime-uNS3QbQU.js +0 -162
@@ -3,7 +3,7 @@ import { apiJson } from "@lattice-php/core/api";
3
3
  import { Renderer } from "@lattice-php/core/renderer";
4
4
  import { LATTICE_EVENT } from "@lattice-php/core/event-names";
5
5
  import { jsx } from "react/jsx-runtime";
6
- import { Skeleton } from "@lattice-php/ui/skeleton";
6
+ import { Skeleton } from "@lattice-php/ui/primitives/skeleton";
7
7
  import { toNodes } from "@lattice-php/core/nodes";
8
8
  //#region resources/js/fragments/fragment.tsx
9
9
  var fragmentSizeHeights = {
@@ -1 +1 @@
1
- {"version":3,"file":"fragment.js","names":[],"sources":["../../resources/js/fragments/fragment.tsx"],"sourcesContent":["import { useCallback, useEffect, useState } from \"react\";\nimport { apiJson } from \"@lattice-php/core/api\";\nimport { Skeleton } from \"@lattice-php/ui/skeleton\";\nimport { Renderer } from \"@lattice-php/core/renderer\";\nimport type { RendererComponent } from \"@lattice-php/core/types\";\nimport type { FragmentResponse } from \"@lattice-php/lattice/types/generated\";\nimport { toNodes } from \"@lattice-php/core/nodes\";\nimport { LATTICE_EVENT, type ReloadComponentEvent } from \"@lattice-php/core/event-names\";\n\nconst fragmentSizeHeights = {\n lg: 320,\n md: 64,\n sm: 40,\n xl: 384,\n xs: 32,\n \"2xl\": 448,\n \"3xl\": 512,\n \"4xl\": 576,\n} as const;\n\nconst FragmentComponent: RendererComponent<\"fragment\"> = ({ node }) => {\n const endpoint = node.props.endpoint ?? \"\";\n const isLazy = node.props.lazy === true;\n const componentRef = node.props.ref ?? \"\";\n const placeholderHeight = fragmentSizeHeights[node.props.size];\n const [components, setComponents] = useState(() => node.schema ?? []);\n const [hasLoaded, setHasLoaded] = useState(!isLazy);\n const [processing, setProcessing] = useState(isLazy && endpoint !== \"\");\n\n const load = useCallback(async (): Promise<void> => {\n if (!endpoint) {\n return;\n }\n\n setProcessing(true);\n\n try {\n const result = await apiJson<FragmentResponse>(endpoint, { ref: componentRef });\n\n setComponents(toNodes(result.schema));\n setHasLoaded(true);\n } finally {\n setProcessing(false);\n }\n }, [endpoint, componentRef]);\n\n useEffect(() => {\n if (!isLazy || hasLoaded) {\n return;\n }\n\n void load();\n }, [hasLoaded, isLazy, load]);\n\n useEffect(() => {\n function reload(event: Event): void {\n const detail = (event as ReloadComponentEvent).detail;\n\n if (detail?.component !== node.id) {\n return;\n }\n\n void load();\n }\n\n window.addEventListener(LATTICE_EVENT.reloadComponent, reload);\n\n return () => window.removeEventListener(LATTICE_EVENT.reloadComponent, reload);\n }, [load, node.id]);\n\n useEffect(() => {\n function reloadOnLocaleChange(): void {\n if (!hasLoaded) {\n return;\n }\n\n void load();\n }\n\n window.addEventListener(LATTICE_EVENT.localeChange, reloadOnLocaleChange);\n\n return () => window.removeEventListener(LATTICE_EVENT.localeChange, reloadOnLocaleChange);\n }, [hasLoaded, load]);\n\n return (\n <div\n data-lattice-fragment={node.id}\n style={processing && components.length === 0 ? { minHeight: placeholderHeight } : undefined}\n >\n {processing && components.length === 0 ? (\n <Skeleton className=\"w-full\" style={{ height: placeholderHeight }} />\n ) : (\n <Renderer nodes={components} />\n )}\n </div>\n );\n};\n\nexport default FragmentComponent;\n"],"mappings":";;;;;;;;AASA,IAAM,sBAAsB;CAC1B,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,OAAO;CACP,OAAO;CACP,OAAO;AACT;AAEA,IAAM,qBAAoD,EAAE,WAAW;CACrE,MAAM,WAAW,KAAK,MAAM,YAAY;CACxC,MAAM,SAAS,KAAK,MAAM,SAAS;CACnC,MAAM,eAAe,KAAK,MAAM,OAAO;CACvC,MAAM,oBAAoB,oBAAoB,KAAK,MAAM;CACzD,MAAM,CAAC,YAAY,iBAAiB,eAAe,KAAK,UAAU,CAAC,CAAC;CACpE,MAAM,CAAC,WAAW,gBAAgB,SAAS,CAAC,MAAM;CAClD,MAAM,CAAC,YAAY,iBAAiB,SAAS,UAAU,aAAa,EAAE;CAEtE,MAAM,OAAO,YAAY,YAA2B;EAClD,IAAI,CAAC,UACH;EAGF,cAAc,IAAI;EAElB,IAAI;GACF,MAAM,SAAS,MAAM,QAA0B,UAAU,EAAE,KAAK,aAAa,CAAC;GAE9E,cAAc,QAAQ,OAAO,MAAM,CAAC;GACpC,aAAa,IAAI;EACnB,UAAU;GACR,cAAc,KAAK;EACrB;CACF,GAAG,CAAC,UAAU,YAAY,CAAC;CAE3B,gBAAgB;EACd,IAAI,CAAC,UAAU,WACb;EAGF,KAAU;CACZ,GAAG;EAAC;EAAW;EAAQ;CAAI,CAAC;CAE5B,gBAAgB;EACd,SAAS,OAAO,OAAoB;GAGlC,IAFgB,MAA+B,QAEnC,cAAc,KAAK,IAC7B;GAGF,KAAU;EACZ;EAEA,OAAO,iBAAiB,cAAc,iBAAiB,MAAM;EAE7D,aAAa,OAAO,oBAAoB,cAAc,iBAAiB,MAAM;CAC/E,GAAG,CAAC,MAAM,KAAK,EAAE,CAAC;CAElB,gBAAgB;EACd,SAAS,uBAA6B;GACpC,IAAI,CAAC,WACH;GAGF,KAAU;EACZ;EAEA,OAAO,iBAAiB,cAAc,cAAc,oBAAoB;EAExE,aAAa,OAAO,oBAAoB,cAAc,cAAc,oBAAoB;CAC1F,GAAG,CAAC,WAAW,IAAI,CAAC;CAEpB,OACE,oBAAC,OAAD;EACE,yBAAuB,KAAK;EAC5B,OAAO,cAAc,WAAW,WAAW,IAAI,EAAE,WAAW,kBAAkB,IAAI,KAAA;EAEjF,UAAA,cAAc,WAAW,WAAW,IACnC,oBAAC,UAAD;GAAU,WAAU;GAAS,OAAO,EAAE,QAAQ,kBAAkB;EAAI,CAAA,IAEpE,oBAAC,UAAD,EAAU,OAAO,WAAa,CAAA;CAE7B,CAAA;AAET"}
1
+ {"version":3,"file":"fragment.js","names":[],"sources":["../../resources/js/fragments/fragment.tsx"],"sourcesContent":["import { useCallback, useEffect, useState } from \"react\";\nimport { apiJson } from \"@lattice-php/core/api\";\nimport { Skeleton } from \"@lattice-php/ui/primitives/skeleton\";\nimport { Renderer } from \"@lattice-php/core/renderer\";\nimport type { RendererComponent } from \"@lattice-php/core/types\";\nimport type { FragmentResponse } from \"@lattice-php/lattice/types/generated\";\nimport { toNodes } from \"@lattice-php/core/nodes\";\nimport { LATTICE_EVENT, type ReloadComponentEvent } from \"@lattice-php/core/event-names\";\n\nconst fragmentSizeHeights = {\n lg: 320,\n md: 64,\n sm: 40,\n xl: 384,\n xs: 32,\n \"2xl\": 448,\n \"3xl\": 512,\n \"4xl\": 576,\n} as const;\n\nconst FragmentComponent: RendererComponent<\"fragment\"> = ({ node }) => {\n const endpoint = node.props.endpoint ?? \"\";\n const isLazy = node.props.lazy === true;\n const componentRef = node.props.ref ?? \"\";\n const placeholderHeight = fragmentSizeHeights[node.props.size];\n const [components, setComponents] = useState(() => node.schema ?? []);\n const [hasLoaded, setHasLoaded] = useState(!isLazy);\n const [processing, setProcessing] = useState(isLazy && endpoint !== \"\");\n\n const load = useCallback(async (): Promise<void> => {\n if (!endpoint) {\n return;\n }\n\n setProcessing(true);\n\n try {\n const result = await apiJson<FragmentResponse>(endpoint, { ref: componentRef });\n\n setComponents(toNodes(result.schema));\n setHasLoaded(true);\n } finally {\n setProcessing(false);\n }\n }, [endpoint, componentRef]);\n\n useEffect(() => {\n if (!isLazy || hasLoaded) {\n return;\n }\n\n void load();\n }, [hasLoaded, isLazy, load]);\n\n useEffect(() => {\n function reload(event: Event): void {\n const detail = (event as ReloadComponentEvent).detail;\n\n if (detail?.component !== node.id) {\n return;\n }\n\n void load();\n }\n\n window.addEventListener(LATTICE_EVENT.reloadComponent, reload);\n\n return () => window.removeEventListener(LATTICE_EVENT.reloadComponent, reload);\n }, [load, node.id]);\n\n useEffect(() => {\n function reloadOnLocaleChange(): void {\n if (!hasLoaded) {\n return;\n }\n\n void load();\n }\n\n window.addEventListener(LATTICE_EVENT.localeChange, reloadOnLocaleChange);\n\n return () => window.removeEventListener(LATTICE_EVENT.localeChange, reloadOnLocaleChange);\n }, [hasLoaded, load]);\n\n return (\n <div\n data-lattice-fragment={node.id}\n style={processing && components.length === 0 ? { minHeight: placeholderHeight } : undefined}\n >\n {processing && components.length === 0 ? (\n <Skeleton className=\"w-full\" style={{ height: placeholderHeight }} />\n ) : (\n <Renderer nodes={components} />\n )}\n </div>\n );\n};\n\nexport default FragmentComponent;\n"],"mappings":";;;;;;;;AASA,IAAM,sBAAsB;CAC1B,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,OAAO;CACP,OAAO;CACP,OAAO;AACT;AAEA,IAAM,qBAAoD,EAAE,WAAW;CACrE,MAAM,WAAW,KAAK,MAAM,YAAY;CACxC,MAAM,SAAS,KAAK,MAAM,SAAS;CACnC,MAAM,eAAe,KAAK,MAAM,OAAO;CACvC,MAAM,oBAAoB,oBAAoB,KAAK,MAAM;CACzD,MAAM,CAAC,YAAY,iBAAiB,eAAe,KAAK,UAAU,CAAC,CAAC;CACpE,MAAM,CAAC,WAAW,gBAAgB,SAAS,CAAC,MAAM;CAClD,MAAM,CAAC,YAAY,iBAAiB,SAAS,UAAU,aAAa,EAAE;CAEtE,MAAM,OAAO,YAAY,YAA2B;EAClD,IAAI,CAAC,UACH;EAGF,cAAc,IAAI;EAElB,IAAI;GACF,MAAM,SAAS,MAAM,QAA0B,UAAU,EAAE,KAAK,aAAa,CAAC;GAE9E,cAAc,QAAQ,OAAO,MAAM,CAAC;GACpC,aAAa,IAAI;EACnB,UAAU;GACR,cAAc,KAAK;EACrB;CACF,GAAG,CAAC,UAAU,YAAY,CAAC;CAE3B,gBAAgB;EACd,IAAI,CAAC,UAAU,WACb;EAGF,KAAU;CACZ,GAAG;EAAC;EAAW;EAAQ;CAAI,CAAC;CAE5B,gBAAgB;EACd,SAAS,OAAO,OAAoB;GAGlC,IAFgB,MAA+B,QAEnC,cAAc,KAAK,IAC7B;GAGF,KAAU;EACZ;EAEA,OAAO,iBAAiB,cAAc,iBAAiB,MAAM;EAE7D,aAAa,OAAO,oBAAoB,cAAc,iBAAiB,MAAM;CAC/E,GAAG,CAAC,MAAM,KAAK,EAAE,CAAC;CAElB,gBAAgB;EACd,SAAS,uBAA6B;GACpC,IAAI,CAAC,WACH;GAGF,KAAU;EACZ;EAEA,OAAO,iBAAiB,cAAc,cAAc,oBAAoB;EAExE,aAAa,OAAO,oBAAoB,cAAc,cAAc,oBAAoB;CAC1F,GAAG,CAAC,WAAW,IAAI,CAAC;CAEpB,OACE,oBAAC,OAAD;EACE,yBAAuB,KAAK;EAC5B,OAAO,cAAc,WAAW,WAAW,IAAI,EAAE,WAAW,kBAAkB,IAAI,KAAA;EAEjF,UAAA,cAAc,WAAW,WAAW,IACnC,oBAAC,UAAD;GAAU,WAAU;GAAS,OAAO,EAAE,QAAQ,kBAAkB;EAAI,CAAA,IAEpE,oBAAC,UAAD,EAAU,OAAO,WAAa,CAAA;CAE7B,CAAA;AAET"}
package/dist/index.d.ts CHANGED
@@ -4,12 +4,12 @@ export { getActionEffects, isActionEffect, dispatchEffects, dispatchActionError,
4
4
  export { useEffectDispatcher } from '@lattice-php/ui/effects/use-effect-dispatcher';
5
5
  export { builtinEffectHandlers, effectHandler, mergeEffectHandlers, } from '@lattice-php/ui/effects/registry';
6
6
  export { initializeAppearance, updateAppearance, useAppearance } from './appearance/index.js';
7
- export { copyToClipboard } from '@lattice-php/ui/copyable-text';
7
+ export { copyToClipboard } from '@lattice-php/ui/primitives/copyable-text';
8
8
  export { createLatticeApp, type CreateLatticeAppI18nOptions, type CreateLatticeAppOptions, } from './create-app.js';
9
9
  export { EventBridge } from './event-bridge.js';
10
10
  export { Icon, IconRenderer, IconRendererProvider, SpriteProvider } from '@lattice-php/ui/icons';
11
11
  export { createLayoutResolver, createPageResolver, pageComponentName, withVisitHeaders, } from './inertia.js';
12
- export { Callouts, layoutComponents, OutletContext, SchemaLayout, SidebarCollapsedContext, useSidebarCollapsed, } from './layout/index.js';
12
+ export { Callouts, layoutComponents, OutletProvider, SchemaLayout, useOutlet } from './layout/index.js';
13
13
  export { Provider, useColumnRegistry, useComponentRegistry } from './provider.js';
14
14
  export { onCallout, onToast, Toaster } from './toast/index.js';
15
15
  export { createRegistry, eagerComponent, extendRegistry, lazyComponent, loadPluginModules, } from '@lattice-php/core/registry';
@@ -18,7 +18,7 @@ export { Renderer } from '@lattice-php/core/renderer';
18
18
  export { LATTICE_REF_HEADER, withRefHeader } from '@lattice-php/core/component-ref';
19
19
  export { withHeaders } from '@lattice-php/core/headers';
20
20
  export { LATTICE_EVENT } from '@lattice-php/core/event-names';
21
- export type { Emphasis } from '@lattice-php/ui/button';
21
+ export type { Emphasis } from '@lattice-php/ui/components/button/button';
22
22
  export type { ReloadComponentEvent } from '@lattice-php/core/event-names';
23
23
  export type { ComponentProps, KnownPageWidth, LayoutPayload, Node, NodeProps, NodeType, PageBreadcrumb, PagePayload, PageWidth, ComponentPropsOf, RendererComponent, RendererComponentModule, RendererComponentProps, Schema, UnknownComponent, } from './types/index.js';
24
24
  export { RealtimeListeners } from './realtime/listeners.js';
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import { onToast } from "./toast/toast.js";
3
3
  import { onCallout } from "./toast/callout.js";
4
4
  import { Toaster } from "./toast/toaster.js";
5
5
  import Callouts from "./layout/components/callouts.js";
6
- import { OutletContext, SidebarCollapsedContext, useSidebarCollapsed } from "./layout/hooks/context.js";
6
+ import { OutletProvider, useOutlet } from "./layout/hooks/context.js";
7
7
  import { layoutComponents } from "./layout/plugin.js";
8
8
  import SchemaLayout from "./layout/components/schema-layout.js";
9
9
  import { RealtimeListeners } from "./realtime/listeners.js";
@@ -21,6 +21,6 @@ import { LATTICE_EVENT } from "@lattice-php/core/event-names";
21
21
  import { dispatchActionError, dispatchEffects, getActionEffects, isActionEffect } from "@lattice-php/ui/effects/dispatch";
22
22
  import { useEffectDispatcher } from "@lattice-php/ui/effects/use-effect-dispatcher";
23
23
  import { builtinEffectHandlers, effectHandler, mergeEffectHandlers } from "@lattice-php/ui/effects/registry";
24
- import { copyToClipboard } from "@lattice-php/ui/copyable-text";
24
+ import { copyToClipboard } from "@lattice-php/ui/primitives/copyable-text";
25
25
  import { columnCell } from "@lattice-php/table/registry";
26
- export { Callouts, EventBridge, Icon, IconRenderer, IconRendererProvider, LATTICE_EVENT, LATTICE_REF_HEADER, OutletContext, Provider, RealtimeListeners, Renderer, SchemaLayout, SidebarCollapsedContext, SpriteProvider, Toaster, builtinEffectHandlers, columnCell, copyToClipboard, createLatticeApp, createLayoutResolver, createPageResolver, createRegistry, dispatchActionError, dispatchEffects, eagerComponent, effectHandler, extendRegistry, getActionEffects, initializeAppearance, isActionEffect, layoutComponents, lazyComponent, loadPluginModules, mergeEffectHandlers, onCallout, onToast, pageComponentName, registry, updateAppearance, useAppearance, useColumnRegistry, useComponentRegistry, useEffectDispatcher, useSidebarCollapsed, withHeaders, withRefHeader, withVisitHeaders };
26
+ export { Callouts, EventBridge, Icon, IconRenderer, IconRendererProvider, LATTICE_EVENT, LATTICE_REF_HEADER, OutletProvider, Provider, RealtimeListeners, Renderer, SchemaLayout, SpriteProvider, Toaster, builtinEffectHandlers, columnCell, copyToClipboard, createLatticeApp, createLayoutResolver, createPageResolver, createRegistry, dispatchActionError, dispatchEffects, eagerComponent, effectHandler, extendRegistry, getActionEffects, initializeAppearance, isActionEffect, layoutComponents, lazyComponent, loadPluginModules, mergeEffectHandlers, onCallout, onToast, pageComponentName, registry, updateAppearance, useAppearance, useColumnRegistry, useComponentRegistry, useEffectDispatcher, useOutlet, withHeaders, withRefHeader, withVisitHeaders };
@@ -1,6 +1,16 @@
1
1
  import { Plugin } from '@lattice-php/core/registry';
2
2
  import { NavigationAdapter } from '@lattice-php/ui/navigation';
3
3
  export declare const inertiaNavigation: NavigationAdapter;
4
+ /**
5
+ * An open modal must not survive the page it was opened on — its content can
6
+ * hold stale props once the visit swaps in a new page. Inertia's `navigate`
7
+ * event only fires for a visit that actually swaps in a different page
8
+ * (a new URL, or history back/forward); a same-URL visit — `router.reload()`,
9
+ * partial reloads, polling — is internally marked as a `replace` and never
10
+ * fires it. Subscribing to `navigate` alone is therefore already the correct
11
+ * discriminator: a modal open during a same-page refresh is left untouched.
12
+ */
13
+ export declare function useCloseModalsOnNavigate(): void;
4
14
  /**
5
15
  * SPA-grade redirect/reload effect handlers. Registered as an effects
6
16
  * extension so they override ui's window-based built-ins wherever the
@@ -1,4 +1,6 @@
1
1
  import { Link, router } from "@inertiajs/react";
2
+ import { useEffect } from "react";
3
+ import { LATTICE_EVENT } from "@lattice-php/core/event-names";
2
4
  import { jsx } from "react/jsx-runtime";
3
5
  import { effectHandler } from "@lattice-php/ui/effects/registry";
4
6
  //#region resources/js/inertia-navigation.tsx
@@ -15,6 +17,21 @@ var inertiaNavigation = {
15
17
  visit: (url, options) => router.visit(url, options),
16
18
  reload: () => router.reload()
17
19
  };
20
+ function closeAllModals() {
21
+ window.dispatchEvent(new CustomEvent(LATTICE_EVENT.closeModal, { detail: { modal: null } }));
22
+ }
23
+ /**
24
+ * An open modal must not survive the page it was opened on — its content can
25
+ * hold stale props once the visit swaps in a new page. Inertia's `navigate`
26
+ * event only fires for a visit that actually swaps in a different page
27
+ * (a new URL, or history back/forward); a same-URL visit — `router.reload()`,
28
+ * partial reloads, polling — is internally marked as a `replace` and never
29
+ * fires it. Subscribing to `navigate` alone is therefore already the correct
30
+ * discriminator: a modal open during a same-page refresh is left untouched.
31
+ */
32
+ function useCloseModalsOnNavigate() {
33
+ useEffect(() => router.on("navigate", closeAllModals), []);
34
+ }
18
35
  /**
19
36
  * SPA-grade redirect/reload effect handlers. Registered as an effects
20
37
  * extension so they override ui's window-based built-ins wherever the
@@ -28,6 +45,6 @@ var navigationPlugin = {
28
45
  } }
29
46
  };
30
47
  //#endregion
31
- export { inertiaNavigation, navigationPlugin };
48
+ export { inertiaNavigation, navigationPlugin, useCloseModalsOnNavigate };
32
49
 
33
50
  //# sourceMappingURL=inertia-navigation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"inertia-navigation.js","names":[],"sources":["../resources/js/inertia-navigation.tsx"],"sourcesContent":["import { Link, router } from \"@inertiajs/react\";\nimport type { Plugin } from \"@lattice-php/core/registry\";\nimport { effectHandler } from \"@lattice-php/ui/effects/registry\";\nimport type { NavigationAdapter, NavLinkProps } from \"@lattice-php/ui/navigation\";\nimport type { ComponentProps } from \"react\";\n\nfunction InertiaLink({ href, method = \"get\", children, ...props }: NavLinkProps) {\n // NavLinkProps types handlers against HTMLAnchorElement; Inertia's Link\n // types them against Element. Same events at runtime, so the cast only\n // bridges React's generic variance.\n return (\n <Link href={href} method={method} {...(props as ComponentProps<typeof Link>)}>\n {children}\n </Link>\n );\n}\n\nexport const inertiaNavigation: NavigationAdapter = {\n Link: InertiaLink,\n visit: (url, options) => router.visit(url, options),\n reload: () => router.reload(),\n};\n\n/**\n * SPA-grade redirect/reload effect handlers. Registered as an effects\n * extension so they override ui's window-based built-ins wherever the\n * framework registry is active.\n */\nexport const navigationPlugin: Plugin = {\n name: \"lattice/navigation\",\n extensions: {\n effects: {\n redirect: effectHandler(\"redirect\", (effect) => router.visit(effect.props.url)),\n \"reload-page\": effectHandler(\"reload-page\", (effect) =>\n effect.props.full ? window.location.reload() : router.reload(),\n ),\n },\n },\n};\n"],"mappings":";;;;AAMA,SAAS,YAAY,EAAE,MAAM,SAAS,OAAO,UAAU,GAAG,SAAuB;CAI/E,OACE,oBAAC,MAAD;EAAY;EAAc;EAAQ,GAAK;EACpC;CACG,CAAA;AAEV;AAEA,IAAa,oBAAuC;CAClD,MAAM;CACN,QAAQ,KAAK,YAAY,OAAO,MAAM,KAAK,OAAO;CAClD,cAAc,OAAO,OAAO;AAC9B;;;;;;AAOA,IAAa,mBAA2B;CACtC,MAAM;CACN,YAAY,EACV,SAAS;EACP,UAAU,cAAc,aAAa,WAAW,OAAO,MAAM,OAAO,MAAM,GAAG,CAAC;EAC9E,eAAe,cAAc,gBAAgB,WAC3C,OAAO,MAAM,OAAO,OAAO,SAAS,OAAO,IAAI,OAAO,OAAO,CAC/D;CACF,EACF;AACF"}
1
+ {"version":3,"file":"inertia-navigation.js","names":[],"sources":["../resources/js/inertia-navigation.tsx"],"sourcesContent":["import { Link, router } from \"@inertiajs/react\";\nimport { useEffect } from \"react\";\nimport type { Plugin } from \"@lattice-php/core/registry\";\nimport { LATTICE_EVENT } from \"@lattice-php/core/event-names\";\nimport { effectHandler } from \"@lattice-php/ui/effects/registry\";\nimport type { NavigationAdapter, NavLinkProps } from \"@lattice-php/ui/navigation\";\nimport type { ComponentProps } from \"react\";\n\nfunction InertiaLink({ href, method = \"get\", children, ...props }: NavLinkProps) {\n // NavLinkProps types handlers against HTMLAnchorElement; Inertia's Link\n // types them against Element. Same events at runtime, so the cast only\n // bridges React's generic variance.\n return (\n <Link href={href} method={method} {...(props as ComponentProps<typeof Link>)}>\n {children}\n </Link>\n );\n}\n\nexport const inertiaNavigation: NavigationAdapter = {\n Link: InertiaLink,\n visit: (url, options) => router.visit(url, options),\n reload: () => router.reload(),\n};\n\nfunction closeAllModals(): void {\n window.dispatchEvent(new CustomEvent(LATTICE_EVENT.closeModal, { detail: { modal: null } }));\n}\n\n/**\n * An open modal must not survive the page it was opened on — its content can\n * hold stale props once the visit swaps in a new page. Inertia's `navigate`\n * event only fires for a visit that actually swaps in a different page\n * (a new URL, or history back/forward); a same-URL visit — `router.reload()`,\n * partial reloads, polling — is internally marked as a `replace` and never\n * fires it. Subscribing to `navigate` alone is therefore already the correct\n * discriminator: a modal open during a same-page refresh is left untouched.\n */\nexport function useCloseModalsOnNavigate(): void {\n useEffect(() => router.on(\"navigate\", closeAllModals), []);\n}\n\n/**\n * SPA-grade redirect/reload effect handlers. Registered as an effects\n * extension so they override ui's window-based built-ins wherever the\n * framework registry is active.\n */\nexport const navigationPlugin: Plugin = {\n name: \"lattice/navigation\",\n extensions: {\n effects: {\n redirect: effectHandler(\"redirect\", (effect) => router.visit(effect.props.url)),\n \"reload-page\": effectHandler(\"reload-page\", (effect) =>\n effect.props.full ? window.location.reload() : router.reload(),\n ),\n },\n },\n};\n"],"mappings":";;;;;;AAQA,SAAS,YAAY,EAAE,MAAM,SAAS,OAAO,UAAU,GAAG,SAAuB;CAI/E,OACE,oBAAC,MAAD;EAAY;EAAc;EAAQ,GAAK;EACpC;CACG,CAAA;AAEV;AAEA,IAAa,oBAAuC;CAClD,MAAM;CACN,QAAQ,KAAK,YAAY,OAAO,MAAM,KAAK,OAAO;CAClD,cAAc,OAAO,OAAO;AAC9B;AAEA,SAAS,iBAAuB;CAC9B,OAAO,cAAc,IAAI,YAAY,cAAc,YAAY,EAAE,QAAQ,EAAE,OAAO,KAAK,EAAE,CAAC,CAAC;AAC7F;;;;;;;;;;AAWA,SAAgB,2BAAiC;CAC/C,gBAAgB,OAAO,GAAG,YAAY,cAAc,GAAG,CAAC,CAAC;AAC3D;;;;;;AAOA,IAAa,mBAA2B;CACtC,MAAM;CACN,YAAY,EACV,SAAS;EACP,UAAU,cAAc,aAAa,WAAW,OAAO,MAAM,OAAO,MAAM,GAAG,CAAC;EAC9E,eAAe,cAAc,gBAAgB,WAC3C,OAAO,MAAM,OAAO,OAAO,SAAS,OAAO,IAAI,OAAO,OAAO,CAC/D;CACF,EACF;AACF"}
@@ -1,11 +1,11 @@
1
1
  import { Popover } from "./popover.js";
2
- import { useSidebarCollapsed } from "../hooks/context.js";
3
2
  import { Link, usePage } from "@inertiajs/react";
4
3
  import { useState } from "react";
5
4
  import { Icon, IconRenderer } from "@lattice-php/ui/icons";
6
5
  import { cn } from "@lattice-php/ui/lib/utils";
7
6
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
7
  import { prefixedNodeTestId } from "@lattice-php/core/test-id";
8
+ import { useCollapsed } from "@lattice-php/core/collapsed-context";
9
9
  import { ActionTrigger, useClickBehavior } from "@lattice-php/ui/click-behavior";
10
10
  //#region resources/js/layout/components/menu-item.tsx
11
11
  var rowClass = "flex items-center gap-2 rounded-lt-sm px-3 py-2 text-base font-medium text-lt-fg transition-colors hover:bg-lt-muted";
@@ -23,7 +23,7 @@ function schemaContainsPath(schema, path) {
23
23
  return (schema ?? []).some((child) => child.props?.href === path || schemaContainsPath(child.schema, path));
24
24
  }
25
25
  var MenuItemComponent = ({ children, node }) => {
26
- const collapsed = useSidebarCollapsed();
26
+ const collapsed = useCollapsed();
27
27
  const { icon, label: rawLabel, prefix, suffix } = node.props;
28
28
  const label = rawLabel ?? "";
29
29
  const iconOnly = Boolean(icon);
@@ -60,7 +60,7 @@ var MenuItemComponent = ({ children, node }) => {
60
60
  action: behavior.action,
61
61
  children: triggerButton
62
62
  }) });
63
- if (behavior.kind === "effects") return /* @__PURE__ */ jsx("li", { children: triggerButton({
63
+ if (behavior.kind === "effects" || behavior.kind === "modal") return /* @__PURE__ */ jsx("li", { children: triggerButton({
64
64
  onClick: behavior.onClick,
65
65
  processing: false
66
66
  }) });
@@ -1 +1 @@
1
- {"version":3,"file":"menu-item.js","names":[],"sources":["../../../resources/js/layout/components/menu-item.tsx"],"sourcesContent":["import { Link, usePage } from \"@inertiajs/react\";\nimport { useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { prefixedNodeTestId } from \"@lattice-php/core/test-id\";\nimport type { RendererComponent, Schema } from \"@lattice-php/core/types\";\nimport { Icon, IconRenderer } from \"@lattice-php/ui/icons\";\nimport { cn } from \"@lattice-php/ui/lib/utils\";\nimport type { Affix } from \"@lattice-php/core\";\nimport { ActionTrigger, type TriggerState, useClickBehavior } from \"@lattice-php/ui/click-behavior\";\nimport { useSidebarCollapsed } from \"@lattice-php/lattice/layout/hooks/context\";\nimport { Popover } from \"./popover\";\n\nconst rowClass =\n \"flex items-center gap-2 rounded-lt-sm px-3 py-2 text-base font-medium text-lt-fg transition-colors hover:bg-lt-muted\";\n\nfunction MenuAffix({ affix, className }: { affix: Affix; className?: string }) {\n if (affix.icon) {\n return <IconRenderer className={cn(\"size-lt-icon-md shrink-0\", className)} icon={affix.icon} />;\n }\n\n return <span className={cn(\"shrink-0 text-sm text-lt-muted-fg\", className)}>{affix.text}</span>;\n}\n\nfunction schemaContainsPath(schema: Schema | undefined, path: string): boolean {\n return (schema ?? []).some(\n (child) => child.props?.href === path || schemaContainsPath(child.schema, path),\n );\n}\n\nconst MenuItemComponent: RendererComponent<\"menu-item\"> = ({ children, node }) => {\n const collapsed = useSidebarCollapsed();\n const { icon, label: rawLabel, prefix, suffix } = node.props;\n const label = rawLabel ?? \"\";\n const iconOnly = Boolean(icon);\n const flyoutIcon = icon ?? prefix?.icon ?? null;\n const currentPath = usePage().url.split(\"?\")[0];\n const testId = prefixedNodeTestId(\"menu\", node);\n const behavior = useClickBehavior(node.props);\n\n const content = icon ? (\n <IconRenderer className=\"size-lt-icon-md shrink-0\" icon={icon} />\n ) : (\n <>\n {prefix ? <MenuAffix affix={prefix} /> : null}\n {collapsed ? (\n <span\n className=\"pointer-events-none absolute top-1/2 left-full z-lt-popover ml-2 hidden -translate-y-1/2 rounded-lt-sm border border-lt-border bg-lt-popover px-2 py-1 text-sm whitespace-nowrap text-lt-popover-fg shadow-lt-md group-hover:block\"\n role=\"tooltip\"\n >\n {label}\n </span>\n ) : (\n <span>{label}</span>\n )}\n {suffix ? <MenuAffix affix={suffix} className=\"ml-auto\" /> : null}\n </>\n );\n\n const rowClassName = cn(\n rowClass,\n \"w-full\",\n collapsed && \"group relative justify-center\",\n iconOnly && \"justify-center\",\n );\n\n const triggerButton = ({ onClick, processing }: TriggerState) => (\n <button\n aria-label={collapsed || iconOnly ? label : undefined}\n className={rowClassName}\n data-test={testId}\n disabled={processing}\n onClick={onClick}\n type=\"button\"\n >\n {content}\n </button>\n );\n\n if (behavior.kind === \"action\") {\n return (\n <li>\n <ActionTrigger action={behavior.action}>{triggerButton}</ActionTrigger>\n </li>\n );\n }\n\n if (behavior.kind === \"effects\") {\n return <li>{triggerButton({ onClick: behavior.onClick, processing: false })}</li>;\n }\n\n if (behavior.kind === \"navigate\") {\n const active = currentPath === behavior.href;\n\n return (\n <li>\n <Link\n aria-current={active ? \"page\" : undefined}\n aria-label={collapsed || iconOnly ? label : undefined}\n as={behavior.method === \"get\" ? undefined : \"button\"}\n className={cn(rowClassName, active && \"bg-lt-muted font-medium\")}\n data-test={testId}\n href={behavior.href}\n method={behavior.method}\n >\n {content}\n </Link>\n </li>\n );\n }\n\n if (!children) {\n return collapsed ? null : (\n <li>\n <span className=\"flex items-center gap-2 px-3 py-2 text-xs font-semibold tracking-wide text-lt-muted-fg uppercase\">\n {content}\n </span>\n </li>\n );\n }\n\n if (collapsed) {\n return (\n <FlyoutGroup icon={flyoutIcon} label={label} testId={testId}>\n {children}\n </FlyoutGroup>\n );\n }\n\n return (\n <CollapsibleItem\n content={content}\n defaultOpen={schemaContainsPath(node.schema, currentPath)}\n testId={testId}\n >\n {children}\n </CollapsibleItem>\n );\n};\n\nfunction CollapsibleItem({\n children,\n content,\n defaultOpen,\n testId,\n}: {\n children: ReactNode;\n content: ReactNode;\n defaultOpen: boolean;\n testId?: string;\n}) {\n const [open, setOpen] = useState(defaultOpen);\n\n return (\n <li>\n <button\n aria-expanded={open}\n className={cn(rowClass, \"w-full\")}\n data-test={testId}\n onClick={() => setOpen((value) => !value)}\n type=\"button\"\n >\n {content}\n <Icon\n name=\"chevron-right\"\n className={cn(\n \"ml-auto size-lt-icon-md shrink-0 transition-transform\",\n open && \"rotate-90\",\n )}\n />\n </button>\n {open ? <ul className=\"mt-1 flex flex-col gap-1 pl-3\">{children}</ul> : null}\n </li>\n );\n}\n\nfunction FlyoutGroup({\n children,\n icon,\n label,\n testId,\n}: {\n children: ReactNode;\n icon?: string | null;\n label: string;\n testId?: string;\n}) {\n return (\n <li>\n <Popover\n className=\"min-w-48\"\n placement=\"right\"\n testId={testId}\n trigger={\n icon ? (\n <IconRenderer className=\"size-lt-icon-md shrink-0\" icon={icon} />\n ) : (\n <span>{label}</span>\n )\n }\n triggerClassName={cn(rowClass, \"justify-center\")}\n triggerLabel={label}\n >\n <ul className=\"flex flex-col gap-1\">\n <li className=\"px-3 py-1.5 text-xs font-semibold tracking-wide text-lt-muted-fg uppercase\">\n {label}\n </li>\n {children}\n </ul>\n </Popover>\n </li>\n );\n}\n\nexport default MenuItemComponent;\n"],"mappings":"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"}
1
+ {"version":3,"file":"menu-item.js","names":[],"sources":["../../../resources/js/layout/components/menu-item.tsx"],"sourcesContent":["import { Link, usePage } from \"@inertiajs/react\";\nimport { useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { prefixedNodeTestId } from \"@lattice-php/core/test-id\";\nimport type { RendererComponent, Schema } from \"@lattice-php/core/types\";\nimport { Icon, IconRenderer } from \"@lattice-php/ui/icons\";\nimport { cn } from \"@lattice-php/ui/lib/utils\";\nimport type { Affix } from \"@lattice-php/core\";\nimport { ActionTrigger, type TriggerState, useClickBehavior } from \"@lattice-php/ui/click-behavior\";\nimport { useCollapsed } from \"@lattice-php/core/collapsed-context\";\nimport { Popover } from \"./popover\";\n\nconst rowClass =\n \"flex items-center gap-2 rounded-lt-sm px-3 py-2 text-base font-medium text-lt-fg transition-colors hover:bg-lt-muted\";\n\nfunction MenuAffix({ affix, className }: { affix: Affix; className?: string }) {\n if (affix.icon) {\n return <IconRenderer className={cn(\"size-lt-icon-md shrink-0\", className)} icon={affix.icon} />;\n }\n\n return <span className={cn(\"shrink-0 text-sm text-lt-muted-fg\", className)}>{affix.text}</span>;\n}\n\nfunction schemaContainsPath(schema: Schema | undefined, path: string): boolean {\n return (schema ?? []).some(\n (child) => child.props?.href === path || schemaContainsPath(child.schema, path),\n );\n}\n\nconst MenuItemComponent: RendererComponent<\"menu-item\"> = ({ children, node }) => {\n const collapsed = useCollapsed();\n const { icon, label: rawLabel, prefix, suffix } = node.props;\n const label = rawLabel ?? \"\";\n const iconOnly = Boolean(icon);\n const flyoutIcon = icon ?? prefix?.icon ?? null;\n const currentPath = usePage().url.split(\"?\")[0];\n const testId = prefixedNodeTestId(\"menu\", node);\n const behavior = useClickBehavior(node.props);\n\n const content = icon ? (\n <IconRenderer className=\"size-lt-icon-md shrink-0\" icon={icon} />\n ) : (\n <>\n {prefix ? <MenuAffix affix={prefix} /> : null}\n {collapsed ? (\n <span\n className=\"pointer-events-none absolute top-1/2 left-full z-lt-popover ml-2 hidden -translate-y-1/2 rounded-lt-sm border border-lt-border bg-lt-popover px-2 py-1 text-sm whitespace-nowrap text-lt-popover-fg shadow-lt-md group-hover:block\"\n role=\"tooltip\"\n >\n {label}\n </span>\n ) : (\n <span>{label}</span>\n )}\n {suffix ? <MenuAffix affix={suffix} className=\"ml-auto\" /> : null}\n </>\n );\n\n const rowClassName = cn(\n rowClass,\n \"w-full\",\n collapsed && \"group relative justify-center\",\n iconOnly && \"justify-center\",\n );\n\n const triggerButton = ({ onClick, processing }: TriggerState) => (\n <button\n aria-label={collapsed || iconOnly ? label : undefined}\n className={rowClassName}\n data-test={testId}\n disabled={processing}\n onClick={onClick}\n type=\"button\"\n >\n {content}\n </button>\n );\n\n if (behavior.kind === \"action\") {\n return (\n <li>\n <ActionTrigger action={behavior.action}>{triggerButton}</ActionTrigger>\n </li>\n );\n }\n\n if (behavior.kind === \"effects\" || behavior.kind === \"modal\") {\n return <li>{triggerButton({ onClick: behavior.onClick, processing: false })}</li>;\n }\n\n if (behavior.kind === \"navigate\") {\n const active = currentPath === behavior.href;\n\n return (\n <li>\n <Link\n aria-current={active ? \"page\" : undefined}\n aria-label={collapsed || iconOnly ? label : undefined}\n as={behavior.method === \"get\" ? undefined : \"button\"}\n className={cn(rowClassName, active && \"bg-lt-muted font-medium\")}\n data-test={testId}\n href={behavior.href}\n method={behavior.method}\n >\n {content}\n </Link>\n </li>\n );\n }\n\n if (!children) {\n return collapsed ? null : (\n <li>\n <span className=\"flex items-center gap-2 px-3 py-2 text-xs font-semibold tracking-wide text-lt-muted-fg uppercase\">\n {content}\n </span>\n </li>\n );\n }\n\n if (collapsed) {\n return (\n <FlyoutGroup icon={flyoutIcon} label={label} testId={testId}>\n {children}\n </FlyoutGroup>\n );\n }\n\n return (\n <CollapsibleItem\n content={content}\n defaultOpen={schemaContainsPath(node.schema, currentPath)}\n testId={testId}\n >\n {children}\n </CollapsibleItem>\n );\n};\n\nfunction CollapsibleItem({\n children,\n content,\n defaultOpen,\n testId,\n}: {\n children: ReactNode;\n content: ReactNode;\n defaultOpen: boolean;\n testId?: string;\n}) {\n const [open, setOpen] = useState(defaultOpen);\n\n return (\n <li>\n <button\n aria-expanded={open}\n className={cn(rowClass, \"w-full\")}\n data-test={testId}\n onClick={() => setOpen((value) => !value)}\n type=\"button\"\n >\n {content}\n <Icon\n name=\"chevron-right\"\n className={cn(\n \"ml-auto size-lt-icon-md shrink-0 transition-transform\",\n open && \"rotate-90\",\n )}\n />\n </button>\n {open ? <ul className=\"mt-1 flex flex-col gap-1 pl-3\">{children}</ul> : null}\n </li>\n );\n}\n\nfunction FlyoutGroup({\n children,\n icon,\n label,\n testId,\n}: {\n children: ReactNode;\n icon?: string | null;\n label: string;\n testId?: string;\n}) {\n return (\n <li>\n <Popover\n className=\"min-w-48\"\n placement=\"right\"\n testId={testId}\n trigger={\n icon ? (\n <IconRenderer className=\"size-lt-icon-md shrink-0\" icon={icon} />\n ) : (\n <span>{label}</span>\n )\n }\n triggerClassName={cn(rowClass, \"justify-center\")}\n triggerLabel={label}\n >\n <ul className=\"flex flex-col gap-1\">\n <li className=\"px-3 py-1.5 text-xs font-semibold tracking-wide text-lt-muted-fg uppercase\">\n {label}\n </li>\n {children}\n </ul>\n </Popover>\n </li>\n );\n}\n\nexport default MenuItemComponent;\n"],"mappings":"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"}
@@ -1,12 +1,11 @@
1
- import { OutletContext } from "../hooks/context.js";
2
- import { useContext } from "react";
3
- import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
1
+ import { useOutlet } from "../hooks/context.js";
2
+ import { Fragment, jsx } from "react/jsx-runtime";
4
3
  //#region resources/js/layout/components/outlet.tsx
5
4
  /**
6
5
  * Renders the active page at the position the layout schema places it.
7
6
  */
8
7
  var OutletComponent = () => {
9
- return /* @__PURE__ */ jsx(Fragment$1, { children: useContext(OutletContext) });
8
+ return /* @__PURE__ */ jsx(Fragment, { children: useOutlet() });
10
9
  };
11
10
  //#endregion
12
11
  export { OutletComponent as default };
@@ -1 +1 @@
1
- {"version":3,"file":"outlet.js","names":[],"sources":["../../../resources/js/layout/components/outlet.tsx"],"sourcesContent":["import { useContext } from \"react\";\nimport type { RendererComponent } from \"@lattice-php/core/types\";\nimport { OutletContext } from \"@lattice-php/lattice/layout/hooks/context\";\n\n/**\n * Renders the active page at the position the layout schema places it.\n */\nconst OutletComponent: RendererComponent<\"outlet\"> = () => {\n return <>{useContext(OutletContext)}</>;\n};\n\nexport default OutletComponent;\n"],"mappings":";;;;;;;AAOA,IAAM,wBAAqD;CACzD,OAAO,oBAAA,YAAA,EAAA,UAAG,WAAW,aAAa,EAAI,CAAA;AACxC"}
1
+ {"version":3,"file":"outlet.js","names":[],"sources":["../../../resources/js/layout/components/outlet.tsx"],"sourcesContent":["import type { RendererComponent } from \"@lattice-php/core/types\";\nimport { useOutlet } from \"@lattice-php/lattice/layout/hooks/context\";\n\n/**\n * Renders the active page at the position the layout schema places it.\n */\nconst OutletComponent: RendererComponent<\"outlet\"> = () => {\n return <>{useOutlet()}</>;\n};\n\nexport default OutletComponent;\n"],"mappings":";;;;;;AAMA,IAAM,wBAAqD;CACzD,OAAO,oBAAA,UAAA,EAAA,UAAG,UAAU,EAAI,CAAA;AAC1B"}
@@ -2,8 +2,8 @@ import { usePage } from "@inertiajs/react";
2
2
  import { useEffect, useState } from "react";
3
3
  import { cn } from "@lattice-php/ui/lib/utils";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
- import { Popover as Popover$1, PopoverContent, PopoverTrigger } from "@lattice-php/ui/popover";
6
- import { CollapsedContext } from "@lattice-php/core/collapsed-context";
5
+ import { Popover as Popover$1, PopoverContent, PopoverTrigger } from "@lattice-php/ui/primitives/popover";
6
+ import { CollapsedProvider } from "@lattice-php/core/collapsed-context";
7
7
  //#region resources/js/layout/components/popover.tsx
8
8
  /**
9
9
  * A navigation-aware popover for the sidebar: it wraps the shared popover
@@ -33,8 +33,8 @@ function Popover({ align = "start", children, className, placement = "bottom", t
33
33
  className: cn("min-w-56 p-1", className),
34
34
  role: "menu",
35
35
  side: placement,
36
- children: /* @__PURE__ */ jsx(CollapsedContext.Provider, {
37
- value: false,
36
+ children: /* @__PURE__ */ jsx(CollapsedProvider, {
37
+ collapsed: false,
38
38
  children
39
39
  })
40
40
  })]
@@ -1 +1 @@
1
- {"version":3,"file":"popover.js","names":[],"sources":["../../../resources/js/layout/components/popover.tsx"],"sourcesContent":["import { usePage } from \"@inertiajs/react\";\nimport { useEffect, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { Popover as PopoverRoot, PopoverContent, PopoverTrigger } from \"@lattice-php/ui/popover\";\nimport { cn } from \"@lattice-php/ui/lib/utils\";\nimport { CollapsedContext } from \"@lattice-php/core/collapsed-context\";\n\n/**\n * A navigation-aware popover for the sidebar: it wraps the shared popover\n * primitive (so positioning and dismissal stay consistent with the rest of the\n * app) and closes itself on every Inertia navigation. Items render expanded\n * regardless of the sidebar's collapsed state.\n */\nexport function Popover({\n align = \"start\",\n children,\n className,\n placement = \"bottom\",\n testId,\n trigger,\n triggerClassName,\n triggerLabel,\n}: {\n align?: \"start\" | \"end\";\n children: ReactNode;\n className?: string;\n placement?: \"bottom\" | \"right\" | \"top\";\n testId?: string;\n trigger: ReactNode;\n triggerClassName?: string;\n triggerLabel?: string;\n}) {\n const [open, setOpen] = useState(false);\n const url = usePage().url;\n\n useEffect(() => setOpen(false), [url]);\n\n return (\n <PopoverRoot open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <button\n aria-label={triggerLabel}\n className={cn(\"w-full\", triggerClassName)}\n data-test={testId}\n title={triggerLabel}\n type=\"button\"\n >\n {trigger}\n </button>\n </PopoverTrigger>\n <PopoverContent\n align={align}\n className={cn(\"min-w-56 p-1\", className)}\n role=\"menu\"\n side={placement}\n >\n <CollapsedContext.Provider value={false}>{children}</CollapsedContext.Provider>\n </PopoverContent>\n </PopoverRoot>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,QAAQ,EACtB,QAAQ,SACR,UACA,WACA,YAAY,UACZ,QACA,SACA,kBACA,gBAUC;CACD,MAAM,CAAC,MAAM,WAAW,SAAS,KAAK;CACtC,MAAM,MAAM,QAAQ,CAAC,CAAC;CAEtB,gBAAgB,QAAQ,KAAK,GAAG,CAAC,GAAG,CAAC;CAErC,OACE,qBAAC,WAAD;EAAmB;EAAM,cAAc;EAAvC,UAAA,CACE,oBAAC,gBAAD;GAAgB,SAAA;GACd,UAAA,oBAAC,UAAD;IACE,cAAY;IACZ,WAAW,GAAG,UAAU,gBAAgB;IACxC,aAAW;IACX,OAAO;IACP,MAAK;IAEJ,UAAA;GACK,CAAA;EACM,CAAA,GAChB,oBAAC,gBAAD;GACS;GACP,WAAW,GAAG,gBAAgB,SAAS;GACvC,MAAK;GACL,MAAM;GAEN,UAAA,oBAAC,iBAAiB,UAAlB;IAA2B,OAAO;IAAQ;GAAoC,CAAA;EAChE,CAAA,CACL;;AAEjB"}
1
+ {"version":3,"file":"popover.js","names":[],"sources":["../../../resources/js/layout/components/popover.tsx"],"sourcesContent":["import { usePage } from \"@inertiajs/react\";\nimport { useEffect, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport {\n Popover as PopoverRoot,\n PopoverContent,\n PopoverTrigger,\n} from \"@lattice-php/ui/primitives/popover\";\nimport { cn } from \"@lattice-php/ui/lib/utils\";\nimport { CollapsedProvider } from \"@lattice-php/core/collapsed-context\";\n\n/**\n * A navigation-aware popover for the sidebar: it wraps the shared popover\n * primitive (so positioning and dismissal stay consistent with the rest of the\n * app) and closes itself on every Inertia navigation. Items render expanded\n * regardless of the sidebar's collapsed state.\n */\nexport function Popover({\n align = \"start\",\n children,\n className,\n placement = \"bottom\",\n testId,\n trigger,\n triggerClassName,\n triggerLabel,\n}: {\n align?: \"start\" | \"end\";\n children: ReactNode;\n className?: string;\n placement?: \"bottom\" | \"right\" | \"top\";\n testId?: string;\n trigger: ReactNode;\n triggerClassName?: string;\n triggerLabel?: string;\n}) {\n const [open, setOpen] = useState(false);\n const url = usePage().url;\n\n useEffect(() => setOpen(false), [url]);\n\n return (\n <PopoverRoot open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <button\n aria-label={triggerLabel}\n className={cn(\"w-full\", triggerClassName)}\n data-test={testId}\n title={triggerLabel}\n type=\"button\"\n >\n {trigger}\n </button>\n </PopoverTrigger>\n <PopoverContent\n align={align}\n className={cn(\"min-w-56 p-1\", className)}\n role=\"menu\"\n side={placement}\n >\n <CollapsedProvider collapsed={false}>{children}</CollapsedProvider>\n </PopoverContent>\n </PopoverRoot>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAiBA,SAAgB,QAAQ,EACtB,QAAQ,SACR,UACA,WACA,YAAY,UACZ,QACA,SACA,kBACA,gBAUC;CACD,MAAM,CAAC,MAAM,WAAW,SAAS,KAAK;CACtC,MAAM,MAAM,QAAQ,CAAC,CAAC;CAEtB,gBAAgB,QAAQ,KAAK,GAAG,CAAC,GAAG,CAAC;CAErC,OACE,qBAAC,WAAD;EAAmB;EAAM,cAAc;EAAvC,UAAA,CACE,oBAAC,gBAAD;GAAgB,SAAA;GACd,UAAA,oBAAC,UAAD;IACE,cAAY;IACZ,WAAW,GAAG,UAAU,gBAAgB;IACxC,aAAW;IACX,OAAO;IACP,MAAK;IAEJ,UAAA;GACK,CAAA;EACM,CAAA,GAChB,oBAAC,gBAAD;GACS;GACP,WAAW,GAAG,gBAAgB,SAAS;GACvC,MAAK;GACL,MAAM;GAEN,UAAA,oBAAC,mBAAD;IAAmB,WAAW;IAAQ;GAA4B,CAAA;EACpD,CAAA,CACL;;AAEjB"}
@@ -1,4 +1,4 @@
1
- import { OutletContext } from "../hooks/context.js";
1
+ import { OutletProvider } from "../hooks/context.js";
2
2
  import { usePage } from "@inertiajs/react";
3
3
  import { Renderer } from "@lattice-php/core/renderer";
4
4
  import { Fragment, jsx } from "react/jsx-runtime";
@@ -12,8 +12,8 @@ import { Fragment, jsx } from "react/jsx-runtime";
12
12
  function SchemaLayout({ children }) {
13
13
  const layout = usePage().props.lattice.layout;
14
14
  if (!layout) return /* @__PURE__ */ jsx(Fragment, { children });
15
- return /* @__PURE__ */ jsx(OutletContext.Provider, {
16
- value: children,
15
+ return /* @__PURE__ */ jsx(OutletProvider, {
16
+ outlet: children,
17
17
  children: /* @__PURE__ */ jsx(Renderer, { nodes: layout.schema })
18
18
  });
19
19
  }
@@ -1 +1 @@
1
- {"version":3,"file":"schema-layout.js","names":[],"sources":["../../../resources/js/layout/components/schema-layout.tsx"],"sourcesContent":["import { usePage } from \"@inertiajs/react\";\nimport type { ReactNode } from \"react\";\nimport { Renderer } from \"@lattice-php/core/renderer\";\nimport type { PagePayload } from \"@lattice-php/lattice/types\";\nimport { OutletContext } from \"@lattice-php/lattice/layout/hooks/context\";\n\n/**\n * Persistent Inertia layout that renders a server-composed layout schema and\n * exposes the active page (`children`) to the Outlet via context. The same\n * component instance is reused across navigations that share a layout, so\n * shell state (open sidebar, scroll position) survives page visits.\n */\nexport default function SchemaLayout({ children }: { children: ReactNode }) {\n const lattice = usePage().props.lattice as PagePayload;\n const layout = lattice.layout;\n\n if (!layout) {\n return <>{children}</>;\n }\n\n return (\n <OutletContext.Provider value={children}>\n <Renderer nodes={layout.schema} />\n </OutletContext.Provider>\n );\n}\n"],"mappings":";;;;;;;;;;;AAYA,SAAwB,aAAa,EAAE,YAAqC;CAE1E,MAAM,SADU,QAAQ,CAAC,CAAC,MAAM,QACT;CAEvB,IAAI,CAAC,QACH,OAAO,oBAAA,UAAA,EAAG,SAAW,CAAA;CAGvB,OACE,oBAAC,cAAc,UAAf;EAAwB,OAAO;EAC7B,UAAA,oBAAC,UAAD,EAAU,OAAO,OAAO,OAAS,CAAA;CACX,CAAA;AAE5B"}
1
+ {"version":3,"file":"schema-layout.js","names":[],"sources":["../../../resources/js/layout/components/schema-layout.tsx"],"sourcesContent":["import { usePage } from \"@inertiajs/react\";\nimport type { ReactNode } from \"react\";\nimport { Renderer } from \"@lattice-php/core/renderer\";\nimport type { PagePayload } from \"@lattice-php/lattice/types\";\nimport { OutletProvider } from \"@lattice-php/lattice/layout/hooks/context\";\n\n/**\n * Persistent Inertia layout that renders a server-composed layout schema and\n * exposes the active page (`children`) to the Outlet via context. The same\n * component instance is reused across navigations that share a layout, so\n * shell state (open sidebar, scroll position) survives page visits.\n */\nexport default function SchemaLayout({ children }: { children: ReactNode }) {\n const lattice = usePage().props.lattice as PagePayload;\n const layout = lattice.layout;\n\n if (!layout) {\n return <>{children}</>;\n }\n\n return (\n <OutletProvider outlet={children}>\n <Renderer nodes={layout.schema} />\n </OutletProvider>\n );\n}\n"],"mappings":";;;;;;;;;;;AAYA,SAAwB,aAAa,EAAE,YAAqC;CAE1E,MAAM,SADU,QAAQ,CAAC,CAAC,MAAM,QACT;CAEvB,IAAI,CAAC,QACH,OAAO,oBAAA,UAAA,EAAG,SAAW,CAAA;CAGvB,OACE,oBAAC,gBAAD;EAAgB,QAAQ;EACtB,UAAA,oBAAC,UAAD,EAAU,OAAO,OAAO,OAAS,CAAA;CACnB,CAAA;AAEpB"}
@@ -4,10 +4,10 @@ import { LATTICE_EVENT } from "@lattice-php/core/event-names";
4
4
  import { cn } from "@lattice-php/ui/lib/utils";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  import { nodeIdentity } from "@lattice-php/core/test-id";
7
- import { CollapsedContext } from "@lattice-php/core/collapsed-context";
7
+ import { CollapsedProvider } from "@lattice-php/core/collapsed-context";
8
8
  import { useWindowEvent } from "@lattice-php/core/hooks/use-window-event";
9
9
  import { useMediaQuery } from "@lattice-php/ui/lib/use-media-query";
10
- import { useCollapsibleState } from "@lattice-php/ui/use-collapsible-state";
10
+ import { useCollapsibleState } from "@lattice-php/ui/lib/use-collapsible-state";
11
11
  //#region resources/js/layout/components/sidebar.tsx
12
12
  var DESKTOP_QUERY = "(min-width: 768px)";
13
13
  function matchesTarget(event, identity) {
@@ -33,8 +33,8 @@ var SidebarComponent = ({ children, node }) => {
33
33
  if (event.key === "Escape") setMobileOpen(false);
34
34
  }, { enabled: mobileOpen });
35
35
  const isCollapsed = collapsible && collapsed && isDesktop;
36
- return /* @__PURE__ */ jsxs(CollapsedContext.Provider, {
37
- value: isCollapsed,
36
+ return /* @__PURE__ */ jsxs(CollapsedProvider, {
37
+ collapsed: isCollapsed,
38
38
  children: [mobileOpen ? /* @__PURE__ */ jsx("div", {
39
39
  "aria-hidden": "true",
40
40
  className: "fixed inset-0 z-lt-overlay bg-lt-overlay md:hidden",
@@ -1 +1 @@
1
- {"version":3,"file":"sidebar.js","names":[],"sources":["../../../resources/js/layout/components/sidebar.tsx"],"sourcesContent":["import { router } from \"@inertiajs/react\";\nimport { useEffect, useState } from \"react\";\nimport type { RendererComponent } from \"@lattice-php/core/types\";\nimport { CollapsedContext } from \"@lattice-php/core/collapsed-context\";\nimport { LATTICE_EVENT } from \"@lattice-php/core/event-names\";\nimport { useWindowEvent } from \"@lattice-php/core/hooks/use-window-event\";\nimport { nodeIdentity } from \"@lattice-php/core/test-id\";\nimport { cn } from \"@lattice-php/ui/lib/utils\";\nimport { useMediaQuery } from \"@lattice-php/ui/lib/use-media-query\";\nimport { useCollapsibleState } from \"@lattice-php/ui/use-collapsible-state\";\n\nconst DESKTOP_QUERY = \"(min-width: 768px)\";\n\nfunction matchesTarget(event: Event, identity: string | undefined): boolean {\n const target = (event as CustomEvent<{ target?: string }>).detail?.target;\n\n return target == null || target === identity;\n}\n\nconst SidebarComponent: RendererComponent<\"sidebar\"> = ({ children, node }) => {\n const collapsible = node.props.collapsible;\n const rememberState = node.props.rememberState;\n const identity = nodeIdentity(node);\n const storageKey = `lattice:sidebar:${identity ?? \"default\"}`;\n const isDesktop = useMediaQuery(DESKTOP_QUERY, true);\n\n const [collapsed, toggleCollapsed] = useCollapsibleState(\n storageKey,\n false,\n collapsible && rememberState,\n );\n const [mobileOpen, setMobileOpen] = useState(false);\n\n useWindowEvent(LATTICE_EVENT.toggleSidebar, (event) => {\n if (!matchesTarget(event, identity)) {\n return;\n }\n\n if (window.matchMedia?.(DESKTOP_QUERY).matches ?? true) {\n if (collapsible) {\n toggleCollapsed();\n }\n } else {\n setMobileOpen((open) => !open);\n }\n });\n\n useEffect(() => router.on(\"navigate\", () => setMobileOpen(false)), []);\n\n useWindowEvent(\n \"keydown\",\n (event) => {\n if ((event as KeyboardEvent).key === \"Escape\") {\n setMobileOpen(false);\n }\n },\n { enabled: mobileOpen },\n );\n\n const isCollapsed = collapsible && collapsed && isDesktop;\n\n return (\n <CollapsedContext.Provider value={isCollapsed}>\n {mobileOpen ? (\n <div\n aria-hidden=\"true\"\n className=\"fixed inset-0 z-lt-overlay bg-lt-overlay md:hidden\"\n data-test=\"sidebar-backdrop\"\n onClick={() => setMobileOpen(false)}\n />\n ) : null}\n <aside\n className={cn(\n \"fixed inset-y-0 left-0 z-lt-modal flex h-svh w-72 max-w-[80vw] shrink-0 flex-col gap-4 border-r border-lt-border bg-lt-bg p-4 transition-transform\",\n \"md:sticky md:top-0 md:z-auto md:max-w-none md:translate-x-0 md:transition-[width]\",\n mobileOpen ? \"translate-x-0\" : \"-translate-x-full\",\n isCollapsed\n ? \"md:w-16 md:overflow-visible\"\n : \"md:w-64 md:overflow-x-hidden md:overflow-y-auto\",\n )}\n data-collapsed={isCollapsed ? \"true\" : \"false\"}\n data-lattice-component={identity}\n data-test=\"sidebar\"\n >\n {children}\n </aside>\n </CollapsedContext.Provider>\n );\n};\n\nexport default SidebarComponent;\n"],"mappings":";;;;;;;;;;;AAWA,IAAM,gBAAgB;AAEtB,SAAS,cAAc,OAAc,UAAuC;CAC1E,MAAM,SAAU,MAA2C,QAAQ;CAEnE,OAAO,UAAU,QAAQ,WAAW;AACtC;AAEA,IAAM,oBAAkD,EAAE,UAAU,WAAW;CAC7E,MAAM,cAAc,KAAK,MAAM;CAC/B,MAAM,gBAAgB,KAAK,MAAM;CACjC,MAAM,WAAW,aAAa,IAAI;CAClC,MAAM,aAAa,mBAAmB,YAAY;CAClD,MAAM,YAAY,cAAc,eAAe,IAAI;CAEnD,MAAM,CAAC,WAAW,mBAAmB,oBACnC,YACA,OACA,eAAe,aACjB;CACA,MAAM,CAAC,YAAY,iBAAiB,SAAS,KAAK;CAElD,eAAe,cAAc,gBAAgB,UAAU;EACrD,IAAI,CAAC,cAAc,OAAO,QAAQ,GAChC;EAGF,IAAI,OAAO,aAAa,aAAa,CAAC,CAAC,WAAW,MAC5C;OAAA,aACF,gBAAgB;EAAA,OAGlB,eAAe,SAAS,CAAC,IAAI;CAEjC,CAAC;CAED,gBAAgB,OAAO,GAAG,kBAAkB,cAAc,KAAK,CAAC,GAAG,CAAC,CAAC;CAErE,eACE,YACC,UAAU;EACT,IAAK,MAAwB,QAAQ,UACnC,cAAc,KAAK;CAEvB,GACA,EAAE,SAAS,WAAW,CACxB;CAEA,MAAM,cAAc,eAAe,aAAa;CAEhD,OACE,qBAAC,iBAAiB,UAAlB;EAA2B,OAAO;EAAlC,UAAA,CACG,aACC,oBAAC,OAAD;GACE,eAAY;GACZ,WAAU;GACV,aAAU;GACV,eAAe,cAAc,KAAK;EACnC,CAAA,IACC,MACJ,oBAAC,SAAD;GACE,WAAW,GACT,sJACA,qFACA,aAAa,kBAAkB,qBAC/B,cACI,gCACA,iDACN;GACA,kBAAgB,cAAc,SAAS;GACvC,0BAAwB;GACxB,aAAU;GAET;EACI,CAAA,CACkB;;AAE/B"}
1
+ {"version":3,"file":"sidebar.js","names":[],"sources":["../../../resources/js/layout/components/sidebar.tsx"],"sourcesContent":["import { router } from \"@inertiajs/react\";\nimport { useEffect, useState } from \"react\";\nimport type { RendererComponent } from \"@lattice-php/core/types\";\nimport { CollapsedProvider } from \"@lattice-php/core/collapsed-context\";\nimport { LATTICE_EVENT } from \"@lattice-php/core/event-names\";\nimport { useWindowEvent } from \"@lattice-php/core/hooks/use-window-event\";\nimport { nodeIdentity } from \"@lattice-php/core/test-id\";\nimport { cn } from \"@lattice-php/ui/lib/utils\";\nimport { useMediaQuery } from \"@lattice-php/ui/lib/use-media-query\";\nimport { useCollapsibleState } from \"@lattice-php/ui/lib/use-collapsible-state\";\n\nconst DESKTOP_QUERY = \"(min-width: 768px)\";\n\nfunction matchesTarget(event: Event, identity: string | undefined): boolean {\n const target = (event as CustomEvent<{ target?: string }>).detail?.target;\n\n return target == null || target === identity;\n}\n\nconst SidebarComponent: RendererComponent<\"sidebar\"> = ({ children, node }) => {\n const collapsible = node.props.collapsible;\n const rememberState = node.props.rememberState;\n const identity = nodeIdentity(node);\n const storageKey = `lattice:sidebar:${identity ?? \"default\"}`;\n const isDesktop = useMediaQuery(DESKTOP_QUERY, true);\n\n const [collapsed, toggleCollapsed] = useCollapsibleState(\n storageKey,\n false,\n collapsible && rememberState,\n );\n const [mobileOpen, setMobileOpen] = useState(false);\n\n useWindowEvent(LATTICE_EVENT.toggleSidebar, (event) => {\n if (!matchesTarget(event, identity)) {\n return;\n }\n\n if (window.matchMedia?.(DESKTOP_QUERY).matches ?? true) {\n if (collapsible) {\n toggleCollapsed();\n }\n } else {\n setMobileOpen((open) => !open);\n }\n });\n\n useEffect(() => router.on(\"navigate\", () => setMobileOpen(false)), []);\n\n useWindowEvent(\n \"keydown\",\n (event) => {\n if ((event as KeyboardEvent).key === \"Escape\") {\n setMobileOpen(false);\n }\n },\n { enabled: mobileOpen },\n );\n\n const isCollapsed = collapsible && collapsed && isDesktop;\n\n return (\n <CollapsedProvider collapsed={isCollapsed}>\n {mobileOpen ? (\n <div\n aria-hidden=\"true\"\n className=\"fixed inset-0 z-lt-overlay bg-lt-overlay md:hidden\"\n data-test=\"sidebar-backdrop\"\n onClick={() => setMobileOpen(false)}\n />\n ) : null}\n <aside\n className={cn(\n \"fixed inset-y-0 left-0 z-lt-modal flex h-svh w-72 max-w-[80vw] shrink-0 flex-col gap-4 border-r border-lt-border bg-lt-bg p-4 transition-transform\",\n \"md:sticky md:top-0 md:z-auto md:max-w-none md:translate-x-0 md:transition-[width]\",\n mobileOpen ? \"translate-x-0\" : \"-translate-x-full\",\n isCollapsed\n ? \"md:w-16 md:overflow-visible\"\n : \"md:w-64 md:overflow-x-hidden md:overflow-y-auto\",\n )}\n data-collapsed={isCollapsed ? \"true\" : \"false\"}\n data-lattice-component={identity}\n data-test=\"sidebar\"\n >\n {children}\n </aside>\n </CollapsedProvider>\n );\n};\n\nexport default SidebarComponent;\n"],"mappings":";;;;;;;;;;;AAWA,IAAM,gBAAgB;AAEtB,SAAS,cAAc,OAAc,UAAuC;CAC1E,MAAM,SAAU,MAA2C,QAAQ;CAEnE,OAAO,UAAU,QAAQ,WAAW;AACtC;AAEA,IAAM,oBAAkD,EAAE,UAAU,WAAW;CAC7E,MAAM,cAAc,KAAK,MAAM;CAC/B,MAAM,gBAAgB,KAAK,MAAM;CACjC,MAAM,WAAW,aAAa,IAAI;CAClC,MAAM,aAAa,mBAAmB,YAAY;CAClD,MAAM,YAAY,cAAc,eAAe,IAAI;CAEnD,MAAM,CAAC,WAAW,mBAAmB,oBACnC,YACA,OACA,eAAe,aACjB;CACA,MAAM,CAAC,YAAY,iBAAiB,SAAS,KAAK;CAElD,eAAe,cAAc,gBAAgB,UAAU;EACrD,IAAI,CAAC,cAAc,OAAO,QAAQ,GAChC;EAGF,IAAI,OAAO,aAAa,aAAa,CAAC,CAAC,WAAW,MAC5C;OAAA,aACF,gBAAgB;EAAA,OAGlB,eAAe,SAAS,CAAC,IAAI;CAEjC,CAAC;CAED,gBAAgB,OAAO,GAAG,kBAAkB,cAAc,KAAK,CAAC,GAAG,CAAC,CAAC;CAErE,eACE,YACC,UAAU;EACT,IAAK,MAAwB,QAAQ,UACnC,cAAc,KAAK;CAEvB,GACA,EAAE,SAAS,WAAW,CACxB;CAEA,MAAM,cAAc,eAAe,aAAa;CAEhD,OACE,qBAAC,mBAAD;EAAmB,WAAW;EAA9B,UAAA,CACG,aACC,oBAAC,OAAD;GACE,eAAY;GACZ,WAAU;GACV,aAAU;GACV,eAAe,cAAc,KAAK;EACnC,CAAA,IACC,MACJ,oBAAC,SAAD;GACE,WAAW,GACT,sJACA,qFACA,aAAa,kBAAkB,qBAC/B,cACI,gCACA,iDACN;GACA,kBAAgB,cAAc,SAAS;GACvC,0BAAwB;GACxB,aAAU;GAET;EACI,CAAA,CACU;;AAEvB"}
@@ -1,4 +1,6 @@
1
1
  import { ReactNode } from 'react';
2
- import { CollapsedContext as SidebarCollapsedContext, useCollapsed as useSidebarCollapsed } from '@lattice-php/core/collapsed-context';
3
- export declare const OutletContext: import('react').Context<ReactNode>;
4
- export { SidebarCollapsedContext, useSidebarCollapsed };
2
+ export declare function OutletProvider({ outlet, children }: {
3
+ outlet: ReactNode;
4
+ children: ReactNode;
5
+ }): import("react").JSX.Element;
6
+ export declare function useOutlet(): ReactNode;
@@ -1,8 +1,17 @@
1
- import { createContext } from "react";
2
- import { CollapsedContext as SidebarCollapsedContext, useCollapsed as useSidebarCollapsed } from "@lattice-php/core/collapsed-context";
3
- //#region resources/js/layout/hooks/context.ts
1
+ import { createContext, useContext } from "react";
2
+ import { jsx } from "react/jsx-runtime";
3
+ //#region resources/js/layout/hooks/context.tsx
4
4
  var OutletContext = createContext(null);
5
+ function OutletProvider({ outlet, children }) {
6
+ return /* @__PURE__ */ jsx(OutletContext.Provider, {
7
+ value: outlet,
8
+ children
9
+ });
10
+ }
11
+ function useOutlet() {
12
+ return useContext(OutletContext);
13
+ }
5
14
  //#endregion
6
- export { OutletContext, SidebarCollapsedContext, useSidebarCollapsed };
15
+ export { OutletProvider, useOutlet };
7
16
 
8
17
  //# sourceMappingURL=context.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","names":[],"sources":["../../../resources/js/layout/hooks/context.ts"],"sourcesContent":["import { createContext } from \"react\";\nimport type { ReactNode } from \"react\";\nimport {\n CollapsedContext as SidebarCollapsedContext,\n useCollapsed as useSidebarCollapsed,\n} from \"@lattice-php/core/collapsed-context\";\n\nexport const OutletContext = createContext<ReactNode>(null);\n\nexport { SidebarCollapsedContext, useSidebarCollapsed };\n"],"mappings":";;;AAOA,IAAa,gBAAgB,cAAyB,IAAI"}
1
+ {"version":3,"file":"context.js","names":[],"sources":["../../../resources/js/layout/hooks/context.tsx"],"sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { ReactNode } from \"react\";\n\nconst OutletContext = createContext<ReactNode>(null);\n\nexport function OutletProvider({ outlet, children }: { outlet: ReactNode; children: ReactNode }) {\n return <OutletContext.Provider value={outlet}>{children}</OutletContext.Provider>;\n}\n\nexport function useOutlet(): ReactNode {\n return useContext(OutletContext);\n}\n"],"mappings":";;;AAGA,IAAM,gBAAgB,cAAyB,IAAI;AAEnD,SAAgB,eAAe,EAAE,QAAQ,YAAwD;CAC/F,OAAO,oBAAC,cAAc,UAAf;EAAwB,OAAO;EAAS;CAAiC,CAAA;AAClF;AAEA,SAAgB,YAAuB;CACrC,OAAO,WAAW,aAAa;AACjC"}
@@ -1,4 +1,4 @@
1
1
  export { default as Callouts } from './components/callouts.js';
2
2
  export { layoutComponents } from './plugin.js';
3
- export { OutletContext, SidebarCollapsedContext, useSidebarCollapsed } from './hooks/context.js';
3
+ export { OutletProvider, useOutlet } from './hooks/context.js';
4
4
  export { default as SchemaLayout } from './components/schema-layout.js';
@@ -1,5 +1,5 @@
1
1
  import Callouts from "./components/callouts.js";
2
- import { OutletContext, SidebarCollapsedContext, useSidebarCollapsed } from "./hooks/context.js";
2
+ import { OutletProvider, useOutlet } from "./hooks/context.js";
3
3
  import { layoutComponents } from "./plugin.js";
4
4
  import SchemaLayout from "./components/schema-layout.js";
5
- export { Callouts, OutletContext, SchemaLayout, SidebarCollapsedContext, layoutComponents, useSidebarCollapsed };
5
+ export { Callouts, OutletProvider, SchemaLayout, layoutComponents, useOutlet };
@@ -2,7 +2,7 @@ import { EmptyState } from "./empty-state.js";
2
2
  import { NotificationItemRow } from "./notification-item.js";
3
3
  import { useT } from "@lattice-php/ui/i18n";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
- import { Skeleton } from "@lattice-php/ui/skeleton";
5
+ import { Skeleton } from "@lattice-php/ui/primitives/skeleton";
6
6
  //#region resources/js/notifications/components/notification-list.tsx
7
7
  function NotificationList({ notifications, status, hasMore, onMarkRead, onDismiss, onLoadMore }) {
8
8
  const { t } = useT("lattice");
@@ -1 +1 @@
1
- {"version":3,"file":"notification-list.js","names":[],"sources":["../../../resources/js/notifications/components/notification-list.tsx"],"sourcesContent":["import { Skeleton } from \"@lattice-php/ui/skeleton\";\nimport { useT } from \"@lattice-php/ui/i18n\";\nimport type { NotificationItem } from \"@lattice-php/lattice/notifications/types\";\nimport type { NotificationsStatus } from \"@lattice-php/lattice/notifications/store\";\nimport { EmptyState } from \"./empty-state\";\nimport { NotificationItemRow } from \"./notification-item\";\n\nexport function NotificationList({\n notifications,\n status,\n hasMore,\n onMarkRead,\n onDismiss,\n onLoadMore,\n}: {\n notifications: NotificationItem[];\n status: NotificationsStatus;\n hasMore: boolean;\n onMarkRead: (id: string) => void;\n onDismiss: (id: string) => void;\n onLoadMore: () => void;\n}) {\n const { t } = useT(\"lattice\");\n\n if (status === \"loading\" && notifications.length === 0) {\n return (\n <div className=\"space-y-2 p-3\">\n <Skeleton className=\"h-12 w-full\" />\n <Skeleton className=\"h-12 w-full\" />\n <Skeleton className=\"h-12 w-full\" />\n </div>\n );\n }\n\n if (notifications.length === 0) {\n return <EmptyState />;\n }\n\n return (\n <div className=\"max-h-96 overflow-y-auto\">\n <ul className=\"divide-y divide-lt-border\">\n {notifications.map((notification) => (\n <NotificationItemRow\n key={notification.id}\n notification={notification}\n onMarkRead={onMarkRead}\n onDismiss={onDismiss}\n />\n ))}\n </ul>\n {hasMore ? (\n <button\n type=\"button\"\n className=\"w-full py-2 text-sm text-lt-muted-fg hover:text-lt-fg\"\n onClick={onLoadMore}\n >\n {t(\"notifications.load-more\", \"Load more\")}\n </button>\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;;;;AAOA,SAAgB,iBAAiB,EAC/B,eACA,QACA,SACA,YACA,WACA,cAQC;CACD,MAAM,EAAE,MAAM,KAAK,SAAS;CAE5B,IAAI,WAAW,aAAa,cAAc,WAAW,GACnD,OACE,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GACE,oBAAC,UAAD,EAAU,WAAU,cAAe,CAAA;GACnC,oBAAC,UAAD,EAAU,WAAU,cAAe,CAAA;GACnC,oBAAC,UAAD,EAAU,WAAU,cAAe,CAAA;EAChC;;CAIT,IAAI,cAAc,WAAW,GAC3B,OAAO,oBAAC,YAAD,CAAa,CAAA;CAGtB,OACE,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,oBAAC,MAAD;GAAI,WAAU;GACX,UAAA,cAAc,KAAK,iBAClB,oBAAC,qBAAD;IAEgB;IACF;IACD;GACZ,GAJM,aAAa,EAInB,CACF;EACC,CAAA,GACH,UACC,oBAAC,UAAD;GACE,MAAK;GACL,WAAU;GACV,SAAS;GAER,UAAA,EAAE,2BAA2B,WAAW;EACnC,CAAA,IACN,IACD;;AAET"}
1
+ {"version":3,"file":"notification-list.js","names":[],"sources":["../../../resources/js/notifications/components/notification-list.tsx"],"sourcesContent":["import { Skeleton } from \"@lattice-php/ui/primitives/skeleton\";\nimport { useT } from \"@lattice-php/ui/i18n\";\nimport type { NotificationItem } from \"@lattice-php/lattice/notifications/types\";\nimport type { NotificationsStatus } from \"@lattice-php/lattice/notifications/store\";\nimport { EmptyState } from \"./empty-state\";\nimport { NotificationItemRow } from \"./notification-item\";\n\nexport function NotificationList({\n notifications,\n status,\n hasMore,\n onMarkRead,\n onDismiss,\n onLoadMore,\n}: {\n notifications: NotificationItem[];\n status: NotificationsStatus;\n hasMore: boolean;\n onMarkRead: (id: string) => void;\n onDismiss: (id: string) => void;\n onLoadMore: () => void;\n}) {\n const { t } = useT(\"lattice\");\n\n if (status === \"loading\" && notifications.length === 0) {\n return (\n <div className=\"space-y-2 p-3\">\n <Skeleton className=\"h-12 w-full\" />\n <Skeleton className=\"h-12 w-full\" />\n <Skeleton className=\"h-12 w-full\" />\n </div>\n );\n }\n\n if (notifications.length === 0) {\n return <EmptyState />;\n }\n\n return (\n <div className=\"max-h-96 overflow-y-auto\">\n <ul className=\"divide-y divide-lt-border\">\n {notifications.map((notification) => (\n <NotificationItemRow\n key={notification.id}\n notification={notification}\n onMarkRead={onMarkRead}\n onDismiss={onDismiss}\n />\n ))}\n </ul>\n {hasMore ? (\n <button\n type=\"button\"\n className=\"w-full py-2 text-sm text-lt-muted-fg hover:text-lt-fg\"\n onClick={onLoadMore}\n >\n {t(\"notifications.load-more\", \"Load more\")}\n </button>\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;;;;AAOA,SAAgB,iBAAiB,EAC/B,eACA,QACA,SACA,YACA,WACA,cAQC;CACD,MAAM,EAAE,MAAM,KAAK,SAAS;CAE5B,IAAI,WAAW,aAAa,cAAc,WAAW,GACnD,OACE,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GACE,oBAAC,UAAD,EAAU,WAAU,cAAe,CAAA;GACnC,oBAAC,UAAD,EAAU,WAAU,cAAe,CAAA;GACnC,oBAAC,UAAD,EAAU,WAAU,cAAe,CAAA;EAChC;;CAIT,IAAI,cAAc,WAAW,GAC3B,OAAO,oBAAC,YAAD,CAAa,CAAA;CAGtB,OACE,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,oBAAC,MAAD;GAAI,WAAU;GACX,UAAA,cAAc,KAAK,iBAClB,oBAAC,qBAAD;IAEgB;IACF;IACD;GACZ,GAJM,aAAa,EAInB,CACF;EACC,CAAA,GACH,UACC,oBAAC,UAAD;GACE,MAAK;GACL,WAAU;GACV,SAAS;GAER,UAAA,EAAE,2BAA2B,WAAW;EACnC,CAAA,IACN,IACD;;AAET"}
@@ -0,0 +1,3 @@
1
+ export declare function NotificationsSheet({ onClosed }: {
2
+ onClosed: () => void;
3
+ }): import("react").JSX.Element;
@@ -0,0 +1,59 @@
1
+ import { useLiveNotifications } from "../context.js";
2
+ import { NotificationList } from "./notification-list.js";
3
+ import { useT } from "@lattice-php/ui/i18n";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { MODAL_MISSING_ERROR, useEmbeddedModal } from "@lattice-php/ui/modal";
6
+ import { Dialog, DialogContent, DialogTitle } from "@lattice-php/ui/primitives/dialog";
7
+ //#region resources/js/notifications/components/notifications-sheet.tsx
8
+ function NotificationsSheet({ onClosed }) {
9
+ const context = useEmbeddedModal();
10
+ if (!context) throw new Error(MODAL_MISSING_ERROR);
11
+ const store = useLiveNotifications();
12
+ const { t } = useT("lattice");
13
+ const label = t("notifications.label", "Notifications");
14
+ const heading = t("notifications.heading", "Notifications");
15
+ return /* @__PURE__ */ jsx(Dialog, {
16
+ open: context.open,
17
+ onOpenChange: context.onOpenChange,
18
+ children: /* @__PURE__ */ jsxs(DialogContent, {
19
+ "aria-describedby": void 0,
20
+ className: "p-0",
21
+ onCloseAutoFocus: (event) => {
22
+ context.onExited(event);
23
+ onClosed();
24
+ },
25
+ placement: "end",
26
+ width: "sm",
27
+ children: [/* @__PURE__ */ jsx(DialogTitle, {
28
+ className: "sr-only",
29
+ children: label
30
+ }), /* @__PURE__ */ jsxs("div", {
31
+ className: "w-full",
32
+ "data-test": "notifications-panel",
33
+ children: [/* @__PURE__ */ jsxs("div", {
34
+ className: "flex items-center justify-between border-b border-lt-border px-3 py-2",
35
+ children: [/* @__PURE__ */ jsx("span", {
36
+ className: "text-sm font-medium",
37
+ children: heading
38
+ }), store.unreadCount > 0 ? /* @__PURE__ */ jsx("button", {
39
+ type: "button",
40
+ className: "text-xs text-lt-muted-fg hover:text-lt-fg",
41
+ onClick: store.markAllRead,
42
+ children: t("notifications.mark-all-read", "Mark all read")
43
+ }) : null]
44
+ }), /* @__PURE__ */ jsx(NotificationList, {
45
+ notifications: store.notifications,
46
+ status: store.status,
47
+ hasMore: store.hasMore,
48
+ onMarkRead: store.markRead,
49
+ onDismiss: store.dismiss,
50
+ onLoadMore: store.loadMore
51
+ })]
52
+ })]
53
+ })
54
+ });
55
+ }
56
+ //#endregion
57
+ export { NotificationsSheet };
58
+
59
+ //# sourceMappingURL=notifications-sheet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifications-sheet.js","names":[],"sources":["../../../resources/js/notifications/components/notifications-sheet.tsx"],"sourcesContent":["import { Dialog, DialogContent, DialogTitle } from \"@lattice-php/ui/primitives/dialog\";\nimport { useT } from \"@lattice-php/ui/i18n\";\nimport { MODAL_MISSING_ERROR, useEmbeddedModal } from \"@lattice-php/ui/modal\";\nimport { useLiveNotifications } from \"../context\";\nimport { NotificationList } from \"./notification-list\";\n\nexport function NotificationsSheet({ onClosed }: { onClosed: () => void }) {\n const context = useEmbeddedModal();\n\n if (!context) {\n throw new Error(MODAL_MISSING_ERROR);\n }\n\n const store = useLiveNotifications();\n const { t } = useT(\"lattice\");\n const label = t(\"notifications.label\", \"Notifications\");\n const heading = t(\"notifications.heading\", \"Notifications\");\n\n return (\n <Dialog open={context.open} onOpenChange={context.onOpenChange}>\n <DialogContent\n aria-describedby={undefined}\n className=\"p-0\"\n onCloseAutoFocus={(event) => {\n context.onExited(event);\n onClosed();\n }}\n placement=\"end\"\n width=\"sm\"\n >\n <DialogTitle className=\"sr-only\">{label}</DialogTitle>\n <div className=\"w-full\" data-test=\"notifications-panel\">\n <div className=\"flex items-center justify-between border-b border-lt-border px-3 py-2\">\n <span className=\"text-sm font-medium\">{heading}</span>\n {store.unreadCount > 0 ? (\n <button\n type=\"button\"\n className=\"text-xs text-lt-muted-fg hover:text-lt-fg\"\n onClick={store.markAllRead}\n >\n {t(\"notifications.mark-all-read\", \"Mark all read\")}\n </button>\n ) : null}\n </div>\n <NotificationList\n notifications={store.notifications}\n status={store.status}\n hasMore={store.hasMore}\n onMarkRead={store.markRead}\n onDismiss={store.dismiss}\n onLoadMore={store.loadMore}\n />\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":";;;;;;;AAMA,SAAgB,mBAAmB,EAAE,YAAsC;CACzE,MAAM,UAAU,iBAAiB;CAEjC,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,mBAAmB;CAGrC,MAAM,QAAQ,qBAAqB;CACnC,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,QAAQ,EAAE,uBAAuB,eAAe;CACtD,MAAM,UAAU,EAAE,yBAAyB,eAAe;CAE1D,OACE,oBAAC,QAAD;EAAQ,MAAM,QAAQ;EAAM,cAAc,QAAQ;EAChD,UAAA,qBAAC,eAAD;GACE,oBAAkB,KAAA;GAClB,WAAU;GACV,mBAAmB,UAAU;IAC3B,QAAQ,SAAS,KAAK;IACtB,SAAS;GACX;GACA,WAAU;GACV,OAAM;GARR,UAAA,CAUE,oBAAC,aAAD;IAAa,WAAU;IAAW,UAAA;GAAmB,CAAA,GACrD,qBAAC,OAAD;IAAK,WAAU;IAAS,aAAU;IAAlC,UAAA,CACE,qBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,oBAAC,QAAD;MAAM,WAAU;MAAuB,UAAA;KAAc,CAAA,GACpD,MAAM,cAAc,IACnB,oBAAC,UAAD;MACE,MAAK;MACL,WAAU;MACV,SAAS,MAAM;MAEd,UAAA,EAAE,+BAA+B,eAAe;KAC3C,CAAA,IACN,IACD;IACL,CAAA,GAAA,oBAAC,kBAAD;KACE,eAAe,MAAM;KACrB,QAAQ,MAAM;KACd,SAAS,MAAM;KACf,YAAY,MAAM;KAClB,WAAW,MAAM;KACjB,YAAY,MAAM;IACnB,CAAA,CACE;GACQ,CAAA,CAAA;;CACT,CAAA;AAEZ"}