@tribepad/themis 1.0.1 → 1.0.2

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 (301) hide show
  1. package/dist/elements/Accordion/index.js +1 -335
  2. package/dist/elements/Accordion/index.js.map +1 -1
  3. package/dist/elements/Accordion/index.mjs +1 -317
  4. package/dist/elements/Accordion/index.mjs.map +1 -1
  5. package/dist/elements/AlertDialog/AlertDialog.d.ts +43 -0
  6. package/dist/elements/AlertDialog/AlertDialog.d.ts.map +1 -0
  7. package/dist/elements/AlertDialog/AlertDialog.styles.d.ts +15 -0
  8. package/dist/elements/AlertDialog/AlertDialog.styles.d.ts.map +1 -0
  9. package/dist/elements/AlertDialog/AlertDialog.types.d.ts +72 -0
  10. package/dist/elements/AlertDialog/AlertDialog.types.d.ts.map +1 -0
  11. package/dist/elements/AlertDialog/index.d.ts +25 -0
  12. package/dist/elements/AlertDialog/index.d.ts.map +1 -0
  13. package/dist/elements/AlertDialog/index.js +3 -0
  14. package/dist/elements/AlertDialog/index.js.map +1 -0
  15. package/dist/elements/AlertDialog/index.mjs +3 -0
  16. package/dist/elements/AlertDialog/index.mjs.map +1 -0
  17. package/dist/elements/Avatar/index.js +1 -468
  18. package/dist/elements/Avatar/index.js.map +1 -1
  19. package/dist/elements/Avatar/index.mjs +1 -456
  20. package/dist/elements/Avatar/index.mjs.map +1 -1
  21. package/dist/elements/Badge/index.js +1 -243
  22. package/dist/elements/Badge/index.js.map +1 -1
  23. package/dist/elements/Badge/index.mjs +1 -234
  24. package/dist/elements/Badge/index.mjs.map +1 -1
  25. package/dist/elements/Breadcrumbs/index.js +1 -821
  26. package/dist/elements/Breadcrumbs/index.js.map +1 -1
  27. package/dist/elements/Breadcrumbs/index.mjs +1 -810
  28. package/dist/elements/Breadcrumbs/index.mjs.map +1 -1
  29. package/dist/elements/Button/Button.d.ts +26 -81
  30. package/dist/elements/Button/Button.d.ts.map +1 -1
  31. package/dist/elements/Button/Button.styles.d.ts +35 -0
  32. package/dist/elements/Button/Button.styles.d.ts.map +1 -0
  33. package/dist/elements/Button/Button.types.d.ts +20 -8
  34. package/dist/elements/Button/Button.types.d.ts.map +1 -1
  35. package/dist/elements/Button/index.js +1 -288
  36. package/dist/elements/Button/index.js.map +1 -1
  37. package/dist/elements/Button/index.mjs +1 -283
  38. package/dist/elements/Button/index.mjs.map +1 -1
  39. package/dist/elements/ButtonGroup/index.js +1 -237
  40. package/dist/elements/ButtonGroup/index.js.map +1 -1
  41. package/dist/elements/ButtonGroup/index.mjs +1 -222
  42. package/dist/elements/ButtonGroup/index.mjs.map +1 -1
  43. package/dist/elements/Card/index.js +1 -579
  44. package/dist/elements/Card/index.js.map +1 -1
  45. package/dist/elements/Card/index.mjs +1 -560
  46. package/dist/elements/Card/index.mjs.map +1 -1
  47. package/dist/elements/Carousel/Carousel.d.ts +1 -11
  48. package/dist/elements/Carousel/Carousel.d.ts.map +1 -1
  49. package/dist/elements/Carousel/LazyCarousel.d.ts +1 -1
  50. package/dist/elements/Carousel/LazyCarousel.d.ts.map +1 -1
  51. package/dist/elements/Carousel/index.js +1 -789
  52. package/dist/elements/Carousel/index.js.map +1 -1
  53. package/dist/elements/Carousel/index.mjs +1 -786
  54. package/dist/elements/Carousel/index.mjs.map +1 -1
  55. package/dist/elements/Chart/ChartContext.d.ts.map +1 -1
  56. package/dist/elements/Chart/index.js +1 -1842
  57. package/dist/elements/Chart/index.js.map +1 -1
  58. package/dist/elements/Chart/index.mjs +1 -1832
  59. package/dist/elements/Chart/index.mjs.map +1 -1
  60. package/dist/elements/Checkbox/index.js +1 -316
  61. package/dist/elements/Checkbox/index.js.map +1 -1
  62. package/dist/elements/Checkbox/index.mjs +1 -306
  63. package/dist/elements/Checkbox/index.mjs.map +1 -1
  64. package/dist/elements/CheckboxGroup/index.js +1 -455
  65. package/dist/elements/CheckboxGroup/index.js.map +1 -1
  66. package/dist/elements/CheckboxGroup/index.mjs +1 -439
  67. package/dist/elements/CheckboxGroup/index.mjs.map +1 -1
  68. package/dist/elements/Combobox/Combobox.d.ts +56 -0
  69. package/dist/elements/Combobox/Combobox.d.ts.map +1 -0
  70. package/dist/elements/Combobox/Combobox.styles.d.ts +29 -0
  71. package/dist/elements/Combobox/Combobox.styles.d.ts.map +1 -0
  72. package/dist/elements/Combobox/Combobox.types.d.ts +67 -0
  73. package/dist/elements/Combobox/Combobox.types.d.ts.map +1 -0
  74. package/dist/elements/Combobox/index.d.ts +20 -0
  75. package/dist/elements/Combobox/index.d.ts.map +1 -0
  76. package/dist/elements/Combobox/index.js +3 -0
  77. package/dist/elements/Combobox/index.js.map +1 -0
  78. package/dist/elements/Combobox/index.mjs +3 -0
  79. package/dist/elements/Combobox/index.mjs.map +1 -0
  80. package/dist/elements/DatePicker/DatePicker.d.ts +1 -1
  81. package/dist/elements/DatePicker/DatePicker.d.ts.map +1 -1
  82. package/dist/elements/DatePicker/index.js +1 -903
  83. package/dist/elements/DatePicker/index.js.map +1 -1
  84. package/dist/elements/DatePicker/index.mjs +1 -853
  85. package/dist/elements/DatePicker/index.mjs.map +1 -1
  86. package/dist/elements/Dropdown/Dropdown.d.ts +7 -15
  87. package/dist/elements/Dropdown/Dropdown.d.ts.map +1 -1
  88. package/dist/elements/Dropdown/Dropdown.styles.d.ts +22 -0
  89. package/dist/elements/Dropdown/Dropdown.styles.d.ts.map +1 -0
  90. package/dist/elements/Dropdown/index.d.ts +1 -0
  91. package/dist/elements/Dropdown/index.d.ts.map +1 -1
  92. package/dist/elements/Dropdown/index.js +1 -193
  93. package/dist/elements/Dropdown/index.js.map +1 -1
  94. package/dist/elements/Dropdown/index.mjs +1 -184
  95. package/dist/elements/Dropdown/index.mjs.map +1 -1
  96. package/dist/elements/FileField/index.js +1 -1539
  97. package/dist/elements/FileField/index.js.map +1 -1
  98. package/dist/elements/FileField/index.mjs +1 -1507
  99. package/dist/elements/FileField/index.mjs.map +1 -1
  100. package/dist/elements/FormLayout/index.js +1 -170
  101. package/dist/elements/FormLayout/index.js.map +1 -1
  102. package/dist/elements/FormLayout/index.mjs +1 -167
  103. package/dist/elements/FormLayout/index.mjs.map +1 -1
  104. package/dist/elements/Modal/Modal.d.ts +9 -14
  105. package/dist/elements/Modal/Modal.d.ts.map +1 -1
  106. package/dist/elements/Modal/Modal.styles.d.ts +29 -0
  107. package/dist/elements/Modal/Modal.styles.d.ts.map +1 -0
  108. package/dist/elements/Modal/index.d.ts +1 -0
  109. package/dist/elements/Modal/index.d.ts.map +1 -1
  110. package/dist/elements/Modal/index.js +1 -232
  111. package/dist/elements/Modal/index.js.map +1 -1
  112. package/dist/elements/Modal/index.mjs +1 -220
  113. package/dist/elements/Modal/index.mjs.map +1 -1
  114. package/dist/elements/NumberField/index.js +1 -666
  115. package/dist/elements/NumberField/index.js.map +1 -1
  116. package/dist/elements/NumberField/index.mjs +1 -654
  117. package/dist/elements/NumberField/index.mjs.map +1 -1
  118. package/dist/elements/OTPInput/OTPInput.d.ts.map +1 -1
  119. package/dist/elements/OTPInput/index.js +1 -734
  120. package/dist/elements/OTPInput/index.js.map +1 -1
  121. package/dist/elements/OTPInput/index.mjs +1 -732
  122. package/dist/elements/OTPInput/index.mjs.map +1 -1
  123. package/dist/elements/Pagination/Pagination.d.ts +45 -0
  124. package/dist/elements/Pagination/Pagination.d.ts.map +1 -0
  125. package/dist/elements/Pagination/Pagination.styles.d.ts +10 -0
  126. package/dist/elements/Pagination/Pagination.styles.d.ts.map +1 -0
  127. package/dist/elements/Pagination/Pagination.types.d.ts +55 -0
  128. package/dist/elements/Pagination/Pagination.types.d.ts.map +1 -0
  129. package/dist/elements/Pagination/index.d.ts +21 -0
  130. package/dist/elements/Pagination/index.d.ts.map +1 -0
  131. package/dist/elements/Pagination/index.js +3 -0
  132. package/dist/elements/Pagination/index.js.map +1 -0
  133. package/dist/elements/Pagination/index.mjs +3 -0
  134. package/dist/elements/Pagination/index.mjs.map +1 -0
  135. package/dist/elements/Panel/index.js +1 -330
  136. package/dist/elements/Panel/index.js.map +1 -1
  137. package/dist/elements/Panel/index.mjs +1 -323
  138. package/dist/elements/Panel/index.mjs.map +1 -1
  139. package/dist/elements/PasswordField/PasswordField.d.ts +27 -0
  140. package/dist/elements/PasswordField/PasswordField.d.ts.map +1 -0
  141. package/dist/elements/PasswordField/PasswordField.styles.d.ts +32 -0
  142. package/dist/elements/PasswordField/PasswordField.styles.d.ts.map +1 -0
  143. package/dist/elements/PasswordField/PasswordField.types.d.ts +100 -0
  144. package/dist/elements/PasswordField/PasswordField.types.d.ts.map +1 -0
  145. package/dist/elements/PasswordField/index.css +2 -0
  146. package/dist/elements/PasswordField/index.css.map +1 -0
  147. package/dist/elements/PasswordField/index.d.ts +20 -0
  148. package/dist/elements/PasswordField/index.d.ts.map +1 -0
  149. package/dist/elements/PasswordField/index.js +3 -0
  150. package/dist/elements/PasswordField/index.js.map +1 -0
  151. package/dist/elements/PasswordField/index.mjs +3 -0
  152. package/dist/elements/PasswordField/index.mjs.map +1 -0
  153. package/dist/elements/Progress/index.js +1 -187
  154. package/dist/elements/Progress/index.js.map +1 -1
  155. package/dist/elements/Progress/index.mjs +1 -181
  156. package/dist/elements/Progress/index.mjs.map +1 -1
  157. package/dist/elements/RadioGroup/index.js +1 -369
  158. package/dist/elements/RadioGroup/index.js.map +1 -1
  159. package/dist/elements/RadioGroup/index.mjs +1 -359
  160. package/dist/elements/RadioGroup/index.mjs.map +1 -1
  161. package/dist/elements/Resizable/index.js +1 -1580
  162. package/dist/elements/Resizable/index.js.map +1 -1
  163. package/dist/elements/Resizable/index.mjs +1 -1566
  164. package/dist/elements/Resizable/index.mjs.map +1 -1
  165. package/dist/elements/SearchField/SearchField.d.ts +27 -0
  166. package/dist/elements/SearchField/SearchField.d.ts.map +1 -0
  167. package/dist/elements/SearchField/SearchField.styles.d.ts +32 -0
  168. package/dist/elements/SearchField/SearchField.styles.d.ts.map +1 -0
  169. package/dist/elements/SearchField/SearchField.types.d.ts +45 -0
  170. package/dist/elements/SearchField/SearchField.types.d.ts.map +1 -0
  171. package/dist/elements/SearchField/index.css +2 -0
  172. package/dist/elements/SearchField/index.css.map +1 -0
  173. package/dist/elements/SearchField/index.d.ts +21 -0
  174. package/dist/elements/SearchField/index.d.ts.map +1 -0
  175. package/dist/elements/SearchField/index.js +3 -0
  176. package/dist/elements/SearchField/index.js.map +1 -0
  177. package/dist/elements/SearchField/index.mjs +3 -0
  178. package/dist/elements/SearchField/index.mjs.map +1 -0
  179. package/dist/elements/Select/Select.d.ts +19 -48
  180. package/dist/elements/Select/Select.d.ts.map +1 -1
  181. package/dist/elements/Select/Select.styles.d.ts +55 -0
  182. package/dist/elements/Select/Select.styles.d.ts.map +1 -0
  183. package/dist/elements/Select/index.js +1 -589
  184. package/dist/elements/Select/index.js.map +1 -1
  185. package/dist/elements/Select/index.mjs +1 -582
  186. package/dist/elements/Select/index.mjs.map +1 -1
  187. package/dist/elements/Skeleton/index.js +1 -82
  188. package/dist/elements/Skeleton/index.js.map +1 -1
  189. package/dist/elements/Skeleton/index.mjs +1 -78
  190. package/dist/elements/Skeleton/index.mjs.map +1 -1
  191. package/dist/elements/Switch/index.js +1 -179
  192. package/dist/elements/Switch/index.js.map +1 -1
  193. package/dist/elements/Switch/index.mjs +1 -173
  194. package/dist/elements/Switch/index.mjs.map +1 -1
  195. package/dist/elements/Table/Table.d.ts +3 -24
  196. package/dist/elements/Table/Table.d.ts.map +1 -1
  197. package/dist/elements/Table/Table.styles.d.ts +24 -0
  198. package/dist/elements/Table/Table.styles.d.ts.map +1 -0
  199. package/dist/elements/Table/index.js +1 -595
  200. package/dist/elements/Table/index.js.map +1 -1
  201. package/dist/elements/Table/index.mjs +1 -578
  202. package/dist/elements/Table/index.mjs.map +1 -1
  203. package/dist/elements/Tabs/index.js +1 -337
  204. package/dist/elements/Tabs/index.js.map +1 -1
  205. package/dist/elements/Tabs/index.mjs +1 -320
  206. package/dist/elements/Tabs/index.mjs.map +1 -1
  207. package/dist/elements/TextField/TextField.d.ts +6 -42
  208. package/dist/elements/TextField/TextField.d.ts.map +1 -1
  209. package/dist/elements/TextField/TextField.hooks.d.ts +63 -0
  210. package/dist/elements/TextField/TextField.hooks.d.ts.map +1 -0
  211. package/dist/elements/TextField/TextField.icons.d.ts +19 -0
  212. package/dist/elements/TextField/TextField.icons.d.ts.map +1 -0
  213. package/dist/elements/TextField/TextField.styles.d.ts +37 -0
  214. package/dist/elements/TextField/TextField.styles.d.ts.map +1 -0
  215. package/dist/elements/TextField/TextField.types.d.ts +3 -0
  216. package/dist/elements/TextField/TextField.types.d.ts.map +1 -1
  217. package/dist/elements/TextField/index.css +1 -22
  218. package/dist/elements/TextField/index.css.map +1 -1
  219. package/dist/elements/TextField/index.js +1 -902
  220. package/dist/elements/TextField/index.js.map +1 -1
  221. package/dist/elements/TextField/index.mjs +1 -882
  222. package/dist/elements/TextField/index.mjs.map +1 -1
  223. package/dist/elements/TimeField/index.js +1 -254
  224. package/dist/elements/TimeField/index.js.map +1 -1
  225. package/dist/elements/TimeField/index.mjs +1 -238
  226. package/dist/elements/TimeField/index.mjs.map +1 -1
  227. package/dist/elements/Toast/Toast.d.ts +0 -22
  228. package/dist/elements/Toast/Toast.d.ts.map +1 -1
  229. package/dist/elements/Toast/index.js +1 -737
  230. package/dist/elements/Toast/index.js.map +1 -1
  231. package/dist/elements/Toast/index.mjs +1 -724
  232. package/dist/elements/Toast/index.mjs.map +1 -1
  233. package/dist/elements/Tooltip/index.js +1 -323
  234. package/dist/elements/Tooltip/index.js.map +1 -1
  235. package/dist/elements/Tooltip/index.mjs +1 -310
  236. package/dist/elements/Tooltip/index.mjs.map +1 -1
  237. package/dist/elements/index.css +1 -22
  238. package/dist/elements/index.css.map +1 -1
  239. package/dist/elements/index.d.ts +13 -1
  240. package/dist/elements/index.d.ts.map +1 -1
  241. package/dist/elements/index.js +1 -12455
  242. package/dist/elements/index.js.map +1 -1
  243. package/dist/elements/index.mjs +1 -12233
  244. package/dist/elements/index.mjs.map +1 -1
  245. package/dist/index.css +1 -22
  246. package/dist/index.css.map +1 -1
  247. package/dist/index.js +2 -12490
  248. package/dist/index.js.map +1 -1
  249. package/dist/index.mjs +2 -12262
  250. package/dist/index.mjs.map +1 -1
  251. package/dist/schemas/index.js +2 -54
  252. package/dist/schemas/index.js.map +1 -1
  253. package/dist/schemas/index.mjs +2 -48
  254. package/dist/schemas/index.mjs.map +1 -1
  255. package/dist/styles/defaults.css +151 -0
  256. package/dist/styles/index.js +1 -166
  257. package/dist/styles/index.js.map +1 -1
  258. package/dist/styles/index.mjs +1 -129
  259. package/dist/styles/index.mjs.map +1 -1
  260. package/dist/utils/index.js +1 -12
  261. package/dist/utils/index.js.map +1 -1
  262. package/dist/utils/index.mjs +1 -10
  263. package/dist/utils/index.mjs.map +1 -1
  264. package/package.json +9 -7
  265. package/src/elements/Accordion/Accordion.stories.tsx +1 -1
  266. package/src/elements/AlertDialog/AlertDialog.stories.tsx +124 -0
  267. package/src/elements/Avatar/Avatar.stories.tsx +1 -1
  268. package/src/elements/Badge/Badge.stories.tsx +1 -1
  269. package/src/elements/Breadcrumbs/Breadcrumbs.stories.tsx +1 -1
  270. package/src/elements/Button/Button.stories.tsx +1 -1
  271. package/src/elements/ButtonGroup/ButtonGroup.stories.tsx +1 -1
  272. package/src/elements/Card/Card.stories.tsx +1 -1
  273. package/src/elements/Carousel/Carousel.stories.tsx +1 -1
  274. package/src/elements/Chart/Chart.stories.tsx +1 -1
  275. package/src/elements/Checkbox/Checkbox.stories.tsx +1 -1
  276. package/src/elements/CheckboxGroup/CheckboxGroup.stories.tsx +1 -1
  277. package/src/elements/Combobox/Combobox.stories.tsx +133 -0
  278. package/src/elements/DatePicker/DatePicker.stories.tsx +1 -1
  279. package/src/elements/Dropdown/Dropdown.stories.tsx +1 -1
  280. package/src/elements/FileField/FileField.stories.tsx +1 -1
  281. package/src/elements/FileField/FileProgress.stories.tsx +1 -1
  282. package/src/elements/FormLayout/FormLayout.stories.tsx +1 -1
  283. package/src/elements/Modal/Modal.stories.tsx +1 -1
  284. package/src/elements/NumberField/NumberField.stories.tsx +1 -1
  285. package/src/elements/OTPInput/OTPInput.stories.tsx +1 -1
  286. package/src/elements/Pagination/Pagination.stories.tsx +203 -0
  287. package/src/elements/Panel/Panel.stories.tsx +1 -1
  288. package/src/elements/PasswordField/PasswordField.stories.tsx +167 -0
  289. package/src/elements/Progress/Progress.stories.tsx +1 -1
  290. package/src/elements/RadioGroup/RadioGroup.stories.tsx +1 -1
  291. package/src/elements/Resizable/Resizable.stories.tsx +1 -1
  292. package/src/elements/SearchField/SearchField.stories.tsx +146 -0
  293. package/src/elements/Select/Select.stories.tsx +1 -1
  294. package/src/elements/Skeleton/Skeleton.stories.tsx +1 -1
  295. package/src/elements/Switch/Switch.stories.tsx +1 -1
  296. package/src/elements/Table/Table.stories.tsx +1 -1
  297. package/src/elements/Tabs/Tabs.stories.tsx +1 -1
  298. package/src/elements/TextField/TextField.stories.tsx +1 -1
  299. package/src/elements/TimeField/TimeField.stories.tsx +1 -1
  300. package/src/elements/Toast/Toast.stories.tsx +1 -1
  301. package/src/elements/Tooltip/Tooltip.stories.tsx +1 -1
