@lssm/lib.design-system 0.0.0-canary-20251217060834 → 0.0.0-canary-20251217072406

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 (234) hide show
  1. package/dist/_virtual/rolldown_runtime.js +1 -12
  2. package/dist/components/agent/AgentMonitor.js +3 -3
  3. package/dist/components/agent/ApprovalQueue.d.ts +2 -2
  4. package/dist/components/agent/ApprovalQueue.js +4 -5
  5. package/dist/components/atoms/ActionButtons.d.ts +3 -3
  6. package/dist/components/atoms/ActionButtons.js +3 -3
  7. package/dist/components/atoms/Button.d.ts +6 -6
  8. package/dist/components/atoms/Button.js +2 -2
  9. package/dist/components/atoms/ButtonLink.js +2 -2
  10. package/dist/components/atoms/Cta.d.ts +7 -7
  11. package/dist/components/atoms/Cta.js +2 -2
  12. package/dist/components/atoms/DataChips.d.ts +2 -2
  13. package/dist/components/atoms/DataChips.js +1 -1
  14. package/dist/components/atoms/EmptyState.d.ts +2 -2
  15. package/dist/components/atoms/EmptyState.js +1 -1
  16. package/dist/components/atoms/ErrorState.d.ts +6 -6
  17. package/dist/components/atoms/ErrorState.js +1 -1
  18. package/dist/components/atoms/Input.d.ts +2 -2
  19. package/dist/components/atoms/Input.js +1 -1
  20. package/dist/components/atoms/Link.web.d.ts +3 -3
  21. package/dist/components/atoms/Link.web.js +4 -6
  22. package/dist/components/atoms/LoaderCircular.d.ts +3 -3
  23. package/dist/components/atoms/LoaderCircular.js +2 -2
  24. package/dist/components/atoms/NavBrand.d.ts +3 -3
  25. package/dist/components/atoms/NavBrand.js +1 -1
  26. package/dist/components/atoms/Stepper.d.ts +2 -2
  27. package/dist/components/atoms/Stepper.js +1 -1
  28. package/dist/components/atoms/Textarea.d.ts +2 -2
  29. package/dist/components/atoms/Textarea.js +4 -4
  30. package/dist/components/data-view/DataViewDetail.d.ts +5 -5
  31. package/dist/components/data-view/DataViewList.d.ts +5 -5
  32. package/dist/components/data-view/DataViewRenderer.d.ts +5 -5
  33. package/dist/components/data-view/DataViewRenderer.js +3 -3
  34. package/dist/components/data-view/DataViewTable.d.ts +6 -6
  35. package/dist/components/forms/ActionForm.d.ts +3 -3
  36. package/dist/components/forms/ActionForm.js +1 -1
  37. package/dist/components/forms/FormCardLayout.d.ts +5 -5
  38. package/dist/components/forms/FormCardLayout.js +2 -2
  39. package/dist/components/forms/FormDialog.d.ts +5 -5
  40. package/dist/components/forms/FormDialog.js +1 -1
  41. package/dist/components/forms/FormLayout.d.ts +4 -4
  42. package/dist/components/forms/FormLayout.js +1 -1
  43. package/dist/components/forms/FormOneByOneLayout.d.ts +6 -6
  44. package/dist/components/forms/FormOneByOneLayout.js +4 -4
  45. package/dist/components/forms/FormStepsLayout.d.ts +4 -4
  46. package/dist/components/forms/FormStepsLayout.js +5 -5
  47. package/dist/components/forms/ZodForm.d.ts +6 -6
  48. package/dist/components/forms/ZodForm.js +1 -1
  49. package/dist/components/legal/atoms/DefinitionList.d.ts +3 -3
  50. package/dist/components/legal/atoms/DefinitionList.js +1 -1
  51. package/dist/components/legal/atoms/KeyValueList.d.ts +3 -3
  52. package/dist/components/legal/atoms/KeyValueList.js +1 -1
  53. package/dist/components/legal/atoms/LegalCallout.d.ts +2 -2
  54. package/dist/components/legal/atoms/LegalCallout.js +1 -1
  55. package/dist/components/legal/atoms/LegalHeading.d.ts +2 -2
  56. package/dist/components/legal/atoms/LegalHeading.js +1 -1
  57. package/dist/components/legal/atoms/LegalList.d.ts +2 -2
  58. package/dist/components/legal/atoms/LegalList.js +1 -1
  59. package/dist/components/legal/atoms/LegalSection.d.ts +2 -2
  60. package/dist/components/legal/atoms/LegalSection.js +1 -1
  61. package/dist/components/legal/atoms/LegalText.d.ts +2 -2
  62. package/dist/components/legal/atoms/LegalText.js +1 -1
  63. package/dist/components/legal/molecules/Consent.d.ts +5 -5
  64. package/dist/components/legal/molecules/Consent.js +2 -2
  65. package/dist/components/legal/molecules/ContactFields.js +1 -1
  66. package/dist/components/legal/molecules/LegalMeta.js +1 -1
  67. package/dist/components/legal/molecules/LegalTOC.d.ts +3 -3
  68. package/dist/components/legal/molecules/LegalTOC.js +1 -1
  69. package/dist/components/legal/organisms/ContactForm.d.ts +2 -2
  70. package/dist/components/legal/organisms/ContactForm.js +3 -3
  71. package/dist/components/legal/organisms/GDPRDataRequest.d.ts +3 -3
  72. package/dist/components/legal/organisms/GDPRDataRequest.js +2 -2
  73. package/dist/components/legal/organisms/GDPRRights.d.ts +3 -3
  74. package/dist/components/legal/organisms/LegalPageLayout.d.ts +3 -3
  75. package/dist/components/legal/organisms/LegalPageLayout.js +1 -1
  76. package/dist/components/legal/templates/ContactTemplate.d.ts +4 -4
  77. package/dist/components/legal/templates/ContactTemplate.js +3 -3
  78. package/dist/components/legal/templates/CookiesTemplate.d.ts +7 -7
  79. package/dist/components/legal/templates/PrivacyTemplate.d.ts +7 -7
  80. package/dist/components/legal/templates/SalesTermsTemplate.d.ts +5 -5
  81. package/dist/components/legal/templates/TermsTemplate.d.ts +5 -5
  82. package/dist/components/marketing/MarketingCard.d.ts +4 -4
  83. package/dist/components/marketing/MarketingCard.js +3 -3
  84. package/dist/components/marketing/MarketingCardsSection.d.ts +2 -2
  85. package/dist/components/marketing/MarketingCardsSection.js +1 -1
  86. package/dist/components/marketing/MarketingComparisonSection.d.ts +5 -5
  87. package/dist/components/marketing/MarketingComparisonSection.js +3 -3
  88. package/dist/components/marketing/MarketingIconCard.d.ts +5 -5
  89. package/dist/components/marketing/MarketingIconCard.js +4 -3
  90. package/dist/components/marketing/MarketingSection.d.ts +5 -5
  91. package/dist/components/marketing/MarketingSection.js +3 -3
  92. package/dist/components/marketing/MarketingStepCard.d.ts +3 -3
  93. package/dist/components/marketing/MarketingStepCard.js +4 -3
  94. package/dist/components/molecules/AiLinkButton.js +5 -5
  95. package/dist/components/molecules/Breadcrumbs.d.ts +2 -2
  96. package/dist/components/molecules/Breadcrumbs.js +3 -3
  97. package/dist/components/molecules/CommandPalette.js +4 -4
  98. package/dist/components/molecules/CommandSearchTrigger.d.ts +2 -2
  99. package/dist/components/molecules/CommandSearchTrigger.js +5 -5
  100. package/dist/components/molecules/EntityCard.d.ts +9 -9
  101. package/dist/components/molecules/EntityCard.js +2 -2
  102. package/dist/components/molecules/FiltersToolbar.d.ts +4 -4
  103. package/dist/components/molecules/FiltersToolbar.js +6 -6
  104. package/dist/components/molecules/HoverPreview.d.ts +3 -3
  105. package/dist/components/molecules/HoverPreview.js +4 -2
  106. package/dist/components/molecules/LangSwitch.d.ts +2 -2
  107. package/dist/components/molecules/LangSwitchDropdown.js +4 -4
  108. package/dist/components/molecules/LoaderBlock.d.ts +3 -3
  109. package/dist/components/molecules/LoaderBlock.js +1 -1
  110. package/dist/components/molecules/MobileNavMenu.js +1 -1
  111. package/dist/components/molecules/NavItemCard.js +1 -1
  112. package/dist/components/molecules/NavMain.js +4 -4
  113. package/dist/components/molecules/NavUser.js +2 -2
  114. package/dist/components/molecules/OverviewCard.d.ts +2 -2
  115. package/dist/components/molecules/OverviewCard.js +3 -5
  116. package/dist/components/molecules/SkeletonBlock/index.web.d.ts +2 -2
  117. package/dist/components/molecules/SkeletonBlock/index.web.js +2 -2
  118. package/dist/components/molecules/SkeletonCircle/index.web.js +1 -1
  119. package/dist/components/molecules/SkeletonList/index.web.d.ts +2 -2
  120. package/dist/components/molecules/SkeletonList/index.web.js +1 -1
  121. package/dist/components/molecules/StatCard.d.ts +8 -8
  122. package/dist/components/molecules/StatCard.js +2 -2
  123. package/dist/components/molecules/StatusChip.d.ts +5 -5
  124. package/dist/components/molecules/StatusChip.js +1 -1
  125. package/dist/components/molecules/hover-previews/Doc.d.ts +6 -6
  126. package/dist/components/molecules/hover-previews/Media.d.ts +5 -5
  127. package/dist/components/molecules/hover-previews/Simple.d.ts +7 -7
  128. package/dist/components/molecules/hover-previews/Stats.d.ts +6 -6
  129. package/dist/components/molecules/hover-previews/User.d.ts +7 -7
  130. package/dist/components/organisms/AcademyLayout.d.ts +7 -7
  131. package/dist/components/organisms/AcademyLayout.js +4 -4
  132. package/dist/components/organisms/AppHeader.d.ts +3 -3
  133. package/dist/components/organisms/AppHeader.js +2 -2
  134. package/dist/components/organisms/AppLayout.d.ts +6 -6
  135. package/dist/components/organisms/AppLayout.js +5 -5
  136. package/dist/components/organisms/AppSidebar.d.ts +3 -3
  137. package/dist/components/organisms/AppSidebar.js +2 -2
  138. package/dist/components/organisms/EmptyDataList.js +1 -1
  139. package/dist/components/organisms/EmptyDataList.types.d.ts +8 -8
  140. package/dist/components/organisms/EmptySearchResult.d.ts +5 -5
  141. package/dist/components/organisms/EmptySearchResult.js +3 -3
  142. package/dist/components/organisms/FAQSection.d.ts +6 -6
  143. package/dist/components/organisms/FAQSection.js +4 -4
  144. package/dist/components/organisms/FeatureCarousel.d.ts +4 -4
  145. package/dist/components/organisms/FeatureCarousel.js +2 -2
  146. package/dist/components/organisms/FeaturesSection.d.ts +4 -4
  147. package/dist/components/organisms/FeaturesSection.js +1 -1
  148. package/dist/components/organisms/Footer.d.ts +5 -5
  149. package/dist/components/organisms/Footer.js +2 -2
  150. package/dist/components/organisms/Header.d.ts +5 -5
  151. package/dist/components/organisms/Header.js +4 -4
  152. package/dist/components/organisms/HeroResponsive.d.ts +2 -2
  153. package/dist/components/organisms/HeroSection.d.ts +3 -3
  154. package/dist/components/organisms/HeroSection.js +1 -1
  155. package/dist/components/organisms/ListCardPage.d.ts +4 -4
  156. package/dist/components/organisms/ListCardPage.js +2 -2
  157. package/dist/components/organisms/ListGridPage.d.ts +4 -4
  158. package/dist/components/organisms/ListGridPage.js +1 -1
  159. package/dist/components/organisms/ListPageResponsive.js +1 -1
  160. package/dist/components/organisms/ListTablePage.d.ts +6 -6
  161. package/dist/components/organisms/ListTablePage.js +2 -2
  162. package/dist/components/organisms/MarketingHeader.d.ts +4 -4
  163. package/dist/components/organisms/MarketingHeader.js +9 -9
  164. package/dist/components/organisms/MarketingHeaderDesktop.js +3 -3
  165. package/dist/components/organisms/MarketingHeaderMobile.js +6 -6
  166. package/dist/components/organisms/MarketingLayout.d.ts +4 -4
  167. package/dist/components/organisms/MarketingLayout.js +2 -2
  168. package/dist/components/organisms/PageHeaderResponsive.js +1 -1
  169. package/dist/components/organisms/PricingCarousel.js +2 -2
  170. package/dist/components/organisms/PricingSection.d.ts +3 -3
  171. package/dist/components/organisms/PricingSection.js +1 -1
  172. package/dist/components/organisms/TestimonialCarousel.d.ts +5 -5
  173. package/dist/components/organisms/TestimonialCarousel.js +2 -2
  174. package/dist/components/templates/lists/ListPageTemplate/index.web.js +5 -5
  175. package/dist/components/templates/lists/ListPageTemplate/types.d.ts +7 -7
  176. package/dist/contracts/dist/client/react/drivers/shadcn.js +11 -0
  177. package/dist/contracts/dist/client/react/form-render.js +298 -0
  178. package/dist/contracts/dist/forms.js +88 -0
  179. package/dist/hooks/useListUrlState.js +7 -7
  180. package/dist/index.js +2 -1
  181. package/dist/platform/useColorScheme.js +3 -3
  182. package/dist/platform/useReducedMotion.js +3 -3
  183. package/dist/platform/useResponsive.js +4 -4
  184. package/dist/renderers/form-contract.d.ts +1 -1
  185. package/dist/renderers/form-contract.js +9 -9
  186. package/dist/types/navigation.d.ts +10 -10
  187. package/dist/ui-kit-web/dist/ui/accordion.js +48 -0
  188. package/dist/ui-kit-web/dist/ui/atoms/FilterSelect/FilterSelect.js +52 -0
  189. package/dist/ui-kit-web/dist/ui/atoms/Pagination/Pagination.js +156 -0
  190. package/dist/ui-kit-web/dist/ui/atoms/SearchInput/SearchInput.js +49 -0
  191. package/dist/ui-kit-web/dist/ui/avatar.js +32 -0
  192. package/dist/ui-kit-web/dist/ui/badge.js +27 -0
  193. package/dist/ui-kit-web/dist/ui/breadcrumb.js +59 -0
  194. package/dist/ui-kit-web/dist/ui/button.js +55 -0
  195. package/dist/ui-kit-web/dist/ui/card.js +44 -0
  196. package/dist/ui-kit-web/dist/ui/carousel.js +135 -0
  197. package/dist/ui-kit-web/dist/ui/checkbox.js +25 -0
  198. package/dist/ui-kit-web/dist/ui/collapsible.js +27 -0
  199. package/dist/ui-kit-web/dist/ui/command.js +90 -0
  200. package/dist/ui-kit-web/dist/ui/dialog.js +84 -0
  201. package/dist/ui-kit-web/dist/ui/dropdown-menu.js +57 -0
  202. package/dist/ui-kit-web/dist/ui/empty-state.js +43 -0
  203. package/dist/ui-kit-web/dist/ui/empty.js +62 -0
  204. package/dist/ui-kit-web/dist/ui/field.js +78 -0
  205. package/dist/ui-kit-web/dist/ui/form.js +16 -0
  206. package/dist/ui-kit-web/dist/ui/hover-card.js +40 -0
  207. package/dist/ui-kit-web/dist/ui/input.js +17 -0
  208. package/dist/ui-kit-web/dist/ui/label.js +19 -0
  209. package/dist/ui-kit-web/dist/ui/marketing/Hero.js +52 -0
  210. package/dist/ui-kit-web/dist/ui/marketing/PricingTable.js +59 -0
  211. package/dist/ui-kit-web/dist/ui/molecules/SearchAndFilter/SearchAndFilter.js +102 -0
  212. package/dist/ui-kit-web/dist/ui/molecules/SkeletonList.js +14 -0
  213. package/dist/ui-kit-web/dist/ui/navigation-menu.js +75 -0
  214. package/dist/ui-kit-web/dist/ui/organisms/ListPage/ListPage.js +197 -0
  215. package/dist/ui-kit-web/dist/ui/page-header.js +40 -0
  216. package/dist/ui-kit-web/dist/ui/pagination.js +65 -0
  217. package/dist/ui-kit-web/dist/ui/radio-group.js +32 -0
  218. package/dist/ui-kit-web/dist/ui/select.js +86 -0
  219. package/dist/ui-kit-web/dist/ui/separator.js +21 -0
  220. package/dist/ui-kit-web/dist/ui/sheet.js +80 -0
  221. package/dist/ui-kit-web/dist/ui/sidebar.js +281 -0
  222. package/dist/ui-kit-web/dist/ui/skeleton.js +14 -0
  223. package/dist/ui-kit-web/dist/ui/stack.js +158 -0
  224. package/dist/ui-kit-web/dist/ui/stepper.js +37 -0
  225. package/dist/ui-kit-web/dist/ui/switch.js +24 -0
  226. package/dist/ui-kit-web/dist/ui/table.js +57 -0
  227. package/dist/ui-kit-web/dist/ui/text.js +16 -0
  228. package/dist/ui-kit-web/dist/ui/textarea.js +16 -0
  229. package/dist/ui-kit-web/dist/ui/tooltip.js +40 -0
  230. package/dist/ui-kit-web/dist/ui/typography.js +23 -0
  231. package/dist/ui-kit-web/dist/ui/use-mobile.js +20 -0
  232. package/dist/ui-kit-web/dist/ui/utils.js +10 -0
  233. package/dist/ui-kit-web/dist/ui-kit-core/dist/utils.js +13 -0
  234. package/package.json +7 -7
