doom-design-system 0.1.15 → 0.2.1
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/components/Accordion/Accordion.d.ts +7 -6
- package/dist/components/Accordion/Accordion.js +36 -28
- package/dist/components/Accordion/Accordion.module.css +78 -22
- package/dist/components/Avatar/Avatar.d.ts +4 -4
- package/dist/components/Avatar/Avatar.js +6 -6
- package/dist/components/Badge/Badge.module.css +3 -3
- package/dist/components/Button/Button.module.css +1 -1
- package/dist/components/Checkbox/Checkbox.d.ts +2 -2
- package/dist/components/Checkbox/Checkbox.js +7 -7
- package/dist/components/Drawer/Drawer.js +5 -3
- package/dist/components/Drawer/Drawer.module.css +8 -6
- package/dist/components/Form/Form.module.css +1 -0
- package/dist/components/Input/Input.d.ts +3 -2
- package/dist/components/Input/Input.js +15 -3
- package/dist/components/Input/Input.module.css +33 -0
- package/dist/components/Modal/Modal.d.ts +5 -4
- package/dist/components/Modal/Modal.js +13 -6
- package/dist/components/Modal/Modal.module.css +70 -29
- package/dist/components/Popover/Popover.d.ts +3 -3
- package/dist/components/Popover/Popover.js +38 -36
- package/dist/components/ProgressBar/ProgressBar.d.ts +2 -1
- package/dist/components/ProgressBar/ProgressBar.js +10 -5
- package/dist/components/ProgressBar/ProgressBar.module.css +7 -0
- package/dist/components/Select/Select.js +6 -4
- package/dist/components/Select/Select.module.css +2 -2
- package/dist/components/Sheet/Sheet.d.ts +2 -2
- package/dist/components/Sheet/Sheet.js +18 -16
- package/dist/components/Sheet/Sheet.module.css +1 -2
- package/dist/components/Slider/Slider.d.ts +3 -3
- package/dist/components/Slider/Slider.js +11 -7
- package/dist/components/Slider/Slider.module.css +1 -1
- package/dist/components/SplitButton/SplitButton.d.ts +2 -2
- package/dist/components/SplitButton/SplitButton.js +8 -8
- package/dist/components/Tabs/Tabs.js +2 -2
- package/dist/components/Tabs/Tabs.module.css +9 -7
- package/dist/components/Textarea/Textarea.d.ts +8 -3
- package/dist/components/Textarea/Textarea.js +25 -6
- package/dist/components/Textarea/Textarea.module.css +71 -0
- package/dist/styles/themes/definitions.d.ts +312 -300
- package/dist/styles/themes/definitions.js +60 -60
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +2 -2
- package/dist/tsconfig.tsbuildinfo +0 -1
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import React from
|
|
1
|
+
import React from "react";
|
|
2
2
|
interface AccordionItemProps {
|
|
3
3
|
value: string;
|
|
4
4
|
trigger: string;
|
|
5
5
|
children: React.ReactNode;
|
|
6
|
-
|
|
7
|
-
onToggle?: () => void;
|
|
6
|
+
className?: string;
|
|
8
7
|
}
|
|
9
|
-
export declare function AccordionItem({ value, trigger, children,
|
|
8
|
+
export declare function AccordionItem({ value, trigger, children, className, }: AccordionItemProps): import("react/jsx-runtime").JSX.Element;
|
|
10
9
|
interface AccordionProps {
|
|
11
|
-
type?:
|
|
10
|
+
type?: "single" | "multiple";
|
|
12
11
|
children: React.ReactNode;
|
|
13
12
|
defaultValue?: string | string[];
|
|
13
|
+
value?: string | string[];
|
|
14
|
+
onValueChange?: (value: string | string[]) => void;
|
|
14
15
|
className?: string;
|
|
15
16
|
}
|
|
16
|
-
export declare function Accordion({ type, children, defaultValue, className }: AccordionProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export declare function Accordion({ type, children, defaultValue, value: controlledValue, onValueChange, className, }: AccordionProps): import("react/jsx-runtime").JSX.Element;
|
|
17
18
|
export {};
|
|
@@ -1,36 +1,44 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import clsx from
|
|
4
|
-
import { ChevronDown } from
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
import { ChevronDown } from "lucide-react";
|
|
5
|
+
import { Stack } from "../Layout/Layout.js";
|
|
6
|
+
import { Button } from "../Button/Button.js";
|
|
7
|
+
import { Text } from "../Text/Text.js";
|
|
8
|
+
import styles from "./Accordion.module.css";
|
|
9
|
+
import React, { useState } from "react";
|
|
10
|
+
const AccordionContext = React.createContext(null);
|
|
11
|
+
export function AccordionItem({ value, trigger, children, className, }) {
|
|
12
|
+
const context = React.useContext(AccordionContext);
|
|
13
|
+
if (!context)
|
|
14
|
+
throw new Error("AccordionItem must be used within Accordion");
|
|
15
|
+
const reactId = React.useId();
|
|
16
|
+
const triggerId = `accordion-trigger-${reactId}`;
|
|
17
|
+
const contentId = `accordion-content-${reactId}`;
|
|
18
|
+
const isOpen = Array.isArray(context.value)
|
|
19
|
+
? context.value.includes(value)
|
|
20
|
+
: context.value === value;
|
|
21
|
+
return (_jsxs("div", { className: clsx(styles.item, isOpen && styles.isOpen, className), children: [_jsx(Text, { variant: "h3", className: styles.header, children: _jsxs(Button, { id: triggerId, variant: "ghost", className: styles.trigger, onClick: () => context.onToggle(value), "aria-expanded": isOpen, "aria-controls": contentId, children: [trigger, _jsx(ChevronDown, { size: 20, strokeWidth: 2.5, className: styles.icon })] }) }), _jsx("div", { id: contentId, className: styles.contentWrapper, "aria-hidden": !isOpen, role: "region", "aria-labelledby": triggerId, children: _jsx("div", { className: styles.contentBody, children: children }) })] }));
|
|
9
22
|
}
|
|
10
|
-
export function Accordion({ type =
|
|
11
|
-
const [
|
|
23
|
+
export function Accordion({ type = "single", children, defaultValue, value: controlledValue, onValueChange, className, }) {
|
|
24
|
+
const [internalValue, setInternalValue] = useState(defaultValue || (type === "multiple" ? [] : ""));
|
|
25
|
+
const isControlled = controlledValue !== undefined;
|
|
26
|
+
const currentValue = isControlled ? controlledValue : internalValue;
|
|
12
27
|
const handleToggle = (itemValue) => {
|
|
13
|
-
|
|
14
|
-
|
|
28
|
+
let newValue;
|
|
29
|
+
if (type === "single") {
|
|
30
|
+
newValue = currentValue === itemValue ? "" : itemValue;
|
|
15
31
|
}
|
|
16
32
|
else {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
return [...arr, itemValue];
|
|
23
|
-
});
|
|
33
|
+
const arr = Array.isArray(currentValue) ? currentValue : [];
|
|
34
|
+
newValue = arr.includes(itemValue)
|
|
35
|
+
? arr.filter((v) => v !== itemValue)
|
|
36
|
+
: [...arr, itemValue];
|
|
24
37
|
}
|
|
38
|
+
if (!isControlled) {
|
|
39
|
+
setInternalValue(newValue);
|
|
40
|
+
}
|
|
41
|
+
onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(newValue);
|
|
25
42
|
};
|
|
26
|
-
return (_jsx(
|
|
27
|
-
if (!React.isValidElement(child))
|
|
28
|
-
return null;
|
|
29
|
-
const itemValue = child.props.value;
|
|
30
|
-
const isOpen = Array.isArray(value) ? value.includes(itemValue) : value === itemValue;
|
|
31
|
-
return React.cloneElement(child, {
|
|
32
|
-
isOpen,
|
|
33
|
-
onToggle: () => handleToggle(itemValue),
|
|
34
|
-
});
|
|
35
|
-
}) }));
|
|
43
|
+
return (_jsx(AccordionContext.Provider, { value: { value: currentValue, onToggle: handleToggle, type }, children: _jsx(Stack, { gap: 0, className: clsx(styles.root, className), children: children }) }));
|
|
36
44
|
}
|
|
@@ -1,52 +1,108 @@
|
|
|
1
1
|
.root {
|
|
2
2
|
display: flex;
|
|
3
3
|
flex-direction: column;
|
|
4
|
-
|
|
5
|
-
border-radius: var(--radius);
|
|
6
|
-
background-color: var(--card-bg);
|
|
7
|
-
overflow: hidden;
|
|
4
|
+
padding: var(--spacing-lg);
|
|
8
5
|
}
|
|
9
6
|
|
|
10
7
|
.item {
|
|
11
|
-
|
|
8
|
+
background-color: var(--card-bg);
|
|
9
|
+
border: var(--border-width) solid var(--card-border);
|
|
10
|
+
box-shadow: var(--shadow-hard);
|
|
11
|
+
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
12
|
+
position: relative;
|
|
13
|
+
overflow: hidden;
|
|
14
|
+
}
|
|
15
|
+
.item:not(:first-child) {
|
|
16
|
+
margin-top: calc(var(--border-width) * -1);
|
|
17
|
+
}
|
|
18
|
+
.item:first-child {
|
|
19
|
+
border-top-left-radius: var(--radius);
|
|
20
|
+
border-top-right-radius: var(--radius);
|
|
12
21
|
}
|
|
13
22
|
.item:last-child {
|
|
14
|
-
border-bottom:
|
|
23
|
+
border-bottom-left-radius: var(--radius);
|
|
24
|
+
border-bottom-right-radius: var(--radius);
|
|
25
|
+
}
|
|
26
|
+
.item.isOpen + .item {
|
|
27
|
+
border-top-left-radius: var(--radius);
|
|
28
|
+
border-top-right-radius: var(--radius);
|
|
29
|
+
margin-top: var(--spacing-sm);
|
|
30
|
+
}
|
|
31
|
+
.item:has(+ .item.isOpen) {
|
|
32
|
+
border-bottom-left-radius: var(--radius);
|
|
33
|
+
border-bottom-right-radius: var(--radius);
|
|
34
|
+
margin-bottom: var(--spacing-sm);
|
|
35
|
+
}
|
|
36
|
+
.item.isOpen {
|
|
37
|
+
margin: var(--spacing-sm) 0;
|
|
38
|
+
transform: translate(-2px, -2px);
|
|
39
|
+
border-radius: var(--radius);
|
|
40
|
+
z-index: 10;
|
|
41
|
+
box-shadow: var(--shadow-hard);
|
|
42
|
+
}
|
|
43
|
+
.item.isOpen + .item.isOpen {
|
|
44
|
+
margin-top: var(--spacing-sm);
|
|
45
|
+
}
|
|
46
|
+
.item.isOpen .trigger.trigger {
|
|
47
|
+
background-color: var(--primary);
|
|
48
|
+
color: var(--primary-foreground);
|
|
49
|
+
}
|
|
50
|
+
.item.isOpen .trigger.trigger:hover {
|
|
51
|
+
background-color: var(--primary);
|
|
52
|
+
filter: brightness(1.1);
|
|
53
|
+
}
|
|
54
|
+
.item.isOpen .contentBody {
|
|
55
|
+
border-top: var(--border-width) solid var(--card-border);
|
|
56
|
+
padding-top: var(--spacing-md);
|
|
15
57
|
}
|
|
16
58
|
|
|
17
59
|
.header {
|
|
18
|
-
display: flex;
|
|
19
60
|
margin: 0;
|
|
61
|
+
display: flex;
|
|
20
62
|
}
|
|
21
63
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
64
|
+
/* Specificity bridge to override Button defaults without !important */
|
|
65
|
+
.trigger.trigger {
|
|
66
|
+
width: 100%;
|
|
67
|
+
height: auto;
|
|
68
|
+
border: none;
|
|
69
|
+
border-radius: 0;
|
|
70
|
+
box-shadow: none;
|
|
71
|
+
transform: none;
|
|
27
72
|
justify-content: space-between;
|
|
28
73
|
padding: var(--spacing-md);
|
|
74
|
+
background-color: var(--card-bg);
|
|
75
|
+
color: var(--foreground);
|
|
76
|
+
text-transform: none;
|
|
77
|
+
letter-spacing: normal;
|
|
29
78
|
font-family: var(--font-heading);
|
|
30
79
|
font-weight: 700;
|
|
31
80
|
font-size: var(--text-base);
|
|
32
|
-
background-color: var(--card-bg);
|
|
33
|
-
color: var(--foreground);
|
|
34
|
-
cursor: pointer;
|
|
35
|
-
transition: background-color 0.2s ease;
|
|
36
81
|
}
|
|
37
|
-
.trigger
|
|
82
|
+
.trigger.trigger::after {
|
|
83
|
+
display: none;
|
|
84
|
+
}
|
|
85
|
+
.trigger.trigger:hover {
|
|
38
86
|
background-color: color-mix(in srgb, var(--primary) 25%, transparent);
|
|
87
|
+
transform: none;
|
|
88
|
+
box-shadow: none;
|
|
39
89
|
}
|
|
40
|
-
.trigger:focus-visible {
|
|
41
|
-
outline:
|
|
90
|
+
.trigger.trigger:focus-visible {
|
|
91
|
+
outline: none;
|
|
92
|
+
background-color: color-mix(in srgb, var(--primary) 15%, transparent);
|
|
93
|
+
box-shadow: inset 0 0 0 2px var(--primary);
|
|
42
94
|
position: relative;
|
|
43
95
|
z-index: 1;
|
|
44
96
|
}
|
|
45
|
-
.trigger
|
|
46
|
-
|
|
97
|
+
.trigger.trigger[aria-expanded=true] {
|
|
98
|
+
background-color: var(--primary);
|
|
99
|
+
color: var(--primary-foreground);
|
|
100
|
+
}
|
|
101
|
+
.trigger.trigger .icon {
|
|
102
|
+
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
47
103
|
transform: rotate(0deg);
|
|
48
104
|
}
|
|
49
|
-
.trigger[aria-expanded=true] .icon {
|
|
105
|
+
.trigger.trigger[aria-expanded=true] .icon {
|
|
50
106
|
transform: rotate(180deg);
|
|
51
107
|
}
|
|
52
108
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
export type AvatarSize =
|
|
3
|
-
export type AvatarShape =
|
|
1
|
+
import React from "react";
|
|
2
|
+
export type AvatarSize = "sm" | "md" | "lg" | "xl";
|
|
3
|
+
export type AvatarShape = "circle" | "square";
|
|
4
4
|
interface AvatarProps {
|
|
5
5
|
src?: string;
|
|
6
6
|
alt?: string;
|
|
@@ -9,5 +9,5 @@ interface AvatarProps {
|
|
|
9
9
|
shape?: AvatarShape;
|
|
10
10
|
className?: string;
|
|
11
11
|
}
|
|
12
|
-
export declare function Avatar({ src, alt, fallback, size, shape, className }: AvatarProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export declare function Avatar({ src, alt, fallback, size, shape, className, }: AvatarProps): import("react/jsx-runtime").JSX.Element;
|
|
13
13
|
export {};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { useState } from
|
|
4
|
-
import clsx from
|
|
5
|
-
import styles from
|
|
6
|
-
export function Avatar({ src, alt =
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import clsx from "clsx";
|
|
5
|
+
import styles from "./Avatar.module.css";
|
|
6
|
+
export function Avatar({ src, alt = "Avatar", fallback, size = "md", shape = "square", className, }) {
|
|
7
7
|
const [hasError, setHasError] = useState(false);
|
|
8
|
-
return (_jsx("div", { className: clsx(styles.avatar, styles[size], styles[shape], className), children: src && !hasError ? (_jsx("img", { src: src, alt: alt, className: styles.image, onError: () => setHasError(true) })) : (_jsx("span", { className: clsx(styles.fallback, styles[size]), children: typeof fallback ===
|
|
8
|
+
return (_jsx("div", { className: clsx(styles.avatar, styles[size], styles[shape], className), children: src && !hasError ? (_jsx("img", { src: src, alt: alt, className: styles.image, onError: () => setHasError(true) })) : (_jsx("span", { className: clsx(styles.fallback, styles[size]), children: typeof fallback === "string" ? fallback.slice(0, 2) : fallback })) }));
|
|
9
9
|
}
|
|
@@ -15,15 +15,15 @@
|
|
|
15
15
|
}
|
|
16
16
|
.badge.success {
|
|
17
17
|
background-color: var(--success);
|
|
18
|
-
color: var(--
|
|
18
|
+
color: var(--success-foreground);
|
|
19
19
|
}
|
|
20
20
|
.badge.warning {
|
|
21
21
|
background-color: var(--warning);
|
|
22
|
-
color: var(--
|
|
22
|
+
color: var(--warning-foreground);
|
|
23
23
|
}
|
|
24
24
|
.badge.error {
|
|
25
25
|
background-color: var(--error);
|
|
26
|
-
color: var(--
|
|
26
|
+
color: var(--error-foreground);
|
|
27
27
|
}
|
|
28
28
|
.badge.secondary {
|
|
29
29
|
background-color: var(--secondary);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
export interface CheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>,
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface CheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> {
|
|
3
3
|
label?: string;
|
|
4
4
|
error?: boolean;
|
|
5
5
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
2
|
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
3
|
var t = {};
|
|
4
4
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
@@ -11,14 +11,14 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
return t;
|
|
12
12
|
};
|
|
13
13
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
14
|
-
import { forwardRef, useId } from
|
|
15
|
-
import clsx from
|
|
16
|
-
import { Check } from
|
|
17
|
-
import { Label } from
|
|
18
|
-
import styles from
|
|
14
|
+
import { forwardRef, useId } from "react";
|
|
15
|
+
import clsx from "clsx";
|
|
16
|
+
import { Check } from "lucide-react";
|
|
17
|
+
import { Label } from "../Label/index.js";
|
|
18
|
+
import styles from "./Checkbox.module.css";
|
|
19
19
|
export const Checkbox = forwardRef((_a, ref) => {
|
|
20
20
|
var { className, label, error, disabled, checked, defaultChecked, onChange, id: propsId } = _a, props = __rest(_a, ["className", "label", "error", "disabled", "checked", "defaultChecked", "onChange", "id"]);
|
|
21
21
|
const generatedId = useId();
|
|
22
22
|
const id = propsId || generatedId;
|
|
23
|
-
return (_jsxs(
|
|
23
|
+
return (_jsxs(Label, { htmlFor: id, className: clsx(styles.checkboxWrapper, disabled && styles.disabled, className), children: [_jsx("input", Object.assign({ id: id, type: "checkbox", className: styles.checkboxInput, ref: ref, disabled: disabled, checked: checked, defaultChecked: defaultChecked, onChange: onChange }, props)), _jsx("span", { className: clsx(styles.checkboxDisplay), "aria-hidden": "true", children: _jsx(Check, { className: styles.icon }) }), label && _jsx("span", { className: styles.labelOverride, children: label })] }));
|
|
24
24
|
});
|
|
@@ -5,8 +5,10 @@ import { createPortal } from "react-dom";
|
|
|
5
5
|
import { X } from "lucide-react";
|
|
6
6
|
import { Button } from "../Button/Button.js";
|
|
7
7
|
import styles from "./Drawer.module.css";
|
|
8
|
-
import { useEffect, useState } from "react";
|
|
8
|
+
import React, { useEffect, useState } from "react";
|
|
9
9
|
export function Drawer({ isOpen, onClose, title, side = "right", children, footer, className, }) {
|
|
10
|
+
const reactId = React.useId();
|
|
11
|
+
const titleId = `drawer-title-${reactId}`;
|
|
10
12
|
const [mounted, setMounted] = useState(false);
|
|
11
13
|
useEffect(() => {
|
|
12
14
|
setMounted(true);
|
|
@@ -29,7 +31,7 @@ export function Drawer({ isOpen, onClose, title, side = "right", children, foote
|
|
|
29
31
|
window.addEventListener("keydown", handleEsc);
|
|
30
32
|
return () => window.removeEventListener("keydown", handleEsc);
|
|
31
33
|
}, [isOpen, onClose]);
|
|
32
|
-
if (!mounted
|
|
34
|
+
if (!mounted)
|
|
33
35
|
return null;
|
|
34
|
-
return createPortal(_jsxs(_Fragment, { children: [_jsx("div", { className: clsx(styles.overlay, isOpen && styles.
|
|
36
|
+
return createPortal(_jsxs(_Fragment, { children: [_jsx("div", { className: clsx(styles.overlay, isOpen && styles.isOpen), onClick: onClose, "aria-hidden": "true" }), _jsxs("div", { className: clsx(styles.panel, styles[side], isOpen && styles.isOpen, className), role: "dialog", "aria-modal": "true", "aria-labelledby": title ? titleId : undefined, "aria-label": !title ? "Drawer" : undefined, children: [_jsxs("div", { className: styles.header, children: [title && (_jsx("h2", { id: titleId, className: styles.title, children: title })), _jsx(Button, { variant: "ghost", size: "sm", onClick: onClose, "aria-label": "Close drawer", children: _jsx(X, { size: 24 }) })] }), _jsx("div", { className: styles.content, children: children }), footer && _jsx("div", { className: styles.footer, children: footer })] })] }), document.body);
|
|
35
37
|
}
|
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
background-color: rgba(0, 0, 0, var(--overlay-opacity));
|
|
5
5
|
backdrop-filter: blur(4px);
|
|
6
6
|
opacity: 0;
|
|
7
|
-
|
|
7
|
+
visibility: hidden;
|
|
8
|
+
transition: opacity 0.2s ease-in-out, visibility 0.2s ease-in-out;
|
|
8
9
|
pointer-events: none;
|
|
9
10
|
z-index: var(--z-dropdown);
|
|
10
11
|
}
|
|
11
|
-
.overlay.
|
|
12
|
+
.overlay.isOpen {
|
|
12
13
|
opacity: 1;
|
|
14
|
+
visibility: visible;
|
|
13
15
|
pointer-events: auto;
|
|
14
16
|
}
|
|
15
17
|
|
|
@@ -19,7 +21,7 @@
|
|
|
19
21
|
z-index: var(--z-modal);
|
|
20
22
|
display: flex;
|
|
21
23
|
flex-direction: column;
|
|
22
|
-
transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
|
24
|
+
transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), visibility 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
|
23
25
|
top: 0;
|
|
24
26
|
bottom: 0;
|
|
25
27
|
width: 100%;
|
|
@@ -31,16 +33,16 @@
|
|
|
31
33
|
box-shadow: 8px 0px 0px 0px var(--card-border);
|
|
32
34
|
border-top-right-radius: var(--radius);
|
|
33
35
|
border-bottom-right-radius: var(--radius);
|
|
34
|
-
transform: translateX(-100%);
|
|
36
|
+
transform: translateX(calc(-100% - 20px));
|
|
35
37
|
}
|
|
36
38
|
.panel.right {
|
|
37
39
|
right: 0;
|
|
38
40
|
box-shadow: -8px 0px 0px 0px var(--card-border);
|
|
39
41
|
border-top-left-radius: var(--radius);
|
|
40
42
|
border-bottom-left-radius: var(--radius);
|
|
41
|
-
transform: translateX(100%);
|
|
43
|
+
transform: translateX(calc(100% + 20px));
|
|
42
44
|
}
|
|
43
|
-
.panel.
|
|
45
|
+
.panel.isOpen {
|
|
44
46
|
visibility: visible;
|
|
45
47
|
transform: translateX(0);
|
|
46
48
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
2
|
+
interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "title"> {
|
|
3
3
|
label?: string;
|
|
4
4
|
error?: string;
|
|
5
5
|
helperText?: string;
|
|
6
6
|
startAdornment?: React.ReactNode;
|
|
7
7
|
endAdornment?: React.ReactNode;
|
|
8
|
+
showCount?: boolean;
|
|
8
9
|
format?: (value: string | number | readonly string[] | undefined) => string;
|
|
9
10
|
validate?: (value: string | number | readonly string[] | undefined) => string | undefined;
|
|
10
11
|
}
|
|
11
|
-
export declare function Input({ label, error: errorProp, helperText, startAdornment, endAdornment, style, className, format, validate, onBlur, onFocus, value, id, required, ...props }: InputProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export declare function Input({ label, error: errorProp, helperText, startAdornment, endAdornment, showCount, style, className, format, validate, onBlur, onFocus, onChange, value, defaultValue, id, required, maxLength, ...props }: InputProps): import("react/jsx-runtime").JSX.Element;
|
|
12
13
|
export {};
|
|
@@ -11,14 +11,15 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
return t;
|
|
12
12
|
};
|
|
13
13
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
14
|
-
import { useState, useId } from "react";
|
|
14
|
+
import React, { useState, useId } from "react";
|
|
15
15
|
import clsx from "clsx";
|
|
16
16
|
import { Label } from "../Label/Label.js";
|
|
17
17
|
import styles from "./Input.module.css";
|
|
18
18
|
export function Input(_a) {
|
|
19
|
-
var { label, error: errorProp, helperText, startAdornment, endAdornment, style, className, format, validate, onBlur, onFocus, value, id, required } = _a, props = __rest(_a, ["label", "error", "helperText", "startAdornment", "endAdornment", "style", "className", "format", "validate", "onBlur", "onFocus", "value", "id", "required"]);
|
|
19
|
+
var { label, error: errorProp, helperText, startAdornment, endAdornment, showCount, style, className, format, validate, onBlur, onFocus, onChange, value, defaultValue, id, required, maxLength } = _a, props = __rest(_a, ["label", "error", "helperText", "startAdornment", "endAdornment", "showCount", "style", "className", "format", "validate", "onBlur", "onFocus", "onChange", "value", "defaultValue", "id", "required", "maxLength"]);
|
|
20
20
|
const [isFocused, setIsFocused] = useState(false);
|
|
21
21
|
const [internalError, setInternalError] = useState(undefined);
|
|
22
|
+
const [charCount, setCharCount] = useState(((value === null || value === void 0 ? void 0 : value.toString()) || (defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue.toString()) || "").length);
|
|
22
23
|
const reactId = useId();
|
|
23
24
|
const inputId = id || `input-${reactId}`;
|
|
24
25
|
const helperId = `${inputId}-helper`;
|
|
@@ -37,7 +38,18 @@ export function Input(_a) {
|
|
|
37
38
|
if (onFocus)
|
|
38
39
|
onFocus(e);
|
|
39
40
|
};
|
|
41
|
+
const handleChange = (e) => {
|
|
42
|
+
setCharCount(e.target.value.length);
|
|
43
|
+
if (onChange)
|
|
44
|
+
onChange(e);
|
|
45
|
+
};
|
|
46
|
+
// Sync charCount if value is updated externally (controlled)
|
|
47
|
+
React.useEffect(() => {
|
|
48
|
+
if (value !== undefined) {
|
|
49
|
+
setCharCount(value.toString().length);
|
|
50
|
+
}
|
|
51
|
+
}, [value]);
|
|
40
52
|
const displayValue = !isFocused && format && value !== undefined ? format(value) : value;
|
|
41
53
|
const describedBy = clsx(helperText && helperId, error && errorId) || undefined;
|
|
42
|
-
return (_jsxs("div", { className: clsx(styles.container, className), style: style, children: [label && (_jsx(Label, { htmlFor: inputId, required: required, children: label })), _jsxs("div", { className: styles.wrapper, children: [startAdornment && (_jsx("span", { className: clsx(styles.adornment, styles.start), children: startAdornment })), _jsx("input", Object.assign({ className: clsx(styles.input, startAdornment && styles.hasStartAdornment, endAdornment && styles.hasEndAdornment, error && styles.error), id: inputId, required: required, value: displayValue, onBlur: handleBlur, onFocus: handleFocus, "aria-invalid": !!error, "aria-describedby": describedBy }, props)), endAdornment && (_jsx("span", { className: clsx(styles.adornment, styles.end), children: endAdornment }))] }), helperText &&
|
|
54
|
+
return (_jsxs("div", { className: clsx(styles.container, className), style: style, children: [label && (_jsx(Label, { htmlFor: inputId, required: required, children: label })), _jsxs("div", { className: styles.wrapper, children: [startAdornment && (_jsx("span", { className: clsx(styles.adornment, styles.start), children: startAdornment })), _jsx("input", Object.assign({ className: clsx(styles.input, startAdornment && styles.hasStartAdornment, endAdornment && styles.hasEndAdornment, error && styles.error), id: inputId, required: required, value: displayValue, defaultValue: defaultValue, onBlur: handleBlur, onFocus: handleFocus, onChange: handleChange, maxLength: maxLength, "aria-invalid": !!error, "aria-describedby": describedBy }, props)), endAdornment && (_jsx("span", { className: clsx(styles.adornment, styles.end), children: endAdornment }))] }), (error || helperText || (showCount !== null && showCount !== void 0 ? showCount : maxLength !== undefined)) && (_jsxs("div", { className: styles.bottomRow, children: [_jsx("div", { className: styles.messageArea, children: error ? (_jsx("span", { id: errorId, className: clsx(styles.helperText, styles.error), role: "alert", children: error })) : (helperText && (_jsx("span", { id: helperId, className: styles.helperText, children: helperText }))) }), (showCount !== null && showCount !== void 0 ? showCount : maxLength !== undefined) && (_jsxs("span", { className: styles.counter, children: [charCount, maxLength !== undefined ? ` / ${maxLength}` : ""] }))] }))] }));
|
|
43
55
|
}
|
|
@@ -41,6 +41,19 @@
|
|
|
41
41
|
border-color: var(--error);
|
|
42
42
|
box-shadow: 7px 7px 0px 0px var(--shadow-error);
|
|
43
43
|
}
|
|
44
|
+
.input:disabled {
|
|
45
|
+
cursor: not-allowed;
|
|
46
|
+
opacity: 0.7;
|
|
47
|
+
background-color: var(--muted);
|
|
48
|
+
background-image: linear-gradient(45deg, transparent 25%, rgba(0, 0, 0, 0.05) 25%, rgba(0, 0, 0, 0.05) 50%, transparent 50%, transparent 75%, rgba(0, 0, 0, 0.05) 75%, rgba(0, 0, 0, 0.05));
|
|
49
|
+
background-size: 20px 20px;
|
|
50
|
+
box-shadow: none;
|
|
51
|
+
transform: none;
|
|
52
|
+
color: var(--muted-foreground);
|
|
53
|
+
}
|
|
54
|
+
.input:disabled::placeholder {
|
|
55
|
+
color: var(--muted-foreground);
|
|
56
|
+
}
|
|
44
57
|
.input.hasStartAdornment {
|
|
45
58
|
padding-left: 2rem;
|
|
46
59
|
}
|
|
@@ -62,10 +75,23 @@
|
|
|
62
75
|
right: 0.75rem;
|
|
63
76
|
}
|
|
64
77
|
|
|
78
|
+
.bottomRow {
|
|
79
|
+
display: flex;
|
|
80
|
+
justify-content: space-between;
|
|
81
|
+
align-items: flex-start;
|
|
82
|
+
gap: 1rem;
|
|
83
|
+
margin-top: -0.25rem;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.messageArea {
|
|
87
|
+
flex: 1;
|
|
88
|
+
}
|
|
89
|
+
|
|
65
90
|
.helperText {
|
|
66
91
|
font-size: var(--text-xs);
|
|
67
92
|
font-weight: var(--font-medium);
|
|
68
93
|
color: var(--muted-foreground);
|
|
94
|
+
display: block;
|
|
69
95
|
}
|
|
70
96
|
.helperText.error {
|
|
71
97
|
color: var(--error);
|
|
@@ -84,3 +110,10 @@
|
|
|
84
110
|
transform: translateX(4px);
|
|
85
111
|
}
|
|
86
112
|
}
|
|
113
|
+
|
|
114
|
+
.counter {
|
|
115
|
+
font-size: var(--text-xs);
|
|
116
|
+
font-weight: var(--font-medium);
|
|
117
|
+
color: var(--muted-foreground);
|
|
118
|
+
white-space: nowrap;
|
|
119
|
+
}
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
interface ModalProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
2
|
+
interface ModalProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
|
|
3
3
|
isOpen: boolean;
|
|
4
4
|
onClose: () => void;
|
|
5
|
-
title?:
|
|
5
|
+
title?: React.ReactNode;
|
|
6
6
|
children: React.ReactNode;
|
|
7
7
|
footer?: React.ReactNode;
|
|
8
8
|
}
|
|
9
|
-
|
|
9
|
+
interface ModalHeaderProps {
|
|
10
10
|
children: React.ReactNode;
|
|
11
11
|
className?: string;
|
|
12
12
|
id?: string;
|
|
13
|
-
}
|
|
13
|
+
}
|
|
14
|
+
export declare function ModalHeader({ children, className, id }: ModalHeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
14
15
|
export declare function ModalBody({ children, className, }: {
|
|
15
16
|
children: React.ReactNode;
|
|
16
17
|
className?: string;
|
|
@@ -17,26 +17,30 @@ import { X } from "lucide-react";
|
|
|
17
17
|
import clsx from "clsx";
|
|
18
18
|
import { Card } from "../Card/Card.js";
|
|
19
19
|
import { Button } from "../Button/Button.js";
|
|
20
|
-
import { Flex } from "../Layout/Layout.js";
|
|
20
|
+
import { Stack, Flex } from "../Layout/Layout.js";
|
|
21
21
|
import styles from "./Modal.module.css";
|
|
22
22
|
const ModalContext = React.createContext({
|
|
23
23
|
onClose: () => { },
|
|
24
24
|
});
|
|
25
|
-
export function ModalHeader({ children, className, id
|
|
25
|
+
export function ModalHeader({ children, className, id }) {
|
|
26
26
|
const { onClose, titleId } = React.useContext(ModalContext);
|
|
27
|
-
return (_jsxs(Flex, { align: "center", justify: "space-between", className: clsx(styles.header, className), children: [_jsx("
|
|
27
|
+
return (_jsxs(Flex, { align: "center", justify: "space-between", className: clsx(styles.header, className), children: [_jsx("div", { id: id || titleId, className: styles.headerContent, children: children }), _jsx(Button, { variant: "ghost", size: "sm", onClick: onClose, "aria-label": "Close modal", className: styles.closeButton, children: _jsx(X, { size: 20, strokeWidth: 2.5 }) })] }));
|
|
28
28
|
}
|
|
29
29
|
export function ModalBody({ children, className, }) {
|
|
30
30
|
return _jsx("div", { className: clsx(styles.body, className), children: children });
|
|
31
31
|
}
|
|
32
32
|
export function ModalFooter({ children, className, }) {
|
|
33
|
-
return _jsx("
|
|
33
|
+
return (_jsx(Flex, { justify: "flex-end", align: "center", gap: "var(--spacing-md)", className: clsx(styles.footer, className), children: children }));
|
|
34
34
|
}
|
|
35
35
|
export function Modal(_a) {
|
|
36
36
|
var { isOpen, onClose, title, children, footer, className, style } = _a, props = __rest(_a, ["isOpen", "onClose", "title", "children", "footer", "className", "style"]);
|
|
37
37
|
const overlayRef = useRef(null);
|
|
38
38
|
const reactId = useId();
|
|
39
39
|
const titleId = `modal-title-${reactId}`;
|
|
40
|
+
const [mounted, setMounted] = React.useState(false);
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
setMounted(true);
|
|
43
|
+
}, []);
|
|
40
44
|
useEffect(() => {
|
|
41
45
|
const handleEscape = (e) => {
|
|
42
46
|
if (e.key === "Escape")
|
|
@@ -46,17 +50,20 @@ export function Modal(_a) {
|
|
|
46
50
|
document.addEventListener("keydown", handleEscape);
|
|
47
51
|
document.body.style.overflow = "hidden";
|
|
48
52
|
}
|
|
53
|
+
else {
|
|
54
|
+
document.body.style.overflow = "unset";
|
|
55
|
+
}
|
|
49
56
|
return () => {
|
|
50
57
|
document.removeEventListener("keydown", handleEscape);
|
|
51
58
|
document.body.style.overflow = "unset";
|
|
52
59
|
};
|
|
53
60
|
}, [isOpen, onClose]);
|
|
54
|
-
if (!
|
|
61
|
+
if (!mounted)
|
|
55
62
|
return null;
|
|
56
63
|
const handleOverlayClick = (e) => {
|
|
57
64
|
if (e.target === overlayRef.current) {
|
|
58
65
|
onClose();
|
|
59
66
|
}
|
|
60
67
|
};
|
|
61
|
-
return createPortal(_jsx(ModalContext.Provider, { value: { onClose, titleId }, children: _jsx("div", { className: clsx(styles.overlay, className), ref: overlayRef, onClick: handleOverlayClick, style: style, children: _jsx("div", { className: styles.contentContainer, children: _jsx("div", Object.assign({ role: "dialog", "aria-modal": "true", "aria-labelledby": title ? titleId : undefined }, props, { children: _jsx(Card, { style: { padding: 0, overflow: "hidden" }, children: title ? (_jsxs(_Fragment, { children: [_jsx(ModalHeader, { children: title }), _jsx(ModalBody, { children: children }), footer && _jsx(ModalFooter, { children: footer })] })) : (children) }) })) }) }) }), document.body);
|
|
68
|
+
return createPortal(_jsx(ModalContext.Provider, { value: { onClose, titleId }, children: _jsx("div", { className: clsx(styles.overlay, isOpen && styles.isOpen, className), ref: overlayRef, onClick: handleOverlayClick, style: style, children: _jsx("div", { className: styles.contentContainer, children: _jsx("div", Object.assign({ role: "dialog", "aria-modal": "true", "aria-labelledby": title ? titleId : undefined }, props, { children: _jsx(Card, { className: styles.modalCard, style: { padding: 0, overflow: "hidden" }, children: title ? (_jsxs(_Fragment, { children: [_jsx(ModalHeader, { children: title }), _jsxs(Stack, { gap: 0, className: styles.modalContent, children: [_jsx(ModalBody, { children: children }), footer && _jsx(ModalFooter, { children: footer })] })] })) : (children) }) })) }) }) }), document.body);
|
|
62
69
|
}
|