@lattice-php/lattice 0.20.0 → 0.22.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 (212) hide show
  1. package/README.md +10 -0
  2. package/dist/action/components/action-form.d.ts +6 -1
  3. package/dist/action/components/action-form.js +48 -58
  4. package/dist/action/components/action-form.js.map +1 -1
  5. package/dist/action/components/action-trigger-provider.d.ts +1 -1
  6. package/dist/action/components/action-trigger-provider.js.map +1 -1
  7. package/dist/action/components/action.js +2 -1
  8. package/dist/action/components/action.js.map +1 -1
  9. package/dist/action/hooks/use-action.d.ts +1 -1
  10. package/dist/action/hooks/use-action.js +42 -33
  11. package/dist/action/hooks/use-action.js.map +1 -1
  12. package/dist/action/index.d.ts +0 -2
  13. package/dist/action/index.js +1 -2
  14. package/dist/action/lib/action-label.d.ts +2 -0
  15. package/dist/action/lib/action-label.js +9 -0
  16. package/dist/action/lib/action-label.js.map +1 -0
  17. package/dist/action/lib/run-action.d.ts +7 -7
  18. package/dist/action/lib/run-action.js +8 -7
  19. package/dist/action/lib/run-action.js.map +1 -1
  20. package/dist/chat/plugin.d.ts +0 -1
  21. package/dist/chat/plugin.js.map +1 -1
  22. package/dist/clipboard/index.d.ts +0 -4
  23. package/dist/clipboard/index.js +1 -15
  24. package/dist/{events → core}/event-names.js +1 -1
  25. package/dist/core/event-names.js.map +1 -0
  26. package/dist/core/hooks/use-window-event.d.ts +8 -0
  27. package/dist/core/hooks/use-window-event.js +22 -0
  28. package/dist/core/hooks/use-window-event.js.map +1 -0
  29. package/dist/core/index.js +3 -3
  30. package/dist/core/types.d.ts +8 -73
  31. package/dist/create-app.js +1 -1
  32. package/dist/effects/dispatch.js +1 -1
  33. package/dist/effects/dispatch.js.map +1 -1
  34. package/dist/effects/registry.d.ts +2 -18
  35. package/dist/effects/registry.js +4 -8
  36. package/dist/effects/registry.js.map +1 -1
  37. package/dist/event-bridge.js +10 -23
  38. package/dist/event-bridge.js.map +1 -1
  39. package/dist/form/components/fields/date-picker-field.js +1 -1
  40. package/dist/form/components/fields/file-upload.js +4 -6
  41. package/dist/form/components/fields/file-upload.js.map +1 -1
  42. package/dist/form/components/fields/select.js +2 -2
  43. package/dist/form/components/fields/select.js.map +1 -1
  44. package/dist/form/components/fields/table-rows.js +3 -15
  45. package/dist/form/components/fields/table-rows.js.map +1 -1
  46. package/dist/form/components/form.js +11 -24
  47. package/dist/form/components/form.js.map +1 -1
  48. package/dist/form/embed.d.ts +4 -0
  49. package/dist/form/embed.js +3 -1
  50. package/dist/form/lib/collect-fields.d.ts +7 -0
  51. package/dist/form/lib/collect-fields.js +19 -0
  52. package/dist/form/lib/collect-fields.js.map +1 -0
  53. package/dist/form/lib/field-errors.d.ts +3 -0
  54. package/dist/form/lib/field-errors.js +11 -0
  55. package/dist/form/lib/field-errors.js.map +1 -0
  56. package/dist/format/format-context.d.ts +6 -0
  57. package/dist/format/format-context.js +17 -0
  58. package/dist/format/format-context.js.map +1 -0
  59. package/dist/format/number.d.ts +1 -1
  60. package/dist/format/number.js.map +1 -1
  61. package/dist/format/value.d.ts +1 -1
  62. package/dist/format/value.js.map +1 -1
  63. package/dist/i18n/backend.js +1 -1
  64. package/dist/i18n/index.js +2 -2
  65. package/dist/i18n/instance.js +1 -1
  66. package/dist/i18n/locale-reload.d.ts +1 -1
  67. package/dist/i18n/locale-reload.js +7 -6
  68. package/dist/i18n/locale-reload.js.map +1 -1
  69. package/dist/i18n/locale.js +1 -1
  70. package/dist/i18n/locale.js.map +1 -1
  71. package/dist/i18n/page-props.js +1 -1
  72. package/dist/i18n/shared-props.js +1 -3
  73. package/dist/i18n/shared-props.js.map +1 -1
  74. package/dist/i18n/timezone.js +1 -1
  75. package/dist/i18n/timezone.js.map +1 -1
  76. package/dist/index.d.ts +6 -7
  77. package/dist/index.js +7 -8
  78. package/dist/lattice.css +105 -0
  79. package/dist/layout/components/callouts.js +1 -1
  80. package/dist/layout/components/sidebar.js +15 -48
  81. package/dist/layout/components/sidebar.js.map +1 -1
  82. package/dist/layout/plugin.d.ts +0 -1
  83. package/dist/layout/plugin.js.map +1 -1
  84. package/dist/lib/is-truthy.d.ts +2 -0
  85. package/dist/lib/is-truthy.js +9 -0
  86. package/dist/lib/is-truthy.js.map +1 -0
  87. package/dist/lib/use-debounced-callback.d.ts +10 -0
  88. package/dist/lib/use-debounced-callback.js +34 -0
  89. package/dist/lib/use-debounced-callback.js.map +1 -0
  90. package/dist/lib/use-media-query.d.ts +5 -0
  91. package/dist/lib/use-media-query.js +26 -0
  92. package/dist/lib/use-media-query.js.map +1 -0
  93. package/dist/lib/use-persistent-state.d.ts +13 -0
  94. package/dist/lib/use-persistent-state.js +62 -0
  95. package/dist/lib/use-persistent-state.js.map +1 -0
  96. package/dist/notifications/components/notifications.js +4 -1
  97. package/dist/notifications/components/notifications.js.map +1 -1
  98. package/dist/notifications/plugin.d.ts +0 -1
  99. package/dist/notifications/plugin.js.map +1 -1
  100. package/dist/provider-base.js +1 -1
  101. package/dist/realtime/listeners.d.ts +1 -1
  102. package/dist/realtime/listeners.js +1 -1
  103. package/dist/realtime/listeners.js.map +1 -1
  104. package/dist/registry.d.ts +5 -0
  105. package/dist/registry.js.map +1 -1
  106. package/dist/remote/components/data-list.js +1 -1
  107. package/dist/remote/plugin.d.ts +0 -1
  108. package/dist/remote/plugin.js.map +1 -1
  109. package/dist/table/components/bulk-bar.js +18 -15
  110. package/dist/table/components/bulk-bar.js.map +1 -1
  111. package/dist/table/components/cells/boolean-cell.js +1 -3
  112. package/dist/table/components/cells/boolean-cell.js.map +1 -1
  113. package/dist/table/components/cells/copyable-cell.d.ts +9 -0
  114. package/dist/table/components/cells/copyable-cell.js +17 -0
  115. package/dist/table/components/cells/copyable-cell.js.map +1 -0
  116. package/dist/table/components/cells/money-cell.js +7 -18
  117. package/dist/table/components/cells/money-cell.js.map +1 -1
  118. package/dist/table/components/cells/number-cell.js +14 -27
  119. package/dist/table/components/cells/number-cell.js.map +1 -1
  120. package/dist/table/components/cells/numeric-cell.d.ts +10 -0
  121. package/dist/table/components/cells/numeric-cell.js +25 -0
  122. package/dist/table/components/cells/numeric-cell.js.map +1 -0
  123. package/dist/table/components/cells/stack-cell.js +1 -1
  124. package/dist/table/components/cells/text-cell.js +4 -5
  125. package/dist/table/components/cells/text-cell.js.map +1 -1
  126. package/dist/table/components/filter-controls.js +1 -3
  127. package/dist/table/components/filter-controls.js.map +1 -1
  128. package/dist/table/components/table-cell.js.map +1 -1
  129. package/dist/table/components/table-search.js +4 -7
  130. package/dist/table/components/table-search.js.map +1 -1
  131. package/dist/table/components/table.js +2 -3
  132. package/dist/table/components/table.js.map +1 -1
  133. package/dist/table/hooks/use-column-visibility.js +29 -58
  134. package/dist/table/hooks/use-column-visibility.js.map +1 -1
  135. package/dist/table/hooks/use-table.js +5 -13
  136. package/dist/table/hooks/use-table.js.map +1 -1
  137. package/dist/table/lib/bulk.d.ts +2 -0
  138. package/dist/table/lib/bulk.js +5 -2
  139. package/dist/table/lib/bulk.js.map +1 -1
  140. package/dist/table/lib/query.js +1 -1
  141. package/dist/table/lib/query.js.map +1 -1
  142. package/dist/table/plugin.d.ts +0 -1
  143. package/dist/table/plugin.js.map +1 -1
  144. package/dist/table/types.d.ts +4 -57
  145. package/dist/toast/callout.js +1 -1
  146. package/dist/toast/callout.js.map +1 -1
  147. package/dist/toast/index.d.ts +1 -1
  148. package/dist/toast/index.js +2 -2
  149. package/dist/toast/toast.d.ts +0 -2
  150. package/dist/toast/toast.js +3 -10
  151. package/dist/toast/toast.js.map +1 -1
  152. package/dist/toast/toaster.js +2 -2
  153. package/dist/types/generated.d.ts +224 -80
  154. package/dist/types/index.d.ts +1 -1
  155. package/dist/ui/button.d.ts +2 -2
  156. package/dist/ui/chart-view.js +7 -30
  157. package/dist/ui/chart-view.js.map +1 -1
  158. package/dist/ui/click-behavior.d.ts +4 -4
  159. package/dist/ui/click-behavior.js.map +1 -1
  160. package/dist/ui/collapsible.js +3 -15
  161. package/dist/ui/collapsible.js.map +1 -1
  162. package/dist/ui/combobox.js +6 -3
  163. package/dist/ui/combobox.js.map +1 -1
  164. package/dist/ui/confirm-dialog.js +1 -1
  165. package/dist/ui/confirm-dialog.js.map +1 -1
  166. package/dist/ui/control.d.ts +1 -1
  167. package/dist/ui/dialog.d.ts +6 -2
  168. package/dist/ui/dialog.js +29 -5
  169. package/dist/ui/dialog.js.map +1 -1
  170. package/dist/ui/fragment.js +1 -1
  171. package/dist/ui/fragment.js.map +1 -1
  172. package/dist/ui/icon-button.d.ts +1 -1
  173. package/dist/ui/image-preview.js +1 -1
  174. package/dist/ui/image-preview.js.map +1 -1
  175. package/dist/ui/modal.js +6 -3
  176. package/dist/ui/modal.js.map +1 -1
  177. package/dist/ui/pill.d.ts +2 -0
  178. package/dist/ui/pill.js +10 -0
  179. package/dist/ui/pill.js.map +1 -0
  180. package/dist/ui/plugin.js.map +1 -1
  181. package/dist/ui/progress.js +1 -1
  182. package/dist/ui/section.js +2 -15
  183. package/dist/ui/section.js.map +1 -1
  184. package/dist/ui/segmented-pills.js +2 -1
  185. package/dist/ui/segmented-pills.js.map +1 -1
  186. package/dist/ui/tabs.js +12 -10
  187. package/dist/ui/tabs.js.map +1 -1
  188. package/dist/ui/tree-context.js +11 -13
  189. package/dist/ui/tree-context.js.map +1 -1
  190. package/dist/ui/tree.js +1 -1
  191. package/dist/ui/tree.js.map +1 -1
  192. package/dist/ui/use-collapsible-state.d.ts +7 -0
  193. package/dist/ui/use-collapsible-state.js +21 -0
  194. package/dist/ui/use-collapsible-state.js.map +1 -0
  195. package/dist-standalone/chunks/chart-view-C6R5iPra.js +36 -0
  196. package/dist-standalone/chunks/{date-picker-field-VbH9xrQO.js → date-picker-field-DhhEB-Mh.js} +1 -1
  197. package/dist-standalone/chunks/{page-props-AIa5UR6n.js → page-props-DOfg30l5.js} +1 -1
  198. package/dist-standalone/chunks/{rich-editor-field-DLQTyt36.js → rich-editor-field-DORXTHPw.js} +2 -2
  199. package/dist-standalone/chunks/{subscriptions-DY2tj9Im.js → subscriptions-DK_s_F2g.js} +1 -1
  200. package/dist-standalone/chunks/use-effect-dispatcher-EVZxmfoo.js +97 -0
  201. package/dist-standalone/lattice.css +1 -1
  202. package/dist-standalone/lattice.js +15 -15
  203. package/dist-standalone/manifest.json +8 -8
  204. package/package.json +3 -3
  205. package/dist/clipboard/index.js.map +0 -1
  206. package/dist/events/event-names.js.map +0 -1
  207. package/dist/table/components/table-action-node.d.ts +0 -4
  208. package/dist/table/components/table-action-node.js +0 -24
  209. package/dist/table/components/table-action-node.js.map +0 -1
  210. package/dist-standalone/chunks/chart-view-wJmnVu7L.js +0 -36
  211. package/dist-standalone/chunks/use-effect-dispatcher-feEs0GCP.js +0 -97
  212. /package/dist/{events → core}/event-names.d.ts +0 -0
