@trycompai/design-system 1.0.1 → 1.0.2
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/package.json +5 -2
- package/src/components/atoms/badge.tsx +8 -7
- package/src/components/atoms/button.tsx +6 -1
- package/src/components/atoms/checkbox.tsx +3 -3
- package/src/components/atoms/heading.tsx +6 -6
- package/src/components/atoms/index.ts +2 -0
- package/src/components/atoms/logo.tsx +52 -0
- package/src/components/atoms/spinner.tsx +3 -3
- package/src/components/atoms/stack.tsx +97 -0
- package/src/components/atoms/switch.tsx +1 -1
- package/src/components/atoms/text.tsx +5 -1
- package/src/components/atoms/toggle.tsx +1 -1
- package/src/components/molecules/accordion.tsx +3 -3
- package/src/components/molecules/ai-chat.tsx +217 -0
- package/src/components/molecules/alert.tsx +5 -5
- package/src/components/molecules/breadcrumb.tsx +8 -7
- package/src/components/molecules/card.tsx +24 -5
- package/src/components/molecules/command-search.tsx +147 -0
- package/src/components/molecules/index.ts +4 -1
- package/src/components/molecules/input-otp.tsx +2 -2
- package/src/components/molecules/page-header.tsx +33 -4
- package/src/components/molecules/pagination.tsx +4 -4
- package/src/components/molecules/popover.tsx +4 -2
- package/src/components/molecules/radio-group.tsx +2 -2
- package/src/components/molecules/section.tsx +1 -1
- package/src/components/molecules/select.tsx +5 -5
- package/src/components/molecules/settings.tsx +169 -0
- package/src/components/molecules/table.tsx +5 -1
- package/src/components/molecules/tabs.tsx +5 -4
- package/src/components/molecules/theme-switcher.tsx +176 -0
- package/src/components/organisms/app-shell.tsx +822 -0
- package/src/components/organisms/calendar.tsx +4 -4
- package/src/components/organisms/carousel.tsx +3 -3
- package/src/components/organisms/combobox.tsx +5 -5
- package/src/components/organisms/command.tsx +3 -3
- package/src/components/organisms/context-menu.tsx +4 -4
- package/src/components/organisms/dialog.tsx +2 -2
- package/src/components/organisms/dropdown-menu.tsx +8 -6
- package/src/components/organisms/index.ts +1 -0
- package/src/components/organisms/menubar.tsx +3 -3
- package/src/components/organisms/navigation-menu.tsx +2 -2
- package/src/components/organisms/page-layout.tsx +50 -20
- package/src/components/organisms/sheet.tsx +2 -2
- package/src/components/organisms/sidebar.tsx +22 -6
- package/src/components/organisms/sonner.tsx +11 -11
- package/src/fonts/TWKLausanne/TWKLausanne-300-Italic.woff +0 -0
- package/src/fonts/TWKLausanne/TWKLausanne-300-Italic.woff2 +0 -0
- package/src/fonts/TWKLausanne/TWKLausanne-300.woff +0 -0
- package/src/fonts/TWKLausanne/TWKLausanne-300.woff2 +0 -0
- package/src/fonts/TWKLausanne/TWKLausanne-350-Italic.woff +0 -0
- package/src/fonts/TWKLausanne/TWKLausanne-350-Italic.woff2 +0 -0
- package/src/fonts/TWKLausanne/TWKLausanne-350.woff +0 -0
- package/src/fonts/TWKLausanne/TWKLausanne-350.woff2 +0 -0
- package/src/fonts/TWKLausanne/TWKLausanne-400-Italic.woff +0 -0
- package/src/fonts/TWKLausanne/TWKLausanne-400-Italic.woff2 +0 -0
- package/src/fonts/TWKLausanne/TWKLausanne-400.woff +0 -0
- package/src/fonts/TWKLausanne/TWKLausanne-400.woff2 +0 -0
- package/src/fonts/TWKLausanne/TWKLausanne-700-Italic.woff +0 -0
- package/src/fonts/TWKLausanne/TWKLausanne-700-Italic.woff2 +0 -0
- package/src/fonts/TWKLausanne/TWKLausanne-700.woff +0 -0
- package/src/fonts/TWKLausanne/TWKLausanne-700.woff2 +0 -0
- package/src/styles/globals.css +155 -23
- package/src/components/molecules/stack.tsx +0 -72
|
@@ -11,19 +11,20 @@ function Tabs({
|
|
|
11
11
|
<TabsPrimitive.Root
|
|
12
12
|
data-slot="tabs"
|
|
13
13
|
data-orientation={orientation}
|
|
14
|
-
className="
|
|
14
|
+
className="group/tabs flex data-[orientation=horizontal]:flex-col"
|
|
15
15
|
{...props}
|
|
16
16
|
/>
|
|
17
17
|
);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const tabsListVariants = cva(
|
|
21
|
-
'rounded-lg p-[3px] group-data-horizontal/tabs:h-8 data-[variant=line]:rounded-none group/tabs-list text-muted-foreground inline-flex w-fit items-center justify-center group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col',
|
|
21
|
+
'rounded-lg p-[3px] group-data-horizontal/tabs:h-8 data-[variant=line]:rounded-none data-[variant=underline]:rounded-none group/tabs-list text-muted-foreground inline-flex w-fit items-center justify-center group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col',
|
|
22
22
|
{
|
|
23
23
|
variants: {
|
|
24
24
|
variant: {
|
|
25
25
|
default: 'bg-muted',
|
|
26
26
|
line: 'gap-1 bg-transparent',
|
|
27
|
+
underline: 'p-0 pb-px bg-transparent w-full justify-start items-stretch shadow-[inset_0_-1px_0_0_var(--color-border)]',
|
|
27
28
|
},
|
|
28
29
|
},
|
|
29
30
|
defaultVariants: {
|
|
@@ -50,7 +51,7 @@ function TabsTrigger({ ...props }: Omit<TabsPrimitive.Tab.Props, 'className'>) {
|
|
|
50
51
|
return (
|
|
51
52
|
<TabsPrimitive.Tab
|
|
52
53
|
data-slot="tabs-trigger"
|
|
53
|
-
className="gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg:not([class*='size-'])]:size-4 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring text-foreground/60 hover:text-foreground dark:text-muted-foreground dark:hover:text-foreground relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center whitespace-nowrap transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent data-active:bg-background dark:data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 data-active:text-foreground after:
|
|
54
|
+
className="gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none group-data-[variant=underline]/tabs-list:data-active:shadow-none [&_svg:not([class*='size-'])]:size-4 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring text-foreground/60 hover:text-foreground dark:text-muted-foreground dark:hover:text-foreground relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center whitespace-nowrap transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent group-data-[variant=underline]/tabs-list:bg-transparent group-data-[variant=underline]/tabs-list:data-active:bg-transparent group-data-[variant=underline]/tabs-list:px-3 group-data-[variant=underline]/tabs-list:py-2 group-data-[variant=underline]/tabs-list:mb-0 group-data-[variant=underline]/tabs-list:hover:bg-muted group-data-[variant=underline]/tabs-list:rounded-t-md group-data-[variant=underline]/tabs-list:flex-none group-data-[variant=underline]/tabs-list:justify-start group-data-[variant=underline]/tabs-list:text-left dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=underline]/tabs-list:data-active:border-transparent dark:group-data-[variant=underline]/tabs-list:data-active:bg-transparent data-active:bg-background dark:data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 data-active:text-foreground after:absolute after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:after:bg-foreground group-data-[variant=underline]/tabs-list:after:bg-primary group-data-[variant=line]/tabs-list:data-active:after:opacity-100 group-data-[variant=underline]/tabs-list:after:h-[3px] group-data-[variant=underline]/tabs-list:after:bottom-0 group-data-[variant=underline]/tabs-list:after:rounded-t-sm group-data-[variant=underline]/tabs-list:h-auto group-data-[variant=underline]/tabs-list:data-active:after:opacity-100"
|
|
54
55
|
{...props}
|
|
55
56
|
/>
|
|
56
57
|
);
|
|
@@ -60,7 +61,7 @@ function TabsContent({ ...props }: Omit<TabsPrimitive.Panel.Props, 'className'>)
|
|
|
60
61
|
return (
|
|
61
62
|
<TabsPrimitive.Panel
|
|
62
63
|
data-slot="tabs-content"
|
|
63
|
-
className="text-sm flex-1 outline-none"
|
|
64
|
+
className="text-sm flex-1 outline-none animate-in fade-in-0 duration-200"
|
|
64
65
|
{...props}
|
|
65
66
|
/>
|
|
66
67
|
);
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
4
|
+
import { Moon, Screen, Sun } from '@carbon/icons-react';
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
|
|
7
|
+
const themeSwitcherVariants = cva(
|
|
8
|
+
'inline-flex items-center rounded-full p-0.5 bg-muted',
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
size: {
|
|
12
|
+
sm: 'gap-0.5',
|
|
13
|
+
default: 'gap-0.5',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
defaultVariants: {
|
|
17
|
+
size: 'default',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const themeSwitcherButtonVariants = cva(
|
|
23
|
+
'inline-flex items-center justify-center rounded-full transition-all duration-200',
|
|
24
|
+
{
|
|
25
|
+
variants: {
|
|
26
|
+
size: {
|
|
27
|
+
sm: 'size-6 [&_svg]:size-3',
|
|
28
|
+
default: 'size-7 [&_svg]:size-3.5',
|
|
29
|
+
},
|
|
30
|
+
isActive: {
|
|
31
|
+
true: 'bg-background text-foreground shadow-sm',
|
|
32
|
+
false: 'text-muted-foreground hover:text-foreground',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
defaultVariants: {
|
|
36
|
+
size: 'default',
|
|
37
|
+
isActive: false,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
type Theme = 'light' | 'dark' | 'system';
|
|
43
|
+
|
|
44
|
+
interface ThemeSwitcherProps
|
|
45
|
+
extends Omit<React.ComponentProps<'div'>, 'className' | 'onChange'>,
|
|
46
|
+
VariantProps<typeof themeSwitcherVariants> {
|
|
47
|
+
/** Current theme value */
|
|
48
|
+
value?: Theme;
|
|
49
|
+
/** Default theme value (uncontrolled) */
|
|
50
|
+
defaultValue?: Theme;
|
|
51
|
+
/** Called when theme changes */
|
|
52
|
+
onChange?: (theme: Theme) => void;
|
|
53
|
+
/** Show system option */
|
|
54
|
+
showSystem?: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function ThemeSwitcher({
|
|
58
|
+
value: valueProp,
|
|
59
|
+
defaultValue = 'system',
|
|
60
|
+
onChange,
|
|
61
|
+
showSystem = true,
|
|
62
|
+
size = 'default',
|
|
63
|
+
...props
|
|
64
|
+
}: ThemeSwitcherProps) {
|
|
65
|
+
const [internalValue, setInternalValue] = React.useState<Theme>(defaultValue);
|
|
66
|
+
const value = valueProp ?? internalValue;
|
|
67
|
+
|
|
68
|
+
const handleChange = (newTheme: Theme) => {
|
|
69
|
+
if (valueProp === undefined) {
|
|
70
|
+
setInternalValue(newTheme);
|
|
71
|
+
}
|
|
72
|
+
onChange?.(newTheme);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const options: { value: Theme; icon: React.ReactNode; label: string }[] = [
|
|
76
|
+
{ value: 'light', icon: <Sun />, label: 'Light mode' },
|
|
77
|
+
{ value: 'dark', icon: <Moon />, label: 'Dark mode' },
|
|
78
|
+
...(showSystem
|
|
79
|
+
? [{ value: 'system' as Theme, icon: <Screen />, label: 'System theme' }]
|
|
80
|
+
: []),
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<div
|
|
85
|
+
data-slot="theme-switcher"
|
|
86
|
+
role="radiogroup"
|
|
87
|
+
aria-label="Theme"
|
|
88
|
+
className={themeSwitcherVariants({ size })}
|
|
89
|
+
{...props}
|
|
90
|
+
>
|
|
91
|
+
{options.map((option) => (
|
|
92
|
+
<button
|
|
93
|
+
key={option.value}
|
|
94
|
+
type="button"
|
|
95
|
+
role="radio"
|
|
96
|
+
aria-checked={value === option.value}
|
|
97
|
+
aria-label={option.label}
|
|
98
|
+
onClick={() => handleChange(option.value)}
|
|
99
|
+
className={themeSwitcherButtonVariants({
|
|
100
|
+
size,
|
|
101
|
+
isActive: value === option.value,
|
|
102
|
+
})}
|
|
103
|
+
>
|
|
104
|
+
{option.icon}
|
|
105
|
+
</button>
|
|
106
|
+
))}
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Simple light/dark toggle for compact spaces
|
|
112
|
+
interface ThemeToggleProps extends Omit<React.ComponentProps<'button'>, 'className' | 'onChange'> {
|
|
113
|
+
/** Current theme - true for dark, false for light */
|
|
114
|
+
isDark?: boolean;
|
|
115
|
+
/** Called when theme changes */
|
|
116
|
+
onChange?: (isDark: boolean) => void;
|
|
117
|
+
/** Size variant */
|
|
118
|
+
size?: 'sm' | 'default';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function ThemeToggle({
|
|
122
|
+
isDark: isDarkProp,
|
|
123
|
+
onChange,
|
|
124
|
+
size = 'default',
|
|
125
|
+
...props
|
|
126
|
+
}: ThemeToggleProps) {
|
|
127
|
+
const [internalIsDark, setInternalIsDark] = React.useState(false);
|
|
128
|
+
const isDark = isDarkProp ?? internalIsDark;
|
|
129
|
+
|
|
130
|
+
const handleToggle = () => {
|
|
131
|
+
const newValue = !isDark;
|
|
132
|
+
if (isDarkProp === undefined) {
|
|
133
|
+
setInternalIsDark(newValue);
|
|
134
|
+
}
|
|
135
|
+
onChange?.(newValue);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const iconSize = size === 'sm' ? 'size-3' : 'size-3.5';
|
|
139
|
+
const buttonSize = size === 'sm' ? 'h-6 w-12' : 'h-7 w-14';
|
|
140
|
+
const thumbSize = size === 'sm' ? 'size-5' : 'size-6';
|
|
141
|
+
const thumbTranslate = size === 'sm' ? 'translate-x-[26px]' : 'translate-x-[30px]';
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<button
|
|
145
|
+
type="button"
|
|
146
|
+
role="switch"
|
|
147
|
+
aria-checked={isDark}
|
|
148
|
+
aria-label={isDark ? 'Switch to light mode' : 'Switch to dark mode'}
|
|
149
|
+
onClick={handleToggle}
|
|
150
|
+
data-slot="theme-toggle"
|
|
151
|
+
className={`${buttonSize} relative inline-flex items-center rounded-full bg-muted p-0.5 transition-colors`}
|
|
152
|
+
{...props}
|
|
153
|
+
>
|
|
154
|
+
{/* Background icons */}
|
|
155
|
+
<span className="absolute inset-0 flex items-center justify-between px-1.5">
|
|
156
|
+
<Sun className={`${iconSize} text-amber-500`} />
|
|
157
|
+
<Moon className={`${iconSize} text-blue-400`} />
|
|
158
|
+
</span>
|
|
159
|
+
{/* Sliding thumb */}
|
|
160
|
+
<span
|
|
161
|
+
className={`${thumbSize} relative z-10 flex items-center justify-center rounded-full bg-background shadow-sm transition-transform duration-200 ${
|
|
162
|
+
isDark ? thumbTranslate : 'translate-x-0'
|
|
163
|
+
}`}
|
|
164
|
+
>
|
|
165
|
+
{isDark ? (
|
|
166
|
+
<Moon className={`${iconSize} text-blue-500`} />
|
|
167
|
+
) : (
|
|
168
|
+
<Sun className={`${iconSize} text-amber-500`} />
|
|
169
|
+
)}
|
|
170
|
+
</span>
|
|
171
|
+
</button>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export { ThemeSwitcher, ThemeToggle };
|
|
176
|
+
export type { ThemeSwitcherProps, ThemeToggleProps, Theme };
|