@lattice-php/lattice 0.19.0 → 0.20.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 (236) hide show
  1. package/dist/core/index.js +2 -2
  2. package/dist/core/types.d.ts +16 -4
  3. package/dist/create-app.d.ts +31 -3
  4. package/dist/create-app.js +48 -10
  5. package/dist/create-app.js.map +1 -1
  6. package/dist/effects/registry.d.ts +3 -3
  7. package/dist/effects/registry.js +4 -4
  8. package/dist/effects/registry.js.map +1 -1
  9. package/dist/form/components/base/field.d.ts +2 -2
  10. package/dist/form/components/base/field.js +2 -2
  11. package/dist/form/components/base/field.js.map +1 -1
  12. package/dist/form/components/fields/color-picker-field.d.ts +2 -0
  13. package/dist/form/components/fields/color-picker-field.js +57 -0
  14. package/dist/form/components/fields/color-picker-field.js.map +1 -0
  15. package/dist/form/components/fields/date-input.js +1 -1
  16. package/dist/form/components/fields/date-picker-field.js +1 -1
  17. package/dist/form/components/fields/date-time-input.js +1 -1
  18. package/dist/form/components/fields/file-upload.js +7 -12
  19. package/dist/form/components/fields/file-upload.js.map +1 -1
  20. package/dist/form/components/fields/number-input.js +1 -1
  21. package/dist/form/components/fields/rich-editor-field.js +35 -276
  22. package/dist/form/components/fields/rich-editor-field.js.map +1 -1
  23. package/dist/form/components/fields/row-action-menu.js +2 -2
  24. package/dist/form/components/fields/row-action-menu.js.map +1 -1
  25. package/dist/form/components/fields/row-actions.js +3 -1
  26. package/dist/form/components/fields/row-actions.js.map +1 -1
  27. package/dist/form/components/fields/select.js +59 -16
  28. package/dist/form/components/fields/select.js.map +1 -1
  29. package/dist/form/components/fields/table-rows.js +2 -2
  30. package/dist/form/components/fields/table-rows.js.map +1 -1
  31. package/dist/form/components/fields/text-input.js +8 -1
  32. package/dist/form/components/fields/text-input.js.map +1 -1
  33. package/dist/form/components/fields/time-input.js +1 -1
  34. package/dist/form/components/index.d.ts +1 -0
  35. package/dist/form/components/index.js +2 -1
  36. package/dist/form/embed.d.ts +1 -0
  37. package/dist/form/embed.js +2 -1
  38. package/dist/form/hooks/use-form-resolver.js.map +1 -1
  39. package/dist/form/index.js +2 -1
  40. package/dist/form/lib/conditions.d.ts +4 -10
  41. package/dist/form/lib/conditions.js.map +1 -1
  42. package/dist/form/lib/field-props.d.ts +4 -18
  43. package/dist/form/lib/field-props.js.map +1 -1
  44. package/dist/form/plugin.js +2 -0
  45. package/dist/form/plugin.js.map +1 -1
  46. package/dist/form/rich-editor/builtins.d.ts +6 -0
  47. package/dist/form/rich-editor/builtins.js +400 -0
  48. package/dist/form/rich-editor/builtins.js.map +1 -0
  49. package/dist/form/rich-editor/index.d.ts +7 -0
  50. package/dist/form/rich-editor/index.js +3 -0
  51. package/dist/form/rich-editor/registry.d.ts +67 -0
  52. package/dist/form/rich-editor/registry.js +79 -0
  53. package/dist/form/rich-editor/registry.js.map +1 -0
  54. package/dist/form/rich-editor/toolbar-button.d.ts +12 -0
  55. package/dist/form/rich-editor/toolbar-button.js +28 -0
  56. package/dist/form/rich-editor/toolbar-button.js.map +1 -0
  57. package/dist/form/types.d.ts +1 -6
  58. package/dist/format/date-time.d.ts +3 -2
  59. package/dist/format/date-time.js.map +1 -1
  60. package/dist/i18n/backend.js +1 -1
  61. package/dist/i18n/date-time.d.ts +3 -2
  62. package/dist/i18n/date-time.js.map +1 -1
  63. package/dist/i18n/index.js +3 -2
  64. package/dist/i18n/instance.js +1 -1
  65. package/dist/i18n/page-props.d.ts +3 -2
  66. package/dist/i18n/page-props.js +1 -15
  67. package/dist/i18n/page-props.js.map +1 -1
  68. package/dist/i18n/shared-props.d.ts +7 -0
  69. package/dist/i18n/shared-props.js +25 -0
  70. package/dist/i18n/shared-props.js.map +1 -0
  71. package/dist/index.d.ts +5 -3
  72. package/dist/index.js +7 -6
  73. package/dist/lattice.css +107 -47
  74. package/dist/layout/components/breadcrumbs.js +3 -1
  75. package/dist/layout/components/breadcrumbs.js.map +1 -1
  76. package/dist/layout/components/callouts.js +1 -1
  77. package/dist/lib/color.d.ts +9 -0
  78. package/dist/lib/color.js +48 -0
  79. package/dist/lib/color.js.map +1 -0
  80. package/dist/notifications/api.d.ts +6 -6
  81. package/dist/notifications/api.js.map +1 -1
  82. package/dist/notifications/types.d.ts +2 -10
  83. package/dist/provider-base.js +1 -1
  84. package/dist/realtime/build-effects.js +5 -5
  85. package/dist/realtime/build-effects.js.map +1 -1
  86. package/dist/realtime/listeners.d.ts +2 -2
  87. package/dist/realtime/listeners.js.map +1 -1
  88. package/dist/realtime/subscriptions.d.ts +2 -2
  89. package/dist/realtime/subscriptions.js.map +1 -1
  90. package/dist/table/components/bulk-bar.js +5 -5
  91. package/dist/table/components/bulk-bar.js.map +1 -1
  92. package/dist/table/components/cells/badge-cell.js +4 -1
  93. package/dist/table/components/cells/badge-cell.js.map +1 -1
  94. package/dist/table/components/cells/boolean-cell.js +3 -1
  95. package/dist/table/components/cells/boolean-cell.js.map +1 -1
  96. package/dist/table/components/cells/icon-cell.js +5 -2
  97. package/dist/table/components/cells/icon-cell.js.map +1 -1
  98. package/dist/table/components/cells/image-cell.js +8 -9
  99. package/dist/table/components/cells/image-cell.js.map +1 -1
  100. package/dist/table/components/cells/money-cell.js +9 -1
  101. package/dist/table/components/cells/money-cell.js.map +1 -1
  102. package/dist/table/components/cells/number-cell.js +9 -1
  103. package/dist/table/components/cells/number-cell.js.map +1 -1
  104. package/dist/table/components/cells/text-cell.js +4 -1
  105. package/dist/table/components/cells/text-cell.js.map +1 -1
  106. package/dist/table/components/column-filter-control.js +25 -32
  107. package/dist/table/components/column-filter-control.js.map +1 -1
  108. package/dist/table/components/column-header.d.ts +3 -3
  109. package/dist/table/components/column-header.js +5 -3
  110. package/dist/table/components/column-header.js.map +1 -1
  111. package/dist/table/components/column-visibility-menu.js +6 -11
  112. package/dist/table/components/column-visibility-menu.js.map +1 -1
  113. package/dist/table/components/filter-bar.d.ts +2 -2
  114. package/dist/table/components/filter-bar.js +13 -22
  115. package/dist/table/components/filter-bar.js.map +1 -1
  116. package/dist/table/components/filter-controls.d.ts +2 -1
  117. package/dist/table/components/filter-controls.js +36 -35
  118. package/dist/table/components/filter-controls.js.map +1 -1
  119. package/dist/table/components/filter-value-input.d.ts +0 -1
  120. package/dist/table/components/filter-value-input.js +21 -20
  121. package/dist/table/components/filter-value-input.js.map +1 -1
  122. package/dist/table/components/pagination.js +16 -20
  123. package/dist/table/components/pagination.js.map +1 -1
  124. package/dist/table/components/sort-bar.d.ts +3 -3
  125. package/dist/table/components/sort-bar.js +11 -12
  126. package/dist/table/components/sort-bar.js.map +1 -1
  127. package/dist/table/components/table-search.d.ts +9 -0
  128. package/dist/table/components/table-search.js +75 -0
  129. package/dist/table/components/table-search.js.map +1 -0
  130. package/dist/table/components/table.js +72 -19
  131. package/dist/table/components/table.js.map +1 -1
  132. package/dist/table/hooks/use-expanded-rows.d.ts +8 -0
  133. package/dist/table/hooks/use-expanded-rows.js +24 -0
  134. package/dist/table/hooks/use-expanded-rows.js.map +1 -0
  135. package/dist/table/hooks/use-table.d.ts +5 -3
  136. package/dist/table/hooks/use-table.js +56 -44
  137. package/dist/table/hooks/use-table.js.map +1 -1
  138. package/dist/table/lib/payload.d.ts +3 -2
  139. package/dist/table/lib/payload.js +16 -10
  140. package/dist/table/lib/payload.js.map +1 -1
  141. package/dist/table/lib/query.d.ts +5 -5
  142. package/dist/table/lib/query.js +20 -18
  143. package/dist/table/lib/query.js.map +1 -1
  144. package/dist/table/types.d.ts +3 -26
  145. package/dist/toast/callout.js +1 -3
  146. package/dist/toast/callout.js.map +1 -1
  147. package/dist/toast/toast.d.ts +1 -5
  148. package/dist/toast/toast.js +2 -7
  149. package/dist/toast/toast.js.map +1 -1
  150. package/dist/toast/toaster.js +2 -2
  151. package/dist/toast/toaster.js.map +1 -1
  152. package/dist/types/generated.d.ts +263 -188
  153. package/dist/ui/affix-group.d.ts +2 -1
  154. package/dist/ui/affix-group.js +9 -7
  155. package/dist/ui/affix-group.js.map +1 -1
  156. package/dist/ui/avatar.d.ts +3 -0
  157. package/dist/ui/avatar.js +52 -0
  158. package/dist/ui/avatar.js.map +1 -0
  159. package/dist/ui/button.d.ts +3 -1
  160. package/dist/ui/button.js +10 -3
  161. package/dist/ui/button.js.map +1 -1
  162. package/dist/ui/chart-view.js +161 -11
  163. package/dist/ui/chart-view.js.map +1 -1
  164. package/dist/ui/click-behavior.d.ts +1 -1
  165. package/dist/ui/click-behavior.js.map +1 -1
  166. package/dist/ui/color-picker.d.ts +8 -0
  167. package/dist/ui/color-picker.js +59 -0
  168. package/dist/ui/color-picker.js.map +1 -0
  169. package/dist/ui/combobox.d.ts +11 -2
  170. package/dist/ui/combobox.js +51 -8
  171. package/dist/ui/combobox.js.map +1 -1
  172. package/dist/ui/copyable-text.d.ts +7 -0
  173. package/dist/ui/copyable-text.js +21 -13
  174. package/dist/ui/copyable-text.js.map +1 -1
  175. package/dist/ui/fragment.js.map +1 -1
  176. package/dist/ui/heading.js +39 -21
  177. package/dist/ui/heading.js.map +1 -1
  178. package/dist/ui/icon-button.d.ts +22 -0
  179. package/dist/ui/icon-button.js +51 -0
  180. package/dist/ui/icon-button.js.map +1 -0
  181. package/dist/ui/icon.js +10 -11
  182. package/dist/ui/icon.js.map +1 -1
  183. package/dist/ui/image-preview.d.ts +12 -0
  184. package/dist/ui/image-preview.js +65 -0
  185. package/dist/ui/image-preview.js.map +1 -0
  186. package/dist/ui/image.d.ts +3 -0
  187. package/dist/ui/image.js +21 -0
  188. package/dist/ui/image.js.map +1 -0
  189. package/dist/ui/index.d.ts +2 -1
  190. package/dist/ui/index.js +4 -3
  191. package/dist/ui/info-tooltip.js +1 -1
  192. package/dist/ui/info-tooltip.js.map +1 -1
  193. package/dist/ui/input.d.ts +3 -1
  194. package/dist/ui/input.js +2 -2
  195. package/dist/ui/input.js.map +1 -1
  196. package/dist/ui/modal.js +14 -2
  197. package/dist/ui/modal.js.map +1 -1
  198. package/dist/ui/native-select.d.ts +10 -0
  199. package/dist/ui/native-select.js +23 -0
  200. package/dist/ui/native-select.js.map +1 -0
  201. package/dist/ui/plugin.js +11 -1
  202. package/dist/ui/plugin.js.map +1 -1
  203. package/dist/ui/progress.d.ts +3 -0
  204. package/dist/ui/progress.js +114 -0
  205. package/dist/ui/progress.js.map +1 -0
  206. package/dist/ui/separator.d.ts +3 -0
  207. package/dist/ui/separator.js +15 -0
  208. package/dist/ui/separator.js.map +1 -0
  209. package/dist/ui/text.js +5 -10
  210. package/dist/ui/text.js.map +1 -1
  211. package/dist/ui/tree-context.d.ts +33 -0
  212. package/dist/ui/tree-context.js +137 -0
  213. package/dist/ui/tree-context.js.map +1 -0
  214. package/dist/ui/tree.d.ts +19 -0
  215. package/dist/ui/tree.js +187 -0
  216. package/dist/ui/tree.js.map +1 -0
  217. package/dist-standalone/chunks/chart-view-wJmnVu7L.js +36 -0
  218. package/dist-standalone/chunks/date-picker-field-VbH9xrQO.js +1 -0
  219. package/dist-standalone/chunks/jsx-runtime-Drll_-DD.js +1 -0
  220. package/dist-standalone/chunks/locale-CXqr7LPR.js +1 -0
  221. package/dist-standalone/chunks/page-props-AIa5UR6n.js +1 -0
  222. package/dist-standalone/chunks/rich-editor-field-DLQTyt36.js +154 -0
  223. package/dist-standalone/chunks/{subscriptions-B3UuDBmx.js → subscriptions-DY2tj9Im.js} +1 -1
  224. package/dist-standalone/chunks/use-effect-dispatcher-feEs0GCP.js +97 -0
  225. package/dist-standalone/lattice.css +1 -1
  226. package/dist-standalone/lattice.js +15 -15
  227. package/dist-standalone/manifest.json +11 -9
  228. package/dist-standalone/sprite.svg +1 -1
  229. package/package.json +6 -1
  230. package/resources/icons/code-xml.svg +17 -0
  231. package/resources/icons/heading.svg +17 -0
  232. package/dist-standalone/chunks/chart-view-CK5fKIkR.js +0 -36
  233. package/dist-standalone/chunks/date-picker-field-BlCPFhZd.js +0 -1
  234. package/dist-standalone/chunks/jsx-runtime-CJ_btYLc.js +0 -1
  235. package/dist-standalone/chunks/rich-editor-field-DH5dS5c1.js +0 -154
  236. package/dist-standalone/chunks/use-effect-dispatcher-k98Nzf9M.js +0 -97