package/dist/index.js CHANGED
@@ -1,17 +1,16 @@
1
1
  import { initializeTheme, updateAppearance, useAppearance } from "./appearance/index.js";
2
2
  import { createPlugin, createRegistry, eagerComponent, extendRegistry, lazyComponent } from "./core/registry.js";
3
3
  import { useColumnRegistry, useComponentRegistry } from "./core/registry-context.js";
4
- import { LATTICE_EVENT } from "./events/event-names.js";
5
- import { onToast, showToast } from "./toast/toast.js";
6
- import { onCallout } from "./toast/callout.js";
4
+ import { LATTICE_EVENT } from "./core/event-names.js";
5
+ import { LATTICE_REF_HEADER, withRefHeader } from "./core/component-ref.js";
6
+ import { withHeaders } from "./core/headers.js";
7
7
  import { Icon, SpriteProvider } from "./icons/sprite.js";
8
8
  import { IconRenderer, IconRendererProvider } from "./icons/icon-renderer.js";
9
9
  import { CollapsedContext, useCollapsed } from "./core/collapsed-context.js";
10
10
  import { Renderer } from "./core/renderer.js";
11
+ import { onToast } from "./toast/toast.js";
12
+ import { onCallout } from "./toast/callout.js";
11
13
  import { Toaster } from "./toast/toaster.js";
12
- import { EventBridge } from "./event-bridge.js";
13
- import { LATTICE_REF_HEADER, withRefHeader } from "./core/component-ref.js";
14
- import { withHeaders } from "./core/headers.js";
15
14
  import Callouts from "./layout/components/callouts.js";
16
15
  import { builtinEffectHandlers, effectHandler, mergeEffectHandlers } from "./effects/registry.js";
17
16
  import { dispatchActionError, dispatchEffects, getActionEffects, isActionEffect } from "./effects/dispatch.js";
@@ -21,6 +20,7 @@ import { layoutComponents } from "./layout/plugin.js";
21
20
  import SchemaLayout from "./layout/components/schema-layout.js";
22
21
  import { RealtimeListeners } from "./realtime/listeners.js";
23
22
  import { createLayoutResolver, createPageResolver, pageComponentName, withVisitHeaders } from "./inertia.js";
23
+ import { EventBridge } from "./event-bridge.js";
24
24
  import { useChat } from "./chat/hooks/use-chat.js";
25
25
  import { chatComponents } from "./chat/plugin.js";
26
26
  import { copyToClipboard } from "./ui/copyable-text.js";
@@ -28,6 +28,5 @@ import { columnCell } from "./table/registry.js";
28
28
  import { registry } from "./registry.js";
29
29
  import { createLatticeApp } from "./create-app.js";
30
30
  import { registerRichEditorExtension } from "./form/rich-editor/registry.js";
31
- import { useClipboard } from "./clipboard/index.js";
32
31
  import { Provider } from "./provider.js";
33
- export { Callouts, EventBridge, Icon, IconRenderer, IconRendererProvider, LATTICE_EVENT, LATTICE_REF_HEADER, OutletContext, Provider, RealtimeListeners, Renderer, SchemaLayout, CollapsedContext as SidebarCollapsedContext, SpriteProvider, Toaster, builtinEffectHandlers, chatComponents, columnCell, copyToClipboard, createLatticeApp, createLayoutResolver, createPageResolver, createPlugin, createRegistry, dispatchActionError, dispatchEffects, eagerComponent, effectHandler, extendRegistry, getActionEffects, initializeTheme, isActionEffect, layoutComponents, lazyComponent, mergeEffectHandlers, onCallout, onToast, pageComponentName, registerRichEditorExtension, registry, showToast, updateAppearance, useAppearance, useChat, useClipboard, useColumnRegistry, useComponentRegistry, useEffectDispatcher, useCollapsed as useSidebarCollapsed, withHeaders, withRefHeader, withVisitHeaders };
32
+ export { Callouts, EventBridge, Icon, IconRenderer, IconRendererProvider, LATTICE_EVENT, LATTICE_REF_HEADER, OutletContext, Provider, RealtimeListeners, Renderer, SchemaLayout, CollapsedContext as SidebarCollapsedContext, SpriteProvider, Toaster, builtinEffectHandlers, chatComponents, columnCell, copyToClipboard, createLatticeApp, createLayoutResolver, createPageResolver, createPlugin, createRegistry, dispatchActionError, dispatchEffects, eagerComponent, effectHandler, extendRegistry, getActionEffects, initializeTheme, isActionEffect, layoutComponents, lazyComponent, mergeEffectHandlers, onCallout, onToast, pageComponentName, registerRichEditorExtension, registry, updateAppearance, useAppearance, useChat, useColumnRegistry, useComponentRegistry, useEffectDispatcher, useCollapsed as useSidebarCollapsed, withHeaders, withRefHeader, withVisitHeaders };
package/dist/lattice.css CHANGED
@@ -85,6 +85,7 @@
85
85
 
86
86
  --lt-duration-fast: 120ms;
87
87
  --lt-duration-base: 160ms;
88
+ --lt-duration-slow: 240ms;
88
89
 
89
90
  --lt-sidebar-w: 16rem;
90
91
  --lt-sidebar-w-collapsed: 4rem;
@@ -96,6 +97,20 @@
96
97
  color-scheme: light;
97
98
  }
98
99
 
100
+ /*
101
+ * Radix keeps dialogs mounted until their exit animation ends, so reduced
102
+ * motion zeroes the durations instead of removing the animations — the
103
+ * animation events still fire and close stays instant.
104
+ */
105
+ @media (prefers-reduced-motion: reduce) {
106
+ :root,
107
+ [data-theme="light"] {
108
+ --lt-duration-fast: 0ms;
109
+ --lt-duration-base: 0ms;
110
+ --lt-duration-slow: 0ms;
111
+ }
112
+ }
113
+
99
114
  [data-theme="dark"],
