@lattice-php/lattice 0.8.0 → 0.10.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 (70) hide show
  1. package/dist/action/components/action.js +16 -85
  2. package/dist/action/components/action.js.map +1 -1
  3. package/dist/action/use-action.d.ts +18 -0
  4. package/dist/action/use-action.js +90 -0
  5. package/dist/action/use-action.js.map +1 -0
  6. package/dist/core/components/control.d.ts +0 -1
  7. package/dist/core/components/control.js +0 -1
  8. package/dist/core/components/control.js.map +1 -1
  9. package/dist/core/components/fragment.js +4 -1
  10. package/dist/core/components/fragment.js.map +1 -1
  11. package/dist/core/components/icon.js +4 -1
  12. package/dist/core/components/icon.js.map +1 -1
  13. package/dist/core/components/link.js +24 -3
  14. package/dist/core/components/link.js.map +1 -1
  15. package/dist/core/components/text.js +4 -1
  16. package/dist/core/components/text.js.map +1 -1
  17. package/dist/core/renderer.d.ts +0 -2
  18. package/dist/core/renderer.js +0 -2
  19. package/dist/core/renderer.js.map +1 -1
  20. package/dist/effects/dispatch.d.ts +0 -1
  21. package/dist/effects/dispatch.js.map +1 -1
  22. package/dist/effects/registry.js +0 -1
  23. package/dist/effects/registry.js.map +1 -1
  24. package/dist/form/components/base/input-otp.d.ts +0 -1
  25. package/dist/form/components/base/input-otp.js +0 -1
  26. package/dist/form/components/base/input-otp.js.map +1 -1
  27. package/dist/form/components/field-props.d.ts +0 -1
  28. package/dist/form/components/field-props.js +0 -1
  29. package/dist/form/components/field-props.js.map +1 -1
  30. package/dist/form/components/fields/repeater-rows.d.ts +0 -2
  31. package/dist/form/components/fields/repeater-rows.js +0 -2
  32. package/dist/form/components/fields/repeater-rows.js.map +1 -1
  33. package/dist/form/components/fields/simple-field.d.ts +0 -1
  34. package/dist/form/components/fields/simple-field.js +0 -1
  35. package/dist/form/components/fields/simple-field.js.map +1 -1
  36. package/dist/form/components/fields/toggle.d.ts +2 -0
  37. package/dist/form/components/fields/toggle.js +65 -0
  38. package/dist/form/components/fields/toggle.js.map +1 -0
  39. package/dist/form/components/index.d.ts +1 -0
  40. package/dist/form/components/index.js +2 -1
  41. package/dist/form/components/use-controlled-field.d.ts +0 -1
  42. package/dist/form/components/use-controlled-field.js +0 -1
  43. package/dist/form/components/use-controlled-field.js.map +1 -1
  44. package/dist/form/index.js +2 -1
  45. package/dist/form/index.js.map +1 -1
  46. package/dist/index.js +3 -3
  47. package/dist/lattice.css +6 -0
  48. package/dist/layout/menu-item.js +21 -0
  49. package/dist/layout/menu-item.js.map +1 -1
  50. package/dist/lib/utils.js +11 -1
  51. package/dist/lib/utils.js.map +1 -1
  52. package/dist/table/components/cells/boolean-cell.d.ts +0 -1
  53. package/dist/table/components/cells/boolean-cell.js +0 -1
  54. package/dist/table/components/cells/boolean-cell.js.map +1 -1
  55. package/dist/table/components/cells/numeric.d.ts +0 -1
  56. package/dist/table/components/cells/numeric.js +0 -1
  57. package/dist/table/components/cells/numeric.js.map +1 -1
  58. package/dist/table/components/cells/stack-cell.d.ts +0 -1
  59. package/dist/table/components/cells/stack-cell.js +0 -1
  60. package/dist/table/components/cells/stack-cell.js.map +1 -1
  61. package/dist/table/registry.d.ts +0 -2
  62. package/dist/table/registry.js.map +1 -1
  63. package/dist/toast/callout.d.ts +0 -2
  64. package/dist/toast/callout.js +0 -2
  65. package/dist/toast/callout.js.map +1 -1
  66. package/dist/toast/toast.d.ts +0 -3
  67. package/dist/toast/toast.js +0 -3
  68. package/dist/toast/toast.js.map +1 -1
  69. package/dist/types/generated.d.ts +33 -2
  70. package/package.json +5 -2
@@ -1,10 +1,8 @@
1
1
  export type RepeaterRow = Record<string, unknown>;
2
2
  export declare const ROW_ID_KEY = "__rowId";
3
- /** Stamp a stable client-only id on a row (no-op if it already has one). */
4
3
  export declare function withRowId(row: RepeaterRow): RepeaterRow;
5
4
  /** Ensure every row has a stable id; returns the SAME array reference if none were missing. */
6
5
  export declare function ensureRowIds(rows: RepeaterRow[]): RepeaterRow[];
7
- /** Seed the row list from a stored value, falling back to `defaultItems` blank rows. */
8
6
  export declare function seedRows(value: unknown, defaultItems: number): RepeaterRow[];
9
7
  export declare function addRow(rows: RepeaterRow[]): RepeaterRow[];
10
8
  export declare function removeRow(rows: RepeaterRow[], index: number): RepeaterRow[];
@@ -1,7 +1,6 @@
1
1
  //#region resources/js/form/components/fields/repeater-rows.ts
2
2
  var ROW_ID_KEY = "__rowId";
3
3
  var rowIdCounter = 0;