@@ -10,8 +10,9 @@ import { Affix } from '../types/generated.js';
10
10
  * affix segments. The control itself stays the focus target; only its own ring
11
11
  * is suppressed.
12
12
  */
13
- export declare function AffixGroup({ prefix, suffix, children, }: {
13
+ export declare function AffixGroup({ prefix, suffix, end, children, }: {
14
14
  prefix?: Affix | null;
15
15
  suffix?: Affix | null;
16
+ end?: ReactNode;
16
17
  children: (controlClassName: string) => ReactNode;
17
18
  }): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import('react').ReactPortal | import('react').ReactElement<unknown, string | import('react').JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react").JSX.Element | null | undefined;
@@ -2,10 +2,10 @@ import { cn } from "../lib/utils.js";
2
2
  import { IconRenderer } from "../icons/icon-renderer.js";
3
3
  import { jsx, jsxs } from "react/jsx-runtime";
4
4
  //#region resources/js/ui/affix-group.tsx
5
- function AffixSegment({ affix, side }) {
5
+ function AffixSegment({ affix, side, squared = false }) {
6
6
  return /* @__PURE__ */ jsx("span", {
7
7
  "data-slot": `affix-${side}`,
8
- className: cn("inline-flex h-lt-control-md shrink-0 items-center border border-lt-input bg-lt-muted px-3 text-base whitespace-nowrap text-lt-muted-fg", "group-has-[:focus-visible]:border-lt-ring", side === "start" ? "rounded-l-lt-sm border-r-0" : "rounded-r-lt-sm border-l-0"),
8
+ className: cn("inline-flex h-lt-control-md shrink-0 items-center border border-lt-input bg-lt-muted px-3 text-base whitespace-nowrap text-lt-muted-fg", "group-has-[:focus-visible]:border-lt-ring", side === "start" ? "rounded-l-lt-sm border-r-0" : "rounded-r-lt-sm border-l-0", squared && "rounded-r-none border-r-0"),
9
9
  children: affix.icon ? /* @__PURE__ */ jsx(IconRenderer, {
10
10
  className: "size-lt-icon-md",
11
11
  icon: affix.icon
@@ -22,8 +22,8 @@ function AffixSegment({ affix, side }) {
22
22
  * affix segments. The control itself stays the focus target; only its own ring
23
23
  * is suppressed.
24
24
  */
25
- function AffixGroup({ prefix, suffix, children }) {
26
- if (!prefix && !suffix) return children("");
25
+ function AffixGroup({ prefix, suffix, end, children }) {
26
+ if (!prefix && !suffix && !end) return children("");
27
27
  return /* @__PURE__ */ jsxs("div", {
28
28
  className: "group flex w-full rounded-lt-sm transition-[color,box-shadow] has-[:focus-visible]:ring-[3px] has-[:focus-visible]:ring-lt-ring/50",
29
29
  "data-slot": "affix-group",
@@ -34,12 +34,14 @@ function AffixGroup({ prefix, suffix, children }) {
34
34
  }) : null,
35
35
  /* @__PURE__ */ jsx("div", {
36
36
  className: "min-w-0 flex-1",
37
- children: children(cn("focus-visible:ring-0", prefix && "rounded-l-none", suffix && "rounded-r-none"))
37
+ children: children(cn("focus-visible:ring-0", prefix && "rounded-l-none", (suffix || end) && "rounded-r-none"))
38
38
  }),
39
39
  suffix ? /* @__PURE__ */ jsx(AffixSegment, {
40
40
  affix: suffix,
41
- side: "end"
42
- }) : null
41
+ side: "end",
42
+ squared: Boolean(end)
43
+ }) : null,
44
+ end
43
45
  ]
44
46
  });
45
47
  }
@@ -1 +1 @@
1
- {"version":3,"file":"affix-group.js","names":[],"sources":["../../resources/js/ui/affix-group.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport type { Affix } from \"@lattice-php/lattice/types/generated\";\nimport { IconRenderer } from \"@lattice-php/lattice/icons\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\n\nfunction AffixSegment({ affix, side }: { affix: Affix; side: \"start\" | \"end\" }) {\n return (\n <span\n data-slot={`affix-${side}`}\n className={cn(\n \"inline-flex h-lt-control-md shrink-0 items-center border border-lt-input bg-lt-muted px-3 text-base whitespace-nowrap text-lt-muted-fg\",\n \"group-has-[:focus-visible]:border-lt-ring\",\n side === \"start\" ? \"rounded-l-lt-sm border-r-0\" : \"rounded-r-lt-sm border-l-0\",\n )}\n >\n {affix.icon ? <IconRenderer className=\"size-lt-icon-md\" icon={affix.icon} /> : affix.text}\n </span>\n );\n}\n\n/**\n * Wraps a single-line control in a prefix/suffix input group. The control is a\n * render prop receiving the class names that square the corners adjacent to an\n * affix; with no affixes the control renders untouched.\n *\n * The focus ring lives on the group, not the bare input, so it surrounds the\n * whole control — affixes included — instead of being clipped by the opaque\n * affix segments. The control itself stays the focus target; only its own ring\n * is suppressed.\n */\nexport function AffixGroup({\n prefix,\n suffix,\n children,\n}: {\n prefix?: Affix | null;\n suffix?: Affix | null;\n children: (controlClassName: string) => ReactNode;\n}) {\n if (!prefix && !suffix) {\n return children(\"\");\n }\n\n return (\n <div\n className=\"group flex w-full rounded-lt-sm transition-[color,box-shadow] has-[:focus-visible]:ring-[3px] has-[:focus-visible]:ring-lt-ring/50\"\n data-slot=\"affix-group\"\n >\n {prefix ? <AffixSegment affix={prefix} side=\"start\" /> : null}\n <div className=\"min-w-0 flex-1\">\n {children(\n cn(\"focus-visible:ring-0\", prefix && \"rounded-l-none\", suffix && \"rounded-r-none\"),\n )}\n </div>\n {suffix ? <AffixSegment affix={suffix} side=\"end\" /> : null}\n </div>\n );\n}\n"],"mappings":";;;;AAKA,SAAS,aAAa,EAAE,OAAO,QAAiD;CAC9E,OACE,oBAAC,QAAD;EACE,aAAW,SAAS;EACpB,WAAW,GACT,0IACA,6CACA,SAAS,UAAU,+BAA+B,4BACpD;YAEC,MAAM,OAAO,oBAAC,cAAD;GAAc,WAAU;GAAkB,MAAM,MAAM;EAAO,CAAA,IAAI,MAAM;CACjF,CAAA;AAEV;;;;;;;;;;;AAYA,SAAgB,WAAW,EACzB,QACA,QACA,YAKC;CACD,IAAI,CAAC,UAAU,CAAC,QACd,OAAO,SAAS,EAAE;CAGpB,OACE,qBAAC,OAAD;EACE,WAAU;EACV,aAAU;YAFZ;GAIG,SAAS,oBAAC,cAAD;IAAc,OAAO;IAAQ,MAAK;GAAS,CAAA,IAAI;GACzD,oBAAC,OAAD;IAAK,WAAU;cACZ,SACC,GAAG,wBAAwB,UAAU,kBAAkB,UAAU,gBAAgB,CACnF;GACG,CAAA;GACJ,SAAS,oBAAC,cAAD;IAAc,OAAO;IAAQ,MAAK;GAAO,CAAA,IAAI;EACpD;;AAET"}
1
+ {"version":3,"file":"affix-group.js","names":[],"sources":["../../resources/js/ui/affix-group.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport type { Affix } from \"@lattice-php/lattice/types/generated\";\nimport { IconRenderer } from \"@lattice-php/lattice/icons\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\n\nfunction AffixSegment({\n affix,\n side,\n squared = false,\n}: {\n affix: Affix;\n side: \"start\" | \"end\";\n squared?: boolean;\n}) {\n return (\n <span\n data-slot={`affix-${side}`}\n className={cn(\n \"inline-flex h-lt-control-md shrink-0 items-center border border-lt-input bg-lt-muted px-3 text-base whitespace-nowrap text-lt-muted-fg\",\n \"group-has-[:focus-visible]:border-lt-ring\",\n side === \"start\" ? \"rounded-l-lt-sm border-r-0\" : \"rounded-r-lt-sm border-l-0\",\n squared && \"rounded-r-none border-r-0\",\n )}\n >\n {affix.icon ? <IconRenderer className=\"size-lt-icon-md\" icon={affix.icon} /> : affix.text}\n </span>\n );\n}\n\n/**\n * Wraps a single-line control in a prefix/suffix input group. The control is a\n * render prop receiving the class names that square the corners adjacent to an\n * affix; with no affixes the control renders untouched.\n *\n * The focus ring lives on the group, not the bare input, so it surrounds the\n * whole control — affixes included — instead of being clipped by the opaque\n * affix segments. The control itself stays the focus target; only its own ring\n * is suppressed.\n */\nexport function AffixGroup({\n prefix,\n suffix,\n end,\n children,\n}: {\n prefix?: Affix | null;\n suffix?: Affix | null;\n end?: ReactNode;\n children: (controlClassName: string) => ReactNode;\n}) {\n if (!prefix && !suffix && !end) {\n return children(\"\");\n }\n\n return (\n <div\n className=\"group flex w-full rounded-lt-sm transition-[color,box-shadow] has-[:focus-visible]:ring-[3px] has-[:focus-visible]:ring-lt-ring/50\"\n data-slot=\"affix-group\"\n >\n {prefix ? <AffixSegment affix={prefix} side=\"start\" /> : null}\n <div className=\"min-w-0 flex-1\">\n {children(\n cn(\n \"focus-visible:ring-0\",\n prefix && \"rounded-l-none\",\n (suffix || end) && \"rounded-r-none\",\n ),\n )}\n </div>\n {suffix ? <AffixSegment affix={suffix} side=\"end\" squared={Boolean(end)} /> : null}\n {end}\n </div>\n );\n}\n"],"mappings":";;;;AAKA,SAAS,aAAa,EACpB,OACA,MACA,UAAU,SAKT;CACD,OACE,oBAAC,QAAD;EACE,aAAW,SAAS;EACpB,WAAW,GACT,0IACA,6CACA,SAAS,UAAU,+BAA+B,8BAClD,WAAW,2BACb;YAEC,MAAM,OAAO,oBAAC,cAAD;GAAc,WAAU;GAAkB,MAAM,MAAM;EAAO,CAAA,IAAI,MAAM;CACjF,CAAA;AAEV;;;;;;;;;;;AAYA,SAAgB,WAAW,EACzB,QACA,QACA,KACA,YAMC;CACD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,KACzB,OAAO,SAAS,EAAE;CAGpB,OACE,qBAAC,OAAD;EACE,WAAU;EACV,aAAU;YAFZ;GAIG,SAAS,oBAAC,cAAD;IAAc,OAAO;IAAQ,MAAK;GAAS,CAAA,IAAI;GACzD,oBAAC,OAAD;IAAK,WAAU;cACZ,SACC,GACE,wBACA,UAAU,mBACT,UAAU,QAAQ,gBACrB,CACF;GACG,CAAA;GACJ,SAAS,oBAAC,cAAD;IAAc,OAAO;IAAQ,MAAK;IAAM,SAAS,QAAQ,GAAG;GAAI,CAAA,IAAI;GAC7E;EACE;;AAET"}
@@ -0,0 +1,3 @@
1
+ import { RendererComponent } from '../core/types.js';
2
+ declare const AvatarComponent: RendererComponent<"avatar">;
3
+ export default AvatarComponent;
@@ -0,0 +1,52 @@
1
+ import { cn } from "../lib/utils.js";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ //#region resources/js/ui/avatar.tsx
4
+ var sizeClass = {
5
+ xs: "size-6 text-[0.5rem]",
6
+ sm: "size-8 text-xs",
7
+ md: "size-10 text-sm",
8
+ lg: "size-12 text-base",
9
+ xl: "size-16 text-lg",
10
+ "2xl": "size-20 text-xl",
11
+ "3xl": "size-24 text-2xl",
12
+ "4xl": "size-32 text-3xl"
13
+ };
14
+ function initials(name) {
15
+ return name.trim().split(/\s+/).slice(0, 2).map((word) => word.charAt(0).toUpperCase()).join("");
16
+ }
17
+ function UserGlyph() {
18
+ return /* @__PURE__ */ jsxs("svg", {
19
+ viewBox: "0 0 24 24",
20
+ fill: "none",
21
+ stroke: "currentColor",
22
+ strokeWidth: 2,
23
+ strokeLinecap: "round",
24
+ strokeLinejoin: "round",
25
+ "aria-hidden": "true",
26
+ className: "size-[60%]",
27
+ children: [/* @__PURE__ */ jsx("path", { d: "M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" }), /* @__PURE__ */ jsx("circle", {
28
+ cx: "12",
29
+ cy: "7",
30
+ r: "4"
31
+ })]
32
+ });
33
+ }
34
+ var AvatarComponent = ({ node }) => {
35
+ const { src, name, size } = node.props;
36
+ const label = name ?? void 0;
37
+ return /* @__PURE__ */ jsx("span", {
38
+ "data-slot": "avatar",
39
+ role: src ? void 0 : "img",
40
+ "aria-label": src ? void 0 : label,
41
+ className: cn("inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-lt-muted font-medium text-lt-muted-fg select-none", sizeClass[size]),
42
+ children: src ? /* @__PURE__ */ jsx("img", {
43
+ src,
44
+ alt: label ?? "",
45
+ className: "size-full object-cover"
46
+ }) : name ? initials(name) : /* @__PURE__ */ jsx(UserGlyph, {})
47
+ });
48
+ };
49
+ //#endregion
50
+ export { AvatarComponent as default };
51
+
52
+ //# sourceMappingURL=avatar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"avatar.js","names":[],"sources":["../../resources/js/ui/avatar.tsx"],"sourcesContent":["import type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport type { Size } from \"@lattice-php/lattice/types/generated\";\n\nconst sizeClass: Record<Size, string> = {\n xs: \"size-6 text-[0.5rem]\",\n sm: \"size-8 text-xs\",\n md: \"size-10 text-sm\",\n lg: \"size-12 text-base\",\n xl: \"size-16 text-lg\",\n \"2xl\": \"size-20 text-xl\",\n \"3xl\": \"size-24 text-2xl\",\n \"4xl\": \"size-32 text-3xl\",\n};\n\nfunction initials(name: string): string {\n return name\n .trim()\n .split(/\\s+/)\n .slice(0, 2)\n .map((word) => word.charAt(0).toUpperCase())\n .join(\"\");\n}\n\nfunction UserGlyph() {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n className=\"size-[60%]\"\n >\n <path d=\"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2\" />\n <circle cx=\"12\" cy=\"7\" r=\"4\" />\n </svg>\n );\n}\n\nconst AvatarComponent: RendererComponent<\"avatar\"> = ({ node }) => {\n const { src, name, size } = node.props;\n const label = name ?? undefined;\n\n return (\n <span\n data-slot=\"avatar\"\n role={src ? undefined : \"img\"}\n aria-label={src ? undefined : label}\n className={cn(\n \"inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-lt-muted font-medium text-lt-muted-fg select-none\",\n sizeClass[size],\n )}\n >\n {src ? (\n <img src={src} alt={label ?? \"\"} className=\"size-full object-cover\" />\n ) : name ? (\n initials(name)\n ) : (\n <UserGlyph />\n )}\n </span>\n );\n};\n\nexport default AvatarComponent;\n"],"mappings":";;;AAIA,IAAM,YAAkC;CACtC,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,OAAO;CACP,OAAO;CACP,OAAO;AACT;AAEA,SAAS,SAAS,MAAsB;CACtC,OAAO,KACJ,KAAK,EACL,MAAM,KAAK,EACX,MAAM,GAAG,CAAC,EACV,KAAK,SAAS,KAAK,OAAO,CAAC,EAAE,YAAY,CAAC,EAC1C,KAAK,EAAE;AACZ;AAEA,SAAS,YAAY;CACnB,OACE,qBAAC,OAAD;EACE,SAAQ;EACR,MAAK;EACL,QAAO;EACP,aAAa;EACb,eAAc;EACd,gBAAe;EACf,eAAY;EACZ,WAAU;YARZ,CAUE,oBAAC,QAAD,EAAM,GAAE,4CAA6C,CAAA,GACrD,oBAAC,UAAD;GAAQ,IAAG;GAAK,IAAG;GAAI,GAAE;EAAK,CAAA,CAC3B;;AAET;AAEA,IAAM,mBAAgD,EAAE,WAAW;CACjE,MAAM,EAAE,KAAK,MAAM,SAAS,KAAK;CACjC,MAAM,QAAQ,QAAQ,KAAA;CAEtB,OACE,oBAAC,QAAD;EACE,aAAU;EACV,MAAM,MAAM,KAAA,IAAY;EACxB,cAAY,MAAM,KAAA,IAAY;EAC9B,WAAW,GACT,sIACA,UAAU,KACZ;YAEC,MACC,oBAAC,OAAD;GAAU;GAAK,KAAK,SAAS;GAAI,WAAU;EAA0B,CAAA,IACnE,OACF,SAAS,IAAI,IAEb,oBAAC,WAAD,CAAY,CAAA;CAEV,CAAA;AAEV"}
@@ -7,8 +7,10 @@ declare const buttonVariants: (props?: ({
7
7
  variant?: "default" | "destructive" | "ghost" | "info" | "link" | "outline" | "secondary" | "success" | null | undefined;
8
8
  size?: "default" | "sm" | "lg" | "icon" | null | undefined;
9
9
  } & import('class-variance-authority/types').ClassProp) | undefined) => string;
10
- declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
10
+ declare function Button({ className, variant, size, asChild, icon, children, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
11
11
  asChild?: boolean;
12
+ /** Leading icon glyph. Ignored with `asChild` (Slot needs a single child). */
13
+ icon?: string;
12
14
  }): React.JSX.Element;
13
15
  declare const ButtonComponent: RendererComponent<"button">;
14
16
  export default ButtonComponent;
package/dist/ui/button.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { cn } from "../lib/utils.js";
2
+ import { Icon } from "../icons/sprite.js";
2
3
  import { IconRenderer } from "../icons/icon-renderer.js";
3
4
  import { nodeIdentity } from "../core/test-id.js";
4
5
  import { ActionTrigger, useClickBehavior } from "./click-behavior.js";
@@ -32,15 +33,21 @@ var buttonVariants = cva("inline-flex items-center justify-center gap-2 whitespa
32
33
  size: "default"
33
34
  }
34
35
  });
35
- function Button({ className, variant, size, asChild = false, ...props }) {
36
- return /* @__PURE__ */ jsx(asChild ? Slot : "button", {
36
+ function Button({ className, variant, size, asChild = false, icon, children, ...props }) {
37
+ const Comp = asChild ? Slot : "button";
38
+ const content = icon && !asChild ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Icon, {
39
+ name: icon,
40
+ "aria-hidden": "true"
41
+ }), children] }) : children;
42
+ return /* @__PURE__ */ jsx(Comp, {
37
43
  "data-slot": "button",
38
44
  className: cn(buttonVariants({
39
45
  variant,
40
46
  size,
41
47
  className
42
48
  })),
43
- ...props
49
+ ...props,
50
+ children: content
44
51
  });
45
52
  }
46
53
  var ButtonComponent = ({ node }) => {
@@ -1 +1 @@
1
- {"version":3,"file":"button.js","names":[],"sources":["../../resources/js/ui/button.tsx"],"sourcesContent":["import { Link } from \"@inertiajs/react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { IconRenderer } from \"@lattice-php/lattice/icons\";\nimport { nodeIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { ActionTrigger, type TriggerState, useClickBehavior } from \"./click-behavior\";\nimport type { ButtonVariant } from \"@lattice-php/lattice/types/generated\";\n\nexport type { ButtonVariant };\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lt-sm text-base font-medium transition-[color,box-shadow] disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-lt-icon-md [&_svg]:shrink-0 outline-none focus-visible:border-lt-ring focus-visible:ring-lt-ring/50 focus-visible:ring-[3px] aria-invalid:ring-lt-danger/20 dark:aria-invalid:ring-lt-danger/40 aria-invalid:border-lt-danger\",\n {\n variants: {\n variant: {\n default:\n \"bg-lt-primary text-lt-primary-fg shadow-lt-xs hover:bg-lt-primary-hover active:bg-lt-primary-active disabled:bg-lt-disabled disabled:text-lt-disabled-fg disabled:shadow-none\",\n destructive:\n \"bg-lt-danger text-lt-danger-fg shadow-lt-xs hover:bg-lt-danger-hover active:bg-lt-danger-active focus-visible:ring-lt-danger/20 dark:focus-visible:ring-lt-danger/40 disabled:bg-lt-disabled disabled:text-lt-disabled-fg disabled:shadow-none\",\n success:\n \"bg-lt-success text-lt-success-fg shadow-lt-xs hover:bg-lt-success-hover active:bg-lt-success-active disabled:bg-lt-disabled disabled:text-lt-disabled-fg disabled:shadow-none\",\n info: \"bg-lt-info text-lt-info-fg shadow-lt-xs hover:bg-lt-info-hover active:bg-lt-info-active disabled:bg-lt-disabled disabled:text-lt-disabled-fg disabled:shadow-none\",\n outline:\n \"border border-lt-input bg-lt-bg shadow-lt-xs hover:bg-lt-accent hover:text-lt-accent-fg disabled:bg-lt-disabled disabled:text-lt-disabled-fg disabled:border-transparent disabled:shadow-none\",\n secondary:\n \"bg-lt-secondary text-lt-secondary-fg shadow-lt-xs hover:bg-lt-secondary-hover active:bg-lt-secondary-active disabled:bg-lt-disabled disabled:text-lt-disabled-fg disabled:shadow-none\",\n ghost: \"hover:bg-lt-accent hover:text-lt-accent-fg disabled:text-lt-disabled-fg\",\n link: \"text-lt-primary underline-offset-4 hover:underline disabled:text-lt-disabled-fg disabled:no-underline\",\n },\n size: {\n default: \"h-lt-control-md px-4 py-2 has-[>svg]:px-3\",\n sm: \"h-lt-control-sm rounded-lt-sm px-3 has-[>svg]:px-2.5\",\n lg: \"h-lt-control-lg rounded-lt-sm px-6 has-[>svg]:px-4\",\n icon: \"size-lt-control-md\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nfunction Button({\n className,\n variant,\n size,\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean;\n }) {\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n data-slot=\"button\"\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\n\nconst ButtonComponent: RendererComponent<\"button\"> = ({ node }) => {\n const { label, icon } = node.props;\n const variant = node.props.variant ?? \"default\";\n const testId = nodeIdentity(node);\n const behavior = useClickBehavior(node.props);\n const size = icon ? \"icon\" : \"default\";\n const content = icon ? (\n <>\n <IconRenderer className=\"size-lt-icon-md\" icon={icon} />\n {label ? <span className=\"sr-only\">{label}</span> : null}\n </>\n ) : (\n label\n );\n\n const triggerButton = ({ onClick, processing }: TriggerState) => (\n <Button\n data-test={testId}\n disabled={processing}\n onClick={onClick}\n size={size}\n variant={variant}\n >\n {content}\n </Button>\n );\n\n if (behavior.kind === \"navigate\") {\n return (\n <Button asChild data-test={testId} variant={variant} size={size}>\n <Link href={behavior.href} method={behavior.method}>\n {content}\n </Link>\n </Button>\n );\n }\n\n if (behavior.kind === \"action\") {\n return <ActionTrigger action={behavior.action}>{triggerButton}</ActionTrigger>;\n }\n\n if (behavior.kind === \"effects\") {\n return triggerButton({ onClick: behavior.onClick, processing: false });\n }\n\n return (\n <Button data-test={testId} size={size} type={node.props.buttonType} variant={variant}>\n {content}\n </Button>\n );\n};\n\nexport default ButtonComponent;\nexport { Button, buttonVariants };\n"],"mappings":";;;;;;;;;;AAaA,IAAM,iBAAiB,IACrB,+bACA;CACE,UAAU;EACR,SAAS;GACP,SACE;GACF,aACE;GACF,SACE;GACF,MAAM;GACN,SACE;GACF,WACE;GACF,OAAO;GACP,MAAM;EACR;EACA,MAAM;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,MAAM;EACR;CACF;CACA,iBAAiB;EACf,SAAS;EACT,MAAM;CACR;AACF,CACF;AAEA,SAAS,OAAO,EACd,WACA,SACA,MACA,UAAU,OACV,GAAG,SAIA;CAGH,OACE,oBAHW,UAAU,OAAO,UAG5B;EACE,aAAU;EACV,WAAW,GAAG,eAAe;GAAE;GAAS;GAAM;EAAU,CAAC,CAAC;EAC1D,GAAI;CACL,CAAA;AAEL;AAEA,IAAM,mBAAgD,EAAE,WAAW;CACjE,MAAM,EAAE,OAAO,SAAS,KAAK;CAC7B,MAAM,UAAU,KAAK,MAAM,WAAW;CACtC,MAAM,SAAS,aAAa,IAAI;CAChC,MAAM,WAAW,iBAAiB,KAAK,KAAK;CAC5C,MAAM,OAAO,OAAO,SAAS;CAC7B,MAAM,UAAU,OACd,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,cAAD;EAAc,WAAU;EAAwB;CAAO,CAAA,GACtD,QAAQ,oBAAC,QAAD;EAAM,WAAU;YAAW;CAAY,CAAA,IAAI,IACpD,EAAA,CAAA,IAEF;CAGF,MAAM,iBAAiB,EAAE,SAAS,iBAChC,oBAAC,QAAD;EACE,aAAW;EACX,UAAU;EACD;EACH;EACG;YAER;CACK,CAAA;CAGV,IAAI,SAAS,SAAS,YACpB,OACE,oBAAC,QAAD;EAAQ,SAAA;EAAQ,aAAW;EAAiB;EAAe;YACzD,oBAAC,MAAD;GAAM,MAAM,SAAS;GAAM,QAAQ,SAAS;aACzC;EACG,CAAA;CACA,CAAA;CAIZ,IAAI,SAAS,SAAS,UACpB,OAAO,oBAAC,eAAD;EAAe,QAAQ,SAAS;YAAS;CAA6B,CAAA;CAG/E,IAAI,SAAS,SAAS,WACpB,OAAO,cAAc;EAAE,SAAS,SAAS;EAAS,YAAY;CAAM,CAAC;CAGvE,OACE,oBAAC,QAAD;EAAQ,aAAW;EAAc;EAAM,MAAM,KAAK,MAAM;EAAqB;YAC1E;CACK,CAAA;AAEZ"}
1
+ {"version":3,"file":"button.js","names":[],"sources":["../../resources/js/ui/button.tsx"],"sourcesContent":["import { Link } from \"@inertiajs/react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { Icon, IconRenderer } from \"@lattice-php/lattice/icons\";\nimport { nodeIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { ActionTrigger, type TriggerState, useClickBehavior } from \"./click-behavior\";\nimport type { ButtonVariant } from \"@lattice-php/lattice/types/generated\";\n\nexport type { ButtonVariant };\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lt-sm text-base font-medium transition-[color,box-shadow] disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-lt-icon-md [&_svg]:shrink-0 outline-none focus-visible:border-lt-ring focus-visible:ring-lt-ring/50 focus-visible:ring-[3px] aria-invalid:ring-lt-danger/20 dark:aria-invalid:ring-lt-danger/40 aria-invalid:border-lt-danger\",\n {\n variants: {\n variant: {\n default:\n \"bg-lt-primary text-lt-primary-fg shadow-lt-xs hover:bg-lt-primary-hover active:bg-lt-primary-active disabled:bg-lt-disabled disabled:text-lt-disabled-fg disabled:shadow-none\",\n destructive:\n \"bg-lt-danger text-lt-danger-fg shadow-lt-xs hover:bg-lt-danger-hover active:bg-lt-danger-active focus-visible:ring-lt-danger/20 dark:focus-visible:ring-lt-danger/40 disabled:bg-lt-disabled disabled:text-lt-disabled-fg disabled:shadow-none\",\n success:\n \"bg-lt-success text-lt-success-fg shadow-lt-xs hover:bg-lt-success-hover active:bg-lt-success-active disabled:bg-lt-disabled disabled:text-lt-disabled-fg disabled:shadow-none\",\n info: \"bg-lt-info text-lt-info-fg shadow-lt-xs hover:bg-lt-info-hover active:bg-lt-info-active disabled:bg-lt-disabled disabled:text-lt-disabled-fg disabled:shadow-none\",\n outline:\n \"border border-lt-input bg-lt-bg shadow-lt-xs hover:bg-lt-accent hover:text-lt-accent-fg disabled:bg-lt-disabled disabled:text-lt-disabled-fg disabled:border-transparent disabled:shadow-none\",\n secondary:\n \"bg-lt-secondary text-lt-secondary-fg shadow-lt-xs hover:bg-lt-secondary-hover active:bg-lt-secondary-active disabled:bg-lt-disabled disabled:text-lt-disabled-fg disabled:shadow-none\",\n ghost: \"hover:bg-lt-accent hover:text-lt-accent-fg disabled:text-lt-disabled-fg\",\n link: \"text-lt-primary underline-offset-4 hover:underline disabled:text-lt-disabled-fg disabled:no-underline\",\n },\n size: {\n default: \"h-lt-control-md px-4 py-2 has-[>svg]:px-3\",\n sm: \"h-lt-control-sm rounded-lt-sm px-3 has-[>svg]:px-2.5\",\n lg: \"h-lt-control-lg rounded-lt-sm px-6 has-[>svg]:px-4\",\n icon: \"size-lt-control-md\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nfunction Button({\n className,\n variant,\n size,\n asChild = false,\n icon,\n children,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean;\n /** Leading icon glyph. Ignored with `asChild` (Slot needs a single child). */\n icon?: string;\n }) {\n const Comp = asChild ? Slot : \"button\";\n const content =\n icon && !asChild ? (\n <>\n <Icon name={icon} aria-hidden=\"true\" />\n {children}\n </>\n ) : (\n children\n );\n\n return (\n <Comp\n data-slot=\"button\"\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n >\n {content}\n </Comp>\n );\n}\n\nconst ButtonComponent: RendererComponent<\"button\"> = ({ node }) => {\n const { label, icon } = node.props;\n const variant = node.props.variant ?? \"default\";\n const testId = nodeIdentity(node);\n const behavior = useClickBehavior(node.props);\n const size = icon ? \"icon\" : \"default\";\n const content = icon ? (\n <>\n <IconRenderer className=\"size-lt-icon-md\" icon={icon} />\n {label ? <span className=\"sr-only\">{label}</span> : null}\n </>\n ) : (\n label\n );\n\n const triggerButton = ({ onClick, processing }: TriggerState) => (\n <Button\n data-test={testId}\n disabled={processing}\n onClick={onClick}\n size={size}\n variant={variant}\n >\n {content}\n </Button>\n );\n\n if (behavior.kind === \"navigate\") {\n return (\n <Button asChild data-test={testId} variant={variant} size={size}>\n <Link href={behavior.href} method={behavior.method}>\n {content}\n </Link>\n </Button>\n );\n }\n\n if (behavior.kind === \"action\") {\n return <ActionTrigger action={behavior.action}>{triggerButton}</ActionTrigger>;\n }\n\n if (behavior.kind === \"effects\") {\n return triggerButton({ onClick: behavior.onClick, processing: false });\n }\n\n return (\n <Button data-test={testId} size={size} type={node.props.buttonType} variant={variant}>\n {content}\n </Button>\n );\n};\n\nexport default ButtonComponent;\nexport { Button, buttonVariants };\n"],"mappings":";;;;;;;;;;;AAaA,IAAM,iBAAiB,IACrB,+bACA;CACE,UAAU;EACR,SAAS;GACP,SACE;GACF,aACE;GACF,SACE;GACF,MAAM;GACN,SACE;GACF,WACE;GACF,OAAO;GACP,MAAM;EACR;EACA,MAAM;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,MAAM;EACR;CACF;CACA,iBAAiB;EACf,SAAS;EACT,MAAM;CACR;AACF,CACF;AAEA,SAAS,OAAO,EACd,WACA,SACA,MACA,UAAU,OACV,MACA,UACA,GAAG,SAMA;CACH,MAAM,OAAO,UAAU,OAAO;CAC9B,MAAM,UACJ,QAAQ,CAAC,UACP,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,MAAD;EAAM,MAAM;EAAM,eAAY;CAAQ,CAAA,GACrC,QACD,EAAA,CAAA,IAEF;CAGJ,OACE,oBAAC,MAAD;EACE,aAAU;EACV,WAAW,GAAG,eAAe;GAAE;GAAS;GAAM;EAAU,CAAC,CAAC;EAC1D,GAAI;YAEH;CACG,CAAA;AAEV;AAEA,IAAM,mBAAgD,EAAE,WAAW;CACjE,MAAM,EAAE,OAAO,SAAS,KAAK;CAC7B,MAAM,UAAU,KAAK,MAAM,WAAW;CACtC,MAAM,SAAS,aAAa,IAAI;CAChC,MAAM,WAAW,iBAAiB,KAAK,KAAK;CAC5C,MAAM,OAAO,OAAO,SAAS;CAC7B,MAAM,UAAU,OACd,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,cAAD;EAAc,WAAU;EAAwB;CAAO,CAAA,GACtD,QAAQ,oBAAC,QAAD;EAAM,WAAU;YAAW;CAAY,CAAA,IAAI,IACpD,EAAA,CAAA,IAEF;CAGF,MAAM,iBAAiB,EAAE,SAAS,iBAChC,oBAAC,QAAD;EACE,aAAW;EACX,UAAU;EACD;EACH;EACG;YAER;CACK,CAAA;CAGV,IAAI,SAAS,SAAS,YACpB,OACE,oBAAC,QAAD;EAAQ,SAAA;EAAQ,aAAW;EAAiB;EAAe;YACzD,oBAAC,MAAD;GAAM,MAAM,SAAS;GAAM,QAAQ,SAAS;aACzC;EACG,CAAA;CACA,CAAA;CAIZ,IAAI,SAAS,SAAS,UACpB,OAAO,oBAAC,eAAD;EAAe,QAAQ,SAAS;YAAS;CAA6B,CAAA;CAG/E,IAAI,SAAS,SAAS,WACpB,OAAO,cAAc;EAAE,SAAS,SAAS;EAAS,YAAY;CAAM,CAAC;CAGvE,OACE,oBAAC,QAAD;EAAQ,aAAW;EAAc;EAAM,MAAM,KAAK,MAAM;EAAqB;YAC1E;CACK,CAAA;AAEZ"}
@@ -1,9 +1,11 @@
1
1
  import { useLocale } from "../i18n/locale.js";
2
2
  import { useTimezone } from "../i18n/timezone.js";
3
3
  import { nodeIdentity } from "../core/test-id.js";
4
+ import { coerceColor, colorValue } from "../lib/color.js";
5
+ import { numericValue } from "../format/numeric.js";
4
6
  import { formatValue } from "../format/value.js";
5
7
  import { jsx, jsxs } from "react/jsx-runtime";
6
- import { Area, AreaChart, Bar, BarChart, CartesianGrid, Cell, ComposedChart, Legend, Line, LineChart, Pie, PieChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
8
+ import { Area, AreaChart, Bar, BarChart, CartesianGrid, Cell, ComposedChart, Legend, Line, LineChart, Pie, PieChart, PolarAngleAxis, RadialBar, RadialBarChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
7
9
  //#region resources/js/ui/chart-view.tsx
8
10
  var chartMargin = {
9
11
  bottom: 0,
@@ -50,12 +52,26 @@ function isRecord(value) {
50
52
  return typeof value === "object" && value !== null && !Array.isArray(value);
51
53
  }
52
54
  function datumColor(datum, series, index) {
53
- if (isRecord(datum) && typeof datum.color === "string") return datum.color;
54
- return series.color ?? colorAt(index);
55
+ if (isRecord(datum)) {
56
+ const color = coerceColor(datum.color);
57
+ if (color) return colorValue(color);
58
+ }
59
+ return series.color ? colorValue(series.color) : colorAt(index);
55
60
  }
56
61
  function isCartesianSeries(series) {
57
62
  return series.type === "area" || series.type === "bar" || series.type === "line";
58
63
  }
64
+ function isSpecialSeries(series) {
65
+ return !isCartesianSeries(series);
66
+ }
67
+ function datumValue(datum, key) {
68
+ return isRecord(datum) ? numericValue(datum[key]) ?? 0 : 0;
69
+ }
70
+ function datumName(datum, series) {
71
+ if (series.nameKey === null || !isRecord(datum)) return series.name;
72
+ const name = datum[series.nameKey];
73
+ return typeof name === "string" ? name : series.name;
74
+ }
59
75
  function cartesianChartFor(series) {
60
76
  const types = new Set(series.map((item) => item.type));
61
77
  const firstSeries = series[0];
@@ -134,13 +150,13 @@ function CartesianChart({ props }) {
134
150
  }),
135
151
  props.legend && /* @__PURE__ */ jsx(Legend, { ...compactLegendProps }),
136
152
  series.map((item, index) => {
137
- const color = item.color ?? colorAt(index);
153
+ const color = item.color ? colorValue(item.color) : colorAt(index);
138
154
  const key = `${item.type}:${item.dataKey}`;
139
155
  if (item.type === "area") return /* @__PURE__ */ jsx(Area, {
140
156
  dataKey: item.dataKey,
141
157
  fill: color,
142
158
  fillOpacity: .16,
143
- name: item.name ?? void 0,
159
+ name: item.name,
144
160
  stackId: item.stackId ?? void 0,
145
161
  stroke: color,
146
162
  type: "monotone"
@@ -148,7 +164,7 @@ function CartesianChart({ props }) {
148
164
  if (item.type === "bar") return /* @__PURE__ */ jsx(Bar, {
149
165
  dataKey: item.dataKey,
150
166
  fill: color,
151
- name: item.name ?? void 0,
167
+ name: item.name,
152
168
  radius: [
153
169
  4,
154
170
  4,
@@ -160,7 +176,7 @@ function CartesianChart({ props }) {
160
176
  return /* @__PURE__ */ jsx(Line, {
161
177
  dataKey: item.dataKey,
162
178
  dot: false,
163
- name: item.name ?? void 0,
179
+ name: item.name,
164
180
  stroke: color,
165
181
  strokeWidth: 2,
166
182
  type: "monotone"
@@ -193,8 +209,9 @@ function PieChartView({ props, series }) {
193
209
  /* @__PURE__ */ jsx(Pie, {
194
210
  data: props.data,
195
211
  dataKey: series.dataKey,
196
- name: series.name ?? void 0,
212
+ name: series.name,
197
213
  nameKey: series.nameKey ?? void 0,
214
+ innerRadius: series.innerRadius,
198
215
  outerRadius: "68%",
199
216
  children: props.data.map((datum, index) => /* @__PURE__ */ jsx(Cell, { fill: datumColor(datum, series, index) }, index))
200
217
  })
@@ -202,9 +219,142 @@ function PieChartView({ props, series }) {
202
219
  })
203
220
  });
204
221
  }
222
+ function ChartLegend({ entries }) {
223
+ return /* @__PURE__ */ jsx("div", {
224
+ className: "flex flex-wrap items-center justify-center gap-x-3 gap-y-1",
225
+ children: entries.map((entry, index) => /* @__PURE__ */ jsxs("span", {
226
+ className: "flex items-center gap-1 text-[11px] leading-[14px] text-lt-muted-fg",
227
+ children: [
228
+ /* @__PURE__ */ jsx("span", {
229
+ "aria-hidden": true,
230
+ className: "size-[7px] rounded-full",
231
+ style: { background: entry.color }
232
+ }),
233
+ entry.label,
234
+ entry.value !== void 0 && /* @__PURE__ */ jsx("span", {
235
+ className: "font-medium text-lt-surface-fg",
236
+ children: entry.value
237
+ })
238
+ ]
239
+ }, index))
240
+ });
241
+ }
242
+ function GaugeChartView({ props, series }) {
243
+ const { locale } = useLocale();
244
+ const { timezone } = useTimezone();
245
+ const ctx = {
246
+ locale,
247
+ timezone
248
+ };
249
+ const values = props.data.map((datum) => datumValue(datum, series.dataKey));
250
+ const max = series.maxValue ?? Math.max(0, ...values);
251
+ const legendEntries = props.data.map((datum, index) => ({
252
+ color: datumColor(datum, series, index),
253
+ label: datumName(datum, series)
254
+ }));
255
+ return /* @__PURE__ */ jsxs("div", {
256
+ className: "flex flex-col gap-1.5",
257
+ children: [props.legend && /* @__PURE__ */ jsx(ChartLegend, { entries: legendEntries }), /* @__PURE__ */ jsxs("div", {
258
+ className: "relative",
259
+ children: [/* @__PURE__ */ jsx(ResponsiveContainer, {
260
+ width: "100%",
261
+ height: props.height,
262
+ debounce: 100,
263
+ children: /* @__PURE__ */ jsxs(RadialBarChart, {
264
+ data: props.data,
265
+ endAngle: -30,
266
+ innerRadius: series.innerRadius,
267
+ margin: chartMargin,
268
+ outerRadius: "100%",
269
+ startAngle: 210,
270
+ children: [
271
+ /* @__PURE__ */ jsx(PolarAngleAxis, {
272
+ type: "number",
273
+ domain: [0, max],
274
+ tick: false
275
+ }),
276
+ props.tooltip && /* @__PURE__ */ jsx(Tooltip, {
277
+ ...tooltipProps,
278
+ formatter: (value) => formatValue(value, props.valueFormat, ctx)
279
+ }),
280
+ /* @__PURE__ */ jsx(RadialBar, {
281
+ background: { fill: "var(--lt-muted)" },
282
+ cornerRadius: 6,
283
+ dataKey: series.dataKey,
284
+ name: series.name,
285
+ children: props.data.map((datum, index) => /* @__PURE__ */ jsx(Cell, { fill: datumColor(datum, series, index) }, index))
286
+ })
287
+ ]
288
+ })
289
+ }), values.length === 1 && /* @__PURE__ */ jsx("div", {
290
+ className: "pointer-events-none absolute inset-0 flex items-center justify-center",
291
+ children: /* @__PURE__ */ jsx("span", {
292
+ className: "text-2xl font-semibold text-lt-surface-fg",
293
+ children: formatValue(values[0], props.valueFormat, ctx)
294
+ })
295
+ })]
296
+ })]
297
+ });
298
+ }
299
+ function DistributionChartView({ props, series }) {
300
+ const { locale } = useLocale();
301
+ const { timezone } = useTimezone();
302
+ const ctx = {
303
+ locale,
304
+ timezone
305
+ };
306
+ const segments = props.data.map((datum, index) => ({
307
+ color: datumColor(datum, series, index),
308
+ label: datumName(datum, series),
309
+ value: datumValue(datum, series.dataKey)
310
+ })).filter((segment) => segment.value > 0);
311
+ const total = segments.reduce((sum, segment) => sum + segment.value, 0);
312
+ if (total <= 0) return /* @__PURE__ */ jsx("div", {
313
+ className: "h-2.5 w-full rounded-full bg-lt-muted",
314
+ "data-lattice-distribution": ""
315
+ });
316
+ const formatPercent = new Intl.NumberFormat(locale, {
317
+ maximumFractionDigits: 1,
318
+ style: "percent"
319
+ });
320
+ return /* @__PURE__ */ jsxs("div", {
321
+ className: "flex flex-col gap-1.5",
322
+ children: [/* @__PURE__ */ jsx("div", {
323
+ className: "flex h-2.5 w-full gap-0.5 overflow-hidden rounded-full",
324
+ "data-lattice-distribution": "",
325
+ children: segments.map((segment, index) => /* @__PURE__ */ jsx("div", {
326
+ style: {
327
+ background: segment.color,
328
+ width: `${segment.value / total * 100}%`
329
+ },
330
+ title: props.tooltip ? `${segment.label}: ${formatValue(segment.value, props.valueFormat, ctx)}` : void 0
331
+ }, index))
332
+ }), props.legend && /* @__PURE__ */ jsx(ChartLegend, { entries: segments.map((segment) => ({
333
+ color: segment.color,
334
+ label: segment.label,
335
+ value: formatPercent.format(segment.value / total)
336
+ })) })]
337
+ });
338
+ }
339
+ function SpecialChart({ props, series }) {
340
+ switch (series.type) {
341
+ case "distribution": return /* @__PURE__ */ jsx(DistributionChartView, {
342
+ props,
343
+ series
344
+ });
345
+ case "gauge": return /* @__PURE__ */ jsx(GaugeChartView, {
346
+ props,
347
+ series
348
+ });
349
+ case "pie": return /* @__PURE__ */ jsx(PieChartView, {
350
+ props,
351
+ series
352
+ });
353
+ }
354
+ }
205
355
  var ChartView = ({ node }) => {
206
356
  const props = node.props;
207
- const pieSeries = props.series.find((series) => series.type === "pie");
357
+ const specialSeries = props.series.find(isSpecialSeries);
208
358
  const hasCartesianSeries = props.series.some(isCartesianSeries);
209
359
  return /* @__PURE__ */ jsx(ChartFrame, {
210
360
  description: props.description,
@@ -212,9 +362,9 @@ var ChartView = ({ node }) => {
212
362
  title: props.title,
213
363
  children: /* @__PURE__ */ jsx("div", {
214
364
  className: "min-h-0 w-full",
215
- children: pieSeries && !hasCartesianSeries ? /* @__PURE__ */ jsx(PieChartView, {
365
+ children: specialSeries !== void 0 && !hasCartesianSeries ? /* @__PURE__ */ jsx(SpecialChart, {
216
366
  props,
217
- series: pieSeries
367
+ series: specialSeries
218
368
  }) : /* @__PURE__ */ jsx(CartesianChart, { props })
219
369
  })
220
370
  });
@@ -1 +1 @@
1
- {"version":3,"file":"chart-view.js","names":[],"sources":["../../resources/js/ui/chart-view.tsx"],"sourcesContent":["import {\n Area,\n AreaChart,\n Bar,\n BarChart,\n CartesianGrid,\n Cell,\n ComposedChart,\n Legend,\n Line,\n LineChart,\n Pie,\n PieChart,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from \"recharts\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { nodeIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport type { PropsOf, RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { useLocale, useTimezone } from \"@lattice-php/lattice/i18n\";\nimport { formatValue } from \"@lattice-php/lattice/format/value\";\n\ntype ChartProps = PropsOf<\"chart\">;\ntype ChartSeries = ChartProps[\"series\"][number];\ntype ChartDatum = ChartProps[\"data\"][number];\ntype CartesianSeries = ChartSeries & { type: \"area\" | \"bar\" | \"line\" };\ntype ChartMargin = { bottom: number; left: number; right: number; top: number };\ntype CartesianChartComponent = ComponentType<{\n children: ReactNode;\n data: ChartProps[\"data\"];\n margin?: ChartMargin;\n}>;\n\nconst chartMargin: ChartMargin = { bottom: 0, left: 0, right: 16, top: 8 };\nconst compactLegendProps = {\n align: \"center\" as const,\n height: 24,\n iconSize: 7,\n verticalAlign: \"top\" as const,\n wrapperStyle: { fontSize: 11, lineHeight: \"14px\", paddingBottom: 6 },\n};\nconst axisTick = { fontSize: 10 };\nconst tooltipProps = {\n contentStyle: {\n background: \"var(--lt-surface)\",\n border: \"1px solid var(--lt-border)\",\n borderRadius: \"var(--lt-radius-sm)\",\n boxShadow: \"var(--lt-shadow-sm)\",\n color: \"var(--lt-surface-fg)\",\n fontSize: 12,\n },\n itemStyle: { color: \"var(--lt-surface-fg)\" },\n labelStyle: { color: \"var(--lt-muted-fg)\" },\n} as const;\n\nconst palette = [\n \"var(--lt-primary)\",\n \"var(--lt-success)\",\n \"var(--lt-info)\",\n \"var(--lt-warning)\",\n \"var(--lt-danger)\",\n \"var(--lt-muted-fg)\",\n];\n\nfunction colorAt(index: number): string {\n return palette[index % palette.length] ?? \"var(--lt-primary)\";\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction datumColor(datum: ChartDatum, series: ChartSeries, index: number): string {\n if (isRecord(datum) && typeof datum.color === \"string\") {\n return datum.color;\n }\n\n return series.color ?? colorAt(index);\n}\n\nfunction isCartesianSeries(series: ChartSeries): series is CartesianSeries {\n return series.type === \"area\" || series.type === \"bar\" || series.type === \"line\";\n}\n\nfunction cartesianChartFor(series: CartesianSeries[]): CartesianChartComponent {\n const types = new Set(series.map((item) => item.type));\n const firstSeries = series[0];\n\n if (firstSeries === undefined || types.size !== 1) {\n return ComposedChart;\n }\n\n switch (firstSeries.type) {\n case \"area\":\n return AreaChart;\n case \"bar\":\n return BarChart;\n case \"line\":\n return LineChart;\n }\n}\n\nfunction ChartFrame({\n children,\n description,\n id,\n title,\n}: {\n children: ReactNode;\n description: string | null;\n id?: string;\n title: string | null;\n}) {\n const hasHeader = title !== null || description !== null;\n\n return (\n <div\n className=\"flex flex-col gap-3 rounded-lt border border-lt-border bg-lt-surface p-4 text-lt-surface-fg shadow-lt-sm\"\n data-lattice-component={id}\n >\n {hasHeader && (\n <div className=\"flex min-w-0 flex-col gap-1.5\">\n {title !== null && <div className=\"text-sm font-semibold leading-tight\">{title}</div>}\n {description !== null && (\n <div className=\"text-xs leading-5 text-lt-muted-fg\">{description}</div>\n )}\n </div>\n )}\n {children}\n </div>\n );\n}\n\nfunction CartesianChart({ props }: { props: ChartProps }) {\n const series = props.series.filter(isCartesianSeries);\n const RechartsChart = cartesianChartFor(series);\n const { locale } = useLocale();\n const { timezone } = useTimezone();\n const ctx = { locale, timezone };\n const formatCategory = (value: unknown) => formatValue(value, props.categoryFormat, ctx);\n const formatValueTick = (value: unknown) => formatValue(value, props.valueFormat, ctx);\n const hasBarSeries = series.some((item) => item.type === \"bar\");\n const tooltipCursor = hasBarSeries\n ? { fill: \"var(--lt-muted-fg)\", fillOpacity: 0.15 }\n : { stroke: \"var(--lt-muted-fg)\", strokeOpacity: 0.4 };\n\n return (\n <ResponsiveContainer width=\"100%\" height={props.height} debounce={100}>\n <RechartsChart data={props.data} margin={chartMargin}>\n {props.grid && <CartesianGrid strokeDasharray=\"3 3\" stroke=\"var(--lt-border)\" />}\n {props.xAxis && props.categoryKey !== null && (\n <XAxis\n dataKey={props.categoryKey}\n stroke=\"var(--lt-muted-fg)\"\n tick={axisTick}\n tickFormatter={formatCategory}\n tickLine={false}\n />\n )}\n {props.yAxis && (\n <YAxis\n stroke=\"var(--lt-muted-fg)\"\n tick={axisTick}\n tickFormatter={formatValueTick}\n tickLine={false}\n width={42}\n />\n )}\n {props.tooltip && (\n <Tooltip\n {...tooltipProps}\n cursor={tooltipCursor}\n formatter={formatValueTick}\n labelFormatter={formatCategory}\n />\n )}\n {props.legend && <Legend {...compactLegendProps} />}\n {series.map((item, index) => {\n const color = item.color ?? colorAt(index);\n const key = `${item.type}:${item.dataKey}`;\n\n if (item.type === \"area\") {\n return (\n <Area\n key={key}\n dataKey={item.dataKey}\n fill={color}\n fillOpacity={0.16}\n name={item.name ?? undefined}\n stackId={item.stackId ?? undefined}\n stroke={color}\n type=\"monotone\"\n />\n );\n }\n\n if (item.type === \"bar\") {\n return (\n <Bar\n key={key}\n dataKey={item.dataKey}\n fill={color}\n name={item.name ?? undefined}\n radius={[4, 4, 0, 0]}\n stackId={item.stackId ?? undefined}\n />\n );\n }\n\n return (\n <Line\n key={key}\n dataKey={item.dataKey}\n dot={false}\n name={item.name ?? undefined}\n stroke={color}\n strokeWidth={2}\n type=\"monotone\"\n />\n );\n })}\n </RechartsChart>\n </ResponsiveContainer>\n );\n}\n\nfunction PieChartView({ props, series }: { props: ChartProps; series: ChartSeries }) {\n const { locale } = useLocale();\n const { timezone } = useTimezone();\n const ctx = { locale, timezone };\n\n return (\n <ResponsiveContainer width=\"100%\" height={props.height} debounce={100}>\n <PieChart margin={chartMargin}>\n {props.tooltip && (\n <Tooltip\n {...tooltipProps}\n formatter={(value) => formatValue(value, props.valueFormat, ctx)}\n labelFormatter={(value) => formatValue(value, props.categoryFormat, ctx)}\n />\n )}\n {props.legend && <Legend {...compactLegendProps} />}\n <Pie\n data={props.data}\n dataKey={series.dataKey}\n name={series.name ?? undefined}\n nameKey={series.nameKey ?? undefined}\n outerRadius=\"68%\"\n >\n {props.data.map((datum, index) => (\n <Cell key={index} fill={datumColor(datum, series, index)} />\n ))}\n </Pie>\n </PieChart>\n </ResponsiveContainer>\n );\n}\n\nconst ChartView: RendererComponent<\"chart\"> = ({ node }) => {\n const props = node.props;\n const pieSeries = props.series.find((series) => series.type === \"pie\");\n const hasCartesianSeries = props.series.some(isCartesianSeries);\n\n return (\n <ChartFrame description={props.description} id={nodeIdentity(node)} title={props.title}>\n <div className=\"min-h-0 w-full\">\n {pieSeries && !hasCartesianSeries ? (\n <PieChartView props={props} series={pieSeries} />\n ) : (\n <CartesianChart props={props} />\n )}\n </div>\n </ChartFrame>\n );\n};\n\nexport default ChartView;\n"],"mappings":"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"}
1
+ {"version":3,"file":"chart-view.js","names":[],"sources":["../../resources/js/ui/chart-view.tsx"],"sourcesContent":["import {\n Area,\n AreaChart,\n Bar,\n BarChart,\n CartesianGrid,\n Cell,\n ComposedChart,\n Legend,\n Line,\n LineChart,\n Pie,\n PieChart,\n PolarAngleAxis,\n RadialBar,\n RadialBarChart,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from \"recharts\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { nodeIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport type { PropsOf, RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { coerceColor, colorValue } from \"@lattice-php/lattice/lib/color\";\nimport { useLocale, useTimezone } from \"@lattice-php/lattice/i18n\";\nimport { numericValue } from \"@lattice-php/lattice/format/numeric\";\nimport { formatValue } from \"@lattice-php/lattice/format/value\";\n\ntype ChartProps = PropsOf<\"chart\">;\ntype ChartSeries = ChartProps[\"series\"][number];\ntype ChartDatum = ChartProps[\"data\"][number];\ntype CartesianSeries = ChartSeries & { type: \"area\" | \"bar\" | \"line\" };\ntype SpecialSeries = ChartSeries & { type: \"distribution\" | \"gauge\" | \"pie\" };\ntype ChartMargin = { bottom: number; left: number; right: number; top: number };\ntype CartesianChartComponent = ComponentType<{\n children: ReactNode;\n data: ChartProps[\"data\"];\n margin?: ChartMargin;\n}>;\n\nconst chartMargin: ChartMargin = { bottom: 0, left: 0, right: 16, top: 8 };\nconst compactLegendProps = {\n align: \"center\" as const,\n height: 24,\n iconSize: 7,\n verticalAlign: \"top\" as const,\n wrapperStyle: { fontSize: 11, lineHeight: \"14px\", paddingBottom: 6 },\n};\nconst axisTick = { fontSize: 10 };\nconst tooltipProps = {\n contentStyle: {\n background: \"var(--lt-surface)\",\n border: \"1px solid var(--lt-border)\",\n borderRadius: \"var(--lt-radius-sm)\",\n boxShadow: \"var(--lt-shadow-sm)\",\n color: \"var(--lt-surface-fg)\",\n fontSize: 12,\n },\n itemStyle: { color: \"var(--lt-surface-fg)\" },\n labelStyle: { color: \"var(--lt-muted-fg)\" },\n} as const;\n\nconst palette = [\n \"var(--lt-primary)\",\n \"var(--lt-success)\",\n \"var(--lt-info)\",\n \"var(--lt-warning)\",\n \"var(--lt-danger)\",\n \"var(--lt-muted-fg)\",\n];\n\nfunction colorAt(index: number): string {\n return palette[index % palette.length] ?? \"var(--lt-primary)\";\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction datumColor(datum: ChartDatum, series: ChartSeries, index: number): string {\n if (isRecord(datum)) {\n const color = coerceColor(datum.color);\n\n if (color) {\n return colorValue(color);\n }\n }\n\n return series.color ? colorValue(series.color) : colorAt(index);\n}\n\nfunction isCartesianSeries(series: ChartSeries): series is CartesianSeries {\n return series.type === \"area\" || series.type === \"bar\" || series.type === \"line\";\n}\n\nfunction isSpecialSeries(series: ChartSeries): series is SpecialSeries {\n return !isCartesianSeries(series);\n}\n\nfunction datumValue(datum: ChartDatum, key: string): number {\n return isRecord(datum) ? (numericValue(datum[key]) ?? 0) : 0;\n}\n\nfunction datumName(datum: ChartDatum, series: ChartSeries): string {\n if (series.nameKey === null || !isRecord(datum)) {\n return series.name;\n }\n\n const name = datum[series.nameKey];\n\n return typeof name === \"string\" ? name : series.name;\n}\n\nfunction cartesianChartFor(series: CartesianSeries[]): CartesianChartComponent {\n const types = new Set(series.map((item) => item.type));\n const firstSeries = series[0];\n\n if (firstSeries === undefined || types.size !== 1) {\n return ComposedChart;\n }\n\n switch (firstSeries.type) {\n case \"area\":\n return AreaChart;\n case \"bar\":\n return BarChart;\n case \"line\":\n return LineChart;\n }\n}\n\nfunction ChartFrame({\n children,\n description,\n id,\n title,\n}: {\n children: ReactNode;\n description: string | null;\n id?: string;\n title: string | null;\n}) {\n const hasHeader = title !== null || description !== null;\n\n return (\n <div\n className=\"flex flex-col gap-3 rounded-lt border border-lt-border bg-lt-surface p-4 text-lt-surface-fg shadow-lt-sm\"\n data-lattice-component={id}\n >\n {hasHeader && (\n <div className=\"flex min-w-0 flex-col gap-1.5\">\n {title !== null && <div className=\"text-sm font-semibold leading-tight\">{title}</div>}\n {description !== null && (\n <div className=\"text-xs leading-5 text-lt-muted-fg\">{description}</div>\n )}\n </div>\n )}\n {children}\n </div>\n );\n}\n\nfunction CartesianChart({ props }: { props: ChartProps }) {\n const series = props.series.filter(isCartesianSeries);\n const RechartsChart = cartesianChartFor(series);\n const { locale } = useLocale();\n const { timezone } = useTimezone();\n const ctx = { locale, timezone };\n const formatCategory = (value: unknown) => formatValue(value, props.categoryFormat, ctx);\n const formatValueTick = (value: unknown) => formatValue(value, props.valueFormat, ctx);\n const hasBarSeries = series.some((item) => item.type === \"bar\");\n const tooltipCursor = hasBarSeries\n ? { fill: \"var(--lt-muted-fg)\", fillOpacity: 0.15 }\n : { stroke: \"var(--lt-muted-fg)\", strokeOpacity: 0.4 };\n\n return (\n <ResponsiveContainer width=\"100%\" height={props.height} debounce={100}>\n <RechartsChart data={props.data} margin={chartMargin}>\n {props.grid && <CartesianGrid strokeDasharray=\"3 3\" stroke=\"var(--lt-border)\" />}\n {props.xAxis && props.categoryKey !== null && (\n <XAxis\n dataKey={props.categoryKey}\n stroke=\"var(--lt-muted-fg)\"\n tick={axisTick}\n tickFormatter={formatCategory}\n tickLine={false}\n />\n )}\n {props.yAxis && (\n <YAxis\n stroke=\"var(--lt-muted-fg)\"\n tick={axisTick}\n tickFormatter={formatValueTick}\n tickLine={false}\n width={42}\n />\n )}\n {props.tooltip && (\n <Tooltip\n {...tooltipProps}\n cursor={tooltipCursor}\n formatter={formatValueTick}\n labelFormatter={formatCategory}\n />\n )}\n {props.legend && <Legend {...compactLegendProps} />}\n {series.map((item, index) => {\n const color = item.color ? colorValue(item.color) : colorAt(index);\n const key = `${item.type}:${item.dataKey}`;\n\n if (item.type === \"area\") {\n return (\n <Area\n key={key}\n dataKey={item.dataKey}\n fill={color}\n fillOpacity={0.16}\n name={item.name}\n stackId={item.stackId ?? undefined}\n stroke={color}\n type=\"monotone\"\n />\n );\n }\n\n if (item.type === \"bar\") {\n return (\n <Bar\n key={key}\n dataKey={item.dataKey}\n fill={color}\n name={item.name}\n radius={[4, 4, 0, 0]}\n stackId={item.stackId ?? undefined}\n />\n );\n }\n\n return (\n <Line\n key={key}\n dataKey={item.dataKey}\n dot={false}\n name={item.name}\n stroke={color}\n strokeWidth={2}\n type=\"monotone\"\n />\n );\n })}\n </RechartsChart>\n </ResponsiveContainer>\n );\n}\n\nfunction PieChartView({ props, series }: { props: ChartProps; series: ChartSeries }) {\n const { locale } = useLocale();\n const { timezone } = useTimezone();\n const ctx = { locale, timezone };\n\n return (\n <ResponsiveContainer width=\"100%\" height={props.height} debounce={100}>\n <PieChart margin={chartMargin}>\n {props.tooltip && (\n <Tooltip\n {...tooltipProps}\n formatter={(value) => formatValue(value, props.valueFormat, ctx)}\n labelFormatter={(value) => formatValue(value, props.categoryFormat, ctx)}\n />\n )}\n {props.legend && <Legend {...compactLegendProps} />}\n <Pie\n data={props.data}\n dataKey={series.dataKey}\n name={series.name}\n nameKey={series.nameKey ?? undefined}\n innerRadius={series.innerRadius}\n outerRadius=\"68%\"\n >\n {props.data.map((datum, index) => (\n <Cell key={index} fill={datumColor(datum, series, index)} />\n ))}\n </Pie>\n </PieChart>\n </ResponsiveContainer>\n );\n}\n\nfunction ChartLegend({\n entries,\n}: {\n entries: Array<{ color: string; label: string; value?: string }>;\n}) {\n return (\n <div className=\"flex flex-wrap items-center justify-center gap-x-3 gap-y-1\">\n {entries.map((entry, index) => (\n <span\n key={index}\n className=\"flex items-center gap-1 text-[11px] leading-[14px] text-lt-muted-fg\"\n >\n <span\n aria-hidden\n className=\"size-[7px] rounded-full\"\n style={{ background: entry.color }}\n />\n {entry.label}\n {entry.value !== undefined && (\n <span className=\"font-medium text-lt-surface-fg\">{entry.value}</span>\n )}\n </span>\n ))}\n </div>\n );\n}\n\nfunction GaugeChartView({ props, series }: { props: ChartProps; series: ChartSeries }) {\n const { locale } = useLocale();\n const { timezone } = useTimezone();\n const ctx = { locale, timezone };\n const values = props.data.map((datum) => datumValue(datum, series.dataKey));\n const max = series.maxValue ?? Math.max(0, ...values);\n const legendEntries = props.data.map((datum, index) => ({\n color: datumColor(datum, series, index),\n label: datumName(datum, series),\n }));\n\n return (\n <div className=\"flex flex-col gap-1.5\">\n {props.legend && <ChartLegend entries={legendEntries} />}\n <div className=\"relative\">\n <ResponsiveContainer width=\"100%\" height={props.height} debounce={100}>\n <RadialBarChart\n data={props.data}\n endAngle={-30}\n innerRadius={series.innerRadius}\n margin={chartMargin}\n outerRadius=\"100%\"\n startAngle={210}\n >\n <PolarAngleAxis type=\"number\" domain={[0, max]} tick={false} />\n {props.tooltip && (\n <Tooltip\n {...tooltipProps}\n formatter={(value) => formatValue(value, props.valueFormat, ctx)}\n />\n )}\n <RadialBar\n background={{ fill: \"var(--lt-muted)\" }}\n cornerRadius={6}\n dataKey={series.dataKey}\n name={series.name}\n >\n {props.data.map((datum, index) => (\n <Cell key={index} fill={datumColor(datum, series, index)} />\n ))}\n </RadialBar>\n </RadialBarChart>\n </ResponsiveContainer>\n {values.length === 1 && (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\n <span className=\"text-2xl font-semibold text-lt-surface-fg\">\n {formatValue(values[0], props.valueFormat, ctx)}\n </span>\n </div>\n )}\n </div>\n </div>\n );\n}\n\nfunction DistributionChartView({ props, series }: { props: ChartProps; series: ChartSeries }) {\n const { locale } = useLocale();\n const { timezone } = useTimezone();\n const ctx = { locale, timezone };\n const segments = props.data\n .map((datum, index) => ({\n color: datumColor(datum, series, index),\n label: datumName(datum, series),\n value: datumValue(datum, series.dataKey),\n }))\n .filter((segment) => segment.value > 0);\n const total = segments.reduce((sum, segment) => sum + segment.value, 0);\n\n if (total <= 0) {\n return <div className=\"h-2.5 w-full rounded-full bg-lt-muted\" data-lattice-distribution=\"\" />;\n }\n\n const formatPercent = new Intl.NumberFormat(locale, {\n maximumFractionDigits: 1,\n style: \"percent\",\n });\n\n return (\n <div className=\"flex flex-col gap-1.5\">\n <div\n className=\"flex h-2.5 w-full gap-0.5 overflow-hidden rounded-full\"\n data-lattice-distribution=\"\"\n >\n {segments.map((segment, index) => (\n <div\n key={index}\n style={{ background: segment.color, width: `${(segment.value / total) * 100}%` }}\n title={\n props.tooltip\n ? `${segment.label}: ${formatValue(segment.value, props.valueFormat, ctx)}`\n : undefined\n }\n />\n ))}\n </div>\n {props.legend && (\n <ChartLegend\n entries={segments.map((segment) => ({\n color: segment.color,\n label: segment.label,\n value: formatPercent.format(segment.value / total),\n }))}\n />\n )}\n </div>\n );\n}\n\nfunction SpecialChart({ props, series }: { props: ChartProps; series: SpecialSeries }) {\n switch (series.type) {\n case \"distribution\":\n return <DistributionChartView props={props} series={series} />;\n case \"gauge\":\n return <GaugeChartView props={props} series={series} />;\n case \"pie\":\n return <PieChartView props={props} series={series} />;\n }\n}\n\nconst ChartView: RendererComponent<\"chart\"> = ({ node }) => {\n const props = node.props;\n const specialSeries = props.series.find(isSpecialSeries);\n const hasCartesianSeries = props.series.some(isCartesianSeries);\n\n return (\n <ChartFrame description={props.description} id={nodeIdentity(node)} title={props.title}>\n <div className=\"min-h-0 w-full\">\n {specialSeries !== undefined && !hasCartesianSeries ? (\n <SpecialChart props={props} series={specialSeries} />\n ) : (\n <CartesianChart props={props} />\n )}\n </div>\n </ChartFrame>\n );\n};\n\nexport default ChartView;\n"],"mappings":"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"}
@@ -35,6 +35,6 @@ export declare function ActionTrigger({ action, children, }: {
35
35
  export declare function useClickBehavior(props: {
36
36
  href?: string | null;
37
37
  method?: Method | null;
38
- action?: Node | null;
38
+ action?: Node<"action"> | null;
39
39
  effects?: Effect[] | null;
40
40
  }): ClickBehavior;
@@ -1 +1 @@
1
- {"version":3,"file":"click-behavior.js","names":[],"sources":["../../resources/js/ui/click-behavior.tsx"],"sourcesContent":["import type { Method } from \"@inertiajs/core\";\nimport { createContext, useContext, type ReactNode } from \"react\";\nimport type { Node } from \"@lattice-php/lattice/core/types\";\nimport { getActionEffects } from \"@lattice-php/lattice/effects/dispatch\";\nimport { useEffectDispatcher } from \"@lattice-php/lattice/effects/use-effect-dispatcher\";\nimport type { Effect } from \"@lattice-php/lattice/types/generated\";\n\nexport type ClickBehavior =\n | { kind: \"navigate\"; href: string; method: Method }\n | { kind: \"action\"; action: Node<\"action\"> }\n | { kind: \"effects\"; onClick: () => void }\n | { kind: \"none\" };\n\nexport type TriggerState = { onClick: () => void; processing: boolean };\n\nexport type ActionTriggerRenderer = (props: {\n action: Node<\"action\">;\n children: (trigger: TriggerState) => ReactNode;\n}) => ReactNode;\n\nconst ActionTriggerContext = createContext<ActionTriggerRenderer | null>(null);\n\nexport function ActionTriggerProvider({\n children,\n render,\n}: {\n children: ReactNode;\n render: ActionTriggerRenderer;\n}) {\n return <ActionTriggerContext.Provider value={render}>{children}</ActionTriggerContext.Provider>;\n}\n\nexport function useActionTrigger(): ActionTriggerRenderer | null {\n return useContext(ActionTriggerContext);\n}\n\nexport function ActionTrigger({\n action,\n children,\n}: {\n action: Node<\"action\">;\n children: (trigger: TriggerState) => ReactNode;\n}) {\n const render = useActionTrigger();\n\n if (!render) {\n throw new Error(\"Action triggers require an ActionTriggerProvider.\");\n }\n\n return <>{render({ action, children })}</>;\n}\n\nexport function useClickBehavior(props: {\n href?: string | null;\n method?: Method | null;\n action?: Node | null;\n effects?: Effect[] | null;\n}): ClickBehavior {\n const dispatch = useEffectDispatcher();\n const action = (props.action ?? null) as Node<\"action\"> | null;\n const effects = props.effects ?? [];\n\n if (action) {\n return { kind: \"action\", action };\n }\n\n if (effects.length > 0) {\n return { kind: \"effects\", onClick: () => dispatch(getActionEffects(effects)) };\n }\n\n if (props.href != null && props.href !== \"\") {\n return { kind: \"navigate\", href: props.href, method: props.method ?? \"get\" };\n }\n\n return { kind: \"none\" };\n}\n"],"mappings":";;;;;AAoBA,IAAM,uBAAuB,cAA4C,IAAI;AAE7E,SAAgB,sBAAsB,EACpC,UACA,UAIC;CACD,OAAO,oBAAC,qBAAqB,UAAtB;EAA+B,OAAO;EAAS;CAAwC,CAAA;AAChG;AAEA,SAAgB,mBAAiD;CAC/D,OAAO,WAAW,oBAAoB;AACxC;AAEA,SAAgB,cAAc,EAC5B,QACA,YAIC;CACD,MAAM,SAAS,iBAAiB;CAEhC,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,mDAAmD;CAGrE,OAAO,oBAAA,YAAA,EAAA,UAAG,OAAO;EAAE;EAAQ;CAAS,CAAC,EAAI,CAAA;AAC3C;AAEA,SAAgB,iBAAiB,OAKf;CAChB,MAAM,WAAW,oBAAoB;CACrC,MAAM,SAAU,MAAM,UAAU;CAChC,MAAM,UAAU,MAAM,WAAW,CAAC;CAElC,IAAI,QACF,OAAO;EAAE,MAAM;EAAU;CAAO;CAGlC,IAAI,QAAQ,SAAS,GACnB,OAAO;EAAE,MAAM;EAAW,eAAe,SAAS,iBAAiB,OAAO,CAAC;CAAE;CAG/E,IAAI,MAAM,QAAQ,QAAQ,MAAM,SAAS,IACvC,OAAO;EAAE,MAAM;EAAY,MAAM,MAAM;EAAM,QAAQ,MAAM,UAAU;CAAM;CAG7E,OAAO,EAAE,MAAM,OAAO;AACxB"}
1
+ {"version":3,"file":"click-behavior.js","names":[],"sources":["../../resources/js/ui/click-behavior.tsx"],"sourcesContent":["import type { Method } from \"@inertiajs/core\";\nimport { createContext, useContext, type ReactNode } from \"react\";\nimport type { Node } from \"@lattice-php/lattice/core/types\";\nimport { getActionEffects } from \"@lattice-php/lattice/effects/dispatch\";\nimport { useEffectDispatcher } from \"@lattice-php/lattice/effects/use-effect-dispatcher\";\nimport type { Effect } from \"@lattice-php/lattice/types/generated\";\n\nexport type ClickBehavior =\n | { kind: \"navigate\"; href: string; method: Method }\n | { kind: \"action\"; action: Node<\"action\"> }\n | { kind: \"effects\"; onClick: () => void }\n | { kind: \"none\" };\n\nexport type TriggerState = { onClick: () => void; processing: boolean };\n\nexport type ActionTriggerRenderer = (props: {\n action: Node<\"action\">;\n children: (trigger: TriggerState) => ReactNode;\n}) => ReactNode;\n\nconst ActionTriggerContext = createContext<ActionTriggerRenderer | null>(null);\n\nexport function ActionTriggerProvider({\n children,\n render,\n}: {\n children: ReactNode;\n render: ActionTriggerRenderer;\n}) {\n return <ActionTriggerContext.Provider value={render}>{children}</ActionTriggerContext.Provider>;\n}\n\nexport function useActionTrigger(): ActionTriggerRenderer | null {\n return useContext(ActionTriggerContext);\n}\n\nexport function ActionTrigger({\n action,\n children,\n}: {\n action: Node<\"action\">;\n children: (trigger: TriggerState) => ReactNode;\n}) {\n const render = useActionTrigger();\n\n if (!render) {\n throw new Error(\"Action triggers require an ActionTriggerProvider.\");\n }\n\n return <>{render({ action, children })}</>;\n}\n\nexport function useClickBehavior(props: {\n href?: string | null;\n method?: Method | null;\n action?: Node<\"action\"> | null;\n effects?: Effect[] | null;\n}): ClickBehavior {\n const dispatch = useEffectDispatcher();\n const action = props.action ?? null;\n const effects = props.effects ?? [];\n\n if (action) {\n return { kind: \"action\", action };\n }\n\n if (effects.length > 0) {\n return { kind: \"effects\", onClick: () => dispatch(getActionEffects(effects)) };\n }\n\n if (props.href != null && props.href !== \"\") {\n return { kind: \"navigate\", href: props.href, method: props.method ?? \"get\" };\n }\n\n return { kind: \"none\" };\n}\n"],"mappings":";;;;;AAoBA,IAAM,uBAAuB,cAA4C,IAAI;AAE7E,SAAgB,sBAAsB,EACpC,UACA,UAIC;CACD,OAAO,oBAAC,qBAAqB,UAAtB;EAA+B,OAAO;EAAS;CAAwC,CAAA;AAChG;AAEA,SAAgB,mBAAiD;CAC/D,OAAO,WAAW,oBAAoB;AACxC;AAEA,SAAgB,cAAc,EAC5B,QACA,YAIC;CACD,MAAM,SAAS,iBAAiB;CAEhC,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,mDAAmD;CAGrE,OAAO,oBAAA,YAAA,EAAA,UAAG,OAAO;EAAE;EAAQ;CAAS,CAAC,EAAI,CAAA;AAC3C;AAEA,SAAgB,iBAAiB,OAKf;CAChB,MAAM,WAAW,oBAAoB;CACrC,MAAM,SAAS,MAAM,UAAU;CAC/B,MAAM,UAAU,MAAM,WAAW,CAAC;CAElC,IAAI,QACF,OAAO;EAAE,MAAM;EAAU;CAAO;CAGlC,IAAI,QAAQ,SAAS,GACnB,OAAO;EAAE,MAAM;EAAW,eAAe,SAAS,iBAAiB,OAAO,CAAC;CAAE;CAG/E,IAAI,MAAM,QAAQ,QAAQ,MAAM,SAAS,IACvC,OAAO;EAAE,MAAM;EAAY,MAAM,MAAM;EAAM,QAAQ,MAAM,UAAU;CAAM;CAG7E,OAAO,EAAE,MAAM,OAAO;AACxB"}
@@ -0,0 +1,8 @@
1
+ export declare function normalizeHex(input: string): string | null;
2
+ export declare function ColorPicker({ value, onChange, palette, hexLabel, paletteLabel, }: {
3
+ value: string;
4
+ onChange: (hex: string) => void;
5
+ palette: string[];
6
+ hexLabel?: string;
7
+ paletteLabel?: string;
8
+ }): import("react").JSX.Element;