doom-design-system 0.1.0
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 +50 -0
- package/dist/DesignSystemProvider.d.ts +7 -0
- package/dist/DesignSystemProvider.js +13 -0
- package/dist/components/ActionRow/ActionRow.d.ts +9 -0
- package/dist/components/ActionRow/ActionRow.js +47 -0
- package/dist/components/ActionRow/index.d.ts +1 -0
- package/dist/components/ActionRow/index.js +1 -0
- package/dist/components/Badge/Badge.d.ts +8 -0
- package/dist/components/Badge/Badge.js +57 -0
- package/dist/components/Badge/index.d.ts +1 -0
- package/dist/components/Badge/index.js +1 -0
- package/dist/components/Button/Button.d.ts +9 -0
- package/dist/components/Button/Button.js +110 -0
- package/dist/components/Button/index.d.ts +1 -0
- package/dist/components/Button/index.js +1 -0
- package/dist/components/Card/Card.d.ts +8 -0
- package/dist/components/Card/Card.js +28 -0
- package/dist/components/Card/index.d.ts +1 -0
- package/dist/components/Card/index.js +1 -0
- package/dist/components/Dropdown/Dropdown.d.ts +11 -0
- package/dist/components/Dropdown/Dropdown.js +47 -0
- package/dist/components/Dropdown/index.d.ts +1 -0
- package/dist/components/Dropdown/index.js +1 -0
- package/dist/components/Form/Form.d.ts +14 -0
- package/dist/components/Form/Form.js +24 -0
- package/dist/components/Form/index.d.ts +1 -0
- package/dist/components/Form/index.js +1 -0
- package/dist/components/Icon/index.d.ts +25 -0
- package/dist/components/Icon/index.js +41 -0
- package/dist/components/Input/Input.d.ts +12 -0
- package/dist/components/Input/Input.js +129 -0
- package/dist/components/Input/index.d.ts +1 -0
- package/dist/components/Input/index.js +1 -0
- package/dist/components/Layout/Layout.d.ts +17 -0
- package/dist/components/Layout/Layout.js +37 -0
- package/dist/components/Layout/index.d.ts +1 -0
- package/dist/components/Layout/index.js +1 -0
- package/dist/components/Link/Link.d.ts +11 -0
- package/dist/components/Link/Link.js +78 -0
- package/dist/components/Link/index.d.ts +1 -0
- package/dist/components/Link/index.js +1 -0
- package/dist/components/Modal/Modal.d.ts +22 -0
- package/dist/components/Modal/Modal.js +89 -0
- package/dist/components/Modal/index.d.ts +1 -0
- package/dist/components/Modal/index.js +1 -0
- package/dist/components/Page/Page.d.ts +13 -0
- package/dist/components/Page/Page.js +27 -0
- package/dist/components/Page/index.d.ts +1 -0
- package/dist/components/Page/index.js +1 -0
- package/dist/components/Popover/Popover.d.ts +11 -0
- package/dist/components/Popover/Popover.js +91 -0
- package/dist/components/Popover/index.d.ts +1 -0
- package/dist/components/Popover/index.js +1 -0
- package/dist/components/ProgressBar/ProgressBar.d.ts +12 -0
- package/dist/components/ProgressBar/ProgressBar.js +42 -0
- package/dist/components/ProgressBar/index.d.ts +1 -0
- package/dist/components/ProgressBar/index.js +1 -0
- package/dist/components/Select/Select.d.ts +12 -0
- package/dist/components/Select/Select.js +202 -0
- package/dist/components/Select/index.d.ts +1 -0
- package/dist/components/Select/index.js +1 -0
- package/dist/components/Skeleton/Skeleton.d.ts +8 -0
- package/dist/components/Skeleton/Skeleton.js +65 -0
- package/dist/components/Skeleton/index.d.ts +1 -0
- package/dist/components/Skeleton/index.js +1 -0
- package/dist/components/SplitButton/SplitButton.d.ts +12 -0
- package/dist/components/SplitButton/SplitButton.js +90 -0
- package/dist/components/SplitButton/index.d.ts +1 -0
- package/dist/components/SplitButton/index.js +1 -0
- package/dist/components/Table/Table.d.ts +19 -0
- package/dist/components/Table/Table.js +176 -0
- package/dist/components/Table/index.d.ts +1 -0
- package/dist/components/Table/index.js +1 -0
- package/dist/components/Tabs/Tabs.d.ts +34 -0
- package/dist/components/Tabs/Tabs.js +94 -0
- package/dist/components/Tabs/index.d.ts +1 -0
- package/dist/components/Tabs/index.js +1 -0
- package/dist/components/Text/Text.d.ts +14 -0
- package/dist/components/Text/Text.js +123 -0
- package/dist/components/Text/index.d.ts +1 -0
- package/dist/components/Text/index.js +1 -0
- package/dist/components/Textarea/Textarea.d.ts +3 -0
- package/dist/components/Textarea/Textarea.js +30 -0
- package/dist/components/Textarea/index.d.ts +1 -0
- package/dist/components/Textarea/index.js +1 -0
- package/dist/components/Toast/Toast.d.ts +14 -0
- package/dist/components/Toast/Toast.js +109 -0
- package/dist/components/Toast/index.d.ts +1 -0
- package/dist/components/Toast/index.js +1 -0
- package/dist/fonts.d.ts +1 -0
- package/dist/fonts.js +6 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +24 -0
- package/dist/styles/index.d.ts +3 -0
- package/dist/styles/index.js +3 -0
- package/dist/styles/reset.d.ts +1 -0
- package/dist/styles/reset.js +29 -0
- package/dist/styles/theme.d.ts +1 -0
- package/dist/styles/theme.js +11 -0
- package/dist/styles/themes/ThemeProvider.d.ts +13 -0
- package/dist/styles/themes/ThemeProvider.js +37 -0
- package/dist/styles/themes/actions.d.ts +3 -0
- package/dist/styles/themes/actions.js +16 -0
- package/dist/styles/themes/definitions.d.ts +211 -0
- package/dist/styles/themes/definitions.js +48 -0
- package/dist/styles/themes/index.d.ts +3 -0
- package/dist/styles/themes/index.js +3 -0
- package/dist/styles/utilities.d.ts +1 -0
- package/dist/styles/utilities.js +184 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
14
|
+
import { useState, useRef, useEffect, useId } from 'react';
|
|
15
|
+
import styled from '@emotion/styled';
|
|
16
|
+
import { Text, Popover } from '../..';
|
|
17
|
+
import { Check, ChevronDown } from 'lucide-react';
|
|
18
|
+
const SelectContainer = styled.div `
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
width: 100%;
|
|
22
|
+
position: relative;
|
|
23
|
+
`;
|
|
24
|
+
const SelectTrigger = styled.button `
|
|
25
|
+
width: 100%;
|
|
26
|
+
background: var(--card-bg);
|
|
27
|
+
border: var(--border-width) solid var(--card-border);
|
|
28
|
+
color: var(--foreground);
|
|
29
|
+
padding: 0.75rem 1rem;
|
|
30
|
+
font-size: var(--text-base);
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
box-shadow: var(--shadow-hard);
|
|
33
|
+
border-radius: var(--radius);
|
|
34
|
+
display: flex;
|
|
35
|
+
justify-content: space-between;
|
|
36
|
+
align-items: center;
|
|
37
|
+
font-weight: 700;
|
|
38
|
+
text-transform: uppercase;
|
|
39
|
+
letter-spacing: 0.05em;
|
|
40
|
+
transition: all 0.1s ease;
|
|
41
|
+
min-height: 42px;
|
|
42
|
+
|
|
43
|
+
&:hover {
|
|
44
|
+
transform: translate(-2px, -2px);
|
|
45
|
+
box-shadow: var(--shadow-hover);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
&:focus {
|
|
49
|
+
outline: none;
|
|
50
|
+
transform: translate(-2px, -2px);
|
|
51
|
+
box-shadow: var(--shadow-hover);
|
|
52
|
+
border-color: var(--primary);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
&[aria-expanded="true"] {
|
|
56
|
+
transform: translate(-2px, -2px);
|
|
57
|
+
box-shadow: var(--shadow-hover);
|
|
58
|
+
border-color: var(--primary);
|
|
59
|
+
}
|
|
60
|
+
`;
|
|
61
|
+
const OptionsList = styled.ul `
|
|
62
|
+
background: var(--card-bg);
|
|
63
|
+
border: var(--border-width) solid var(--primary);
|
|
64
|
+
border-radius: var(--radius);
|
|
65
|
+
box-shadow: var(--shadow-hover);
|
|
66
|
+
min-width: 200px;
|
|
67
|
+
max-height: 300px;
|
|
68
|
+
overflow-y: auto;
|
|
69
|
+
display: flex;
|
|
70
|
+
flex-direction: column;
|
|
71
|
+
gap: 0.25rem;
|
|
72
|
+
padding: 0.25rem;
|
|
73
|
+
margin: 0;
|
|
74
|
+
list-style: none;
|
|
75
|
+
`;
|
|
76
|
+
const OptionItem = styled.li `
|
|
77
|
+
text-align: left;
|
|
78
|
+
padding: 0.75rem 1rem;
|
|
79
|
+
background: ${props => props.isSelected ? 'var(--primary)' : (props.isHighlighted ? 'color-mix(in srgb, var(--primary), transparent 85%)' : 'transparent')};
|
|
80
|
+
border: none;
|
|
81
|
+
border-radius: calc(var(--radius) - 2px);
|
|
82
|
+
color: ${props => props.isSelected ? 'var(--primary-foreground)' : (props.isHighlighted ? 'var(--primary)' : 'var(--foreground)')};
|
|
83
|
+
cursor: pointer;
|
|
84
|
+
font-size: var(--text-base);
|
|
85
|
+
font-weight: ${props => props.isSelected ? '700' : '400'};
|
|
86
|
+
transition: all 0.1s ease;
|
|
87
|
+
width: 100%;
|
|
88
|
+
display: flex;
|
|
89
|
+
align-items: center;
|
|
90
|
+
justify-content: space-between;
|
|
91
|
+
user-select: none;
|
|
92
|
+
|
|
93
|
+
&:hover {
|
|
94
|
+
background-color: color-mix(in srgb, var(--primary), transparent 85%);
|
|
95
|
+
color: var(--primary);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
${props => props.isSelected && `
|
|
99
|
+
&:hover {
|
|
100
|
+
background-color: var(--primary);
|
|
101
|
+
color: var(--primary-foreground);
|
|
102
|
+
filter: brightness(1.1);
|
|
103
|
+
}
|
|
104
|
+
`}
|
|
105
|
+
`;
|
|
106
|
+
export function Select(_a) {
|
|
107
|
+
var _b;
|
|
108
|
+
var { options, className, label, style, value, defaultValue, onChange, placeholder, id, required, disabled, name, form, autoFocus } = _a, props = __rest(_a, ["options", "className", "label", "style", "value", "defaultValue", "onChange", "placeholder", "id", "required", "disabled", "name", "form", "autoFocus"]);
|
|
109
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
110
|
+
const [internalValue, setInternalValue] = useState(defaultValue || '');
|
|
111
|
+
const [highlightedIndex, setHighlightedIndex] = useState(-1);
|
|
112
|
+
const reactId = useId();
|
|
113
|
+
const triggerRef = useRef(null);
|
|
114
|
+
const listboxId = id ? `${id}-listbox` : `select-listbox-${reactId}`;
|
|
115
|
+
const labelId = id ? `${id}-label` : `select-label-${reactId}`;
|
|
116
|
+
const currentValue = value !== undefined ? value : internalValue;
|
|
117
|
+
const selectedOption = options.find(opt => String(opt.value) === String(currentValue));
|
|
118
|
+
// Reset highlighted index when opening
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
if (isOpen) {
|
|
121
|
+
const index = options.findIndex(opt => String(opt.value) === String(currentValue));
|
|
122
|
+
setHighlightedIndex(index >= 0 ? index : 0);
|
|
123
|
+
}
|
|
124
|
+
}, [isOpen, currentValue, options]);
|
|
125
|
+
const handleSelect = (newValue) => {
|
|
126
|
+
var _a;
|
|
127
|
+
if (value === undefined) {
|
|
128
|
+
setInternalValue(newValue);
|
|
129
|
+
}
|
|
130
|
+
if (onChange) {
|
|
131
|
+
const syntheticEvent = {
|
|
132
|
+
target: { value: newValue, name: name },
|
|
133
|
+
currentTarget: { value: newValue, name: name },
|
|
134
|
+
preventDefault: () => { },
|
|
135
|
+
stopPropagation: () => { },
|
|
136
|
+
};
|
|
137
|
+
onChange(syntheticEvent);
|
|
138
|
+
}
|
|
139
|
+
setIsOpen(false);
|
|
140
|
+
(_a = triggerRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
141
|
+
};
|
|
142
|
+
const handleKeyDown = (e) => {
|
|
143
|
+
var _a;
|
|
144
|
+
switch (e.key) {
|
|
145
|
+
case 'Enter':
|
|
146
|
+
case ' ':
|
|
147
|
+
e.preventDefault();
|
|
148
|
+
if (isOpen) {
|
|
149
|
+
if (highlightedIndex >= 0 && highlightedIndex < options.length) {
|
|
150
|
+
handleSelect(options[highlightedIndex].value);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
setIsOpen(true);
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
case 'ArrowDown':
|
|
158
|
+
e.preventDefault();
|
|
159
|
+
if (!isOpen) {
|
|
160
|
+
setIsOpen(true);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
setHighlightedIndex(prev => (prev < options.length - 1 ? prev + 1 : 0));
|
|
164
|
+
}
|
|
165
|
+
break;
|
|
166
|
+
case 'ArrowUp':
|
|
167
|
+
e.preventDefault();
|
|
168
|
+
if (!isOpen) {
|
|
169
|
+
setIsOpen(true);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
setHighlightedIndex(prev => (prev > 0 ? prev - 1 : options.length - 1));
|
|
173
|
+
}
|
|
174
|
+
break;
|
|
175
|
+
case 'Escape':
|
|
176
|
+
if (isOpen) {
|
|
177
|
+
e.preventDefault();
|
|
178
|
+
setIsOpen(false);
|
|
179
|
+
(_a = triggerRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
180
|
+
}
|
|
181
|
+
break;
|
|
182
|
+
case 'Tab':
|
|
183
|
+
if (isOpen) {
|
|
184
|
+
setIsOpen(false);
|
|
185
|
+
}
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
return (_jsxs(SelectContainer, { className: className, style: style, children: [label && (_jsx(Text, { as: "label", id: labelId, variant: "small", weight: "bold", color: "muted", className: "mb-1 block", htmlFor: id, children: label })), _jsx(Popover, { isOpen: isOpen, onClose: () => setIsOpen(false), placement: "bottom-start", trigger: _jsxs(SelectTrigger, Object.assign({ ref: triggerRef, type: "button", id: id, onClick: () => setIsOpen(!isOpen), onKeyDown: handleKeyDown, "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-controls": listboxId, "aria-labelledby": label ? labelId : undefined, disabled: disabled, autoFocus: autoFocus }, props, { children: [_jsx("span", { children: selectedOption ? selectedOption.label : (placeholder || 'Select...') }), _jsx(ChevronDown, { size: 16, strokeWidth: 2.5, style: { marginLeft: '0.5rem' } })] })), content: _jsx(OptionsList, { id: listboxId, role: "listbox", "aria-labelledby": label ? labelId : undefined, style: { width: (_b = triggerRef.current) === null || _b === void 0 ? void 0 : _b.offsetWidth }, children: options.map((opt, index) => {
|
|
190
|
+
const isSelected = String(opt.value) === String(currentValue);
|
|
191
|
+
const isHighlighted = index === highlightedIndex;
|
|
192
|
+
return (_jsxs(OptionItem, { id: `${listboxId}-option-${index}`, role: "option", "aria-selected": isSelected, isSelected: isSelected, isHighlighted: isHighlighted, onClick: () => handleSelect(opt.value), onMouseEnter: () => setHighlightedIndex(index), children: [_jsx("span", { children: opt.label }), isSelected && _jsx(Check, { size: 14, strokeWidth: 3 })] }, opt.value));
|
|
193
|
+
}) }) }), _jsx("input", { type: "text", name: name, value: currentValue, required: required, form: form, tabIndex: -1, readOnly: true, style: {
|
|
194
|
+
opacity: 0,
|
|
195
|
+
height: '1px',
|
|
196
|
+
width: '1px',
|
|
197
|
+
position: 'absolute',
|
|
198
|
+
bottom: 0,
|
|
199
|
+
left: 0,
|
|
200
|
+
pointerEvents: 'none',
|
|
201
|
+
} })] }));
|
|
202
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Select';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Select';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface SkeletonProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
3
|
+
width?: string;
|
|
4
|
+
height?: string;
|
|
5
|
+
variant?: 'text' | 'circular' | 'rectangular';
|
|
6
|
+
}
|
|
7
|
+
export declare function Skeleton({ width, height, variant, className, style, ...props }: SkeletonProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
14
|
+
import styled from '@emotion/styled';
|
|
15
|
+
import { keyframes } from '@emotion/react';
|
|
16
|
+
const shimmer = keyframes `
|
|
17
|
+
0% {
|
|
18
|
+
background-position: -200% 0;
|
|
19
|
+
}
|
|
20
|
+
100% {
|
|
21
|
+
background-position: 200% 0;
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
24
|
+
const StyledSkeleton = styled.div `
|
|
25
|
+
background: linear-gradient(
|
|
26
|
+
90deg,
|
|
27
|
+
rgba(0, 0, 0, 0.06) 25%,
|
|
28
|
+
rgba(0, 0, 0, 0.12) 37%,
|
|
29
|
+
rgba(0, 0, 0, 0.06) 63%
|
|
30
|
+
);
|
|
31
|
+
background-size: 200% 100%;
|
|
32
|
+
animation: ${shimmer} 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
33
|
+
|
|
34
|
+
/* Dimensions */
|
|
35
|
+
width: ${props => props.$width || '100%'};
|
|
36
|
+
height: ${props => props.$height || 'auto'};
|
|
37
|
+
|
|
38
|
+
/* Variant Styles */
|
|
39
|
+
${props => {
|
|
40
|
+
switch (props.$variant) {
|
|
41
|
+
case 'circular':
|
|
42
|
+
return `
|
|
43
|
+
border-radius: 50%;
|
|
44
|
+
height: ${props.$height || props.$width || '3rem'};
|
|
45
|
+
width: ${props.$width || props.$height || '3rem'};
|
|
46
|
+
`;
|
|
47
|
+
case 'text':
|
|
48
|
+
return `
|
|
49
|
+
height: ${props.$height || '1em'};
|
|
50
|
+
border-radius: var(--radius);
|
|
51
|
+
margin-bottom: 0.5rem;
|
|
52
|
+
`;
|
|
53
|
+
case 'rectangular':
|
|
54
|
+
default:
|
|
55
|
+
return `
|
|
56
|
+
height: ${props.$height || '10rem'};
|
|
57
|
+
border-radius: var(--radius);
|
|
58
|
+
`;
|
|
59
|
+
}
|
|
60
|
+
}}
|
|
61
|
+
`;
|
|
62
|
+
export function Skeleton(_a) {
|
|
63
|
+
var { width, height, variant = 'rectangular', className, style } = _a, props = __rest(_a, ["width", "height", "variant", "className", "style"]);
|
|
64
|
+
return (_jsx(StyledSkeleton, Object.assign({ "$width": width, "$height": height, "$variant": variant, className: className, style: style }, props)));
|
|
65
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Skeleton';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Skeleton';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface SplitButtonItem {
|
|
2
|
+
label: string;
|
|
3
|
+
onClick: () => void;
|
|
4
|
+
}
|
|
5
|
+
interface SplitButtonProps {
|
|
6
|
+
primaryLabel: string;
|
|
7
|
+
onPrimaryClick: () => void;
|
|
8
|
+
items: SplitButtonItem[];
|
|
9
|
+
variant?: 'primary' | 'secondary';
|
|
10
|
+
}
|
|
11
|
+
export declare function SplitButton({ primaryLabel, onPrimaryClick, items, variant }: SplitButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import styled from '@emotion/styled';
|
|
5
|
+
import { Popover } from '../..';
|
|
6
|
+
import { ChevronDown } from 'lucide-react';
|
|
7
|
+
const StyledContainer = styled.div `
|
|
8
|
+
display: inline-flex;
|
|
9
|
+
border: var(--border-width) solid var(--card-border);
|
|
10
|
+
border-radius: var(--radius);
|
|
11
|
+
box-shadow: var(--shadow-hard);
|
|
12
|
+
background-color: ${props => props.variant === 'primary' ? 'var(--primary)' : 'var(--secondary)'};
|
|
13
|
+
color: ${props => props.variant === 'primary' ? 'var(--primary-foreground)' : 'var(--secondary-foreground)'};
|
|
14
|
+
transition: all 0.1s ease;
|
|
15
|
+
&:hover {
|
|
16
|
+
transform: translate(-2px, -2px);
|
|
17
|
+
box-shadow: var(--shadow-hover);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
&[aria-expanded="true"] {
|
|
21
|
+
transform: translate(-2px, -2px);
|
|
22
|
+
box-shadow: var(--shadow-hover);
|
|
23
|
+
}
|
|
24
|
+
`;
|
|
25
|
+
const StyledMainButton = styled.button `
|
|
26
|
+
border: none;
|
|
27
|
+
background: transparent;
|
|
28
|
+
color: inherit;
|
|
29
|
+
padding: 0.75rem 1rem;
|
|
30
|
+
padding-right: 0.75rem;
|
|
31
|
+
font-weight: 700;
|
|
32
|
+
font-size: var(--text-base);
|
|
33
|
+
text-transform: uppercase;
|
|
34
|
+
letter-spacing: 0.05em;
|
|
35
|
+
cursor: pointer;
|
|
36
|
+
border-right: 2px solid rgba(0,0,0,0.2);
|
|
37
|
+
|
|
38
|
+
&:hover {
|
|
39
|
+
background-color: rgba(0,0,0,0.25);
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
const StyledDropdownTrigger = styled.button `
|
|
43
|
+
border: none;
|
|
44
|
+
background: transparent;
|
|
45
|
+
color: inherit;
|
|
46
|
+
padding: 0.75rem 0.75rem;
|
|
47
|
+
cursor: pointer;
|
|
48
|
+
display: flex;
|
|
49
|
+
align-items: center;
|
|
50
|
+
|
|
51
|
+
&:hover {
|
|
52
|
+
background-color: rgba(0,0,0,0.25);
|
|
53
|
+
}
|
|
54
|
+
`;
|
|
55
|
+
const StyledDropdownMenu = styled.div `
|
|
56
|
+
background: var(--card-bg);
|
|
57
|
+
border: var(--border-width) solid var(--primary);
|
|
58
|
+
border-radius: var(--radius);
|
|
59
|
+
box-shadow: var(--shadow-hover);
|
|
60
|
+
min-width: 200px;
|
|
61
|
+
overflow: hidden;
|
|
62
|
+
display: flex;
|
|
63
|
+
flex-direction: column;
|
|
64
|
+
gap: 0.25rem;
|
|
65
|
+
padding: 0.25rem;
|
|
66
|
+
`;
|
|
67
|
+
const StyledDropdownItem = styled.button `
|
|
68
|
+
text-align: left;
|
|
69
|
+
padding: 0.75rem 1rem;
|
|
70
|
+
background: transparent;
|
|
71
|
+
border: none;
|
|
72
|
+
color: var(--foreground);
|
|
73
|
+
cursor: pointer;
|
|
74
|
+
border-radius: calc(var(--radius) - 2px);
|
|
75
|
+
font-size: var(--text-base);
|
|
76
|
+
font-weight: 600;
|
|
77
|
+
transition: background-color 0.2s;
|
|
78
|
+
|
|
79
|
+
&:hover {
|
|
80
|
+
background-color: color-mix(in srgb, var(--primary), transparent 85%);
|
|
81
|
+
color: var(--primary);
|
|
82
|
+
}
|
|
83
|
+
`;
|
|
84
|
+
export function SplitButton({ primaryLabel, onPrimaryClick, items, variant = 'primary' }) {
|
|
85
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
86
|
+
return (_jsx(Popover, { isOpen: isOpen, onClose: () => setIsOpen(false), placement: "bottom-end", trigger: _jsxs(StyledContainer, { variant: variant, children: [_jsx(StyledMainButton, { onClick: onPrimaryClick, children: primaryLabel }), _jsx(StyledDropdownTrigger, { onClick: () => setIsOpen(!isOpen), children: _jsx(ChevronDown, { size: 16, strokeWidth: 3 }) })] }), content: _jsx(StyledDropdownMenu, { children: items.map((item, index) => (_jsx(StyledDropdownItem, { onClick: () => {
|
|
87
|
+
item.onClick();
|
|
88
|
+
setIsOpen(false);
|
|
89
|
+
}, children: item.label }, index))) }) }));
|
|
90
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './SplitButton';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './SplitButton';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ColumnDef } from '@tanstack/react-table';
|
|
3
|
+
interface TableProps<T> {
|
|
4
|
+
data: T[];
|
|
5
|
+
columns: ColumnDef<T>[];
|
|
6
|
+
enablePagination?: boolean;
|
|
7
|
+
enableFiltering?: boolean;
|
|
8
|
+
enableSorting?: boolean;
|
|
9
|
+
pageSize?: number;
|
|
10
|
+
height?: string | number;
|
|
11
|
+
className?: string;
|
|
12
|
+
style?: React.CSSProperties;
|
|
13
|
+
variant?: 'default' | 'flat';
|
|
14
|
+
density?: 'compact' | 'standard' | 'relaxed';
|
|
15
|
+
toolbarContent?: React.ReactNode;
|
|
16
|
+
striped?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare function Table<T>({ data, columns, enablePagination, enableFiltering, enableSorting, pageSize, height, className, style, variant, density, toolbarContent, striped, }: TableProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,176 @@
|
|
|
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 { useReactTable, getCoreRowModel, getSortedRowModel, getPaginationRowModel, getFilteredRowModel, flexRender, } from '@tanstack/react-table';
|
|
6
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
7
|
+
import { Button, Input, Select, Flex, Text } from '../..';
|
|
8
|
+
// --- Styled Components ---
|
|
9
|
+
const TableContainer = styled.div `
|
|
10
|
+
width: 100%;
|
|
11
|
+
border: var(--border-width) solid var(--card-border);
|
|
12
|
+
border-radius: var(--radius);
|
|
13
|
+
background: var(--card-bg);
|
|
14
|
+
box-shadow: var(--shadow-hard);
|
|
15
|
+
overflow: hidden;
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
`;
|
|
19
|
+
const Toolbar = styled.div `
|
|
20
|
+
padding: 1rem;
|
|
21
|
+
border-bottom: var(--border-width) solid var(--card-border);
|
|
22
|
+
background: var(--background);
|
|
23
|
+
display: flex;
|
|
24
|
+
justify-content: space-between;
|
|
25
|
+
align-items: center;
|
|
26
|
+
gap: 1rem;
|
|
27
|
+
`;
|
|
28
|
+
const StyledTable = styled.table `
|
|
29
|
+
width: 100%;
|
|
30
|
+
border-collapse: collapse;
|
|
31
|
+
font-size: var(--text-base);
|
|
32
|
+
`;
|
|
33
|
+
const getDensityPadding = (density = 'standard') => {
|
|
34
|
+
switch (density) {
|
|
35
|
+
case 'compact': return '0.5rem 1rem';
|
|
36
|
+
case 'relaxed': return '1.5rem 1rem';
|
|
37
|
+
case 'standard':
|
|
38
|
+
default: return '1rem';
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const Th = styled.th `
|
|
42
|
+
text-align: left;
|
|
43
|
+
padding: ${props => getDensityPadding(props.$density)};
|
|
44
|
+
background: var(--secondary);
|
|
45
|
+
border-bottom: var(--border-width) solid var(--card-border);
|
|
46
|
+
font-weight: 700;
|
|
47
|
+
text-transform: uppercase;
|
|
48
|
+
letter-spacing: 0.05em;
|
|
49
|
+
cursor: ${props => props.isSortable ? 'pointer' : 'default'};
|
|
50
|
+
user-select: none;
|
|
51
|
+
white-space: nowrap;
|
|
52
|
+
color: var(--secondary-foreground);
|
|
53
|
+
|
|
54
|
+
${props => props.isSortable && `
|
|
55
|
+
&:hover {
|
|
56
|
+
filter: brightness(0.95);
|
|
57
|
+
color: var(--secondary-foreground);
|
|
58
|
+
}
|
|
59
|
+
`}
|
|
60
|
+
`;
|
|
61
|
+
const Td = styled.td `
|
|
62
|
+
padding: ${props => getDensityPadding(props.$density)};
|
|
63
|
+
border-bottom: 1px solid var(--card-border);
|
|
64
|
+
color: var(--foreground);
|
|
65
|
+
`;
|
|
66
|
+
const Tr = styled.tr `
|
|
67
|
+
&:last-child td {
|
|
68
|
+
border-bottom: none;
|
|
69
|
+
}
|
|
70
|
+
&:hover {
|
|
71
|
+
background-color: rgba(var(--muted-rgb, 113, 128, 150), 0.1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
${props => props.$striped && `
|
|
75
|
+
&:nth-of-type(even) {
|
|
76
|
+
background-color: rgba(var(--muted-rgb, 113, 128, 150), 0.05);
|
|
77
|
+
}
|
|
78
|
+
&:hover {
|
|
79
|
+
background-color: rgba(var(--muted-rgb, 113, 128, 150), 0.15);
|
|
80
|
+
}
|
|
81
|
+
`}
|
|
82
|
+
|
|
83
|
+
/* Hide actions by default */
|
|
84
|
+
& .row-actions {
|
|
85
|
+
opacity: 0;
|
|
86
|
+
transition: opacity 0.2s ease-in-out;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* Show actions on hover */
|
|
90
|
+
&:hover .row-actions {
|
|
91
|
+
opacity: 1;
|
|
92
|
+
}
|
|
93
|
+
`;
|
|
94
|
+
const PaginationContainer = styled.div `
|
|
95
|
+
padding: 1rem;
|
|
96
|
+
border-top: var(--border-width) solid var(--card-border);
|
|
97
|
+
background: var(--background);
|
|
98
|
+
display: flex;
|
|
99
|
+
justify-content: space-between;
|
|
100
|
+
align-items: center;
|
|
101
|
+
`;
|
|
102
|
+
export function Table({ data, columns, enablePagination = true, enableFiltering = true, enableSorting = true, pageSize = 10, height, className, style, variant = 'default', density = 'standard', toolbarContent, striped = false, }) {
|
|
103
|
+
const [sorting, setSorting] = useState([]);
|
|
104
|
+
const [globalFilter, setGlobalFilter] = useState('');
|
|
105
|
+
const [pagination, setPagination] = useState({
|
|
106
|
+
pageIndex: 0,
|
|
107
|
+
pageSize: pageSize,
|
|
108
|
+
});
|
|
109
|
+
const table = useReactTable({
|
|
110
|
+
data,
|
|
111
|
+
columns,
|
|
112
|
+
state: {
|
|
113
|
+
sorting,
|
|
114
|
+
globalFilter,
|
|
115
|
+
pagination,
|
|
116
|
+
},
|
|
117
|
+
enableSorting, // Pass this to useReactTable
|
|
118
|
+
onSortingChange: setSorting,
|
|
119
|
+
onGlobalFilterChange: setGlobalFilter,
|
|
120
|
+
onPaginationChange: setPagination,
|
|
121
|
+
getCoreRowModel: getCoreRowModel(),
|
|
122
|
+
getSortedRowModel: getSortedRowModel(), // Always provide the model, enableSorting controls if it's used
|
|
123
|
+
getPaginationRowModel: enablePagination ? getPaginationRowModel() : undefined,
|
|
124
|
+
getFilteredRowModel: enableFiltering ? getFilteredRowModel() : undefined,
|
|
125
|
+
});
|
|
126
|
+
// Virtualization Logic (Simplified for rows)
|
|
127
|
+
const parentRef = React.useRef(null);
|
|
128
|
+
const { rows } = table.getRowModel();
|
|
129
|
+
const rowVirtualizer = useVirtualizer({
|
|
130
|
+
count: rows.length,
|
|
131
|
+
getScrollElement: () => parentRef.current,
|
|
132
|
+
estimateSize: () => 50, // Estimate row height
|
|
133
|
+
overscan: 5,
|
|
134
|
+
});
|
|
135
|
+
const isVirtual = !!height;
|
|
136
|
+
const variantStyles = variant === 'flat' ? {
|
|
137
|
+
border: 'none',
|
|
138
|
+
boxShadow: 'none',
|
|
139
|
+
background: 'transparent',
|
|
140
|
+
borderRadius: 0,
|
|
141
|
+
} : {};
|
|
142
|
+
return (_jsxs(TableContainer, { className: className, style: Object.assign(Object.assign({}, variantStyles), style), children: [enableFiltering && (_jsxs(Toolbar, { children: [_jsx("div", { style: { width: '300px' }, children: _jsx(Input, { placeholder: "Search...", value: globalFilter !== null && globalFilter !== void 0 ? globalFilter : '', onChange: (e) => setGlobalFilter(e.target.value) }) }), toolbarContent && (_jsx(Flex, { gap: "1rem", align: "center", children: toolbarContent }))] })), _jsxs("div", { ref: parentRef, style: {
|
|
143
|
+
height: height ? height : 'auto',
|
|
144
|
+
overflowY: height ? 'auto' : 'visible',
|
|
145
|
+
overflowX: 'auto',
|
|
146
|
+
width: '100%'
|
|
147
|
+
}, children: [_jsxs(StyledTable, { children: [_jsx("thead", { children: table.getHeaderGroups().map((headerGroup) => (_jsx("tr", { children: headerGroup.headers.map((header) => {
|
|
148
|
+
var _a;
|
|
149
|
+
const canSort = header.column.getCanSort();
|
|
150
|
+
return (_jsx(Th, { onClick: canSort ? header.column.getToggleSortingHandler() : undefined, style: { width: header.getSize() }, isSortable: canSort, "$density": density, children: _jsxs(Flex, { align: "center", gap: "0.5rem", children: [flexRender(header.column.columnDef.header, header.getContext()), canSort && ((_a = {
|
|
151
|
+
asc: ' ▲',
|
|
152
|
+
desc: ' ▼',
|
|
153
|
+
}[header.column.getIsSorted()]) !== null && _a !== void 0 ? _a : null)] }) }, header.id));
|
|
154
|
+
}) }, headerGroup.id))) }), isVirtual ? (_jsx("tbody", { style: {
|
|
155
|
+
height: `${rowVirtualizer.getTotalSize()}px`,
|
|
156
|
+
width: '100%',
|
|
157
|
+
position: 'relative',
|
|
158
|
+
}, children: rowVirtualizer.getVirtualItems().map((virtualRow) => {
|
|
159
|
+
const row = rows[virtualRow.index];
|
|
160
|
+
return (_jsx(Tr, { "$striped": striped, style: {
|
|
161
|
+
position: 'absolute',
|
|
162
|
+
top: 0,
|
|
163
|
+
left: 0,
|
|
164
|
+
width: '100%',
|
|
165
|
+
height: `${virtualRow.size}px`,
|
|
166
|
+
transform: `translateY(${virtualRow.start}px)`,
|
|
167
|
+
}, children: row.getVisibleCells().map((cell) => (_jsx(Td, { "$density": density, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))) }, row.id));
|
|
168
|
+
}) })) : (_jsx("tbody", { children: table.getRowModel().rows.map((row) => (_jsx(Tr, { className: "group", "$striped": striped, children: row.getVisibleCells().map((cell) => (_jsx(Td, { "$density": density, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))) }, row.id))) }))] }), table.getRowModel().rows.length === 0 && (_jsx("div", { style: { padding: '2rem', textAlign: 'center', color: 'var(--muted-foreground)' }, children: "No results found." }))] }), enablePagination && !isVirtual && (_jsxs(PaginationContainer, { children: [_jsxs(Flex, { gap: "1rem", align: "center", children: [_jsxs(Text, { color: "muted", className: "min-w-fit", children: ["Page ", table.getState().pagination.pageIndex + 1, " of", ' ', table.getPageCount()] }), _jsx(Select, { value: table.getState().pagination.pageSize, onChange: (e) => {
|
|
169
|
+
table.setPageSize(Number(e.target.value));
|
|
170
|
+
}, options: [
|
|
171
|
+
{ value: 10, label: '10 rows' },
|
|
172
|
+
{ value: 20, label: '20 rows' },
|
|
173
|
+
{ value: 50, label: '50 rows' },
|
|
174
|
+
{ value: 100, label: '100 rows' },
|
|
175
|
+
] })] }), _jsxs(Flex, { gap: "0.5rem", children: [_jsx(Button, { variant: "secondary", size: "sm", onClick: () => table.previousPage(), disabled: !table.getCanPreviousPage(), children: "Previous" }), _jsx(Button, { variant: "secondary", size: "sm", onClick: () => table.nextPage(), disabled: !table.getCanNextPage(), children: "Next" })] })] }))] }));
|
|
176
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Table';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Table';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface TabsProps {
|
|
3
|
+
defaultValue?: string;
|
|
4
|
+
value?: string;
|
|
5
|
+
onValueChange?: (value: string) => void;
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function Tabs({ defaultValue, value, onValueChange, children, className }: TabsProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
interface TabsListProps {
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function TabsList({ children, className }: TabsListProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
interface TabsTriggerProps {
|
|
16
|
+
value: string;
|
|
17
|
+
children: React.ReactNode;
|
|
18
|
+
className?: string;
|
|
19
|
+
onClick?: () => void;
|
|
20
|
+
}
|
|
21
|
+
export declare function TabsTrigger({ value, children, className, onClick }: TabsTriggerProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
interface TabsBodyProps {
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
className?: string;
|
|
25
|
+
style?: React.CSSProperties;
|
|
26
|
+
}
|
|
27
|
+
export declare function TabsBody({ children, className, style }: TabsBodyProps): import("react/jsx-runtime").JSX.Element;
|
|
28
|
+
interface TabsContentProps {
|
|
29
|
+
value: string;
|
|
30
|
+
children: React.ReactNode;
|
|
31
|
+
className?: string;
|
|
32
|
+
}
|
|
33
|
+
export declare function TabsContent({ value, children, className }: TabsContentProps): import("react/jsx-runtime").JSX.Element | null;
|
|
34
|
+
export {};
|