@tribepad/themis 1.0.1 → 1.0.3
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.
- package/dist/elements/Accordion/index.js +1 -335
- package/dist/elements/Accordion/index.js.map +1 -1
- package/dist/elements/Accordion/index.mjs +1 -317
- package/dist/elements/Accordion/index.mjs.map +1 -1
- package/dist/elements/AlertDialog/AlertDialog.d.ts +43 -0
- package/dist/elements/AlertDialog/AlertDialog.d.ts.map +1 -0
- package/dist/elements/AlertDialog/AlertDialog.styles.d.ts +15 -0
- package/dist/elements/AlertDialog/AlertDialog.styles.d.ts.map +1 -0
- package/dist/elements/AlertDialog/AlertDialog.types.d.ts +72 -0
- package/dist/elements/AlertDialog/AlertDialog.types.d.ts.map +1 -0
- package/dist/elements/AlertDialog/index.d.ts +25 -0
- package/dist/elements/AlertDialog/index.d.ts.map +1 -0
- package/dist/elements/AlertDialog/index.js +3 -0
- package/dist/elements/AlertDialog/index.js.map +1 -0
- package/dist/elements/AlertDialog/index.mjs +3 -0
- package/dist/elements/AlertDialog/index.mjs.map +1 -0
- package/dist/elements/Avatar/index.js +1 -468
- package/dist/elements/Avatar/index.js.map +1 -1
- package/dist/elements/Avatar/index.mjs +1 -456
- package/dist/elements/Avatar/index.mjs.map +1 -1
- package/dist/elements/Badge/index.js +1 -243
- package/dist/elements/Badge/index.js.map +1 -1
- package/dist/elements/Badge/index.mjs +1 -234
- package/dist/elements/Badge/index.mjs.map +1 -1
- package/dist/elements/Breadcrumbs/index.js +1 -821
- package/dist/elements/Breadcrumbs/index.js.map +1 -1
- package/dist/elements/Breadcrumbs/index.mjs +1 -810
- package/dist/elements/Breadcrumbs/index.mjs.map +1 -1
- package/dist/elements/Button/Button.d.ts +26 -81
- package/dist/elements/Button/Button.d.ts.map +1 -1
- package/dist/elements/Button/Button.styles.d.ts +35 -0
- package/dist/elements/Button/Button.styles.d.ts.map +1 -0
- package/dist/elements/Button/Button.types.d.ts +20 -8
- package/dist/elements/Button/Button.types.d.ts.map +1 -1
- package/dist/elements/Button/index.js +1 -288
- package/dist/elements/Button/index.js.map +1 -1
- package/dist/elements/Button/index.mjs +1 -283
- package/dist/elements/Button/index.mjs.map +1 -1
- package/dist/elements/ButtonGroup/index.js +1 -237
- package/dist/elements/ButtonGroup/index.js.map +1 -1
- package/dist/elements/ButtonGroup/index.mjs +1 -222
- package/dist/elements/ButtonGroup/index.mjs.map +1 -1
- package/dist/elements/Card/index.js +1 -579
- package/dist/elements/Card/index.js.map +1 -1
- package/dist/elements/Card/index.mjs +1 -560
- package/dist/elements/Card/index.mjs.map +1 -1
- package/dist/elements/Carousel/Carousel.d.ts +1 -11
- package/dist/elements/Carousel/Carousel.d.ts.map +1 -1
- package/dist/elements/Carousel/LazyCarousel.d.ts +1 -1
- package/dist/elements/Carousel/LazyCarousel.d.ts.map +1 -1
- package/dist/elements/Carousel/index.js +1 -789
- package/dist/elements/Carousel/index.js.map +1 -1
- package/dist/elements/Carousel/index.mjs +1 -786
- package/dist/elements/Carousel/index.mjs.map +1 -1
- package/dist/elements/Chart/ChartContext.d.ts.map +1 -1
- package/dist/elements/Chart/index.js +1 -1842
- package/dist/elements/Chart/index.js.map +1 -1
- package/dist/elements/Chart/index.mjs +1 -1832
- package/dist/elements/Chart/index.mjs.map +1 -1
- package/dist/elements/Checkbox/index.js +1 -316
- package/dist/elements/Checkbox/index.js.map +1 -1
- package/dist/elements/Checkbox/index.mjs +1 -306
- package/dist/elements/Checkbox/index.mjs.map +1 -1
- package/dist/elements/CheckboxGroup/index.js +1 -455
- package/dist/elements/CheckboxGroup/index.js.map +1 -1
- package/dist/elements/CheckboxGroup/index.mjs +1 -439
- package/dist/elements/CheckboxGroup/index.mjs.map +1 -1
- package/dist/elements/Combobox/Combobox.d.ts +56 -0
- package/dist/elements/Combobox/Combobox.d.ts.map +1 -0
- package/dist/elements/Combobox/Combobox.styles.d.ts +29 -0
- package/dist/elements/Combobox/Combobox.styles.d.ts.map +1 -0
- package/dist/elements/Combobox/Combobox.types.d.ts +67 -0
- package/dist/elements/Combobox/Combobox.types.d.ts.map +1 -0
- package/dist/elements/Combobox/index.d.ts +20 -0
- package/dist/elements/Combobox/index.d.ts.map +1 -0
- package/dist/elements/Combobox/index.js +3 -0
- package/dist/elements/Combobox/index.js.map +1 -0
- package/dist/elements/Combobox/index.mjs +3 -0
- package/dist/elements/Combobox/index.mjs.map +1 -0
- package/dist/elements/DatePicker/DatePicker.d.ts +1 -1
- package/dist/elements/DatePicker/DatePicker.d.ts.map +1 -1
- package/dist/elements/DatePicker/index.js +1 -903
- package/dist/elements/DatePicker/index.js.map +1 -1
- package/dist/elements/DatePicker/index.mjs +1 -853
- package/dist/elements/DatePicker/index.mjs.map +1 -1
- package/dist/elements/Dropdown/Dropdown.d.ts +7 -15
- package/dist/elements/Dropdown/Dropdown.d.ts.map +1 -1
- package/dist/elements/Dropdown/Dropdown.styles.d.ts +22 -0
- package/dist/elements/Dropdown/Dropdown.styles.d.ts.map +1 -0
- package/dist/elements/Dropdown/index.d.ts +1 -0
- package/dist/elements/Dropdown/index.d.ts.map +1 -1
- package/dist/elements/Dropdown/index.js +1 -193
- package/dist/elements/Dropdown/index.js.map +1 -1
- package/dist/elements/Dropdown/index.mjs +1 -184
- package/dist/elements/Dropdown/index.mjs.map +1 -1
- package/dist/elements/FileField/index.js +1 -1539
- package/dist/elements/FileField/index.js.map +1 -1
- package/dist/elements/FileField/index.mjs +1 -1507
- package/dist/elements/FileField/index.mjs.map +1 -1
- package/dist/elements/FormLayout/index.js +1 -170
- package/dist/elements/FormLayout/index.js.map +1 -1
- package/dist/elements/FormLayout/index.mjs +1 -167
- package/dist/elements/FormLayout/index.mjs.map +1 -1
- package/dist/elements/Modal/Modal.d.ts +9 -14
- package/dist/elements/Modal/Modal.d.ts.map +1 -1
- package/dist/elements/Modal/Modal.styles.d.ts +29 -0
- package/dist/elements/Modal/Modal.styles.d.ts.map +1 -0
- package/dist/elements/Modal/index.d.ts +1 -0
- package/dist/elements/Modal/index.d.ts.map +1 -1
- package/dist/elements/Modal/index.js +1 -232
- package/dist/elements/Modal/index.js.map +1 -1
- package/dist/elements/Modal/index.mjs +1 -220
- package/dist/elements/Modal/index.mjs.map +1 -1
- package/dist/elements/NumberField/NumberField.variants.d.ts +1 -1
- package/dist/elements/NumberField/index.js +1 -666
- package/dist/elements/NumberField/index.js.map +1 -1
- package/dist/elements/NumberField/index.mjs +1 -654
- package/dist/elements/NumberField/index.mjs.map +1 -1
- package/dist/elements/OTPInput/OTPInput.d.ts.map +1 -1
- package/dist/elements/OTPInput/index.js +1 -734
- package/dist/elements/OTPInput/index.js.map +1 -1
- package/dist/elements/OTPInput/index.mjs +1 -732
- package/dist/elements/OTPInput/index.mjs.map +1 -1
- package/dist/elements/Pagination/Pagination.d.ts +45 -0
- package/dist/elements/Pagination/Pagination.d.ts.map +1 -0
- package/dist/elements/Pagination/Pagination.styles.d.ts +10 -0
- package/dist/elements/Pagination/Pagination.styles.d.ts.map +1 -0
- package/dist/elements/Pagination/Pagination.types.d.ts +55 -0
- package/dist/elements/Pagination/Pagination.types.d.ts.map +1 -0
- package/dist/elements/Pagination/index.d.ts +21 -0
- package/dist/elements/Pagination/index.d.ts.map +1 -0
- package/dist/elements/Pagination/index.js +3 -0
- package/dist/elements/Pagination/index.js.map +1 -0
- package/dist/elements/Pagination/index.mjs +3 -0
- package/dist/elements/Pagination/index.mjs.map +1 -0
- package/dist/elements/Panel/index.js +1 -330
- package/dist/elements/Panel/index.js.map +1 -1
- package/dist/elements/Panel/index.mjs +1 -323
- package/dist/elements/Panel/index.mjs.map +1 -1
- package/dist/elements/PasswordField/PasswordField.d.ts +27 -0
- package/dist/elements/PasswordField/PasswordField.d.ts.map +1 -0
- package/dist/elements/PasswordField/PasswordField.styles.d.ts +32 -0
- package/dist/elements/PasswordField/PasswordField.styles.d.ts.map +1 -0
- package/dist/elements/PasswordField/PasswordField.types.d.ts +100 -0
- package/dist/elements/PasswordField/PasswordField.types.d.ts.map +1 -0
- package/dist/elements/PasswordField/index.css +2 -0
- package/dist/elements/PasswordField/index.css.map +1 -0
- package/dist/elements/PasswordField/index.d.ts +20 -0
- package/dist/elements/PasswordField/index.d.ts.map +1 -0
- package/dist/elements/PasswordField/index.js +3 -0
- package/dist/elements/PasswordField/index.js.map +1 -0
- package/dist/elements/PasswordField/index.mjs +3 -0
- package/dist/elements/PasswordField/index.mjs.map +1 -0
- package/dist/elements/Progress/index.js +1 -187
- package/dist/elements/Progress/index.js.map +1 -1
- package/dist/elements/Progress/index.mjs +1 -181
- package/dist/elements/Progress/index.mjs.map +1 -1
- package/dist/elements/RadioGroup/index.js +1 -369
- package/dist/elements/RadioGroup/index.js.map +1 -1
- package/dist/elements/RadioGroup/index.mjs +1 -359
- package/dist/elements/RadioGroup/index.mjs.map +1 -1
- package/dist/elements/Resizable/index.js +1 -1580
- package/dist/elements/Resizable/index.js.map +1 -1
- package/dist/elements/Resizable/index.mjs +1 -1566
- package/dist/elements/Resizable/index.mjs.map +1 -1
- package/dist/elements/SearchField/SearchField.d.ts +27 -0
- package/dist/elements/SearchField/SearchField.d.ts.map +1 -0
- package/dist/elements/SearchField/SearchField.styles.d.ts +32 -0
- package/dist/elements/SearchField/SearchField.styles.d.ts.map +1 -0
- package/dist/elements/SearchField/SearchField.types.d.ts +45 -0
- package/dist/elements/SearchField/SearchField.types.d.ts.map +1 -0
- package/dist/elements/SearchField/index.css +2 -0
- package/dist/elements/SearchField/index.css.map +1 -0
- package/dist/elements/SearchField/index.d.ts +21 -0
- package/dist/elements/SearchField/index.d.ts.map +1 -0
- package/dist/elements/SearchField/index.js +3 -0
- package/dist/elements/SearchField/index.js.map +1 -0
- package/dist/elements/SearchField/index.mjs +3 -0
- package/dist/elements/SearchField/index.mjs.map +1 -0
- package/dist/elements/Select/Select.d.ts +19 -48
- package/dist/elements/Select/Select.d.ts.map +1 -1
- package/dist/elements/Select/Select.styles.d.ts +55 -0
- package/dist/elements/Select/Select.styles.d.ts.map +1 -0
- package/dist/elements/Select/index.js +1 -589
- package/dist/elements/Select/index.js.map +1 -1
- package/dist/elements/Select/index.mjs +1 -582
- package/dist/elements/Select/index.mjs.map +1 -1
- package/dist/elements/Skeleton/index.js +1 -82
- package/dist/elements/Skeleton/index.js.map +1 -1
- package/dist/elements/Skeleton/index.mjs +1 -78
- package/dist/elements/Skeleton/index.mjs.map +1 -1
- package/dist/elements/Switch/index.js +1 -179
- package/dist/elements/Switch/index.js.map +1 -1
- package/dist/elements/Switch/index.mjs +1 -173
- package/dist/elements/Switch/index.mjs.map +1 -1
- package/dist/elements/Table/Table.d.ts +3 -24
- package/dist/elements/Table/Table.d.ts.map +1 -1
- package/dist/elements/Table/Table.styles.d.ts +24 -0
- package/dist/elements/Table/Table.styles.d.ts.map +1 -0
- package/dist/elements/Table/index.js +1 -595
- package/dist/elements/Table/index.js.map +1 -1
- package/dist/elements/Table/index.mjs +1 -578
- package/dist/elements/Table/index.mjs.map +1 -1
- package/dist/elements/Tabs/Tabs.d.ts +5 -3
- package/dist/elements/Tabs/Tabs.d.ts.map +1 -1
- package/dist/elements/Tabs/Tabs.types.d.ts +15 -0
- package/dist/elements/Tabs/Tabs.types.d.ts.map +1 -1
- package/dist/elements/Tabs/index.js +1 -337
- package/dist/elements/Tabs/index.js.map +1 -1
- package/dist/elements/Tabs/index.mjs +1 -320
- package/dist/elements/Tabs/index.mjs.map +1 -1
- package/dist/elements/TextField/TextField.d.ts +6 -42
- package/dist/elements/TextField/TextField.d.ts.map +1 -1
- package/dist/elements/TextField/TextField.hooks.d.ts +63 -0
- package/dist/elements/TextField/TextField.hooks.d.ts.map +1 -0
- package/dist/elements/TextField/TextField.icons.d.ts +19 -0
- package/dist/elements/TextField/TextField.icons.d.ts.map +1 -0
- package/dist/elements/TextField/TextField.styles.d.ts +37 -0
- package/dist/elements/TextField/TextField.styles.d.ts.map +1 -0
- package/dist/elements/TextField/TextField.types.d.ts +3 -0
- package/dist/elements/TextField/TextField.types.d.ts.map +1 -1
- package/dist/elements/TextField/index.css +1 -22
- package/dist/elements/TextField/index.css.map +1 -1
- package/dist/elements/TextField/index.js +1 -902
- package/dist/elements/TextField/index.js.map +1 -1
- package/dist/elements/TextField/index.mjs +1 -882
- package/dist/elements/TextField/index.mjs.map +1 -1
- package/dist/elements/TimeField/index.js +1 -254
- package/dist/elements/TimeField/index.js.map +1 -1
- package/dist/elements/TimeField/index.mjs +1 -238
- package/dist/elements/TimeField/index.mjs.map +1 -1
- package/dist/elements/Toast/Toast.d.ts +0 -22
- package/dist/elements/Toast/Toast.d.ts.map +1 -1
- package/dist/elements/Toast/index.js +1 -737
- package/dist/elements/Toast/index.js.map +1 -1
- package/dist/elements/Toast/index.mjs +1 -724
- package/dist/elements/Toast/index.mjs.map +1 -1
- package/dist/elements/Tooltip/index.js +1 -323
- package/dist/elements/Tooltip/index.js.map +1 -1
- package/dist/elements/Tooltip/index.mjs +1 -310
- package/dist/elements/Tooltip/index.mjs.map +1 -1
- package/dist/elements/index.css +1 -22
- package/dist/elements/index.css.map +1 -1
- package/dist/elements/index.d.ts +13 -1
- package/dist/elements/index.d.ts.map +1 -1
- package/dist/elements/index.js +1 -12455
- package/dist/elements/index.js.map +1 -1
- package/dist/elements/index.mjs +1 -12233
- package/dist/elements/index.mjs.map +1 -1
- package/dist/index.css +1 -22
- package/dist/index.css.map +1 -1
- package/dist/index.js +2 -12490
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -12262
- package/dist/index.mjs.map +1 -1
- package/dist/schemas/index.js +2 -54
- package/dist/schemas/index.js.map +1 -1
- package/dist/schemas/index.mjs +2 -48
- package/dist/schemas/index.mjs.map +1 -1
- package/dist/styles/defaults.css +151 -0
- package/dist/styles/index.js +1 -166
- package/dist/styles/index.js.map +1 -1
- package/dist/styles/index.mjs +1 -129
- package/dist/styles/index.mjs.map +1 -1
- package/dist/styles/shared-variants.d.ts +3 -3
- package/dist/styles/shared-variants.d.ts.map +1 -1
- package/dist/utils/index.js +1 -12
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/index.mjs +1 -10
- package/dist/utils/index.mjs.map +1 -1
- package/package.json +9 -7
- package/src/elements/Accordion/Accordion.stories.tsx +1 -1
- package/src/elements/AlertDialog/AlertDialog.stories.tsx +124 -0
- package/src/elements/Avatar/Avatar.stories.tsx +1 -1
- package/src/elements/Badge/Badge.stories.tsx +1 -1
- package/src/elements/Breadcrumbs/Breadcrumbs.stories.tsx +1 -1
- package/src/elements/Button/Button.stories.tsx +1 -1
- package/src/elements/ButtonGroup/ButtonGroup.stories.tsx +1 -1
- package/src/elements/Card/Card.stories.tsx +1 -1
- package/src/elements/Carousel/Carousel.stories.tsx +1 -1
- package/src/elements/Chart/Chart.stories.tsx +1 -1
- package/src/elements/Checkbox/Checkbox.stories.tsx +1 -1
- package/src/elements/CheckboxGroup/CheckboxGroup.stories.tsx +4 -4
- package/src/elements/Combobox/Combobox.stories.tsx +133 -0
- package/src/elements/DatePicker/DatePicker.stories.tsx +1 -1
- package/src/elements/Dropdown/Dropdown.stories.tsx +1 -1
- package/src/elements/FileField/FileField.stories.tsx +2 -2
- package/src/elements/FileField/FileProgress.stories.tsx +1 -1
- package/src/elements/FormLayout/FormLayout.stories.tsx +1 -1
- package/src/elements/Modal/Modal.stories.tsx +1 -1
- package/src/elements/NumberField/NumberField.stories.tsx +1 -1
- package/src/elements/OTPInput/OTPInput.stories.tsx +1 -1
- package/src/elements/Pagination/Pagination.stories.tsx +203 -0
- package/src/elements/Panel/Panel.stories.tsx +1 -1
- package/src/elements/PasswordField/PasswordField.stories.tsx +167 -0
- package/src/elements/Progress/Progress.stories.tsx +7 -2
- package/src/elements/RadioGroup/RadioGroup.stories.tsx +3 -3
- package/src/elements/Resizable/Resizable.stories.tsx +1 -1
- package/src/elements/SearchField/SearchField.stories.tsx +146 -0
- package/src/elements/Select/Select.stories.tsx +1 -1
- package/src/elements/Skeleton/Skeleton.stories.tsx +1 -1
- package/src/elements/Switch/Switch.stories.tsx +1 -1
- package/src/elements/Table/Table.stories.tsx +1 -1
- package/src/elements/Tabs/Tabs.stories.tsx +46 -2
- package/src/elements/TextField/TextField.stories.tsx +1 -1
- package/src/elements/TimeField/TimeField.stories.tsx +1 -1
- package/src/elements/Toast/Toast.stories.tsx +1 -1
- 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":"4OAcO,SAASA,KAAMC,CAAAA,CAA8B,CAClD,OAAOC,qBAAAA,CAAQC,SAAAA,CAAKF,CAAM,CAAC,CAC7B,CCMA,IAAMG,CAAAA,CAAyBC,0BAAAA,CAE7B,uBAAA,CACA,CACE,QAAA,CAAU,CAER,QAAS,CACP,CAAA,CAAG,GACH,CAAA,CAAG,gBACL,EAEA,OAAA,CAAS,CACP,QAAS,OAAA,CACT,OAAA,CAAS,QACT,WAAA,CAAa,OACf,CACF,CAAA,CACA,eAAA,CAAiB,CACf,OAAA,CAAS,CAAA,CACT,QAAS,SACX,CACF,CACF,CAAA,CAaMC,CAAAA,CAA4B,CAAC,CAAE,KAAA,CAAAC,EAAO,EAAA,CAAAC,CAAAA,CAAI,UAAAC,CAAAA,CAAW,QAAA,CAAAC,CAAS,CAAA,GAAM,CACxE,IAAMC,CAAAA,CAAM,CAAA,CAAA,EAAIJ,CAAK,CAAA,CAAA,CAErB,OACEK,cAAAA,CAACD,EAAA,CAAI,EAAA,CAAIH,EAAI,SAAA,CAAWC,CAAAA,CACrB,SAAAC,CAAAA,CACH,CAEJ,EAMaG,CAAAA,CAAaC,gBAAAA,CACxB,CACE,CACE,KAAA,CAAAC,EACA,YAAA,CAAAC,CAAAA,CACA,YAAAC,CAAAA,CACA,OAAA,CAAAC,EAAU,CAAA,CACV,YAAA,CAAAC,EAAe,CAAA,CACf,OAAA,CAAAC,EAAU,SAAA,CACV,YAAA,CAAAC,EACA,cAAA,CAAAC,CAAAA,CACA,YAAaC,CAAAA,CACb,eAAA,CAAAC,EAAkB,KAAA,CAClB,gBAAA,CAAAC,EAAmB,GAAA,CACnB,SAAA,CAAAhB,EACA,QAAA,CAAAC,CAAAA,CACA,OAAA,CAAAgB,CAAAA,CACA,EAAA,CAAAlB,CAAAA,CACA,cAAemB,CAAAA,CACf,YAAA,CAAcC,EACd,iBAAA,CAAmBC,CAAAA,CACnB,mBAAoBC,CAAAA,CACpB,GAAGC,CACL,CAAA,CACAC,CAAAA,GACG,CAEH,IAAMC,CAAAA,CAAYC,aAAM,CAClBC,CAAAA,CAAiB3B,EAAK,CAAA,EAAGA,CAAE,CAAA,MAAA,CAAA,CAAWyB,CAAAA,CAGtCG,CAAAA,CAAsBP,CAAAA,EAAkBM,EAGxC,CAACE,CAAAA,CAAaC,CAAc,CAAA,CAAIC,cAAAA,CAAS,IAAI,CAAA,CAGnD,OAAAC,gBAAU,IAAM,CAKd,GAHAF,CAAAA,CAAe,IAAI,EAGfhB,CAAAA,EAAkBE,CAAAA,CAAiB,CACrC,IAAMiB,CAAAA,CAAQ,UAAA,CAAW,IAAM,CAC7BH,CAAAA,CAAe,KAAK,EACtB,CAAA,CAAGb,CAAgB,CAAA,CAEnB,OAAO,IAAY,YAAA,CAAagB,CAAK,CACvC,CACF,CAAA,CAAG,CAACnB,CAAAA,CAAgBE,CAAAA,CAAiBC,CAAgB,CAAC,CAAA,CAGpDiB,gBAAC,SAAA,CAAA,CACC,GAAA,CAAKV,EACL,EAAA,CAAIxB,CAAAA,CACJ,cAAamB,CAAAA,CACb,YAAA,CAAYC,EACZ,iBAAA,CAAkBA,CAAAA,CAAkC,OAAtBQ,CAAAA,CAC9B,kBAAA,CAAkBN,EAClB,SAAA,CAAW9B,CAAAA,CAAG,qBAAsBS,CAAS,CAAA,CAC5C,GAAGsB,CAAAA,CAGH,QAAA,CAAA,CAAAV,GACCqB,eAAAA,CAAC,KAAA,CAAA,CACC,IAAA,CAAK,OAAA,CACL,WAAA,CAAU,WAAA,CACV,cAAY,mBAAA,CACZ,SAAA,CAAW1C,EACT,0CAAA,CACA,gHAAA,CACA,wBACF,CAAA,CAEA,QAAA,CAAA,CAAAY,eAAC+B,uBAAAA,CAAA,CAAY,UAAU,8BAAA,CAA+B,aAAA,CAAY,OAAO,CAAA,CACzED,eAAAA,CAAC,OAAI,SAAA,CAAU,QAAA,CACb,QAAA,CAAA,CAAA9B,cAAAA,CAAC,GAAA,CAAA,CAAE,SAAA,CAAU,cAAc,QAAA,CAAA,OAAA,CAAK,CAAA,CAChCA,eAAC,GAAA,CAAA,CAAE,SAAA,CAAU,OAAQ,QAAA,CAAAS,CAAAA,CAAa,GACpC,CAAA,CAAA,CACF,CAAA,CAIDC,GAAkBe,CAAAA,EACjBK,eAAAA,CAAC,OACC,IAAA,CAAK,QAAA,CACL,YAAU,QAAA,CACV,aAAA,CAAY,qBAAA,CACZ,SAAA,CAAW1C,CAAAA,CACT,0CAAA,CACA,4CACA,wBACF,CAAA,CAEA,UAAAY,cAAAA,CAACgC,wBAAAA,CAAA,CAAa,SAAA,CAAU,8BAAA,CAA+B,cAAY,MAAA,CAAO,CAAA,CAC1EF,gBAAC,KAAA,CAAA,CAAI,SAAA,CAAU,SACb,QAAA,CAAA,CAAA9B,cAAAA,CAAC,KAAE,SAAA,CAAU,aAAA,CAAc,mBAAO,CAAA,CAClCA,cAAAA,CAAC,KAAE,SAAA,CAAU,MAAA,CAAQ,SAAAU,CAAAA,CAAe,CAAA,CAAA,CACtC,GACF,CAAA,CAIFoB,eAAAA,CAAC,UAAO,SAAA,CAAU,yBAAA,CAEhB,UAAA9B,cAAAA,CAACN,CAAAA,CAAA,CACC,KAAA,CAAOa,CAAAA,CACP,GAAIgB,CAAAA,CACJ,SAAA,CAAU,gDAAA,CAET,QAAA,CAAApB,CAAAA,CACH,CAAA,CAGCE,GACCL,cAAAA,CAAC,GAAA,CAAA,CAAE,UAAU,+DAAA,CACV,QAAA,CAAAK,EACH,CAAA,CAIFL,cAAAA,CAAC,KAAE,SAAA,CAAU,0CAAA,CACV,SAAAI,CAAAA,CACH,CAAA,CAAA,CACF,EAGAJ,cAAAA,CAAC,KAAA,CAAA,CACC,cAAY,kBAAA,CACZ,SAAA,CAAWZ,CAAAA,CACTI,CAAAA,CAAuB,CACrB,OAAA,CAAAc,EACA,OAAA,CAAAE,CACF,CAAC,CACH,CAAA,CAEC,SAAAV,CAAAA,CACH,CAAA,CAGCgB,GACCd,cAAAA,CAAC,KAAA,CAAA,CAAI,cAAY,qBAAA,CAAsB,SAAA,CAAU,6EAC9C,QAAA,CAAAc,CAAAA,CACH,GAEJ,CAEJ,CACF,EAEAb,CAAAA,CAAW,WAAA,CAAc,YAAA","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 {
|
|
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
|
|
136
|
-
*
|
|
137
|
-
*
|
|
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
|
|
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;
|
|
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"}
|