@jiwambe/components 0.2.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 (179) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +367 -0
  3. package/dist/client.d.ts +40 -0
  4. package/dist/client.d.ts.map +1 -0
  5. package/dist/client.js +46 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/components/Accordion/Accordion.d.ts +74 -0
  8. package/dist/components/Accordion/Accordion.d.ts.map +1 -0
  9. package/dist/components/Accordion/Accordion.js +297 -0
  10. package/dist/components/Accordion/Accordion.js.map +1 -0
  11. package/dist/components/Box/Box.d.ts +56 -0
  12. package/dist/components/Box/Box.d.ts.map +1 -0
  13. package/dist/components/Box/Box.js +51 -0
  14. package/dist/components/Box/Box.js.map +1 -0
  15. package/dist/components/Breadcrumb/Breadcrumb.d.ts +66 -0
  16. package/dist/components/Breadcrumb/Breadcrumb.d.ts.map +1 -0
  17. package/dist/components/Button/Button.d.ts +54 -0
  18. package/dist/components/Button/Button.d.ts.map +1 -0
  19. package/dist/components/Button/Button.js +92 -0
  20. package/dist/components/Button/Button.js.map +1 -0
  21. package/dist/components/Card/Card.d.ts +54 -0
  22. package/dist/components/Card/Card.d.ts.map +1 -0
  23. package/dist/components/Card/Card.js +98 -0
  24. package/dist/components/Card/Card.js.map +1 -0
  25. package/dist/components/CheckboxGroup/CheckboxGroup.d.ts +61 -0
  26. package/dist/components/CheckboxGroup/CheckboxGroup.d.ts.map +1 -0
  27. package/dist/components/CheckboxGroup/CheckboxGroup.js +205 -0
  28. package/dist/components/CheckboxGroup/CheckboxGroup.js.map +1 -0
  29. package/dist/components/Container/Container.d.ts +72 -0
  30. package/dist/components/Container/Container.d.ts.map +1 -0
  31. package/dist/components/Container/Container.js +69 -0
  32. package/dist/components/Container/Container.js.map +1 -0
  33. package/dist/components/DateInput/DateInput.d.ts +61 -0
  34. package/dist/components/DateInput/DateInput.d.ts.map +1 -0
  35. package/dist/components/DateInput/DateInput.js +234 -0
  36. package/dist/components/DateInput/DateInput.js.map +1 -0
  37. package/dist/components/Divider/Divider.d.ts +44 -0
  38. package/dist/components/Divider/Divider.d.ts.map +1 -0
  39. package/dist/components/Divider/Divider.js +35 -0
  40. package/dist/components/Divider/Divider.js.map +1 -0
  41. package/dist/components/Drawer/Drawer.d.ts +35 -0
  42. package/dist/components/Drawer/Drawer.d.ts.map +1 -0
  43. package/dist/components/Drawer/Drawer.js +37 -0
  44. package/dist/components/Drawer/Drawer.js.map +1 -0
  45. package/dist/components/FAQ/FAQ.d.ts +40 -0
  46. package/dist/components/FAQ/FAQ.d.ts.map +1 -0
  47. package/dist/components/FAQ/FAQ.js +161 -0
  48. package/dist/components/FAQ/FAQ.js.map +1 -0
  49. package/dist/components/Grid/Grid.d.ts +61 -0
  50. package/dist/components/Grid/Grid.d.ts.map +1 -0
  51. package/dist/components/Grid/Grid.js +95 -0
  52. package/dist/components/Grid/Grid.js.map +1 -0
  53. package/dist/components/Icon/Icon.d.ts +21 -0
  54. package/dist/components/Icon/Icon.d.ts.map +1 -0
  55. package/dist/components/Icon/Icon.js +167 -0
  56. package/dist/components/Icon/Icon.js.map +1 -0
  57. package/dist/components/Link/Link.d.ts +49 -0
  58. package/dist/components/Link/Link.d.ts.map +1 -0
  59. package/dist/components/Link/Link.js +70 -0
  60. package/dist/components/Link/Link.js.map +1 -0
  61. package/dist/components/List/List.d.ts +36 -0
  62. package/dist/components/List/List.d.ts.map +1 -0
  63. package/dist/components/List/List.js +42 -0
  64. package/dist/components/List/List.js.map +1 -0
  65. package/dist/components/List/index.d.ts +3 -0
  66. package/dist/components/List/index.d.ts.map +1 -0
  67. package/dist/components/Overlay/Overlay.d.ts +35 -0
  68. package/dist/components/Overlay/Overlay.d.ts.map +1 -0
  69. package/dist/components/Overlay/Overlay.js +51 -0
  70. package/dist/components/Overlay/Overlay.js.map +1 -0
  71. package/dist/components/PhoneInput/PhoneInput.d.ts +55 -0
  72. package/dist/components/PhoneInput/PhoneInput.d.ts.map +1 -0
  73. package/dist/components/PhoneInput/PhoneInput.js +255 -0
  74. package/dist/components/PhoneInput/PhoneInput.js.map +1 -0
  75. package/dist/components/Popover/Popover.d.ts +46 -0
  76. package/dist/components/Popover/Popover.d.ts.map +1 -0
  77. package/dist/components/Popover/Popover.js +57 -0
  78. package/dist/components/Popover/Popover.js.map +1 -0
  79. package/dist/components/ProductImage/ProductImage.d.ts +78 -0
  80. package/dist/components/ProductImage/ProductImage.d.ts.map +1 -0
  81. package/dist/components/ProductImage/ProductImage.js +220 -0
  82. package/dist/components/ProductImage/ProductImage.js.map +1 -0
  83. package/dist/components/RadioGroup/RadioGroup.d.ts +63 -0
  84. package/dist/components/RadioGroup/RadioGroup.d.ts.map +1 -0
  85. package/dist/components/RadioGroup/RadioGroup.js +233 -0
  86. package/dist/components/RadioGroup/RadioGroup.js.map +1 -0
  87. package/dist/components/Section/Section.d.ts +44 -0
  88. package/dist/components/Section/Section.d.ts.map +1 -0
  89. package/dist/components/Section/Section.js +48 -0
  90. package/dist/components/Section/Section.js.map +1 -0
  91. package/dist/components/Select/Select.d.ts +47 -0
  92. package/dist/components/Select/Select.d.ts.map +1 -0
  93. package/dist/components/Select/Select.js +153 -0
  94. package/dist/components/Select/Select.js.map +1 -0
  95. package/dist/components/SelectTab/SelectTab.d.ts +62 -0
  96. package/dist/components/SelectTab/SelectTab.d.ts.map +1 -0
  97. package/dist/components/SelectTab/SelectTab.js +192 -0
  98. package/dist/components/SelectTab/SelectTab.js.map +1 -0
  99. package/dist/components/Skeleton/Skeleton.d.ts +87 -0
  100. package/dist/components/Skeleton/Skeleton.d.ts.map +1 -0
  101. package/dist/components/Skeleton/Skeleton.js +97 -0
  102. package/dist/components/Skeleton/Skeleton.js.map +1 -0
  103. package/dist/components/Skeleton/index.d.ts +3 -0
  104. package/dist/components/Skeleton/index.d.ts.map +1 -0
  105. package/dist/components/Slider/Slider.d.ts +47 -0
  106. package/dist/components/Slider/Slider.d.ts.map +1 -0
  107. package/dist/components/Slider/Slider.js +147 -0
  108. package/dist/components/Slider/Slider.js.map +1 -0
  109. package/dist/components/Stack/Stack.d.ts +145 -0
  110. package/dist/components/Stack/Stack.d.ts.map +1 -0
  111. package/dist/components/Stack/Stack.js +80 -0
  112. package/dist/components/Stack/Stack.js.map +1 -0
  113. package/dist/components/Tab/Tab.d.ts +38 -0
  114. package/dist/components/Tab/Tab.d.ts.map +1 -0
  115. package/dist/components/Tab/Tab.js +146 -0
  116. package/dist/components/Tab/Tab.js.map +1 -0
  117. package/dist/components/TextArea/TextArea.d.ts +32 -0
  118. package/dist/components/TextArea/TextArea.d.ts.map +1 -0
  119. package/dist/components/TextArea/TextArea.js +118 -0
  120. package/dist/components/TextArea/TextArea.js.map +1 -0
  121. package/dist/components/TextInput/TextInput.d.ts +35 -0
  122. package/dist/components/TextInput/TextInput.d.ts.map +1 -0
  123. package/dist/components/TextInput/TextInput.js +128 -0
  124. package/dist/components/TextInput/TextInput.js.map +1 -0
  125. package/dist/components/Toggle/Toggle.d.ts +83 -0
  126. package/dist/components/Toggle/Toggle.d.ts.map +1 -0
  127. package/dist/components/Toggle/Toggle.js +121 -0
  128. package/dist/components/Toggle/Toggle.js.map +1 -0
  129. package/dist/components/Typography/Typography.d.ts +321 -0
  130. package/dist/components/Typography/Typography.d.ts.map +1 -0
  131. package/dist/components/Typography/Typography.js +21 -0
  132. package/dist/components/Typography/Typography.js.map +1 -0
  133. package/dist/components/UploadInput/UploadInput.d.ts +39 -0
  134. package/dist/components/UploadInput/UploadInput.d.ts.map +1 -0
  135. package/dist/components/UploadInput/UploadInput.js +297 -0
  136. package/dist/components/UploadInput/UploadInput.js.map +1 -0
  137. package/dist/components/index.d.ts +65 -0
  138. package/dist/components/index.d.ts.map +1 -0
  139. package/dist/index.d.ts +7 -0
  140. package/dist/index.d.ts.map +1 -0
  141. package/dist/index.js +69 -0
  142. package/dist/index.js.map +1 -0
  143. package/dist/plugin/jiwambe-plugin.d.ts +37 -0
  144. package/dist/plugin/jiwambe-plugin.d.ts.map +1 -0
  145. package/dist/plugin/jiwambe-plugin.js +640 -0
  146. package/dist/plugin/jiwambe-plugin.js.map +1 -0
  147. package/dist/server.d.ts +22 -0
  148. package/dist/server.d.ts.map +1 -0
  149. package/dist/server.js +23 -0
  150. package/dist/server.js.map +1 -0
  151. package/dist/types/index.d.ts +103 -0
  152. package/dist/types/index.d.ts.map +1 -0
  153. package/dist/types/layout.d.ts +138 -0
  154. package/dist/types/layout.d.ts.map +1 -0
  155. package/dist/types/list.d.ts +69 -0
  156. package/dist/types/list.d.ts.map +1 -0
  157. package/dist/types/list.js +9 -0
  158. package/dist/types/list.js.map +1 -0
  159. package/dist/types/skeleton.d.ts +38 -0
  160. package/dist/types/skeleton.d.ts.map +1 -0
  161. package/dist/types/skeleton.js +13 -0
  162. package/dist/types/skeleton.js.map +1 -0
  163. package/dist/types/spacing.d.ts +105 -0
  164. package/dist/types/spacing.d.ts.map +1 -0
  165. package/dist/utils/layoutClasses.d.ts +44 -0
  166. package/dist/utils/layoutClasses.d.ts.map +1 -0
  167. package/dist/utils/layoutClasses.js +88 -0
  168. package/dist/utils/layoutClasses.js.map +1 -0
  169. package/dist/utils/responsive-props.d.ts +60 -0
  170. package/dist/utils/responsive-props.d.ts.map +1 -0
  171. package/dist/utils/responsive-props.js +184 -0
  172. package/dist/utils/responsive-props.js.map +1 -0
  173. package/dist/utils/spacing.d.ts +52 -0
  174. package/dist/utils/spacing.d.ts.map +1 -0
  175. package/dist/utils/spacing.js +625 -0
  176. package/dist/utils/spacing.js.map +1 -0
  177. package/package.json +96 -0
  178. package/tailwind.preset.d.ts +3 -0
  179. package/tailwind.preset.ts +21 -0