4
- /** Stamp a stable client-only id on a row (no-op if it already has one). */
5
4
  function withRowId(row) {
6
5
  return row["__rowId"] ? row : {
7
6
  ...row,
@@ -13,7 +12,6 @@ function ensureRowIds(rows) {
13
12
  if (rows.every((row) => Boolean(row["__rowId"]))) return rows;
14
13
  return rows.map(withRowId);
15
14
  }
16
- /** Seed the row list from a stored value, falling back to `defaultItems` blank rows. */
17
15
  function seedRows(value, defaultItems) {
18
16
  if (Array.isArray(value) && value.length > 0) return value.map((row) => row && typeof row === "object" ? { ...row } : {});
19
17
  return Array.from({ length: Math.max(0, defaultItems) }, () => ({}));
@@ -1 +1 @@
1
- {"version":3,"file":"repeater-rows.js","names":[],"sources":["../../../../resources/js/form/components/fields/repeater-rows.ts"],"sourcesContent":["export type RepeaterRow = Record<string, unknown>;\n\nexport const ROW_ID_KEY = \"__rowId\";\n\nlet rowIdCounter = 0;\n\n/** Stamp a stable client-only id on a row (no-op if it already has one). */\nexport function withRowId(row: RepeaterRow): RepeaterRow {\n return row[ROW_ID_KEY] ? row : { ...row, [ROW_ID_KEY]: `r${rowIdCounter++}` };\n}\n\n/** Ensure every row has a stable id; returns the SAME array reference if none were missing. */\nexport function ensureRowIds(rows: RepeaterRow[]): RepeaterRow[] {\n if (rows.every((row) => Boolean(row[ROW_ID_KEY]))) {\n return rows;\n }\n return rows.map(withRowId);\n}\n\n/** Seed the row list from a stored value, falling back to `defaultItems` blank rows. */\nexport function seedRows(value: unknown, defaultItems: number): RepeaterRow[] {\n if (Array.isArray(value) && value.length > 0) {\n return value.map((row) => (row && typeof row === \"object\" ? { ...(row as RepeaterRow) } : {}));\n }\n\n return Array.from({ length: Math.max(0, defaultItems) }, () => ({}));\n}\n\nexport function addRow(rows: RepeaterRow[]): RepeaterRow[] {\n return [...rows, {}];\n}\n\nexport function removeRow(rows: RepeaterRow[], index: number): RepeaterRow[] {\n return rows.filter((_, i) => i !== index);\n}\n\nexport function duplicateRow(rows: RepeaterRow[], index: number): RepeaterRow[] {\n const source = rows[index];\n if (!source) {\n return rows;\n }\n\n const { [ROW_ID_KEY]: _id, ...copy } = source;\n return [...rows.slice(0, index + 1), withRowId(copy), ...rows.slice(index + 1)];\n}\n\nexport function moveRow(rows: RepeaterRow[], from: number, to: number): RepeaterRow[] {\n const next = [...rows];\n const [moved] = next.splice(from, 1);\n next.splice(to, 0, moved);\n return next;\n}\n"],"mappings":";AAEA,IAAa,aAAa;AAE1B,IAAI,eAAe;;AAGnB,SAAgB,UAAU,KAA+B;CACvD,OAAO,IAAA,aAAkB,MAAM;EAAE,GAAG;GAAM,aAAa,IAAI;CAAiB;AAC9E;;AAGA,SAAgB,aAAa,MAAoC;CAC/D,IAAI,KAAK,OAAO,QAAQ,QAAQ,IAAA,UAAe,CAAC,GAC9C,OAAO;CAET,OAAO,KAAK,IAAI,SAAS;AAC3B;;AAGA,SAAgB,SAAS,OAAgB,cAAqC;CAC5E,IAAI,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,GACzC,OAAO,MAAM,KAAK,QAAS,OAAO,OAAO,QAAQ,WAAW,EAAE,GAAI,IAAoB,IAAI,CAAC,CAAE;CAG/F,OAAO,MAAM,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,YAAY,EAAE,UAAU,CAAC,EAAE;AACrE;AAEA,SAAgB,OAAO,MAAoC;CACzD,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC;AACrB;AAEA,SAAgB,UAAU,MAAqB,OAA8B;CAC3E,OAAO,KAAK,QAAQ,GAAG,MAAM,MAAM,KAAK;AAC1C;AAEA,SAAgB,aAAa,MAAqB,OAA8B;CAC9E,MAAM,SAAS,KAAK;CACpB,IAAI,CAAC,QACH,OAAO;CAGT,MAAM,GAAG,aAAa,KAAK,GAAG,SAAS;CACvC,OAAO;EAAC,GAAG,KAAK,MAAM,GAAG,QAAQ,CAAC;EAAG,UAAU,IAAI;EAAG,GAAG,KAAK,MAAM,QAAQ,CAAC;CAAC;AAChF;AAEA,SAAgB,QAAQ,MAAqB,MAAc,IAA2B;CACpF,MAAM,OAAO,CAAC,GAAG,IAAI;CACrB,MAAM,CAAC,SAAS,KAAK,OAAO,MAAM,CAAC;CACnC,KAAK,OAAO,IAAI,GAAG,KAAK;CACxB,OAAO;AACT"}
1
+ {"version":3,"file":"repeater-rows.js","names":[],"sources":["../../../../resources/js/form/components/fields/repeater-rows.ts"],"sourcesContent":["export type RepeaterRow = Record<string, unknown>;\n\nexport const ROW_ID_KEY = \"__rowId\";\n\nlet rowIdCounter = 0;\n\nexport function withRowId(row: RepeaterRow): RepeaterRow {\n return row[ROW_ID_KEY] ? row : { ...row, [ROW_ID_KEY]: `r${rowIdCounter++}` };\n}\n\n/** Ensure every row has a stable id; returns the SAME array reference if none were missing. */\nexport function ensureRowIds(rows: RepeaterRow[]): RepeaterRow[] {\n if (rows.every((row) => Boolean(row[ROW_ID_KEY]))) {\n return rows;\n }\n return rows.map(withRowId);\n}\n\nexport function seedRows(value: unknown, defaultItems: number): RepeaterRow[] {\n if (Array.isArray(value) && value.length > 0) {\n return value.map((row) => (row && typeof row === \"object\" ? { ...(row as RepeaterRow) } : {}));\n }\n\n return Array.from({ length: Math.max(0, defaultItems) }, () => ({}));\n}\n\nexport function addRow(rows: RepeaterRow[]): RepeaterRow[] {\n return [...rows, {}];\n}\n\nexport function removeRow(rows: RepeaterRow[], index: number): RepeaterRow[] {\n return rows.filter((_, i) => i !== index);\n}\n\nexport function duplicateRow(rows: RepeaterRow[], index: number): RepeaterRow[] {\n const source = rows[index];\n if (!source) {\n return rows;\n }\n\n const { [ROW_ID_KEY]: _id, ...copy } = source;\n return [...rows.slice(0, index + 1), withRowId(copy), ...rows.slice(index + 1)];\n}\n\nexport function moveRow(rows: RepeaterRow[], from: number, to: number): RepeaterRow[] {\n const next = [...rows];\n const [moved] = next.splice(from, 1);\n next.splice(to, 0, moved);\n return next;\n}\n"],"mappings":";AAEA,IAAa,aAAa;AAE1B,IAAI,eAAe;AAEnB,SAAgB,UAAU,KAA+B;CACvD,OAAO,IAAA,aAAkB,MAAM;EAAE,GAAG;GAAM,aAAa,IAAI;CAAiB;AAC9E;;AAGA,SAAgB,aAAa,MAAoC;CAC/D,IAAI,KAAK,OAAO,QAAQ,QAAQ,IAAA,UAAe,CAAC,GAC9C,OAAO;CAET,OAAO,KAAK,IAAI,SAAS;AAC3B;AAEA,SAAgB,SAAS,OAAgB,cAAqC;CAC5E,IAAI,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,GACzC,OAAO,MAAM,KAAK,QAAS,OAAO,OAAO,QAAQ,WAAW,EAAE,GAAI,IAAoB,IAAI,CAAC,CAAE;CAG/F,OAAO,MAAM,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,YAAY,EAAE,UAAU,CAAC,EAAE;AACrE;AAEA,SAAgB,OAAO,MAAoC;CACzD,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC;AACrB;AAEA,SAAgB,UAAU,MAAqB,OAA8B;CAC3E,OAAO,KAAK,QAAQ,GAAG,MAAM,MAAM,KAAK;AAC1C;AAEA,SAAgB,aAAa,MAAqB,OAA8B;CAC9E,MAAM,SAAS,KAAK;CACpB,IAAI,CAAC,QACH,OAAO;CAGT,MAAM,GAAG,aAAa,KAAK,GAAG,SAAS;CACvC,OAAO;EAAC,GAAG,KAAK,MAAM,GAAG,QAAQ,CAAC;EAAG,UAAU,IAAI;EAAG,GAAG,KAAK,MAAM,QAAQ,CAAC;CAAC;AAChF;AAEA,SAAgB,QAAQ,MAAqB,MAAc,IAA2B;CACpF,MAAM,OAAO,CAAC,GAAG,IAAI;CACrB,MAAM,CAAC,SAAS,KAAK,OAAO,MAAM,CAAC;CACnC,KAAK,OAAO,IAAI,GAAG,KAAK;CACxB,OAAO;AACT"}
@@ -1,7 +1,6 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { Node } from '../../../core/types';
3
3
  import { ControlledField } from '../use-controlled-field';
4
- /** The shared shell for single-input fields; the control is a render prop receiving the resolved field state. */
5
4
  export declare function SimpleField({ node, label, children, }: {
6
5
  node: Node;
7
6
  label: string;
@@ -3,7 +3,6 @@ import { FormFieldFrame } from "../base/field.js";
3
3
  import { useControlledField } from "../use-controlled-field.js";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  //#region resources/js/form/components/fields/simple-field.tsx
6
- /** The shared shell for single-input fields; the control is a render prop receiving the resolved field state. */
7
6
  function SimpleField({ node, label, children }) {
8
7
  const field = useControlledField(node);
9
8
  if (field.hidden) return null;
@@ -1 +1 @@
1
- {"version":3,"file":"simple-field.js","names":[],"sources":["../../../../resources/js/form/components/fields/simple-field.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport type { Node } from \"@lattice-php/lattice/core/types\";\nimport { FormFieldFrame } from \"../base/field\";\nimport { fieldProps } from \"../field-props\";\nimport { type ControlledField, useControlledField } from \"../use-controlled-field\";\n\n/** The shared shell for single-input fields; the control is a render prop receiving the resolved field state. */\nexport function SimpleField({\n node,\n label,\n children,\n}: {\n node: Node;\n label: string;\n children: (field: ControlledField) => ReactNode;\n}) {\n const field = useControlledField(node);\n\n if (field.hidden) {\n return null;\n }\n\n return (\n <FormFieldFrame\n error={field.error}\n helperText={fieldProps(node).helperText ?? undefined}\n tooltip={fieldProps(node).tooltip ?? undefined}\n label={label}\n name={field.name}\n required={field.required}\n >\n {children(field)}\n </FormFieldFrame>\n );\n}\n"],"mappings":";;;;;;AAOA,SAAgB,YAAY,EAC1B,MACA,OACA,YAKC;CACD,MAAM,QAAQ,mBAAmB,IAAI;CAErC,IAAI,MAAM,QACR,OAAO;CAGT,OACE,oBAAC,gBAAD;EACE,OAAO,MAAM;EACb,YAAY,WAAW,IAAI,EAAE,cAAc,KAAA;EAC3C,SAAS,WAAW,IAAI,EAAE,WAAW,KAAA;EAC9B;EACP,MAAM,MAAM;EACZ,UAAU,MAAM;YAEf,SAAS,KAAK;CACD,CAAA;AAEpB"}
1
+ {"version":3,"file":"simple-field.js","names":[],"sources":["../../../../resources/js/form/components/fields/simple-field.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport type { Node } from \"@lattice-php/lattice/core/types\";\nimport { FormFieldFrame } from \"../base/field\";\nimport { fieldProps } from \"../field-props\";\nimport { type ControlledField, useControlledField } from \"../use-controlled-field\";\n\nexport function SimpleField({\n node,\n label,\n children,\n}: {\n node: Node;\n label: string;\n children: (field: ControlledField) => ReactNode;\n}) {\n const field = useControlledField(node);\n\n if (field.hidden) {\n return null;\n }\n\n return (\n <FormFieldFrame\n error={field.error}\n helperText={fieldProps(node).helperText ?? undefined}\n tooltip={fieldProps(node).tooltip ?? undefined}\n label={label}\n name={field.name}\n required={field.required}\n >\n {children(field)}\n </FormFieldFrame>\n );\n}\n"],"mappings":";;;;;AAMA,SAAgB,YAAY,EAC1B,MACA,OACA,YAKC;CACD,MAAM,QAAQ,mBAAmB,IAAI;CAErC,IAAI,MAAM,QACR,OAAO;CAGT,OACE,oBAAC,gBAAD;EACE,OAAO,MAAM;EACb,YAAY,WAAW,IAAI,EAAE,cAAc,KAAA;EAC3C,SAAS,WAAW,IAAI,EAAE,WAAW,KAAA;EAC9B;EACP,MAAM,MAAM;EACZ,UAAU,MAAM;YAEf,SAAS,KAAK;CACD,CAAA;AAEpB"}
@@ -0,0 +1,2 @@
1
+ import { RendererComponent } from '../../../core/types';
2
+ export declare const ToggleComponent: RendererComponent<"field.toggle">;
@@ -0,0 +1,65 @@
1
+ import { cn } from "../../../lib/utils.js";
2
+ import { testIdentity } from "../../../core/test-id.js";
3
+ import { useFormContext } from "../context.js";
4
+ import { useFieldScope } from "../field-scope.js";
5
+ import { useFormValue } from "../values.js";
6
+ import { toBoolean } from "../conditions.js";
7
+ import { FormFieldFrame } from "../base/field.js";
8
+ import { useDependentField } from "../use-dependent-field.js";
9
+ import { useFieldCommit } from "../use-field-commit.js";
10
+ import { useSeedDefault } from "../use-seed-default.js";
11
+ import { jsx, jsxs } from "react/jsx-runtime";
12
+ //#region resources/js/form/components/fields/toggle.tsx
13
+ var ToggleComponent = ({ node }) => {
14
+ const { hidden, required, readOnly, disabled } = useDependentField(node);
15
+ const props = node.props;
16
+ const localName = props.name;
17
+ const scope = useFieldScope();
18
+ const name = scope ? scope.scopedName(localName) : localName;
19
+ const errorKey = scope ? scope.errorKey(localName) : localName;
20
+ const globalValue = useFormValue(localName);
21
+ const storedValue = scope ? scope.getValue(localName) : globalValue;
22
+ const defaultChecked = toBoolean(props.value);
23
+ const checked = storedValue !== void 0 ? toBoolean(storedValue) : defaultChecked;
24
+ const locked = readOnly || disabled;
25
+ const { errors } = useFormContext();
26
+ const { commit } = useFieldCommit();
27
+ useSeedDefault(localName, defaultChecked);
28
+ if (hidden) return null;
29
+ return /* @__PURE__ */ jsxs(FormFieldFrame, {
30
+ error: errors[errorKey],
31
+ helperText: props.helperText ?? void 0,
32
+ tooltip: props.tooltip ?? void 0,
33
+ label: props.label ?? "",
34
+ name,
35
+ required,
36
+ children: [/* @__PURE__ */ jsx("input", {
37
+ disabled: locked,
38
+ name,
39
+ type: "hidden",
40
+ value: checked ? "1" : "0"
41
+ }), /* @__PURE__ */ jsx("button", {
42
+ "aria-checked": checked,
43
+ "aria-label": props.label ?? localName,
44
+ autoFocus: props.autoFocus ?? false,
45
+ className: cn("inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border border-transparent bg-lt-muted p-0.5 shadow-lt-xs transition-colors outline-none focus-visible:border-lt-ring focus-visible:ring-[3px] focus-visible:ring-lt-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-lt-primary"),
46
+ "data-state": checked ? "checked" : "unchecked",
47
+ "data-test": testIdentity(localName),
48
+ disabled: locked,
49
+ id: name,
50
+ name,
51
+ onClick: () => commit(localName, !checked),
52
+ role: "switch",
53
+ tabIndex: props.tabIndex ?? void 0,
54
+ type: "button",
55
+ children: /* @__PURE__ */ jsx("span", {
56
+ className: "size-5 rounded-full bg-lt-bg shadow-lt-sm transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0",
57
+ "data-state": checked ? "checked" : "unchecked"
58
+ })
59
+ })]
60
+ });
61
+ };
62
+ //#endregion
63
+ export { ToggleComponent };
64
+
65
+ //# sourceMappingURL=toggle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toggle.js","names":[],"sources":["../../../../resources/js/form/components/fields/toggle.tsx"],"sourcesContent":["import type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { testIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { FormFieldFrame } from \"../base/field\";\nimport { toBoolean } from \"../conditions\";\nimport { useFormContext } from \"../context\";\nimport { useFieldScope } from \"../field-scope\";\nimport { useDependentField } from \"../use-dependent-field\";\nimport { useFieldCommit } from \"../use-field-commit\";\nimport { useSeedDefault } from \"../use-seed-default\";\nimport { useFormValue } from \"../values\";\n\nexport const ToggleComponent: RendererComponent<\"field.toggle\"> = ({ node }) => {\n const { hidden, required, readOnly, disabled } = useDependentField(node);\n const props = node.props;\n const localName = props.name;\n const scope = useFieldScope();\n const name = scope ? scope.scopedName(localName) : localName;\n const errorKey = scope ? scope.errorKey(localName) : localName;\n const globalValue = useFormValue(localName);\n const storedValue = scope ? scope.getValue(localName) : globalValue;\n const defaultChecked = toBoolean(props.value);\n const checked = storedValue !== undefined ? toBoolean(storedValue) : defaultChecked;\n const locked = readOnly || disabled;\n const { errors } = useFormContext();\n const { commit } = useFieldCommit();\n\n useSeedDefault(localName, defaultChecked);\n\n if (hidden) {\n return null;\n }\n\n return (\n <FormFieldFrame\n error={errors[errorKey]}\n helperText={props.helperText ?? undefined}\n tooltip={props.tooltip ?? undefined}\n label={props.label ?? \"\"}\n name={name}\n required={required}\n >\n <input disabled={locked} name={name} type=\"hidden\" value={checked ? \"1\" : \"0\"} />\n <button\n aria-checked={checked}\n aria-label={props.label ?? localName}\n autoFocus={props.autoFocus ?? false}\n className={cn(\n \"inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border border-transparent bg-lt-muted p-0.5 shadow-lt-xs transition-colors outline-none focus-visible:border-lt-ring focus-visible:ring-[3px] focus-visible:ring-lt-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-lt-primary\",\n )}\n data-state={checked ? \"checked\" : \"unchecked\"}\n data-test={testIdentity(localName)}\n disabled={locked}\n id={name}\n name={name}\n onClick={() => commit(localName, !checked)}\n role=\"switch\"\n tabIndex={props.tabIndex ?? undefined}\n type=\"button\"\n >\n <span\n className=\"size-5 rounded-full bg-lt-bg shadow-lt-sm transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0\"\n data-state={checked ? \"checked\" : \"unchecked\"}\n />\n </button>\n </FormFieldFrame>\n );\n};\n"],"mappings":";;;;;;;;;;;;AAYA,IAAa,mBAAsD,EAAE,WAAW;CAC9E,MAAM,EAAE,QAAQ,UAAU,UAAU,aAAa,kBAAkB,IAAI;CACvE,MAAM,QAAQ,KAAK;CACnB,MAAM,YAAY,MAAM;CACxB,MAAM,QAAQ,cAAc;CAC5B,MAAM,OAAO,QAAQ,MAAM,WAAW,SAAS,IAAI;CACnD,MAAM,WAAW,QAAQ,MAAM,SAAS,SAAS,IAAI;CACrD,MAAM,cAAc,aAAa,SAAS;CAC1C,MAAM,cAAc,QAAQ,MAAM,SAAS,SAAS,IAAI;CACxD,MAAM,iBAAiB,UAAU,MAAM,KAAK;CAC5C,MAAM,UAAU,gBAAgB,KAAA,IAAY,UAAU,WAAW,IAAI;CACrE,MAAM,SAAS,YAAY;CAC3B,MAAM,EAAE,WAAW,eAAe;CAClC,MAAM,EAAE,WAAW,eAAe;CAElC,eAAe,WAAW,cAAc;CAExC,IAAI,QACF,OAAO;CAGT,OACE,qBAAC,gBAAD;EACE,OAAO,OAAO;EACd,YAAY,MAAM,cAAc,KAAA;EAChC,SAAS,MAAM,WAAW,KAAA;EAC1B,OAAO,MAAM,SAAS;EAChB;EACI;YANZ,CAQE,oBAAC,SAAD;GAAO,UAAU;GAAc;GAAM,MAAK;GAAS,OAAO,UAAU,MAAM;EAAM,CAAA,GAChF,oBAAC,UAAD;GACE,gBAAc;GACd,cAAY,MAAM,SAAS;GAC3B,WAAW,MAAM,aAAa;GAC9B,WAAW,GACT,uUACF;GACA,cAAY,UAAU,YAAY;GAClC,aAAW,aAAa,SAAS;GACjC,UAAU;GACV,IAAI;GACE;GACN,eAAe,OAAO,WAAW,CAAC,OAAO;GACzC,MAAK;GACL,UAAU,MAAM,YAAY,KAAA;GAC5B,MAAK;aAEL,oBAAC,QAAD;IACE,WAAU;IACV,cAAY,UAAU,YAAY;GACnC,CAAA;EACK,CAAA,CACM;;AAEpB"}
@@ -12,3 +12,4 @@ export { RepeaterComponent } from './fields/repeater';
12
12
  export { SelectComponent } from './fields/select';
13
13
  export { TextareaComponent } from './fields/textarea';
14
14
  export { TextInputComponent } from './fields/text-input';
15
+ export { ToggleComponent } from './fields/toggle';
@@ -12,4 +12,5 @@ import { RepeaterComponent } from "./fields/repeater.js";
12
12
  import { SelectComponent } from "./fields/select.js";
13
13
  import { TextareaComponent } from "./fields/textarea.js";
14
14
  import { TextInputComponent } from "./fields/text-input.js";
15
- export { BuilderComponent, CheckboxComponent, ChoiceComponent, DateInputComponent, FileUploadComponent, FormComponent, HiddenInputComponent, NumberInputComponent, OtpInputComponent, PasswordInputComponent, RepeaterComponent, SelectComponent, TextInputComponent, TextareaComponent };
15
+ import { ToggleComponent } from "./fields/toggle.js";
16
+ export { BuilderComponent, CheckboxComponent, ChoiceComponent, DateInputComponent, FileUploadComponent, FormComponent, HiddenInputComponent, NumberInputComponent, OtpInputComponent, PasswordInputComponent, RepeaterComponent, SelectComponent, TextInputComponent, TextareaComponent, ToggleComponent };
@@ -8,5 +8,4 @@ export type ControlledField = FieldState & {
8
8
  error?: string;
9
9
  commit: (value: unknown) => void;
10
10
  };
11
- /** Shared wiring read by every store-controlled, condition-aware field. */
12
11
  export declare function useControlledField(node: Node): ControlledField;
@@ -6,7 +6,6 @@ import { useFormValue } from "./values.js";
6
6
  import { useDependentField } from "./use-dependent-field.js";
7
7
  import { useFieldCommit } from "./use-field-commit.js";
8
8
  //#region resources/js/form/components/use-controlled-field.ts
9
- /** Shared wiring read by every store-controlled, condition-aware field. */
10
9
  function useControlledField(node) {
11
10
  const { errors } = useFormContext();
12
11
  const scope = useFieldScope();
@@ -1 +1 @@
1
- {"version":3,"file":"use-controlled-field.js","names":[],"sources":["../../../resources/js/form/components/use-controlled-field.ts"],"sourcesContent":["import type { Node } from \"@lattice-php/lattice/core/types\";\nimport { testIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport type { FieldState } from \"./conditions\";\nimport { fieldProps } from \"./field-props\";\nimport { useFieldScope } from \"./field-scope\";\nimport { useFormContext } from \"./context\";\nimport { useDependentField } from \"./use-dependent-field\";\nimport { useFieldCommit } from \"./use-field-commit\";\nimport { useFormValue } from \"./values\";\n\nexport type ControlledField = FieldState & {\n localName: string;\n name: string;\n testId?: string;\n value: string;\n error?: string;\n commit: (value: unknown) => void;\n};\n\n/** Shared wiring read by every store-controlled, condition-aware field. */\nexport function useControlledField(node: Node): ControlledField {\n const { errors } = useFormContext();\n const scope = useFieldScope();\n const state = useDependentField(node);\n const props = fieldProps(node);\n const localName = props.name ?? \"\";\n\n const globalValue = useFormValue(localName);\n const storedValue = scope ? scope.getValue(localName) : globalValue;\n const currentValue = storedValue !== undefined ? storedValue : props.value;\n const value =\n typeof currentValue === \"string\" || typeof currentValue === \"number\"\n ? String(currentValue)\n : \"\";\n\n const domName = scope ? scope.scopedName(localName) : localName;\n const errorKey = scope ? scope.errorKey(localName) : localName;\n\n const { commit: commitField } = useFieldCommit();\n const commit = (next: unknown): void => commitField(localName, next);\n\n return {\n ...state,\n localName,\n name: domName,\n testId: testIdentity(localName),\n value,\n error: errors[errorKey],\n commit,\n };\n}\n"],"mappings":";;;;;;;;;AAoBA,SAAgB,mBAAmB,MAA6B;CAC9D,MAAM,EAAE,WAAW,eAAe;CAClC,MAAM,QAAQ,cAAc;CAC5B,MAAM,QAAQ,kBAAkB,IAAI;CACpC,MAAM,QAAQ,WAAW,IAAI;CAC7B,MAAM,YAAY,MAAM,QAAQ;CAEhC,MAAM,cAAc,aAAa,SAAS;CAC1C,MAAM,cAAc,QAAQ,MAAM,SAAS,SAAS,IAAI;CACxD,MAAM,eAAe,gBAAgB,KAAA,IAAY,cAAc,MAAM;CACrE,MAAM,QACJ,OAAO,iBAAiB,YAAY,OAAO,iBAAiB,WACxD,OAAO,YAAY,IACnB;CAEN,MAAM,UAAU,QAAQ,MAAM,WAAW,SAAS,IAAI;CACtD,MAAM,WAAW,QAAQ,MAAM,SAAS,SAAS,IAAI;CAErD,MAAM,EAAE,QAAQ,gBAAgB,eAAe;CAC/C,MAAM,UAAU,SAAwB,YAAY,WAAW,IAAI;CAEnE,OAAO;EACL,GAAG;EACH;EACA,MAAM;EACN,QAAQ,aAAa,SAAS;EAC9B;EACA,OAAO,OAAO;EACd;CACF;AACF"}
1
+ {"version":3,"file":"use-controlled-field.js","names":[],"sources":["../../../resources/js/form/components/use-controlled-field.ts"],"sourcesContent":["import type { Node } from \"@lattice-php/lattice/core/types\";\nimport { testIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport type { FieldState } from \"./conditions\";\nimport { fieldProps } from \"./field-props\";\nimport { useFieldScope } from \"./field-scope\";\nimport { useFormContext } from \"./context\";\nimport { useDependentField } from \"./use-dependent-field\";\nimport { useFieldCommit } from \"./use-field-commit\";\nimport { useFormValue } from \"./values\";\n\nexport type ControlledField = FieldState & {\n localName: string;\n name: string;\n testId?: string;\n value: string;\n error?: string;\n commit: (value: unknown) => void;\n};\n\nexport function useControlledField(node: Node): ControlledField {\n const { errors } = useFormContext();\n const scope = useFieldScope();\n const state = useDependentField(node);\n const props = fieldProps(node);\n const localName = props.name ?? \"\";\n\n const globalValue = useFormValue(localName);\n const storedValue = scope ? scope.getValue(localName) : globalValue;\n const currentValue = storedValue !== undefined ? storedValue : props.value;\n const value =\n typeof currentValue === \"string\" || typeof currentValue === \"number\"\n ? String(currentValue)\n : \"\";\n\n const domName = scope ? scope.scopedName(localName) : localName;\n const errorKey = scope ? scope.errorKey(localName) : localName;\n\n const { commit: commitField } = useFieldCommit();\n const commit = (next: unknown): void => commitField(localName, next);\n\n return {\n ...state,\n localName,\n name: domName,\n testId: testIdentity(localName),\n value,\n error: errors[errorKey],\n commit,\n };\n}\n"],"mappings":";;;;;;;;AAmBA,SAAgB,mBAAmB,MAA6B;CAC9D,MAAM,EAAE,WAAW,eAAe;CAClC,MAAM,QAAQ,cAAc;CAC5B,MAAM,QAAQ,kBAAkB,IAAI;CACpC,MAAM,QAAQ,WAAW,IAAI;CAC7B,MAAM,YAAY,MAAM,QAAQ;CAEhC,MAAM,cAAc,aAAa,SAAS;CAC1C,MAAM,cAAc,QAAQ,MAAM,SAAS,SAAS,IAAI;CACxD,MAAM,eAAe,gBAAgB,KAAA,IAAY,cAAc,MAAM;CACrE,MAAM,QACJ,OAAO,iBAAiB,YAAY,OAAO,iBAAiB,WACxD,OAAO,YAAY,IACnB;CAEN,MAAM,UAAU,QAAQ,MAAM,WAAW,SAAS,IAAI;CACtD,MAAM,WAAW,QAAQ,MAAM,SAAS,SAAS,IAAI;CAErD,MAAM,EAAE,QAAQ,gBAAgB,eAAe;CAC/C,MAAM,UAAU,SAAwB,YAAY,WAAW,IAAI;CAEnE,OAAO;EACL,GAAG;EACH;EACA,MAAM;EACN,QAAQ,aAAa,SAAS;EAC9B;EACA,OAAO,OAAO;EACd;CACF;AACF"}
@@ -26,7 +26,8 @@ var formComponents = createPlugin({
26
26
  }),
27
27
  "field.select": lazyComponent(loadFormComponent("SelectComponent")),
28
28
  "field.textarea": lazyComponent(loadFormComponent("TextareaComponent")),
29
- "field.text-input": lazyComponent(loadFormComponent("TextInputComponent"))
29
+ "field.text-input": lazyComponent(loadFormComponent("TextInputComponent")),
30
+ "field.toggle": lazyComponent(loadFormComponent("ToggleComponent"))
30
31
  },
31
32
  name: "lattice/form"
32
33
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../resources/js/form/index.ts"],"sourcesContent":["import { createPlugin, lazyComponent } from \"@lattice-php/lattice/core/registry\";\nimport type { RendererComponent, RendererComponentModule } from \"@lattice-php/lattice/core/types\";\nimport { FormSkeletonComponent } from \"./skeleton\";\n\ntype FormComponentName =\n | \"BuilderComponent\"\n | \"CheckboxComponent\"\n | \"ChoiceComponent\"\n | \"DateInputComponent\"\n | \"FileUploadComponent\"\n | \"FormComponent\"\n | \"HiddenInputComponent\"\n | \"NumberInputComponent\"\n | \"OtpInputComponent\"\n | \"PasswordInputComponent\"\n | \"RepeaterComponent\"\n | \"SelectComponent\"\n | \"TextareaComponent\"\n | \"TextInputComponent\";\n\nconst loadFormComponents = () => import(\"./components\");\n\nfunction loadFormComponent<TType extends string>(\n name: FormComponentName,\n): () => Promise<RendererComponentModule<TType>> {\n return async () => {\n const components = await loadFormComponents();\n\n return {\n default: components[name] as unknown as RendererComponent<TType>,\n };\n };\n}\n\nexport const formComponents = createPlugin({\n components: {\n form: lazyComponent(loadFormComponent(\"FormComponent\"), {\n fallback: FormSkeletonComponent,\n }),\n \"field.builder\": lazyComponent(loadFormComponent(\"BuilderComponent\")),\n \"field.checkbox\": lazyComponent(loadFormComponent(\"CheckboxComponent\")),\n \"field.choice\": lazyComponent(loadFormComponent(\"ChoiceComponent\")),\n \"field.date-input\": lazyComponent(loadFormComponent(\"DateInputComponent\")),\n \"field.file-upload\": lazyComponent(loadFormComponent(\"FileUploadComponent\")),\n \"field.hidden-input\": lazyComponent(loadFormComponent(\"HiddenInputComponent\")),\n \"field.number-input\": lazyComponent(loadFormComponent(\"NumberInputComponent\")),\n \"field.otp\": lazyComponent(loadFormComponent(\"OtpInputComponent\")),\n \"field.password-input\": lazyComponent(loadFormComponent(\"PasswordInputComponent\")),\n \"field.repeater\": lazyComponent(loadFormComponent(\"RepeaterComponent\")),\n // Loaded from its own module so TipTap is split into a separate chunk,\n // only fetched on pages that actually render a rich editor.\n \"field.rich-editor\": lazyComponent<\"field.rich-editor\">(async () => {\n const { RichEditorComponent } = await import(\"./components/fields/rich-editor\");\n\n return { default: RichEditorComponent };\n }),\n \"field.select\": lazyComponent(loadFormComponent(\"SelectComponent\")),\n \"field.textarea\": lazyComponent(loadFormComponent(\"TextareaComponent\")),\n \"field.text-input\": lazyComponent(loadFormComponent(\"TextInputComponent\")),\n },\n name: \"lattice/form\",\n});\n"],"mappings":";;;AAoBA,IAAM,2BAA2B,OAAO;AAExC,SAAS,kBACP,MAC+C;CAC/C,OAAO,YAAY;EAGjB,OAAO,EACL,UAAS,MAHc,mBAAmB,GAGtB,MACtB;CACF;AACF;AAEA,IAAa,iBAAiB,aAAa;CACzC,YAAY;EACV,MAAM,cAAc,kBAAkB,eAAe,GAAG,EACtD,UAAU,sBACZ,CAAC;EACD,iBAAiB,cAAc,kBAAkB,kBAAkB,CAAC;EACpE,kBAAkB,cAAc,kBAAkB,mBAAmB,CAAC;EACtE,gBAAgB,cAAc,kBAAkB,iBAAiB,CAAC;EAClE,oBAAoB,cAAc,kBAAkB,oBAAoB,CAAC;EACzE,qBAAqB,cAAc,kBAAkB,qBAAqB,CAAC;EAC3E,sBAAsB,cAAc,kBAAkB,sBAAsB,CAAC;EAC7E,sBAAsB,cAAc,kBAAkB,sBAAsB,CAAC;EAC7E,aAAa,cAAc,kBAAkB,mBAAmB,CAAC;EACjE,wBAAwB,cAAc,kBAAkB,wBAAwB,CAAC;EACjF,kBAAkB,cAAc,kBAAkB,mBAAmB,CAAC;EAGtE,qBAAqB,cAAmC,YAAY;GAClE,MAAM,EAAE,wBAAwB,MAAM,OAAO;GAE7C,OAAO,EAAE,SAAS,oBAAoB;EACxC,CAAC;EACD,gBAAgB,cAAc,kBAAkB,iBAAiB,CAAC;EAClE,kBAAkB,cAAc,kBAAkB,mBAAmB,CAAC;EACtE,oBAAoB,cAAc,kBAAkB,oBAAoB,CAAC;CAC3E;CACA,MAAM;AACR,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../resources/js/form/index.ts"],"sourcesContent":["import { createPlugin, lazyComponent } from \"@lattice-php/lattice/core/registry\";\nimport type { RendererComponent, RendererComponentModule } from \"@lattice-php/lattice/core/types\";\nimport { FormSkeletonComponent } from \"./skeleton\";\n\ntype FormComponentName =\n | \"BuilderComponent\"\n | \"CheckboxComponent\"\n | \"ChoiceComponent\"\n | \"DateInputComponent\"\n | \"FileUploadComponent\"\n | \"FormComponent\"\n | \"HiddenInputComponent\"\n | \"NumberInputComponent\"\n | \"OtpInputComponent\"\n | \"PasswordInputComponent\"\n | \"RepeaterComponent\"\n | \"SelectComponent\"\n | \"TextareaComponent\"\n | \"TextInputComponent\"\n | \"ToggleComponent\";\n\nconst loadFormComponents = () => import(\"./components\");\n\nfunction loadFormComponent<TType extends string>(\n name: FormComponentName,\n): () => Promise<RendererComponentModule<TType>> {\n return async () => {\n const components = await loadFormComponents();\n\n return {\n default: components[name] as unknown as RendererComponent<TType>,\n };\n };\n}\n\nexport const formComponents = createPlugin({\n components: {\n form: lazyComponent(loadFormComponent(\"FormComponent\"), {\n fallback: FormSkeletonComponent,\n }),\n \"field.builder\": lazyComponent(loadFormComponent(\"BuilderComponent\")),\n \"field.checkbox\": lazyComponent(loadFormComponent(\"CheckboxComponent\")),\n \"field.choice\": lazyComponent(loadFormComponent(\"ChoiceComponent\")),\n \"field.date-input\": lazyComponent(loadFormComponent(\"DateInputComponent\")),\n \"field.file-upload\": lazyComponent(loadFormComponent(\"FileUploadComponent\")),\n \"field.hidden-input\": lazyComponent(loadFormComponent(\"HiddenInputComponent\")),\n \"field.number-input\": lazyComponent(loadFormComponent(\"NumberInputComponent\")),\n \"field.otp\": lazyComponent(loadFormComponent(\"OtpInputComponent\")),\n \"field.password-input\": lazyComponent(loadFormComponent(\"PasswordInputComponent\")),\n \"field.repeater\": lazyComponent(loadFormComponent(\"RepeaterComponent\")),\n // Loaded from its own module so TipTap is split into a separate chunk,\n // only fetched on pages that actually render a rich editor.\n \"field.rich-editor\": lazyComponent<\"field.rich-editor\">(async () => {\n const { RichEditorComponent } = await import(\"./components/fields/rich-editor\");\n\n return { default: RichEditorComponent };\n }),\n \"field.select\": lazyComponent(loadFormComponent(\"SelectComponent\")),\n \"field.textarea\": lazyComponent(loadFormComponent(\"TextareaComponent\")),\n \"field.text-input\": lazyComponent(loadFormComponent(\"TextInputComponent\")),\n \"field.toggle\": lazyComponent(loadFormComponent(\"ToggleComponent\")),\n },\n name: \"lattice/form\",\n});\n"],"mappings":";;;AAqBA,IAAM,2BAA2B,OAAO;AAExC,SAAS,kBACP,MAC+C;CAC/C,OAAO,YAAY;EAGjB,OAAO,EACL,UAAS,MAHc,mBAAmB,GAGtB,MACtB;CACF;AACF;AAEA,IAAa,iBAAiB,aAAa;CACzC,YAAY;EACV,MAAM,cAAc,kBAAkB,eAAe,GAAG,EACtD,UAAU,sBACZ,CAAC;EACD,iBAAiB,cAAc,kBAAkB,kBAAkB,CAAC;EACpE,kBAAkB,cAAc,kBAAkB,mBAAmB,CAAC;EACtE,gBAAgB,cAAc,kBAAkB,iBAAiB,CAAC;EAClE,oBAAoB,cAAc,kBAAkB,oBAAoB,CAAC;EACzE,qBAAqB,cAAc,kBAAkB,qBAAqB,CAAC;EAC3E,sBAAsB,cAAc,kBAAkB,sBAAsB,CAAC;EAC7E,sBAAsB,cAAc,kBAAkB,sBAAsB,CAAC;EAC7E,aAAa,cAAc,kBAAkB,mBAAmB,CAAC;EACjE,wBAAwB,cAAc,kBAAkB,wBAAwB,CAAC;EACjF,kBAAkB,cAAc,kBAAkB,mBAAmB,CAAC;EAGtE,qBAAqB,cAAmC,YAAY;GAClE,MAAM,EAAE,wBAAwB,MAAM,OAAO;GAE7C,OAAO,EAAE,SAAS,oBAAoB;EACxC,CAAC;EACD,gBAAgB,cAAc,kBAAkB,iBAAiB,CAAC;EAClE,kBAAkB,cAAc,kBAAkB,mBAAmB,CAAC;EACtE,oBAAoB,cAAc,kBAAkB,oBAAoB,CAAC;EACzE,gBAAgB,cAAc,kBAAkB,iBAAiB,CAAC;CACpE;CACA,MAAM;AACR,CAAC"}
package/dist/index.js CHANGED
@@ -12,14 +12,14 @@ import { onCallout } from "./toast/callout.js";
12
12
  import { EventBridge } from "./events/event-bridge.js";
13
13
  import Callouts from "./layout/callouts.js";
14
14
  import { createPlugin, createRegistry, eagerComponent, extendRegistry, lazyComponent } from "./core/registry.js";
15
+ import { builtinEffectHandlers, effectHandler, mergeEffectHandlers } from "./effects/registry.js";
16
+ import { dispatchActionError, dispatchEffects, getActionEffects, isActionEffect } from "./effects/dispatch.js";
17
+ import { useEffectDispatcher } from "./effects/use-effect-dispatcher.js";
15
18
  import { OutletContext } from "./layout/context.js";
16
19
  import { layoutComponents } from "./layout/components.js";
17
20
  import SchemaLayout from "./layout/schema-layout.js";
18
21
  import { RealtimeListeners } from "./realtime/listeners.js";
19
22
  import { createLayoutResolver, createPageResolver, pageComponentName, withVisitHeaders } from "./inertia.js";
20
- import { builtinEffectHandlers, effectHandler, mergeEffectHandlers } from "./effects/registry.js";
21
- import { dispatchActionError, dispatchEffects, getActionEffects, isActionEffect } from "./effects/dispatch.js";
22
- import { useEffectDispatcher } from "./effects/use-effect-dispatcher.js";
23
23
  import { useChat } from "./chat/use-chat.js";
24
24
  import { chatPlugin } from "./chat/index.js";
25
25
  import { registry } from "./registry.js";
package/dist/lattice.css CHANGED
@@ -51,6 +51,9 @@
51
51
  --lt-icon-md: 1rem;
52
52
  --lt-icon-lg: 1.25rem;
53
53
  --lt-icon-xl: 1.5rem;
54
+ --lt-icon-2xl: 2rem;
55
+ --lt-icon-3xl: 2.5rem;
56
+ --lt-icon-4xl: 3rem;
54
57
  --lt-icon-stroke: 1.5;
55
58
 
56
59
  /* Control heights — density lever */
@@ -192,6 +195,9 @@
192
195
  --spacing-lt-icon-md: var(--lt-icon-md);
193
196
  --spacing-lt-icon-lg: var(--lt-icon-lg);
194
197
  --spacing-lt-icon-xl: var(--lt-icon-xl);
198
+ --spacing-lt-icon-2xl: var(--lt-icon-2xl);
199
+ --spacing-lt-icon-3xl: var(--lt-icon-3xl);
200
+ --spacing-lt-icon-4xl: var(--lt-icon-4xl);
195
201
  --spacing-lt-control-sm: var(--lt-control-h-sm);
196
202
  --spacing-lt-control-md: var(--lt-control-h-md);
197
203
  --spacing-lt-control-lg: var(--lt-control-h-lg);
@@ -4,6 +4,7 @@ import { IconRenderer } from "../icons/icon-renderer.js";
4
4
  import { useCollapsed } from "../core/collapsed-context.js";
5
5
  import { prefixedNodeTestId } from "../core/test-id.js";
6
6
  import { Popover } from "./popover.js";
7
+ import { useAction } from "../action/use-action.js";
7
8
  import { Link, usePage } from "@inertiajs/react";
8
9
  import { useState } from "react";
9
10
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -48,6 +49,14 @@ var MenuItemComponent = ({ children, node }) => {
48
49
  className: "ml-auto"
49
50
  }) : null
50
51
  ] });
