doom-design-system 0.1.3 → 0.1.4
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/README.md +5 -2
- package/dist/components/Accordion/Accordion.d.ts +17 -0
- package/dist/components/Accordion/Accordion.js +94 -0
- package/dist/components/Accordion/index.d.ts +1 -0
- package/dist/components/Accordion/index.js +1 -0
- package/dist/components/Alert/Alert.d.ts +11 -0
- package/dist/components/Alert/Alert.js +70 -0
- package/dist/components/Alert/index.d.ts +1 -0
- package/dist/components/Alert/index.js +1 -0
- package/dist/components/Avatar/Avatar.d.ts +13 -0
- package/dist/components/Avatar/Avatar.js +51 -0
- package/dist/components/Avatar/index.d.ts +1 -0
- package/dist/components/Avatar/index.js +1 -0
- package/dist/components/Badge/Badge.js +1 -1
- package/dist/components/Breadcrumbs/Breadcrumbs.d.ts +13 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.js +46 -0
- package/dist/components/Breadcrumbs/index.d.ts +1 -0
- package/dist/components/Breadcrumbs/index.js +1 -0
- package/dist/components/Button/Button.js +39 -6
- package/dist/components/Card/Card.js +1 -1
- package/dist/components/Drawer/Drawer.d.ts +12 -0
- package/dist/components/Drawer/Drawer.js +101 -0
- package/dist/components/Drawer/index.d.ts +1 -0
- package/dist/components/Drawer/index.js +1 -0
- package/dist/components/Form/Form.d.ts +20 -7
- package/dist/components/Form/Form.js +49 -9
- package/dist/components/Input/Input.d.ts +1 -1
- package/dist/components/Input/Input.js +27 -60
- package/dist/components/Label/Label.d.ts +6 -0
- package/dist/components/Label/Label.js +38 -0
- package/dist/components/Label/index.d.ts +1 -0
- package/dist/components/Label/index.js +1 -0
- package/dist/components/Link/Link.js +1 -1
- package/dist/components/Modal/Modal.js +7 -7
- package/dist/components/Pagination/Pagination.d.ts +8 -0
- package/dist/components/Pagination/Pagination.js +94 -0
- package/dist/components/Pagination/index.d.ts +1 -0
- package/dist/components/Pagination/index.js +1 -0
- package/dist/components/Popover/Popover.d.ts +1 -1
- package/dist/components/Popover/Popover.js +47 -29
- package/dist/components/RadioGroup/RadioGroup.d.ts +19 -0
- package/dist/components/RadioGroup/RadioGroup.js +101 -0
- package/dist/components/RadioGroup/index.d.ts +1 -0
- package/dist/components/RadioGroup/index.js +1 -0
- package/dist/components/Select/Select.js +5 -17
- package/dist/components/Sheet/Sheet.d.ts +10 -0
- package/dist/components/Sheet/Sheet.js +87 -0
- package/dist/components/Sheet/index.d.ts +1 -0
- package/dist/components/Sheet/index.js +1 -0
- package/dist/components/Slider/Slider.d.ts +10 -0
- package/dist/components/Slider/Slider.js +168 -0
- package/dist/components/Slider/index.d.ts +1 -0
- package/dist/components/Slider/index.js +1 -0
- package/dist/components/Switch/Switch.d.ts +11 -0
- package/dist/components/Switch/Switch.js +77 -0
- package/dist/components/Switch/index.d.ts +1 -0
- package/dist/components/Switch/index.js +1 -0
- package/dist/components/Table/Table.js +6 -6
- package/dist/components/Tabs/Tabs.js +6 -9
- package/dist/components/Textarea/Textarea.js +3 -10
- package/dist/components/Toast/Toast.js +1 -1
- package/dist/components/Tooltip/Tooltip.d.ts +9 -0
- package/dist/components/Tooltip/Tooltip.js +37 -0
- package/dist/components/Tooltip/index.d.ts +1 -0
- package/dist/components/Tooltip/index.js +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +12 -0
- package/dist/styles/mixins.d.ts +3 -0
- package/dist/styles/mixins.js +25 -0
- package/dist/styles/themes/definitions.d.ts +112 -0
- package/dist/styles/themes/definitions.js +37 -4
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,14 +30,17 @@ npm install @emotion/react @emotion/styled lucide-react
|
|
|
30
30
|
|
|
31
31
|
### 1. Setup Provider
|
|
32
32
|
|
|
33
|
-
Wrap your application with the `DesignSystemProvider` to ensure all styles and themes are applied correctly.
|
|
33
|
+
Wrap your application with the `DesignSystemProvider` to ensure all styles and themes are applied correctly. You can also pass a custom font class using `fontClassName`.
|
|
34
34
|
|
|
35
35
|
```tsx
|
|
36
36
|
import { DesignSystemProvider } from 'doom-design-system';
|
|
37
|
+
import { Montserrat } from 'next/font/google';
|
|
38
|
+
|
|
39
|
+
const montserrat = Montserrat({ subsets: ['latin'] });
|
|
37
40
|
|
|
38
41
|
export default function App() {
|
|
39
42
|
return (
|
|
40
|
-
<DesignSystemProvider>
|
|
43
|
+
<DesignSystemProvider fontClassName={montserrat.className}>
|
|
41
44
|
<YourApp />
|
|
42
45
|
</DesignSystemProvider>
|
|
43
46
|
);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface AccordionItemProps {
|
|
3
|
+
value: string;
|
|
4
|
+
trigger: string;
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
isOpen?: boolean;
|
|
7
|
+
onToggle?: () => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function AccordionItem({ value, trigger, children, isOpen, onToggle }: AccordionItemProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
interface AccordionProps {
|
|
11
|
+
type?: 'single' | 'multiple';
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
defaultValue?: string | string[];
|
|
14
|
+
className?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function Accordion({ type, children, defaultValue, className }: AccordionProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
|
+
import styled from '@emotion/styled';
|
|
5
|
+
import { ChevronDown } from 'lucide-react';
|
|
6
|
+
const AccordionRoot = styled.div `
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
border: var(--border-width) solid var(--card-border);
|
|
10
|
+
border-radius: var(--radius);
|
|
11
|
+
background-color: var(--card-bg);
|
|
12
|
+
overflow: hidden;
|
|
13
|
+
`;
|
|
14
|
+
const Item = styled.div `
|
|
15
|
+
border-bottom: var(--border-width) solid var(--card-border);
|
|
16
|
+
&:last-child {
|
|
17
|
+
border-bottom: none;
|
|
18
|
+
}
|
|
19
|
+
`;
|
|
20
|
+
const Header = styled.h3 `
|
|
21
|
+
display: flex;
|
|
22
|
+
margin: 0;
|
|
23
|
+
`;
|
|
24
|
+
const Trigger = styled.button `
|
|
25
|
+
all: unset;
|
|
26
|
+
flex: 1;
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
justify-content: space-between;
|
|
30
|
+
padding: var(--spacing-md);
|
|
31
|
+
font-family: var(--font-heading);
|
|
32
|
+
font-weight: 700;
|
|
33
|
+
font-size: var(--text-base);
|
|
34
|
+
background-color: var(--card-bg);
|
|
35
|
+
color: var(--foreground);
|
|
36
|
+
cursor: pointer;
|
|
37
|
+
transition: background-color 0.2s ease;
|
|
38
|
+
|
|
39
|
+
&:hover {
|
|
40
|
+
background-color: color-mix(in srgb, var(--primary) 25%, transparent);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
&:focus-visible {
|
|
44
|
+
outline: 2px solid var(--primary);
|
|
45
|
+
z-index: 1;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
& > svg {
|
|
49
|
+
transition: transform 0.2s ease;
|
|
50
|
+
transform: ${props => props.isOpen ? 'rotate(180deg)' : 'rotate(0deg)'};
|
|
51
|
+
}
|
|
52
|
+
`;
|
|
53
|
+
const ContentWrapper = styled.div `
|
|
54
|
+
height: ${props => props.isOpen ? 'auto' : '0'};
|
|
55
|
+
overflow: hidden;
|
|
56
|
+
display: ${props => props.isOpen ? 'block' : 'none'};
|
|
57
|
+
`;
|
|
58
|
+
const ContentBody = styled.div `
|
|
59
|
+
padding: var(--spacing-md);
|
|
60
|
+
padding-top: 0;
|
|
61
|
+
font-size: var(--text-base);
|
|
62
|
+
color: var(--muted-foreground);
|
|
63
|
+
line-height: 1.6;
|
|
64
|
+
`;
|
|
65
|
+
export function AccordionItem({ value, trigger, children, isOpen, onToggle }) {
|
|
66
|
+
return (_jsxs(Item, { children: [_jsx(Header, { children: _jsxs(Trigger, { type: "button", isOpen: !!isOpen, onClick: onToggle, "aria-expanded": isOpen, children: [trigger, _jsx(ChevronDown, { size: 20, strokeWidth: 2.5 })] }) }), _jsx(ContentWrapper, { isOpen: !!isOpen, role: "region", children: _jsx(ContentBody, { children: children }) })] }));
|
|
67
|
+
}
|
|
68
|
+
export function Accordion({ type = 'single', children, defaultValue, className }) {
|
|
69
|
+
const [value, setValue] = useState(defaultValue || (type === 'multiple' ? [] : ''));
|
|
70
|
+
const handleToggle = (itemValue) => {
|
|
71
|
+
if (type === 'single') {
|
|
72
|
+
setValue(prev => prev === itemValue ? '' : itemValue);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
setValue(prev => {
|
|
76
|
+
const arr = Array.isArray(prev) ? prev : [];
|
|
77
|
+
if (arr.includes(itemValue)) {
|
|
78
|
+
return arr.filter(v => v !== itemValue);
|
|
79
|
+
}
|
|
80
|
+
return [...arr, itemValue];
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
return (_jsx(AccordionRoot, { className: className, children: React.Children.map(children, (child) => {
|
|
85
|
+
if (!React.isValidElement(child))
|
|
86
|
+
return null;
|
|
87
|
+
const itemValue = child.props.value;
|
|
88
|
+
const isOpen = Array.isArray(value) ? value.includes(itemValue) : value === itemValue;
|
|
89
|
+
return React.cloneElement(child, {
|
|
90
|
+
isOpen,
|
|
91
|
+
onToggle: () => handleToggle(itemValue),
|
|
92
|
+
});
|
|
93
|
+
}) }));
|
|
94
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Accordion, AccordionItem } from './Accordion';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Accordion, AccordionItem } from './Accordion';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type AlertVariant = 'info' | 'success' | 'warning' | 'error';
|
|
3
|
+
interface AlertProps {
|
|
4
|
+
variant?: AlertVariant;
|
|
5
|
+
title: string;
|
|
6
|
+
description?: React.ReactNode;
|
|
7
|
+
icon?: React.ReactNode;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function Alert({ variant, title, description, icon, className }: AlertProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import styled from '@emotion/styled';
|
|
4
|
+
import { css } from '@emotion/react';
|
|
5
|
+
import { AlertCircle, CheckCircle, Info, XCircle } from 'lucide-react';
|
|
6
|
+
const AlertContainer = styled.div `
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: flex-start;
|
|
9
|
+
gap: 0.75rem;
|
|
10
|
+
padding: var(--spacing-md);
|
|
11
|
+
width: 100%;
|
|
12
|
+
border: var(--border-width) solid var(--card-border);
|
|
13
|
+
border-radius: var(--radius);
|
|
14
|
+
background-color: var(--card-bg);
|
|
15
|
+
box-shadow: var(--shadow-sm); /* Subtle depth, not full hard shadow usually */
|
|
16
|
+
position: relative;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
|
|
19
|
+
/* Left accent border */
|
|
20
|
+
&::before {
|
|
21
|
+
content: '';
|
|
22
|
+
position: absolute;
|
|
23
|
+
top: 0;
|
|
24
|
+
left: 0;
|
|
25
|
+
bottom: 0;
|
|
26
|
+
width: 6px;
|
|
27
|
+
background-color: ${props => `var(--${props.variant})`};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
${props => props.variant === 'info' && css `
|
|
31
|
+
/* Info Styles */
|
|
32
|
+
`}
|
|
33
|
+
`;
|
|
34
|
+
const Content = styled.div `
|
|
35
|
+
display: flex;
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
gap: 0.25rem;
|
|
38
|
+
flex: 1;
|
|
39
|
+
`;
|
|
40
|
+
const Title = styled.h5 `
|
|
41
|
+
margin: 0;
|
|
42
|
+
font-family: var(--font-heading);
|
|
43
|
+
font-weight: 700;
|
|
44
|
+
font-size: var(--text-base);
|
|
45
|
+
color: var(--foreground);
|
|
46
|
+
text-transform: uppercase;
|
|
47
|
+
letter-spacing: 0.05em;
|
|
48
|
+
`;
|
|
49
|
+
const Description = styled.p `
|
|
50
|
+
margin: 0;
|
|
51
|
+
font-size: var(--text-sm);
|
|
52
|
+
color: var(--muted-foreground);
|
|
53
|
+
line-height: 1.5;
|
|
54
|
+
`;
|
|
55
|
+
const IconWrapper = styled.div `
|
|
56
|
+
color: ${props => `var(--${props.variant})`};
|
|
57
|
+
display: flex;
|
|
58
|
+
align-items: center;
|
|
59
|
+
flex-shrink: 0;
|
|
60
|
+
`;
|
|
61
|
+
const icons = {
|
|
62
|
+
info: Info,
|
|
63
|
+
success: CheckCircle,
|
|
64
|
+
warning: AlertCircle,
|
|
65
|
+
error: XCircle,
|
|
66
|
+
};
|
|
67
|
+
export function Alert({ variant = 'info', title, description, icon, className }) {
|
|
68
|
+
const IconComponent = icons[variant];
|
|
69
|
+
return (_jsxs(AlertContainer, { variant: variant, className: className, role: "alert", children: [_jsx(IconWrapper, { variant: variant, children: icon || _jsx(IconComponent, { size: 20, strokeWidth: 2.5 }) }), _jsxs(Content, { children: [_jsx(Title, { children: title }), description && _jsx(Description, { children: description })] })] }));
|
|
70
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Alert, type AlertVariant } from './Alert';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Alert } from './Alert';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type AvatarSize = 'sm' | 'md' | 'lg' | 'xl';
|
|
3
|
+
export type AvatarShape = 'circle' | 'square';
|
|
4
|
+
interface AvatarProps {
|
|
5
|
+
src?: string;
|
|
6
|
+
alt?: string;
|
|
7
|
+
fallback: React.ReactNode;
|
|
8
|
+
size?: AvatarSize;
|
|
9
|
+
shape?: AvatarShape;
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function Avatar({ src, alt, fallback, size, shape, className }: AvatarProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import styled from '@emotion/styled';
|
|
5
|
+
const sizeMap = {
|
|
6
|
+
sm: '32px',
|
|
7
|
+
md: '48px',
|
|
8
|
+
lg: '64px',
|
|
9
|
+
xl: '96px',
|
|
10
|
+
};
|
|
11
|
+
const fontSizeMap = {
|
|
12
|
+
sm: 'var(--text-xs)',
|
|
13
|
+
md: 'var(--text-base)',
|
|
14
|
+
lg: 'var(--text-lg)',
|
|
15
|
+
xl: 'var(--text-2xl)',
|
|
16
|
+
};
|
|
17
|
+
const AvatarContainer = styled.div `
|
|
18
|
+
position: relative;
|
|
19
|
+
width: ${props => sizeMap[props.size]};
|
|
20
|
+
height: ${props => sizeMap[props.size]};
|
|
21
|
+
border: var(--border-width) solid var(--card-border);
|
|
22
|
+
border-radius: ${props => props.shape === 'circle' ? '50%' : 'var(--radius)'};
|
|
23
|
+
overflow: hidden;
|
|
24
|
+
background-color: var(--card-bg); /* Fallback bg */
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
justify-content: center;
|
|
28
|
+
flex-shrink: 0;
|
|
29
|
+
`;
|
|
30
|
+
const AvatarImage = styled.img `
|
|
31
|
+
width: 100%;
|
|
32
|
+
height: 100%;
|
|
33
|
+
object-fit: cover;
|
|
34
|
+
`;
|
|
35
|
+
const Fallback = styled.span `
|
|
36
|
+
font-family: var(--font-heading);
|
|
37
|
+
font-weight: 700;
|
|
38
|
+
font-size: ${props => fontSizeMap[props.size]};
|
|
39
|
+
color: var(--primary-foreground);
|
|
40
|
+
background-color: var(--primary);
|
|
41
|
+
width: 100%;
|
|
42
|
+
height: 100%;
|
|
43
|
+
display: flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
justify-content: center;
|
|
46
|
+
text-transform: uppercase;
|
|
47
|
+
`;
|
|
48
|
+
export function Avatar({ src, alt = 'Avatar', fallback, size = 'md', shape = 'circle', className }) {
|
|
49
|
+
const [hasError, setHasError] = useState(false);
|
|
50
|
+
return (_jsx(AvatarContainer, { size: size, shape: shape, className: className, children: src && !hasError ? (_jsx(AvatarImage, { src: src, alt: alt, onError: () => setHasError(true) })) : (_jsx(Fallback, { size: size, children: typeof fallback === 'string' ? fallback.slice(0, 2) : fallback })) }));
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Avatar, type AvatarSize, type AvatarShape } from './Avatar';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Avatar } from './Avatar';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface BreadcrumbsProps {
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
className?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function Breadcrumbs({ children, className }: BreadcrumbsProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
interface BreadcrumbItemProps {
|
|
8
|
+
href?: string;
|
|
9
|
+
isCurrent?: boolean;
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
}
|
|
12
|
+
export declare function BreadcrumbItem({ href, isCurrent, children }: BreadcrumbItemProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import styled from '@emotion/styled';
|
|
4
|
+
import { Link } from '../Link';
|
|
5
|
+
const BreadcrumbNav = styled.nav `
|
|
6
|
+
display: flex;
|
|
7
|
+
align-items: center;
|
|
8
|
+
font-size: var(--text-sm);
|
|
9
|
+
color: var(--muted-foreground);
|
|
10
|
+
`;
|
|
11
|
+
const BreadcrumbList = styled.ol `
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-wrap: wrap;
|
|
14
|
+
align-items: center;
|
|
15
|
+
padding: 0;
|
|
16
|
+
margin: 0;
|
|
17
|
+
list-style: none;
|
|
18
|
+
gap: 0.5rem;
|
|
19
|
+
`;
|
|
20
|
+
const BreadcrumbLi = styled.li `
|
|
21
|
+
display: inline-flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
gap: 0.5rem;
|
|
24
|
+
|
|
25
|
+
&:not(:last-child)::after {
|
|
26
|
+
/* Separator */
|
|
27
|
+
content: '/';
|
|
28
|
+
margin-left: 0.25rem;
|
|
29
|
+
color: var(--muted-foreground);
|
|
30
|
+
opacity: 0.5;
|
|
31
|
+
font-weight: 700;
|
|
32
|
+
}
|
|
33
|
+
`;
|
|
34
|
+
const CurrentPage = styled.span `
|
|
35
|
+
font-weight: 700;
|
|
36
|
+
color: var(--foreground);
|
|
37
|
+
`;
|
|
38
|
+
export function Breadcrumbs({ children, className }) {
|
|
39
|
+
return (_jsx(BreadcrumbNav, { "aria-label": "breadcrumb", className: className, children: _jsx(BreadcrumbList, { children: children }) }));
|
|
40
|
+
}
|
|
41
|
+
export function BreadcrumbItem({ href, isCurrent, children }) {
|
|
42
|
+
if (isCurrent) {
|
|
43
|
+
return (_jsx(BreadcrumbLi, { "aria-current": "page", children: _jsx(CurrentPage, { children: children }) }));
|
|
44
|
+
}
|
|
45
|
+
return (_jsx(BreadcrumbLi, { children: href ? (_jsx(Link, { href: href, variant: "default", style: { fontSize: 'inherit' }, children: children })) : (_jsx("span", { children: children })) }));
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Breadcrumbs, BreadcrumbItem } from './Breadcrumbs';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Breadcrumbs, BreadcrumbItem } from './Breadcrumbs';
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
'use client';
|
|
2
1
|
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
2
|
var t = {};
|
|
4
3
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
@@ -14,6 +13,9 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
14
13
|
import styled from '@emotion/styled';
|
|
15
14
|
import { css } from '@emotion/react';
|
|
16
15
|
const StyledButton = styled.button `
|
|
16
|
+
--btn-focus-border: var(--primary);
|
|
17
|
+
--btn-focus-shadow: var(--shadow-primary);
|
|
18
|
+
|
|
17
19
|
display: inline-flex;
|
|
18
20
|
align-items: center;
|
|
19
21
|
justify-content: center;
|
|
@@ -21,22 +23,36 @@ const StyledButton = styled.button `
|
|
|
21
23
|
font-weight: 700;
|
|
22
24
|
text-transform: uppercase;
|
|
23
25
|
letter-spacing: 0.05em;
|
|
24
|
-
transition:
|
|
26
|
+
transition:
|
|
27
|
+
transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1),
|
|
28
|
+
box-shadow 0.2s cubic-bezier(0.34, 1.56, 0.64, 1),
|
|
29
|
+
background-color 0.2s ease,
|
|
30
|
+
color 0.2s ease,
|
|
31
|
+
border-color 0.2s ease;
|
|
25
32
|
cursor: pointer;
|
|
26
33
|
border: var(--border-width) solid var(--card-border);
|
|
27
34
|
box-shadow: var(--shadow-hard);
|
|
28
35
|
background-color: var(--card-bg);
|
|
29
36
|
color: var(--foreground);
|
|
30
37
|
font-size: var(--text-base);
|
|
38
|
+
position: relative;
|
|
31
39
|
|
|
32
40
|
&:hover {
|
|
33
41
|
transform: translate(-2px, -2px);
|
|
34
42
|
box-shadow: var(--shadow-hover);
|
|
35
43
|
}
|
|
36
44
|
|
|
45
|
+
&:focus {
|
|
46
|
+
outline: none;
|
|
47
|
+
box-shadow: 7px 7px 0px 0px var(--btn-focus-shadow);
|
|
48
|
+
transform: translate(-2px, -2px);
|
|
49
|
+
border-color: var(--btn-focus-border);
|
|
50
|
+
}
|
|
51
|
+
|
|
37
52
|
&:active {
|
|
38
|
-
|
|
39
|
-
|
|
53
|
+
transition: none;
|
|
54
|
+
transform: translate(2px, 2px);
|
|
55
|
+
box-shadow: none;
|
|
40
56
|
}
|
|
41
57
|
|
|
42
58
|
&:disabled {
|
|
@@ -48,6 +64,10 @@ const StyledButton = styled.button `
|
|
|
48
64
|
|
|
49
65
|
/* Variants */
|
|
50
66
|
${props => props.variant === 'primary' && css `
|
|
67
|
+
/* Contrast Override */
|
|
68
|
+
--btn-focus-border: var(--card-border);
|
|
69
|
+
--btn-focus-shadow: #000000;
|
|
70
|
+
|
|
51
71
|
background-color: var(--primary);
|
|
52
72
|
color: var(--primary-foreground);
|
|
53
73
|
&:hover {
|
|
@@ -56,6 +76,10 @@ const StyledButton = styled.button `
|
|
|
56
76
|
`}
|
|
57
77
|
|
|
58
78
|
${props => props.variant === 'secondary' && css `
|
|
79
|
+
/* Contrast Override */
|
|
80
|
+
--btn-focus-border: var(--card-border);
|
|
81
|
+
--btn-focus-shadow: #000000;
|
|
82
|
+
|
|
59
83
|
background-color: var(--secondary);
|
|
60
84
|
color: var(--secondary-foreground);
|
|
61
85
|
&:hover {
|
|
@@ -64,6 +88,10 @@ const StyledButton = styled.button `
|
|
|
64
88
|
`}
|
|
65
89
|
|
|
66
90
|
${props => props.variant === 'success' && css `
|
|
91
|
+
/* Contrast Override */
|
|
92
|
+
--btn-focus-border: var(--card-border);
|
|
93
|
+
--btn-focus-shadow: #000000;
|
|
94
|
+
|
|
67
95
|
background-color: var(--success);
|
|
68
96
|
color: var(--card-bg);
|
|
69
97
|
&:hover {
|
|
@@ -73,18 +101,23 @@ const StyledButton = styled.button `
|
|
|
73
101
|
|
|
74
102
|
${props => props.variant === 'outline' && css `
|
|
75
103
|
background-color: transparent;
|
|
76
|
-
/* Outline is default style basically */
|
|
77
104
|
`}
|
|
78
105
|
|
|
79
106
|
${props => props.variant === 'ghost' && css `
|
|
80
107
|
background-color: transparent;
|
|
81
108
|
border-color: transparent;
|
|
82
109
|
box-shadow: none;
|
|
83
|
-
&:hover {
|
|
110
|
+
&:hover, &:focus {
|
|
84
111
|
background-color: color-mix(in srgb, var(--primary), transparent 90%);
|
|
85
112
|
color: var(--primary);
|
|
86
113
|
transform: none;
|
|
87
114
|
box-shadow: none;
|
|
115
|
+
border-color: transparent;
|
|
116
|
+
outline: none;
|
|
117
|
+
}
|
|
118
|
+
&:active {
|
|
119
|
+
transform: scale(0.95);
|
|
120
|
+
transition: none;
|
|
88
121
|
}
|
|
89
122
|
`}
|
|
90
123
|
|
|
@@ -18,7 +18,7 @@ const StyledCard = styled.div `
|
|
|
18
18
|
background-color: var(--card-bg);
|
|
19
19
|
border: var(--border-width) solid var(--card-border);
|
|
20
20
|
border-radius: var(--radius);
|
|
21
|
-
padding:
|
|
21
|
+
padding: var(--spacing-lg);
|
|
22
22
|
box-shadow: var(--shadow-hard);
|
|
23
23
|
min-width: 0; /* Safe default for grid/flex items */
|
|
24
24
|
`;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface DrawerProps {
|
|
3
|
+
isOpen: boolean;
|
|
4
|
+
onClose: () => void;
|
|
5
|
+
title?: string;
|
|
6
|
+
side?: 'left' | 'right';
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
footer?: React.ReactNode;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function Drawer({ isOpen, onClose, title, side, children, footer, className }: DrawerProps): React.ReactPortal | null;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
import { createPortal } from 'react-dom';
|
|
5
|
+
import styled from '@emotion/styled';
|
|
6
|
+
import { X } from 'lucide-react';
|
|
7
|
+
import { Button } from '../Button';
|
|
8
|
+
import { Flex } from '../Layout';
|
|
9
|
+
const Overlay = styled.div `
|
|
10
|
+
position: fixed;
|
|
11
|
+
inset: 0;
|
|
12
|
+
background-color: rgba(0, 0, 0, var(--overlay-opacity));
|
|
13
|
+
backdrop-filter: blur(4px);
|
|
14
|
+
opacity: ${props => props.isOpen ? 1 : 0};
|
|
15
|
+
transition: opacity 0.2s ease-in-out;
|
|
16
|
+
pointer-events: ${props => props.isOpen ? 'auto' : 'none'};
|
|
17
|
+
z-index: var(--z-dropdown);
|
|
18
|
+
`;
|
|
19
|
+
const Panel = styled.div `
|
|
20
|
+
position: fixed;
|
|
21
|
+
background-color: var(--card-bg);
|
|
22
|
+
box-shadow: ${props => props.side === 'left'
|
|
23
|
+
? '8px 0px 0px 0px var(--card-border)'
|
|
24
|
+
: '-8px 0px 0px 0px var(--card-border)'};
|
|
25
|
+
z-index: var(--z-modal);
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
|
29
|
+
|
|
30
|
+
top: 0;
|
|
31
|
+
bottom: 0;
|
|
32
|
+
${props => props.side}: 0;
|
|
33
|
+
width: 100%;
|
|
34
|
+
max-width: 400px;
|
|
35
|
+
|
|
36
|
+
${props => props.side === 'left' ? `
|
|
37
|
+
border-top-right-radius: var(--radius);
|
|
38
|
+
border-bottom-right-radius: var(--radius);
|
|
39
|
+
` : `
|
|
40
|
+
border-top-left-radius: var(--radius);
|
|
41
|
+
border-bottom-left-radius: var(--radius);
|
|
42
|
+
`}
|
|
43
|
+
|
|
44
|
+
visibility: ${props => props.isOpen ? 'visible' : 'hidden'};
|
|
45
|
+
|
|
46
|
+
transform: ${props => {
|
|
47
|
+
if (props.isOpen)
|
|
48
|
+
return 'translateX(0)';
|
|
49
|
+
return props.side === 'right' ? 'translateX(100%)' : 'translateX(-100%)';
|
|
50
|
+
}};
|
|
51
|
+
`;
|
|
52
|
+
const Header = styled(Flex) `
|
|
53
|
+
padding: var(--spacing-lg);
|
|
54
|
+
border-bottom-width: var(--border-width);
|
|
55
|
+
border-bottom-style: solid;
|
|
56
|
+
border-bottom-color: var(--card-border);
|
|
57
|
+
`;
|
|
58
|
+
const Title = styled.h2 `
|
|
59
|
+
margin: 0;
|
|
60
|
+
font-family: var(--font-heading);
|
|
61
|
+
font-size: var(--text-xl);
|
|
62
|
+
font-weight: 700;
|
|
63
|
+
`;
|
|
64
|
+
const Content = styled.div `
|
|
65
|
+
flex: 1;
|
|
66
|
+
padding: var(--spacing-lg);
|
|
67
|
+
overflow-y: auto;
|
|
68
|
+
`;
|
|
69
|
+
const Footer = styled(Flex) `
|
|
70
|
+
padding: var(--spacing-lg);
|
|
71
|
+
border-top-width: var(--border-width);
|
|
72
|
+
border-top-style: solid;
|
|
73
|
+
border-top-color: var(--card-border);
|
|
74
|
+
`;
|
|
75
|
+
export function Drawer({ isOpen, onClose, title, side = 'right', children, footer, className }) {
|
|
76
|
+
const [mounted, setMounted] = useState(false);
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
setMounted(true);
|
|
79
|
+
if (isOpen) {
|
|
80
|
+
document.body.style.overflow = 'hidden';
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
document.body.style.overflow = '';
|
|
84
|
+
}
|
|
85
|
+
return () => {
|
|
86
|
+
document.body.style.overflow = '';
|
|
87
|
+
};
|
|
88
|
+
}, [isOpen]);
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
const handleEsc = (e) => {
|
|
91
|
+
if (e.key === 'Escape')
|
|
92
|
+
onClose();
|
|
93
|
+
};
|
|
94
|
+
if (isOpen)
|
|
95
|
+
window.addEventListener('keydown', handleEsc);
|
|
96
|
+
return () => window.removeEventListener('keydown', handleEsc);
|
|
97
|
+
}, [isOpen, onClose]);
|
|
98
|
+
if (!mounted)
|
|
99
|
+
return null;
|
|
100
|
+
return createPortal(_jsxs(_Fragment, { children: [_jsx(Overlay, { isOpen: isOpen, onClick: onClose, "aria-hidden": "true" }), _jsxs(Panel, { isOpen: isOpen, side: side, role: "dialog", "aria-modal": "true", className: className, children: [_jsxs(Header, { align: "center", justify: "space-between", children: [_jsx(Title, { children: title }), _jsx(Button, { variant: "ghost", size: "sm", onClick: onClose, "aria-label": "Close drawer", children: _jsx(X, { size: 24 }) })] }), _jsx(Content, { children: children }), footer && _jsx(Footer, { justify: "flex-end", gap: "var(--spacing-md)", children: footer })] })] }), document.body);
|
|
101
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Drawer } from './Drawer';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Drawer } from './Drawer';
|
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
2
|
+
import { Label } from '../Label/Label';
|
|
3
|
+
export { Label };
|
|
4
|
+
export interface FormProps extends React.FormHTMLAttributes<HTMLFormElement> {
|
|
3
5
|
children: React.ReactNode;
|
|
4
6
|
}
|
|
5
|
-
export declare function Form({ children,
|
|
6
|
-
interface
|
|
7
|
+
export declare function Form({ children, ...props }: FormProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export interface FormMessageProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
7
9
|
children: React.ReactNode;
|
|
10
|
+
variant?: 'error' | 'description';
|
|
8
11
|
}
|
|
9
|
-
export declare function
|
|
10
|
-
interface
|
|
12
|
+
export declare function FormMessage({ children, variant, ...props }: FormMessageProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export interface FieldProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
11
14
|
children: React.ReactNode;
|
|
15
|
+
label?: string;
|
|
16
|
+
error?: string | boolean;
|
|
17
|
+
description?: string;
|
|
18
|
+
htmlFor?: string;
|
|
19
|
+
required?: boolean;
|
|
12
20
|
}
|
|
13
|
-
export declare function
|
|
14
|
-
export {
|
|
21
|
+
export declare function Field({ children, label, error, description, htmlFor, required, ...props }: FieldProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
export declare const FormGroup: import("@emotion/styled").StyledComponent<{
|
|
23
|
+
theme?: import("@emotion/react").Theme;
|
|
24
|
+
as?: React.ElementType;
|
|
25
|
+
} & {
|
|
26
|
+
error?: boolean;
|
|
27
|
+
}, React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
|