@lattice-php/lattice 0.33.0 → 0.35.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 (78) hide show
  1. package/dist/appearance/index.js +2 -6
  2. package/dist/appearance/index.js.map +1 -1
  3. package/dist/chat/lib/transport.d.ts +2 -2
  4. package/dist/chat/lib/transport.js +22 -23
  5. package/dist/chat/lib/transport.js.map +1 -1
  6. package/dist/core/api.d.ts +0 -1
  7. package/dist/core/api.js +2 -2
  8. package/dist/core/api.js.map +1 -1
  9. package/dist/core/hooks/column-sizing.d.ts +1 -1
  10. package/dist/core/hooks/column-sizing.js +7 -14
  11. package/dist/core/hooks/column-sizing.js.map +1 -1
  12. package/dist/core/hooks/use-column-resizing.js +22 -54
  13. package/dist/core/hooks/use-column-resizing.js.map +1 -1
  14. package/dist/core/index.d.ts +1 -1
  15. package/dist/core/index.js +2 -2
  16. package/dist/core/registry.d.ts +1 -5
  17. package/dist/core/registry.js +1 -2
  18. package/dist/core/registry.js.map +1 -1
  19. package/dist/core/renderer.js +4 -10
  20. package/dist/core/renderer.js.map +1 -1
  21. package/dist/effects/registry.js +1 -4
  22. package/dist/effects/registry.js.map +1 -1
  23. package/dist/event-bridge.d.ts +1 -4
  24. package/dist/event-bridge.js +1 -11
  25. package/dist/event-bridge.js.map +1 -1
  26. package/dist/form/components/fields/choice.js +1 -1
  27. package/dist/form/components/fields/choice.js.map +1 -1
  28. package/dist/form/types.d.ts +1 -4
  29. package/dist/i18n/config.d.ts +1 -1
  30. package/dist/i18n/config.js +5 -21
  31. package/dist/i18n/config.js.map +1 -1
  32. package/dist/i18n/index.d.ts +2 -2
  33. package/dist/i18n/index.js +2 -2
  34. package/dist/i18n/locale-switcher.d.ts +0 -5
  35. package/dist/i18n/locale-switcher.js +1 -5
  36. package/dist/i18n/locale-switcher.js.map +1 -1
  37. package/dist/i18n/locale.js +2 -10
  38. package/dist/i18n/locale.js.map +1 -1
  39. package/dist/i18n/timezone.js +2 -10
  40. package/dist/i18n/timezone.js.map +1 -1
  41. package/dist/icons/icon-renderer.d.ts +1 -2
  42. package/dist/icons/icon-renderer.js +2 -6
  43. package/dist/icons/icon-renderer.js.map +1 -1
  44. package/dist/lattice.css +89 -0
  45. package/dist/lib/listeners.d.ts +5 -0
  46. package/dist/lib/listeners.js +19 -0
  47. package/dist/lib/listeners.js.map +1 -0
  48. package/dist/table/hooks/use-column-visibility.js +4 -12
  49. package/dist/table/hooks/use-column-visibility.js.map +1 -1
  50. package/dist/toast/callout.js +8 -16
  51. package/dist/toast/callout.js.map +1 -1
  52. package/dist/toast/toast.d.ts +2 -0
  53. package/dist/toast/toast.js +16 -10
  54. package/dist/toast/toast.js.map +1 -1
  55. package/dist/types/generated.d.ts +3 -1
  56. package/dist/ui/grid.js +17 -5
  57. package/dist/ui/grid.js.map +1 -1
  58. package/dist/ui/segmented-pills.d.ts +1 -1
  59. package/dist/ui/segmented-pills.js +1 -1
  60. package/dist/ui/segmented-pills.js.map +1 -1
  61. package/dist-standalone/chunks/{chart-view-WqyMTZiX.js → chart-view-CR1X2aGE.js} +1 -1
  62. package/dist-standalone/chunks/{date-picker-field-CCnkf4SC.js → date-picker-field-B4Xzow-A.js} +1 -1
  63. package/dist-standalone/chunks/{dist-CK9gk4iB.js → dist-ci5ng714.js} +1 -1
  64. package/dist-standalone/chunks/{icon-button-D4DKcnhp.js → icon-button-D3AltHNz.js} +1 -1
  65. package/dist-standalone/chunks/{instance-BQeVkUpw.js → instance-C4O1XWa6.js} +1 -1
  66. package/dist-standalone/chunks/locale-GyqtaX1O.js +1 -0
  67. package/dist-standalone/chunks/{page-props-BA5zqTIT.js → page-props-BHNNVkgJ.js} +1 -1
  68. package/dist-standalone/chunks/{rich-editor-field-BeeSr2Zx.js → rich-editor-field-JM3jBEV1.js} +1 -1
  69. package/dist-standalone/chunks/{subscriptions-Cwd1jFs5.js → subscriptions-BV4g0s0U.js} +1 -1
  70. package/dist-standalone/chunks/{time-picker-DSs9fDWe.js → time-picker-kbhOHEPd.js} +1 -1
  71. package/dist-standalone/chunks/timezone-CyoBch3a.js +1 -0
  72. package/dist-standalone/chunks/{use-effect-dispatcher-CiWYRvsF.js → use-effect-dispatcher-CJXFkAv1.js} +2 -2
  73. package/dist-standalone/lattice.css +1 -1
  74. package/dist-standalone/lattice.js +4 -4
  75. package/dist-standalone/manifest.json +1 -27
  76. package/package.json +2 -2
  77. package/dist-standalone/chunks/locale-CgtcxthU.js +0 -1
  78. package/dist-standalone/chunks/timezone-BORzyZp2.js +0 -1
@@ -1,3 +1,4 @@
1
+ import { createListeners } from "../lib/listeners.js";
1
2
  import { useSyncExternalStore } from "react";
2
3
  //#region resources/js/appearance/index.ts
3
4
  var appearances = [
@@ -5,7 +6,7 @@ var appearances = [
5
6
  "dark",
6
7
  "system"
7
8
  ];
8
- var listeners = /* @__PURE__ */ new Set();
9
+ var { subscribe, notify } = createListeners();
9
10
  var currentAppearance = "system";
10
11
  var serverAppearance = "system";
11
12
  /**
@@ -43,11 +44,6 @@ var applyAppearance = (appearance) => {
43
44
  document.documentElement.classList.toggle("dark", isDark);
44
45
  document.documentElement.style.colorScheme = isDark ? "dark" : "light";
45
46
  };
46
- var subscribe = (callback) => {
47
- listeners.add(callback);
48
- return () => listeners.delete(callback);
49
- };
50
- var notify = () => listeners.forEach((listener) => listener());
51
47
  var mediaQuery = () => {
52
48
  if (typeof window === "undefined") return null;
53
49
  return window.matchMedia("(prefers-color-scheme: dark)");
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../resources/js/appearance/index.ts"],"sourcesContent":["import { useSyncExternalStore } from \"react\";\n\nconst appearances = [\"light\", \"dark\", \"system\"] as const;\n\nexport type Appearance = (typeof appearances)[number];\n\nexport type ResolvedAppearance = \"light\" | \"dark\";\n\nexport type UseAppearanceReturn = {\n readonly appearance: Appearance;\n readonly resolvedAppearance: ResolvedAppearance;\n readonly updateAppearance: (mode: Appearance) => void;\n};\n\nconst listeners = new Set<() => void>();\nlet currentAppearance: Appearance = \"system\";\nlet serverAppearance: Appearance = \"system\";\n\n/**\n * Seed the SSR/hydration snapshot from the backend-shared `lattice.appearance`\n * prop. The server render and the client's hydration pass must read the same\n * value, or components branching on the appearance mismatch on hydration.\n */\nexport function seedAppearance(value: unknown): void {\n if (isAppearance(value)) {\n serverAppearance = value;\n }\n}\n\nexport function isAppearance(value: unknown): value is Appearance {\n return appearances.some((appearance) => appearance === value);\n}\n\nconst prefersDark = (): boolean => {\n if (typeof window === \"undefined\") {\n return false;\n }\n\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n};\n\nconst setCookie = (name: string, value: string, days = 365): void => {\n if (typeof document === \"undefined\") {\n return;\n }\n\n const maxAge = days * 24 * 60 * 60;\n document.cookie = `${name}=${value};path=/;max-age=${maxAge};SameSite=Lax`;\n};\n\nconst getStoredAppearance = (): Appearance => {\n if (typeof window === \"undefined\") {\n return \"system\";\n }\n\n const stored = localStorage.getItem(\"appearance\");\n\n return isAppearance(stored) ? stored : \"system\";\n};\n\nconst isDarkMode = (appearance: Appearance): boolean => {\n return appearance === \"dark\" || (appearance === \"system\" && prefersDark());\n};\n\nconst getSystemAppearance = (): ResolvedAppearance => (prefersDark() ? \"dark\" : \"light\");\n\nconst applyAppearance = (appearance: Appearance): void => {\n if (typeof document === \"undefined\") {\n return;\n }\n\n const isDark = isDarkMode(appearance);\n\n document.documentElement.classList.toggle(\"dark\", isDark);\n document.documentElement.style.colorScheme = isDark ? \"dark\" : \"light\";\n};\n\nconst subscribe = (callback: () => void) => {\n listeners.add(callback);\n\n return () => listeners.delete(callback);\n};\n\nconst notify = (): void => listeners.forEach((listener) => listener());\n\nconst mediaQuery = (): MediaQueryList | null => {\n if (typeof window === \"undefined\") {\n return null;\n }\n\n return window.matchMedia(\"(prefers-color-scheme: dark)\");\n};\n\nconst handleSystemAppearanceChange = (): void => {\n applyAppearance(currentAppearance);\n notify();\n};\n\nexport function updateAppearance(mode: Appearance): void {\n currentAppearance = mode;\n\n localStorage.setItem(\"appearance\", mode);\n\n setCookie(\"appearance\", mode);\n\n applyAppearance(mode);\n notify();\n}\n\nexport function initializeAppearance(): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n if (!localStorage.getItem(\"appearance\")) {\n localStorage.setItem(\"appearance\", \"system\");\n setCookie(\"appearance\", \"system\");\n }\n\n currentAppearance = getStoredAppearance();\n applyAppearance(currentAppearance);\n\n mediaQuery()?.addEventListener(\"change\", handleSystemAppearanceChange);\n}\n\nexport function useAppearance(): UseAppearanceReturn {\n const appearance: Appearance = useSyncExternalStore(\n subscribe,\n () => currentAppearance,\n () => serverAppearance,\n );\n const systemAppearance: ResolvedAppearance = useSyncExternalStore(\n subscribe,\n getSystemAppearance,\n () => \"light\",\n );\n\n const resolvedAppearance: ResolvedAppearance =\n appearance === \"system\" ? systemAppearance : appearance;\n\n return { appearance, resolvedAppearance, updateAppearance } as const;\n}\n"],"mappings":";;AAEA,IAAM,cAAc;CAAC;CAAS;CAAQ;AAAQ;AAY9C,IAAM,4BAAY,IAAI,IAAgB;AACtC,IAAI,oBAAgC;AACpC,IAAI,mBAA+B;;;;;;AAOnC,SAAgB,eAAe,OAAsB;CACnD,IAAI,aAAa,KAAK,GACpB,mBAAmB;AAEvB;AAEA,SAAgB,aAAa,OAAqC;CAChE,OAAO,YAAY,MAAM,eAAe,eAAe,KAAK;AAC9D;AAEA,IAAM,oBAA6B;CACjC,IAAI,OAAO,WAAW,aACpB,OAAO;CAGT,OAAO,OAAO,WAAW,8BAA8B,CAAC,CAAC;AAC3D;AAEA,IAAM,aAAa,MAAc,OAAe,OAAO,QAAc;CACnE,IAAI,OAAO,aAAa,aACtB;CAGF,MAAM,SAAS,OAAO,KAAK,KAAK;CAChC,SAAS,SAAS,GAAG,KAAK,GAAG,MAAM,kBAAkB,OAAO;AAC9D;AAEA,IAAM,4BAAwC;CAC5C,IAAI,OAAO,WAAW,aACpB,OAAO;CAGT,MAAM,SAAS,aAAa,QAAQ,YAAY;CAEhD,OAAO,aAAa,MAAM,IAAI,SAAS;AACzC;AAEA,IAAM,cAAc,eAAoC;CACtD,OAAO,eAAe,UAAW,eAAe,YAAY,YAAY;AAC1E;AAEA,IAAM,4BAAiD,YAAY,IAAI,SAAS;AAEhF,IAAM,mBAAmB,eAAiC;CACxD,IAAI,OAAO,aAAa,aACtB;CAGF,MAAM,SAAS,WAAW,UAAU;CAEpC,SAAS,gBAAgB,UAAU,OAAO,QAAQ,MAAM;CACxD,SAAS,gBAAgB,MAAM,cAAc,SAAS,SAAS;AACjE;AAEA,IAAM,aAAa,aAAyB;CAC1C,UAAU,IAAI,QAAQ;CAEtB,aAAa,UAAU,OAAO,QAAQ;AACxC;AAEA,IAAM,eAAqB,UAAU,SAAS,aAAa,SAAS,CAAC;AAErE,IAAM,mBAA0C;CAC9C,IAAI,OAAO,WAAW,aACpB,OAAO;CAGT,OAAO,OAAO,WAAW,8BAA8B;AACzD;AAEA,IAAM,qCAA2C;CAC/C,gBAAgB,iBAAiB;CACjC,OAAO;AACT;AAEA,SAAgB,iBAAiB,MAAwB;CACvD,oBAAoB;CAEpB,aAAa,QAAQ,cAAc,IAAI;CAEvC,UAAU,cAAc,IAAI;CAE5B,gBAAgB,IAAI;CACpB,OAAO;AACT;AAEA,SAAgB,uBAA6B;CAC3C,IAAI,OAAO,WAAW,aACpB;CAGF,IAAI,CAAC,aAAa,QAAQ,YAAY,GAAG;EACvC,aAAa,QAAQ,cAAc,QAAQ;EAC3C,UAAU,cAAc,QAAQ;CAClC;CAEA,oBAAoB,oBAAoB;CACxC,gBAAgB,iBAAiB;CAEjC,WAAW,CAAC,EAAE,iBAAiB,UAAU,4BAA4B;AACvE;AAEA,SAAgB,gBAAqC;CACnD,MAAM,aAAyB,qBAC7B,iBACM,yBACA,gBACR;CACA,MAAM,mBAAuC,qBAC3C,WACA,2BACM,OACR;CAKA,OAAO;EAAE;EAAY,oBAFnB,eAAe,WAAW,mBAAmB;EAEN;CAAiB;AAC5D"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../resources/js/appearance/index.ts"],"sourcesContent":["import { useSyncExternalStore } from \"react\";\nimport { createListeners } from \"@lattice-php/lattice/lib/listeners\";\n\nconst appearances = [\"light\", \"dark\", \"system\"] as const;\n\nexport type Appearance = (typeof appearances)[number];\n\nexport type ResolvedAppearance = \"light\" | \"dark\";\n\nexport type UseAppearanceReturn = {\n readonly appearance: Appearance;\n readonly resolvedAppearance: ResolvedAppearance;\n readonly updateAppearance: (mode: Appearance) => void;\n};\n\nconst { subscribe, notify } = createListeners();\nlet currentAppearance: Appearance = \"system\";\nlet serverAppearance: Appearance = \"system\";\n\n/**\n * Seed the SSR/hydration snapshot from the backend-shared `lattice.appearance`\n * prop. The server render and the client's hydration pass must read the same\n * value, or components branching on the appearance mismatch on hydration.\n */\nexport function seedAppearance(value: unknown): void {\n if (isAppearance(value)) {\n serverAppearance = value;\n }\n}\n\nexport function isAppearance(value: unknown): value is Appearance {\n return appearances.some((appearance) => appearance === value);\n}\n\nconst prefersDark = (): boolean => {\n if (typeof window === \"undefined\") {\n return false;\n }\n\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n};\n\nconst setCookie = (name: string, value: string, days = 365): void => {\n if (typeof document === \"undefined\") {\n return;\n }\n\n const maxAge = days * 24 * 60 * 60;\n document.cookie = `${name}=${value};path=/;max-age=${maxAge};SameSite=Lax`;\n};\n\nconst getStoredAppearance = (): Appearance => {\n if (typeof window === \"undefined\") {\n return \"system\";\n }\n\n const stored = localStorage.getItem(\"appearance\");\n\n return isAppearance(stored) ? stored : \"system\";\n};\n\nconst isDarkMode = (appearance: Appearance): boolean => {\n return appearance === \"dark\" || (appearance === \"system\" && prefersDark());\n};\n\nconst getSystemAppearance = (): ResolvedAppearance => (prefersDark() ? \"dark\" : \"light\");\n\nconst applyAppearance = (appearance: Appearance): void => {\n if (typeof document === \"undefined\") {\n return;\n }\n\n const isDark = isDarkMode(appearance);\n\n document.documentElement.classList.toggle(\"dark\", isDark);\n document.documentElement.style.colorScheme = isDark ? \"dark\" : \"light\";\n};\n\nconst mediaQuery = (): MediaQueryList | null => {\n if (typeof window === \"undefined\") {\n return null;\n }\n\n return window.matchMedia(\"(prefers-color-scheme: dark)\");\n};\n\nconst handleSystemAppearanceChange = (): void => {\n applyAppearance(currentAppearance);\n notify();\n};\n\nexport function updateAppearance(mode: Appearance): void {\n currentAppearance = mode;\n\n localStorage.setItem(\"appearance\", mode);\n\n setCookie(\"appearance\", mode);\n\n applyAppearance(mode);\n notify();\n}\n\nexport function initializeAppearance(): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n if (!localStorage.getItem(\"appearance\")) {\n localStorage.setItem(\"appearance\", \"system\");\n setCookie(\"appearance\", \"system\");\n }\n\n currentAppearance = getStoredAppearance();\n applyAppearance(currentAppearance);\n\n mediaQuery()?.addEventListener(\"change\", handleSystemAppearanceChange);\n}\n\nexport function useAppearance(): UseAppearanceReturn {\n const appearance: Appearance = useSyncExternalStore(\n subscribe,\n () => currentAppearance,\n () => serverAppearance,\n );\n const systemAppearance: ResolvedAppearance = useSyncExternalStore(\n subscribe,\n getSystemAppearance,\n () => \"light\",\n );\n\n const resolvedAppearance: ResolvedAppearance =\n appearance === \"system\" ? systemAppearance : appearance;\n\n return { appearance, resolvedAppearance, updateAppearance } as const;\n}\n"],"mappings":";;;AAGA,IAAM,cAAc;CAAC;CAAS;CAAQ;AAAQ;AAY9C,IAAM,EAAE,WAAW,WAAW,gBAAgB;AAC9C,IAAI,oBAAgC;AACpC,IAAI,mBAA+B;;;;;;AAOnC,SAAgB,eAAe,OAAsB;CACnD,IAAI,aAAa,KAAK,GACpB,mBAAmB;AAEvB;AAEA,SAAgB,aAAa,OAAqC;CAChE,OAAO,YAAY,MAAM,eAAe,eAAe,KAAK;AAC9D;AAEA,IAAM,oBAA6B;CACjC,IAAI,OAAO,WAAW,aACpB,OAAO;CAGT,OAAO,OAAO,WAAW,8BAA8B,CAAC,CAAC;AAC3D;AAEA,IAAM,aAAa,MAAc,OAAe,OAAO,QAAc;CACnE,IAAI,OAAO,aAAa,aACtB;CAGF,MAAM,SAAS,OAAO,KAAK,KAAK;CAChC,SAAS,SAAS,GAAG,KAAK,GAAG,MAAM,kBAAkB,OAAO;AAC9D;AAEA,IAAM,4BAAwC;CAC5C,IAAI,OAAO,WAAW,aACpB,OAAO;CAGT,MAAM,SAAS,aAAa,QAAQ,YAAY;CAEhD,OAAO,aAAa,MAAM,IAAI,SAAS;AACzC;AAEA,IAAM,cAAc,eAAoC;CACtD,OAAO,eAAe,UAAW,eAAe,YAAY,YAAY;AAC1E;AAEA,IAAM,4BAAiD,YAAY,IAAI,SAAS;AAEhF,IAAM,mBAAmB,eAAiC;CACxD,IAAI,OAAO,aAAa,aACtB;CAGF,MAAM,SAAS,WAAW,UAAU;CAEpC,SAAS,gBAAgB,UAAU,OAAO,QAAQ,MAAM;CACxD,SAAS,gBAAgB,MAAM,cAAc,SAAS,SAAS;AACjE;AAEA,IAAM,mBAA0C;CAC9C,IAAI,OAAO,WAAW,aACpB,OAAO;CAGT,OAAO,OAAO,WAAW,8BAA8B;AACzD;AAEA,IAAM,qCAA2C;CAC/C,gBAAgB,iBAAiB;CACjC,OAAO;AACT;AAEA,SAAgB,iBAAiB,MAAwB;CACvD,oBAAoB;CAEpB,aAAa,QAAQ,cAAc,IAAI;CAEvC,UAAU,cAAc,IAAI;CAE5B,gBAAgB,IAAI;CACpB,OAAO;AACT;AAEA,SAAgB,uBAA6B;CAC3C,IAAI,OAAO,WAAW,aACpB;CAGF,IAAI,CAAC,aAAa,QAAQ,YAAY,GAAG;EACvC,aAAa,QAAQ,cAAc,QAAQ;EAC3C,UAAU,cAAc,QAAQ;CAClC;CAEA,oBAAoB,oBAAoB;CACxC,gBAAgB,iBAAiB;CAEjC,WAAW,CAAC,EAAE,iBAAiB,UAAU,4BAA4B;AACvE;AAEA,SAAgB,gBAAqC;CACnD,MAAM,aAAyB,qBAC7B,iBACM,yBACA,gBACR;CACA,MAAM,mBAAuC,qBAC3C,WACA,2BACM,OACR;CAKA,OAAO;EAAE;EAAY,oBAFnB,eAAe,WAAW,mBAAmB;EAEN;CAAiB;AAC5D"}
@@ -1,4 +1,4 @@
1
1
  import { RemoteAccess } from '../../core/api.js';