@@ -1,172 +1,3 @@
1
1
  "use client";
2
- 'use strict';
3
-
4
- var react = require('react');
5
- var classVarianceAuthority = require('class-variance-authority');
6
- var lucideReact = require('lucide-react');
7
- var clsx = require('clsx');
8
- var tailwindMerge = require('tailwind-merge');
9
- var jsxRuntime = require('react/jsx-runtime');
10
-
11
- // src/elements/FormLayout/FormLayout.tsx
12
- function cn(...inputs) {
13
- return tailwindMerge.twMerge(clsx.clsx(inputs));
14
- }
15
- var formLayoutBodyVariants = classVarianceAuthority.cva(
16
- // Base styles - grid layout with responsive columns
17
- "grid grid-cols-1 mt-6",
18
- {
19
- variants: {
20
- // Column variants (responsive: mobile single, desktop optional double)
21
- columns: {
22
- 1: "",
23
- // Single column (no additional classes needed)
24
- 2: "md:grid-cols-2"
25
- // Double column on md breakpoint and up
26
- },
27
- // Density variants (controls gap between form fields)
28
- density: {
29
- default: "gap-4",
30
- // 16px gap
31
- compact: "gap-2",
32
- // 8px gap
33
- comfortable: "gap-6"
34
- // 24px gap
35
- }
36
- },
37
- defaultVariants: {
38
- columns: 1,
39
- density: "default"
40
- }
41
- }
42
- );
43
- var Heading = ({ level, id, className, children }) => {
44
- const Tag = `h${level}`;
45
- return /* @__PURE__ */ jsxRuntime.jsx(Tag, { id, className, children });
46
- };
47
- var FormLayout = react.forwardRef(
48
- ({
49
- title,
50
- requiredNote,
51
- description,
52
- columns = 1,
53
- headingLevel = 2,
54
- density = "default",
55
- errorMessage,
56
- successMessage,
57
- fieldErrors: _fieldErrors,
58
- autoHideSuccess = false,
59
- autoHideDuration = 5e3,
60
- className,
61
- children,
62
- actions,
63
- id,
64
- "data-testid": dataTestId,
65
- "aria-label": ariaLabel,
66
- "aria-labelledby": ariaLabelledby,
67
- "aria-describedby": ariaDescribedby,
68
- ...props
69
- }, ref) => {
70
- const headingId = react.useId();
71
- const finalHeadingId = id ? `${id}-title` : headingId;
72
- const finalAriaLabelledby = ariaLabelledby || finalHeadingId;
73
- const [showSuccess, setShowSuccess] = react.useState(true);
74
- react.useEffect(() => {
75
- setShowSuccess(true);
76
- if (successMessage && autoHideSuccess) {
77
- const timer = setTimeout(() => {
78
- setShowSuccess(false);
79
- }, autoHideDuration);
80
- return () => clearTimeout(timer);
81
- }
82
- }, [successMessage, autoHideSuccess, autoHideDuration]);
83
- return /* @__PURE__ */ jsxRuntime.jsxs(
84
- "section",
85
- {
86
- ref,
87
- id,
88
- "data-testid": dataTestId,
89
- "aria-label": ariaLabel,
90
- "aria-labelledby": !ariaLabel ? finalAriaLabelledby : void 0,
91
- "aria-describedby": ariaDescribedby,
92
- className: cn("form-layout w-full", className),
93
- ...props,
94
- children: [
95
- errorMessage && /* @__PURE__ */ jsxRuntime.jsxs(
96
- "div",
97
- {
98
- role: "alert",
99
- "aria-live": "assertive",
100
- "data-testid": "form-layout-error",
101
- className: cn(
102
- "rounded-lg border px-4 py-3 mb-6 text-sm",
103
- "border-[var(--destructive-background)] bg-[var(--destructive-foreground)] text-[var(--destructive-background)]",
104
- "flex items-start gap-3"
105
- ),
106
- children: [
107
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-5 w-5 flex-shrink-0 mt-0.5", "aria-hidden": "true" }),
108
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
109
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium", children: "Error" }),
110
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1", children: errorMessage })
111
- ] })
112
- ]
113
- }
114
- ),
115
- successMessage && showSuccess && /* @__PURE__ */ jsxRuntime.jsxs(
116
- "div",
117
- {
118
- role: "status",
119
- "aria-live": "polite",
120
- "data-testid": "form-layout-success",
121
- className: cn(
122
- "rounded-lg border px-4 py-3 mb-6 text-sm",
123
- "border-success bg-success/10 text-success",
124
- "flex items-start gap-3"
125
- ),
126
- children: [
127
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle2, { className: "h-5 w-5 flex-shrink-0 mt-0.5", "aria-hidden": "true" }),
128
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
129
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium", children: "Success" }),
130
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1", children: successMessage })
131
- ] })
132
- ]
133
- }
134
- ),
135
- /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "form-layout-header mb-6", children: [
136
- /* @__PURE__ */ jsxRuntime.jsx(
137
- Heading,
138
- {
139
- level: headingLevel,
140
- id: finalHeadingId,
141
- className: "text-[var(--content-foreground)] font-semibold",
142
- children: title
143
- }
144
- ),
145
- description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-[var(--content-foreground)] leading-relaxed mt-2", children: description }),
146
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-[var(--content-foreground)]", children: requiredNote })
147
- ] }),
148
- /* @__PURE__ */ jsxRuntime.jsx(
149
- "div",
150
- {
151
- "data-testid": "form-layout-body",
152
- className: cn(
153
- formLayoutBodyVariants({
154
- columns,
155
- density
156
- })
157
- ),
158
- children
159
- }
160
- ),
161
- actions && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-testid": "form-layout-actions", className: "flex justify-between items-center mt-4 gap-2 max-md:flex-col max-md:w-full", children: actions })
162
- ]
163
- }
164
- );
165
- }
166
- );
167
- FormLayout.displayName = "FormLayout";
168
-
169
- exports.FormLayout = FormLayout;
170
- exports.formLayoutBodyVariants = formLayoutBodyVariants;
171
- //# sourceMappingURL=index.js.map
2
+ 'use strict';var react=require('react'),classVarianceAuthority=require('class-variance-authority'),lucideReact=require('lucide-react'),clsx=require('clsx'),tailwindMerge=require('tailwind-merge'),jsxRuntime=require('react/jsx-runtime');function r(...a){return tailwindMerge.twMerge(clsx.clsx(a))}var g=classVarianceAuthority.cva("grid grid-cols-1 mt-6",{variants:{columns:{1:"",2:"md:grid-cols-2"},density:{default:"gap-4",compact:"gap-2",comfortable:"gap-6"}},defaultVariants:{columns:1,density:"default"}}),_=({level:a,id:l,className:o,children:d})=>{let i=`h${a}`;return jsxRuntime.jsx(i,{id:l,className:o,children:d})},x=react.forwardRef(({title:a,requiredNote:l,description:o,columns:d=1,headingLevel:i=2,density:b="default",errorMessage:n,successMessage:s,fieldErrors:j,autoHideSuccess:c=false,autoHideDuration:f=5e3,className:N,children:h,actions:u,id:m,"data-testid":L,"aria-label":p,"aria-labelledby":F,"aria-describedby":w,...C},k)=>{let E=react.useId(),y=m?`${m}-title`:E,H=F||y,[S,v]=react.useState(true);return react.useEffect(()=>{if(v(true),s&&c){let P=setTimeout(()=>{v(false);},f);return ()=>clearTimeout(P)}},[s,c,f]),jsxRuntime.jsxs("section",{ref:k,id:m,"data-testid":L,"aria-label":p,"aria-labelledby":p?void 0:H,"aria-describedby":w,className:r("form-layout w-full",N),...C,children:[n&&jsxRuntime.jsxs("div",{role:"alert","aria-live":"assertive","data-testid":"form-layout-error",className:r("rounded-lg border px-4 py-3 mb-6 text-sm","border-[var(--destructive-background)] bg-[var(--destructive-foreground)] text-[var(--destructive-background)]","flex items-start gap-3"),children:[jsxRuntime.jsx(lucideReact.AlertCircle,{className:"h-5 w-5 flex-shrink-0 mt-0.5","aria-hidden":"true"}),jsxRuntime.jsxs("div",{className:"flex-1",children:[jsxRuntime.jsx("p",{className:"font-medium",children:"Error"}),jsxRuntime.jsx("p",{className:"mt-1",children:n})]})]}),s&&S&&jsxRuntime.jsxs("div",{role:"status","aria-live":"polite","data-testid":"form-layout-success",className:r("rounded-lg border px-4 py-3 mb-6 text-sm","border-success bg-success/10 text-success","flex items-start gap-3"),children:[jsxRuntime.jsx(lucideReact.CheckCircle2,{className:"h-5 w-5 flex-shrink-0 mt-0.5","aria-hidden":"true"}),jsxRuntime.jsxs("div",{className:"flex-1",children:[jsxRuntime.jsx("p",{className:"font-medium",children:"Success"}),jsxRuntime.jsx("p",{className:"mt-1",children:s})]})]}),jsxRuntime.jsxs("header",{className:"form-layout-header mb-6",children:[jsxRuntime.jsx(_,{level:i,id:y,className:"text-[var(--content-foreground)] font-semibold",children:a}),o&&jsxRuntime.jsx("p",{className:"text-sm text-[var(--content-foreground)] leading-relaxed mt-2",children:o}),jsxRuntime.jsx("p",{className:"text-sm text-[var(--content-foreground)]",children:l})]}),jsxRuntime.jsx("div",{"data-testid":"form-layout-body",className:r(g({columns:d,density:b})),children:h}),u&&jsxRuntime.jsx("div",{"data-testid":"form-layout-actions",className:"flex justify-between items-center mt-4 gap-2 max-md:flex-col max-md:w-full",children:u})]})});x.displayName="FormLayout";exports.FormLayout=x;exports.formLayoutBodyVariants=g;//# sourceMappingURL=index.js.map
172
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/elements/FormLayout/FormLayout.tsx"],"names":["twMerge","clsx","cva","jsx","forwardRef","useId","useState","useEffect","jsxs","AlertCircle","CheckCircle2"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Class Name Utility\n * Merges Tailwind CSS classes with conflict resolution\n *\n * Combines clsx for conditional classes and tailwind-merge for deduplication\n *\n * @example\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4' (px-4 overrides px-2)\n * cn('text-red-500', condition && 'text-blue-500') // => conditional application\n */\n\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\n/**\n * FormLayout Component\n * Structural layout component for consistent form presentation\n *\n * @see FormLayout-plan.md (Component API Design)\n * @see FormLayout-design-decisions.md (Approved design specifications)\n * @see spec.md FR-009 to FR-014 (Accessibility Requirements - WCAG 2.2 AAA)\n * @see constitution.md Principle IV (Accessibility First)\n */\n\nimport { JSX, forwardRef, FC, useId, ReactNode, useState, useEffect } from 'react';\nimport { cva } from 'class-variance-authority';\nimport { AlertCircle, CheckCircle2 } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport type { FormLayoutProps, HeadingLevel } from './FormLayout.types';\n\n/**\n * Body variant styles using CVA\n * Controls column layout and field spacing (density)\n */\nconst formLayoutBodyVariants = cva(\n // Base styles - grid layout with responsive columns\n \"grid grid-cols-1 mt-6\",\n {\n variants: {\n // Column variants (responsive: mobile single, desktop optional double)\n columns: {\n 1: \"\", // Single column (no additional classes needed)\n 2: \"md:grid-cols-2\", // Double column on md breakpoint and up\n },\n // Density variants (controls gap between form fields)\n density: {\n default: \"gap-4\", // 16px gap\n compact: \"gap-2\", // 8px gap\n comfortable: \"gap-6\", // 24px gap\n },\n },\n defaultVariants: {\n columns: 1,\n density: \"default\",\n },\n }\n);\n\n/**\n * Polymorphic Heading Component\n * Renders h1-h6 based on headingLevel prop for proper document hierarchy\n */\ninterface HeadingProps {\n level: HeadingLevel;\n id: string;\n className?: string;\n children: ReactNode;\n}\n\nconst Heading: FC<HeadingProps> = ({ level, id, className, children }) => {\n const Tag = `h${level}` as keyof JSX.IntrinsicElements;\n\n return (\n <Tag id={id} className={className}>\n {children}\n </Tag>\n );\n};\n\n/**\n * FormLayout Component\n * Provides consistent form structure with header, body, and optional actions\n */\nexport const FormLayout = forwardRef<HTMLElement, FormLayoutProps>(\n (\n {\n title,\n requiredNote,\n description,\n columns = 1,\n headingLevel = 2,\n density = 'default',\n errorMessage,\n successMessage,\n fieldErrors: _fieldErrors,\n autoHideSuccess = false,\n autoHideDuration = 5000,\n className,\n children,\n actions,\n id,\n 'data-testid': dataTestId,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n 'aria-describedby': ariaDescribedby,\n ...props\n },\n ref\n ) => {\n // Generate unique IDs for accessibility\n const headingId = useId();\n const finalHeadingId = id ? `${id}-title` : headingId;\n\n // Determine aria-labelledby (use prop if provided, otherwise point to heading)\n const finalAriaLabelledby = ariaLabelledby || finalHeadingId;\n\n // Auto-hide success message state\n const [showSuccess, setShowSuccess] = useState(true);\n\n // Auto-hide success message after duration\n useEffect(() => {\n // Reset show state when message changes (must come first!)\n setShowSuccess(true);\n\n // Set timeout to hide if autoHideSuccess is enabled\n if (successMessage && autoHideSuccess) {\n const timer = setTimeout(() => {\n setShowSuccess(false);\n }, autoHideDuration);\n\n return (): void => clearTimeout(timer);\n }\n }, [successMessage, autoHideSuccess, autoHideDuration]);\n\n return (\n <section\n ref={ref}\n id={id}\n data-testid={dataTestId}\n aria-label={ariaLabel}\n aria-labelledby={!ariaLabel ? finalAriaLabelledby : undefined}\n aria-describedby={ariaDescribedby}\n className={cn(\"form-layout w-full\", className)}\n {...props}\n >\n {/* Error Message Alert */}\n {errorMessage && (\n <div\n role=\"alert\"\n aria-live=\"assertive\"\n data-testid=\"form-layout-error\"\n className={cn(\n \"rounded-lg border px-4 py-3 mb-6 text-sm\",\n \"border-[var(--destructive-background)] bg-[var(--destructive-foreground)] text-[var(--destructive-background)]\",\n \"flex items-start gap-3\"\n )}\n >\n <AlertCircle className=\"h-5 w-5 flex-shrink-0 mt-0.5\" aria-hidden=\"true\" />\n <div className=\"flex-1\">\n <p className=\"font-medium\">Error</p>\n <p className=\"mt-1\">{errorMessage}</p>\n </div>\n </div>\n )}\n\n {/* Success Message Alert */}\n {successMessage && showSuccess && (\n <div\n role=\"status\"\n aria-live=\"polite\"\n data-testid=\"form-layout-success\"\n className={cn(\n \"rounded-lg border px-4 py-3 mb-6 text-sm\",\n \"border-success bg-success/10 text-success\",\n \"flex items-start gap-3\"\n )}\n >\n <CheckCircle2 className=\"h-5 w-5 flex-shrink-0 mt-0.5\" aria-hidden=\"true\" />\n <div className=\"flex-1\">\n <p className=\"font-medium\">Success</p>\n <p className=\"mt-1\">{successMessage}</p>\n </div>\n </div>\n )}\n\n {/* Header Section: Title → Description → Required Note */}\n <header className=\"form-layout-header mb-6\">\n {/* Title (configurable heading level) */}\n <Heading\n level={headingLevel}\n id={finalHeadingId}\n className=\"text-[var(--content-foreground)] font-semibold\"\n >\n {title}\n </Heading>\n\n {/* Description (optional) */}\n {description && (\n <p className=\"text-sm text-[var(--content-foreground)] leading-relaxed mt-2\">\n {description}\n </p>\n )}\n\n {/* Required Note (always visible) */}\n <p className=\"text-sm text-[var(--content-foreground)]\">\n {requiredNote}\n </p>\n </header>\n\n {/* Body Section: Form fields with configurable layout */}\n <div\n data-testid=\"form-layout-body\"\n className={cn(\n formLayoutBodyVariants({\n columns,\n density,\n })\n )}\n >\n {children}\n </div>\n\n {/* Actions Section: Optional footer for buttons */}\n {actions && (\n <div data-testid=\"form-layout-actions\" className=\"flex justify-between items-center mt-4 gap-2 max-md:flex-col max-md:w-full\">\n {actions}\n </div>\n )}\n </section>\n );\n }\n);\n\nFormLayout.displayName = \"FormLayout\";\n\nexport { formLayoutBodyVariants };\n"]}
1
+ {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/elements/FormLayout/FormLayout.tsx"],"names":["cn","inputs","twMerge","clsx","formLayoutBodyVariants","cva","Heading","level","id","className","children","Tag","jsx","FormLayout","forwardRef","title","requiredNote","description","columns","headingLevel","density","errorMessage","successMessage","_fieldErrors","autoHideSuccess","autoHideDuration","actions","dataTestId","ariaLabel","ariaLabelledby","ariaDescribedby","props","ref","headingId","useId","finalHeadingId","finalAriaLabelledby","showSuccess","setShowSuccess","useState","useEffect","timer","jsxs","AlertCircle","CheckCircle2"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Class Name Utility\n * Merges Tailwind CSS classes with conflict resolution\n *\n * Combines clsx for conditional classes and tailwind-merge for deduplication\n *\n * @example\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4' (px-4 overrides px-2)\n * cn('text-red-500', condition && 'text-blue-500') // => conditional application\n */\n\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\n/**\n * FormLayout Component\n * Structural layout component for consistent form presentation\n *\n * @see FormLayout-plan.md (Component API Design)\n * @see FormLayout-design-decisions.md (Approved design specifications)\n * @see spec.md FR-009 to FR-014 (Accessibility Requirements - WCAG 2.2 AAA)\n * @see constitution.md Principle IV (Accessibility First)\n */\n\nimport { JSX, forwardRef, FC, useId, ReactNode, useState, useEffect } from 'react';\nimport { cva } from 'class-variance-authority';\nimport { AlertCircle, CheckCircle2 } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport type { FormLayoutProps, HeadingLevel } from './FormLayout.types';\n\n/**\n * Body variant styles using CVA\n * Controls column layout and field spacing (density)\n */\nconst formLayoutBodyVariants = cva(\n // Base styles - grid layout with responsive columns\n \"grid grid-cols-1 mt-6\",\n {\n variants: {\n // Column variants (responsive: mobile single, desktop optional double)\n columns: {\n 1: \"\", // Single column (no additional classes needed)\n 2: \"md:grid-cols-2\", // Double column on md breakpoint and up\n },\n // Density variants (controls gap between form fields)\n density: {\n default: \"gap-4\", // 16px gap\n compact: \"gap-2\", // 8px gap\n comfortable: \"gap-6\", // 24px gap\n },\n },\n defaultVariants: {\n columns: 1,\n density: \"default\",\n },\n }\n);\n\n/**\n * Polymorphic Heading Component\n * Renders h1-h6 based on headingLevel prop for proper document hierarchy\n */\ninterface HeadingProps {\n level: HeadingLevel;\n id: string;\n className?: string;\n children: ReactNode;\n}\n\nconst Heading: FC<HeadingProps> = ({ level, id, className, children }) => {\n const Tag = `h${level}` as keyof JSX.IntrinsicElements;\n\n return (\n <Tag id={id} className={className}>\n {children}\n </Tag>\n );\n};\n\n/**\n * FormLayout Component\n * Provides consistent form structure with header, body, and optional actions\n */\nexport const FormLayout = forwardRef<HTMLElement, FormLayoutProps>(\n (\n {\n title,\n requiredNote,\n description,\n columns = 1,\n headingLevel = 2,\n density = 'default',\n errorMessage,\n successMessage,\n fieldErrors: _fieldErrors,\n autoHideSuccess = false,\n autoHideDuration = 5000,\n className,\n children,\n actions,\n id,\n 'data-testid': dataTestId,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n 'aria-describedby': ariaDescribedby,\n ...props\n },\n ref\n ) => {\n // Generate unique IDs for accessibility\n const headingId = useId();\n const finalHeadingId = id ? `${id}-title` : headingId;\n\n // Determine aria-labelledby (use prop if provided, otherwise point to heading)\n const finalAriaLabelledby = ariaLabelledby || finalHeadingId;\n\n // Auto-hide success message state\n const [showSuccess, setShowSuccess] = useState(true);\n\n // Auto-hide success message after duration\n useEffect(() => {\n // Reset show state when message changes (must come first!)\n setShowSuccess(true);\n\n // Set timeout to hide if autoHideSuccess is enabled\n if (successMessage && autoHideSuccess) {\n const timer = setTimeout(() => {\n setShowSuccess(false);\n }, autoHideDuration);\n\n return (): void => clearTimeout(timer);\n }\n }, [successMessage, autoHideSuccess, autoHideDuration]);\n\n return (\n <section\n ref={ref}\n id={id}\n data-testid={dataTestId}\n aria-label={ariaLabel}\n aria-labelledby={!ariaLabel ? finalAriaLabelledby : undefined}\n aria-describedby={ariaDescribedby}\n className={cn(\"form-layout w-full\", className)}\n {...props}\n >\n {/* Error Message Alert */}\n {errorMessage && (\n <div\n role=\"alert\"\n aria-live=\"assertive\"\n data-testid=\"form-layout-error\"\n className={cn(\n \"rounded-lg border px-4 py-3 mb-6 text-sm\",\n \"border-[var(--destructive-background)] bg-[var(--destructive-foreground)] text-[var(--destructive-background)]\",\n \"flex items-start gap-3\"\n )}\n >\n <AlertCircle className=\"h-5 w-5 flex-shrink-0 mt-0.5\" aria-hidden=\"true\" />\n <div className=\"flex-1\">\n <p className=\"font-medium\">Error</p>\n <p className=\"mt-1\">{errorMessage}</p>\n </div>\n </div>\n )}\n\n {/* Success Message Alert */}\n {successMessage && showSuccess && (\n <div\n role=\"status\"\n aria-live=\"polite\"\n data-testid=\"form-layout-success\"\n className={cn(\n \"rounded-lg border px-4 py-3 mb-6 text-sm\",\n \"border-success bg-success/10 text-success\",\n \"flex items-start gap-3\"\n )}\n >\n <CheckCircle2 className=\"h-5 w-5 flex-shrink-0 mt-0.5\" aria-hidden=\"true\" />\n <div className=\"flex-1\">\n <p className=\"font-medium\">Success</p>\n <p className=\"mt-1\">{successMessage}</p>\n </div>\n </div>\n )}\n\n {/* Header Section: Title → Description → Required Note */}\n <header className=\"form-layout-header mb-6\">\n {/* Title (configurable heading level) */}\n <Heading\n level={headingLevel}\n id={finalHeadingId}\n className=\"text-[var(--content-foreground)] font-semibold\"\n >\n {title}\n </Heading>\n\n {/* Description (optional) */}\n {description && (\n <p className=\"text-sm text-[var(--content-foreground)] leading-relaxed mt-2\">\n {description}\n </p>\n )}\n\n {/* Required Note (always visible) */}\n <p className=\"text-sm text-[var(--content-foreground)]\">\n {requiredNote}\n </p>\n </header>\n\n {/* Body Section: Form fields with configurable layout */}\n <div\n data-testid=\"form-layout-body\"\n className={cn(\n formLayoutBodyVariants({\n columns,\n density,\n })\n )}\n >\n {children}\n </div>\n\n {/* Actions Section: Optional footer for buttons */}\n {actions && (\n <div data-testid=\"form-layout-actions\" className=\"flex justify-between items-center mt-4 gap-2 max-md:flex-col max-md:w-full\">\n {actions}\n </div>\n )}\n </section>\n );\n }\n);\n\nFormLayout.displayName = \"FormLayout\";\n\nexport { formLayoutBodyVariants };\n"]}
@@ -1,169 +1,3 @@
1
1
  "use client";