@@ -1,7 +1,7 @@
1
+ import { Card, CardContent } from "../../ui-kit-web/dist/ui/card.js";
2
+ import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "../../ui-kit-web/dist/ui/carousel.js";
1
3
  import "react";
2
4
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { Card, CardContent } from "@lssm/lib.ui-kit-web/ui/card";
4
- import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "@lssm/lib.ui-kit-web/ui/carousel";
5
5
 
6
6
  //#region src/components/organisms/TestimonialCarousel.tsx
7
7
  function TestimonialCarousel({ testimonials, className }) {
@@ -1,18 +1,18 @@
1
+ import { cn } from "../../../../ui-kit-web/dist/ui/utils.js";
2
+ import { Card, CardContent } from "../../../../ui-kit-web/dist/ui/card.js";
3
+ import { Skeleton } from "../../../../ui-kit-web/dist/ui/skeleton.js";
1
4
  import { PageHeaderResponsive } from "../../../organisms/PageHeaderResponsive.js";
2
5
  import { FiltersToolbar } from "../../../molecules/FiltersToolbar.js";
3
6
  import { AiLinkButton } from "../../../molecules/AiLinkButton.js";
4
7
  import { EmptyDataList } from "../../../organisms/EmptyDataList.js";
5
- import * as React from "react";
8
+ import * as React$1 from "react";
6
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
10
  import { cva } from "class-variance-authority";
8
- import { cn } from "@lssm/lib.ui-kit-web/ui/utils";
9
- import { Card, CardContent } from "@lssm/lib.ui-kit-web/ui/card";
10
- import { Skeleton } from "@lssm/lib.ui-kit-web/ui/skeleton";
11
11
 
12
12
  //#region src/components/templates/lists/ListPageTemplate/index.web.tsx
13
13
  const containerVariants = cva("space-y-4 md:space-y-6");
14
14
  function ListPageTemplate({ children, title, description, breadcrumb, actions, className, searchPlaceholder, searchValue, onSearchChange, onSearchSubmit, isLoading, data, renderItem, emptyProps }) {
15
- const mdHref = React.useMemo(() => {
15
+ const mdHref = React$1.useMemo(() => {
16
16
  if (typeof window === "undefined") return void 0;
17
17
  const url = new URL(window.location.href);
18
18
  url.pathname = url.pathname.replace(/\/$/, "") + ".md";
@@ -1,13 +1,13 @@
1
1
  import { EmptyDataListProps } from "../../../organisms/EmptyDataList.types.js";
2
- import * as React from "react";
2
+ import * as React$1 from "react";
3
3
 
4
4
  //#region src/components/templates/lists/ListPageTemplate/types.d.ts
5
5
  interface ListPageTemplateProps<T = any> {
6
- children?: React.ReactNode;
7
- title: React.ReactNode;
8
- description?: React.ReactNode;
9
- breadcrumb?: React.ReactNode;
10
- actions?: React.ReactNode;
6
+ children?: React$1.ReactNode;
7
+ title: React$1.ReactNode;
8
+ description?: React$1.ReactNode;
9
+ breadcrumb?: React$1.ReactNode;
10
+ actions?: React$1.ReactNode;
11
11
  className?: string;
12
12
  searchPlaceholder?: string;
13
13
  searchValue?: string;
@@ -18,7 +18,7 @@ interface ListPageTemplateProps<T = any> {
18
18
  renderItem: (args: {
19
19
  item: T;
20
20
  index: number;
21
- }) => React.ReactNode;
21
+ }) => React$1.ReactNode;
22
22
  emptyProps: EmptyDataListProps;
23
23
  }
24
24
  //#endregion
@@ -0,0 +1,11 @@
1
+ //#region ../contracts/dist/client/react/drivers/shadcn.js
2
+ /**
3
+ * Create a shadcn/ui driver by mapping required slots to components.
4
+ * Host apps should import their shadcn primitives and pass them here.
5
+ */
6
+ function shadcnDriver(slots) {
7
+ return slots;
8
+ }
9
+
10
+ //#endregion
11
+ export { shadcnDriver };
@@ -0,0 +1,298 @@
1
+ import { buildZodWithRelations, evalPredicate } from "../../forms.js";
2
+ import React, { useEffect, useMemo, useState } from "react";
3
+ import { Controller, useFieldArray, useForm } from "react-hook-form";
4
+ import { zodResolver } from "@hookform/resolvers/zod";
5
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
+
7
+ //#region ../contracts/dist/client/react/form-render.js
8
+ function toOptionsArray(src) {
9
+ if (!src) return void 0;
10
+ if (Array.isArray(src)) return {
11
+ kind: "static",
12
+ options: src
13
+ };
14
+ return src;
15
+ }
16
+ function getAtPath(values, path) {
17
+ if (!path) return void 0;
18
+ const segs = path.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
19
+ let cur = values;
20
+ for (const s of segs) {
21
+ if (cur == null) return void 0;
22
+ cur = cur[s];
23
+ }
24
+ return cur;
25
+ }
26
+ function makeDepsKey(values, deps) {
27
+ if (!deps || deps.length === 0) return "[]";
28
+ try {
29
+ return JSON.stringify(deps.map((d) => getAtPath(values, d)));
30
+ } catch {
31
+ return "[]";
32
+ }
33
+ }
34
+ function useResolvedOptions(values, source, resolvers) {
35
+ const [opts, setOpts] = useState([]);
36
+ useEffect(() => {
37
+ let mounted = true;
38
+ const run = async () => {
39
+ if (!source) return setOpts([]);
40
+ if (source.kind === "static") return setOpts([...source.options ?? []]);
41
+ const fn = resolvers?.[source.resolverKey];
42
+ if (!fn) return setOpts([]);
43
+ const res = await fn(values, source.args);
44
+ if (mounted) setOpts([...res ?? []]);
45
+ };
46
+ run();
47
+ return () => {
48
+ mounted = false;
49
+ };
50
+ }, [useMemo(() => {
51
+ if (!source) return "nil";
52
+ if (source.kind === "static") return JSON.stringify(source.options ?? []);
53
+ return makeDepsKey(values, source.deps);
54
+ }, [source, values]), source && source.resolverKey]);
55
+ return opts;
56
+ }
57
+ function fieldPath(parent, name, arrayIndex) {
58
+ if (!name) return parent ?? "";
59
+ const child = typeof arrayIndex === "number" ? `${name.replace(/^\$index$/, String(arrayIndex))}` : name;
60
+ return parent ? `${parent}${typeof arrayIndex === "number" ? `.${arrayIndex}` : ""}.${child}`.replace(/\.+/g, ".") : child;
61
+ }
62
+ function createFormRenderer(base) {
63
+ const conf = base;
64
+ const { driver } = conf;
65
+ function InternalForm(props) {
66
+ const { spec, options, merged } = props;
67
+ const baseZod = useMemo(() => buildZodWithRelations(spec), [spec]);
68
+ const form = useForm({
69
+ ...merged.formOptions,
70
+ resolver: zodResolver(baseZod),
71
+ defaultValues: options?.defaultValues
72
+ });
73
+ const values = form.watch();
74
+ const renderOne = (f, parent, arrayIndex) => {
75
+ const DriverField = driver.Field;
76
+ const DriverLabel = driver.FieldLabel;
77
+ const DriverDesc = driver.FieldDescription;
78
+ const DriverError = driver.FieldError;
79
+ const name = fieldPath(parent, f.name, arrayIndex);
80
+ const visible = evalPredicate(values, f.visibleWhen);
81
+ const enabled = evalPredicate(values, f.enabledWhen);
82
+ const invalid = Boolean(form.getFieldState(name)?.invalid);
83
+ if (!visible) return null;
84
+ const id = name?.replace(/\./g, "-");
85
+ const commonWrapProps = {
86
+ "data-invalid": invalid,
87
+ hidden: !visible,
88
+ disabled: !enabled
89
+ };
90
+ const labelNode = f.labelI18n ? /* @__PURE__ */ jsx(DriverLabel, {
91
+ htmlFor: id,
92
+ children: f.labelI18n
93
+ }) : null;
94
+ const descNode = f.descriptionI18n ? /* @__PURE__ */ jsx(DriverDesc, { children: f.descriptionI18n }) : null;
95
+ if (f.kind === "group") {
96
+ const children = f.fields.map((c, i) => /* @__PURE__ */ jsx(React.Fragment, { children: renderOne(c, name, arrayIndex) }, `${name}-${i}`));
97
+ return /* @__PURE__ */ jsxs(DriverField, {
98
+ ...commonWrapProps,
99
+ children: [
100
+ labelNode,
101
+ children,
102
+ descNode
103
+ ]
104
+ });
105
+ }
106
+ if (f.kind === "array") return renderArray(f, parent);
107
+ return /* @__PURE__ */ jsx(Controller, {
108
+ name,
109
+ control: form.control,
110
+ render: ({ field, fieldState }) => {
111
+ const err = fieldState.error ? [fieldState.error] : [];
112
+ const ariaInvalid = fieldState.invalid || void 0;
113
+ if (f.kind === "text") {
114
+ const Input = driver.Input;
115
+ return /* @__PURE__ */ jsxs(DriverField, {
116
+ ...commonWrapProps,
117
+ children: [
118
+ labelNode,
119
+ /* @__PURE__ */ jsx(Input, {
120
+ id,
121
+ "aria-invalid": ariaInvalid,
122
+ placeholder: f.placeholderI18n,
123
+ autoComplete: f.autoComplete,
124
+ inputMode: f.inputMode,
125
+ maxLength: f.maxLength,
126
+ minLength: f.minLength,
127
+ disabled: !enabled,
128
+ ...field,
129
+ ...f.uiProps,
130
+ keyboard: f.keyboard,
131
+ autoComplete: f.keyboard?.autoComplete ?? f.autoComplete
132
+ }),
133
+ descNode,
134
+ fieldState.invalid ? /* @__PURE__ */ jsx(DriverError, { errors: err }) : null
135
+ ]
136
+ });
137
+ }
138
+ if (f.kind === "textarea") {
139
+ const Textarea = driver.Textarea;
140
+ return /* @__PURE__ */ jsxs(DriverField, {
141
+ ...commonWrapProps,
142
+ children: [
143
+ labelNode,
144
+ /* @__PURE__ */ jsx(Textarea, {
145
+ id,
146
+ "aria-invalid": ariaInvalid,
147
+ placeholder: f.placeholderI18n,
148
+ rows: f.rows,
149
+ maxLength: f.maxLength,
150
+ disabled: !enabled,
151
+ ...field,
152
+ ...f.uiProps,
153
+ keyboard: f.keyboard,
154
+ autoComplete: f.keyboard?.autoComplete ?? f.autoComplete
155
+ }),
156
+ descNode,
157
+ fieldState.invalid ? /* @__PURE__ */ jsx(DriverError, { errors: err }) : null
158
+ ]
159
+ });
160
+ }
161
+ if (f.kind === "select") {
162
+ const Select = driver.Select;
163
+ const opts = useResolvedOptions(values, toOptionsArray(f.options), merged.resolvers);
164
+ return /* @__PURE__ */ jsxs(DriverField, {
165
+ ...commonWrapProps,
166
+ children: [
167
+ labelNode,
168
+ /* @__PURE__ */ jsx(Select, {
169
+ id,
170
+ name,
171
+ "aria-invalid": ariaInvalid,
172
+ disabled: !enabled,
173
+ value: field.value,
174
+ onChange: (v) => field.onChange(v),
175
+ options: opts,
176
+ ...f.uiProps
177
+ }),
178
+ descNode,
179
+ fieldState.invalid ? /* @__PURE__ */ jsx(DriverError, { errors: err }) : null
180
+ ]
181
+ });
182
+ }
183
+ if (f.kind === "checkbox") {
184
+ const Checkbox = driver.Checkbox;
185
+ return /* @__PURE__ */ jsxs(DriverField, {
186
+ ...commonWrapProps,
187
+ children: [
188
+ labelNode,
189
+ /* @__PURE__ */ jsx(Checkbox, {
190
+ id,
191
+ name,
192
+ disabled: !enabled,
193
+ checked: !!field.value,
194
+ onCheckedChange: (v) => field.onChange(v),
195
+ ...f.uiProps
196
+ }),
197
+ descNode,
198
+ fieldState.invalid ? /* @__PURE__ */ jsx(DriverError, { errors: err }) : null
199
+ ]
200
+ });
201
+ }
202
+ if (f.kind === "radio") {
203
+ const RadioGroup = driver.RadioGroup;
204
+ const opts = useResolvedOptions(values, toOptionsArray(f.options), merged.resolvers);
205
+ return /* @__PURE__ */ jsxs(DriverField, {
206
+ ...commonWrapProps,
207
+ children: [
208
+ labelNode,
209
+ /* @__PURE__ */ jsx(RadioGroup, {
210
+ id,
211
+ name,
212
+ disabled: !enabled,
213
+ value: field.value,
214
+ onValueChange: (v) => field.onChange(v),
215
+ options: opts,
216
+ ...f.uiProps
217
+ }),
218
+ descNode,
219
+ fieldState.invalid ? /* @__PURE__ */ jsx(DriverError, { errors: err }) : null
220
+ ]
221
+ });
222
+ }
223
+ if (f.kind === "switch") {
224
+ const Switch = driver.Switch;
225
+ return /* @__PURE__ */ jsxs(DriverField, {
226
+ ...commonWrapProps,
227
+ children: [
228
+ labelNode,
229
+ /* @__PURE__ */ jsx(Switch, {
230
+ id,
231
+ name,
232
+ disabled: !enabled,
233
+ checked: !!field.value,
234
+ onCheckedChange: (v) => field.onChange(v),
235
+ ...f.uiProps
236
+ }),
237
+ descNode,
238
+ fieldState.invalid ? /* @__PURE__ */ jsx(DriverError, { errors: err }) : null
239
+ ]
240
+ });
241
+ }
242
+ return /* @__PURE__ */ jsx(Fragment, {});
243
+ }
244
+ }, name);
245
+ };
246
+ const renderArray = (f, parent) => {
247
+ const name = fieldPath(parent, f.name);
248
+ const { fields, append, remove } = useFieldArray({
249
+ control: form.control,
250
+ name
251
+ });
252
+ const canAdd = f.max == null || fields.length < f.max;
253
+ const canRemove = (idx) => (f.min == null ? fields.length > 0 : fields.length > f.min) && idx >= 0;
254
+ const Button$1 = driver.Button;
255
+ const Label = driver.FieldLabel;
256
+ return /* @__PURE__ */ jsxs("div", { children: [
257
+ f.labelI18n ? /* @__PURE__ */ jsx(Label, { children: f.labelI18n }) : null,
258
+ fields.map((row, idx) => /* @__PURE__ */ jsxs("div", { children: [renderOne(f.of, name, idx), canRemove(idx) ? /* @__PURE__ */ jsx(Button$1, {
259
+ type: "button",
260
+ variant: "ghost",
261
+ size: "sm",
262
+ onClick: () => remove(idx),
263
+ children: "Remove"
264
+ }) : null] }, row.id ?? idx)),
265
+ canAdd ? /* @__PURE__ */ jsx(Button$1, {
266
+ type: "button",
267
+ variant: "outline",
268
+ size: "sm",
269
+ onClick: () => append({}),
270
+ children: "Add"
271
+ }) : null
272
+ ] }, name);
273
+ };
274
+ const onSubmit = async (data) => {
275
+ const actionKey = spec.actions?.[0]?.key ?? "submit";
276
+ if (merged.onSubmitOverride) return merged.onSubmitOverride(data, actionKey);
277
+ };
278
+ const Button = driver.Button;
279
+ return /* @__PURE__ */ jsxs("form", {
280
+ onSubmit: form.handleSubmit(onSubmit),
281
+ children: [(spec.fields || []).map((f, i) => /* @__PURE__ */ jsx(React.Fragment, { children: renderOne(f) }, i)), spec.actions && spec.actions.length ? /* @__PURE__ */ jsx("div", { children: spec.actions.map((a) => /* @__PURE__ */ jsx(Button, {
282
+ type: "submit",
283
+ children: a.labelI18n
284
+ }, a.key)) }) : null]
285
+ });
286
+ }
287
+ return { render: (spec, options) => /* @__PURE__ */ jsx(InternalForm, {
288
+ spec,
289
+ options,
290
+ merged: {
291
+ ...conf,
292
+ ...options?.overrides ?? {}
293
+ }
294
+ }) };
295
+ }
296
+
297
+ //#endregion
298
+ export { createFormRenderer };
@@ -0,0 +1,88 @@
1
+ //#region ../contracts/dist/forms.js
2
+ function getAtPath(values, path) {
3
+ if (!path) return void 0;
4
+ const segs = path.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
5
+ let cur = values;
6
+ for (const s of segs) {
7
+ if (cur == null) return void 0;
8
+ cur = cur[s];
9
+ }
10
+ return cur;
11
+ }
12
+ function evalPredicate(values, pred) {
13
+ if (!pred) return true;
14
+ if (pred.not) return !evalPredicate(values, pred.not);
15
+ if (pred.all && pred.all.length) return pred.all.every((p) => evalPredicate(values, p));
16
+ if (pred.any && pred.any.length) return pred.any.some((p) => evalPredicate(values, p));
17
+ if (pred.when) {
18
+ const { path, op = "truthy", value } = pred.when;
19
+ const v = getAtPath(values, path);
20
+ switch (op) {
21
+ case "equals": return v === value;
22
+ case "notEquals": return v !== value;
23
+ case "in": return Array.isArray(value) && value.includes(v);
24
+ case "notIn": return Array.isArray(value) && !value.includes(v);
25
+ case "gt": return Number(v) > Number(value);
26
+ case "gte": return Number(v) >= Number(value);
27
+ case "lt": return Number(v) < Number(value);
28
+ case "lte": return Number(v) <= Number(value);
29
+ case "empty": return v == null || (Array.isArray(v) ? v.length === 0 : String(v).length === 0);
30
+ case "lengthGt": return (Array.isArray(v) || typeof v === "string") && v.length > Number(value ?? 0);
31
+ case "lengthGte": return (Array.isArray(v) || typeof v === "string") && v.length >= Number(value ?? 0);
32
+ case "lengthLt": return (Array.isArray(v) || typeof v === "string") && v.length < Number(value ?? 0);
33
+ case "lengthLte": return (Array.isArray(v) || typeof v === "string") && v.length <= Number(value ?? 0);
34
+ case "truthy":
35
+ default: return Boolean(v);
36
+ }
37
+ }
38
+ return true;
39
+ }
40
+ /**
41
+ * Wrap the base zod schema with relation-driven refinements (requiredWhen, array min/max)
42
+ * and optional custom constraints. Call this when wiring RHF resolver.
43
+ */
44
+ function buildZodWithRelations(spec, handlers) {
45
+ return spec.model.getZod().superRefine((values, ctx) => {
46
+ const visit = (field, parentPath) => {
47
+ const path = field.name ? parentPath ? `${parentPath}.${field.name}` : field.name : parentPath ?? "";
48
+ if (field.requiredWhen) {
49
+ if (evalPredicate(values, field.requiredWhen)) {
50
+ const v = getAtPath(values, path);
51
+ if (v == null || typeof v === "string" && v.trim().length === 0 || Array.isArray(v) && v.length === 0) ctx.addIssue({
52
+ code: "custom",
53
+ path: path.split("."),
54
+ message: "required"
55
+ });
56
+ }
57
+ }
58
+ if (field.kind === "array") {
59
+ const arr = getAtPath(values, path);
60
+ if (field.min != null && Array.isArray(arr) && arr.length < field.min) ctx.addIssue({
61
+ code: "custom",
62
+ path: path.split("."),
63
+ message: `min:${field.min}`
64
+ });
65
+ if (field.max != null && Array.isArray(arr) && arr.length > field.max) ctx.addIssue({
66
+ code: "custom",
67
+ path: path.split("."),
68
+ message: `max:${field.max}`
69
+ });
70
+ visit(field.of, path);
71
+ } else if (field.kind === "group") for (const child of field.fields) visit(child, path);
72
+ };
73
+ for (const f of spec.fields) visit(f);
74
+ if (spec.constraints && handlers) for (const c of spec.constraints) {
75
+ const fn = handlers[c.key];
76
+ if (!fn) continue;
77
+ const res = fn(values, c.paths, c.args);
78
+ if (!res.ok) ctx.addIssue({
79
+ code: "custom",
80
+ path: (res.path ?? c.paths[0] ?? "").split(".").filter(Boolean),
81
+ message: res.message ?? c.messageI18n
82
+ });
83
+ }
84
+ });
85
+ }
86
+
87
+ //#endregion
88
+ export { buildZodWithRelations, evalPredicate };
@@ -1,4 +1,4 @@
1
- import * as React from "react";
1
+ import * as React$1 from "react";
2
2
 
3
3
  //#region src/hooks/useListUrlState.ts
4
4
  function useListUrlState({ defaults, paramKeys = {
@@ -8,7 +8,7 @@ function useListUrlState({ defaults, paramKeys = {
8
8
  sort: "sort",
9
9
  filters: "f"
10
10
  }, replaceState = true }) {
11
- const read = React.useCallback(() => {
11
+ const read = React$1.useCallback(() => {
12
12
  if (typeof window === "undefined") return defaults;
13
13
  const qs = new URL(window.location.href).searchParams;
14
14
  const parseNum = (v, d) => {
@@ -30,8 +30,8 @@ function useListUrlState({ defaults, paramKeys = {
30
30
  filters
31
31
  };
32
32
  }, [defaults, paramKeys]);
33
- const [state, setState] = React.useState(read);
34
- const write = React.useCallback((next) => {
33
+ const [state, setState] = React$1.useState(read);
34
+ const write = React$1.useCallback((next) => {
35
35
  if (typeof window === "undefined") return;
36
36
  const url = new URL(window.location.href);
37
37
  const qs = url.searchParams;
@@ -60,19 +60,19 @@ function useListUrlState({ defaults, paramKeys = {
60
60
  paramKeys,
61
61
  replaceState
62
62
  ]);
63
- const setFilter = React.useCallback((key, value) => {
63
+ const setFilter = React$1.useCallback((key, value) => {
64
64
  write({ filters: {
65
65
  ...state.filters,
66
66
  [key]: value
67
67
  } });
68
68
  }, [state.filters, write]);
69
- const clearFilters = React.useCallback(() => {
69
+ const clearFilters = React$1.useCallback(() => {
70
70
  write({
71
71
  filters: {},
72
72
  page: 1
73
73
  });
74
74
  }, [write]);
75
- React.useEffect(() => {
75
+ React$1.useEffect(() => {
76
76
  const onPop = () => setState(read());
77
77
  window.addEventListener("popstate", onPop);
78
78
  return () => window.removeEventListener("popstate", onPop);
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@ import { DeleteButton, EditButton, ToggleButton, ToggleLeftButton, ToggleRightBu
11
11
  import { DateChip, DurationChip, PlaceChip, TimeChip } from "./components/atoms/DataChips.js";
12
12
  import { NavMain } from "./components/molecules/NavMain.js";
13
13
  import { NavUser } from "./components/molecules/NavUser.js";
14
+ import { CardContent, CardDescription, CardHeader, CardTitle } from "./ui-kit-web/dist/ui/card.js";
14
15
  import { ApprovalQueue } from "./components/agent/ApprovalQueue.js";
15
16
  import { AgentMonitor } from "./components/agent/AgentMonitor.js";
16
17
  import { AppSidebar } from "./components/organisms/AppSidebar.js";
@@ -25,7 +26,7 @@ import { MarketingHeaderMobile } from "./components/organisms/MarketingHeaderMob
25
26
  import { MarketingLayout } from "./components/organisms/MarketingLayout.js";
26
27
  import { MarketingSection } from "./components/marketing/MarketingSection.js";
27
28
  import { MarketingCardsSection } from "./components/marketing/MarketingCardsSection.js";
28
- import { CardContent, CardDescription, CardHeader, CardTitle, MarketingCard } from "./components/marketing/MarketingCard.js";
29
+ import { MarketingCard } from "./components/marketing/MarketingCard.js";
29
30
  import { MarketingIconCard } from "./components/marketing/MarketingIconCard.js";
30
31
  import { MarketingStepCard } from "./components/marketing/MarketingStepCard.js";
31
32
  import { MarketingComparisonSection } from "./components/marketing/MarketingComparisonSection.js";
@@ -1,11 +1,11 @@
1
1
  'use client';
2
2
 
3
- import * as React from "react";
3
+ import * as React$1 from "react";
4
4
 
5
5
  //#region src/platform/useColorScheme.ts
6
6
  function useColorScheme() {
7
- const [scheme, setScheme] = React.useState("light");
8
- React.useEffect(() => {
7
+ const [scheme, setScheme] = React$1.useState("light");
8
+ React$1.useEffect(() => {
9
9
  if (typeof window === "undefined" || !window.matchMedia) return;
10
10
  const media = window.matchMedia("(prefers-color-scheme: dark)");
11
11
  const update = () => setScheme(media.matches ? "dark" : "light");
@@ -1,11 +1,11 @@
1
1
  'use client';
2
2
 
3
- import * as React from "react";
3
+ import * as React$1 from "react";
4
4
 
5
5
  //#region src/platform/useReducedMotion.ts
6
6
  function useReducedMotion() {
7
- const [reduced, setReduced] = React.useState(false);
8
- React.useEffect(() => {
7
+ const [reduced, setReduced] = React$1.useState(false);
8
+ React$1.useEffect(() => {
9
9
  if (typeof window === "undefined" || !window.matchMedia) return;
10
10
  const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
11
11
  const onChange = () => setReduced(mq.matches);
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import * as React from "react";
3
+ import * as React$1 from "react";
4
4
 
5
5
  //#region src/platform/useResponsive.ts
6
6
  const DEFAULT = {
@@ -10,12 +10,12 @@ const DEFAULT = {
10
10
  xl: 1280
11
11
  };
12
12
  function useResponsive(custom) {
13
- const bp = React.useMemo(() => ({
13
+ const bp = React$1.useMemo(() => ({
14
14
  ...DEFAULT,
15
15
  ...custom
16
16
  }), [custom]);
17
- const [width, setWidth] = React.useState(typeof window !== "undefined" ? window.innerWidth : bp.md);
18
- React.useEffect(() => {
17
+ const [width, setWidth] = React$1.useState(typeof window !== "undefined" ? window.innerWidth : bp.md);
18
+ React$1.useEffect(() => {
19
19
  if (typeof window === "undefined") return;
20
20
  const onResize = () => setWidth(window.innerWidth);
21
21
  window.addEventListener("resize", onResize);
@@ -1,7 +1,7 @@
1
- import * as _lssm_lib_contracts_client_react_form_render0 from "@lssm/lib.contracts/client/react/form-render";
2
1
  import * as react_jsx_runtime108 from "react/jsx-runtime";
3
2
  import * as _lssm_lib_schema0 from "@lssm/lib.schema";
4
3
  import * as _lssm_lib_contracts_forms0 from "@lssm/lib.contracts/forms";
4
+ import * as _lssm_lib_contracts_client_react_form_render0 from "@lssm/lib.contracts/client/react/form-render";
5
5
 
6
6
  //#region src/renderers/form-contract.d.ts
7
7
  declare const formRenderer: {
@@ -1,18 +1,18 @@
1
1
  'use client';
2
2
 
3
+ import { createFormRenderer } from "../contracts/dist/client/react/form-render.js";
4
+ import { shadcnDriver } from "../contracts/dist/client/react/drivers/shadcn.js";
5
+ import { Label } from "../ui-kit-web/dist/ui/label.js";
6
+ import { Field, FieldDescription, FieldError, FieldGroup, FieldLabel } from "../ui-kit-web/dist/ui/field.js";
3
7
  import { Button } from "../components/atoms/Button.js";
4
8
  import { Input } from "../components/atoms/Input.js";
5
9
  import { Textarea } from "../components/atoms/Textarea.js";
6
- import "react";
7
- import { createFormRenderer } from "@lssm/lib.contracts/client/react/form-render";
8
- import { shadcnDriver } from "@lssm/lib.contracts/client/react/drivers/shadcn";
9
- import { Field, FieldDescription, FieldError, FieldGroup, FieldLabel } from "@lssm/lib.ui-kit-web/ui/field";
10
+ import { Switch } from "../ui-kit-web/dist/ui/switch.js";
11
+ import { RadioGroup, RadioGroupItem } from "../ui-kit-web/dist/ui/radio-group.js";
12
+ import { Checkbox } from "../ui-kit-web/dist/ui/checkbox.js";
13
+ import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "../ui-kit-web/dist/ui/select.js";
14
+ import React from "react";
10
15
  import { jsx, jsxs } from "react/jsx-runtime";
11
- import { Switch } from "@lssm/lib.ui-kit-web/ui/switch";
12
- import { RadioGroup, RadioGroupItem } from "@lssm/lib.ui-kit-web/ui/radio-group";
13
- import { Checkbox } from "@lssm/lib.ui-kit-web/ui/checkbox";
14
- import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@lssm/lib.ui-kit-web/ui/select";
15
- import { Label } from "@lssm/lib.ui-kit-web/ui/label";
16
16
 
17
17
  //#region src/renderers/form-contract.tsx
18
18
  const Select$1 = (props) => {
@@ -1,40 +1,40 @@
1
- import * as React from "react";
1
+ import * as React$1 from "react";
2
2
 
3
3
  //#region src/types/navigation.d.ts
4
4
  interface NavLink {
5
- label: React.ReactNode;
5
+ label: React$1.ReactNode;
6
6
  href: string;
7
- icon?: React.ReactNode;
7
+ icon?: React$1.ReactNode;
8
8
  badge?: string | number;
9
9
  target?: '_self' | '_blank';
10
10
  ariaLabel?: string;
11
11
  external?: boolean;
12
- description?: React.ReactNode;
12
+ description?: React$1.ReactNode;
13
13
  imageSrc?: string;
14
14
  imageAlt?: string;
15
15
  categories?: string[];
16
16
  }
17
17
  interface NavSection {
18
- title?: React.ReactNode;
18
+ title?: React$1.ReactNode;
19
19
  items: NavLink[];
20
20
  }
21
21
  interface UserMenuItem {
22
- label: React.ReactNode;
22
+ label: React$1.ReactNode;
23
23
  href?: string;
24
24
  onClick?: () => void;
25
- icon?: React.ReactNode;
25
+ icon?: React$1.ReactNode;
26
26
  danger?: boolean;
27
27
  }
28
28
  interface HeaderNavItem {
29
- label: React.ReactNode;
29
+ label: React$1.ReactNode;
30
30
  href?: string;
31
- description?: React.ReactNode;
31
+ description?: React$1.ReactNode;
32
32
  key?: string;
33
33
  items?: NavLink[];
34
34
  match?: 'exact' | 'startsWith';
35
35
  }
36
36
  interface CtaAction {
37
- label: React.ReactNode;
37
+ label: React$1.ReactNode;
38
38
  href?: string;
39
39
  onClick?: () => void;
40
40
  variant?: 'default' | 'secondary' | 'outline' | 'ghost' | 'destructive';