100
115
  .dark {
101
116
  --lt-bg: var(--background, oklch(0.145 0 0));
@@ -205,6 +220,14 @@
205
220
  --font-weight-lt-bold: var(--lt-font-weight-bold);
206
221
  --animate-lt-toast-in: lt-toast-in var(--lt-duration-base) ease-out;
207
222
  --animate-lt-toast-out: lt-toast-out var(--lt-duration-fast) ease-in;
223
+ --animate-lt-fade-in: lt-fade-in var(--lt-duration-base) ease-out;
224
+ --animate-lt-fade-out: lt-fade-out var(--lt-duration-fast) ease-in;
225
+ --animate-lt-dialog-in: lt-dialog-in var(--lt-duration-base) ease-out;
226
+ --animate-lt-dialog-out: lt-dialog-out var(--lt-duration-fast) ease-in;
227
+ --animate-lt-sheet-in-start: lt-sheet-in-start var(--lt-duration-slow) ease-out;
228
+ --animate-lt-sheet-out-start: lt-sheet-out-start var(--lt-duration-fast) ease-in;
229
+ --animate-lt-sheet-in-end: lt-sheet-in-end var(--lt-duration-slow) ease-out;
230
+ --animate-lt-sheet-out-end: lt-sheet-out-end var(--lt-duration-fast) ease-in;
208
231
  }
209
232
 
210
233
  @theme {
@@ -285,6 +308,88 @@
285
308
  }
286
309
  }
287
310
 