2
2
  import { ChatFrame, ChatTransportRequest } from '../types.js';
3
- export declare function ndjsonChatTransport({ url, body, signal, }: ChatTransportRequest): AsyncGenerator<ChatFrame>;
4
- export declare function createRemoteNdjsonChatTransport(remote: RemoteAccess): ({ url, body, signal, }: ChatTransportRequest) => AsyncGenerator<ChatFrame>;
3
+ export declare const ndjsonChatTransport: (request: ChatTransportRequest) => AsyncGenerator<ChatFrame>;
4
+ export declare function createRemoteNdjsonChatTransport(remote: RemoteAccess): (request: ChatTransportRequest) => AsyncGenerator<ChatFrame>;
@@ -7,33 +7,32 @@ function parseFrame(line) {
7
7
  return null;
8
8
  }
9
9
  }
10
- async function* ndjsonChatTransport({ url, body, signal }) {
11
- const res = await apiFetch(url, {
10
+ function ndjsonTransport(fetchResponse) {
11
+ return async function* transport(request) {
12
+ const res = await fetchResponse(request);
13
+ if (!res.ok || !res.body) throw new Error(`Chat stream failed (${res.status})`);
14
+ yield* readNdjsonFrames(res.body);
15
+ };
16
+ }
17
+ var ndjsonChatTransport = ndjsonTransport(({ url, body, signal }) => apiFetch(url, {
18
+ method: "POST",
19
+ signal,
20
+ headers: { Accept: "application/x-ndjson" },
21
+ body: JSON.stringify(body),
22
+ throwOnError: false
23
+ }));
24
+ function createRemoteNdjsonChatTransport(remote) {
25
+ return ndjsonTransport(({ url, body, signal }) => remoteFetch(url, {
26
+ remote,
12
27
  method: "POST",
13
28
  signal,
14
- headers: { Accept: "application/x-ndjson" },
29
+ headers: {
30
+ Accept: "application/x-ndjson",
31
+ "Content-Type": "application/json"
32
+ },
15
33
  body: JSON.stringify(body),
16
34
  throwOnError: false
17
- });
18
- if (!res.ok || !res.body) throw new Error(`Chat stream failed (${res.status})`);
19
- yield* readNdjsonFrames(res.body);
20
- }
21
- function createRemoteNdjsonChatTransport(remote) {
22
- return async function* remoteNdjsonChatTransport({ url, body, signal }) {
23
- const res = await remoteFetch(url, {
24
- remote,
25
- method: "POST",
26
- signal,
27
- headers: {
28
- Accept: "application/x-ndjson",
29
- "Content-Type": "application/json"
30
- },
31
- body: JSON.stringify(body),
32
- throwOnError: false
33
- });
34
- if (!res.ok || !res.body) throw new Error(`Chat stream failed (${res.status})`);
35
- yield* readNdjsonFrames(res.body);
36
- };
35
+ }));
37
36
  }
38
37
  async function* readNdjsonFrames(body) {
39
38
  const reader = body.getReader();
@@ -1 +1 @@
1
- {"version":3,"file":"transport.js","names":[],"sources":["../../../resources/js/chat/lib/transport.ts"],"sourcesContent":["import { apiFetch, remoteFetch, type RemoteAccess } from \"@lattice-php/lattice/core/api\";\nimport type { ChatFrame, ChatTransportRequest } from \"@lattice-php/lattice/chat/types\";\n\nfunction parseFrame(line: string): ChatFrame | null {\n try {\n return JSON.parse(line) as ChatFrame;\n } catch {\n return null;\n }\n}\n\nexport async function* ndjsonChatTransport({\n url,\n body,\n signal,\n}: ChatTransportRequest): AsyncGenerator<ChatFrame> {\n const res = await apiFetch(url, {\n method: \"POST\",\n signal,\n headers: { Accept: \"application/x-ndjson\" },\n body: JSON.stringify(body),\n throwOnError: false,\n });\n\n if (!res.ok || !res.body) {\n throw new Error(`Chat stream failed (${res.status})`);\n }\n\n yield* readNdjsonFrames(res.body);\n}\n\nexport function createRemoteNdjsonChatTransport(remote: RemoteAccess) {\n return async function* remoteNdjsonChatTransport({\n url,\n body,\n signal,\n }: ChatTransportRequest): AsyncGenerator<ChatFrame> {\n const res = await remoteFetch(url, {\n remote,\n method: \"POST\",\n signal,\n headers: {\n Accept: \"application/x-ndjson\",\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n throwOnError: false,\n });\n\n if (!res.ok || !res.body) {\n throw new Error(`Chat stream failed (${res.status})`);\n }\n\n yield* readNdjsonFrames(res.body);\n };\n}\n\nasync function* readNdjsonFrames(body: ReadableStream<Uint8Array>): AsyncGenerator<ChatFrame> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (line.trim() === \"\") {\n continue;\n }\n\n const frame = parseFrame(line);\n if (frame) {\n yield frame;\n }\n }\n }\n\n buffer += decoder.decode();\n if (buffer.trim() !== \"\") {\n const frame = parseFrame(buffer);\n if (frame) {\n yield frame;\n }\n }\n } finally {\n await reader.cancel().catch(() => {});\n }\n}\n"],"mappings":";;AAGA,SAAS,WAAW,MAAgC;CAClD,IAAI;EACF,OAAO,KAAK,MAAM,IAAI;CACxB,QAAQ;EACN,OAAO;CACT;AACF;AAEA,gBAAuB,oBAAoB,EACzC,KACA,MACA,UACkD;CAClD,MAAM,MAAM,MAAM,SAAS,KAAK;EAC9B,QAAQ;EACR;EACA,SAAS,EAAE,QAAQ,uBAAuB;EAC1C,MAAM,KAAK,UAAU,IAAI;EACzB,cAAc;CAChB,CAAC;CAED,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,MAClB,MAAM,IAAI,MAAM,uBAAuB,IAAI,OAAO,EAAE;CAGtD,OAAO,iBAAiB,IAAI,IAAI;AAClC;AAEA,SAAgB,gCAAgC,QAAsB;CACpE,OAAO,gBAAgB,0BAA0B,EAC/C,KACA,MACA,UACkD;EAClD,MAAM,MAAM,MAAM,YAAY,KAAK;GACjC;GACA,QAAQ;GACR;GACA,SAAS;IACP,QAAQ;IACR,gBAAgB;GAClB;GACA,MAAM,KAAK,UAAU,IAAI;GACzB,cAAc;EAChB,CAAC;EAED,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,MAClB,MAAM,IAAI,MAAM,uBAAuB,IAAI,OAAO,EAAE;EAGtD,OAAO,iBAAiB,IAAI,IAAI;CAClC;AACF;AAEA,gBAAgB,iBAAiB,MAA6D;CAC5F,MAAM,SAAS,KAAK,UAAU;CAC9B,MAAM,UAAU,IAAI,YAAY;CAChC,IAAI,SAAS;CAEb,IAAI;EACF,SAAS;GACP,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;GAC1C,IAAI,MACF;GAGF,UAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;GAChD,MAAM,QAAQ,OAAO,MAAM,IAAI;GAC/B,SAAS,MAAM,IAAI,KAAK;GAExB,KAAK,MAAM,QAAQ,OAAO;IACxB,IAAI,KAAK,KAAK,MAAM,IAClB;IAGF,MAAM,QAAQ,WAAW,IAAI;IAC7B,IAAI,OACF,MAAM;GAEV;EACF;EAEA,UAAU,QAAQ,OAAO;EACzB,IAAI,OAAO,KAAK,MAAM,IAAI;GACxB,MAAM,QAAQ,WAAW,MAAM;GAC/B,IAAI,OACF,MAAM;EAEV;CACF,UAAU;EACR,MAAM,OAAO,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC;CACtC;AACF"}
1
+ {"version":3,"file":"transport.js","names":[],"sources":["../../../resources/js/chat/lib/transport.ts"],"sourcesContent":["import { apiFetch, remoteFetch, type RemoteAccess } from \"@lattice-php/lattice/core/api\";\nimport type { ChatFrame, ChatTransportRequest } from \"@lattice-php/lattice/chat/types\";\n\nfunction parseFrame(line: string): ChatFrame | null {\n try {\n return JSON.parse(line) as ChatFrame;\n } catch {\n return null;\n }\n}\n\nfunction ndjsonTransport(fetchResponse: (request: ChatTransportRequest) => Promise<Response>) {\n return async function* transport(request: ChatTransportRequest): AsyncGenerator<ChatFrame> {\n const res = await fetchResponse(request);\n\n if (!res.ok || !res.body) {\n throw new Error(`Chat stream failed (${res.status})`);\n }\n\n yield* readNdjsonFrames(res.body);\n };\n}\n\nexport const ndjsonChatTransport = ndjsonTransport(({ url, body, signal }) =>\n apiFetch(url, {\n method: \"POST\",\n signal,\n headers: { Accept: \"application/x-ndjson\" },\n body: JSON.stringify(body),\n throwOnError: false,\n }),\n);\n\nexport function createRemoteNdjsonChatTransport(remote: RemoteAccess) {\n return ndjsonTransport(({ url, body, signal }) =>\n remoteFetch(url, {\n remote,\n method: \"POST\",\n signal,\n headers: {\n Accept: \"application/x-ndjson\",\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n throwOnError: false,\n }),\n );\n}\n\nasync function* readNdjsonFrames(body: ReadableStream<Uint8Array>): AsyncGenerator<ChatFrame> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (line.trim() === \"\") {\n continue;\n }\n\n const frame = parseFrame(line);\n if (frame) {\n yield frame;\n }\n }\n }\n\n buffer += decoder.decode();\n if (buffer.trim() !== \"\") {\n const frame = parseFrame(buffer);\n if (frame) {\n yield frame;\n }\n }\n } finally {\n await reader.cancel().catch(() => {});\n }\n}\n"],"mappings":";;AAGA,SAAS,WAAW,MAAgC;CAClD,IAAI;EACF,OAAO,KAAK,MAAM,IAAI;CACxB,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,gBAAgB,eAAqE;CAC5F,OAAO,gBAAgB,UAAU,SAA0D;EACzF,MAAM,MAAM,MAAM,cAAc,OAAO;EAEvC,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,MAClB,MAAM,IAAI,MAAM,uBAAuB,IAAI,OAAO,EAAE;EAGtD,OAAO,iBAAiB,IAAI,IAAI;CAClC;AACF;AAEA,IAAa,sBAAsB,iBAAiB,EAAE,KAAK,MAAM,aAC/D,SAAS,KAAK;CACZ,QAAQ;CACR;CACA,SAAS,EAAE,QAAQ,uBAAuB;CAC1C,MAAM,KAAK,UAAU,IAAI;CACzB,cAAc;AAChB,CAAC,CACH;AAEA,SAAgB,gCAAgC,QAAsB;CACpE,OAAO,iBAAiB,EAAE,KAAK,MAAM,aACnC,YAAY,KAAK;EACf;EACA,QAAQ;EACR;EACA,SAAS;GACP,QAAQ;GACR,gBAAgB;EAClB;EACA,MAAM,KAAK,UAAU,IAAI;EACzB,cAAc;CAChB,CAAC,CACH;AACF;AAEA,gBAAgB,iBAAiB,MAA6D;CAC5F,MAAM,SAAS,KAAK,UAAU;CAC9B,MAAM,UAAU,IAAI,YAAY;CAChC,IAAI,SAAS;CAEb,IAAI;EACF,SAAS;GACP,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;GAC1C,IAAI,MACF;GAGF,UAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;GAChD,MAAM,QAAQ,OAAO,MAAM,IAAI;GAC/B,SAAS,MAAM,IAAI,KAAK;GAExB,KAAK,MAAM,QAAQ,OAAO;IACxB,IAAI,KAAK,KAAK,MAAM,IAClB;IAGF,MAAM,QAAQ,WAAW,IAAI;IAC7B,IAAI,OACF,MAAM;GAEV;EACF;EAEA,UAAU,QAAQ,OAAO;EACzB,IAAI,OAAO,KAAK,MAAM,IAAI;GACxB,MAAM,QAAQ,WAAW,MAAM;GAC/B,IAAI,OACF,MAAM;EAEV;CACF,UAAU;EACR,MAAM,OAAO,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC;CACtC;AACF"}
@@ -18,7 +18,6 @@ export type RemoteAccess = GeneratedRemoteAccess;
18
18
  export type RemoteInit = Omit<RequestInit, "credentials" | "headers"> & {
19
19
  headers?: Record<string, string>;
20
20
  remote: RemoteAccess;
21
- retryOnUnauthorized?: boolean;
22
21
  throwOnError?: boolean;
23
22
  };
24
23
  /**
package/dist/core/api.js CHANGED
@@ -145,10 +145,10 @@ async function fetchRemoteWithToken(url, init, token) {
145
145
  });
146
146
  }
147
147
  async function remoteFetch(url, init) {
148
- const { remote, retryOnUnauthorized = true, throwOnError = true, ...request } = init;
148
+ const { remote, throwOnError = true, ...request } = init;
149
149
  let token = await remoteToken(remote);
150
150
  let response = await fetchRemoteWithToken(url, request, token);
151
- if (retryOnUnauthorized && isUnauthorized(response)) {
151
+ if (isUnauthorized(response)) {
152
152
  invalidateRemoteToken(remote);
153
153
  token = await remoteToken(remote);
154
154
  response = await fetchRemoteWithToken(url, request, token);
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","names":[],"sources":["../../resources/js/core/api.ts"],"sourcesContent":["/**\n * The single funnel for raw-fetch HTTP, so the cross-cutting header, credential,\n * and error policy has one home and call sites only declare intent. Returns a\n * Response (not parsed data) so one primitive serves JSON, the chat's streaming\n * getReader(), and multipart alike. Not for the Inertia world (router/useHttp),\n * which carries its own headers.\n */\n\nimport { latestRef, storeRefreshedRef } from \"./component-ref\";\nimport { withHeaders } from \"./headers\";\nimport { localeHeader } from \"@lattice-php/lattice/i18n/locale\";\nimport type {\n BrowserToken as GeneratedBrowserToken,\n RemoteAccess as GeneratedRemoteAccess,\n} from \"@lattice-php/lattice/types/generated\";\n\nexport class ApiError extends Error {\n constructor(readonly response: Response) {\n super(`HTTP ${response.status}`);\n }\n}\n\n/**\n * Exported so the few transports that cannot go through apiFetch — an XHR that\n * reports upload progress, for instance — still send the same CSRF header.\n */\nexport function xsrfToken(): string {\n const match = document.cookie.match(/XSRF-TOKEN=([^;]+)/);\n\n return match ? decodeURIComponent(match[1]) : \"\";\n}\n\nexport type ApiInit = Omit<RequestInit, \"headers\"> & {\n ref?: string;\n headers?: Record<string, string>;\n throwOnError?: boolean;\n};\n\nexport type BrowserToken = GeneratedBrowserToken;\nexport type RemoteAccess = GeneratedRemoteAccess;\n\nexport type RemoteInit = Omit<RequestInit, \"credentials\" | \"headers\"> & {\n headers?: Record<string, string>;\n remote: RemoteAccess;\n retryOnUnauthorized?: boolean;\n throwOnError?: boolean;\n};\n\ntype CachedBrowserToken = {\n expiresAt: number;\n token: BrowserToken;\n};\n\nconst tokenRefreshSkewMs = 10_000;\nconst remoteTokenCache = new Map<string, CachedBrowserToken>();\nconst pendingRemoteTokens = new Map<string, Promise<BrowserToken>>();\n\nfunction defaultHeaders(method: string | undefined): Record<string, string> {\n const isWrite = method !== undefined && method !== \"GET\" && method !== \"HEAD\";\n\n if (!isWrite) {\n return { Accept: \"application/json\" };\n }\n\n return {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n \"X-Requested-With\": \"XMLHttpRequest\",\n \"X-XSRF-TOKEN\": xsrfToken(),\n };\n}\n\nlet refRefreshEndpoint = \"/lattice/refs/refresh\";\n\n/**\n * Boot paths call this with the server-minted URL so subdirectory installs\n * refresh against the right path; the default covers root installs.\n */\nexport function setRefRefreshEndpoint(url: string): void {\n refRefreshEndpoint = url;\n}\n\nconst pendingRefRefreshes = new Map<string, Promise<string | null>>();\n\n/**\n * Trade an expired-but-authentic ref for a fresh one. Deduped per original ref\n * so a burst of 403s from one component costs a single round-trip. The renewed\n * token lands in the component-ref map, so every later request that resolves\n * its ref through withHeaders picks it up — this is the transport-agnostic\n * primitive both the fetch funnel below and the Inertia form retry build on.\n */\nexport async function refreshRef(componentRef: string): Promise<string | null> {\n const pending = pendingRefRefreshes.get(componentRef);\n\n if (pending) {\n return pending;\n }\n\n const request = apiJson<{ ref: string }>(refRefreshEndpoint, {\n method: \"POST\",\n body: JSON.stringify({ ref: latestRef(componentRef) }),\n })\n .then((data) => {\n storeRefreshedRef(componentRef, data.ref);\n\n return data.ref;\n })\n .catch(() => null)\n .finally(() => {\n pendingRefRefreshes.delete(componentRef);\n });\n\n pendingRefRefreshes.set(componentRef, request);\n\n return request;\n}\n\nexport async function apiFetch(url: string, init: ApiInit = {}): Promise<Response> {\n const { ref = \"\", headers, throwOnError = true, method, ...rest } = init;\n // Some servers reject a lower-case method line, and fetch only normalizes the\n // standard verbs, so upper-case it here.\n const normalizedMethod = method?.toUpperCase();\n const request = () =>\n fetch(url, {\n credentials: \"same-origin\",\n ...rest,\n method: normalizedMethod,\n // withHeaders resolves the latest refreshed token for the ref, so the\n // retry below picks up the renewal without threading it through.\n headers: withHeaders(ref, { ...defaultHeaders(normalizedMethod), ...headers }),\n });\n\n let response = await request();\n\n if (ref !== \"\" && response.status === 403 && (await refreshRef(ref)) !== null) {\n response = await request();\n }\n\n if (throwOnError && !response.ok) {\n throw new ApiError(response);\n }\n\n return response;\n}\n\nexport async function apiJson<T>(url: string, init?: ApiInit): Promise<T> {\n return (await apiFetch(url, init)).json() as Promise<T>;\n}\n\nfunction remoteTokenKey(remote: RemoteAccess): string {\n return [remote.source, remote.audience, [...remote.scopes].sort().join(\" \")].join(\"\\u001f\");\n}\n\nexport function clearRemoteTokenCache(): void {\n remoteTokenCache.clear();\n pendingRemoteTokens.clear();\n}\n\nexport function invalidateRemoteToken(remote: RemoteAccess): void {\n remoteTokenCache.delete(remoteTokenKey(remote));\n}\n\nexport async function remoteToken(remote: RemoteAccess): Promise<BrowserToken> {\n const key = remoteTokenKey(remote);\n const cached = remoteTokenCache.get(key);\n\n if (cached && cached.expiresAt > Date.now() + tokenRefreshSkewMs) {\n return cached.token;\n }\n\n const pending = pendingRemoteTokens.get(key);\n\n if (pending) {\n return pending;\n }\n\n const request = apiJson<BrowserToken>(remote.tokenEndpoint, {\n method: \"POST\",\n ref: remote.ref,\n body: JSON.stringify({\n nodeId: remote.nodeId,\n nodeType: remote.nodeType,\n audience: remote.audience,\n scopes: remote.scopes,\n }),\n })\n .then((token) => {\n remoteTokenCache.set(key, {\n token,\n expiresAt: Date.now() + Math.max(0, token.expiresIn) * 1000,\n });\n\n return token;\n })\n .finally(() => {\n pendingRemoteTokens.delete(key);\n });\n\n pendingRemoteTokens.set(key, request);\n\n return request;\n}\n\nfunction isUnauthorized(response: Response): boolean {\n return response.status === 401 || response.status === 403;\n}\n\nasync function fetchRemoteWithToken(\n url: string,\n init: Omit<RemoteInit, \"remote\" | \"retryOnUnauthorized\" | \"throwOnError\">,\n token: BrowserToken,\n): Promise<Response> {\n const { headers, ...rest } = init;\n\n return fetch(url, {\n ...rest,\n credentials: \"omit\",\n headers: {\n Accept: \"application/json\",\n ...localeHeader(),\n ...headers,\n Authorization: `${token.tokenType} ${token.accessToken}`,\n },\n });\n}\n\nexport async function remoteFetch(url: string, init: RemoteInit): Promise<Response> {\n const { remote, retryOnUnauthorized = true, throwOnError = true, ...request } = init;\n let token = await remoteToken(remote);\n let response = await fetchRemoteWithToken(url, request, token);\n\n if (retryOnUnauthorized && isUnauthorized(response)) {\n invalidateRemoteToken(remote);\n token = await remoteToken(remote);\n response = await fetchRemoteWithToken(url, request, token);\n }\n\n if (throwOnError && !response.ok) {\n throw new ApiError(response);\n }\n\n return response;\n}\n\nexport async function remoteJson<T>(url: string, init: RemoteInit): Promise<T> {\n return (await remoteFetch(url, init)).json() as Promise<T>;\n}\n"],"mappings":";;;;;;;;;;;AAgBA,IAAa,WAAb,cAA8B,MAAM;CACb;CAArB,YAAY,UAA6B;EACvC,MAAM,QAAQ,SAAS,QAAQ;EADZ,KAAA,WAAA;CAErB;AACF;;;;;AAMA,SAAgB,YAAoB;CAClC,MAAM,QAAQ,SAAS,OAAO,MAAM,oBAAoB;CAExD,OAAO,QAAQ,mBAAmB,MAAM,EAAE,IAAI;AAChD;AAuBA,IAAM,qBAAqB;AAC3B,IAAM,mCAAmB,IAAI,IAAgC;AAC7D,IAAM,sCAAsB,IAAI,IAAmC;AAEnE,SAAS,eAAe,QAAoD;CAG1E,IAAI,EAFY,WAAW,KAAA,KAAa,WAAW,SAAS,WAAW,SAGrE,OAAO,EAAE,QAAQ,mBAAmB;CAGtC,OAAO;EACL,QAAQ;EACR,gBAAgB;EAChB,oBAAoB;EACpB,gBAAgB,UAAU;CAC5B;AACF;AAEA,IAAI,qBAAqB;;;;;AAMzB,SAAgB,sBAAsB,KAAmB;CACvD,qBAAqB;AACvB;AAEA,IAAM,sCAAsB,IAAI,IAAoC;;;;;;;;AASpE,eAAsB,WAAW,cAA8C;CAC7E,MAAM,UAAU,oBAAoB,IAAI,YAAY;CAEpD,IAAI,SACF,OAAO;CAGT,MAAM,UAAU,QAAyB,oBAAoB;EAC3D,QAAQ;EACR,MAAM,KAAK,UAAU,EAAE,KAAK,UAAU,YAAY,EAAE,CAAC;CACvD,CAAC,CAAC,CACC,MAAM,SAAS;EACd,kBAAkB,cAAc,KAAK,GAAG;EAExC,OAAO,KAAK;CACd,CAAC,CAAC,CACD,YAAY,IAAI,CAAC,CACjB,cAAc;EACb,oBAAoB,OAAO,YAAY;CACzC,CAAC;CAEH,oBAAoB,IAAI,cAAc,OAAO;CAE7C,OAAO;AACT;AAEA,eAAsB,SAAS,KAAa,OAAgB,CAAC,GAAsB;CACjF,MAAM,EAAE,MAAM,IAAI,SAAS,eAAe,MAAM,QAAQ,GAAG,SAAS;CAGpE,MAAM,mBAAmB,QAAQ,YAAY;CAC7C,MAAM,gBACJ,MAAM,KAAK;EACT,aAAa;EACb,GAAG;EACH,QAAQ;EAGR,SAAS,YAAY,KAAK;GAAE,GAAG,eAAe,gBAAgB;GAAG,GAAG;EAAQ,CAAC;CAC/E,CAAC;CAEH,IAAI,WAAW,MAAM,QAAQ;CAE7B,IAAI,QAAQ,MAAM,SAAS,WAAW,OAAQ,MAAM,WAAW,GAAG,MAAO,MACvE,WAAW,MAAM,QAAQ;CAG3B,IAAI,gBAAgB,CAAC,SAAS,IAC5B,MAAM,IAAI,SAAS,QAAQ;CAG7B,OAAO;AACT;AAEA,eAAsB,QAAW,KAAa,MAA4B;CACxE,QAAQ,MAAM,SAAS,KAAK,IAAI,EAAA,CAAG,KAAK;AAC1C;AAEA,SAAS,eAAe,QAA8B;CACpD,OAAO;EAAC,OAAO;EAAQ,OAAO;EAAU,CAAC,GAAG,OAAO,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG;CAAC,CAAC,CAAC,KAAK,GAAQ;AAC5F;AAEA,SAAgB,wBAA8B;CAC5C,iBAAiB,MAAM;CACvB,oBAAoB,MAAM;AAC5B;AAEA,SAAgB,sBAAsB,QAA4B;CAChE,iBAAiB,OAAO,eAAe,MAAM,CAAC;AAChD;AAEA,eAAsB,YAAY,QAA6C;CAC7E,MAAM,MAAM,eAAe,MAAM;CACjC,MAAM,SAAS,iBAAiB,IAAI,GAAG;CAEvC,IAAI,UAAU,OAAO,YAAY,KAAK,IAAI,IAAI,oBAC5C,OAAO,OAAO;CAGhB,MAAM,UAAU,oBAAoB,IAAI,GAAG;CAE3C,IAAI,SACF,OAAO;CAGT,MAAM,UAAU,QAAsB,OAAO,eAAe;EAC1D,QAAQ;EACR,KAAK,OAAO;EACZ,MAAM,KAAK,UAAU;GACnB,QAAQ,OAAO;GACf,UAAU,OAAO;GACjB,UAAU,OAAO;GACjB,QAAQ,OAAO;EACjB,CAAC;CACH,CAAC,CAAC,CACC,MAAM,UAAU;EACf,iBAAiB,IAAI,KAAK;GACxB;GACA,WAAW,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,MAAM,SAAS,IAAI;EACzD,CAAC;EAED,OAAO;CACT,CAAC,CAAC,CACD,cAAc;EACb,oBAAoB,OAAO,GAAG;CAChC,CAAC;CAEH,oBAAoB,IAAI,KAAK,OAAO;CAEpC,OAAO;AACT;AAEA,SAAS,eAAe,UAA6B;CACnD,OAAO,SAAS,WAAW,OAAO,SAAS,WAAW;AACxD;AAEA,eAAe,qBACb,KACA,MACA,OACmB;CACnB,MAAM,EAAE,SAAS,GAAG,SAAS;CAE7B,OAAO,MAAM,KAAK;EAChB,GAAG;EACH,aAAa;EACb,SAAS;GACP,QAAQ;GACR,GAAG,aAAa;GAChB,GAAG;GACH,eAAe,GAAG,MAAM,UAAU,GAAG,MAAM;EAC7C;CACF,CAAC;AACH;AAEA,eAAsB,YAAY,KAAa,MAAqC;CAClF,MAAM,EAAE,QAAQ,sBAAsB,MAAM,eAAe,MAAM,GAAG,YAAY;CAChF,IAAI,QAAQ,MAAM,YAAY,MAAM;CACpC,IAAI,WAAW,MAAM,qBAAqB,KAAK,SAAS,KAAK;CAE7D,IAAI,uBAAuB,eAAe,QAAQ,GAAG;EACnD,sBAAsB,MAAM;EAC5B,QAAQ,MAAM,YAAY,MAAM;EAChC,WAAW,MAAM,qBAAqB,KAAK,SAAS,KAAK;CAC3D;CAEA,IAAI,gBAAgB,CAAC,SAAS,IAC5B,MAAM,IAAI,SAAS,QAAQ;CAG7B,OAAO;AACT;AAEA,eAAsB,WAAc,KAAa,MAA8B;CAC7E,QAAQ,MAAM,YAAY,KAAK,IAAI,EAAA,CAAG,KAAK;AAC7C"}
1
+ {"version":3,"file":"api.js","names":[],"sources":["../../resources/js/core/api.ts"],"sourcesContent":["/**\n * The single funnel for raw-fetch HTTP, so the cross-cutting header, credential,\n * and error policy has one home and call sites only declare intent. Returns a\n * Response (not parsed data) so one primitive serves JSON, the chat's streaming\n * getReader(), and multipart alike. Not for the Inertia world (router/useHttp),\n * which carries its own headers.\n */\n\nimport { latestRef, storeRefreshedRef } from \"./component-ref\";\nimport { withHeaders } from \"./headers\";\nimport { localeHeader } from \"@lattice-php/lattice/i18n/locale\";\nimport type {\n BrowserToken as GeneratedBrowserToken,\n RemoteAccess as GeneratedRemoteAccess,\n} from \"@lattice-php/lattice/types/generated\";\n\nexport class ApiError extends Error {\n constructor(readonly response: Response) {\n super(`HTTP ${response.status}`);\n }\n}\n\n/**\n * Exported so the few transports that cannot go through apiFetch — an XHR that\n * reports upload progress, for instance — still send the same CSRF header.\n */\nexport function xsrfToken(): string {\n const match = document.cookie.match(/XSRF-TOKEN=([^;]+)/);\n\n return match ? decodeURIComponent(match[1]) : \"\";\n}\n\nexport type ApiInit = Omit<RequestInit, \"headers\"> & {\n ref?: string;\n headers?: Record<string, string>;\n throwOnError?: boolean;\n};\n\nexport type BrowserToken = GeneratedBrowserToken;\nexport type RemoteAccess = GeneratedRemoteAccess;\n\nexport type RemoteInit = Omit<RequestInit, \"credentials\" | \"headers\"> & {\n headers?: Record<string, string>;\n remote: RemoteAccess;\n throwOnError?: boolean;\n};\n\ntype CachedBrowserToken = {\n expiresAt: number;\n token: BrowserToken;\n};\n\nconst tokenRefreshSkewMs = 10_000;\nconst remoteTokenCache = new Map<string, CachedBrowserToken>();\nconst pendingRemoteTokens = new Map<string, Promise<BrowserToken>>();\n\nfunction defaultHeaders(method: string | undefined): Record<string, string> {\n const isWrite = method !== undefined && method !== \"GET\" && method !== \"HEAD\";\n\n if (!isWrite) {\n return { Accept: \"application/json\" };\n }\n\n return {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n \"X-Requested-With\": \"XMLHttpRequest\",\n \"X-XSRF-TOKEN\": xsrfToken(),\n };\n}\n\nlet refRefreshEndpoint = \"/lattice/refs/refresh\";\n\n/**\n * Boot paths call this with the server-minted URL so subdirectory installs\n * refresh against the right path; the default covers root installs.\n */\nexport function setRefRefreshEndpoint(url: string): void {\n refRefreshEndpoint = url;\n}\n\nconst pendingRefRefreshes = new Map<string, Promise<string | null>>();\n\n/**\n * Trade an expired-but-authentic ref for a fresh one. Deduped per original ref\n * so a burst of 403s from one component costs a single round-trip. The renewed\n * token lands in the component-ref map, so every later request that resolves\n * its ref through withHeaders picks it up — this is the transport-agnostic\n * primitive both the fetch funnel below and the Inertia form retry build on.\n */\nexport async function refreshRef(componentRef: string): Promise<string | null> {\n const pending = pendingRefRefreshes.get(componentRef);\n\n if (pending) {\n return pending;\n }\n\n const request = apiJson<{ ref: string }>(refRefreshEndpoint, {\n method: \"POST\",\n body: JSON.stringify({ ref: latestRef(componentRef) }),\n })\n .then((data) => {\n storeRefreshedRef(componentRef, data.ref);\n\n return data.ref;\n })\n .catch(() => null)\n .finally(() => {\n pendingRefRefreshes.delete(componentRef);\n });\n\n pendingRefRefreshes.set(componentRef, request);\n\n return request;\n}\n\nexport async function apiFetch(url: string, init: ApiInit = {}): Promise<Response> {\n const { ref = \"\", headers, throwOnError = true, method, ...rest } = init;\n // Some servers reject a lower-case method line, and fetch only normalizes the\n // standard verbs, so upper-case it here.\n const normalizedMethod = method?.toUpperCase();\n const request = () =>\n fetch(url, {\n credentials: \"same-origin\",\n ...rest,\n method: normalizedMethod,\n // withHeaders resolves the latest refreshed token for the ref, so the\n // retry below picks up the renewal without threading it through.\n headers: withHeaders(ref, { ...defaultHeaders(normalizedMethod), ...headers }),\n });\n\n let response = await request();\n\n if (ref !== \"\" && response.status === 403 && (await refreshRef(ref)) !== null) {\n response = await request();\n }\n\n if (throwOnError && !response.ok) {\n throw new ApiError(response);\n }\n\n return response;\n}\n\nexport async function apiJson<T>(url: string, init?: ApiInit): Promise<T> {\n return (await apiFetch(url, init)).json() as Promise<T>;\n}\n\nfunction remoteTokenKey(remote: RemoteAccess): string {\n return [remote.source, remote.audience, [...remote.scopes].sort().join(\" \")].join(\"\\u001f\");\n}\n\nexport function clearRemoteTokenCache(): void {\n remoteTokenCache.clear();\n pendingRemoteTokens.clear();\n}\n\nexport function invalidateRemoteToken(remote: RemoteAccess): void {\n remoteTokenCache.delete(remoteTokenKey(remote));\n}\n\nexport async function remoteToken(remote: RemoteAccess): Promise<BrowserToken> {\n const key = remoteTokenKey(remote);\n const cached = remoteTokenCache.get(key);\n\n if (cached && cached.expiresAt > Date.now() + tokenRefreshSkewMs) {\n return cached.token;\n }\n\n const pending = pendingRemoteTokens.get(key);\n\n if (pending) {\n return pending;\n }\n\n const request = apiJson<BrowserToken>(remote.tokenEndpoint, {\n method: \"POST\",\n ref: remote.ref,\n body: JSON.stringify({\n nodeId: remote.nodeId,\n nodeType: remote.nodeType,\n audience: remote.audience,\n scopes: remote.scopes,\n }),\n })\n .then((token) => {\n remoteTokenCache.set(key, {\n token,\n expiresAt: Date.now() + Math.max(0, token.expiresIn) * 1000,\n });\n\n return token;\n })\n .finally(() => {\n pendingRemoteTokens.delete(key);\n });\n\n pendingRemoteTokens.set(key, request);\n\n return request;\n}\n\nfunction isUnauthorized(response: Response): boolean {\n return response.status === 401 || response.status === 403;\n}\n\nasync function fetchRemoteWithToken(\n url: string,\n init: Omit<RemoteInit, \"remote\" | \"throwOnError\">,\n token: BrowserToken,\n): Promise<Response> {\n const { headers, ...rest } = init;\n\n return fetch(url, {\n ...rest,\n credentials: \"omit\",\n headers: {\n Accept: \"application/json\",\n ...localeHeader(),\n ...headers,\n Authorization: `${token.tokenType} ${token.accessToken}`,\n },\n });\n}\n\nexport async function remoteFetch(url: string, init: RemoteInit): Promise<Response> {\n const { remote, throwOnError = true, ...request } = init;\n let token = await remoteToken(remote);\n let response = await fetchRemoteWithToken(url, request, token);\n\n if (isUnauthorized(response)) {\n invalidateRemoteToken(remote);\n token = await remoteToken(remote);\n response = await fetchRemoteWithToken(url, request, token);\n }\n\n if (throwOnError && !response.ok) {\n throw new ApiError(response);\n }\n\n return response;\n}\n\nexport async function remoteJson<T>(url: string, init: RemoteInit): Promise<T> {\n return (await remoteFetch(url, init)).json() as Promise<T>;\n}\n"],"mappings":"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"}
@@ -5,10 +5,10 @@ export type SizableColumn = {
5
5
  width: ColumnWidth;
6
6
  };