@@ -0,0 +1,92 @@
1
+ "use client";
2
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
3
+ const variantClasses = {
4
+ primary: [
5
+ "bg-fill-action-primary text-text-action-primary",
6
+ "hover:bg-fill-action-primary-hover",
7
+ "active:bg-fill-action-primary-active",
8
+ "disabled:bg-fill-action-primary-disabled disabled:text-text-action-primary-disabled disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none"
9
+ ].join(" "),
10
+ secondary: [
11
+ "bg-fill-action-secondary text-text-action-secondary",
12
+ "hover:bg-fill-action-secondary-hover",
13
+ "active:bg-fill-action-secondary-active",
14
+ "disabled:bg-fill-action-secondary-disabled disabled:text-text-action-secondary-disabled disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none"
15
+ ].join(" "),
16
+ inverse: [
17
+ "bg-fill-action-inverse text-text-action-secondary font-secondary",
18
+ "hover:bg-fill-action-inverse-hover",
19
+ "active:bg-fill-action-inverse-active",
20
+ "disabled:bg-fill-action-inverse-disabled disabled:text-text-action-secondary-disabled disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none"
21
+ ].join(" "),
22
+ ghost: [
23
+ "bg-fill-action-ghost text-text-action-tertiary",
24
+ "hover:bg-fill-action-ghost-hover",
25
+ "active:bg-fill-action-ghost-active",
26
+ "disabled:bg-fill-action-ghost-disabled disabled:text-text-action-tertiary-disabled disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none"
27
+ ].join(" ")
28
+ };
29
+ const sizeClasses = {
30
+ small: {
31
+ text: "h-[40px] px-4 py-0 gap-2 rounded-rad-sm text-btn-small",
32
+ iconOnly: "h-[40px] w-[40px] p-0 rounded-rad-sm",
33
+ icon: "w-4 h-4"
34
+ },
35
+ large: {
36
+ text: "h-[48px] px-5 py-0 gap-2 rounded-rad-md text-btn-reg",
37
+ iconOnly: "h-[48px] w-[48px] p-0 rounded-rad-md",
38
+ icon: "w-5 h-5"
39
+ }
40
+ };
41
+ function Button({
42
+ variant,
43
+ size = "small",
44
+ iconLeft,
45
+ iconRight,
46
+ iconOnly,
47
+ disabled = false,
48
+ children,
49
+ className = "",
50
+ style,
51
+ ref,
52
+ ...rest
53
+ }) {
54
+ const isIconOnly = !!iconOnly;
55
+ const config = sizeClasses[size];
56
+ const buttonClasses = [
57
+ "peer",
58
+ "inline-flex items-center justify-center whitespace-nowrap transition-colors duration-150 outline-none border-none",
59
+ variantClasses[variant],
60
+ isIconOnly ? config.iconOnly : config.text,
61
+ className
62
+ ].filter(Boolean).join(" ");
63
+ return /* @__PURE__ */ jsxs("div", { className: "relative inline-flex", children: [
64
+ /* @__PURE__ */ jsx(
65
+ "button",
66
+ {
67
+ ref,
68
+ className: buttonClasses,
69
+ style,
70
+ disabled,
71
+ "aria-disabled": disabled || void 0,
72
+ ...rest,
73
+ children: isIconOnly ? /* @__PURE__ */ jsx("span", { className: `${config.icon} inline-flex items-center justify-center`, children: iconOnly }) : /* @__PURE__ */ jsxs(Fragment, { children: [
74
+ iconLeft && /* @__PURE__ */ jsx("span", { className: `${config.icon} inline-flex items-center justify-center`, children: iconLeft }),
75
+ children && /* @__PURE__ */ jsx("span", { children }),
76
+ iconRight && /* @__PURE__ */ jsx("span", { className: `${config.icon} inline-flex items-center justify-center`, children: iconRight })
77
+ ] })
78
+ }
79
+ ),
80
+ /* @__PURE__ */ jsx(
81
+ "div",
82
+ {
83
+ className: "absolute -left-1 -top-1 w-[calc(100%+8px)] h-[calc(100%+8px)] rounded-rad-md border-border-md border-border-focus pointer-events-none hidden peer-focus-visible:block",
84
+ "aria-hidden": "true"
85
+ }
86
+ )
87
+ ] });
88
+ }
89
+ export {
90
+ Button
91
+ };
92
+ //# sourceMappingURL=Button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Button.js","sources":["../../../src/components/Button/Button.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\nimport type { ButtonVariant, ButtonSize } from '../../types';\n\n/**\n * Props for the Button component. Extends native button HTML attributes.\n */\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Visual style variant (primary, secondary, inverse, ghost). */\n variant: ButtonVariant;\n /**\n * Button size. Affects height, padding, and typography.\n * @default 'small'\n */\n size?: ButtonSize;\n /** Icon or element rendered before the button text. @default undefined */\n iconLeft?: React.ReactNode;\n /** Icon or element rendered after the button text. @default undefined */\n iconRight?: React.ReactNode;\n /**\n * When provided, renders a square icon-only button with no text.\n * You must provide aria-label for accessibility.\n * @default undefined\n * @see aria-label\n */\n iconOnly?: React.ReactNode;\n /** When true, disables the button and applies disabled styling. @default false */\n disabled?: boolean;\n /** Button label or content. Omit when using iconOnly. @default undefined */\n children?: React.ReactNode;\n /** Additional CSS class names. Merged with variant/size classes. @default undefined */\n className?: string;\n /** Inline styles. @default undefined */\n style?: React.CSSProperties;\n /**\n * Accessible label. Required when iconOnly is set; recommended for icon buttons.\n * @default undefined\n * @see iconOnly\n */\n 'aria-label'?: string;\n /** Forwarded ref for the button element. @default undefined */\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n// ---------------------------------------------------------------------------\n// Variant classes (default, hover, active, disabled)\n// ---------------------------------------------------------------------------\n\nconst variantClasses: Record<ButtonVariant, string> = {\n primary: [\n 'bg-fill-action-primary text-text-action-primary',\n 'hover:bg-fill-action-primary-hover',\n 'active:bg-fill-action-primary-active',\n 'disabled:bg-fill-action-primary-disabled disabled:text-text-action-primary-disabled disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none',\n ].join(' '),\n\n secondary: [\n 'bg-fill-action-secondary text-text-action-secondary',\n 'hover:bg-fill-action-secondary-hover',\n 'active:bg-fill-action-secondary-active',\n 'disabled:bg-fill-action-secondary-disabled disabled:text-text-action-secondary-disabled disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none',\n ].join(' '),\n\n inverse: [\n 'bg-fill-action-inverse text-text-action-secondary font-secondary',\n 'hover:bg-fill-action-inverse-hover',\n 'active:bg-fill-action-inverse-active',\n 'disabled:bg-fill-action-inverse-disabled disabled:text-text-action-secondary-disabled disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none',\n ].join(' '),\n\n ghost: [\n 'bg-fill-action-ghost text-text-action-tertiary',\n 'hover:bg-fill-action-ghost-hover',\n 'active:bg-fill-action-ghost-active',\n 'disabled:bg-fill-action-ghost-disabled disabled:text-text-action-tertiary-disabled disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none',\n ].join(' '),\n};\n\n// ---------------------------------------------------------------------------\n// Size classes\n// ---------------------------------------------------------------------------\n\ninterface SizeConfig {\n /** Classes for buttons with text (and optional icons). */\n text: string;\n /** Classes for icon-only buttons. */\n iconOnly: string;\n /** Icon dimension classes. */\n icon: string;\n}\n\nconst sizeClasses: Record<ButtonSize, SizeConfig> = {\n small: {\n text: 'h-[40px] px-4 py-0 gap-2 rounded-rad-sm text-btn-small',\n iconOnly: 'h-[40px] w-[40px] p-0 rounded-rad-sm',\n icon: 'w-4 h-4',\n },\n large: {\n text: 'h-[48px] px-5 py-0 gap-2 rounded-rad-md text-btn-reg',\n iconOnly: 'h-[48px] w-[48px] p-0 rounded-rad-md',\n icon: 'w-5 h-5',\n },\n};\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\n/**\n * Button component with design-system variants, sizes, icon support, and\n * an accessible focus ring that appears only on keyboard navigation.\n *\n * @example\n * ```tsx\n * <Button variant=\"primary\" size=\"large\">Click Me</Button>\n * <Button variant=\"secondary\" iconLeft={<SearchIcon />}>Search</Button>\n * <Button variant=\"ghost\" iconOnly={<MenuIcon />} aria-label=\"Open menu\" />\n * ```\n */\nexport function Button({\n variant,\n size = 'small',\n iconLeft,\n iconRight,\n iconOnly,\n disabled = false,\n children,\n className = '',\n style,\n ref,\n ...rest\n}: ButtonProps) {\n const isIconOnly = !!iconOnly;\n const config = sizeClasses[size];\n\n const buttonClasses = [\n 'peer',\n 'inline-flex items-center justify-center whitespace-nowrap transition-colors duration-150 outline-none border-none',\n variantClasses[variant],\n isIconOnly ? config.iconOnly : config.text,\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <div className=\"relative inline-flex\">\n {/* Button element */}\n <button\n ref={ref}\n className={buttonClasses}\n style={style}\n disabled={disabled}\n aria-disabled={disabled || undefined}\n {...rest}\n >\n {isIconOnly ? (\n <span className={`${config.icon} inline-flex items-center justify-center`}>\n {iconOnly}\n </span>\n ) : (\n <>\n {iconLeft && (\n <span className={`${config.icon} inline-flex items-center justify-center`}>\n {iconLeft}\n </span>\n )}\n {children && <span>{children}</span>}\n {iconRight && (\n <span className={`${config.icon} inline-flex items-center justify-center`}>\n {iconRight}\n </span>\n )}\n </>\n )}\n </button>\n\n {/* Focus ring – only visible on keyboard focus (:focus-visible) */}\n <div\n className=\"absolute -left-1 -top-1 w-[calc(100%+8px)] h-[calc(100%+8px)] rounded-rad-md border-border-md border-border-focus pointer-events-none hidden peer-focus-visible:block\"\n aria-hidden=\"true\"\n />\n </div>\n );\n}\n"],"names":[],"mappings":";;AAiDA;AAAsD;AAC3C;AACP;AACA;AACA;AACA;AACQ;AAEC;AACT;AACA;AACA;AACA;AACQ;AAED;AACP;AACA;AACA;AACA;AACQ;AAEH;AACL;AACA;AACA;AACA;AAEJ;AAeA;AAAoD;AAC3C;AACC;AACI;AACJ;AAAA;AAED;AACC;AACI;AACJ;AAEV;AAiBO;AAAgB;AACrB;AACO;AACP;AACA;AACA;AACW;AACX;AACY;AACZ;AACA;AAEF;AACE;AACA;AAEA;AAAsB;AACpB;AACA;AACsB;AACgB;AACtC;AAKF;AAGI;AAAA;AAAC;AAAA;AACC;AACW;AACX;AACA;AAC2B;AACvB;AAQC;AAGC;AAE2B;AAI3B;AAEJ;AAAA;AAAA;AAKJ;AAAC;AAAA;AACW;AACE;AAAA;AAAA;AAIpB;;;;"}
@@ -0,0 +1,54 @@
1
+ import { default as React } from 'react';
2
+ /**
3
+ * Card layout type. 'text-only' — no image; 'media-horizontal' — image above content, video aspect;
4
+ * 'media-vertical' — image above content, 4:3 aspect.
5
+ */
6
+ export type CardType = 'text-only' | 'media-horizontal' | 'media-vertical';
7
+ /**
8
+ * Props for the Card component. Title is required; image and action are optional.
9
+ */
10
+ export interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
11
+ /** Layout: text-only, or media with horizontal/vertical image aspect. @default 'text-only' */
12
+ type?: CardType;
13
+ /** Image source URL. Required when type is media-horizontal or media-vertical. @default undefined */
14
+ imageSrc?: string;
15
+ /** Image alt text for accessibility. @default '' */
16
+ imageAlt?: string;
17
+ /** Width for next/image. @default 600 */
18
+ imageWidth?: number;
19
+ /** Height for next/image. @default 400 */
20
+ imageHeight?: number;
21
+ /** Next.js image quality (1-100). @default undefined */
22
+ imageQuality?: number;
23
+ /** Bypass Next.js image optimization. Useful for small sharp PNGs. @default false */
24
+ imageUnoptimized?: boolean;
25
+ /** Overlay message displayed centered on the image. @default undefined */
26
+ message?: string;
27
+ /** Card title (required). Rendered as heading. */
28
+ title: string;
29
+ /** Card description text below the title. @default undefined */
30
+ description?: string;
31
+ /** Label for the action button or link. @default undefined */
32
+ label?: string;
33
+ /** Called when the action button is clicked (when labelHref is not set). @default undefined */
34
+ onLabelClick?: () => void;
35
+ /** When set, the action renders as a link to this href. @default undefined */
36
+ labelHref?: string;
37
+ /** Component to render the action link as (e.g. next/link). @default 'a' */
38
+ linkAs?: React.ElementType;
39
+ /** Forwarded ref for the root div. @default undefined */
40
+ ref?: React.Ref<HTMLDivElement>;
41
+ }
42
+ /**
43
+ * Card with optional image, title, description, and action. Renders a bordered container
44
+ * with design-token styling. Use for product cards, feature highlights, or content blocks.
45
+ * For image-only or custom layouts use Box or a custom component.
46
+ *
47
+ * @example
48
+ * <Card title="Feature" description="Description here." type="text-only" />
49
+ *
50
+ * @example
51
+ * <Card type="media-horizontal" imageSrc="/img.jpg" imageAlt="Product" title="Product" label="View" labelHref="/product" />
52
+ */
53
+ export declare function Card({ type, imageSrc, imageAlt, imageWidth, imageHeight, imageQuality, imageUnoptimized, message, title, description, label, onLabelClick, labelHref, linkAs, className, ref, ...rest }: CardProps): import("react/jsx-runtime").JSX.Element;
54
+ //# sourceMappingURL=Card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Card.d.ts","sourceRoot":"","sources":["../../../src/components/Card/Card.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,kBAAkB,GAAG,gBAAgB,CAAC;AAE3E;;GAEG;AACH,MAAM,WAAW,SAAU,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IACrE,8FAA8F;IAC9F,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,qGAAqG;IACrG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+FAA+F;IAC/F,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC;IAC3B,yDAAyD;IACzD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;CACjC;AAmFD;;;;;;;;;;GAUG;AACH,wBAAgB,IAAI,CAAC,EACnB,IAAkB,EAClB,QAAQ,EACR,QAAa,EACb,UAAgB,EAChB,WAAiB,EACjB,YAAY,EACZ,gBAAgB,EAChB,OAAO,EACP,KAAK,EACL,WAAW,EACX,KAAK,EACL,YAAY,EACZ,SAAS,EACT,MAAM,EACN,SAAc,EACd,GAAG,EACH,GAAG,IAAI,EACR,EAAE,SAAS,2CAuCX"}
@@ -0,0 +1,98 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import Image from "next/image";
3
+ function CardImage({
4
+ src,
5
+ alt,
6
+ ratio,
7
+ message,
8
+ width,
9
+ height,
10
+ quality,
11
+ unoptimized
12
+ }) {
13
+ const aspectClass = ratio === "horizontal" ? "aspect-video" : "aspect-[4/3]";
14
+ return /* @__PURE__ */ jsxs("div", { className: `relative w-full overflow-hidden rounded-t-rad-md ${aspectClass}`, children: [
15
+ /* @__PURE__ */ jsx(
16
+ Image,
17
+ {
18
+ src,
19
+ alt,
20
+ width,
21
+ height,
22
+ quality,
23
+ unoptimized,
24
+ className: "h-full w-full object-cover"
25
+ }
26
+ ),
27
+ message && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-green-950/50", children: /* @__PURE__ */ jsx("span", { className: "text-title-md text-neutral-0 text-center px-space-4", children: message }) })
28
+ ] });
29
+ }
30
+ function CardAction({
31
+ label,
32
+ onClick,
33
+ href,
34
+ linkAs
35
+ }) {
36
+ const classes = "inline-flex items-center justify-center rounded-rad-sm px-space-4 py-space-2 text-btn-small bg-fill-action-primary text-text-action-primary hover:bg-fill-action-primary-hover active:bg-fill-action-primary-active transition-colors";
37
+ if (href) {
38
+ const LinkComponent = linkAs || "a";
39
+ return /* @__PURE__ */ jsx(LinkComponent, { href, className: classes, children: label });
40
+ }
41
+ return /* @__PURE__ */ jsx("button", { type: "button", onClick, className: classes, children: label });
42
+ }
43
+ function Card({
44
+ type = "text-only",
45
+ imageSrc,
46
+ imageAlt = "",
47
+ imageWidth = 600,
48
+ imageHeight = 400,
49
+ imageQuality,
50
+ imageUnoptimized,
51
+ message,
52
+ title,
53
+ description,
54
+ label,
55
+ onLabelClick,
56
+ labelHref,
57
+ linkAs,
58
+ className = "",
59
+ ref,
60
+ ...rest
61
+ }) {
62
+ const isMedia = type !== "text-only";
63
+ const ratio = type === "media-vertical" ? "vertical" : "horizontal";
64
+ return /* @__PURE__ */ jsxs(
65
+ "div",
66
+ {
67
+ ref,
68
+ className: `flex flex-col rounded-rad-md border border-border-light bg-fill-bg-primary overflow-hidden ${className}`,
69
+ ...rest,
70
+ children: [
71
+ isMedia && imageSrc && /* @__PURE__ */ jsx(
72
+ CardImage,
73
+ {
74
+ src: imageSrc,
75
+ alt: imageAlt,
76
+ ratio,
77
+ message,
78
+ width: imageWidth,
79
+ height: imageHeight,
80
+ quality: imageQuality,
81
+ unoptimized: imageUnoptimized
82
+ }
83
+ ),
84
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-space-3 p-space-4", children: [
85
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-space-1", children: [
86
+ /* @__PURE__ */ jsx("h3", { className: "text-title-sm text-text-primary", children: title }),
87
+ description && /* @__PURE__ */ jsx("p", { className: "text-text-sm text-text-secondary", children: description })
88
+ ] }),
89
+ label && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(CardAction, { label, onClick: onLabelClick, href: labelHref, linkAs }) })
90
+ ] })
91
+ ]
92
+ }
93
+ );
94
+ }
95
+ export {
96
+ Card
97
+ };
98
+ //# sourceMappingURL=Card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Card.js","sources":["../../../src/components/Card/Card.tsx"],"sourcesContent":["import React from 'react';\nimport Image from 'next/image';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Card layout type. 'text-only' — no image; 'media-horizontal' — image above content, video aspect;\n * 'media-vertical' — image above content, 4:3 aspect.\n */\nexport type CardType = 'text-only' | 'media-horizontal' | 'media-vertical';\n\n/**\n * Props for the Card component. Title is required; image and action are optional.\n */\nexport interface CardProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Layout: text-only, or media with horizontal/vertical image aspect. @default 'text-only' */\n type?: CardType;\n /** Image source URL. Required when type is media-horizontal or media-vertical. @default undefined */\n imageSrc?: string;\n /** Image alt text for accessibility. @default '' */\n imageAlt?: string;\n /** Width for next/image. @default 600 */\n imageWidth?: number;\n /** Height for next/image. @default 400 */\n imageHeight?: number;\n /** Next.js image quality (1-100). @default undefined */\n imageQuality?: number;\n /** Bypass Next.js image optimization. Useful for small sharp PNGs. @default false */\n imageUnoptimized?: boolean;\n /** Overlay message displayed centered on the image. @default undefined */\n message?: string;\n /** Card title (required). Rendered as heading. */\n title: string;\n /** Card description text below the title. @default undefined */\n description?: string;\n /** Label for the action button or link. @default undefined */\n label?: string;\n /** Called when the action button is clicked (when labelHref is not set). @default undefined */\n onLabelClick?: () => void;\n /** When set, the action renders as a link to this href. @default undefined */\n labelHref?: string;\n /** Component to render the action link as (e.g. next/link). @default 'a' */\n linkAs?: React.ElementType;\n /** Forwarded ref for the root div. @default undefined */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n// ---------------------------------------------------------------------------\n// Sub-components\n// ---------------------------------------------------------------------------\n\nfunction CardImage({\n src,\n alt,\n ratio,\n message,\n width,\n height,\n quality,\n unoptimized,\n}: {\n src: string;\n alt: string;\n ratio: 'horizontal' | 'vertical';\n message?: string;\n width: number;\n height: number;\n quality?: number;\n unoptimized?: boolean;\n}) {\n const aspectClass = ratio === 'horizontal' ? 'aspect-video' : 'aspect-[4/3]';\n\n return (\n <div className={`relative w-full overflow-hidden rounded-t-rad-md ${aspectClass}`}>\n <Image\n src={src}\n alt={alt}\n width={width}\n height={height}\n quality={quality}\n unoptimized={unoptimized}\n className=\"h-full w-full object-cover\"\n />\n {message && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-green-950/50\">\n <span className=\"text-title-md text-neutral-0 text-center px-space-4\">\n {message}\n </span>\n </div>\n )}\n </div>\n );\n}\n\nfunction CardAction({\n label,\n onClick,\n href,\n linkAs,\n}: {\n label: string;\n onClick?: () => void;\n href?: string;\n linkAs?: React.ElementType;\n}) {\n const classes =\n 'inline-flex items-center justify-center rounded-rad-sm px-space-4 py-space-2 text-btn-small bg-fill-action-primary text-text-action-primary hover:bg-fill-action-primary-hover active:bg-fill-action-primary-active transition-colors';\n\n if (href) {\n const LinkComponent = linkAs || 'a';\n return (\n <LinkComponent href={href} className={classes}>\n {label}\n </LinkComponent>\n );\n }\n\n return (\n <button type=\"button\" onClick={onClick} className={classes}>\n {label}\n </button>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Card\n// ---------------------------------------------------------------------------\n\n/**\n * Card with optional image, title, description, and action. Renders a bordered container\n * with design-token styling. Use for product cards, feature highlights, or content blocks.\n * For image-only or custom layouts use Box or a custom component.\n *\n * @example\n * <Card title=\"Feature\" description=\"Description here.\" type=\"text-only\" />\n *\n * @example\n * <Card type=\"media-horizontal\" imageSrc=\"/img.jpg\" imageAlt=\"Product\" title=\"Product\" label=\"View\" labelHref=\"/product\" />\n */\nexport function Card({\n type = 'text-only',\n imageSrc,\n imageAlt = '',\n imageWidth = 600,\n imageHeight = 400,\n imageQuality,\n imageUnoptimized,\n message,\n title,\n description,\n label,\n onLabelClick,\n labelHref,\n linkAs,\n className = '',\n ref,\n ...rest\n}: CardProps) {\n const isMedia = type !== 'text-only';\n const ratio = type === 'media-vertical' ? 'vertical' : 'horizontal';\n\n return (\n <div\n ref={ref}\n className={`flex flex-col rounded-rad-md border border-border-light bg-fill-bg-primary overflow-hidden ${className}`}\n {...rest}\n >\n {isMedia && imageSrc && (\n <CardImage\n src={imageSrc}\n alt={imageAlt}\n ratio={ratio}\n message={message}\n width={imageWidth}\n height={imageHeight}\n quality={imageQuality}\n unoptimized={imageUnoptimized}\n />\n )}\n\n <div className=\"flex flex-col gap-space-3 p-space-4\">\n <div className=\"flex flex-col gap-space-1\">\n <h3 className=\"text-title-sm text-text-primary\">{title}</h3>\n {description && (\n <p className=\"text-text-sm text-text-secondary\">{description}</p>\n )}\n </div>\n\n {label && (\n <div>\n <CardAction label={label} onClick={onLabelClick} href={labelHref} linkAs={linkAs} />\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"names":[],"mappings":";;AAqDA,SAAS,UAAU;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GASG;AACD,QAAM,cAAc,UAAU,eAAe,iBAAiB;AAE9D,SACE,qBAAC,OAAA,EAAI,WAAW,oDAAoD,WAAW,IAC7E,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAU;AAAA,MAAA;AAAA,IAAA;AAAA,IAEX,WACC,oBAAC,OAAA,EAAI,WAAU,qEACb,8BAAC,QAAA,EAAK,WAAU,uDACb,UAAA,QAAA,CACH,EAAA,CACF;AAAA,EAAA,GAEJ;AAEJ;AAEA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,UACJ;AAEF,MAAI,MAAM;AACR,UAAM,gBAAgB,UAAU;AAChC,WACE,oBAAC,eAAA,EAAc,MAAY,WAAW,SACnC,UAAA,OACH;AAAA,EAEJ;AAEA,6BACG,UAAA,EAAO,MAAK,UAAS,SAAkB,WAAW,SAChD,UAAA,OACH;AAEJ;AAiBO,SAAS,KAAK;AAAA,EACnB,OAAO;AAAA,EACP;AAAA,EACA,WAAW;AAAA,EACX,aAAa;AAAA,EACb,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA,GAAG;AACL,GAAc;AACZ,QAAM,UAAU,SAAS;AACzB,QAAM,QAAQ,SAAS,mBAAmB,aAAa;AAEvD,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,WAAW,8FAA8F,SAAS;AAAA,MACjH,GAAG;AAAA,MAEH,UAAA;AAAA,QAAA,WAAW,YACV;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,KAAK;AAAA,YACL,KAAK;AAAA,YACL;AAAA,YACA;AAAA,YACA,OAAO;AAAA,YACP,QAAQ;AAAA,YACR,SAAS;AAAA,YACT,aAAa;AAAA,UAAA;AAAA,QAAA;AAAA,QAIjB,qBAAC,OAAA,EAAI,WAAU,uCACb,UAAA;AAAA,UAAA,qBAAC,OAAA,EAAI,WAAU,6BACb,UAAA;AAAA,YAAA,oBAAC,MAAA,EAAG,WAAU,mCAAmC,UAAA,OAAM;AAAA,YACtD,eACC,oBAAC,KAAA,EAAE,WAAU,oCAAoC,UAAA,YAAA,CAAY;AAAA,UAAA,GAEjE;AAAA,UAEC,SACC,oBAAC,OAAA,EACC,UAAA,oBAAC,YAAA,EAAW,OAAc,SAAS,cAAc,MAAM,WAAW,OAAA,CAAgB,EAAA,CACpF;AAAA,QAAA,EAAA,CAEJ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGN;"}
@@ -0,0 +1,61 @@
1
+ import { default as React } from 'react';
2
+ /**
3
+ * Shape of a single checkbox option. Pass an array of these to CheckboxGroup's options prop.
4
+ */
5
+ export interface CheckboxOption {
6
+ /** Value submitted when this option is checked. Must be unique within the group. */
7
+ value: string;
8
+ /** Label shown next to the checkbox. */
9
+ label: string;
10
+ /** When true, this option cannot be toggled. @default undefined */
11
+ disabled?: boolean;
12
+ }
13
+ /**
14
+ * Props for the CheckboxGroup component. Supports controlled (value/onChange) or
15
+ * uncontrolled (defaultValue) usage. value and onChange work together for controlled mode.
16
+ */
17
+ export interface CheckboxGroupProps {
18
+ /** Main label above the group (legend). @default undefined */
19
+ label?: string;
20
+ /** Secondary label shown lighter beside the main label. @default undefined */
21
+ secondaryLabel?: string;
22
+ /** Supportive text below the label. @default undefined */
23
+ supportText?: string;
24
+ /** Name for the checkbox group; each input gets name={name}. @default derived from id */
25
+ name?: string;
26
+ /** Controlled selected values (array of option values). Use with onChange. @default undefined */
27
+ value?: string[];
28
+ /** Uncontrolled default selected values. @default undefined */
29
+ defaultValue?: string[];
30
+ /** Called when selection changes. Receives the new array of selected option values. @default undefined */
31
+ onChange?: (value: string[]) => void;
32
+ /** Options to render. Each option: value, label, disabled?. @see CheckboxOption */
33
+ options: CheckboxOption[];
34
+ /** Disables the entire group. @default false */
35
+ disabled?: boolean;
36
+ /** Shows error state (borders and label) and optional errorMessage. @default false */
37
+ error?: boolean;
38
+ /** Error message shown in the alert below the group when error is true. @default undefined */
39
+ errorMessage?: string;
40
+ /** Shows required asterisk next to the label. @default undefined */
41
+ required?: boolean;
42
+ /** Additional class name on the wrapper. @default undefined */
43
+ className?: string;
44
+ /** ID for the group (used for aria-describedby and generated input ids). @default undefined */
45
+ id?: string;
46
+ /** Forwarded ref for the fieldset. @default undefined */
47
+ ref?: React.Ref<HTMLFieldSetElement>;
48
+ }
49
+ /**
50
+ * Group of checkboxes with a shared label, support text, and optional error state.
51
+ * Renders a fieldset with design-token styling. Use for multi-select options (e.g. preferences,
52
+ * filters). For a single checkbox use a native input or a custom control.
53
+ *
54
+ * @example
55
+ * <CheckboxGroup label="Toppings" options={[{ value: 'a', label: 'Option A' }]} onChange={setValues} />
56
+ *
57
+ * @example
58
+ * <CheckboxGroup label="Choose" value={selected} onChange={setSelected} options={opts} error errorMessage="Required" />
59
+ */
60
+ export declare function CheckboxGroup({ label, secondaryLabel, supportText, name: nameProp, value: valueProp, defaultValue, onChange, options, disabled, error, errorMessage, required, className, id: idProp, ref, }: CheckboxGroupProps): import("react/jsx-runtime").JSX.Element;
61
+ //# sourceMappingURL=CheckboxGroup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CheckboxGroup.d.ts","sourceRoot":"","sources":["../../../src/components/CheckboxGroup/CheckboxGroup.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAgB,MAAM,OAAO,CAAC;AAMrC;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,oFAAoF;IACpF,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yFAAyF;IACzF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iGAAiG;IACjG,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,0GAA0G;IAC1G,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACrC,mFAAmF;IACnF,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,gDAAgD;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sFAAsF;IACtF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,8FAA8F;IAC9F,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+FAA+F;IAC/F,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,yDAAyD;IACzD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;CACtC;AAiID;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,cAAc,EACd,WAAW,EACX,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,SAAS,EAChB,YAAY,EACZ,QAAQ,EACR,OAAO,EACP,QAAgB,EAChB,KAAa,EACb,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,EAAE,EAAE,MAAM,EACV,GAAG,GACJ,EAAE,kBAAkB,2CAkGpB"}
@@ -0,0 +1,205 @@
1
+ "use client";
2
+ import { jsxs, jsx } from "react/jsx-runtime";
3
+ import React, { useId } from "react";
4
+ function CheckmarkIcon({ className }) {
5
+ return /* @__PURE__ */ jsx(
6
+ "svg",
7
+ {
8
+ className,
9
+ width: "16",
10
+ height: "16",
11
+ viewBox: "0 0 16 16",
12
+ fill: "none",
13
+ stroke: "currentColor",
14
+ strokeWidth: "1.5",
15
+ strokeLinecap: "round",
16
+ strokeLinejoin: "round",
17
+ "aria-hidden": "true",
18
+ children: /* @__PURE__ */ jsx("path", { d: "M13.3332 4L5.99984 11.3333L2.6665 8" })
19
+ }
20
+ );
21
+ }
22
+ function ErrorIcon({ className }) {
23
+ return /* @__PURE__ */ jsx(
24
+ "svg",
25
+ {
26
+ className,
27
+ viewBox: "0 0 20 20",
28
+ fill: "currentColor",
29
+ "aria-hidden": "true",
30
+ children: /* @__PURE__ */ jsx(
31
+ "path",
32
+ {
33
+ fillRule: "evenodd",
34
+ d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z",
35
+ clipRule: "evenodd"
36
+ }
37
+ )
38
+ }
39
+ );
40
+ }
41
+ function CheckboxItem({
42
+ name,
43
+ value,
44
+ checked,
45
+ disabled,
46
+ error,
47
+ label,
48
+ id,
49
+ onChange
50
+ }) {
51
+ const boxClasses = [
52
+ "pointer-events-none absolute inset-0 rounded-sm border-2 transition-all duration-150 flex items-center justify-center",
53
+ !disabled && !error && !checked && "border-border-form-primary bg-fill-bg-primary",
54
+ !disabled && !error && !checked && "group-hover:border-border-form-primary-hover",
55
+ !disabled && !error && !checked && "group-active:bg-neutral-100",
56
+ !disabled && error && !checked && "border-border-err bg-fill-bg-primary",
57
+ !disabled && checked && !error && "border-green-900 bg-green-900",
58
+ !disabled && checked && error && "border-border-err bg-fill-bg-primary",
59
+ disabled && !checked && "border-border-form-primary-disabled bg-fill-form-primary-disabled",
60
+ disabled && checked && "border-neutral-300 bg-neutral-300",
61
+ !disabled && "peer-focus-visible:ring-2 peer-focus-visible:ring-border-focus peer-focus-visible:ring-offset-2 peer-focus-visible:rounded-sm"
62
+ ].filter(Boolean).join(" ");
63
+ const labelClasses = [
64
+ "flex-1 text-text-sm cursor-pointer select-none",
65
+ disabled && "text-text-disabled",
66
+ !disabled && error && !checked && "text-text-err",
67
+ !disabled && checked && !error && "text-green-900",
68
+ !disabled && checked && error && "text-text-err"
69
+ ].filter(Boolean).join(" ");
70
+ return /* @__PURE__ */ jsxs(
71
+ "label",
72
+ {
73
+ htmlFor: id,
74
+ className: `group flex cursor-pointer items-center gap-space-2 ${disabled ? "cursor-not-allowed" : ""}`,
75
+ children: [
76
+ /* @__PURE__ */ jsxs("span", { className: "relative inline-flex h-5 w-5 shrink-0 items-center justify-center", children: [
77
+ /* @__PURE__ */ jsx(
78
+ "input",
79
+ {
80
+ type: "checkbox",
81
+ id,
82
+ name,
83
+ value,
84
+ checked,
85
+ disabled,
86
+ onChange: (e) => onChange(e.target.checked),
87
+ className: "peer sr-only",
88
+ "aria-invalid": error || void 0
89
+ }
90
+ ),
91
+ /* @__PURE__ */ jsx("span", { className: boxClasses, children: checked && /* @__PURE__ */ jsx(
92
+ CheckmarkIcon,
93
+ {
94
+ className: `h-4 w-4 shrink-0 aspect-square ${disabled ? "text-neutral-500" : error ? "text-green-900" : "text-neutral-0"}`
95
+ }
96
+ ) })
97
+ ] }),
98
+ /* @__PURE__ */ jsx("span", { className: labelClasses, children: label })
99
+ ]
100
+ }
101
+ );
102
+ }
103
+ function CheckboxGroup({
104
+ label,
105
+ secondaryLabel,
106
+ supportText,
107
+ name: nameProp,
108
+ value: valueProp,
109
+ defaultValue,
110
+ onChange,
111
+ options,
112
+ disabled = false,
113
+ error = false,
114
+ errorMessage,
115
+ required,
116
+ className,
117
+ id: idProp,
118
+ ref
119
+ }) {
120
+ const autoId = useId();
121
+ const baseId = idProp ?? autoId;
122
+ const name = nameProp ?? baseId;
123
+ const isControlled = valueProp !== void 0;
124
+ const [internal, setInternal] = React.useState(defaultValue ?? []);
125
+ const value = isControlled ? valueProp : internal;
126
+ const handleChange = (optionValue, checked) => {
127
+ const next = checked ? [...value, optionValue] : value.filter((v) => v !== optionValue);
128
+ if (!isControlled) setInternal(next);
129
+ onChange == null ? void 0 : onChange(next);
130
+ };
131
+ const showErrorAlert = error && errorMessage;
132
+ return /* @__PURE__ */ jsxs(
133
+ "fieldset",
134
+ {
135
+ ref,
136
+ disabled,
137
+ className: `flex flex-col gap-space-2 border-none p-0 ${className ?? ""}`,
138
+ "aria-invalid": error || void 0,
139
+ "aria-describedby": showErrorAlert ? `${baseId}-error` : void 0,
140
+ children: [
141
+ (label || supportText) && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-space-1", children: [
142
+ label && /* @__PURE__ */ jsxs(
143
+ "legend",
144
+ {
145
+ className: `text-form-label ${disabled ? "text-text-disabled" : "text-text-primary"}`,
146
+ children: [
147
+ label,
148
+ secondaryLabel && /* @__PURE__ */ jsx("span", { className: "ml-1 font-normal text-text-secondary", children: secondaryLabel }),
149
+ required && /* @__PURE__ */ jsx("span", { className: "ml-0.5 text-text-err", children: "*" })
150
+ ]
151
+ }
152
+ ),
153
+ supportText && /* @__PURE__ */ jsx(
154
+ "p",
155
+ {
156
+ className: `text-text-xs ${disabled ? "text-text-disabled" : "text-text-secondary"}`,
157
+ children: supportText
158
+ }
159
+ )
160
+ ] }),
161
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-space-3", children: options.map((opt, index) => /* @__PURE__ */ jsx(
162
+ CheckboxItem,
163
+ {
164
+ id: `${baseId}-${index}`,
165
+ name,
166
+ value: opt.value,
167
+ checked: value.includes(opt.value),
168
+ disabled: disabled || !!opt.disabled,
169
+ error,
170
+ label: opt.label,
171
+ onChange: (checked) => handleChange(opt.value, checked)
172
+ },
173
+ opt.value
174
+ )) }),
175
+ showErrorAlert && /* @__PURE__ */ jsxs(
176
+ "div",
177
+ {
178
+ id: `${baseId}-error`,
179
+ role: "alert",
180
+ className: `flex items-start gap-space-4 rounded-rad-md p-space-4 ${disabled ? "bg-fill-bg-secondary" : "bg-fill-bg-err"}`,
181
+ children: [
182
+ /* @__PURE__ */ jsx(
183
+ ErrorIcon,
184
+ {
185
+ className: `h-5 w-5 shrink-0 ${disabled ? "text-icon-primary-disabled" : "text-icon-err"}`
186
+ }
187
+ ),
188
+ /* @__PURE__ */ jsx(
189
+ "span",
190
+ {
191
+ className: `text-text-xs ${disabled ? "text-text-disabled" : "text-text-err"}`,
192
+ children: errorMessage
193
+ }
194
+ )
195
+ ]
196
+ }
197
+ )
198
+ ]
199
+ }
200
+ );
201
+ }
202
+ export {
203
+ CheckboxGroup
204
+ };
205
+ //# sourceMappingURL=CheckboxGroup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CheckboxGroup.js","sources":["../../../src/components/CheckboxGroup/CheckboxGroup.tsx"],"sourcesContent":["'use client';\n\nimport React, { useId } from 'react';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Shape of a single checkbox option. Pass an array of these to CheckboxGroup's options prop.\n */\nexport interface CheckboxOption {\n /** Value submitted when this option is checked. Must be unique within the group. */\n value: string;\n /** Label shown next to the checkbox. */\n label: string;\n /** When true, this option cannot be toggled. @default undefined */\n disabled?: boolean;\n}\n\n/**\n * Props for the CheckboxGroup component. Supports controlled (value/onChange) or\n * uncontrolled (defaultValue) usage. value and onChange work together for controlled mode.\n */\nexport interface CheckboxGroupProps {\n /** Main label above the group (legend). @default undefined */\n label?: string;\n /** Secondary label shown lighter beside the main label. @default undefined */\n secondaryLabel?: string;\n /** Supportive text below the label. @default undefined */\n supportText?: string;\n /** Name for the checkbox group; each input gets name={name}. @default derived from id */\n name?: string;\n /** Controlled selected values (array of option values). Use with onChange. @default undefined */\n value?: string[];\n /** Uncontrolled default selected values. @default undefined */\n defaultValue?: string[];\n /** Called when selection changes. Receives the new array of selected option values. @default undefined */\n onChange?: (value: string[]) => void;\n /** Options to render. Each option: value, label, disabled?. @see CheckboxOption */\n options: CheckboxOption[];\n /** Disables the entire group. @default false */\n disabled?: boolean;\n /** Shows error state (borders and label) and optional errorMessage. @default false */\n error?: boolean;\n /** Error message shown in the alert below the group when error is true. @default undefined */\n errorMessage?: string;\n /** Shows required asterisk next to the label. @default undefined */\n required?: boolean;\n /** Additional class name on the wrapper. @default undefined */\n className?: string;\n /** ID for the group (used for aria-describedby and generated input ids). @default undefined */\n id?: string;\n /** Forwarded ref for the fieldset. @default undefined */\n ref?: React.Ref<HTMLFieldSetElement>;\n}\n\n// ---------------------------------------------------------------------------\n// Icons\n// ---------------------------------------------------------------------------\n\nfunction CheckmarkIcon({ className }: { className?: string }) {\n return (\n <svg\n className={className}\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M13.3332 4L5.99984 11.3333L2.6665 8\" />\n </svg>\n );\n}\n\nfunction ErrorIcon({ className }: { className?: string }) {\n return (\n <svg\n className={className}\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z\"\n clipRule=\"evenodd\"\n />\n </svg>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Checkbox item (box + label)\n// ---------------------------------------------------------------------------\n\ninterface CheckboxItemProps {\n name: string;\n value: string;\n checked: boolean;\n disabled: boolean;\n error: boolean;\n label: string;\n id: string;\n onChange: (checked: boolean) => void;\n}\n\nfunction CheckboxItem({\n name,\n value,\n checked,\n disabled,\n error,\n label,\n id,\n onChange,\n}: CheckboxItemProps) {\n const boxClasses = [\n 'pointer-events-none absolute inset-0 rounded-sm border-2 transition-all duration-150 flex items-center justify-center',\n !disabled && !error && !checked && 'border-border-form-primary bg-fill-bg-primary',\n !disabled && !error && !checked && 'group-hover:border-border-form-primary-hover',\n !disabled && !error && !checked && 'group-active:bg-neutral-100',\n !disabled && error && !checked && 'border-border-err bg-fill-bg-primary',\n !disabled && checked && !error && 'border-green-900 bg-green-900',\n !disabled && checked && error && 'border-border-err bg-fill-bg-primary',\n disabled && !checked && 'border-border-form-primary-disabled bg-fill-form-primary-disabled',\n disabled && checked && 'border-neutral-300 bg-neutral-300',\n !disabled &&\n 'peer-focus-visible:ring-2 peer-focus-visible:ring-border-focus peer-focus-visible:ring-offset-2 peer-focus-visible:rounded-sm',\n ]\n .filter(Boolean)\n .join(' ');\n\n const labelClasses = [\n 'flex-1 text-text-sm cursor-pointer select-none',\n disabled && 'text-text-disabled',\n !disabled && error && !checked && 'text-text-err',\n !disabled && checked && !error && 'text-green-900',\n !disabled && checked && error && 'text-text-err',\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <label\n htmlFor={id}\n className={`group flex cursor-pointer items-center gap-space-2 ${disabled ? 'cursor-not-allowed' : ''}`}\n >\n <span className=\"relative inline-flex h-5 w-5 shrink-0 items-center justify-center\">\n <input\n type=\"checkbox\"\n id={id}\n name={name}\n value={value}\n checked={checked}\n disabled={disabled}\n onChange={(e) => onChange(e.target.checked)}\n className=\"peer sr-only\"\n aria-invalid={error || undefined}\n />\n <span className={boxClasses}>\n {checked && (\n <CheckmarkIcon\n className={`h-4 w-4 shrink-0 aspect-square ${\n disabled ? 'text-neutral-500' : error ? 'text-green-900' : 'text-neutral-0'\n }`}\n />\n )}\n </span>\n </span>\n <span className={labelClasses}>{label}</span>\n </label>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\n/**\n * Group of checkboxes with a shared label, support text, and optional error state.\n * Renders a fieldset with design-token styling. Use for multi-select options (e.g. preferences,\n * filters). For a single checkbox use a native input or a custom control.\n *\n * @example\n * <CheckboxGroup label=\"Toppings\" options={[{ value: 'a', label: 'Option A' }]} onChange={setValues} />\n *\n * @example\n * <CheckboxGroup label=\"Choose\" value={selected} onChange={setSelected} options={opts} error errorMessage=\"Required\" />\n */\nexport function CheckboxGroup({\n label,\n secondaryLabel,\n supportText,\n name: nameProp,\n value: valueProp,\n defaultValue,\n onChange,\n options,\n disabled = false,\n error = false,\n errorMessage,\n required,\n className,\n id: idProp,\n ref,\n}: CheckboxGroupProps) {\n const autoId = useId();\n const baseId = idProp ?? autoId;\n const name = nameProp ?? baseId;\n\n const isControlled = valueProp !== undefined;\n const [internal, setInternal] = React.useState<string[]>(defaultValue ?? []);\n const value = isControlled ? valueProp : internal;\n\n const handleChange = (optionValue: string, checked: boolean) => {\n const next = checked\n ? [...value, optionValue]\n : value.filter((v) => v !== optionValue);\n if (!isControlled) setInternal(next);\n onChange?.(next);\n };\n\n const showErrorAlert = error && errorMessage;\n\n return (\n <fieldset\n ref={ref}\n disabled={disabled}\n className={`flex flex-col gap-space-2 border-none p-0 ${className ?? ''}`}\n aria-invalid={error || undefined}\n aria-describedby={showErrorAlert ? `${baseId}-error` : undefined}\n >\n {(label || supportText) && (\n <div className=\"flex flex-col gap-space-1\">\n {label && (\n <legend\n className={`text-form-label ${\n disabled ? 'text-text-disabled' : 'text-text-primary'\n }`}\n >\n {label}\n {secondaryLabel && (\n <span className=\"ml-1 font-normal text-text-secondary\">\n {secondaryLabel}\n </span>\n )}\n {required && (\n <span className=\"ml-0.5 text-text-err\">*</span>\n )}\n </legend>\n )}\n {supportText && (\n <p\n className={`text-text-xs ${\n disabled ? 'text-text-disabled' : 'text-text-secondary'\n }`}\n >\n {supportText}\n </p>\n )}\n </div>\n )}\n\n <div className=\"flex flex-col gap-space-3\">\n {options.map((opt, index) => (\n <CheckboxItem\n key={opt.value}\n id={`${baseId}-${index}`}\n name={name}\n value={opt.value}\n checked={value.includes(opt.value)}\n disabled={disabled || !!opt.disabled}\n error={error}\n label={opt.label}\n onChange={(checked) => handleChange(opt.value, checked)}\n />\n ))}\n </div>\n\n {showErrorAlert && (\n <div\n id={`${baseId}-error`}\n role=\"alert\"\n className={`flex items-start gap-space-4 rounded-rad-md p-space-4 ${\n disabled ? 'bg-fill-bg-secondary' : 'bg-fill-bg-err'\n }`}\n >\n <ErrorIcon\n className={`h-5 w-5 shrink-0 ${\n disabled ? 'text-icon-primary-disabled' : 'text-icon-err'\n }`}\n />\n <span\n className={`text-text-xs ${\n disabled ? 'text-text-disabled' : 'text-text-err'\n }`}\n >\n {errorMessage}\n </span>\n </div>\n )}\n </fieldset>\n );\n}\n"],"names":[],"mappings":";;;AA6DA;AACE;AACE;AAAC;AAAA;AACC;AACM;AACC;AACC;AACH;AACE;AACK;AACE;AACC;AACH;AAEkC;AAAA;AAGpD;AAEA;AACE;AACE;AAAC;AAAA;AACC;AACQ;AACH;AACO;AAEZ;AAAC;AAAA;AACU;AACP;AACO;AAAA;AAAA;AACX;AAGN;AAiBA;AAAsB;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AAEF;AACE;AAAmB;AACjB;AACmC;AACA;AACA;AACD;AACA;AACD;AACT;AACD;AAErB;AAKJ;AAAqB;AACnB;AACY;AACsB;AACA;AACD;AAKnC;AACE;AAAC;AAAA;AACU;AAC4F;AAErG;AACE;AAAA;AAAC;AAAA;AACM;AACL;AACA;AACA;AACA;AACA;AAC0C;AAChC;AACa;AAAA;AAAA;AAIrB;AAAC;AAAA;AAGC;AAAA;AAGN;AACF;AACsC;AAAA;AAAA;AAG5C;AAiBO;AAAuB;AAC5B;AACA;AACA;AACM;AACC;AACP;AACA;AACA;AACW;AACH;AACR;AACA;AACA;AACI;AAEN;AACE;AACA;AACA;AAEA;AACA;AACA;AAEA;AACE;AAGA;AACA;AAAW;AAGb;AAEA;AACE;AAAC;AAAA;AACC;AACA;AACuE;AAChD;AACgC;AAErD;AAEG;AACC;AAAC;AAAA;AAGC;AAEC;AAAA;AAIC;AAGwC;AAAA;AAAA;AAAA;AAK5C;AAAC;AAAA;AAGC;AAEC;AAAA;AAAA;AAGP;AAKE;AAAC;AAAA;AAEuB;AACtB;AACW;AACsB;AACL;AAC5B;AACW;AAC2C;AAAA;AAR7C;AAWf;AAGE;AAAC;AAAA;AACc;AACR;AAGL;AAEA;AAAA;AAAC;AAAA;AAGC;AAAA;AAAA;AAEF;AAAC;AAAA;AAGC;AAEC;AAAA;AAAA;AACH;AAAA;AAAA;AACF;AAAA;AAIR;;;;"}