2
- import { forwardRef, useId, useState, useEffect } from 'react';
3
- import { cva } from 'class-variance-authority';
4
- import { AlertCircle, CheckCircle2 } from 'lucide-react';
5
- import { clsx } from 'clsx';
6
- import { twMerge } from 'tailwind-merge';
7
- import { jsxs, jsx } from 'react/jsx-runtime';
8
-
9
- // src/elements/FormLayout/FormLayout.tsx
10
- function cn(...inputs) {
11
- return twMerge(clsx(inputs));
12
- }
13
- var formLayoutBodyVariants = cva(
14
- // Base styles - grid layout with responsive columns
15
- "grid grid-cols-1 mt-6",
16
- {
17
- variants: {
18
- // Column variants (responsive: mobile single, desktop optional double)
19
- columns: {
20
- 1: "",
21
- // Single column (no additional classes needed)
22
- 2: "md:grid-cols-2"
23
- // Double column on md breakpoint and up
24
- },
25
- // Density variants (controls gap between form fields)
26
- density: {
27
- default: "gap-4",
28
- // 16px gap
29
- compact: "gap-2",
30
- // 8px gap
31
- comfortable: "gap-6"
32
- // 24px gap
33
- }
34
- },
35
- defaultVariants: {
36
- columns: 1,
37
- density: "default"
38
- }
39
- }
40
- );
41
- var Heading = ({ level, id, className, children }) => {
42
- const Tag = `h${level}`;
43
- return /* @__PURE__ */ jsx(Tag, { id, className, children });
44
- };
45
- var FormLayout = forwardRef(
46
- ({
47
- title,
48
- requiredNote,
49
- description,
50
- columns = 1,
51
- headingLevel = 2,
52
- density = "default",
53
- errorMessage,
54
- successMessage,
55
- fieldErrors: _fieldErrors,
56
- autoHideSuccess = false,
57
- autoHideDuration = 5e3,
58
- className,
59
- children,
60
- actions,
61
- id,
62
- "data-testid": dataTestId,
63
- "aria-label": ariaLabel,
64
- "aria-labelledby": ariaLabelledby,
65
- "aria-describedby": ariaDescribedby,
66
- ...props
67
- }, ref) => {
68
- const headingId = useId();
69
- const finalHeadingId = id ? `${id}-title` : headingId;
70
- const finalAriaLabelledby = ariaLabelledby || finalHeadingId;
71
- const [showSuccess, setShowSuccess] = useState(true);
72
- useEffect(() => {
73
- setShowSuccess(true);
74
- if (successMessage && autoHideSuccess) {
75
- const timer = setTimeout(() => {
76
- setShowSuccess(false);
77
- }, autoHideDuration);
78
- return () => clearTimeout(timer);
79
- }
80
- }, [successMessage, autoHideSuccess, autoHideDuration]);
81
- return /* @__PURE__ */ jsxs(
82
- "section",
83
- {
84
- ref,
85
- id,
86
- "data-testid": dataTestId,
87
- "aria-label": ariaLabel,
88
- "aria-labelledby": !ariaLabel ? finalAriaLabelledby : void 0,
89
- "aria-describedby": ariaDescribedby,
90
- className: cn("form-layout w-full", className),
91
- ...props,
92
- children: [
93
- errorMessage && /* @__PURE__ */ jsxs(
94
- "div",
95
- {
96
- role: "alert",
97
- "aria-live": "assertive",
98
- "data-testid": "form-layout-error",
99
- className: cn(
100
- "rounded-lg border px-4 py-3 mb-6 text-sm",
101
- "border-[var(--destructive-background)] bg-[var(--destructive-foreground)] text-[var(--destructive-background)]",
102
- "flex items-start gap-3"
103
- ),
104
- children: [
105
- /* @__PURE__ */ jsx(AlertCircle, { className: "h-5 w-5 flex-shrink-0 mt-0.5", "aria-hidden": "true" }),
106
- /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
107
- /* @__PURE__ */ jsx("p", { className: "font-medium", children: "Error" }),
108
- /* @__PURE__ */ jsx("p", { className: "mt-1", children: errorMessage })
109
- ] })
110
- ]
111
- }
112
- ),
113
- successMessage && showSuccess && /* @__PURE__ */ jsxs(
114
- "div",
115
- {
116
- role: "status",
117
- "aria-live": "polite",
118
- "data-testid": "form-layout-success",
119
- className: cn(
120
- "rounded-lg border px-4 py-3 mb-6 text-sm",
121
- "border-success bg-success/10 text-success",
122
- "flex items-start gap-3"
123
- ),
124
- children: [
125
- /* @__PURE__ */ jsx(CheckCircle2, { className: "h-5 w-5 flex-shrink-0 mt-0.5", "aria-hidden": "true" }),
126
- /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
127
- /* @__PURE__ */ jsx("p", { className: "font-medium", children: "Success" }),
128
- /* @__PURE__ */ jsx("p", { className: "mt-1", children: successMessage })
129
- ] })
130
- ]
131
- }
132
- ),
133
- /* @__PURE__ */ jsxs("header", { className: "form-layout-header mb-6", children: [
134
- /* @__PURE__ */ jsx(
135
- Heading,
136
- {
137
- level: headingLevel,
138
- id: finalHeadingId,
139
- className: "text-[var(--content-foreground)] font-semibold",
140
- children: title
141
- }
142
- ),
143
- description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--content-foreground)] leading-relaxed mt-2", children: description }),
144
- /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--content-foreground)]", children: requiredNote })
145
- ] }),
146
- /* @__PURE__ */ jsx(
147
- "div",
148
- {
149
- "data-testid": "form-layout-body",
150
- className: cn(
151
- formLayoutBodyVariants({
152
- columns,
153
- density
154
- })
155
- ),
156
- children
157
- }
158
- ),
159
- actions && /* @__PURE__ */ jsx("div", { "data-testid": "form-layout-actions", className: "flex justify-between items-center mt-4 gap-2 max-md:flex-col max-md:w-full", children: actions })
160
- ]
161
- }
162
- );
163
- }
164
- );
165
- FormLayout.displayName = "FormLayout";
166
-
167
- export { FormLayout, formLayoutBodyVariants };
168
- //# sourceMappingURL=index.mjs.map
2
+ import {forwardRef,useId,useState,useEffect}from'react';import {cva}from'class-variance-authority';import {AlertCircle,CheckCircle2}from'lucide-react';import {clsx}from'clsx';import {twMerge}from'tailwind-merge';import {jsxs,jsx}from'react/jsx-runtime';function r(...a){return twMerge(clsx(a))}var g=cva("grid grid-cols-1 mt-6",{variants:{columns:{1:"",2:"md:grid-cols-2"},density:{default:"gap-4",compact:"gap-2",comfortable:"gap-6"}},defaultVariants:{columns:1,density:"default"}}),_=({level:a,id:l,className:o,children:d})=>{let i=`h${a}`;return jsx(i,{id:l,className:o,children:d})},x=forwardRef(({title:a,requiredNote:l,description:o,columns:d=1,headingLevel:i=2,density:b="default",errorMessage:n,successMessage:s,fieldErrors:j,autoHideSuccess:c=false,autoHideDuration:f=5e3,className:N,children:h,actions:u,id:m,"data-testid":L,"aria-label":p,"aria-labelledby":F,"aria-describedby":w,...C},k)=>{let E=useId(),y=m?`${m}-title`:E,H=F||y,[S,v]=useState(true);return useEffect(()=>{if(v(true),s&&c){let P=setTimeout(()=>{v(false);},f);return ()=>clearTimeout(P)}},[s,c,f]),jsxs("section",{ref:k,id:m,"data-testid":L,"aria-label":p,"aria-labelledby":p?void 0:H,"aria-describedby":w,className:r("form-layout w-full",N),...C,children:[n&&jsxs("div",{role:"alert","aria-live":"assertive","data-testid":"form-layout-error",className:r("rounded-lg border px-4 py-3 mb-6 text-sm","border-[var(--destructive-background)] bg-[var(--destructive-foreground)] text-[var(--destructive-background)]","flex items-start gap-3"),children:[jsx(AlertCircle,{className:"h-5 w-5 flex-shrink-0 mt-0.5","aria-hidden":"true"}),jsxs("div",{className:"flex-1",children:[jsx("p",{className:"font-medium",children:"Error"}),jsx("p",{className:"mt-1",children:n})]})]}),s&&S&&jsxs("div",{role:"status","aria-live":"polite","data-testid":"form-layout-success",className:r("rounded-lg border px-4 py-3 mb-6 text-sm","border-success bg-success/10 text-success","flex items-start gap-3"),children:[jsx(CheckCircle2,{className:"h-5 w-5 flex-shrink-0 mt-0.5","aria-hidden":"true"}),jsxs("div",{className:"flex-1",children:[jsx("p",{className:"font-medium",children:"Success"}),jsx("p",{className:"mt-1",children:s})]})]}),jsxs("header",{className:"form-layout-header mb-6",children:[jsx(_,{level:i,id:y,className:"text-[var(--content-foreground)] font-semibold",children:a}),o&&jsx("p",{className:"text-sm text-[var(--content-foreground)] leading-relaxed mt-2",children:o}),jsx("p",{className:"text-sm text-[var(--content-foreground)]",children:l})]}),jsx("div",{"data-testid":"form-layout-body",className:r(g({columns:d,density:b})),children:h}),u&&jsx("div",{"data-testid":"form-layout-actions",className:"flex justify-between items-center mt-4 gap-2 max-md:flex-col max-md:w-full",children:u})]})});x.displayName="FormLayout";export{x as FormLayout,g as formLayoutBodyVariants};//# sourceMappingURL=index.mjs.map
169
3
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/elements/FormLayout/FormLayout.tsx"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Class Name Utility\n * Merges Tailwind CSS classes with conflict resolution\n *\n * Combines clsx for conditional classes and tailwind-merge for deduplication\n *\n * @example\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4' (px-4 overrides px-2)\n * cn('text-red-500', condition && 'text-blue-500') // => conditional application\n */\n\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\n/**\n * FormLayout Component\n * Structural layout component for consistent form presentation\n *\n * @see FormLayout-plan.md (Component API Design)\n * @see FormLayout-design-decisions.md (Approved design specifications)\n * @see spec.md FR-009 to FR-014 (Accessibility Requirements - WCAG 2.2 AAA)\n * @see constitution.md Principle IV (Accessibility First)\n */\n\nimport { JSX, forwardRef, FC, useId, ReactNode, useState, useEffect } from 'react';\nimport { cva } from 'class-variance-authority';\nimport { AlertCircle, CheckCircle2 } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport type { FormLayoutProps, HeadingLevel } from './FormLayout.types';\n\n/**\n * Body variant styles using CVA\n * Controls column layout and field spacing (density)\n */\nconst formLayoutBodyVariants = cva(\n // Base styles - grid layout with responsive columns\n \"grid grid-cols-1 mt-6\",\n {\n variants: {\n // Column variants (responsive: mobile single, desktop optional double)\n columns: {\n 1: \"\", // Single column (no additional classes needed)\n 2: \"md:grid-cols-2\", // Double column on md breakpoint and up\n },\n // Density variants (controls gap between form fields)\n density: {\n default: \"gap-4\", // 16px gap\n compact: \"gap-2\", // 8px gap\n comfortable: \"gap-6\", // 24px gap\n },\n },\n defaultVariants: {\n columns: 1,\n density: \"default\",\n },\n }\n);\n\n/**\n * Polymorphic Heading Component\n * Renders h1-h6 based on headingLevel prop for proper document hierarchy\n */\ninterface HeadingProps {\n level: HeadingLevel;\n id: string;\n className?: string;\n children: ReactNode;\n}\n\nconst Heading: FC<HeadingProps> = ({ level, id, className, children }) => {\n const Tag = `h${level}` as keyof JSX.IntrinsicElements;\n\n return (\n <Tag id={id} className={className}>\n {children}\n </Tag>\n );\n};\n\n/**\n * FormLayout Component\n * Provides consistent form structure with header, body, and optional actions\n */\nexport const FormLayout = forwardRef<HTMLElement, FormLayoutProps>(\n (\n {\n title,\n requiredNote,\n description,\n columns = 1,\n headingLevel = 2,\n density = 'default',\n errorMessage,\n successMessage,\n fieldErrors: _fieldErrors,\n autoHideSuccess = false,\n autoHideDuration = 5000,\n className,\n children,\n actions,\n id,\n 'data-testid': dataTestId,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n 'aria-describedby': ariaDescribedby,\n ...props\n },\n ref\n ) => {\n // Generate unique IDs for accessibility\n const headingId = useId();\n const finalHeadingId = id ? `${id}-title` : headingId;\n\n // Determine aria-labelledby (use prop if provided, otherwise point to heading)\n const finalAriaLabelledby = ariaLabelledby || finalHeadingId;\n\n // Auto-hide success message state\n const [showSuccess, setShowSuccess] = useState(true);\n\n // Auto-hide success message after duration\n useEffect(() => {\n // Reset show state when message changes (must come first!)\n setShowSuccess(true);\n\n // Set timeout to hide if autoHideSuccess is enabled\n if (successMessage && autoHideSuccess) {\n const timer = setTimeout(() => {\n setShowSuccess(false);\n }, autoHideDuration);\n\n return (): void => clearTimeout(timer);\n }\n }, [successMessage, autoHideSuccess, autoHideDuration]);\n\n return (\n <section\n ref={ref}\n id={id}\n data-testid={dataTestId}\n aria-label={ariaLabel}\n aria-labelledby={!ariaLabel ? finalAriaLabelledby : undefined}\n aria-describedby={ariaDescribedby}\n className={cn(\"form-layout w-full\", className)}\n {...props}\n >\n {/* Error Message Alert */}\n {errorMessage && (\n <div\n role=\"alert\"\n aria-live=\"assertive\"\n data-testid=\"form-layout-error\"\n className={cn(\n \"rounded-lg border px-4 py-3 mb-6 text-sm\",\n \"border-[var(--destructive-background)] bg-[var(--destructive-foreground)] text-[var(--destructive-background)]\",\n \"flex items-start gap-3\"\n )}\n >\n <AlertCircle className=\"h-5 w-5 flex-shrink-0 mt-0.5\" aria-hidden=\"true\" />\n <div className=\"flex-1\">\n <p className=\"font-medium\">Error</p>\n <p className=\"mt-1\">{errorMessage}</p>\n </div>\n </div>\n )}\n\n {/* Success Message Alert */}\n {successMessage && showSuccess && (\n <div\n role=\"status\"\n aria-live=\"polite\"\n data-testid=\"form-layout-success\"\n className={cn(\n \"rounded-lg border px-4 py-3 mb-6 text-sm\",\n \"border-success bg-success/10 text-success\",\n \"flex items-start gap-3\"\n )}\n >\n <CheckCircle2 className=\"h-5 w-5 flex-shrink-0 mt-0.5\" aria-hidden=\"true\" />\n <div className=\"flex-1\">\n <p className=\"font-medium\">Success</p>\n <p className=\"mt-1\">{successMessage}</p>\n </div>\n </div>\n )}\n\n {/* Header Section: Title → Description → Required Note */}\n <header className=\"form-layout-header mb-6\">\n {/* Title (configurable heading level) */}\n <Heading\n level={headingLevel}\n id={finalHeadingId}\n className=\"text-[var(--content-foreground)] font-semibold\"\n >\n {title}\n </Heading>\n\n {/* Description (optional) */}\n {description && (\n <p className=\"text-sm text-[var(--content-foreground)] leading-relaxed mt-2\">\n {description}\n </p>\n )}\n\n {/* Required Note (always visible) */}\n <p className=\"text-sm text-[var(--content-foreground)]\">\n {requiredNote}\n </p>\n </header>\n\n {/* Body Section: Form fields with configurable layout */}\n <div\n data-testid=\"form-layout-body\"\n className={cn(\n formLayoutBodyVariants({\n columns,\n density,\n })\n )}\n >\n {children}\n </div>\n\n {/* Actions Section: Optional footer for buttons */}\n {actions && (\n <div data-testid=\"form-layout-actions\" className=\"flex justify-between items-center mt-4 gap-2 max-md:flex-col max-md:w-full\">\n {actions}\n </div>\n )}\n </section>\n );\n }\n);\n\nFormLayout.displayName = \"FormLayout\";\n\nexport { formLayoutBodyVariants };\n"]}
1
+ {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/elements/FormLayout/FormLayout.tsx"],"names":["cn","inputs","twMerge","clsx","formLayoutBodyVariants","cva","Heading","level","id","className","children","Tag","jsx","FormLayout","forwardRef","title","requiredNote","description","columns","headingLevel","density","errorMessage","successMessage","_fieldErrors","autoHideSuccess","autoHideDuration","actions","dataTestId","ariaLabel","ariaLabelledby","ariaDescribedby","props","ref","headingId","useId","finalHeadingId","finalAriaLabelledby","showSuccess","setShowSuccess","useState","useEffect","timer","jsxs","AlertCircle","CheckCircle2"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Class Name Utility\n * Merges Tailwind CSS classes with conflict resolution\n *\n * Combines clsx for conditional classes and tailwind-merge for deduplication\n *\n * @example\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4' (px-4 overrides px-2)\n * cn('text-red-500', condition && 'text-blue-500') // => conditional application\n */\n\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\n/**\n * FormLayout Component\n * Structural layout component for consistent form presentation\n *\n * @see FormLayout-plan.md (Component API Design)\n * @see FormLayout-design-decisions.md (Approved design specifications)\n * @see spec.md FR-009 to FR-014 (Accessibility Requirements - WCAG 2.2 AAA)\n * @see constitution.md Principle IV (Accessibility First)\n */\n\nimport { JSX, forwardRef, FC, useId, ReactNode, useState, useEffect } from 'react';\nimport { cva } from 'class-variance-authority';\nimport { AlertCircle, CheckCircle2 } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport type { FormLayoutProps, HeadingLevel } from './FormLayout.types';\n\n/**\n * Body variant styles using CVA\n * Controls column layout and field spacing (density)\n */\nconst formLayoutBodyVariants = cva(\n // Base styles - grid layout with responsive columns\n \"grid grid-cols-1 mt-6\",\n {\n variants: {\n // Column variants (responsive: mobile single, desktop optional double)\n columns: {\n 1: \"\", // Single column (no additional classes needed)\n 2: \"md:grid-cols-2\", // Double column on md breakpoint and up\n },\n // Density variants (controls gap between form fields)\n density: {\n default: \"gap-4\", // 16px gap\n compact: \"gap-2\", // 8px gap\n comfortable: \"gap-6\", // 24px gap\n },\n },\n defaultVariants: {\n columns: 1,\n density: \"default\",\n },\n }\n);\n\n/**\n * Polymorphic Heading Component\n * Renders h1-h6 based on headingLevel prop for proper document hierarchy\n */\ninterface HeadingProps {\n level: HeadingLevel;\n id: string;\n className?: string;\n children: ReactNode;\n}\n\nconst Heading: FC<HeadingProps> = ({ level, id, className, children }) => {\n const Tag = `h${level}` as keyof JSX.IntrinsicElements;\n\n return (\n <Tag id={id} className={className}>\n {children}\n </Tag>\n );\n};\n\n/**\n * FormLayout Component\n * Provides consistent form structure with header, body, and optional actions\n */\nexport const FormLayout = forwardRef<HTMLElement, FormLayoutProps>(\n (\n {\n title,\n requiredNote,\n description,\n columns = 1,\n headingLevel = 2,\n density = 'default',\n errorMessage,\n successMessage,\n fieldErrors: _fieldErrors,\n autoHideSuccess = false,\n autoHideDuration = 5000,\n className,\n children,\n actions,\n id,\n 'data-testid': dataTestId,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n 'aria-describedby': ariaDescribedby,\n ...props\n },\n ref\n ) => {\n // Generate unique IDs for accessibility\n const headingId = useId();\n const finalHeadingId = id ? `${id}-title` : headingId;\n\n // Determine aria-labelledby (use prop if provided, otherwise point to heading)\n const finalAriaLabelledby = ariaLabelledby || finalHeadingId;\n\n // Auto-hide success message state\n const [showSuccess, setShowSuccess] = useState(true);\n\n // Auto-hide success message after duration\n useEffect(() => {\n // Reset show state when message changes (must come first!)\n setShowSuccess(true);\n\n // Set timeout to hide if autoHideSuccess is enabled\n if (successMessage && autoHideSuccess) {\n const timer = setTimeout(() => {\n setShowSuccess(false);\n }, autoHideDuration);\n\n return (): void => clearTimeout(timer);\n }\n }, [successMessage, autoHideSuccess, autoHideDuration]);\n\n return (\n <section\n ref={ref}\n id={id}\n data-testid={dataTestId}\n aria-label={ariaLabel}\n aria-labelledby={!ariaLabel ? finalAriaLabelledby : undefined}\n aria-describedby={ariaDescribedby}\n className={cn(\"form-layout w-full\", className)}\n {...props}\n >\n {/* Error Message Alert */}\n {errorMessage && (\n <div\n role=\"alert\"\n aria-live=\"assertive\"\n data-testid=\"form-layout-error\"\n className={cn(\n \"rounded-lg border px-4 py-3 mb-6 text-sm\",\n \"border-[var(--destructive-background)] bg-[var(--destructive-foreground)] text-[var(--destructive-background)]\",\n \"flex items-start gap-3\"\n )}\n >\n <AlertCircle className=\"h-5 w-5 flex-shrink-0 mt-0.5\" aria-hidden=\"true\" />\n <div className=\"flex-1\">\n <p className=\"font-medium\">Error</p>\n <p className=\"mt-1\">{errorMessage}</p>\n </div>\n </div>\n )}\n\n {/* Success Message Alert */}\n {successMessage && showSuccess && (\n <div\n role=\"status\"\n aria-live=\"polite\"\n data-testid=\"form-layout-success\"\n className={cn(\n \"rounded-lg border px-4 py-3 mb-6 text-sm\",\n \"border-success bg-success/10 text-success\",\n \"flex items-start gap-3\"\n )}\n >\n <CheckCircle2 className=\"h-5 w-5 flex-shrink-0 mt-0.5\" aria-hidden=\"true\" />\n <div className=\"flex-1\">\n <p className=\"font-medium\">Success</p>\n <p className=\"mt-1\">{successMessage}</p>\n </div>\n </div>\n )}\n\n {/* Header Section: Title → Description → Required Note */}\n <header className=\"form-layout-header mb-6\">\n {/* Title (configurable heading level) */}\n <Heading\n level={headingLevel}\n id={finalHeadingId}\n className=\"text-[var(--content-foreground)] font-semibold\"\n >\n {title}\n </Heading>\n\n {/* Description (optional) */}\n {description && (\n <p className=\"text-sm text-[var(--content-foreground)] leading-relaxed mt-2\">\n {description}\n </p>\n )}\n\n {/* Required Note (always visible) */}\n <p className=\"text-sm text-[var(--content-foreground)]\">\n {requiredNote}\n </p>\n </header>\n\n {/* Body Section: Form fields with configurable layout */}\n <div\n data-testid=\"form-layout-body\"\n className={cn(\n formLayoutBodyVariants({\n columns,\n density,\n })\n )}\n >\n {children}\n </div>\n\n {/* Actions Section: Optional footer for buttons */}\n {actions && (\n <div data-testid=\"form-layout-actions\" className=\"flex justify-between items-center mt-4 gap-2 max-md:flex-col max-md:w-full\">\n {actions}\n </div>\n )}\n </section>\n );\n }\n);\n\nFormLayout.displayName = \"FormLayout\";\n\nexport { formLayoutBodyVariants };\n"]}
@@ -132,25 +132,20 @@ declare namespace ModalClose {
132
132
  var displayName: string;
133
133
  }
