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.
Files changed (73) hide show
  1. package/README.md +5 -2
  2. package/dist/components/Accordion/Accordion.d.ts +17 -0
  3. package/dist/components/Accordion/Accordion.js +94 -0
  4. package/dist/components/Accordion/index.d.ts +1 -0
  5. package/dist/components/Accordion/index.js +1 -0
  6. package/dist/components/Alert/Alert.d.ts +11 -0
  7. package/dist/components/Alert/Alert.js +70 -0
  8. package/dist/components/Alert/index.d.ts +1 -0
  9. package/dist/components/Alert/index.js +1 -0
  10. package/dist/components/Avatar/Avatar.d.ts +13 -0
  11. package/dist/components/Avatar/Avatar.js +51 -0
  12. package/dist/components/Avatar/index.d.ts +1 -0
  13. package/dist/components/Avatar/index.js +1 -0
  14. package/dist/components/Badge/Badge.js +1 -1
  15. package/dist/components/Breadcrumbs/Breadcrumbs.d.ts +13 -0
  16. package/dist/components/Breadcrumbs/Breadcrumbs.js +46 -0
  17. package/dist/components/Breadcrumbs/index.d.ts +1 -0
  18. package/dist/components/Breadcrumbs/index.js +1 -0
  19. package/dist/components/Button/Button.js +39 -6
  20. package/dist/components/Card/Card.js +1 -1
  21. package/dist/components/Drawer/Drawer.d.ts +12 -0
  22. package/dist/components/Drawer/Drawer.js +101 -0
  23. package/dist/components/Drawer/index.d.ts +1 -0
  24. package/dist/components/Drawer/index.js +1 -0
  25. package/dist/components/Form/Form.d.ts +20 -7
  26. package/dist/components/Form/Form.js +49 -9
  27. package/dist/components/Input/Input.d.ts +1 -1
  28. package/dist/components/Input/Input.js +27 -60
  29. package/dist/components/Label/Label.d.ts +6 -0
  30. package/dist/components/Label/Label.js +38 -0
  31. package/dist/components/Label/index.d.ts +1 -0
  32. package/dist/components/Label/index.js +1 -0
  33. package/dist/components/Link/Link.js +1 -1
  34. package/dist/components/Modal/Modal.js +7 -7
  35. package/dist/components/Pagination/Pagination.d.ts +8 -0
  36. package/dist/components/Pagination/Pagination.js +94 -0
  37. package/dist/components/Pagination/index.d.ts +1 -0
  38. package/dist/components/Pagination/index.js +1 -0
  39. package/dist/components/Popover/Popover.d.ts +1 -1
  40. package/dist/components/Popover/Popover.js +47 -29
  41. package/dist/components/RadioGroup/RadioGroup.d.ts +19 -0
  42. package/dist/components/RadioGroup/RadioGroup.js +101 -0
  43. package/dist/components/RadioGroup/index.d.ts +1 -0
  44. package/dist/components/RadioGroup/index.js +1 -0
  45. package/dist/components/Select/Select.js +5 -17
  46. package/dist/components/Sheet/Sheet.d.ts +10 -0
  47. package/dist/components/Sheet/Sheet.js +87 -0
  48. package/dist/components/Sheet/index.d.ts +1 -0
  49. package/dist/components/Sheet/index.js +1 -0
  50. package/dist/components/Slider/Slider.d.ts +10 -0
  51. package/dist/components/Slider/Slider.js +168 -0
  52. package/dist/components/Slider/index.d.ts +1 -0
  53. package/dist/components/Slider/index.js +1 -0
  54. package/dist/components/Switch/Switch.d.ts +11 -0
  55. package/dist/components/Switch/Switch.js +77 -0
  56. package/dist/components/Switch/index.d.ts +1 -0
  57. package/dist/components/Switch/index.js +1 -0
  58. package/dist/components/Table/Table.js +6 -6
  59. package/dist/components/Tabs/Tabs.js +6 -9
  60. package/dist/components/Textarea/Textarea.js +3 -10
  61. package/dist/components/Toast/Toast.js +1 -1
  62. package/dist/components/Tooltip/Tooltip.d.ts +9 -0
  63. package/dist/components/Tooltip/Tooltip.js +37 -0
  64. package/dist/components/Tooltip/index.d.ts +1 -0
  65. package/dist/components/Tooltip/index.js +1 -0
  66. package/dist/index.d.ts +12 -0
  67. package/dist/index.js +12 -0
  68. package/dist/styles/mixins.d.ts +3 -0
  69. package/dist/styles/mixins.js +25 -0
  70. package/dist/styles/themes/definitions.d.ts +112 -0
  71. package/dist/styles/themes/definitions.js +37 -4
  72. package/dist/tsconfig.tsbuildinfo +1 -1
  73. 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';
@@ -19,7 +19,7 @@ const StyledBadge = styled.span `
19
19
  display: inline-flex;
20
20
  align-items: center;
21
21
  padding: 0.25rem 0.75rem;
22
- border-radius: 9999px;
22
+ border-radius: var(--radius-pill);
23
23
  font-size: 0.75rem;
24
24
  font-weight: 700;
25
25
  text-transform: uppercase;
@@ -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: all 0.1s ease;
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
- transform: translate(0px, 0px);
39
- box-shadow: 0px 0px 0px 0px var(--card-border);
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: 1.5rem;
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
- interface FormProps extends React.FormHTMLAttributes<HTMLFormElement> {
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, className, style, ...props }: FormProps): import("react/jsx-runtime").JSX.Element;
6
- interface FormGroupProps extends React.HTMLAttributes<HTMLDivElement> {
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 FormGroup({ children, className, style, ...props }: FormGroupProps): import("react/jsx-runtime").JSX.Element;
10
- interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {
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 Label({ children, className, style, ...props }: LabelProps): import("react/jsx-runtime").JSX.Element;
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>, {}>;