52
+ const action = node.props.action;
53
+ if (action && href === "" && !children) return /* @__PURE__ */ jsx(ActionMenuItem, {
54
+ action,
55
+ className: cn(rowClass, "w-full", collapsed && "group relative justify-center", iconOnly && "justify-center"),
56
+ label: collapsed || iconOnly ? label : void 0,
57
+ testId,
58
+ children: content
59
+ });
51
60
  if (href === "") {
52
61
  if (!children) return collapsed ? null : /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("span", {
53
62
  className: "flex items-center gap-2 px-3 py-2 text-xs font-semibold tracking-wide text-lt-muted-fg uppercase",
@@ -116,6 +125,18 @@ function FlyoutGroup({ children, icon, label, testId }) {
116
125
  })
117
126
  }) });
118
127
  }
128
+ function ActionMenuItem({ action, children, className, label, testId }) {
129
+ const { processing, requestSubmit, overlays } = useAction(action);
130
+ return /* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("button", {
131
+ "aria-label": label,
132
+ className,
133
+ "data-test": testId,
134
+ disabled: processing,
135
+ onClick: requestSubmit,
136
+ type: "button",
137
+ children
138
+ }), overlays] });
139
+ }
119
140
  //#endregion
120
141
  export { MenuItemComponent as default };
121
142
 
@@ -1 +1 @@
1
- {"version":3,"file":"menu-item.js","names":[],"sources":["../../resources/js/layout/menu-item.tsx"],"sourcesContent":["import { Link, usePage } from \"@inertiajs/react\";\nimport { useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { prefixedNodeTestId } from \"@lattice-php/lattice/core/test-id\";\nimport type { RendererComponent, Schema } from \"@lattice-php/lattice/core/types\";\nimport { Icon, IconRenderer } from \"@lattice-php/lattice/icons\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport type { Affix } from \"@lattice-php/lattice/types/generated\";\nimport { useSidebarCollapsed } from \"./context\";\nimport { Popover } from \"./popover\";\n\nconst rowClass =\n \"flex items-center gap-2 rounded-lt-sm px-3 py-2 text-base font-medium text-lt-fg transition-colors hover:bg-lt-muted\";\n\nfunction MenuAffix({ affix, className }: { affix: Affix; className?: string }) {\n if (affix.icon) {\n return <IconRenderer className={cn(\"size-lt-icon-md shrink-0\", className)} icon={affix.icon} />;\n }\n\n return <span className={cn(\"shrink-0 text-sm text-lt-muted-fg\", className)}>{affix.text}</span>;\n}\n\nfunction schemaContainsPath(schema: Schema | undefined, path: string): boolean {\n return (schema ?? []).some(\n (child) => child.props?.href === path || schemaContainsPath(child.schema, path),\n );\n}\n\nconst MenuItemComponent: RendererComponent<\"menu-item\"> = ({ children, node }) => {\n const collapsed = useSidebarCollapsed();\n const icon = node.props.icon;\n const label = node.props.label;\n const prefix = node.props.prefix;\n const suffix = node.props.suffix;\n const iconOnly = Boolean(icon);\n const flyoutIcon = icon ?? prefix?.icon ?? null;\n const href = node.props.href ?? \"\";\n const currentPath = usePage().url.split(\"?\")[0];\n const testId = prefixedNodeTestId(\"menu\", node);\n\n const content = icon ? (\n <IconRenderer className=\"size-lt-icon-md shrink-0\" icon={icon} />\n ) : (\n <>\n {prefix ? <MenuAffix affix={prefix} /> : null}\n {collapsed ? (\n <span\n className=\"pointer-events-none absolute top-1/2 left-full z-50 ml-2 hidden -translate-y-1/2 rounded-lt-sm border border-lt-border bg-lt-popover px-2 py-1 text-sm whitespace-nowrap text-lt-popover-fg shadow-lt-md group-hover:block\"\n role=\"tooltip\"\n >\n {label}\n </span>\n ) : (\n <span>{label}</span>\n )}\n {suffix ? <MenuAffix affix={suffix} className=\"ml-auto\" /> : null}\n </>\n );\n\n if (href === \"\") {\n if (!children) {\n return collapsed ? null : (\n <li>\n <span className=\"flex items-center gap-2 px-3 py-2 text-xs font-semibold tracking-wide text-lt-muted-fg uppercase\">\n {content}\n </span>\n </li>\n );\n }\n\n if (collapsed) {\n return (\n <FlyoutGroup icon={flyoutIcon} label={label} testId={testId}>\n {children}\n </FlyoutGroup>\n );\n }\n\n return (\n <CollapsibleItem\n content={content}\n defaultOpen={schemaContainsPath(node.schema, currentPath)}\n testId={testId}\n >\n {children}\n </CollapsibleItem>\n );\n }\n\n const active = currentPath === href;\n const method = node.props.method ?? \"get\";\n\n return (\n <li>\n <Link\n aria-current={active ? \"page\" : undefined}\n aria-label={collapsed || iconOnly ? label : undefined}\n as={method === \"get\" ? undefined : \"button\"}\n className={cn(\n rowClass,\n \"w-full\",\n collapsed && \"group relative justify-center\",\n iconOnly && \"justify-center\",\n active && \"bg-lt-muted font-medium\",\n )}\n data-test={testId}\n href={href}\n method={method}\n >\n {content}\n </Link>\n </li>\n );\n};\n\nfunction CollapsibleItem({\n children,\n content,\n defaultOpen,\n testId,\n}: {\n children: ReactNode;\n content: ReactNode;\n defaultOpen: boolean;\n testId?: string;\n}) {\n const [open, setOpen] = useState(defaultOpen);\n\n return (\n <li>\n <button\n aria-expanded={open}\n className={cn(rowClass, \"w-full\")}\n data-test={testId}\n onClick={() => setOpen((value) => !value)}\n type=\"button\"\n >\n {content}\n <Icon\n name=\"chevron-right\"\n className={cn(\n \"ml-auto size-lt-icon-md shrink-0 transition-transform\",\n open && \"rotate-90\",\n )}\n />\n </button>\n {open ? <ul className=\"mt-1 flex flex-col gap-1 pl-3\">{children}</ul> : null}\n </li>\n );\n}\n\nfunction FlyoutGroup({\n children,\n icon,\n label,\n testId,\n}: {\n children: ReactNode;\n icon?: string | null;\n label: string;\n testId?: string;\n}) {\n return (\n <li>\n <Popover\n className=\"min-w-48\"\n placement=\"right\"\n testId={testId}\n trigger={\n icon ? (\n <IconRenderer className=\"size-lt-icon-md shrink-0\" icon={icon} />\n ) : (\n <span>{label}</span>\n )\n }\n triggerClassName={cn(rowClass, \"justify-center\")}\n triggerLabel={label}\n >\n <ul className=\"flex flex-col gap-1\">\n <li className=\"px-3 py-1.5 text-xs font-semibold tracking-wide text-lt-muted-fg uppercase\">\n {label}\n </li>\n {children}\n </ul>\n </Popover>\n </li>\n );\n}\n\nexport default MenuItemComponent;\n"],"mappings":"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"}
1
+ {"version":3,"file":"menu-item.js","names":[],"sources":["../../resources/js/layout/menu-item.tsx"],"sourcesContent":["import { Link, usePage } from \"@inertiajs/react\";\nimport { useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { useAction } from \"@lattice-php/lattice/action/use-action\";\nimport { prefixedNodeTestId } from \"@lattice-php/lattice/core/test-id\";\nimport type { Node, RendererComponent, Schema } from \"@lattice-php/lattice/core/types\";\nimport { Icon, IconRenderer } from \"@lattice-php/lattice/icons\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport type { Affix } from \"@lattice-php/lattice/types/generated\";\nimport { useSidebarCollapsed } from \"./context\";\nimport { Popover } from \"./popover\";\n\nconst rowClass =\n \"flex items-center gap-2 rounded-lt-sm px-3 py-2 text-base font-medium text-lt-fg transition-colors hover:bg-lt-muted\";\n\nfunction MenuAffix({ affix, className }: { affix: Affix; className?: string }) {\n if (affix.icon) {\n return <IconRenderer className={cn(\"size-lt-icon-md shrink-0\", className)} icon={affix.icon} />;\n }\n\n return <span className={cn(\"shrink-0 text-sm text-lt-muted-fg\", className)}>{affix.text}</span>;\n}\n\nfunction schemaContainsPath(schema: Schema | undefined, path: string): boolean {\n return (schema ?? []).some(\n (child) => child.props?.href === path || schemaContainsPath(child.schema, path),\n );\n}\n\nconst MenuItemComponent: RendererComponent<\"menu-item\"> = ({ children, node }) => {\n const collapsed = useSidebarCollapsed();\n const icon = node.props.icon;\n const label = node.props.label;\n const prefix = node.props.prefix;\n const suffix = node.props.suffix;\n const iconOnly = Boolean(icon);\n const flyoutIcon = icon ?? prefix?.icon ?? null;\n const href = node.props.href ?? \"\";\n const currentPath = usePage().url.split(\"?\")[0];\n const testId = prefixedNodeTestId(\"menu\", node);\n\n const content = icon ? (\n <IconRenderer className=\"size-lt-icon-md shrink-0\" icon={icon} />\n ) : (\n <>\n {prefix ? <MenuAffix affix={prefix} /> : null}\n {collapsed ? (\n <span\n className=\"pointer-events-none absolute top-1/2 left-full z-50 ml-2 hidden -translate-y-1/2 rounded-lt-sm border border-lt-border bg-lt-popover px-2 py-1 text-sm whitespace-nowrap text-lt-popover-fg shadow-lt-md group-hover:block\"\n role=\"tooltip\"\n >\n {label}\n </span>\n ) : (\n <span>{label}</span>\n )}\n {suffix ? <MenuAffix affix={suffix} className=\"ml-auto\" /> : null}\n </>\n );\n\n const action = node.props.action;\n\n if (action && href === \"\" && !children) {\n return (\n <ActionMenuItem\n action={action as Node<\"action\">}\n className={cn(\n rowClass,\n \"w-full\",\n collapsed && \"group relative justify-center\",\n iconOnly && \"justify-center\",\n )}\n label={collapsed || iconOnly ? label : undefined}\n testId={testId}\n >\n {content}\n </ActionMenuItem>\n );\n }\n\n if (href === \"\") {\n if (!children) {\n return collapsed ? null : (\n <li>\n <span className=\"flex items-center gap-2 px-3 py-2 text-xs font-semibold tracking-wide text-lt-muted-fg uppercase\">\n {content}\n </span>\n </li>\n );\n }\n\n if (collapsed) {\n return (\n <FlyoutGroup icon={flyoutIcon} label={label} testId={testId}>\n {children}\n </FlyoutGroup>\n );\n }\n\n return (\n <CollapsibleItem\n content={content}\n defaultOpen={schemaContainsPath(node.schema, currentPath)}\n testId={testId}\n >\n {children}\n </CollapsibleItem>\n );\n }\n\n const active = currentPath === href;\n const method = node.props.method ?? \"get\";\n\n return (\n <li>\n <Link\n aria-current={active ? \"page\" : undefined}\n aria-label={collapsed || iconOnly ? label : undefined}\n as={method === \"get\" ? undefined : \"button\"}\n className={cn(\n rowClass,\n \"w-full\",\n collapsed && \"group relative justify-center\",\n iconOnly && \"justify-center\",\n active && \"bg-lt-muted font-medium\",\n )}\n data-test={testId}\n href={href}\n method={method}\n >\n {content}\n </Link>\n </li>\n );\n};\n\nfunction CollapsibleItem({\n children,\n content,\n defaultOpen,\n testId,\n}: {\n children: ReactNode;\n content: ReactNode;\n defaultOpen: boolean;\n testId?: string;\n}) {\n const [open, setOpen] = useState(defaultOpen);\n\n return (\n <li>\n <button\n aria-expanded={open}\n className={cn(rowClass, \"w-full\")}\n data-test={testId}\n onClick={() => setOpen((value) => !value)}\n type=\"button\"\n >\n {content}\n <Icon\n name=\"chevron-right\"\n className={cn(\n \"ml-auto size-lt-icon-md shrink-0 transition-transform\",\n open && \"rotate-90\",\n )}\n />\n </button>\n {open ? <ul className=\"mt-1 flex flex-col gap-1 pl-3\">{children}</ul> : null}\n </li>\n );\n}\n\nfunction FlyoutGroup({\n children,\n icon,\n label,\n testId,\n}: {\n children: ReactNode;\n icon?: string | null;\n label: string;\n testId?: string;\n}) {\n return (\n <li>\n <Popover\n className=\"min-w-48\"\n placement=\"right\"\n testId={testId}\n trigger={\n icon ? (\n <IconRenderer className=\"size-lt-icon-md shrink-0\" icon={icon} />\n ) : (\n <span>{label}</span>\n )\n }\n triggerClassName={cn(rowClass, \"justify-center\")}\n triggerLabel={label}\n >\n <ul className=\"flex flex-col gap-1\">\n <li className=\"px-3 py-1.5 text-xs font-semibold tracking-wide text-lt-muted-fg uppercase\">\n {label}\n </li>\n {children}\n </ul>\n </Popover>\n </li>\n );\n}\n\nfunction ActionMenuItem({\n action,\n children,\n className,\n label,\n testId,\n}: {\n action: Node<\"action\">;\n children: ReactNode;\n className: string;\n label?: string;\n testId?: string;\n}) {\n const { processing, requestSubmit, overlays } = useAction(action);\n\n return (\n <li>\n <button\n aria-label={label}\n className={className}\n data-test={testId}\n disabled={processing}\n onClick={requestSubmit}\n type=\"button\"\n >\n {children}\n </button>\n {overlays}\n </li>\n );\n}\n\nexport default MenuItemComponent;\n"],"mappings":"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"}
package/dist/lib/utils.js CHANGED
@@ -1,6 +1,16 @@
1
1
  import { clsx } from "clsx";
2
- import { twMerge } from "tailwind-merge";
2
+ import { extendTailwindMerge } from "tailwind-merge";
3
3
  //#region resources/js/lib/utils.ts
4
+ var twMerge = extendTailwindMerge({ extend: { classGroups: { "lt-icon-size": [{ size: [{ lt: [{ icon: [
5
+ "xs",
6
+ "sm",
7
+ "md",
8
+ "lg",
9
+ "xl",
10
+ "2xl",
11
+ "3xl",
12
+ "4xl"
13
+ ] }] }] }] } } });
4
14
  function cn(...inputs) {
5
15
  return twMerge(clsx(inputs));
6
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../../resources/js/lib/utils.ts"],"sourcesContent":["import { clsx } from \"clsx\";\nimport type { ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"],"mappings":";;;AAIA,SAAgB,GAAG,GAAG,QAAsB;CAC1C,OAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B"}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../resources/js/lib/utils.ts"],"sourcesContent":["import { clsx } from \"clsx\";\nimport type { ClassValue } from \"clsx\";\nimport { extendTailwindMerge } from \"tailwind-merge\";\n\n// The sprite renderer always applies `size-lt-icon-md` as a baseline, and these\n// custom utilities are invisible to tailwind-merge's stock config, so an explicit\n// `size-lt-icon-*` would otherwise be left to fight the baseline on source order.\n// Registering them as one conflicting group lets the explicit size always win.\nconst twMerge = extendTailwindMerge<\"lt-icon-size\">({\n extend: {\n classGroups: {\n \"lt-icon-size\": [\n { size: [{ lt: [{ icon: [\"xs\", \"sm\", \"md\", \"lg\", \"xl\", \"2xl\", \"3xl\", \"4xl\"] }] }] },\n ],\n },\n },\n});\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"],"mappings":";;;AAQA,IAAM,UAAU,oBAAoC,EAClD,QAAQ,EACN,aAAa,EACX,gBAAgB,CACd,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM;CAAC;CAAM;CAAM;CAAM;CAAM;CAAM;CAAO;CAAO;AAAK,EAAE,CAAC,EAAE,CAAC,EAAE,CACpF,EACF,EACF,EACF,CAAC;AAED,SAAgB,GAAG,GAAG,QAAsB;CAC1C,OAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B"}
@@ -1,3 +1,2 @@
1
1
  import { ColumnCellComponent } from '../../registry';
2
- /** Renders a boolean column as a check (truthy) or cross (falsy) icon. */
3
2
  export declare const BooleanCell: ColumnCellComponent<"column.boolean">;
@@ -5,7 +5,6 @@ import { jsx } from "react/jsx-runtime";
5
5
  function isTruthy(value) {
6
6
  return value === true || value === 1 || value === "1" || value === "true";
7
7
  }
8
- /** Renders a boolean column as a check (truthy) or cross (falsy) icon. */
9
8
  var BooleanCell = ({ value }) => {
10
9
  const truthy = isTruthy(value);
11
10
  return /* @__PURE__ */ jsx("span", {
@@ -1 +1 @@
1
- {"version":3,"file":"boolean-cell.js","names":[],"sources":["../../../../resources/js/table/components/cells/boolean-cell.tsx"],"sourcesContent":["import { Icon } from \"@lattice-php/lattice/icons\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport type { ColumnCellComponent } from \"../../registry\";\n\nfunction isTruthy(value: unknown): boolean {\n return value === true || value === 1 || value === \"1\" || value === \"true\";\n}\n\n/** Renders a boolean column as a check (truthy) or cross (falsy) icon. */\nexport const BooleanCell: ColumnCellComponent<\"column.boolean\"> = ({ value }) => {\n const truthy = isTruthy(value);\n\n return (\n <span aria-label={String(truthy)} role=\"img\">\n <Icon\n name={truthy ? \"check\" : \"x\"}\n className={cn(\"size-lt-icon-md\", truthy ? \"text-lt-success\" : \"text-lt-muted-fg\")}\n />\n </span>\n );\n};\n"],"mappings":";;;;AAIA,SAAS,SAAS,OAAyB;CACzC,OAAO,UAAU,QAAQ,UAAU,KAAK,UAAU,OAAO,UAAU;AACrE;;AAGA,IAAa,eAAsD,EAAE,YAAY;CAC/E,MAAM,SAAS,SAAS,KAAK;CAE7B,OACE,oBAAC,QAAD;EAAM,cAAY,OAAO,MAAM;EAAG,MAAK;YACrC,oBAAC,MAAD;GACE,MAAM,SAAS,UAAU;GACzB,WAAW,GAAG,mBAAmB,SAAS,oBAAoB,kBAAkB;EACjF,CAAA;CACG,CAAA;AAEV"}
1
+ {"version":3,"file":"boolean-cell.js","names":[],"sources":["../../../../resources/js/table/components/cells/boolean-cell.tsx"],"sourcesContent":["import { Icon } from \"@lattice-php/lattice/icons\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport type { ColumnCellComponent } from \"../../registry\";\n\nfunction isTruthy(value: unknown): boolean {\n return value === true || value === 1 || value === \"1\" || value === \"true\";\n}\n\nexport const BooleanCell: ColumnCellComponent<\"column.boolean\"> = ({ value }) => {\n const truthy = isTruthy(value);\n\n return (\n <span aria-label={String(truthy)} role=\"img\">\n <Icon\n name={truthy ? \"check\" : \"x\"}\n className={cn(\"size-lt-icon-md\", truthy ? \"text-lt-success\" : \"text-lt-muted-fg\")}\n />\n </span>\n );\n};\n"],"mappings":";;;;AAIA,SAAS,SAAS,OAAyB;CACzC,OAAO,UAAU,QAAQ,UAAU,KAAK,UAAU,OAAO,UAAU;AACrE;AAEA,IAAa,eAAsD,EAAE,YAAY;CAC/E,MAAM,SAAS,SAAS,KAAK;CAE7B,OACE,oBAAC,QAAD;EAAM,cAAY,OAAO,MAAM;EAAG,MAAK;YACrC,oBAAC,MAAD;GACE,MAAM,SAAS,UAAU;GACzB,WAAW,GAAG,mBAAmB,SAAS,oBAAoB,kBAAkB;EACjF,CAAA;CACG,CAAA;AAEV"}
@@ -1,2 +1 @@
1
- /** Parses a cell value to a finite number, or null when it isn't numeric. */
2
1
  export declare function numericValue(value: unknown): number | null;
@@ -1,5 +1,4 @@
1
1
  //#region resources/js/table/components/cells/numeric.ts
2
- /** Parses a cell value to a finite number, or null when it isn't numeric. */
3
2
  function numericValue(value) {
4
3
  const number = typeof value === "number" ? value : Number(value);
5
4
  return value !== null && value !== void 0 && value !== "" && !Number.isNaN(number) ? number : null;
@@ -1 +1 @@
1
- {"version":3,"file":"numeric.js","names":[],"sources":["../../../../resources/js/table/components/cells/numeric.ts"],"sourcesContent":["/** Parses a cell value to a finite number, or null when it isn't numeric. */\nexport function numericValue(value: unknown): number | null {\n const number = typeof value === \"number\" ? value : Number(value);\n\n return value !== null && value !== undefined && value !== \"\" && !Number.isNaN(number)\n ? number\n : null;\n}\n"],"mappings":";;AACA,SAAgB,aAAa,OAA+B;CAC1D,MAAM,SAAS,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK;CAE/D,OAAO,UAAU,QAAQ,UAAU,KAAA,KAAa,UAAU,MAAM,CAAC,OAAO,MAAM,MAAM,IAChF,SACA;AACN"}
1
+ {"version":3,"file":"numeric.js","names":[],"sources":["../../../../resources/js/table/components/cells/numeric.ts"],"sourcesContent":["export function numericValue(value: unknown): number | null {\n const number = typeof value === \"number\" ? value : Number(value);\n\n return value !== null && value !== undefined && value !== \"\" && !Number.isNaN(number)\n ? number\n : null;\n}\n"],"mappings":";AAAA,SAAgB,aAAa,OAA+B;CAC1D,MAAM,SAAS,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK;CAE/D,OAAO,UAAU,QAAQ,UAAU,KAAA,KAAa,UAAU,MAAM,CAAC,OAAO,MAAM,MAAM,IAChF,SACA;AACN"}
@@ -1,3 +1,2 @@
1
1
  import { ColumnCellComponent } from '../../registry';
2
- /** Renders a stack column's nested columns as stacked text rows. */
3
2
  export declare const StackCell: ColumnCellComponent<"column.stack">;
@@ -1,7 +1,6 @@
1
1
  import { TextCell } from "./text-cell.js";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  //#region resources/js/table/components/cells/stack-cell.tsx
4
- /** Renders a stack column's nested columns as stacked text rows. */
5
4
  var StackCell = ({ column, row }) => /* @__PURE__ */ jsx("div", {
6
5
  className: "grid gap-1",
7
6
  children: (column.columns ?? []).map((stackedColumn) => /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(TextCell, {
@@ -1 +1 @@
1
- {"version":3,"file":"stack-cell.js","names":[],"sources":["../../../../resources/js/table/components/cells/stack-cell.tsx"],"sourcesContent":["import type { ColumnCellComponent } from \"../../registry\";\nimport type { ColumnPropsOf } from \"../../types\";\nimport { TextCell } from \"./text-cell\";\n\n/** Renders a stack column's nested columns as stacked text rows. */\nexport const StackCell: ColumnCellComponent<\"column.stack\"> = ({ column, row }) => (\n <div className=\"grid gap-1\">\n {(column.columns ?? []).map((stackedColumn) => (\n <span key={stackedColumn.key}>\n <TextCell\n column={stackedColumn}\n props={(stackedColumn.props ?? {}) as ColumnPropsOf<\"column.text\">}\n row={row}\n value={row[stackedColumn.key]}\n />\n </span>\n ))}\n </div>\n);\n"],"mappings":";;;;AAKA,IAAa,aAAkD,EAAE,QAAQ,UACvE,oBAAC,OAAD;CAAK,WAAU;YACX,OAAO,WAAW,CAAC,GAAG,KAAK,kBAC3B,oBAAC,QAAD,EAAA,UACE,oBAAC,UAAD;EACE,QAAQ;EACR,OAAQ,cAAc,SAAS,CAAC;EAC3B;EACL,OAAO,IAAI,cAAc;CAC1B,CAAA,EACG,GAPK,cAAc,GAOnB,CACP;AACE,CAAA"}
1
+ {"version":3,"file":"stack-cell.js","names":[],"sources":["../../../../resources/js/table/components/cells/stack-cell.tsx"],"sourcesContent":["import type { ColumnCellComponent } from \"../../registry\";\nimport type { ColumnPropsOf } from \"../../types\";\nimport { TextCell } from \"./text-cell\";\n\nexport const StackCell: ColumnCellComponent<\"column.stack\"> = ({ column, row }) => (\n <div className=\"grid gap-1\">\n {(column.columns ?? []).map((stackedColumn) => (\n <span key={stackedColumn.key}>\n <TextCell\n column={stackedColumn}\n props={(stackedColumn.props ?? {}) as ColumnPropsOf<\"column.text\">}\n row={row}\n value={row[stackedColumn.key]}\n />\n </span>\n ))}\n </div>\n);\n"],"mappings":";;;AAIA,IAAa,aAAkD,EAAE,QAAQ,UACvE,oBAAC,OAAD;CAAK,WAAU;YACX,OAAO,WAAW,CAAC,GAAG,KAAK,kBAC3B,oBAAC,QAAD,EAAA,UACE,oBAAC,UAAD;EACE,QAAQ;EACR,OAAQ,cAAc,SAAS,CAAC;EAC3B;EACL,OAAO,IAAI,cAAc;CAC1B,CAAA,EACG,GAPK,cAAc,GAOnB,CACP;AACE,CAAA"}
@@ -1,9 +1,7 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { ColumnPropsOf, TableColumn, TableRow } from './types';
3
3
  export type ColumnCellArgs<TType extends string = string> = {
4
- /** The full wire column (key, label, type, nested columns). */
5
4
  column: TableColumn;
6
- /** The column's `props`, resolved to the type's shape. */
7
5
  props: ColumnPropsOf<TType>;
8
6
  row: TableRow;
9
7
  value: unknown;
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","names":[],"sources":["../../resources/js/table/registry.ts"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport type { ColumnPropsOf, TableColumn, TableRow } from \"./types\";\n\nexport type ColumnCellArgs<TType extends string = string> = {\n /** The full wire column (key, label, type, nested columns). */\n column: TableColumn;\n /** The column's `props`, resolved to the type's shape. */\n props: ColumnPropsOf<TType>;\n row: TableRow;\n value: unknown;\n};\n\nexport type ColumnCellComponent<TType extends string = string> = (\n args: ColumnCellArgs<TType>,\n) => ReactNode;\n\nexport type ColumnRegistry = Record<string, ColumnCellComponent>;\n\n/**\n * Registers a typed column cell, erasing the type parameter so it fits the\n * registry. Mirrors `eagerComponent`/`lazyComponent` for the component registry:\n * author against `ColumnCellComponent<\"my.type\">` for typed `props`, register\n * through this.\n */\nexport function columnCell<TType extends string>(\n cell: ColumnCellComponent<TType>,\n): ColumnCellComponent {\n return cell as ColumnCellComponent;\n}\n"],"mappings":";;;;;;;AAwBA,SAAgB,WACd,MACqB;CACrB,OAAO;AACT"}
1
+ {"version":3,"file":"registry.js","names":[],"sources":["../../resources/js/table/registry.ts"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport type { ColumnPropsOf, TableColumn, TableRow } from \"./types\";\n\nexport type ColumnCellArgs<TType extends string = string> = {\n column: TableColumn;\n props: ColumnPropsOf<TType>;\n row: TableRow;\n value: unknown;\n};\n\nexport type ColumnCellComponent<TType extends string = string> = (\n args: ColumnCellArgs<TType>,\n) => ReactNode;\n\nexport type ColumnRegistry = Record<string, ColumnCellComponent>;\n\n/**\n * Registers a typed column cell, erasing the type parameter so it fits the\n * registry. Mirrors `eagerComponent`/`lazyComponent` for the component registry:\n * author against `ColumnCellComponent<\"my.type\">` for typed `props`, register\n * through this.\n */\nexport function columnCell<TType extends string>(\n cell: ColumnCellComponent<TType>,\n): ColumnCellComponent {\n return cell as ColumnCellComponent;\n}\n"],"mappings":";;;;;;;AAsBA,SAAgB,WACd,MACqB;CACrB,OAAO;AACT"}
@@ -1,6 +1,4 @@
1
1
  import { Callout } from '../types/generated';
2
2
  export type { Callout };
3
- /** Coerce a raw callout value (effect detail `{ callout: {...} }`) into a Callout. */
4
3
  export declare function normalizeCallout(detail: unknown): Callout | null;
5
- /** Subscribe to the `lattice:callout` bus. Returns an unsubscribe function. */
6
4
  export declare function onCallout(callback: (callout: Callout) => void): () => void;
@@ -1,7 +1,6 @@
1
1
  import { LATTICE_EVENT } from "../events/event-names.js";
2
2
  import { isVariant } from "./toast.js";
3
3
  //#region resources/js/toast/callout.ts
4
- /** Coerce a raw callout value (effect detail `{ callout: {...} }`) into a Callout. */
5
4
  function normalizeCallout(detail) {
6
5
  if (typeof detail !== "object" || detail === null) return null;
7
6
  const value = detail.callout;
@@ -16,7 +15,6 @@ function normalizeCallout(detail) {
16
15
  variant: isVariant(callout.variant) ? callout.variant : "info"
17
16
  };
18
17
  }
19
- /** Subscribe to the `lattice:callout` bus. Returns an unsubscribe function. */
20
18
  function onCallout(callback) {
21
19
  const listener = (event) => {
22
20
  const callout = normalizeCallout(event.detail);
@@ -1 +1 @@
1
- {"version":3,"file":"callout.js","names":[],"sources":["../../resources/js/toast/callout.ts"],"sourcesContent":["import type { Callout } from \"@lattice-php/lattice/types/generated\";\nimport { LATTICE_EVENT } from \"@lattice-php/lattice/events/event-names\";\nimport { isVariant } from \"@lattice-php/lattice/toast/toast\";\n\nexport type { Callout };\n\n/** Coerce a raw callout value (effect detail `{ callout: {...} }`) into a Callout. */\nexport function normalizeCallout(detail: unknown): Callout | null {\n if (typeof detail !== \"object\" || detail === null) {\n return null;\n }\n\n const value = (detail as { callout?: unknown }).callout;\n\n if (typeof value !== \"object\" || value === null) {\n return null;\n }\n\n const callout = value as Record<string, unknown>;\n\n if (typeof callout.message !== \"string\" || callout.message === \"\") {\n return null;\n }\n\n return {\n action: (callout.action as Callout[\"action\"]) ?? null,\n dismissible: callout.dismissible !== false,\n message: callout.message,\n title: typeof callout.title === \"string\" ? callout.title : null,\n variant: isVariant(callout.variant) ? callout.variant : \"info\",\n };\n}\n\n/** Subscribe to the `lattice:callout` bus. Returns an unsubscribe function. */\nexport function onCallout(callback: (callout: Callout) => void): () => void {\n const listener = (event: Event): void => {\n const callout = normalizeCallout((event as CustomEvent).detail);\n\n if (callout) {\n callback(callout);\n }\n };\n\n window.addEventListener(LATTICE_EVENT.callout, listener);\n\n return () => window.removeEventListener(LATTICE_EVENT.callout, listener);\n}\n"],"mappings":";;;;AAOA,SAAgB,iBAAiB,QAAiC;CAChE,IAAI,OAAO,WAAW,YAAY,WAAW,MAC3C,OAAO;CAGT,MAAM,QAAS,OAAiC;CAEhD,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC,OAAO;CAGT,MAAM,UAAU;CAEhB,IAAI,OAAO,QAAQ,YAAY,YAAY,QAAQ,YAAY,IAC7D,OAAO;CAGT,OAAO;EACL,QAAS,QAAQ,UAAgC;EACjD,aAAa,QAAQ,gBAAgB;EACrC,SAAS,QAAQ;EACjB,OAAO,OAAO,QAAQ,UAAU,WAAW,QAAQ,QAAQ;EAC3D,SAAS,UAAU,QAAQ,OAAO,IAAI,QAAQ,UAAU;CAC1D;AACF;;AAGA,SAAgB,UAAU,UAAkD;CAC1E,MAAM,YAAY,UAAuB;EACvC,MAAM,UAAU,iBAAkB,MAAsB,MAAM;EAE9D,IAAI,SACF,SAAS,OAAO;CAEpB;CAEA,OAAO,iBAAiB,cAAc,SAAS,QAAQ;CAEvD,aAAa,OAAO,oBAAoB,cAAc,SAAS,QAAQ;AACzE"}
1
+ {"version":3,"file":"callout.js","names":[],"sources":["../../resources/js/toast/callout.ts"],"sourcesContent":["import type { Callout } from \"@lattice-php/lattice/types/generated\";\nimport { LATTICE_EVENT } from \"@lattice-php/lattice/events/event-names\";\nimport { isVariant } from \"@lattice-php/lattice/toast/toast\";\n\nexport type { Callout };\n\nexport function normalizeCallout(detail: unknown): Callout | null {\n if (typeof detail !== \"object\" || detail === null) {\n return null;\n }\n\n const value = (detail as { callout?: unknown }).callout;\n\n if (typeof value !== \"object\" || value === null) {\n return null;\n }\n\n const callout = value as Record<string, unknown>;\n\n if (typeof callout.message !== \"string\" || callout.message === \"\") {\n return null;\n }\n\n return {\n action: (callout.action as Callout[\"action\"]) ?? null,\n dismissible: callout.dismissible !== false,\n message: callout.message,\n title: typeof callout.title === \"string\" ? callout.title : null,\n variant: isVariant(callout.variant) ? callout.variant : \"info\",\n };\n}\n\nexport function onCallout(callback: (callout: Callout) => void): () => void {\n const listener = (event: Event): void => {\n const callout = normalizeCallout((event as CustomEvent).detail);\n\n if (callout) {\n callback(callout);\n }\n };\n\n window.addEventListener(LATTICE_EVENT.callout, listener);\n\n return () => window.removeEventListener(LATTICE_EVENT.callout, listener);\n}\n"],"mappings":";;;AAMA,SAAgB,iBAAiB,QAAiC;CAChE,IAAI,OAAO,WAAW,YAAY,WAAW,MAC3C,OAAO;CAGT,MAAM,QAAS,OAAiC;CAEhD,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC,OAAO;CAGT,MAAM,UAAU;CAEhB,IAAI,OAAO,QAAQ,YAAY,YAAY,QAAQ,YAAY,IAC7D,OAAO;CAGT,OAAO;EACL,QAAS,QAAQ,UAAgC;EACjD,aAAa,QAAQ,gBAAgB;EACrC,SAAS,QAAQ;EACjB,OAAO,OAAO,QAAQ,UAAU,WAAW,QAAQ,QAAQ;EAC3D,SAAS,UAAU,QAAQ,OAAO,IAAI,QAAQ,UAAU;CAC1D;AACF;AAEA,SAAgB,UAAU,UAAkD;CAC1E,MAAM,YAAY,UAAuB;EACvC,MAAM,UAAU,iBAAkB,MAAsB,MAAM;EAE9D,IAAI,SACF,SAAS,OAAO;CAEpB;CAEA,OAAO,iBAAiB,cAAc,SAAS,QAAQ;CAEvD,aAAa,OAAO,oBAAoB,cAAc,SAAS,QAAQ;AACzE"}
@@ -6,9 +6,6 @@ export declare function isVariant(value: unknown): value is Variant;
6
6
  * ToastMessage, defaulting optional fields, or null when it is malformed.
7
7
  */
8
8
  export declare function normalizeToastMessage(value: unknown): ToastMessage | null;
9
- /** Coerce a `lattice:toast` event detail (`{ toast: {...} }`) into a ToastMessage. */
10
9
  export declare function normalizeToast(detail: unknown): ToastMessage | null;
11
- /** Emit a toast onto the shared `lattice:toast` bus. */
12
10
  export declare function showToast(toast: ToastMessage): void;
13
- /** Subscribe to the shared toast bus. Returns an unsubscribe function. */
14
11
  export declare function onToast(callback: (toast: ToastMessage) => void): () => void;
@@ -29,17 +29,14 @@ function normalizeToastMessage(value) {
29
29
  variant: isVariant(toast.variant) ? toast.variant : "success"
30
30
  };
31
31
  }
32
- /** Coerce a `lattice:toast` event detail (`{ toast: {...} }`) into a ToastMessage. */
33
32
  function normalizeToast(detail) {
34
33
  if (typeof detail !== "object" || detail === null) return null;
35
34
  return normalizeToastMessage(detail.toast);
36
35
  }
37
- /** Emit a toast onto the shared `lattice:toast` bus. */
38
36
  function showToast(toast) {
39
37
  if (typeof window === "undefined") return;
40
38
  window.dispatchEvent(new CustomEvent(LATTICE_EVENT.toast, { detail: { toast } }));
41
39
  }
42
- /** Subscribe to the shared toast bus. Returns an unsubscribe function. */
43
40
  function onToast(callback) {
44
41
  const listener = (event) => {
45
42
  const toast = normalizeToast(event.detail);
@@ -1 +1 @@
1
- {"version":3,"file":"toast.js","names":[],"sources":["../../resources/js/toast/toast.ts"],"sourcesContent":["import type { ToastMessage, Variant } from \"@lattice-php/lattice/types/generated\";\nimport { LATTICE_EVENT } from \"@lattice-php/lattice/events/event-names\";\nimport { isTranslatable } from \"@lattice-php/lattice/i18n/translatable\";\n\nexport type { ToastMessage, Variant };\n\nconst variants = [\"success\", \"info\", \"warning\", \"error\"] as const satisfies readonly Variant[];\n\nexport function isVariant(value: unknown): value is Variant {\n return variants.some((variant) => variant === value);\n}\n\n/**\n * Coerce a raw toast value (server flash payload or event detail) into a\n * ToastMessage, defaulting optional fields, or null when it is malformed.\n */\nexport function normalizeToastMessage(value: unknown): ToastMessage | null {\n if (typeof value !== \"object\" || value === null) {\n return null;\n }\n\n const toast = value as Record<string, unknown>;\n\n const rawMessage = toast.message;\n const message =\n typeof rawMessage === \"string\" ? rawMessage : isTranslatable(rawMessage) ? rawMessage : null;\n\n if (message === null || message === \"\") {\n return null;\n }\n\n return {\n action: (toast.action as ToastMessage[\"action\"]) ?? null,\n dismissible: toast.dismissible !== false,\n duration: typeof toast.duration === \"number\" ? toast.duration : null,\n message,\n persistent: toast.persistent === true,\n variant: isVariant(toast.variant) ? toast.variant : \"success\",\n };\n}\n\n/** Coerce a `lattice:toast` event detail (`{ toast: {...} }`) into a ToastMessage. */\nexport function normalizeToast(detail: unknown): ToastMessage | null {\n if (typeof detail !== \"object\" || detail === null) {\n return null;\n }\n\n return normalizeToastMessage((detail as { toast?: unknown }).toast);\n}\n\n/** Emit a toast onto the shared `lattice:toast` bus. */\nexport function showToast(toast: ToastMessage): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n window.dispatchEvent(new CustomEvent(LATTICE_EVENT.toast, { detail: { toast } }));\n}\n\n/** Subscribe to the shared toast bus. Returns an unsubscribe function. */\nexport function onToast(callback: (toast: ToastMessage) => void): () => void {\n const listener = (event: Event): void => {\n const toast = normalizeToast((event as CustomEvent).detail);\n\n if (toast) {\n callback(toast);\n }\n };\n\n window.addEventListener(LATTICE_EVENT.toast, listener);\n\n return () => window.removeEventListener(LATTICE_EVENT.toast, listener);\n}\n"],"mappings":";;;AAMA,IAAM,WAAW;CAAC;CAAW;CAAQ;CAAW;AAAO;AAEvD,SAAgB,UAAU,OAAkC;CAC1D,OAAO,SAAS,MAAM,YAAY,YAAY,KAAK;AACrD;;;;;AAMA,SAAgB,sBAAsB,OAAqC;CACzE,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC,OAAO;CAGT,MAAM,QAAQ;CAEd,MAAM,aAAa,MAAM;CACzB,MAAM,UACJ,OAAO,eAAe,WAAW,aAAa,eAAe,UAAU,IAAI,aAAa;CAE1F,IAAI,YAAY,QAAQ,YAAY,IAClC,OAAO;CAGT,OAAO;EACL,QAAS,MAAM,UAAqC;EACpD,aAAa,MAAM,gBAAgB;EACnC,UAAU,OAAO,MAAM,aAAa,WAAW,MAAM,WAAW;EAChE;EACA,YAAY,MAAM,eAAe;EACjC,SAAS,UAAU,MAAM,OAAO,IAAI,MAAM,UAAU;CACtD;AACF;;AAGA,SAAgB,eAAe,QAAsC;CACnE,IAAI,OAAO,WAAW,YAAY,WAAW,MAC3C,OAAO;CAGT,OAAO,sBAAuB,OAA+B,KAAK;AACpE;;AAGA,SAAgB,UAAU,OAA2B;CACnD,IAAI,OAAO,WAAW,aACpB;CAGF,OAAO,cAAc,IAAI,YAAY,cAAc,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;AAClF;;AAGA,SAAgB,QAAQ,UAAqD;CAC3E,MAAM,YAAY,UAAuB;EACvC,MAAM,QAAQ,eAAgB,MAAsB,MAAM;EAE1D,IAAI,OACF,SAAS,KAAK;CAElB;CAEA,OAAO,iBAAiB,cAAc,OAAO,QAAQ;CAErD,aAAa,OAAO,oBAAoB,cAAc,OAAO,QAAQ;AACvE"}
1
+ {"version":3,"file":"toast.js","names":[],"sources":["../../resources/js/toast/toast.ts"],"sourcesContent":["import type { ToastMessage, Variant } from \"@lattice-php/lattice/types/generated\";\nimport { LATTICE_EVENT } from \"@lattice-php/lattice/events/event-names\";\nimport { isTranslatable } from \"@lattice-php/lattice/i18n/translatable\";\n\nexport type { ToastMessage, Variant };\n\nconst variants = [\"success\", \"info\", \"warning\", \"error\"] as const satisfies readonly Variant[];\n\nexport function isVariant(value: unknown): value is Variant {\n return variants.some((variant) => variant === value);\n}\n\n/**\n * Coerce a raw toast value (server flash payload or event detail) into a\n * ToastMessage, defaulting optional fields, or null when it is malformed.\n */\nexport function normalizeToastMessage(value: unknown): ToastMessage | null {\n if (typeof value !== \"object\" || value === null) {\n return null;\n }\n\n const toast = value as Record<string, unknown>;\n\n const rawMessage = toast.message;\n const message =\n typeof rawMessage === \"string\" ? rawMessage : isTranslatable(rawMessage) ? rawMessage : null;\n\n if (message === null || message === \"\") {\n return null;\n }\n\n return {\n action: (toast.action as ToastMessage[\"action\"]) ?? null,\n dismissible: toast.dismissible !== false,\n duration: typeof toast.duration === \"number\" ? toast.duration : null,\n message,\n persistent: toast.persistent === true,\n variant: isVariant(toast.variant) ? toast.variant : \"success\",\n };\n}\n\nexport function normalizeToast(detail: unknown): ToastMessage | null {\n if (typeof detail !== \"object\" || detail === null) {\n return null;\n }\n\n return normalizeToastMessage((detail as { toast?: unknown }).toast);\n}\n\nexport function showToast(toast: ToastMessage): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n window.dispatchEvent(new CustomEvent(LATTICE_EVENT.toast, { detail: { toast } }));\n}\n\nexport function onToast(callback: (toast: ToastMessage) => void): () => void {\n const listener = (event: Event): void => {\n const toast = normalizeToast((event as CustomEvent).detail);\n\n if (toast) {\n callback(toast);\n }\n };\n\n window.addEventListener(LATTICE_EVENT.toast, listener);\n\n return () => window.removeEventListener(LATTICE_EVENT.toast, listener);\n}\n"],"mappings":";;;AAMA,IAAM,WAAW;CAAC;CAAW;CAAQ;CAAW;AAAO;AAEvD,SAAgB,UAAU,OAAkC;CAC1D,OAAO,SAAS,MAAM,YAAY,YAAY,KAAK;AACrD;;;;;AAMA,SAAgB,sBAAsB,OAAqC;CACzE,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC,OAAO;CAGT,MAAM,QAAQ;CAEd,MAAM,aAAa,MAAM;CACzB,MAAM,UACJ,OAAO,eAAe,WAAW,aAAa,eAAe,UAAU,IAAI,aAAa;CAE1F,IAAI,YAAY,QAAQ,YAAY,IAClC,OAAO;CAGT,OAAO;EACL,QAAS,MAAM,UAAqC;EACpD,aAAa,MAAM,gBAAgB;EACnC,UAAU,OAAO,MAAM,aAAa,WAAW,MAAM,WAAW;EAChE;EACA,YAAY,MAAM,eAAe;EACjC,SAAS,UAAU,MAAM,OAAO,IAAI,MAAM,UAAU;CACtD;AACF;AAEA,SAAgB,eAAe,QAAsC;CACnE,IAAI,OAAO,WAAW,YAAY,WAAW,MAC3C,OAAO;CAGT,OAAO,sBAAuB,OAA+B,KAAK;AACpE;AAEA,SAAgB,UAAU,OAA2B;CACnD,IAAI,OAAO,WAAW,aACpB;CAGF,OAAO,cAAc,IAAI,YAAY,cAAc,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;AAClF;AAEA,SAAgB,QAAQ,UAAqD;CAC3E,MAAM,YAAY,UAAuB;EACvC,MAAM,QAAQ,eAAgB,MAAsB,MAAM;EAE1D,IAAI,OACF,SAAS,KAAK;CAElB;CAEA,OAAO,iBAAiB,cAAc,OAAO,QAAQ;CAErD,aAAa,OAAO,oBAAoB,cAAc,OAAO,QAAQ;AACvE"}