7
7
  export declare const DEFAULT_COLUMN_WIDTH: ColumnWidth;
8
+ export declare const maxColumnWidthPx = 1024;
8
9
  export declare function columnWidthTrack(width: ColumnWidth): string;
9
10
  export declare function minColumnWidthPx(column: SizableColumn): number;
10
11
  export declare function defaultColumnWidthPx(column: SizableColumn): number;
11
- export declare function maxColumnWidthPx(column: SizableColumn): number;
12
12
  export declare function buildColumnGridTemplate({ columns, leadingTracks, trailingTracks, overrides, }: {
13
13
  columns: SizableColumn[];
14
14
  leadingTracks?: string[];
@@ -4,34 +4,30 @@ var tracks = {
4
4
  xs: {
5
5
  track: "minmax(4rem, 0.35fr)",
6
6
  minPx: 64,
7
- defaultPx: 96,
8
- maxPx: 1024
7
+ defaultPx: 96
9
8
  },
10
9
  sm: {
11
10
  track: "minmax(6rem, 0.5fr)",
12
11
  minPx: 96,
13
- defaultPx: 128,
14
- maxPx: 1024
12
+ defaultPx: 128
15
13
  },
16
14
  md: {
17
15
  track: "minmax(8rem, 1fr)",
18
16
  minPx: 128,
19
- defaultPx: 176,
20
- maxPx: 1024
17
+ defaultPx: 176
21
18
  },
22
19
  lg: {
23
20
  track: "minmax(12rem, 1.5fr)",
24
21
  minPx: 192,
25
- defaultPx: 240,
26
- maxPx: 1024
22
+ defaultPx: 240
27
23
  },
28
24
  xl: {
29
25
  track: "minmax(16rem, 2fr)",
30
26
  minPx: 256,
31
- defaultPx: 320,
32
- maxPx: 1024
27
+ defaultPx: 320
33
28
  }
34
29
  };
30
+ var maxColumnWidthPx = 1024;
35
31
  function columnWidthTrack(width) {
36
32
  return tracks[width].track;
37
33
  }
@@ -41,15 +37,12 @@ function minColumnWidthPx(column) {
41
37
  function defaultColumnWidthPx(column) {
42
38
  return tracks[column.width].defaultPx;
43
39
  }
44
- function maxColumnWidthPx(column) {
45
- return tracks[column.width].maxPx;
46
- }
47
40
  function buildColumnGridTemplate({ columns, leadingTracks = [], trailingTracks = [], overrides = {} }) {
48
41
  return [
49
42
  ...leadingTracks,
50
43
  ...columns.map((column) => {
51
44
  const override = overrides[column.key];
52
- if (override !== void 0) return `${Math.min(maxColumnWidthPx(column), Math.max(minColumnWidthPx(column), override))}px`;
45
+ if (override !== void 0) return `${Math.min(maxColumnWidthPx, Math.max(minColumnWidthPx(column), override))}px`;
53
46
  return columnWidthTrack(column.width);
54
47
  }),
55
48
  ...trailingTracks
@@ -1 +1 @@
1
- {"version":3,"file":"column-sizing.js","names":[],"sources":["../../../resources/js/core/hooks/column-sizing.ts"],"sourcesContent":["import type { ColumnWidth } from \"@lattice-php/lattice/types/generated\";\n\nexport type SizableColumn = {\n key: string;\n label?: string | null;\n width: ColumnWidth;\n};\n\nexport const DEFAULT_COLUMN_WIDTH: ColumnWidth = \"md\";\n\nconst tracks: Record<\n ColumnWidth,\n { track: string; minPx: number; defaultPx: number; maxPx: number }\n> = {\n xs: { track: \"minmax(4rem, 0.35fr)\", minPx: 64, defaultPx: 96, maxPx: 1024 },\n sm: { track: \"minmax(6rem, 0.5fr)\", minPx: 96, defaultPx: 128, maxPx: 1024 },\n md: { track: \"minmax(8rem, 1fr)\", minPx: 128, defaultPx: 176, maxPx: 1024 },\n lg: { track: \"minmax(12rem, 1.5fr)\", minPx: 192, defaultPx: 240, maxPx: 1024 },\n xl: { track: \"minmax(16rem, 2fr)\", minPx: 256, defaultPx: 320, maxPx: 1024 },\n};\n\nexport function columnWidthTrack(width: ColumnWidth): string {\n return tracks[width].track;\n}\n\nexport function minColumnWidthPx(column: SizableColumn): number {\n return tracks[column.width].minPx;\n}\n\nexport function defaultColumnWidthPx(column: SizableColumn): number {\n return tracks[column.width].defaultPx;\n}\n\nexport function maxColumnWidthPx(column: SizableColumn): number {\n return tracks[column.width].maxPx;\n}\n\nexport function buildColumnGridTemplate({\n columns,\n leadingTracks = [],\n trailingTracks = [],\n overrides = {},\n}: {\n columns: SizableColumn[];\n leadingTracks?: string[];\n trailingTracks?: string[];\n overrides?: Record<string, number | undefined>;\n}): string {\n return [\n ...leadingTracks,\n ...columns.map((column) => {\n const override = overrides[column.key];\n\n if (override !== undefined) {\n return `${Math.min(maxColumnWidthPx(column), Math.max(minColumnWidthPx(column), override))}px`;\n }\n\n return columnWidthTrack(column.width);\n }),\n ...trailingTracks,\n ].join(\" \");\n}\n"],"mappings":";AAQA,IAAa,uBAAoC;AAEjD,IAAM,SAGF;CACF,IAAI;EAAE,OAAO;EAAwB,OAAO;EAAI,WAAW;EAAI,OAAO;CAAK;CAC3E,IAAI;EAAE,OAAO;EAAuB,OAAO;EAAI,WAAW;EAAK,OAAO;CAAK;CAC3E,IAAI;EAAE,OAAO;EAAqB,OAAO;EAAK,WAAW;EAAK,OAAO;CAAK;CAC1E,IAAI;EAAE,OAAO;EAAwB,OAAO;EAAK,WAAW;EAAK,OAAO;CAAK;CAC7E,IAAI;EAAE,OAAO;EAAsB,OAAO;EAAK,WAAW;EAAK,OAAO;CAAK;AAC7E;AAEA,SAAgB,iBAAiB,OAA4B;CAC3D,OAAO,OAAO,MAAM,CAAC;AACvB;AAEA,SAAgB,iBAAiB,QAA+B;CAC9D,OAAO,OAAO,OAAO,MAAM,CAAC;AAC9B;AAEA,SAAgB,qBAAqB,QAA+B;CAClE,OAAO,OAAO,OAAO,MAAM,CAAC;AAC9B;AAEA,SAAgB,iBAAiB,QAA+B;CAC9D,OAAO,OAAO,OAAO,MAAM,CAAC;AAC9B;AAEA,SAAgB,wBAAwB,EACtC,SACA,gBAAgB,CAAC,GACjB,iBAAiB,CAAC,GAClB,YAAY,CAAC,KAMJ;CACT,OAAO;EACL,GAAG;EACH,GAAG,QAAQ,KAAK,WAAW;GACzB,MAAM,WAAW,UAAU,OAAO;GAElC,IAAI,aAAa,KAAA,GACf,OAAO,GAAG,KAAK,IAAI,iBAAiB,MAAM,GAAG,KAAK,IAAI,iBAAiB,MAAM,GAAG,QAAQ,CAAC,EAAE;GAG7F,OAAO,iBAAiB,OAAO,KAAK;EACtC,CAAC;EACD,GAAG;CACL,CAAC,CAAC,KAAK,GAAG;AACZ"}
1
+ {"version":3,"file":"column-sizing.js","names":[],"sources":["../../../resources/js/core/hooks/column-sizing.ts"],"sourcesContent":["import type { ColumnWidth } from \"@lattice-php/lattice/types/generated\";\n\nexport type SizableColumn = {\n key: string;\n label?: string | null;\n width: ColumnWidth;\n};\n\nexport const DEFAULT_COLUMN_WIDTH: ColumnWidth = \"md\";\n\nconst tracks: Record<ColumnWidth, { track: string; minPx: number; defaultPx: number }> = {\n xs: { track: \"minmax(4rem, 0.35fr)\", minPx: 64, defaultPx: 96 },\n sm: { track: \"minmax(6rem, 0.5fr)\", minPx: 96, defaultPx: 128 },\n md: { track: \"minmax(8rem, 1fr)\", minPx: 128, defaultPx: 176 },\n lg: { track: \"minmax(12rem, 1.5fr)\", minPx: 192, defaultPx: 240 },\n xl: { track: \"minmax(16rem, 2fr)\", minPx: 256, defaultPx: 320 },\n};\n\nexport const maxColumnWidthPx = 1024;\n\nexport function columnWidthTrack(width: ColumnWidth): string {\n return tracks[width].track;\n}\n\nexport function minColumnWidthPx(column: SizableColumn): number {\n return tracks[column.width].minPx;\n}\n\nexport function defaultColumnWidthPx(column: SizableColumn): number {\n return tracks[column.width].defaultPx;\n}\n\nexport function buildColumnGridTemplate({\n columns,\n leadingTracks = [],\n trailingTracks = [],\n overrides = {},\n}: {\n columns: SizableColumn[];\n leadingTracks?: string[];\n trailingTracks?: string[];\n overrides?: Record<string, number | undefined>;\n}): string {\n return [\n ...leadingTracks,\n ...columns.map((column) => {\n const override = overrides[column.key];\n\n if (override !== undefined) {\n return `${Math.min(maxColumnWidthPx, Math.max(minColumnWidthPx(column), override))}px`;\n }\n\n return columnWidthTrack(column.width);\n }),\n ...trailingTracks,\n ].join(\" \");\n}\n"],"mappings":";AAQA,IAAa,uBAAoC;AAEjD,IAAM,SAAmF;CACvF,IAAI;EAAE,OAAO;EAAwB,OAAO;EAAI,WAAW;CAAG;CAC9D,IAAI;EAAE,OAAO;EAAuB,OAAO;EAAI,WAAW;CAAI;CAC9D,IAAI;EAAE,OAAO;EAAqB,OAAO;EAAK,WAAW;CAAI;CAC7D,IAAI;EAAE,OAAO;EAAwB,OAAO;EAAK,WAAW;CAAI;CAChE,IAAI;EAAE,OAAO;EAAsB,OAAO;EAAK,WAAW;CAAI;AAChE;AAEA,IAAa,mBAAmB;AAEhC,SAAgB,iBAAiB,OAA4B;CAC3D,OAAO,OAAO,MAAM,CAAC;AACvB;AAEA,SAAgB,iBAAiB,QAA+B;CAC9D,OAAO,OAAO,OAAO,MAAM,CAAC;AAC9B;AAEA,SAAgB,qBAAqB,QAA+B;CAClE,OAAO,OAAO,OAAO,MAAM,CAAC;AAC9B;AAEA,SAAgB,wBAAwB,EACtC,SACA,gBAAgB,CAAC,GACjB,iBAAiB,CAAC,GAClB,YAAY,CAAC,KAMJ;CACT,OAAO;EACL,GAAG;EACH,GAAG,QAAQ,KAAK,WAAW;GACzB,MAAM,WAAW,UAAU,OAAO;GAElC,IAAI,aAAa,KAAA,GACf,OAAO,GAAG,KAAK,IAAI,kBAAkB,KAAK,IAAI,iBAAiB,MAAM,GAAG,QAAQ,CAAC,EAAE;GAGrF,OAAO,iBAAiB,OAAO,KAAK;EACtC,CAAC;EACD,GAAG;CACL,CAAC,CAAC,KAAK,GAAG;AACZ"}
@@ -1,10 +1,15 @@
1
+ import { usePersistentState } from "../../lib/use-persistent-state.js";
1
2
  import { buildColumnGridTemplate, defaultColumnWidthPx, maxColumnWidthPx, minColumnWidthPx } from "./column-sizing.js";
2
- import { useCallback, useMemo, useRef, useState } from "react";
3
+ import { useCallback, useMemo, useRef } from "react";
3
4
  //#region resources/js/core/hooks/use-column-resizing.ts
4
5
  var emptyTracks = [];
5
6
  function useColumnResizing({ columnGapPx = 0, columns, enabled, leadingTracks = emptyTracks, showIndicator = false, storageKey, trailingTracks = emptyTracks }) {
6
7
  const columnKeys = useMemo(() => columns.map((column) => column.key), [columns]);
7
- const [overrides, setOverrides] = useState(() => readStoredOverrides(storageKey, columns));
8
+ const [overrides, setOverrides] = usePersistentState(storageKey ?? "", {}, {
9
+ enabled: Boolean(storageKey),
10
+ parse: (raw) => parseStoredOverrides(raw, columns),
11
+ serialize: (value) => serializeOverrides(value, columnKeys)
12
+ });
8
13
  const overridesRef = useRef(overrides);
9
14
  const drag = useRef(null);
10
15
  const resizeRootRef = useRef(null);
@@ -29,17 +34,15 @@ function useColumnResizing({ columnGapPx = 0, columns, enabled, leadingTracks =
29
34
  const commitOverrides = useCallback((next) => {
30
35
  overridesRef.current = next;
31
36
  setOverrides(next);
32
- writeStoredOverrides(storageKey, columnKeys, next);
33
37
  applyTemplate(templateForOverrides(next));
34
38
  }, [
35
39
  applyTemplate,
36
- columnKeys,
37
- storageKey,
40
+ setOverrides,
38
41
  templateForOverrides
39
42
  ]);
40
43
  const overridesWithColumnWidth = useCallback((current, column, width, maxWidth) => ({
41
44
  ...current,
42
- [column.key]: Math.min(maxWidth ?? maxColumnWidthPx(column), Math.max(minColumnWidthPx(column), width))
45
+ [column.key]: Math.min(maxWidth ?? 1024, Math.max(minColumnWidthPx(column), width))
43
46
  }), []);
44
47
  const setColumnWidth = useCallback((column, width, maxWidth) => {
45
48
  commitOverrides(overridesWithColumnWidth(overridesRef.current, column, width, maxWidth));
@@ -56,7 +59,7 @@ function useColumnResizing({ columnGapPx = 0, columns, enabled, leadingTracks =
56
59
  const currentColumnWidth = useCallback((column) => overridesRef.current[column.key] ?? defaultColumnWidthPx(column), []);
57
60
  return {
58
61
  getResizeHandleProps: useCallback((column) => {
59
- const max = maxColumnWidthPx(column);
62
+ const max = maxColumnWidthPx;
60
63
  const min = minColumnWidthPx(column);
61
64
  const current = currentColumnWidth(column);
62
65
  const label = column.label ?? column.key;
@@ -167,69 +170,34 @@ function useColumnResizing({ columnGapPx = 0, columns, enabled, leadingTracks =
167
170
  }
168
171
  function maxColumnWidthForGrid({ column, columnGapPx, columns, grid, leadingTracks, trailingTracks }) {
169
172
  const gridWidth = grid?.getBoundingClientRect().width ?? 0;
170
- if (gridWidth <= 0) return maxColumnWidthPx(column);
173
+ if (gridWidth <= 0) return maxColumnWidthPx;
171
174
  const utilityWidth = [...leadingTracks, ...trailingTracks].reduce((sum, track) => sum + fixedTrackWidthPx(track), 0);
172
175
  const siblingMinWidth = columns.reduce((sum, sibling) => sibling.key === column.key ? sum : sum + minColumnWidthPx(sibling), 0);
173
176
  const trackCount = columns.length + leadingTracks.length + trailingTracks.length;
174
177
  const gapWidth = Math.max(0, trackCount - 1) * columnGapPx;
175
178
  const available = gridWidth - utilityWidth - siblingMinWidth - gapWidth;
176
- return Math.min(maxColumnWidthPx(column), Math.max(minColumnWidthPx(column), available));
179
+ return Math.min(maxColumnWidthPx, Math.max(minColumnWidthPx(column), available));
177
180
  }
178
- function readStoredOverrides(storageKey, columns) {
179
- if (!storageKey || typeof window === "undefined") return {};
180
- try {
181
- const raw = window.localStorage.getItem(storageKey);
182
- if (raw === null) return {};
183
- const stored = JSON.parse(raw);
184
- if (!isStoredColumnWidths(stored)) {
185
- removeStoredOverrides(storageKey);
186
- return {};
187
- }
188
- const overrides = sanitizeOverrides(stored.overrides, columns);
189
- if (Object.keys(overrides).length === 0) removeStoredOverrides(storageKey);
190
- return overrides;
191
- } catch {
192
- removeStoredOverrides(storageKey);
193
- return {};
194
- }
181
+ function parseStoredOverrides(raw, columns) {
182
+ const overrides = JSON.parse(raw)?.overrides;
183
+ if (typeof overrides !== "object" || overrides === null || Array.isArray(overrides)) throw new Error("unexpected stored column widths shape");
184
+ const sanitized = sanitizeOverrides(overrides, columns);
185
+ if (Object.keys(sanitized).length === 0) throw new Error("stored column widths hold no usable overrides");
186
+ return sanitized;
195
187
  }
196
- function writeStoredOverrides(storageKey, columnKeys, overrides) {
197
- if (!storageKey || typeof window === "undefined") return;
188
+ function serializeOverrides(overrides, columnKeys) {
198
189
  const stored = {};
199
190
  const knownKeys = new Set(columnKeys);
200
191
  for (const [key, value] of Object.entries(overrides)) if (knownKeys.has(key) && typeof value === "number" && Number.isFinite(value)) stored[key] = value;
201
- if (Object.keys(stored).length === 0) {
202
- removeStoredOverrides(storageKey);
203
- return;
204
- }
205
- try {
206
- window.localStorage.setItem(storageKey, JSON.stringify({
207
- columns: columnKeys,
208
- overrides: stored
209
- }));
210
- } catch {
211
- return;
212
- }
213
- }
214
- function removeStoredOverrides(storageKey) {
215
- if (typeof window === "undefined") return;
216
- try {
217
- window.localStorage.removeItem(storageKey);
218
- } catch {
219
- return;
220
- }
221
- }
222
- function isStoredColumnWidths(value) {
223
- if (typeof value !== "object" || value === null) return false;
224
- const record = value;
225
- return Array.isArray(record.columns) && record.columns.every((column) => typeof column === "string") && typeof record.overrides === "object" && record.overrides !== null && !Array.isArray(record.overrides);
192
+ if (Object.keys(stored).length === 0) return null;
193
+ return JSON.stringify({ overrides: stored });
226
194
  }
227
195
  function sanitizeOverrides(overrides, columns) {
228
196
  const next = {};
229
197
  for (const column of columns) {
230
198
  const value = overrides[column.key];
231
199
  if (typeof value !== "number" || !Number.isFinite(value)) continue;
232
- next[column.key] = Math.min(maxColumnWidthPx(column), Math.max(minColumnWidthPx(column), value));
200
+ next[column.key] = Math.min(maxColumnWidthPx, Math.max(minColumnWidthPx(column), value));
233
201
  }
234
202
  return next;
235
203
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-column-resizing.js","names":[],"sources":["../../../resources/js/core/hooks/use-column-resizing.ts"],"sourcesContent":["import type { HTMLAttributes, KeyboardEvent, PointerEvent } from \"react\";\nimport { useCallback, useMemo, useRef, useState } from \"react\";\nimport {\n buildColumnGridTemplate,\n defaultColumnWidthPx,\n maxColumnWidthPx,\n minColumnWidthPx,\n type SizableColumn,\n} from \"./column-sizing\";\n\ntype DragState = {\n key: string;\n maxWidth: number;\n overrides: Record<string, number | undefined> | null;\n startWidth: number;\n startX: number;\n};\n\ntype ResizeHandleProps = HTMLAttributes<HTMLDivElement> & {\n \"aria-label\": string;\n \"aria-orientation\": \"vertical\";\n \"aria-valuemax\": number;\n \"aria-valuemin\": number;\n \"aria-valuenow\": number;\n role: \"separator\";\n tabIndex: number;\n};\n\ntype StoredColumnWidths = {\n columns: string[];\n overrides: Record<string, number>;\n};\n\nconst emptyTracks: string[] = [];\n\nexport function useColumnResizing({\n columnGapPx = 0,\n columns,\n enabled,\n leadingTracks = emptyTracks,\n showIndicator = false,\n storageKey,\n trailingTracks = emptyTracks,\n}: {\n columnGapPx?: number;\n columns: SizableColumn[];\n enabled: boolean;\n leadingTracks?: string[];\n showIndicator?: boolean;\n storageKey?: string;\n trailingTracks?: string[];\n}) {\n const columnKeys = useMemo(() => columns.map((column) => column.key), [columns]);\n const [overrides, setOverrides] = useState<Record<string, number | undefined>>(() =>\n readStoredOverrides(storageKey, columns),\n );\n const overridesRef = useRef(overrides);\n const drag = useRef<DragState | null>(null);\n const resizeRootRef = useRef<HTMLDivElement | null>(null);\n\n const templateForOverrides = useCallback(\n (nextOverrides: Record<string, number | undefined>): string =>\n buildColumnGridTemplate({\n columns,\n leadingTracks,\n trailingTracks,\n overrides: enabled ? nextOverrides : {},\n }),\n [columns, enabled, leadingTracks, trailingTracks],\n );\n\n const gridTemplateColumns = useMemo(\n () => templateForOverrides(overrides),\n [overrides, templateForOverrides],\n );\n\n const applyTemplate = useCallback((template: string): void => {\n const root = resizeRootRef.current;\n\n if (!root) {\n return;\n }\n\n root.style.gridTemplateColumns = template;\n root.style.setProperty(\"--lattice-table-columns\", template);\n }, []);\n\n const commitOverrides = useCallback(\n (next: Record<string, number | undefined>) => {\n overridesRef.current = next;\n setOverrides(next);\n writeStoredOverrides(storageKey, columnKeys, next);\n applyTemplate(templateForOverrides(next));\n },\n [applyTemplate, columnKeys, storageKey, templateForOverrides],\n );\n\n const overridesWithColumnWidth = useCallback(\n (\n current: Record<string, number | undefined>,\n column: SizableColumn,\n width: number,\n maxWidth?: number,\n ): Record<string, number | undefined> => ({\n ...current,\n [column.key]: Math.min(\n maxWidth ?? maxColumnWidthPx(column),\n Math.max(minColumnWidthPx(column), width),\n ),\n }),\n [],\n );\n\n const setColumnWidth = useCallback(\n (column: SizableColumn, width: number, maxWidth?: number) => {\n commitOverrides(overridesWithColumnWidth(overridesRef.current, column, width, maxWidth));\n },\n [commitOverrides, overridesWithColumnWidth],\n );\n\n const resetColumnWidth = useCallback(\n (column: SizableColumn) => {\n const next = { ...overridesRef.current };\n delete next[column.key];\n\n commitOverrides(next);\n },\n [commitOverrides],\n );\n\n const resetColumns = useCallback(() => {\n commitOverrides({});\n }, [commitOverrides]);\n\n const hasOverrides =\n enabled && Object.values(overrides).some((width) => typeof width === \"number\");\n\n const currentColumnWidth = useCallback(\n (column: SizableColumn): number =>\n overridesRef.current[column.key] ?? defaultColumnWidthPx(column),\n [],\n );\n\n const getResizeHandleProps = useCallback(\n (column: SizableColumn): ResizeHandleProps => {\n const max = maxColumnWidthPx(column);\n const min = minColumnWidthPx(column);\n const current = currentColumnWidth(column);\n const label = column.label ?? column.key;\n const indicatorClass = showIndicator ? \"after:bg-lt-border\" : \"after:bg-transparent\";\n\n const maxWidthForHandle = (handle: HTMLDivElement): number =>\n maxColumnWidthForGrid({\n column,\n columnGapPx,\n columns,\n grid: resizeRootRef.current ?? handle.parentElement?.parentElement,\n leadingTracks,\n trailingTracks,\n });\n\n const resizeBy = (handle: HTMLDivElement, delta: number): void =>\n setColumnWidth(column, current + delta, maxWidthForHandle(handle));\n\n const finishDrag = (event: PointerEvent<HTMLDivElement>, releaseCapture: boolean): void => {\n const active = drag.current;\n\n if (active?.key !== column.key) {\n return;\n }\n\n drag.current = null;\n if (active.overrides !== null) {\n commitOverrides(active.overrides);\n }\n\n if (releaseCapture) {\n event.currentTarget.releasePointerCapture?.(event.pointerId);\n }\n };\n\n return {\n \"aria-label\": `Resize ${label}`,\n \"aria-orientation\": \"vertical\",\n \"aria-valuemax\": max,\n \"aria-valuemin\": min,\n \"aria-valuenow\": current,\n className: `absolute inset-y-0 right-0 hidden w-2 cursor-col-resize touch-none items-stretch justify-center md:flex after:my-1 after:w-px ${indicatorClass} hover:after:bg-lt-border focus-visible:outline-none focus-visible:after:bg-lt-ring`,\n onDoubleClick: () => resetColumnWidth(column),\n onKeyDown: (event: KeyboardEvent<HTMLDivElement>) => {\n if (!enabled) {\n return;\n }\n\n const step = event.shiftKey ? 32 : 8;\n\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n resizeBy(event.currentTarget, -step);\n }\n\n if (event.key === \"ArrowRight\") {\n event.preventDefault();\n resizeBy(event.currentTarget, step);\n }\n\n if (event.key === \"Home\") {\n event.preventDefault();\n setColumnWidth(column, min);\n }\n\n if (event.key === \"End\") {\n event.preventDefault();\n const handleMax = maxWidthForHandle(event.currentTarget);\n setColumnWidth(column, handleMax, handleMax);\n }\n\n if (event.key === \"Enter\" || event.key === \"Escape\") {\n event.preventDefault();\n resetColumnWidth(column);\n }\n },\n onPointerDown: (event: PointerEvent<HTMLDivElement>) => {\n if (!enabled) {\n return;\n }\n\n const parentWidth = event.currentTarget.parentElement?.getBoundingClientRect().width ?? 0;\n drag.current = {\n key: column.key,\n maxWidth: maxWidthForHandle(event.currentTarget),\n overrides: null,\n startWidth: parentWidth > 0 ? parentWidth : current,\n startX: event.clientX,\n };\n event.currentTarget.setPointerCapture?.(event.pointerId);\n event.preventDefault();\n },\n onPointerMove: (event: PointerEvent<HTMLDivElement>) => {\n const active = drag.current;\n\n if (!enabled || active?.key !== column.key) {\n return;\n }\n\n const next = overridesWithColumnWidth(\n overridesRef.current,\n column,\n active.startWidth + event.clientX - active.startX,\n active.maxWidth,\n );\n\n active.overrides = next;\n overridesRef.current = next;\n applyTemplate(templateForOverrides(next));\n },\n onPointerUp: (event: PointerEvent<HTMLDivElement>) => {\n finishDrag(event, true);\n },\n onPointerCancel: (event: PointerEvent<HTMLDivElement>) => {\n finishDrag(event, true);\n },\n onLostPointerCapture: (event: PointerEvent<HTMLDivElement>) => {\n finishDrag(event, false);\n },\n role: \"separator\",\n tabIndex: 0,\n };\n },\n [\n columnGapPx,\n columns,\n currentColumnWidth,\n enabled,\n leadingTracks,\n applyTemplate,\n commitOverrides,\n overridesWithColumnWidth,\n resetColumnWidth,\n setColumnWidth,\n showIndicator,\n templateForOverrides,\n trailingTracks,\n ],\n );\n\n return {\n getResizeHandleProps,\n gridTemplateColumns,\n hasOverrides,\n resizeRootRef,\n resetColumns,\n resetColumnWidth,\n };\n}\n\nfunction maxColumnWidthForGrid({\n column,\n columnGapPx,\n columns,\n grid,\n leadingTracks,\n trailingTracks,\n}: {\n column: SizableColumn;\n columnGapPx: number;\n columns: SizableColumn[];\n grid: Element | null | undefined;\n leadingTracks: string[];\n trailingTracks: string[];\n}): number {\n const gridWidth = grid?.getBoundingClientRect().width ?? 0;\n\n if (gridWidth <= 0) {\n return maxColumnWidthPx(column);\n }\n\n const utilityWidth = [...leadingTracks, ...trailingTracks].reduce(\n (sum, track) => sum + fixedTrackWidthPx(track),\n 0,\n );\n const siblingMinWidth = columns.reduce(\n (sum, sibling) => (sibling.key === column.key ? sum : sum + minColumnWidthPx(sibling)),\n 0,\n );\n const trackCount = columns.length + leadingTracks.length + trailingTracks.length;\n const gapWidth = Math.max(0, trackCount - 1) * columnGapPx;\n const available = gridWidth - utilityWidth - siblingMinWidth - gapWidth;\n\n return Math.min(maxColumnWidthPx(column), Math.max(minColumnWidthPx(column), available));\n}\n\nfunction readStoredOverrides(\n storageKey: string | undefined,\n columns: SizableColumn[],\n): Record<string, number | undefined> {\n if (!storageKey || typeof window === \"undefined\") {\n return {};\n }\n\n try {\n const raw = window.localStorage.getItem(storageKey);\n\n if (raw === null) {\n return {};\n }\n\n const stored = JSON.parse(raw) as unknown;\n\n if (!isStoredColumnWidths(stored)) {\n removeStoredOverrides(storageKey);\n\n return {};\n }\n\n const overrides = sanitizeOverrides(stored.overrides, columns);\n\n if (Object.keys(overrides).length === 0) {\n removeStoredOverrides(storageKey);\n }\n\n return overrides;\n } catch {\n removeStoredOverrides(storageKey);\n\n return {};\n }\n}\n\nfunction writeStoredOverrides(\n storageKey: string | undefined,\n columnKeys: string[],\n overrides: Record<string, number | undefined>,\n): void {\n if (!storageKey || typeof window === \"undefined\") {\n return;\n }\n\n const stored: Record<string, number> = {};\n const knownKeys = new Set(columnKeys);\n\n for (const [key, value] of Object.entries(overrides)) {\n if (knownKeys.has(key) && typeof value === \"number\" && Number.isFinite(value)) {\n stored[key] = value;\n }\n }\n\n if (Object.keys(stored).length === 0) {\n removeStoredOverrides(storageKey);\n\n return;\n }\n\n try {\n window.localStorage.setItem(\n storageKey,\n JSON.stringify({\n columns: columnKeys,\n overrides: stored,\n }),\n );\n } catch {\n return;\n }\n}\n\nfunction removeStoredOverrides(storageKey: string): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n try {\n window.localStorage.removeItem(storageKey);\n } catch {\n return;\n }\n}\n\nfunction isStoredColumnWidths(value: unknown): value is StoredColumnWidths {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n\n const record = value as Record<string, unknown>;\n\n return (\n Array.isArray(record.columns) &&\n record.columns.every((column) => typeof column === \"string\") &&\n typeof record.overrides === \"object\" &&\n record.overrides !== null &&\n !Array.isArray(record.overrides)\n );\n}\n\nfunction sanitizeOverrides(\n overrides: Record<string, unknown>,\n columns: SizableColumn[],\n): Record<string, number | undefined> {\n const next: Record<string, number | undefined> = {};\n\n for (const column of columns) {\n const value = overrides[column.key];\n\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n continue;\n }\n\n next[column.key] = Math.min(\n maxColumnWidthPx(column),\n Math.max(minColumnWidthPx(column), value),\n );\n }\n\n return next;\n}\n\nfunction fixedTrackWidthPx(track: string): number {\n const value = track.trim();\n const px = value.match(/^([0-9.]+)px$/);\n\n if (px) {\n return Number.parseFloat(px[1]);\n }\n\n const rem = value.match(/^([0-9.]+)rem$/);\n\n if (rem) {\n return Number.parseFloat(rem[1]) * rootFontSizePx();\n }\n\n return 0;\n}\n\nfunction rootFontSizePx(): number {\n if (typeof window === \"undefined\") {\n return 16;\n }\n\n const parsed = Number.parseFloat(window.getComputedStyle(document.documentElement).fontSize);\n\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 16;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-column-resizing.js","names":[],"sources":["../../../resources/js/core/hooks/use-column-resizing.ts"],"sourcesContent":["import type { HTMLAttributes, KeyboardEvent, PointerEvent } from \"react\";\nimport { useCallback, useMemo, useRef } from \"react\";\nimport { usePersistentState } from \"@lattice-php/lattice/lib/use-persistent-state\";\nimport {\n buildColumnGridTemplate,\n defaultColumnWidthPx,\n maxColumnWidthPx,\n minColumnWidthPx,\n type SizableColumn,\n} from \"./column-sizing\";\n\ntype DragState = {\n key: string;\n maxWidth: number;\n overrides: Record<string, number | undefined> | null;\n startWidth: number;\n startX: number;\n};\n\ntype ResizeHandleProps = HTMLAttributes<HTMLDivElement> & {\n \"aria-label\": string;\n \"aria-orientation\": \"vertical\";\n \"aria-valuemax\": number;\n \"aria-valuemin\": number;\n \"aria-valuenow\": number;\n role: \"separator\";\n tabIndex: number;\n};\n\nconst emptyTracks: string[] = [];\n\nexport function useColumnResizing({\n columnGapPx = 0,\n columns,\n enabled,\n leadingTracks = emptyTracks,\n showIndicator = false,\n storageKey,\n trailingTracks = emptyTracks,\n}: {\n columnGapPx?: number;\n columns: SizableColumn[];\n enabled: boolean;\n leadingTracks?: string[];\n showIndicator?: boolean;\n storageKey?: string;\n trailingTracks?: string[];\n}) {\n const columnKeys = useMemo(() => columns.map((column) => column.key), [columns]);\n const [overrides, setOverrides] = usePersistentState<Record<string, number | undefined>>(\n storageKey ?? \"\",\n {},\n {\n enabled: Boolean(storageKey),\n parse: (raw) => parseStoredOverrides(raw, columns),\n serialize: (value) => serializeOverrides(value, columnKeys),\n },\n );\n const overridesRef = useRef(overrides);\n const drag = useRef<DragState | null>(null);\n const resizeRootRef = useRef<HTMLDivElement | null>(null);\n\n const templateForOverrides = useCallback(\n (nextOverrides: Record<string, number | undefined>): string =>\n buildColumnGridTemplate({\n columns,\n leadingTracks,\n trailingTracks,\n overrides: enabled ? nextOverrides : {},\n }),\n [columns, enabled, leadingTracks, trailingTracks],\n );\n\n const gridTemplateColumns = useMemo(\n () => templateForOverrides(overrides),\n [overrides, templateForOverrides],\n );\n\n const applyTemplate = useCallback((template: string): void => {\n const root = resizeRootRef.current;\n\n if (!root) {\n return;\n }\n\n root.style.gridTemplateColumns = template;\n root.style.setProperty(\"--lattice-table-columns\", template);\n }, []);\n\n const commitOverrides = useCallback(\n (next: Record<string, number | undefined>) => {\n overridesRef.current = next;\n setOverrides(next);\n applyTemplate(templateForOverrides(next));\n },\n [applyTemplate, setOverrides, templateForOverrides],\n );\n\n const overridesWithColumnWidth = useCallback(\n (\n current: Record<string, number | undefined>,\n column: SizableColumn,\n width: number,\n maxWidth?: number,\n ): Record<string, number | undefined> => ({\n ...current,\n [column.key]: Math.min(\n maxWidth ?? maxColumnWidthPx,\n Math.max(minColumnWidthPx(column), width),\n ),\n }),\n [],\n );\n\n const setColumnWidth = useCallback(\n (column: SizableColumn, width: number, maxWidth?: number) => {\n commitOverrides(overridesWithColumnWidth(overridesRef.current, column, width, maxWidth));\n },\n [commitOverrides, overridesWithColumnWidth],\n );\n\n const resetColumnWidth = useCallback(\n (column: SizableColumn) => {\n const next = { ...overridesRef.current };\n delete next[column.key];\n\n commitOverrides(next);\n },\n [commitOverrides],\n );\n\n const resetColumns = useCallback(() => {\n commitOverrides({});\n }, [commitOverrides]);\n\n const hasOverrides =\n enabled && Object.values(overrides).some((width) => typeof width === \"number\");\n\n const currentColumnWidth = useCallback(\n (column: SizableColumn): number =>\n overridesRef.current[column.key] ?? defaultColumnWidthPx(column),\n [],\n );\n\n const getResizeHandleProps = useCallback(\n (column: SizableColumn): ResizeHandleProps => {\n const max = maxColumnWidthPx;\n const min = minColumnWidthPx(column);\n const current = currentColumnWidth(column);\n const label = column.label ?? column.key;\n const indicatorClass = showIndicator ? \"after:bg-lt-border\" : \"after:bg-transparent\";\n\n const maxWidthForHandle = (handle: HTMLDivElement): number =>\n maxColumnWidthForGrid({\n column,\n columnGapPx,\n columns,\n grid: resizeRootRef.current ?? handle.parentElement?.parentElement,\n leadingTracks,\n trailingTracks,\n });\n\n const resizeBy = (handle: HTMLDivElement, delta: number): void =>\n setColumnWidth(column, current + delta, maxWidthForHandle(handle));\n\n const finishDrag = (event: PointerEvent<HTMLDivElement>, releaseCapture: boolean): void => {\n const active = drag.current;\n\n if (active?.key !== column.key) {\n return;\n }\n\n drag.current = null;\n if (active.overrides !== null) {\n commitOverrides(active.overrides);\n }\n\n if (releaseCapture) {\n event.currentTarget.releasePointerCapture?.(event.pointerId);\n }\n };\n\n return {\n \"aria-label\": `Resize ${label}`,\n \"aria-orientation\": \"vertical\",\n \"aria-valuemax\": max,\n \"aria-valuemin\": min,\n \"aria-valuenow\": current,\n className: `absolute inset-y-0 right-0 hidden w-2 cursor-col-resize touch-none items-stretch justify-center md:flex after:my-1 after:w-px ${indicatorClass} hover:after:bg-lt-border focus-visible:outline-none focus-visible:after:bg-lt-ring`,\n onDoubleClick: () => resetColumnWidth(column),\n onKeyDown: (event: KeyboardEvent<HTMLDivElement>) => {\n if (!enabled) {\n return;\n }\n\n const step = event.shiftKey ? 32 : 8;\n\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n resizeBy(event.currentTarget, -step);\n }\n\n if (event.key === \"ArrowRight\") {\n event.preventDefault();\n resizeBy(event.currentTarget, step);\n }\n\n if (event.key === \"Home\") {\n event.preventDefault();\n setColumnWidth(column, min);\n }\n\n if (event.key === \"End\") {\n event.preventDefault();\n const handleMax = maxWidthForHandle(event.currentTarget);\n setColumnWidth(column, handleMax, handleMax);\n }\n\n if (event.key === \"Enter\" || event.key === \"Escape\") {\n event.preventDefault();\n resetColumnWidth(column);\n }\n },\n onPointerDown: (event: PointerEvent<HTMLDivElement>) => {\n if (!enabled) {\n return;\n }\n\n const parentWidth = event.currentTarget.parentElement?.getBoundingClientRect().width ?? 0;\n drag.current = {\n key: column.key,\n maxWidth: maxWidthForHandle(event.currentTarget),\n overrides: null,\n startWidth: parentWidth > 0 ? parentWidth : current,\n startX: event.clientX,\n };\n event.currentTarget.setPointerCapture?.(event.pointerId);\n event.preventDefault();\n },\n onPointerMove: (event: PointerEvent<HTMLDivElement>) => {\n const active = drag.current;\n\n if (!enabled || active?.key !== column.key) {\n return;\n }\n\n const next = overridesWithColumnWidth(\n overridesRef.current,\n column,\n active.startWidth + event.clientX - active.startX,\n active.maxWidth,\n );\n\n active.overrides = next;\n overridesRef.current = next;\n applyTemplate(templateForOverrides(next));\n },\n onPointerUp: (event: PointerEvent<HTMLDivElement>) => {\n finishDrag(event, true);\n },\n onPointerCancel: (event: PointerEvent<HTMLDivElement>) => {\n finishDrag(event, true);\n },\n onLostPointerCapture: (event: PointerEvent<HTMLDivElement>) => {\n finishDrag(event, false);\n },\n role: \"separator\",\n tabIndex: 0,\n };\n },\n [\n columnGapPx,\n columns,\n currentColumnWidth,\n enabled,\n leadingTracks,\n applyTemplate,\n commitOverrides,\n overridesWithColumnWidth,\n resetColumnWidth,\n setColumnWidth,\n showIndicator,\n templateForOverrides,\n trailingTracks,\n ],\n );\n\n return {\n getResizeHandleProps,\n gridTemplateColumns,\n hasOverrides,\n resizeRootRef,\n resetColumns,\n resetColumnWidth,\n };\n}\n\nfunction maxColumnWidthForGrid({\n column,\n columnGapPx,\n columns,\n grid,\n leadingTracks,\n trailingTracks,\n}: {\n column: SizableColumn;\n columnGapPx: number;\n columns: SizableColumn[];\n grid: Element | null | undefined;\n leadingTracks: string[];\n trailingTracks: string[];\n}): number {\n const gridWidth = grid?.getBoundingClientRect().width ?? 0;\n\n if (gridWidth <= 0) {\n return maxColumnWidthPx;\n }\n\n const utilityWidth = [...leadingTracks, ...trailingTracks].reduce(\n (sum, track) => sum + fixedTrackWidthPx(track),\n 0,\n );\n const siblingMinWidth = columns.reduce(\n (sum, sibling) => (sibling.key === column.key ? sum : sum + minColumnWidthPx(sibling)),\n 0,\n );\n const trackCount = columns.length + leadingTracks.length + trailingTracks.length;\n const gapWidth = Math.max(0, trackCount - 1) * columnGapPx;\n const available = gridWidth - utilityWidth - siblingMinWidth - gapWidth;\n\n return Math.min(maxColumnWidthPx, Math.max(minColumnWidthPx(column), available));\n}\n\nfunction parseStoredOverrides(\n raw: string,\n columns: SizableColumn[],\n): Record<string, number | undefined> {\n const stored = JSON.parse(raw) as { overrides?: unknown };\n const overrides = stored?.overrides;\n\n if (typeof overrides !== \"object\" || overrides === null || Array.isArray(overrides)) {\n throw new Error(\"unexpected stored column widths shape\");\n }\n\n const sanitized = sanitizeOverrides(overrides as Record<string, unknown>, columns);\n\n if (Object.keys(sanitized).length === 0) {\n throw new Error(\"stored column widths hold no usable overrides\");\n }\n\n return sanitized;\n}\n\nfunction serializeOverrides(\n overrides: Record<string, number | undefined>,\n columnKeys: string[],\n): string | null {\n const stored: Record<string, number> = {};\n const knownKeys = new Set(columnKeys);\n\n for (const [key, value] of Object.entries(overrides)) {\n if (knownKeys.has(key) && typeof value === \"number\" && Number.isFinite(value)) {\n stored[key] = value;\n }\n }\n\n if (Object.keys(stored).length === 0) {\n return null;\n }\n\n return JSON.stringify({ overrides: stored });\n}\n\nfunction sanitizeOverrides(\n overrides: Record<string, unknown>,\n columns: SizableColumn[],\n): Record<string, number | undefined> {\n const next: Record<string, number | undefined> = {};\n\n for (const column of columns) {\n const value = overrides[column.key];\n\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n continue;\n }\n\n next[column.key] = Math.min(maxColumnWidthPx, Math.max(minColumnWidthPx(column), value));\n }\n\n return next;\n}\n\nfunction fixedTrackWidthPx(track: string): number {\n const value = track.trim();\n const px = value.match(/^([0-9.]+)px$/);\n\n if (px) {\n return Number.parseFloat(px[1]);\n }\n\n const rem = value.match(/^([0-9.]+)rem$/);\n\n if (rem) {\n return Number.parseFloat(rem[1]) * rootFontSizePx();\n }\n\n return 0;\n}\n\nfunction rootFontSizePx(): number {\n if (typeof window === \"undefined\") {\n return 16;\n }\n\n const parsed = Number.parseFloat(window.getComputedStyle(document.documentElement).fontSize);\n\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 16;\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- export { ApiError, apiFetch, apiJson, clearRemoteTokenCache, invalidateRemoteToken, remoteFetch, remoteJson, remoteToken, xsrfToken, } from './api.js';
1
+ export { ApiError, apiFetch, apiJson, invalidateRemoteToken, remoteFetch, remoteJson, remoteToken, xsrfToken, } from './api.js';
2
2
  export type { ApiInit, BrowserToken, RemoteAccess, RemoteInit } from './api.js';
3
3
  export { CollapsedContext, useCollapsed } from './collapsed-context.js';
4
4
  export { LATTICE_REF_HEADER, withRefHeader } from './component-ref.js';
@@ -1,6 +1,6 @@
1
1
  import { LATTICE_REF_HEADER, withRefHeader } from "./component-ref.js";
2
2
  import { withHeaders } from "./headers.js";
3
- import { ApiError, apiFetch, apiJson, clearRemoteTokenCache, invalidateRemoteToken, remoteFetch, remoteJson, remoteToken, xsrfToken } from "./api.js";
3
+ import { ApiError, apiFetch, apiJson, invalidateRemoteToken, remoteFetch, remoteJson, remoteToken, xsrfToken } from "./api.js";
4
4
  import { dataBindings, isRecord, materializeNode, materializeProps, materializeSchema, rowValue } from "./materialize.js";
5
5
  import { createPlugin, createRegistry, eagerComponent, extendRegistry, lazyComponent } from "./registry.js";
6
6
  import { RegistryContext, setDefaultRegistry, useColumnRegistry, useComponentRegistry, useEffectHandlerRegistry } from "./registry-context.js";
@@ -10,4 +10,4 @@ import { CollapsedContext, useCollapsed } from "./collapsed-context.js";
10
10
  import { RenderNode, Renderer } from "./renderer.js";
11
11
  import { nodeIdentity } from "./test-id.js";
12
12
  import { usePersistentState } from "../lib/use-persistent-state.js";
13
- export { ApiError, CollapsedContext, LATTICE_REF_HEADER, RegistryContext, RenderNode, Renderer, apiFetch, apiJson, clearRemoteTokenCache, cn, createPlugin, createRegistry, dataBindings, eagerComponent, extendRegistry, invalidateRemoteToken, isRecord, lazyComponent, materializeNode, materializeProps, materializeSchema, nodeIdentity, remoteFetch, remoteJson, remoteToken, rowValue, setDefaultRegistry, toNodes, useCollapsed, useColumnRegistry, useComponentRegistry, useEffectHandlerRegistry, usePersistentState, withHeaders, withRefHeader, xsrfToken };
13
+ export { ApiError, CollapsedContext, LATTICE_REF_HEADER, RegistryContext, RenderNode, Renderer, apiFetch, apiJson, cn, createPlugin, createRegistry, dataBindings, eagerComponent, extendRegistry, invalidateRemoteToken, isRecord, lazyComponent, materializeNode, materializeProps, materializeSchema, nodeIdentity, remoteFetch, remoteJson, remoteToken, rowValue, setDefaultRegistry, toNodes, useCollapsed, useColumnRegistry, useComponentRegistry, useEffectHandlerRegistry, usePersistentState, withHeaders, withRefHeader, xsrfToken };
@@ -9,7 +9,6 @@ export type EagerComponentRegistration = {
9
9
  };
10
10
  export type LazyComponentRegistration = {
11
11
  component: LazyExoticComponent<RendererComponent>;
12
- fallback?: RendererComponent;
13
12
  load: () => Promise<RendererComponentModule>;
14
13
  mode: "lazy";
15
14
  };
@@ -32,11 +31,8 @@ export type Registry = {
32
31
  columns: ColumnRegistry;
33
32
  effects: EffectHandlerRegistry;
34
33
  };
35
- export type LazyComponentOptions<TType extends string> = {
36
- fallback?: RendererComponent<TType>;
37
- };
38
34
  export declare function eagerComponent<TType extends string>(component: RendererComponent<TType>): EagerComponentRegistration;
39
- export declare function lazyComponent<TType extends string>(load: () => Promise<RendererComponentModule<TType>>, options?: LazyComponentOptions<TType>): LazyComponentRegistration;
35
+ export declare function lazyComponent<TType extends string>(load: () => Promise<RendererComponentModule<TType>>): LazyComponentRegistration;
40
36
  export declare function createPlugin(plugin: Plugin): Plugin;
41
37
  export declare function createRegistry(...plugins: Plugin[]): Registry;
42
38
  export declare function extendRegistry(registry: Registry, ...plugins: Plugin[]): Registry;
@@ -6,11 +6,10 @@ function eagerComponent(component) {
6
6
  mode: "eager"
7
7
  };
8
8
  }
9
- function lazyComponent(load, options = {}) {
9
+ function lazyComponent(load) {
10
10
  const erasedLoader = load;
11
11
  return {
12
12
  component: lazy(erasedLoader),
13
- fallback: options.fallback,
14
13
  load: erasedLoader,
15
14
  mode: "lazy"
16
15
  };
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","names":[],"sources":["../../resources/js/core/registry.ts"],"sourcesContent":["import { lazy } from \"react\";\nimport type { LazyExoticComponent } from \"react\";\nimport type { RendererComponent, RendererComponentModule } from \"./types\";\nimport type { EffectHandlerRegistry } from \"@lattice-php/lattice/effects/registry\";\nimport type { ColumnRegistry } from \"@lattice-php/lattice/table/registry\";\nimport type { ComponentPropsMap } from \"@lattice-php/lattice/types/generated\";\n\nexport type EagerComponentRegistration = {\n component: RendererComponent;\n mode: \"eager\";\n};\n\nexport type LazyComponentRegistration = {\n component: LazyExoticComponent<RendererComponent>;\n fallback?: RendererComponent;\n load: () => Promise<RendererComponentModule>;\n mode: \"lazy\";\n};\n\nexport type ComponentRegistration = EagerComponentRegistration | LazyComponentRegistration;\n\nexport type ComponentRegistry = Record<string, ComponentRegistration>;\n\nexport type ComponentRegistryFor<TTypes extends keyof ComponentPropsMap & string> = Record<\n TTypes,\n ComponentRegistration\n>;\n\nexport type PluginI18n = {\n /** i18next namespace the plugin's components translate under. */\n namespace: string;\n};\n\nexport type Plugin = {\n name: string;\n components?: ComponentRegistry;\n columns?: ColumnRegistry;\n effects?: EffectHandlerRegistry;\n i18n?: PluginI18n;\n};\n\nexport type Registry = {\n components: ComponentRegistry;\n columns: ColumnRegistry;\n effects: EffectHandlerRegistry;\n};\n\nexport type LazyComponentOptions<TType extends string> = {\n fallback?: RendererComponent<TType>;\n};\n\nexport function eagerComponent<TType extends string>(\n component: RendererComponent<TType>,\n): EagerComponentRegistration {\n return {\n component: component as unknown as RendererComponent,\n mode: \"eager\",\n };\n}\n\nexport function lazyComponent<TType extends string>(\n load: () => Promise<RendererComponentModule<TType>>,\n options: LazyComponentOptions<TType> = {},\n): LazyComponentRegistration {\n const erasedLoader = load as unknown as () => Promise<RendererComponentModule>;\n\n return {\n component: lazy(erasedLoader),\n fallback: options.fallback as RendererComponent | undefined,\n load: erasedLoader,\n mode: \"lazy\",\n };\n}\n\nexport function createPlugin(plugin: Plugin): Plugin {\n return plugin;\n}\n\nexport function createRegistry(...plugins: Plugin[]): Registry {\n return plugins.reduce<Registry>(\n (registry, plugin) => ({\n components: { ...registry.components, ...plugin.components },\n columns: { ...registry.columns, ...plugin.columns },\n effects: { ...registry.effects, ...plugin.effects },\n }),\n { components: {}, columns: {}, effects: {} },\n );\n}\n\nexport function extendRegistry(registry: Registry, ...plugins: Plugin[]): Registry {\n const merged = createRegistry(...plugins);\n\n return {\n components: { ...registry.components, ...merged.components },\n columns: { ...registry.columns, ...merged.columns },\n effects: { ...registry.effects, ...merged.effects },\n };\n}\n"],"mappings":";;AAmDA,SAAgB,eACd,WAC4B;CAC5B,OAAO;EACM;EACX,MAAM;CACR;AACF;AAEA,SAAgB,cACd,MACA,UAAuC,CAAC,GACb;CAC3B,MAAM,eAAe;CAErB,OAAO;EACL,WAAW,KAAK,YAAY;EAC5B,UAAU,QAAQ;EAClB,MAAM;EACN,MAAM;CACR;AACF;AAEA,SAAgB,aAAa,QAAwB;CACnD,OAAO;AACT;AAEA,SAAgB,eAAe,GAAG,SAA6B;CAC7D,OAAO,QAAQ,QACZ,UAAU,YAAY;EACrB,YAAY;GAAE,GAAG,SAAS;GAAY,GAAG,OAAO;EAAW;EAC3D,SAAS;GAAE,GAAG,SAAS;GAAS,GAAG,OAAO;EAAQ;EAClD,SAAS;GAAE,GAAG,SAAS;GAAS,GAAG,OAAO;EAAQ;CACpD,IACA;EAAE,YAAY,CAAC;EAAG,SAAS,CAAC;EAAG,SAAS,CAAC;CAAE,CAC7C;AACF;AAEA,SAAgB,eAAe,UAAoB,GAAG,SAA6B;CACjF,MAAM,SAAS,eAAe,GAAG,OAAO;CAExC,OAAO;EACL,YAAY;GAAE,GAAG,SAAS;GAAY,GAAG,OAAO;EAAW;EAC3D,SAAS;GAAE,GAAG,SAAS;GAAS,GAAG,OAAO;EAAQ;EAClD,SAAS;GAAE,GAAG,SAAS;GAAS,GAAG,OAAO;EAAQ;CACpD;AACF"}
1
+ {"version":3,"file":"registry.js","names":[],"sources":["../../resources/js/core/registry.ts"],"sourcesContent":["import { lazy } from \"react\";\nimport type { LazyExoticComponent } from \"react\";\nimport type { RendererComponent, RendererComponentModule } from \"./types\";\nimport type { EffectHandlerRegistry } from \"@lattice-php/lattice/effects/registry\";\nimport type { ColumnRegistry } from \"@lattice-php/lattice/table/registry\";\nimport type { ComponentPropsMap } from \"@lattice-php/lattice/types/generated\";\n\nexport type EagerComponentRegistration = {\n component: RendererComponent;\n mode: \"eager\";\n};\n\nexport type LazyComponentRegistration = {\n component: LazyExoticComponent<RendererComponent>;\n load: () => Promise<RendererComponentModule>;\n mode: \"lazy\";\n};\n\nexport type ComponentRegistration = EagerComponentRegistration | LazyComponentRegistration;\n\nexport type ComponentRegistry = Record<string, ComponentRegistration>;\n\nexport type ComponentRegistryFor<TTypes extends keyof ComponentPropsMap & string> = Record<\n TTypes,\n ComponentRegistration\n>;\n\nexport type PluginI18n = {\n /** i18next namespace the plugin's components translate under. */\n namespace: string;\n};\n\nexport type Plugin = {\n name: string;\n components?: ComponentRegistry;\n columns?: ColumnRegistry;\n effects?: EffectHandlerRegistry;\n i18n?: PluginI18n;\n};\n\nexport type Registry = {\n components: ComponentRegistry;\n columns: ColumnRegistry;\n effects: EffectHandlerRegistry;\n};\n\nexport function eagerComponent<TType extends string>(\n component: RendererComponent<TType>,\n): EagerComponentRegistration {\n return {\n component: component as unknown as RendererComponent,\n mode: \"eager\",\n };\n}\n\nexport function lazyComponent<TType extends string>(\n load: () => Promise<RendererComponentModule<TType>>,\n): LazyComponentRegistration {\n const erasedLoader = load as unknown as () => Promise<RendererComponentModule>;\n\n return {\n component: lazy(erasedLoader),\n load: erasedLoader,\n mode: \"lazy\",\n };\n}\n\nexport function createPlugin(plugin: Plugin): Plugin {\n return plugin;\n}\n\nexport function createRegistry(...plugins: Plugin[]): Registry {\n return plugins.reduce<Registry>(\n (registry, plugin) => ({\n components: { ...registry.components, ...plugin.components },\n columns: { ...registry.columns, ...plugin.columns },\n effects: { ...registry.effects, ...plugin.effects },\n }),\n { components: {}, columns: {}, effects: {} },\n );\n}\n\nexport function extendRegistry(registry: Registry, ...plugins: Plugin[]): Registry {\n const merged = createRegistry(...plugins);\n\n return {\n components: { ...registry.components, ...merged.components },\n columns: { ...registry.columns, ...merged.columns },\n effects: { ...registry.effects, ...merged.effects },\n };\n}\n"],"mappings":";;AA8CA,SAAgB,eACd,WAC4B;CAC5B,OAAO;EACM;EACX,MAAM;CACR;AACF;AAEA,SAAgB,cACd,MAC2B;CAC3B,MAAM,eAAe;CAErB,OAAO;EACL,WAAW,KAAK,YAAY;EAC5B,MAAM;EACN,MAAM;CACR;AACF;AAEA,SAAgB,aAAa,QAAwB;CACnD,OAAO;AACT;AAEA,SAAgB,eAAe,GAAG,SAA6B;CAC7D,OAAO,QAAQ,QACZ,UAAU,YAAY;EACrB,YAAY;GAAE,GAAG,SAAS;GAAY,GAAG,OAAO;EAAW;EAC3D,SAAS;GAAE,GAAG,SAAS;GAAS,GAAG,OAAO;EAAQ;EAClD,SAAS;GAAE,GAAG,SAAS;GAAS,GAAG,OAAO;EAAQ;CACpD,IACA;EAAE,YAAY,CAAC;EAAG,SAAS,CAAC;EAAG,SAAS,CAAC;CAAE,CAC7C;AACF;AAEA,SAAgB,eAAe,UAAoB,GAAG,SAA6B;CACjF,MAAM,SAAS,eAAe,GAAG,OAAO;CAExC,OAAO;EACL,YAAY;GAAE,GAAG,SAAS;GAAY,GAAG,OAAO;EAAW;EAC3D,SAAS;GAAE,GAAG,SAAS;GAAS,GAAG,OAAO;EAAQ;EAClD,SAAS;GAAE,GAAG,SAAS;GAAS,GAAG,OAAO;EAAQ;CACpD;AACF"}