134
134
  /**
135
- * CVA Variants for Modal.Content
136
- *
137
- * @see PRD.md TR-001 (CVA Variant Styling)
138
- * @see PRD.md DS-001 (Size Variants)
135
+ * Re-export CVA variants from Modal.styles.ts for backwards compatibility.
136
+ * Consumers importing { modalContentVariants, modalOverlayVariants } from './Modal'
137
+ * will continue to work.
139
138
  */
140
- export declare const modalContentVariants: (props?: ({
141
- size?: "sm" | "lg" | "md" | "xl" | "full" | null | undefined;
142
- animation?: "none" | "slide" | "fade-zoom" | "fade" | null | undefined;
143
- } & import("class-variance-authority/types").ClassProp) | undefined) => string;
144
- /**
145
- * CVA Variants for Modal Overlay
146
- */
147
- export declare const modalOverlayVariants: (props?: ({
148
- animation?: "none" | "slide" | "fade-zoom" | "fade" | null | undefined;
149
- } & import("class-variance-authority/types").ClassProp) | undefined) => string;
139
+ export { modalContentVariants, modalOverlayVariants } from './Modal.styles';
150
140
  /**
151
141
  * Compound Component Export
152
142
  *
153
143
  * Follows Themis library pattern using Object.assign() for compound components.
144
+ * Enables usage like Modal.Trigger, Modal.Content, etc.
145
+ *
146
+ * @deprecated The Object.assign compound pattern will be removed in v2.
147
+ * Use the direct named exports (ModalTrigger, ModalContent, etc.) instead.
148
+ * This pattern is kept for backwards compatibility only.
154
149
  *
155
150
  * @see RESEARCH.md Section 1 (Compound Component Pattern)
156
151
  */