311
+ @keyframes lt-fade-in {
312
+ from {
313
+ opacity: 0;
314
+ }
315
+ to {
316
+ opacity: 1;
317
+ }
318
+ }
319
+
320
+ @keyframes lt-fade-out {
321
+ from {
322
+ opacity: 1;
323
+ }
324
+ to {
325
+ opacity: 0;
326
+ }
327
+ }
328
+
329
+ /* Centering uses Tailwind's standalone translate property, which composes
330
+ with transform — so these keyframes may only animate opacity and scale,
331
+ never a translate of their own. */
332
+ @keyframes lt-dialog-in {
333
+ from {
334
+ opacity: 0;
335
+ transform: scale(0.98);
336
+ }
337
+ to {
338
+ opacity: 1;
339
+ transform: scale(1);
340
+ }
341
+ }
342
+
343
+ @keyframes lt-dialog-out {
344
+ from {
345
+ opacity: 1;
346
+ transform: scale(1);
347
+ }
348
+ to {
349
+ opacity: 0;
350
+ transform: scale(0.98);
351
+ }
352
+ }
353
+
354
+ /* Sheet slides are physical (translateX): under RTL a sheet docks to the
355
+ correct logical edge but enters from the mirrored side. Shipped locales
356
+ are LTR. */
357
+ @keyframes lt-sheet-in-start {
358
+ from {
359
+ transform: translateX(-100%);
360
+ }
361
+ to {
362
+ transform: translateX(0);
363
+ }
364
+ }
365
+
366
+ @keyframes lt-sheet-out-start {
367
+ from {
368
+ transform: translateX(0);
369
+ }
370
+ to {
371
+ transform: translateX(-100%);
372
+ }
373
+ }
374
+
375
+ @keyframes lt-sheet-in-end {
376
+ from {
377
+ transform: translateX(100%);
378
+ }
379
+ to {
380
+ transform: translateX(0);
381
+ }
382
+ }
383
+
384
+ @keyframes lt-sheet-out-end {
385
+ from {
386
+ transform: translateX(0);
387
+ }
388
+ to {
389
+ transform: translateX(100%);
390
+ }
391
+ }
392
+
288
393
  /*
289
394
  * Named colours. Semantic names alias the theme tokens; hue names carry their
290
395
  * own oklch values with `.dark` overrides. Renderers resolve a named colour to
@@ -1,7 +1,7 @@
1
- import { onCallout } from "../../toast/callout.js";
2
1
  import { cn } from "../../lib/utils.js";
3
2
  import { Icon } from "../../icons/sprite.js";
4
3
  import { RenderNode } from "../../core/renderer.js";
4
+ import { onCallout } from "../../toast/callout.js";
5
5
  import { useT } from "../../i18n/instance.js";
6
6
  import { variantStyles } from "../../toast/variant-styles.js";
7
7
  import { useEffect, useState } from "react";
@@ -1,70 +1,37 @@
1
- import { LATTICE_EVENT } from "../../events/event-names.js";
1
+ import { LATTICE_EVENT } from "../../core/event-names.js";
2
+ import { useWindowEvent } from "../../core/hooks/use-window-event.js";
2
3
  import { cn } from "../../lib/utils.js";
3
4
  import { CollapsedContext } from "../../core/collapsed-context.js";
4
5
  import { nodeIdentity } from "../../core/test-id.js";
6
+ import { useMediaQuery } from "../../lib/use-media-query.js";
7
+ import { useCollapsibleState } from "../../ui/use-collapsible-state.js";
5
8
  import { router } from "@inertiajs/react";
6
9
  import { useEffect, useState } from "react";
7
10
  import { jsx, jsxs } from "react/jsx-runtime";
8
11
  //#region resources/js/layout/components/sidebar.tsx
9
12
  var DESKTOP_QUERY = "(min-width: 768px)";
10
- function readStored(key, remember) {
11
- if (!remember || typeof window === "undefined") return false;
12
- return window.localStorage.getItem(key) === "true";
13
- }
14
13
  function matchesTarget(event, identity) {
15
14
  const target = event.detail?.target;
16
15
  return target == null || target === identity;
17
16
  }
18
- function useIsDesktop() {
19
- const supported = () => typeof window !== "undefined" && typeof window.matchMedia === "function";
20
- const [isDesktop, setIsDesktop] = useState(() => supported() ? window.matchMedia(DESKTOP_QUERY).matches : true);
21
- useEffect(() => {
22
- if (!supported()) return;
23
- const query = window.matchMedia(DESKTOP_QUERY);
24
- const update = () => setIsDesktop(query.matches);
25
- update();
26
- query.addEventListener("change", update);
27
- return () => query.removeEventListener("change", update);
28
- }, []);
29
- return isDesktop;
30
- }
31
17
  var SidebarComponent = ({ children, node }) => {
32
18
  const collapsible = node.props.collapsible;
33
19
  const rememberState = node.props.rememberState;
34
20
  const identity = nodeIdentity(node);
35
21
  const storageKey = `lattice:sidebar:${identity ?? "default"}`;
36
- const isDesktop = useIsDesktop();
37
- const [collapsed, setCollapsed] = useState(() => readStored(storageKey, collapsible && rememberState));
22
+ const isDesktop = useMediaQuery(DESKTOP_QUERY, true);
23
+ const [collapsed, toggleCollapsed] = useCollapsibleState(storageKey, false, collapsible && rememberState);
38
24
  const [mobileOpen, setMobileOpen] = useState(false);
39
- useEffect(() => {
40
- function toggle(event) {
41
- if (!matchesTarget(event, identity)) return;
42
- if (window.matchMedia?.(DESKTOP_QUERY).matches ?? true) {
43
- if (!collapsible) return;
44
- setCollapsed((value) => {
45
- const next = !value;
46
- if (rememberState && typeof window !== "undefined") window.localStorage.setItem(storageKey, String(next));
47
- return next;
48
- });
49
- } else setMobileOpen((open) => !open);
50
- }
51
- window.addEventListener(LATTICE_EVENT.toggleSidebar, toggle);
52
- return () => window.removeEventListener(LATTICE_EVENT.toggleSidebar, toggle);
53
- }, [
54
- identity,
55
- collapsible,
56
- rememberState,
57
- storageKey
58
- ]);
25
+ useWindowEvent(LATTICE_EVENT.toggleSidebar, (event) => {
26
+ if (!matchesTarget(event, identity)) return;
27
+ if (window.matchMedia?.(DESKTOP_QUERY).matches ?? true) {
28
+ if (collapsible) toggleCollapsed();
29
+ } else setMobileOpen((open) => !open);
30
+ });
59
31
  useEffect(() => router.on("navigate", () => setMobileOpen(false)), []);
60
- useEffect(() => {
61
- if (!mobileOpen) return;
62
- function close(event) {
63
- if (event.key === "Escape") setMobileOpen(false);
64
- }
65
- window.addEventListener("keydown", close);
66
- return () => window.removeEventListener("keydown", close);
67
- }, [mobileOpen]);
32
+ useWindowEvent("keydown", (event) => {
33
+ if (event.key === "Escape") setMobileOpen(false);
34
+ }, { enabled: mobileOpen });
68
35
  const isCollapsed = collapsible && collapsed && isDesktop;
69
36
  return /* @__PURE__ */ jsxs(CollapsedContext.Provider, {
70
37
  value: isCollapsed,
@@ -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/lattice/core/types\";\nimport { CollapsedContext } from \"@lattice-php/lattice/core/collapsed-context\";\nimport { LATTICE_EVENT } from \"@lattice-php/lattice/events/event-names\";\nimport { nodeIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\n\nconst DESKTOP_QUERY = \"(min-width: 768px)\";\n\nfunction readStored(key: string, remember: boolean): boolean {\n if (!remember || typeof window === \"undefined\") {\n return false;\n }\n\n return window.localStorage.getItem(key) === \"true\";\n}\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\nfunction useIsDesktop(): boolean {\n const supported = () => typeof window !== \"undefined\" && typeof window.matchMedia === \"function\";\n const [isDesktop, setIsDesktop] = useState(() =>\n supported() ? window.matchMedia(DESKTOP_QUERY).matches : true,\n );\n\n useEffect(() => {\n if (!supported()) {\n return;\n }\n\n const query = window.matchMedia(DESKTOP_QUERY);\n const update = (): void => setIsDesktop(query.matches);\n\n update();\n query.addEventListener(\"change\", update);\n\n return () => query.removeEventListener(\"change\", update);\n }, []);\n\n return isDesktop;\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 = useIsDesktop();\n\n const [collapsed, setCollapsed] = useState(() =>\n readStored(storageKey, collapsible && rememberState),\n );\n const [mobileOpen, setMobileOpen] = useState(false);\n\n useEffect(() => {\n function toggle(event: Event): void {\n if (!matchesTarget(event, identity)) {\n return;\n }\n\n if (window.matchMedia?.(DESKTOP_QUERY).matches ?? true) {\n if (!collapsible) {\n return;\n }\n\n setCollapsed((value) => {\n const next = !value;\n\n if (rememberState && typeof window !== \"undefined\") {\n window.localStorage.setItem(storageKey, String(next));\n }\n\n return next;\n });\n } else {\n setMobileOpen((open) => !open);\n }\n }\n\n window.addEventListener(LATTICE_EVENT.toggleSidebar, toggle);\n\n return () => window.removeEventListener(LATTICE_EVENT.toggleSidebar, toggle);\n }, [identity, collapsible, rememberState, storageKey]);\n\n useEffect(() => router.on(\"navigate\", () => setMobileOpen(false)), []);\n\n useEffect(() => {\n if (!mobileOpen) {\n return;\n }\n\n function close(event: KeyboardEvent): void {\n if (event.key === \"Escape\") {\n setMobileOpen(false);\n }\n }\n\n window.addEventListener(\"keydown\", close);\n\n return () => window.removeEventListener(\"keydown\", close);\n }, [mobileOpen]);\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":";;;;;;;;AAQA,IAAM,gBAAgB;AAEtB,SAAS,WAAW,KAAa,UAA4B;CAC3D,IAAI,CAAC,YAAY,OAAO,WAAW,aACjC,OAAO;CAGT,OAAO,OAAO,aAAa,QAAQ,GAAG,MAAM;AAC9C;AAEA,SAAS,cAAc,OAAc,UAAuC;CAC1E,MAAM,SAAU,MAA2C,QAAQ;CAEnE,OAAO,UAAU,QAAQ,WAAW;AACtC;AAEA,SAAS,eAAwB;CAC/B,MAAM,kBAAkB,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe;CACtF,MAAM,CAAC,WAAW,gBAAgB,eAChC,UAAU,IAAI,OAAO,WAAW,aAAa,EAAE,UAAU,IAC3D;CAEA,gBAAgB;EACd,IAAI,CAAC,UAAU,GACb;EAGF,MAAM,QAAQ,OAAO,WAAW,aAAa;EAC7C,MAAM,eAAqB,aAAa,MAAM,OAAO;EAErD,OAAO;EACP,MAAM,iBAAiB,UAAU,MAAM;EAEvC,aAAa,MAAM,oBAAoB,UAAU,MAAM;CACzD,GAAG,CAAC,CAAC;CAEL,OAAO;AACT;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,aAAa;CAE/B,MAAM,CAAC,WAAW,gBAAgB,eAChC,WAAW,YAAY,eAAe,aAAa,CACrD;CACA,MAAM,CAAC,YAAY,iBAAiB,SAAS,KAAK;CAElD,gBAAgB;EACd,SAAS,OAAO,OAAoB;GAClC,IAAI,CAAC,cAAc,OAAO,QAAQ,GAChC;GAGF,IAAI,OAAO,aAAa,aAAa,EAAE,WAAW,MAAM;IACtD,IAAI,CAAC,aACH;IAGF,cAAc,UAAU;KACtB,MAAM,OAAO,CAAC;KAEd,IAAI,iBAAiB,OAAO,WAAW,aACrC,OAAO,aAAa,QAAQ,YAAY,OAAO,IAAI,CAAC;KAGtD,OAAO;IACT,CAAC;GACH,OACE,eAAe,SAAS,CAAC,IAAI;EAEjC;EAEA,OAAO,iBAAiB,cAAc,eAAe,MAAM;EAE3D,aAAa,OAAO,oBAAoB,cAAc,eAAe,MAAM;CAC7E,GAAG;EAAC;EAAU;EAAa;EAAe;CAAU,CAAC;CAErD,gBAAgB,OAAO,GAAG,kBAAkB,cAAc,KAAK,CAAC,GAAG,CAAC,CAAC;CAErE,gBAAgB;EACd,IAAI,CAAC,YACH;EAGF,SAAS,MAAM,OAA4B;GACzC,IAAI,MAAM,QAAQ,UAChB,cAAc,KAAK;EAEvB;EAEA,OAAO,iBAAiB,WAAW,KAAK;EAExC,aAAa,OAAO,oBAAoB,WAAW,KAAK;CAC1D,GAAG,CAAC,UAAU,CAAC;CAEf,MAAM,cAAc,eAAe,aAAa;CAEhD,OACE,qBAAC,iBAAiB,UAAlB;EAA2B,OAAO;YAAlC,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/lattice/core/types\";\nimport { CollapsedContext } from \"@lattice-php/lattice/core/collapsed-context\";\nimport { LATTICE_EVENT } from \"@lattice-php/lattice/core/event-names\";\nimport { useWindowEvent } from \"@lattice-php/lattice/core/hooks/use-window-event\";\nimport { nodeIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { useMediaQuery } from \"@lattice-php/lattice/lib/use-media-query\";\nimport { useCollapsibleState } from \"@lattice-php/lattice/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,EAAE,WAAW;OAC5C,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;YAAlC,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,2 +1 @@
1
- export type LayoutComponentType = "breadcrumbs" | "callouts" | "dropdown" | "menu" | "menu-item" | "outlet" | "sidebar" | "topbar";
2
1
  export declare const layoutComponents: import('../core/registry.js').Plugin;
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":[],"sources":["../../resources/js/layout/plugin.ts"],"sourcesContent":["import {\n createPlugin,\n eagerComponent,\n type ComponentRegistryFor,\n} from \"@lattice-php/lattice/core/registry\";\nimport BreadcrumbsComponent from \"./components/breadcrumbs\";\nimport CalloutsComponent from \"./components/callouts\";\nimport DropdownComponent from \"./components/dropdown\";\nimport MenuComponent from \"./components/menu\";\nimport MenuItemComponent from \"./components/menu-item\";\nimport OutletComponent from \"./components/outlet\";\nimport SidebarComponent from \"./components/sidebar\";\nimport TopbarComponent from \"./components/topbar\";\n\nexport type LayoutComponentType =\n | \"breadcrumbs\"\n | \"callouts\"\n | \"dropdown\"\n | \"menu\"\n | \"menu-item\"\n | \"outlet\"\n | \"sidebar\"\n | \"topbar\";\n\nexport const layoutComponents = createPlugin({\n components: {\n breadcrumbs: eagerComponent(BreadcrumbsComponent),\n callouts: eagerComponent(CalloutsComponent),\n dropdown: eagerComponent(DropdownComponent),\n menu: eagerComponent(MenuComponent),\n \"menu-item\": eagerComponent(MenuItemComponent),\n outlet: eagerComponent(OutletComponent),\n sidebar: eagerComponent(SidebarComponent),\n topbar: eagerComponent(TopbarComponent),\n } satisfies ComponentRegistryFor<LayoutComponentType>,\n name: \"lattice/layout\",\n});\n"],"mappings":";;;;;;;;;;AAwBA,IAAa,mBAAmB,aAAa;CAC3C,YAAY;EACV,aAAa,eAAe,oBAAoB;EAChD,UAAU,eAAe,QAAiB;EAC1C,UAAU,eAAe,iBAAiB;EAC1C,MAAM,eAAe,aAAa;EAClC,aAAa,eAAe,iBAAiB;EAC7C,QAAQ,eAAe,eAAe;EACtC,SAAS,eAAe,gBAAgB;EACxC,QAAQ,eAAe,eAAe;CACxC;CACA,MAAM;AACR,CAAC"}
1
+ {"version":3,"file":"plugin.js","names":[],"sources":["../../resources/js/layout/plugin.ts"],"sourcesContent":["import {\n createPlugin,\n eagerComponent,\n type ComponentRegistryFor,\n} from \"@lattice-php/lattice/core/registry\";\nimport type { LayoutNodeType } from \"@lattice-php/lattice/types/generated\";\nimport BreadcrumbsComponent from \"./components/breadcrumbs\";\nimport CalloutsComponent from \"./components/callouts\";\nimport DropdownComponent from \"./components/dropdown\";\nimport MenuComponent from \"./components/menu\";\nimport MenuItemComponent from \"./components/menu-item\";\nimport OutletComponent from \"./components/outlet\";\nimport SidebarComponent from \"./components/sidebar\";\nimport TopbarComponent from \"./components/topbar\";\n\nexport const layoutComponents = createPlugin({\n components: {\n breadcrumbs: eagerComponent(BreadcrumbsComponent),\n callouts: eagerComponent(CalloutsComponent),\n dropdown: eagerComponent(DropdownComponent),\n menu: eagerComponent(MenuComponent),\n \"menu-item\": eagerComponent(MenuItemComponent),\n outlet: eagerComponent(OutletComponent),\n sidebar: eagerComponent(SidebarComponent),\n topbar: eagerComponent(TopbarComponent),\n } satisfies ComponentRegistryFor<LayoutNodeType>,\n name: \"lattice/layout\",\n});\n"],"mappings":";;;;;;;;;;AAeA,IAAa,mBAAmB,aAAa;CAC3C,YAAY;EACV,aAAa,eAAe,oBAAoB;EAChD,UAAU,eAAe,QAAiB;EAC1C,UAAU,eAAe,iBAAiB;EAC1C,MAAM,eAAe,aAAa;EAClC,aAAa,eAAe,iBAAiB;EAC7C,QAAQ,eAAe,eAAe;EACtC,SAAS,eAAe,gBAAgB;EACxC,QAAQ,eAAe,eAAe;CACxC;CACA,MAAM;AACR,CAAC"}
@@ -0,0 +1,2 @@
1
+ /** Coerce a wire value (boolean, `1`/`0`, `"1"`/`"true"`) to a boolean. */
2
+ export declare function isTruthy(value: unknown): boolean;
@@ -0,0 +1,9 @@
1
+ //#region resources/js/lib/is-truthy.ts
2
+ /** Coerce a wire value (boolean, `1`/`0`, `"1"`/`"true"`) to a boolean. */
3
+ function isTruthy(value) {
4
+ return value === true || value === 1 || value === "1" || value === "true";
5
+ }
6
+ //#endregion
7
+ export { isTruthy };
8
+
9
+ //# sourceMappingURL=is-truthy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-truthy.js","names":[],"sources":["../../resources/js/lib/is-truthy.ts"],"sourcesContent":["/** Coerce a wire value (boolean, `1`/`0`, `\"1\"`/`\"true\"`) to a boolean. */\nexport function isTruthy(value: unknown): boolean {\n return value === true || value === 1 || value === \"1\" || value === \"true\";\n}\n"],"mappings":";;AACA,SAAgB,SAAS,OAAyB;CAChD,OAAO,UAAU,QAAQ,UAAU,KAAK,UAAU,OAAO,UAAU;AACrE"}
@@ -0,0 +1,10 @@
1
+ export type DebouncedCallback<A extends unknown[]> = {
2
+ (...args: A): void;
3
+ cancel: () => void;
4
+ };
5
+ /**
6
+ * A debounced wrapper around `callback` that is stable across renders (it reads
7
+ * the latest callback via a ref) and clears its pending timer on unmount, so a
8
+ * late fire can never run against a torn-down component.
9
+ */
10
+ export declare function useDebouncedCallback<A extends unknown[]>(callback: (...args: A) => void, delayMs: number): DebouncedCallback<A>;
@@ -0,0 +1,34 @@
1
+ import { useCallback, useEffect, useMemo, useRef } from "react";
2
+ //#region resources/js/lib/use-debounced-callback.ts
3
+ /**
4
+ * A debounced wrapper around `callback` that is stable across renders (it reads
5
+ * the latest callback via a ref) and clears its pending timer on unmount, so a
6
+ * late fire can never run against a torn-down component.
7
+ */
8
+ function useDebouncedCallback(callback, delayMs) {
9
+ const callbackRef = useRef(callback);
10
+ callbackRef.current = callback;
11
+ const timerRef = useRef(null);
12
+ const cancel = useCallback(() => {
13
+ if (timerRef.current !== null) {
14
+ clearTimeout(timerRef.current);
15
+ timerRef.current = null;
16
+ }
17
+ }, []);
18
+ useEffect(() => cancel, [cancel]);
19
+ return useMemo(() => {
20
+ const debounced = (...args) => {
21
+ cancel();
22
+ timerRef.current = setTimeout(() => {
23
+ timerRef.current = null;
24
+ callbackRef.current(...args);
25
+ }, delayMs);
26
+ };
27
+ debounced.cancel = cancel;
28
+ return debounced;
29
+ }, [cancel, delayMs]);
30
+ }
31
+ //#endregion
32
+ export { useDebouncedCallback };
33
+
34
+ //# sourceMappingURL=use-debounced-callback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-debounced-callback.js","names":[],"sources":["../../resources/js/lib/use-debounced-callback.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef } from \"react\";\n\nexport type DebouncedCallback<A extends unknown[]> = {\n (...args: A): void;\n cancel: () => void;\n};\n\n/**\n * A debounced wrapper around `callback` that is stable across renders (it reads\n * the latest callback via a ref) and clears its pending timer on unmount, so a\n * late fire can never run against a torn-down component.\n */\nexport function useDebouncedCallback<A extends unknown[]>(\n callback: (...args: A) => void,\n delayMs: number,\n): DebouncedCallback<A> {\n const callbackRef = useRef(callback);\n callbackRef.current = callback;\n\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const cancel = useCallback(() => {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }, []);\n\n useEffect(() => cancel, [cancel]);\n\n return useMemo(() => {\n const debounced = (...args: A): void => {\n cancel();\n timerRef.current = setTimeout(() => {\n timerRef.current = null;\n callbackRef.current(...args);\n }, delayMs);\n };\n\n debounced.cancel = cancel;\n\n return debounced;\n }, [cancel, delayMs]);\n}\n"],"mappings":";;;;;;;AAYA,SAAgB,qBACd,UACA,SACsB;CACtB,MAAM,cAAc,OAAO,QAAQ;CACnC,YAAY,UAAU;CAEtB,MAAM,WAAW,OAA6C,IAAI;CAElE,MAAM,SAAS,kBAAkB;EAC/B,IAAI,SAAS,YAAY,MAAM;GAC7B,aAAa,SAAS,OAAO;GAC7B,SAAS,UAAU;EACrB;CACF,GAAG,CAAC,CAAC;CAEL,gBAAgB,QAAQ,CAAC,MAAM,CAAC;CAEhC,OAAO,cAAc;EACnB,MAAM,aAAa,GAAG,SAAkB;GACtC,OAAO;GACP,SAAS,UAAU,iBAAiB;IAClC,SAAS,UAAU;IACnB,YAAY,QAAQ,GAAG,IAAI;GAC7B,GAAG,OAAO;EACZ;EAEA,UAAU,SAAS;EAEnB,OAAO;CACT,GAAG,CAAC,QAAQ,OAAO,CAAC;AACtB"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Track a CSS media query. `fallback` is the SSR / no-`matchMedia` value and the
3
+ * initial state before the effect subscribes.
4
+ */
5
+ export declare function useMediaQuery(query: string, fallback?: boolean): boolean;
@@ -0,0 +1,26 @@
1
+ import { useEffect, useState } from "react";
2
+ //#region resources/js/lib/use-media-query.ts
3
+ function matches(query, fallback) {
4
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return fallback;
5
+ return window.matchMedia(query).matches;
6
+ }
7
+ /**
8
+ * Track a CSS media query. `fallback` is the SSR / no-`matchMedia` value and the
9
+ * initial state before the effect subscribes.
10
+ */
11
+ function useMediaQuery(query, fallback = false) {
12
+ const [state, setState] = useState(() => matches(query, fallback));
13
+ useEffect(() => {
14
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
15
+ const media = window.matchMedia(query);
16
+ const update = () => setState(media.matches);
17
+ update();
18
+ media.addEventListener("change", update);
19
+ return () => media.removeEventListener("change", update);
20
+ }, [query]);
21
+ return state;
22
+ }
23
+ //#endregion
24
+ export { useMediaQuery };
25
+
26
+ //# sourceMappingURL=use-media-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-media-query.js","names":[],"sources":["../../resources/js/lib/use-media-query.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nfunction matches(query: string, fallback: boolean): boolean {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return fallback;\n }\n\n return window.matchMedia(query).matches;\n}\n\n/**\n * Track a CSS media query. `fallback` is the SSR / no-`matchMedia` value and the\n * initial state before the effect subscribes.\n */\nexport function useMediaQuery(query: string, fallback = false): boolean {\n const [state, setState] = useState(() => matches(query, fallback));\n\n useEffect(() => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return;\n }\n\n const media = window.matchMedia(query);\n const update = (): void => setState(media.matches);\n\n update();\n media.addEventListener(\"change\", update);\n\n return () => media.removeEventListener(\"change\", update);\n }, [query]);\n\n return state;\n}\n"],"mappings":";;AAEA,SAAS,QAAQ,OAAe,UAA4B;CAC1D,IAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAChE,OAAO;CAGT,OAAO,OAAO,WAAW,KAAK,EAAE;AAClC;;;;;AAMA,SAAgB,cAAc,OAAe,WAAW,OAAgB;CACtE,MAAM,CAAC,OAAO,YAAY,eAAe,QAAQ,OAAO,QAAQ,CAAC;CAEjE,gBAAgB;EACd,IAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAChE;EAGF,MAAM,QAAQ,OAAO,WAAW,KAAK;EACrC,MAAM,eAAqB,SAAS,MAAM,OAAO;EAEjD,OAAO;EACP,MAAM,iBAAiB,UAAU,MAAM;EAEvC,aAAa,MAAM,oBAAoB,UAAU,MAAM;CACzD,GAAG,CAAC,KAAK,CAAC;CAEV,OAAO;AACT"}
@@ -0,0 +1,13 @@
1
+ import { Dispatch, SetStateAction } from 'react';
2
+ export type PersistentStateOptions<T> = {
3
+ enabled?: boolean;
4
+ parse?: (raw: string) => T;
5
+ serialize?: (value: T) => string | null;
6
+ };
7
+ /**
8
+ * `useState` backed by `localStorage`. The SSR guard and read/write `try/catch`
9
+ * live here once; `parse`/`serialize` default to JSON. Persistence happens in
10
+ * the setter (not inside the state updater), so a StrictMode double-invoke never
11
+ * double-writes, and a `serialize` returning `null` removes the key.
12
+ */
13
+ export declare function usePersistentState<T>(key: string, fallback: T | (() => T), options?: PersistentStateOptions<T>): [T, Dispatch<SetStateAction<T>>];
@@ -0,0 +1,62 @@
1
+ import { useCallback, useRef, useState } from "react";
2
+ //#region resources/js/lib/use-persistent-state.ts
3
+ /**
4
+ * `useState` backed by `localStorage`. The SSR guard and read/write `try/catch`
5
+ * live here once; `parse`/`serialize` default to JSON. Persistence happens in
6
+ * the setter (not inside the state updater), so a StrictMode double-invoke never
7
+ * double-writes, and a `serialize` returning `null` removes the key.
8
+ */
9
+ function usePersistentState(key, fallback, options = {}) {
10
+ const { enabled = true, parse, serialize } = options;
11
+ const resolveFallback = () => typeof fallback === "function" ? fallback() : fallback;
12
+ const [value, setValue] = useState(() => {
13
+ if (!enabled || typeof window === "undefined") return resolveFallback();
14
+ let raw;
15
+ try {
16
+ raw = window.localStorage.getItem(key);
17
+ } catch {
18
+ return resolveFallback();
19
+ }
20
+ if (raw === null) return resolveFallback();
21
+ try {
22
+ return parse ? parse(raw) : JSON.parse(raw);
23
+ } catch {
24
+ try {
25
+ window.localStorage.removeItem(key);
26
+ } catch {
27
+ return resolveFallback();
28
+ }
29
+ return resolveFallback();
30
+ }
31
+ });
32
+ const valueRef = useRef(value);
33
+ valueRef.current = value;
34
+ const persistRef = useRef({
35
+ enabled,
36
+ key,
37
+ serialize
38
+ });
39
+ persistRef.current = {
40
+ enabled,
41
+ key,
42
+ serialize
43
+ };
44
+ return [value, useCallback((action) => {
45
+ const next = typeof action === "function" ? action(valueRef.current) : action;
46
+ valueRef.current = next;
47
+ setValue(next);
48
+ const { enabled: on, key: storageKey, serialize: encode } = persistRef.current;
49
+ if (!on || typeof window === "undefined") return;
50
+ try {
51
+ const encoded = encode ? encode(next) : JSON.stringify(next);
52
+ if (encoded === null) window.localStorage.removeItem(storageKey);
53
+ else window.localStorage.setItem(storageKey, encoded);
54
+ } catch {
55
+ return;
56
+ }
57
+ }, [])];
58
+ }
59
+ //#endregion
60
+ export { usePersistentState };
61
+
62
+ //# sourceMappingURL=use-persistent-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-persistent-state.js","names":[],"sources":["../../resources/js/lib/use-persistent-state.ts"],"sourcesContent":["import type { Dispatch, SetStateAction } from \"react\";\nimport { useCallback, useRef, useState } from \"react\";\n\nexport type PersistentStateOptions<T> = {\n enabled?: boolean;\n parse?: (raw: string) => T;\n serialize?: (value: T) => string | null;\n};\n\n/**\n * `useState` backed by `localStorage`. The SSR guard and read/write `try/catch`\n * live here once; `parse`/`serialize` default to JSON. Persistence happens in\n * the setter (not inside the state updater), so a StrictMode double-invoke never\n * double-writes, and a `serialize` returning `null` removes the key.\n */\nexport function usePersistentState<T>(\n key: string,\n fallback: T | (() => T),\n options: PersistentStateOptions<T> = {},\n): [T, Dispatch<SetStateAction<T>>] {\n const { enabled = true, parse, serialize } = options;\n\n const resolveFallback = (): T =>\n typeof fallback === \"function\" ? (fallback as () => T)() : fallback;\n\n const [value, setValue] = useState<T>(() => {\n if (!enabled || typeof window === \"undefined\") {\n return resolveFallback();\n }\n\n let raw: string | null;\n try {\n raw = window.localStorage.getItem(key);\n } catch {\n return resolveFallback();\n }\n\n if (raw === null) {\n return resolveFallback();\n }\n\n try {\n return parse ? parse(raw) : (JSON.parse(raw) as T);\n } catch {\n // Drop corrupt data so it cannot resurface on the next read.\n try {\n window.localStorage.removeItem(key);\n } catch {\n return resolveFallback();\n }\n\n return resolveFallback();\n }\n });\n\n const valueRef = useRef(value);\n valueRef.current = value;\n\n const persistRef = useRef({ enabled, key, serialize });\n persistRef.current = { enabled, key, serialize };\n\n const setPersistentValue = useCallback<Dispatch<SetStateAction<T>>>((action) => {\n const next =\n typeof action === \"function\" ? (action as (prev: T) => T)(valueRef.current) : action;\n\n valueRef.current = next;\n setValue(next);\n\n const { enabled: on, key: storageKey, serialize: encode } = persistRef.current;\n\n if (!on || typeof window === \"undefined\") {\n return;\n }\n\n try {\n const encoded = encode ? encode(next) : JSON.stringify(next);\n\n if (encoded === null) {\n window.localStorage.removeItem(storageKey);\n } else {\n window.localStorage.setItem(storageKey, encoded);\n }\n } catch {\n return;\n }\n }, []);\n\n return [value, setPersistentValue];\n}\n"],"mappings":";;;;;;;;AAeA,SAAgB,mBACd,KACA,UACA,UAAqC,CAAC,GACJ;CAClC,MAAM,EAAE,UAAU,MAAM,OAAO,cAAc;CAE7C,MAAM,wBACJ,OAAO,aAAa,aAAc,SAAqB,IAAI;CAE7D,MAAM,CAAC,OAAO,YAAY,eAAkB;EAC1C,IAAI,CAAC,WAAW,OAAO,WAAW,aAChC,OAAO,gBAAgB;EAGzB,IAAI;EACJ,IAAI;GACF,MAAM,OAAO,aAAa,QAAQ,GAAG;EACvC,QAAQ;GACN,OAAO,gBAAgB;EACzB;EAEA,IAAI,QAAQ,MACV,OAAO,gBAAgB;EAGzB,IAAI;GACF,OAAO,QAAQ,MAAM,GAAG,IAAK,KAAK,MAAM,GAAG;EAC7C,QAAQ;GAEN,IAAI;IACF,OAAO,aAAa,WAAW,GAAG;GACpC,QAAQ;IACN,OAAO,gBAAgB;GACzB;GAEA,OAAO,gBAAgB;EACzB;CACF,CAAC;CAED,MAAM,WAAW,OAAO,KAAK;CAC7B,SAAS,UAAU;CAEnB,MAAM,aAAa,OAAO;EAAE;EAAS;EAAK;CAAU,CAAC;CACrD,WAAW,UAAU;EAAE;EAAS;EAAK;CAAU;CA4B/C,OAAO,CAAC,OA1BmB,aAA0C,WAAW;EAC9E,MAAM,OACJ,OAAO,WAAW,aAAc,OAA0B,SAAS,OAAO,IAAI;EAEhF,SAAS,UAAU;EACnB,SAAS,IAAI;EAEb,MAAM,EAAE,SAAS,IAAI,KAAK,YAAY,WAAW,WAAW,WAAW;EAEvE,IAAI,CAAC,MAAM,OAAO,WAAW,aAC3B;EAGF,IAAI;GACF,MAAM,UAAU,SAAS,OAAO,IAAI,IAAI,KAAK,UAAU,IAAI;GAE3D,IAAI,YAAY,MACd,OAAO,aAAa,WAAW,UAAU;QAEzC,OAAO,aAAa,QAAQ,YAAY,OAAO;EAEnD,QAAQ;GACN;EACF;CACF,GAAG,CAAC,CAEW,CAAkB;AACnC"}
@@ -79,7 +79,10 @@ var NotificationsComponent = ({ node }) => {
79
79
  open: store.open,
80
80
  onOpenChange: store.setOpen,
81
81
  children: /* @__PURE__ */ jsxs(DialogContent, {
82
- className: "fixed left-auto right-0 top-0 h-full w-96 max-w-full translate-x-0 translate-y-0 overflow-y-auto p-0",
82
+ "aria-describedby": void 0,
83
+ className: "p-0",
84
+ placement: "end",
85
+ width: "sm",
83
86
  children: [/* @__PURE__ */ jsx(DialogTitle, {
84
87
  className: "sr-only",
85
88
  children: label
@@ -1 +1 @@
1
- {"version":3,"file":"notifications.js","names":[],"sources":["../../../resources/js/notifications/components/notifications.tsx"],"sourcesContent":["import { Component, lazy, Suspense, type ReactNode } from \"react\";\nimport { Badge } from \"@lattice-php/lattice/ui/badge\";\nimport { Dialog, DialogContent, DialogTitle } from \"@lattice-php/lattice/ui/dialog\";\nimport {\n Popover as PopoverRoot,\n PopoverContent,\n PopoverTrigger,\n} from \"@lattice-php/lattice/ui/popover\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { useNotifications } from \"@lattice-php/lattice/notifications/store\";\nimport type { NotificationItem } from \"@lattice-php/lattice/notifications/types\";\nimport { NotificationList } from \"./notification-list\";\n\nconst NotificationsEcho = lazy(() =>\n import(\"./notifications-echo\").then((m) => ({ default: m.NotificationsEcho })),\n);\n\nclass EchoBoundary extends Component<{ children: ReactNode }, { failed: boolean }> {\n state = { failed: false };\n\n static getDerivedStateFromError(): { failed: boolean } {\n return { failed: true };\n }\n\n componentDidCatch(): void {\n console.warn(\n \"[lattice] The notifications bell declares a realtime channel but Echo is unavailable. Install @laravel/echo-react and call configureEcho().\",\n );\n }\n\n render(): ReactNode {\n return this.state.failed ? null : this.props.children;\n }\n}\n\nconst NotificationsComponent: RendererComponent<\"notifications\"> = ({ node }) => {\n const { t } = useT(\"lattice\");\n const store = useNotifications({\n endpoint: node.props.endpoint,\n pollingInterval: node.props.pollingInterval,\n });\n\n const label = t(\"notifications.label\", \"Notifications\");\n\n const trigger = (\n <span className=\"relative inline-flex items-center justify-center rounded-lt-sm p-2 hover:bg-lt-muted\">\n <Icon name=\"bell\" className=\"size-lt-icon-md\" />\n {store.unreadCount > 0 ? (\n <Badge\n variant=\"destructive\"\n data-test=\"notifications-badge\"\n className=\"absolute -right-0.5 -top-0.5 min-w-4 px-1 py-0 text-[10px]\"\n >\n {store.unreadCount}\n </Badge>\n ) : null}\n </span>\n );\n\n const panel = (\n <div className=\"w-80\" 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\">{t(\"notifications.heading\", \"Notifications\")}</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 );\n\n return (\n <>\n {node.props.channel ? (\n <EchoBoundary>\n <Suspense fallback={null}>\n <NotificationsEcho\n channel={node.props.channel}\n onReceive={(item: NotificationItem) => store.receive(item)}\n />\n </Suspense>\n </EchoBoundary>\n ) : null}\n\n {node.props.slideOut ? (\n <>\n <button\n type=\"button\"\n aria-label={label}\n data-test=\"notifications-trigger\"\n onClick={() => store.setOpen(true)}\n >\n {trigger}\n </button>\n <Dialog open={store.open} onOpenChange={store.setOpen}>\n <DialogContent className=\"fixed left-auto right-0 top-0 h-full w-96 max-w-full translate-x-0 translate-y-0 overflow-y-auto p-0\">\n <DialogTitle className=\"sr-only\">{label}</DialogTitle>\n {panel}\n </DialogContent>\n </Dialog>\n </>\n ) : (\n <PopoverRoot open={store.open} onOpenChange={store.setOpen}>\n <PopoverTrigger asChild>\n <button type=\"button\" aria-label={label} data-test=\"notifications-trigger\">\n {trigger}\n </button>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"p-0\">\n {panel}\n </PopoverContent>\n </PopoverRoot>\n )}\n </>\n );\n};\n\nexport default NotificationsComponent;\n"],"mappings":";;;;;;;;;;AAeA,IAAM,oBAAoB,WACxB,OAAO,2BAAwB,MAAM,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAC/E;AAEA,IAAM,eAAN,cAA2B,UAAwD;CACjF,QAAQ,EAAE,QAAQ,MAAM;CAExB,OAAO,2BAAgD;EACrD,OAAO,EAAE,QAAQ,KAAK;CACxB;CAEA,oBAA0B;EACxB,QAAQ,KACN,6IACF;CACF;CAEA,SAAoB;EAClB,OAAO,KAAK,MAAM,SAAS,OAAO,KAAK,MAAM;CAC/C;AACF;AAEA,IAAM,0BAA8D,EAAE,WAAW;CAC/E,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,QAAQ,iBAAiB;EAC7B,UAAU,KAAK,MAAM;EACrB,iBAAiB,KAAK,MAAM;CAC9B,CAAC;CAED,MAAM,QAAQ,EAAE,uBAAuB,eAAe;CAEtD,MAAM,UACJ,qBAAC,QAAD;EAAM,WAAU;YAAhB,CACE,oBAAC,MAAD;GAAM,MAAK;GAAO,WAAU;EAAmB,CAAA,GAC9C,MAAM,cAAc,IACnB,oBAAC,OAAD;GACE,SAAQ;GACR,aAAU;GACV,WAAU;aAET,MAAM;EACF,CAAA,IACL,IACA;;CAGR,MAAM,QACJ,qBAAC,OAAD;EAAK,WAAU;EAAO,aAAU;YAAhC,CACE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,QAAD;IAAM,WAAU;cAAuB,EAAE,yBAAyB,eAAe;GAAQ,CAAA,GACxF,MAAM,cAAc,IACnB,oBAAC,UAAD;IACE,MAAK;IACL,WAAU;IACV,SAAS,MAAM;cAEd,EAAE,+BAA+B,eAAe;GAC3C,CAAA,IACN,IACD;MACL,oBAAC,kBAAD;GACE,eAAe,MAAM;GACrB,QAAQ,MAAM;GACd,SAAS,MAAM;GACf,YAAY,MAAM;GAClB,WAAW,MAAM;GACjB,YAAY,MAAM;EACnB,CAAA,CACE;;CAGP,OACE,qBAAA,YAAA,EAAA,UAAA,CACG,KAAK,MAAM,UACV,oBAAC,cAAD,EAAA,UACE,oBAAC,UAAD;EAAU,UAAU;YAClB,oBAAC,mBAAD;GACE,SAAS,KAAK,MAAM;GACpB,YAAY,SAA2B,MAAM,QAAQ,IAAI;EAC1D,CAAA;CACO,CAAA,EACE,CAAA,IACZ,MAEH,KAAK,MAAM,WACV,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,UAAD;EACE,MAAK;EACL,cAAY;EACZ,aAAU;EACV,eAAe,MAAM,QAAQ,IAAI;YAEhC;CACK,CAAA,GACR,oBAAC,QAAD;EAAQ,MAAM,MAAM;EAAM,cAAc,MAAM;YAC5C,qBAAC,eAAD;GAAe,WAAU;aAAzB,CACE,oBAAC,aAAD;IAAa,WAAU;cAAW;GAAmB,CAAA,GACpD,KACY;;CACT,CAAA,CACR,EAAA,CAAA,IAEF,qBAAC,SAAD;EAAa,MAAM,MAAM;EAAM,cAAc,MAAM;YAAnD,CACE,oBAAC,gBAAD;GAAgB,SAAA;aACd,oBAAC,UAAD;IAAQ,MAAK;IAAS,cAAY;IAAO,aAAU;cAChD;GACK,CAAA;EACM,CAAA,GAChB,oBAAC,gBAAD;GAAgB,OAAM;GAAM,WAAU;aACnC;EACa,CAAA,CACL;GAEf,EAAA,CAAA;AAEN"}
1
+ {"version":3,"file":"notifications.js","names":[],"sources":["../../../resources/js/notifications/components/notifications.tsx"],"sourcesContent":["import { Component, lazy, Suspense, type ReactNode } from \"react\";\nimport { Badge } from \"@lattice-php/lattice/ui/badge\";\nimport { Dialog, DialogContent, DialogTitle } from \"@lattice-php/lattice/ui/dialog\";\nimport {\n Popover as PopoverRoot,\n PopoverContent,\n PopoverTrigger,\n} from \"@lattice-php/lattice/ui/popover\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { useNotifications } from \"@lattice-php/lattice/notifications/store\";\nimport type { NotificationItem } from \"@lattice-php/lattice/notifications/types\";\nimport { NotificationList } from \"./notification-list\";\n\nconst NotificationsEcho = lazy(() =>\n import(\"./notifications-echo\").then((m) => ({ default: m.NotificationsEcho })),\n);\n\nclass EchoBoundary extends Component<{ children: ReactNode }, { failed: boolean }> {\n state = { failed: false };\n\n static getDerivedStateFromError(): { failed: boolean } {\n return { failed: true };\n }\n\n componentDidCatch(): void {\n console.warn(\n \"[lattice] The notifications bell declares a realtime channel but Echo is unavailable. Install @laravel/echo-react and call configureEcho().\",\n );\n }\n\n render(): ReactNode {\n return this.state.failed ? null : this.props.children;\n }\n}\n\nconst NotificationsComponent: RendererComponent<\"notifications\"> = ({ node }) => {\n const { t } = useT(\"lattice\");\n const store = useNotifications({\n endpoint: node.props.endpoint,\n pollingInterval: node.props.pollingInterval,\n });\n\n const label = t(\"notifications.label\", \"Notifications\");\n\n const trigger = (\n <span className=\"relative inline-flex items-center justify-center rounded-lt-sm p-2 hover:bg-lt-muted\">\n <Icon name=\"bell\" className=\"size-lt-icon-md\" />\n {store.unreadCount > 0 ? (\n <Badge\n variant=\"destructive\"\n data-test=\"notifications-badge\"\n className=\"absolute -right-0.5 -top-0.5 min-w-4 px-1 py-0 text-[10px]\"\n >\n {store.unreadCount}\n </Badge>\n ) : null}\n </span>\n );\n\n const panel = (\n <div className=\"w-80\" 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\">{t(\"notifications.heading\", \"Notifications\")}</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 );\n\n return (\n <>\n {node.props.channel ? (\n <EchoBoundary>\n <Suspense fallback={null}>\n <NotificationsEcho\n channel={node.props.channel}\n onReceive={(item: NotificationItem) => store.receive(item)}\n />\n </Suspense>\n </EchoBoundary>\n ) : null}\n\n {node.props.slideOut ? (\n <>\n <button\n type=\"button\"\n aria-label={label}\n data-test=\"notifications-trigger\"\n onClick={() => store.setOpen(true)}\n >\n {trigger}\n </button>\n <Dialog open={store.open} onOpenChange={store.setOpen}>\n <DialogContent aria-describedby={undefined} className=\"p-0\" placement=\"end\" width=\"sm\">\n <DialogTitle className=\"sr-only\">{label}</DialogTitle>\n {panel}\n </DialogContent>\n </Dialog>\n </>\n ) : (\n <PopoverRoot open={store.open} onOpenChange={store.setOpen}>\n <PopoverTrigger asChild>\n <button type=\"button\" aria-label={label} data-test=\"notifications-trigger\">\n {trigger}\n </button>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"p-0\">\n {panel}\n </PopoverContent>\n </PopoverRoot>\n )}\n </>\n );\n};\n\nexport default NotificationsComponent;\n"],"mappings":";;;;;;;;;;AAeA,IAAM,oBAAoB,WACxB,OAAO,2BAAwB,MAAM,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAC/E;AAEA,IAAM,eAAN,cAA2B,UAAwD;CACjF,QAAQ,EAAE,QAAQ,MAAM;CAExB,OAAO,2BAAgD;EACrD,OAAO,EAAE,QAAQ,KAAK;CACxB;CAEA,oBAA0B;EACxB,QAAQ,KACN,6IACF;CACF;CAEA,SAAoB;EAClB,OAAO,KAAK,MAAM,SAAS,OAAO,KAAK,MAAM;CAC/C;AACF;AAEA,IAAM,0BAA8D,EAAE,WAAW;CAC/E,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,QAAQ,iBAAiB;EAC7B,UAAU,KAAK,MAAM;EACrB,iBAAiB,KAAK,MAAM;CAC9B,CAAC;CAED,MAAM,QAAQ,EAAE,uBAAuB,eAAe;CAEtD,MAAM,UACJ,qBAAC,QAAD;EAAM,WAAU;YAAhB,CACE,oBAAC,MAAD;GAAM,MAAK;GAAO,WAAU;EAAmB,CAAA,GAC9C,MAAM,cAAc,IACnB,oBAAC,OAAD;GACE,SAAQ;GACR,aAAU;GACV,WAAU;aAET,MAAM;EACF,CAAA,IACL,IACA;;CAGR,MAAM,QACJ,qBAAC,OAAD;EAAK,WAAU;EAAO,aAAU;YAAhC,CACE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,QAAD;IAAM,WAAU;cAAuB,EAAE,yBAAyB,eAAe;GAAQ,CAAA,GACxF,MAAM,cAAc,IACnB,oBAAC,UAAD;IACE,MAAK;IACL,WAAU;IACV,SAAS,MAAM;cAEd,EAAE,+BAA+B,eAAe;GAC3C,CAAA,IACN,IACD;MACL,oBAAC,kBAAD;GACE,eAAe,MAAM;GACrB,QAAQ,MAAM;GACd,SAAS,MAAM;GACf,YAAY,MAAM;GAClB,WAAW,MAAM;GACjB,YAAY,MAAM;EACnB,CAAA,CACE;;CAGP,OACE,qBAAA,YAAA,EAAA,UAAA,CACG,KAAK,MAAM,UACV,oBAAC,cAAD,EAAA,UACE,oBAAC,UAAD;EAAU,UAAU;YAClB,oBAAC,mBAAD;GACE,SAAS,KAAK,MAAM;GACpB,YAAY,SAA2B,MAAM,QAAQ,IAAI;EAC1D,CAAA;CACO,CAAA,EACE,CAAA,IACZ,MAEH,KAAK,MAAM,WACV,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,UAAD;EACE,MAAK;EACL,cAAY;EACZ,aAAU;EACV,eAAe,MAAM,QAAQ,IAAI;YAEhC;CACK,CAAA,GACR,oBAAC,QAAD;EAAQ,MAAM,MAAM;EAAM,cAAc,MAAM;YAC5C,qBAAC,eAAD;GAAe,oBAAkB,KAAA;GAAW,WAAU;GAAM,WAAU;GAAM,OAAM;aAAlF,CACE,oBAAC,aAAD;IAAa,WAAU;cAAW;GAAmB,CAAA,GACpD,KACY;;CACT,CAAA,CACR,EAAA,CAAA,IAEF,qBAAC,SAAD;EAAa,MAAM,MAAM;EAAM,cAAc,MAAM;YAAnD,CACE,oBAAC,gBAAD;GAAgB,SAAA;aACd,oBAAC,UAAD;IAAQ,MAAK;IAAS,cAAY;IAAO,aAAU;cAChD;GACK,CAAA;EACM,CAAA,GAChB,oBAAC,gBAAD;GAAgB,OAAM;GAAM,WAAU;aACnC;EACa,CAAA,CACL;GAEf,EAAA,CAAA;AAEN"}
@@ -1,2 +1 @@
1
- export type NotificationsComponentType = "notifications";
2
1
  export declare const notificationsComponents: import('../core/registry.js').Plugin;
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":[],"sources":["../../resources/js/notifications/plugin.ts"],"sourcesContent":["import {\n createPlugin,\n eagerComponent,\n type ComponentRegistryFor,\n} from \"@lattice-php/lattice/core/registry\";\nimport NotificationsComponent from \"./components/notifications\";\n\nexport type NotificationsComponentType = \"notifications\";\n\nexport const notificationsComponents = createPlugin({\n name: \"lattice/notifications\",\n components: {\n notifications: eagerComponent(NotificationsComponent),\n } satisfies ComponentRegistryFor<NotificationsComponentType>,\n});\n"],"mappings":";;;AASA,IAAa,0BAA0B,aAAa;CAClD,MAAM;CACN,YAAY,EACV,eAAe,eAAe,sBAAsB,EACtD;AACF,CAAC"}
1
+ {"version":3,"file":"plugin.js","names":[],"sources":["../../resources/js/notifications/plugin.ts"],"sourcesContent":["import {\n createPlugin,\n eagerComponent,\n type ComponentRegistryFor,\n} from \"@lattice-php/lattice/core/registry\";\nimport type { NotificationNodeType } from \"@lattice-php/lattice/types/generated\";\nimport NotificationsComponent from \"./components/notifications\";\n\nexport const notificationsComponents = createPlugin({\n name: \"lattice/notifications\",\n components: {\n notifications: eagerComponent(NotificationsComponent),\n } satisfies ComponentRegistryFor<NotificationNodeType>,\n});\n"],"mappings":";;;AAQA,IAAa,0BAA0B,aAAa;CAClD,MAAM;CACN,YAAY,EACV,eAAe,eAAe,sBAAsB,EACtD;AACF,CAAC"}
@@ -2,9 +2,9 @@ import { updateAppearance } from "./appearance/index.js";
2
2
  import { RegistryContext } from "./core/registry-context.js";
3
3
  import { SpriteProvider } from "./icons/sprite.js";
4
4
  import { Toaster } from "./toast/toaster.js";
5
- import { EventBridge } from "./event-bridge.js";
6
5
  import { ActionInteractionProvider } from "./action/components/action-trigger-provider.js";
7
6
  import { useFlashEffects } from "./effects/use-flash-effects.js";
7
+ import { EventBridge } from "./event-bridge.js";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
9
9
  //#region resources/js/provider-base.tsx
10
10
  var defaultSprite = { href: "" };
@@ -4,5 +4,5 @@ import { Listen } from '../types/generated.js';
4
4
  * is only fetched where it is actually needed.
5
5
  */
6
6
  export declare function RealtimeListeners({ listeners }: {
7
- listeners?: Listen[];
7
+ listeners: Listen[];
8
8
  }): import("react").JSX.Element | null;
@@ -19,7 +19,7 @@ var EchoBoundary = class extends Component {
19
19
  * is only fetched where it is actually needed.
20
20
  */
21
21
  function RealtimeListeners({ listeners }) {
22
- if (listeners === void 0 || listeners.length === 0) return null;
22
+ if (listeners.length === 0) return null;
23
23
  return /* @__PURE__ */ jsx(EchoBoundary, { children: /* @__PURE__ */ jsx(Suspense, {
24
24
  fallback: null,
25
25
  children: /* @__PURE__ */ jsx(Subscriptions, { listeners })
@@ -1 +1 @@
1
- {"version":3,"file":"listeners.js","names":[],"sources":["../../resources/js/realtime/listeners.tsx"],"sourcesContent":["import { Component, lazy, Suspense } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { Listen } from \"@lattice-php/lattice/types/generated\";\n\nconst Subscriptions = lazy(() => import(\"./subscriptions\"));\n\nclass EchoBoundary extends Component<{ children: ReactNode }, { failed: boolean }> {\n state = { failed: false };\n\n static getDerivedStateFromError(): { failed: boolean } {\n return { failed: true };\n }\n\n componentDidCatch(): void {\n console.warn(\n \"[lattice] Real-time listeners are declared but Echo is unavailable. Install @laravel/echo-react and call configureEcho().\",\n );\n }\n\n render(): ReactNode {\n return this.state.failed ? null : this.props.children;\n }\n}\n\n/**\n * Renders nothing when a page declares no listeners, so the echo-react chunk\n * is only fetched where it is actually needed.\n */\nexport function RealtimeListeners({ listeners }: { listeners?: Listen[] }) {\n if (listeners === undefined || listeners.length === 0) {\n return null;\n }\n\n return (\n <EchoBoundary>\n <Suspense fallback={null}>\n <Subscriptions listeners={listeners} />\n </Suspense>\n </EchoBoundary>\n );\n}\n"],"mappings":";;;AAIA,IAAM,gBAAgB,WAAW,OAAO,qBAAkB;AAE1D,IAAM,eAAN,cAA2B,UAAwD;CACjF,QAAQ,EAAE,QAAQ,MAAM;CAExB,OAAO,2BAAgD;EACrD,OAAO,EAAE,QAAQ,KAAK;CACxB;CAEA,oBAA0B;EACxB,QAAQ,KACN,2HACF;CACF;CAEA,SAAoB;EAClB,OAAO,KAAK,MAAM,SAAS,OAAO,KAAK,MAAM;CAC/C;AACF;;;;;AAMA,SAAgB,kBAAkB,EAAE,aAAuC;CACzE,IAAI,cAAc,KAAA,KAAa,UAAU,WAAW,GAClD,OAAO;CAGT,OACE,oBAAC,cAAD,EAAA,UACE,oBAAC,UAAD;EAAU,UAAU;YAClB,oBAAC,eAAD,EAA0B,UAAY,CAAA;CAC9B,CAAA,EACE,CAAA;AAElB"}
1
+ {"version":3,"file":"listeners.js","names":[],"sources":["../../resources/js/realtime/listeners.tsx"],"sourcesContent":["import { Component, lazy, Suspense } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { Listen } from \"@lattice-php/lattice/types/generated\";\n\nconst Subscriptions = lazy(() => import(\"./subscriptions\"));\n\nclass EchoBoundary extends Component<{ children: ReactNode }, { failed: boolean }> {\n state = { failed: false };\n\n static getDerivedStateFromError(): { failed: boolean } {\n return { failed: true };\n }\n\n componentDidCatch(): void {\n console.warn(\n \"[lattice] Real-time listeners are declared but Echo is unavailable. Install @laravel/echo-react and call configureEcho().\",\n );\n }\n\n render(): ReactNode {\n return this.state.failed ? null : this.props.children;\n }\n}\n\n/**\n * Renders nothing when a page declares no listeners, so the echo-react chunk\n * is only fetched where it is actually needed.\n */\nexport function RealtimeListeners({ listeners }: { listeners: Listen[] }) {\n if (listeners.length === 0) {\n return null;\n }\n\n return (\n <EchoBoundary>\n <Suspense fallback={null}>\n <Subscriptions listeners={listeners} />\n </Suspense>\n </EchoBoundary>\n );\n}\n"],"mappings":";;;AAIA,IAAM,gBAAgB,WAAW,OAAO,qBAAkB;AAE1D,IAAM,eAAN,cAA2B,UAAwD;CACjF,QAAQ,EAAE,QAAQ,MAAM;CAExB,OAAO,2BAAgD;EACrD,OAAO,EAAE,QAAQ,KAAK;CACxB;CAEA,oBAA0B;EACxB,QAAQ,KACN,2HACF;CACF;CAEA,SAAoB;EAClB,OAAO,KAAK,MAAM,SAAS,OAAO,KAAK,MAAM;CAC/C;AACF;;;;;AAMA,SAAgB,kBAAkB,EAAE,aAAsC;CACxE,IAAI,UAAU,WAAW,GACvB,OAAO;CAGT,OACE,oBAAC,cAAD,EAAA,UACE,oBAAC,UAAD;EAAU,UAAU;YAClB,oBAAC,eAAD,EAA0B,UAAY,CAAA;CAC9B,CAAA,EACE,CAAA;AAElB"}