@@ -1 +1 @@
1
- {"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../../src/elements/Modal/Modal.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,OAAO,EAML,KAAK,YAAY,EAElB,MAAM,OAAO,CAAC;AAYf,OAAO,KAAK,EACV,UAAU,EACV,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,eAAe,EAChB,MAAM,eAAe,CAAC;AAYvB;;;;;;;;;;;GAWG;AACH,iBAAS,SAAS,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,IAAe,EAAE,EAAE,UAAU,GAAG,YAAY,CA6C7G;kBA7CQ,SAAS;;;AAiDlB;;;;;;;;;;;;;GAaG;AACH,iBAAS,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE,iBAAiB,GAAG,YAAY,CAInE;kBAJQ,YAAY;;;AAQrB;;;;;;;;;;GAUG;AACH,iBAAS,YAAY,CAAC,EACpB,QAAQ,EACR,IAAW,EACX,SAAuB,EACvB,iBAAuB,EACvB,aAAoB,EACpB,yBAAiC,EACjC,SAAgB,EAChB,SAAS,GACV,EAAE,iBAAiB,GAAG,YAAY,CA8ClC;kBAvDQ,YAAY;;;AA2DrB;;;GAGG;AACH,iBAAS,YAAY,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAErD;kBAFQ,YAAY;;;AAMrB;;;;;;;GAOG;AACH,iBAAS,WAAW,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,gBAAgB,GAAG,YAAY,CAM5E;kBANQ,WAAW;;;AAUpB;;;;;;;;GAQG;AACH,iBAAS,UAAU,CAAC,EAAE,QAAQ,EAAE,EAAS,EAAE,SAAS,EAAE,EAAE,eAAe,GAAG,YAAY,CAUrF;kBAVQ,UAAU;;;AAcnB;;;;;;;;GAQG;AACH,iBAAS,gBAAgB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,qBAAqB,GAAG,YAAY,CAUtF;kBAVQ,gBAAgB;;;AAczB;;;;;;;;;GASG;AACH,iBAAS,WAAW,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,gBAAgB,GAAG,YAAY,CAM5E;kBANQ,WAAW;;;AAUpB;;;;;;;GAOG;AACH,iBAAS,UAAU,CAAC,EAAE,QAAQ,EAAE,EAAE,eAAe,GAAG,YAAY,CA+B/D;kBA/BQ,UAAU;;;AAmCnB;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB;;;8EAoChC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB;;8EAyBhC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,KAAK;;;;;;;;;CAShB,CAAC;AAGH,OAAO,EACL,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,WAAW,EACX,UAAU,GACX,CAAC"}
1
+ {"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../../src/elements/Modal/Modal.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,OAAO,EAML,KAAK,YAAY,EAElB,MAAM,OAAO,CAAC;AAaf,OAAO,KAAK,EACV,UAAU,EACV,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,eAAe,EAChB,MAAM,eAAe,CAAC;AAYvB;;;;;;;;;;;GAWG;AACH,iBAAS,SAAS,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,IAAe,EAAE,EAAE,UAAU,GAAG,YAAY,CA6D7G;kBA7DQ,SAAS;;;AAiElB;;;;;;;;;;;;;GAaG;AACH,iBAAS,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE,iBAAiB,GAAG,YAAY,CAInE;kBAJQ,YAAY;;;AAQrB;;;;;;;;;;GAUG;AACH,iBAAS,YAAY,CAAC,EACpB,QAAQ,EACR,IAAW,EACX,SAAuB,EACvB,iBAAuB,EACvB,aAAoB,EACpB,yBAAiC,EACjC,SAAgB,EAChB,SAAS,GACV,EAAE,iBAAiB,GAAG,YAAY,CA2ClC;kBApDQ,YAAY;;;AAwDrB;;;GAGG;AACH,iBAAS,YAAY,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAErD;kBAFQ,YAAY;;;AAMrB;;;;;;;GAOG;AACH,iBAAS,WAAW,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,gBAAgB,GAAG,YAAY,CAE5E;kBAFQ,WAAW;;;AAMpB;;;;;;;;GAQG;AACH,iBAAS,UAAU,CAAC,EAAE,QAAQ,EAAE,EAAS,EAAE,SAAS,EAAE,EAAE,eAAe,GAAG,YAAY,CAMrF;kBANQ,UAAU;;;AAUnB;;;;;;;;GAQG;AACH,iBAAS,gBAAgB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,qBAAqB,GAAG,YAAY,CAMtF;kBANQ,gBAAgB;;;AAUzB;;;;;;;;;GASG;AACH,iBAAS,WAAW,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,gBAAgB,GAAG,YAAY,CAE5E;kBAFQ,WAAW;;;AAMpB;;;;;;;GAOG;AACH,iBAAS,UAAU,CAAC,EAAE,QAAQ,EAAE,EAAE,eAAe,GAAG,YAAY,CA+B/D;kBA/BQ,UAAU;;;AAmCnB;;;;GAIG;AACH,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE5E;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,KAAK;;;;;;;;;CAShB,CAAC;AAGH,OAAO,EACL,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,WAAW,EACX,UAAU,GACX,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { type VariantProps } from 'class-variance-authority';
2
+ /**
3
+ * CVA Variants for Modal.Content
4
+ *
5
+ * Size and animation variant definitions for the modal content container.
6
+ * Uses CSS custom properties for all colours to support theming.
7
+ *
8
+ * @see Modal.types.ts (ModalSize, ModalAnimation)
9
+ */
10
+ export declare const modalContentVariants: (props?: ({
11
+ size?: "sm" | "lg" | "md" | "xl" | "full" | null | undefined;
12
+ animation?: "none" | "slide" | "fade-zoom" | "fade" | null | undefined;
13
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
14
+ /**
15
+ * CVA Variants for Modal Overlay
16
+ *
17
+ * Animation variant definitions for the modal backdrop overlay.
18
+ * Uses CSS custom properties for all colours to support theming.
19
+ */
20
+ export declare const modalOverlayVariants: (props?: ({
21
+ animation?: "none" | "slide" | "fade-zoom" | "fade" | null | undefined;
22
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
23
+ /**
24
+ * Type exports for variant props
25
+ * Allows TypeScript inference of variant combinations
26
+ */
27
+ export type ModalContentVariantProps = VariantProps<typeof modalContentVariants>;
28
+ export type ModalOverlayVariantProps = VariantProps<typeof modalOverlayVariants>;
29
+ //# sourceMappingURL=Modal.styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Modal.styles.d.ts","sourceRoot":"","sources":["../../../src/elements/Modal/Modal.styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAElE;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB;;;8EAoChC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB;;8EAyBhC,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG,YAAY,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACjF,MAAM,MAAM,wBAAwB,GAAG,YAAY,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
@@ -11,4 +11,5 @@
11
11
  */
12
12
  export { Modal, ModalTrigger, ModalContent, ModalOverlay, ModalHeader, ModalTitle, ModalDescription, ModalFooter, ModalClose, modalContentVariants, modalOverlayVariants, } from './Modal';
13
13
  export type { ModalProps, ModalTriggerProps, ModalContentProps, ModalOverlayProps, ModalHeaderProps, ModalTitleProps, ModalDescriptionProps, ModalFooterProps, ModalCloseProps, ModalSize, ModalAnimation, } from './Modal.types';
14
+ export type { ModalContentVariantProps, ModalOverlayVariantProps, } from './Modal.styles';
14
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/elements/Modal/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EACL,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,WAAW,EACX,UAAU,EACV,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,cAAc,GACf,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/elements/Modal/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EACL,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,WAAW,EACX,UAAU,EACV,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,cAAc,GACf,MAAM,eAAe,CAAC;AAEvB,YAAY,EACV,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,gBAAgB,CAAC"}