@trycompai/design-system 1.0.0 → 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 +6 -3
- package/src/components/atoms/badge.tsx +49 -0
- package/src/components/{ui → atoms}/button.tsx +6 -1
- package/src/components/{ui → atoms}/checkbox.tsx +3 -3
- package/src/components/{ui → atoms}/heading.tsx +6 -6
- package/src/components/atoms/index.ts +21 -0
- package/src/components/atoms/kbd.tsx +21 -0
- package/src/components/atoms/logo.tsx +52 -0
- package/src/components/{ui → atoms}/slider.tsx +4 -4
- package/src/components/{ui → atoms}/spinner.tsx +3 -3
- package/src/components/atoms/stack.tsx +97 -0
- package/src/components/{ui → atoms}/switch.tsx +1 -1
- package/src/components/{ui → atoms}/text.tsx +5 -1
- package/src/components/{ui → atoms}/textarea.tsx +8 -2
- package/src/components/{ui → atoms}/toggle.tsx +3 -6
- package/src/components/{ui → molecules}/accordion.tsx +3 -3
- package/src/components/molecules/ai-chat.tsx +217 -0
- package/src/components/{ui → molecules}/alert.tsx +5 -5
- package/src/components/{ui → molecules}/breadcrumb.tsx +9 -8
- package/src/components/{ui → molecules}/card.tsx +24 -5
- package/src/components/molecules/command-search.tsx +147 -0
- package/src/components/molecules/empty.tsx +82 -0
- package/src/components/{ui → molecules}/field.tsx +16 -37
- package/src/components/{ui → molecules}/hover-card.tsx +2 -8
- package/src/components/molecules/index.ts +29 -0
- package/src/components/{ui → molecules}/input-group.tsx +1 -1
- package/src/components/molecules/input-otp.tsx +70 -0
- package/src/components/{ui → molecules}/item.tsx +18 -36
- package/src/components/molecules/page-header.tsx +80 -0
- package/src/components/{ui → molecules}/pagination.tsx +14 -23
- package/src/components/{ui → molecules}/popover.tsx +4 -2
- package/src/components/molecules/radio-group.tsx +33 -0
- package/src/components/{ui → molecules}/scroll-area.tsx +8 -11
- package/src/components/{ui → molecules}/section.tsx +3 -3
- package/src/components/{ui → molecules}/select.tsx +22 -10
- package/src/components/molecules/settings.tsx +169 -0
- package/src/components/{ui → molecules}/table.tsx +16 -3
- package/src/components/molecules/tabs.tsx +70 -0
- package/src/components/molecules/theme-switcher.tsx +176 -0
- package/src/components/{ui → molecules}/toggle-group.tsx +1 -1
- package/src/components/organisms/alert-dialog.tsx +135 -0
- package/src/components/organisms/app-shell.tsx +822 -0
- package/src/components/{ui → organisms}/calendar.tsx +6 -7
- package/src/components/{ui → organisms}/carousel.tsx +9 -11
- package/src/components/{ui → organisms}/chart.tsx +9 -24
- package/src/components/{ui → organisms}/combobox.tsx +7 -7
- package/src/components/{ui → organisms}/command.tsx +3 -3
- package/src/components/{ui → organisms}/context-menu.tsx +23 -53
- package/src/components/{ui → organisms}/dialog.tsx +3 -3
- package/src/components/{ui → organisms}/dropdown-menu.tsx +8 -6
- package/src/components/organisms/index.ts +17 -0
- package/src/components/{ui → organisms}/menubar.tsx +3 -3
- package/src/components/organisms/navigation-menu.tsx +137 -0
- package/src/components/organisms/page-layout.tsx +95 -0
- package/src/components/{ui → organisms}/sheet.tsx +7 -7
- package/src/components/{ui → organisms}/sidebar.tsx +61 -86
- package/src/components/organisms/sonner.tsx +41 -0
- package/src/components/ui/index.ts +3 -61
- 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 +167 -23
- package/src/components/ui/alert-dialog.tsx +0 -161
- package/src/components/ui/badge.tsx +0 -48
- package/src/components/ui/empty.tsx +0 -94
- package/src/components/ui/input-otp.tsx +0 -84
- package/src/components/ui/kbd.tsx +0 -26
- package/src/components/ui/navigation-menu.tsx +0 -147
- package/src/components/ui/page-header.tsx +0 -51
- package/src/components/ui/page-layout.tsx +0 -65
- package/src/components/ui/radio-group.tsx +0 -37
- package/src/components/ui/sonner.tsx +0 -43
- package/src/components/ui/stack.tsx +0 -72
- package/src/components/ui/tabs.tsx +0 -69
- /package/src/components/{ui → atoms}/aspect-ratio.tsx +0 -0
- /package/src/components/{ui → atoms}/avatar.tsx +0 -0
- /package/src/components/{ui → atoms}/container.tsx +0 -0
- /package/src/components/{ui → atoms}/input.tsx +0 -0
- /package/src/components/{ui → atoms}/label.tsx +0 -0
- /package/src/components/{ui → atoms}/progress.tsx +0 -0
- /package/src/components/{ui → atoms}/separator.tsx +0 -0
- /package/src/components/{ui → atoms}/skeleton.tsx +0 -0
- /package/src/components/{ui → molecules}/button-group.tsx +0 -0
- /package/src/components/{ui → molecules}/collapsible.tsx +0 -0
- /package/src/components/{ui → molecules}/grid.tsx +0 -0
- /package/src/components/{ui → molecules}/resizable.tsx +0 -0
- /package/src/components/{ui → molecules}/tooltip.tsx +0 -0
- /package/src/components/{ui → organisms}/drawer.tsx +0 -0
|
@@ -4,47 +4,36 @@ import { cva, type VariantProps } from 'class-variance-authority';
|
|
|
4
4
|
import { useMemo } from 'react';
|
|
5
5
|
|
|
6
6
|
import { Separator as SeparatorPrimitive } from '@base-ui/react/separator';
|
|
7
|
-
import { cn } from '../../../lib/utils';
|
|
8
7
|
|
|
9
|
-
function FieldSet({
|
|
8
|
+
function FieldSet({ ...props }: Omit<React.ComponentProps<'fieldset'>, 'className'>) {
|
|
10
9
|
return (
|
|
11
10
|
<fieldset
|
|
12
11
|
data-slot="field-set"
|
|
13
|
-
className=
|
|
14
|
-
'gap-4 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3 flex flex-col',
|
|
15
|
-
className,
|
|
16
|
-
)}
|
|
12
|
+
className="gap-4 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3 flex flex-col"
|
|
17
13
|
{...props}
|
|
18
14
|
/>
|
|
19
15
|
);
|
|
20
16
|
}
|
|
21
17
|
|
|
22
18
|
function FieldLegend({
|
|
23
|
-
className,
|
|
24
19
|
variant = 'legend',
|
|
25
20
|
...props
|
|
26
|
-
}: React.ComponentProps<'legend'> & { variant?: 'legend' | 'label' }) {
|
|
21
|
+
}: Omit<React.ComponentProps<'legend'>, 'className'> & { variant?: 'legend' | 'label' }) {
|
|
27
22
|
return (
|
|
28
23
|
<legend
|
|
29
24
|
data-slot="field-legend"
|
|
30
25
|
data-variant={variant}
|
|
31
|
-
className=
|
|
32
|
-
'mb-1.5 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base',
|
|
33
|
-
className,
|
|
34
|
-
)}
|
|
26
|
+
className="mb-1.5 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base"
|
|
35
27
|
{...props}
|
|
36
28
|
/>
|
|
37
29
|
);
|
|
38
30
|
}
|
|
39
31
|
|
|
40
|
-
function FieldGroup({
|
|
32
|
+
function FieldGroup({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
|
|
41
33
|
return (
|
|
42
34
|
<div
|
|
43
35
|
data-slot="field-group"
|
|
44
|
-
className=
|
|
45
|
-
'gap-5 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4 group/field-group @container/field-group flex w-full flex-col',
|
|
46
|
-
className,
|
|
47
|
-
)}
|
|
36
|
+
className="gap-5 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4 group/field-group @container/field-group flex w-full flex-col"
|
|
48
37
|
{...props}
|
|
49
38
|
/>
|
|
50
39
|
);
|
|
@@ -66,26 +55,25 @@ const fieldVariants = cva('data-[invalid=true]:text-destructive gap-2 group/fiel
|
|
|
66
55
|
});
|
|
67
56
|
|
|
68
57
|
function Field({
|
|
69
|
-
className,
|
|
70
58
|
orientation = 'vertical',
|
|
71
59
|
...props
|
|
72
|
-
}: React.ComponentProps<'div'> & VariantProps<typeof fieldVariants>) {
|
|
60
|
+
}: Omit<React.ComponentProps<'div'>, 'className'> & VariantProps<typeof fieldVariants>) {
|
|
73
61
|
return (
|
|
74
62
|
<div
|
|
75
63
|
role="group"
|
|
76
64
|
data-slot="field"
|
|
77
65
|
data-orientation={orientation}
|
|
78
|
-
className={
|
|
66
|
+
className={fieldVariants({ orientation })}
|
|
79
67
|
{...props}
|
|
80
68
|
/>
|
|
81
69
|
);
|
|
82
70
|
}
|
|
83
71
|
|
|
84
|
-
function FieldContent({
|
|
72
|
+
function FieldContent({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
|
|
85
73
|
return (
|
|
86
74
|
<div
|
|
87
75
|
data-slot="field-content"
|
|
88
|
-
className=
|
|
76
|
+
className="gap-0.5 group/field-content flex flex-1 flex-col leading-snug"
|
|
89
77
|
{...props}
|
|
90
78
|
/>
|
|
91
79
|
);
|
|
@@ -101,29 +89,21 @@ function FieldLabel({ ...props }: Omit<React.ComponentProps<'label'>, 'className
|
|
|
101
89
|
);
|
|
102
90
|
}
|
|
103
91
|
|
|
104
|
-
function FieldTitle({
|
|
92
|
+
function FieldTitle({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
|
|
105
93
|
return (
|
|
106
94
|
<div
|
|
107
95
|
data-slot="field-label"
|
|
108
|
-
className=
|
|
109
|
-
'gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50 flex w-fit items-center leading-snug',
|
|
110
|
-
className,
|
|
111
|
-
)}
|
|
96
|
+
className="gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50 flex w-fit items-center leading-snug"
|
|
112
97
|
{...props}
|
|
113
98
|
/>
|
|
114
99
|
);
|
|
115
100
|
}
|
|
116
101
|
|
|
117
|
-
function FieldDescription({
|
|
102
|
+
function FieldDescription({ ...props }: Omit<React.ComponentProps<'p'>, 'className'>) {
|
|
118
103
|
return (
|
|
119
104
|
<p
|
|
120
105
|
data-slot="field-description"
|
|
121
|
-
className=
|
|
122
|
-
'text-muted-foreground text-left text-sm [[data-variant=legend]+&]:-mt-1.5 leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance',
|
|
123
|
-
'last:mt-0 nth-last-2:-mt-1',
|
|
124
|
-
'[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4',
|
|
125
|
-
className,
|
|
126
|
-
)}
|
|
106
|
+
className="text-muted-foreground text-left text-sm [[data-variant=legend]+&]:-mt-1.5 leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance last:mt-0 nth-last-2:-mt-1 [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4"
|
|
127
107
|
{...props}
|
|
128
108
|
/>
|
|
129
109
|
);
|
|
@@ -156,11 +136,10 @@ function FieldSeparator({
|
|
|
156
136
|
}
|
|
157
137
|
|
|
158
138
|
function FieldError({
|
|
159
|
-
className,
|
|
160
139
|
children,
|
|
161
140
|
errors,
|
|
162
141
|
...props
|
|
163
|
-
}: React.ComponentProps<'div'> & {
|
|
142
|
+
}: Omit<React.ComponentProps<'div'>, 'className'> & {
|
|
164
143
|
errors?: Array<{ message?: string } | undefined>;
|
|
165
144
|
}) {
|
|
166
145
|
const content = useMemo(() => {
|
|
@@ -193,7 +172,7 @@ function FieldError({
|
|
|
193
172
|
<div
|
|
194
173
|
role="alert"
|
|
195
174
|
data-slot="field-error"
|
|
196
|
-
className=
|
|
175
|
+
className="text-destructive text-sm font-normal"
|
|
197
176
|
{...props}
|
|
198
177
|
>
|
|
199
178
|
{content}
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import { PreviewCard as PreviewCardPrimitive } from '@base-ui/react/preview-card';
|
|
4
4
|
|
|
5
|
-
import { cn } from '../../../lib/utils';
|
|
6
|
-
|
|
7
5
|
function HoverCard({ ...props }: PreviewCardPrimitive.Root.Props) {
|
|
8
6
|
return <PreviewCardPrimitive.Root data-slot="hover-card" {...props} />;
|
|
9
7
|
}
|
|
@@ -13,13 +11,12 @@ function HoverCardTrigger({ ...props }: PreviewCardPrimitive.Trigger.Props) {
|
|
|
13
11
|
}
|
|
14
12
|
|
|
15
13
|
function HoverCardContent({
|
|
16
|
-
className,
|
|
17
14
|
side = 'bottom',
|
|
18
15
|
sideOffset = 4,
|
|
19
16
|
align = 'center',
|
|
20
17
|
alignOffset = 4,
|
|
21
18
|
...props
|
|
22
|
-
}: PreviewCardPrimitive.Popup.Props &
|
|
19
|
+
}: Omit<PreviewCardPrimitive.Popup.Props, 'className'> &
|
|
23
20
|
Pick<PreviewCardPrimitive.Positioner.Props, 'align' | 'alignOffset' | 'side' | 'sideOffset'>) {
|
|
24
21
|
return (
|
|
25
22
|
<PreviewCardPrimitive.Portal data-slot="hover-card-portal">
|
|
@@ -32,10 +29,7 @@ function HoverCardContent({
|
|
|
32
29
|
>
|
|
33
30
|
<PreviewCardPrimitive.Popup
|
|
34
31
|
data-slot="hover-card-content"
|
|
35
|
-
className=
|
|
36
|
-
'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 bg-popover text-popover-foreground w-64 rounded-lg p-4 text-sm shadow-md ring-1 duration-100 z-50 origin-(--transform-origin) outline-hidden',
|
|
37
|
-
className,
|
|
38
|
-
)}
|
|
32
|
+
className="data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 bg-popover text-popover-foreground w-64 rounded-lg p-4 text-sm shadow-md ring-1 duration-100 z-50 origin-(--transform-origin) outline-hidden"
|
|
39
33
|
{...props}
|
|
40
34
|
/>
|
|
41
35
|
</PreviewCardPrimitive.Positioner>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export * from './accordion';
|
|
2
|
+
export * from './ai-chat';
|
|
3
|
+
export * from './alert';
|
|
4
|
+
export * from './breadcrumb';
|
|
5
|
+
export * from './button-group';
|
|
6
|
+
export * from './card';
|
|
7
|
+
export * from './collapsible';
|
|
8
|
+
export * from './command-search';
|
|
9
|
+
export * from './empty';
|
|
10
|
+
export * from './field';
|
|
11
|
+
export * from './grid';
|
|
12
|
+
export * from './hover-card';
|
|
13
|
+
export * from './input-group';
|
|
14
|
+
export * from './input-otp';
|
|
15
|
+
export * from './item';
|
|
16
|
+
export * from './page-header';
|
|
17
|
+
export * from './pagination';
|
|
18
|
+
export * from './popover';
|
|
19
|
+
export * from './radio-group';
|
|
20
|
+
export * from './resizable';
|
|
21
|
+
export * from './scroll-area';
|
|
22
|
+
export * from './section';
|
|
23
|
+
export * from './select';
|
|
24
|
+
export * from './settings';
|
|
25
|
+
export * from './table';
|
|
26
|
+
export * from './tabs';
|
|
27
|
+
export * from './theme-switcher';
|
|
28
|
+
export * from './toggle-group';
|
|
29
|
+
export * from './tooltip';
|
|
@@ -4,7 +4,7 @@ import { cva, type VariantProps } from 'class-variance-authority';
|
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
|
|
6
6
|
import { cn } from '../../../lib/utils';
|
|
7
|
-
import { buttonVariants } from '
|
|
7
|
+
import { buttonVariants } from '../atoms/button';
|
|
8
8
|
|
|
9
9
|
function InputGroup({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
|
|
10
10
|
return (
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { OTPInput, OTPInputContext } from 'input-otp';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
import { Subtract } from '@carbon/icons-react';
|
|
5
|
+
|
|
6
|
+
function InputOTP({
|
|
7
|
+
className: _className,
|
|
8
|
+
...props
|
|
9
|
+
}: React.ComponentProps<typeof OTPInput>) {
|
|
10
|
+
return (
|
|
11
|
+
<OTPInput
|
|
12
|
+
data-slot="input-otp"
|
|
13
|
+
containerClassName="cn-input-otp flex items-center has-disabled:opacity-50"
|
|
14
|
+
spellCheck={false}
|
|
15
|
+
className="disabled:cursor-not-allowed"
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function InputOTPGroup({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
|
|
22
|
+
return (
|
|
23
|
+
<div
|
|
24
|
+
data-slot="input-otp-group"
|
|
25
|
+
className="has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive rounded-md has-aria-invalid:ring-[3px] flex items-center"
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function InputOTPSlot({
|
|
32
|
+
index,
|
|
33
|
+
...props
|
|
34
|
+
}: Omit<React.ComponentProps<'div'>, 'className'> & {
|
|
35
|
+
index: number;
|
|
36
|
+
}) {
|
|
37
|
+
const inputOTPContext = React.useContext(OTPInputContext);
|
|
38
|
+
const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {};
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div
|
|
42
|
+
data-slot="input-otp-slot"
|
|
43
|
+
data-active={isActive}
|
|
44
|
+
className="dark:bg-input/30 border-input data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive size-9 border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:ring-[3px] relative flex items-center justify-center data-[active=true]:z-10"
|
|
45
|
+
{...props}
|
|
46
|
+
>
|
|
47
|
+
{char}
|
|
48
|
+
{hasFakeCaret && (
|
|
49
|
+
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
|
50
|
+
<div className="animate-caret-blink bg-foreground h-4 w-px duration-1000 bg-foreground h-4 w-px" />
|
|
51
|
+
</div>
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function InputOTPSeparator({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
|
|
58
|
+
return (
|
|
59
|
+
<div
|
|
60
|
+
data-slot="input-otp-separator"
|
|
61
|
+
className="[&_svg:not([class*='size-'])]:size-4 flex items-center"
|
|
62
|
+
role="separator"
|
|
63
|
+
{...props}
|
|
64
|
+
>
|
|
65
|
+
<Subtract />
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot };
|
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
import { mergeProps } from '@base-ui/react/merge-props';
|
|
2
2
|
import { useRender } from '@base-ui/react/use-render';
|
|
3
3
|
import { cva, type VariantProps } from 'class-variance-authority';
|
|
4
|
-
import * as React from 'react';
|
|
5
4
|
|
|
6
5
|
import { Separator as SeparatorPrimitive } from '@base-ui/react/separator';
|
|
7
|
-
import { cn } from '../../../lib/utils';
|
|
8
6
|
|
|
9
|
-
function ItemGroup({
|
|
7
|
+
function ItemGroup({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
|
|
10
8
|
return (
|
|
11
9
|
<div
|
|
12
10
|
role="list"
|
|
13
11
|
data-slot="item-group"
|
|
14
|
-
className=
|
|
15
|
-
'gap-4 has-[[data-size=sm]]:gap-2.5 has-[[data-size=xs]]:gap-2 group/item-group flex w-full flex-col',
|
|
16
|
-
className,
|
|
17
|
-
)}
|
|
12
|
+
className="gap-4 has-[[data-size=sm]]:gap-2.5 has-[[data-size=xs]]:gap-2 group/item-group flex w-full flex-col"
|
|
18
13
|
{...props}
|
|
19
14
|
/>
|
|
20
15
|
);
|
|
@@ -54,17 +49,16 @@ const itemVariants = cva(
|
|
|
54
49
|
);
|
|
55
50
|
|
|
56
51
|
function Item({
|
|
57
|
-
className,
|
|
58
52
|
variant = 'default',
|
|
59
53
|
size = 'default',
|
|
60
54
|
render,
|
|
61
55
|
...props
|
|
62
|
-
}: useRender.ComponentProps<'div'> & VariantProps<typeof itemVariants>) {
|
|
56
|
+
}: Omit<useRender.ComponentProps<'div'>, 'className'> & VariantProps<typeof itemVariants>) {
|
|
63
57
|
return useRender({
|
|
64
58
|
defaultTagName: 'div',
|
|
65
59
|
props: mergeProps<'div'>(
|
|
66
60
|
{
|
|
67
|
-
className:
|
|
61
|
+
className: itemVariants({ variant, size }),
|
|
68
62
|
},
|
|
69
63
|
props,
|
|
70
64
|
),
|
|
@@ -95,80 +89,68 @@ const itemMediaVariants = cva(
|
|
|
95
89
|
);
|
|
96
90
|
|
|
97
91
|
function ItemMedia({
|
|
98
|
-
className,
|
|
99
92
|
variant = 'default',
|
|
100
93
|
...props
|
|
101
|
-
}: React.ComponentProps<'div'> & VariantProps<typeof itemMediaVariants>) {
|
|
94
|
+
}: Omit<React.ComponentProps<'div'>, 'className'> & VariantProps<typeof itemMediaVariants>) {
|
|
102
95
|
return (
|
|
103
96
|
<div
|
|
104
97
|
data-slot="item-media"
|
|
105
98
|
data-variant={variant}
|
|
106
|
-
className={
|
|
99
|
+
className={itemMediaVariants({ variant })}
|
|
107
100
|
{...props}
|
|
108
101
|
/>
|
|
109
102
|
);
|
|
110
103
|
}
|
|
111
104
|
|
|
112
|
-
function ItemContent({
|
|
105
|
+
function ItemContent({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
|
|
113
106
|
return (
|
|
114
107
|
<div
|
|
115
108
|
data-slot="item-content"
|
|
116
|
-
className=
|
|
117
|
-
'gap-1 group-data-[size=xs]/item:gap-0 flex flex-1 flex-col [&+[data-slot=item-content]]:flex-none',
|
|
118
|
-
className,
|
|
119
|
-
)}
|
|
109
|
+
className="gap-1 group-data-[size=xs]/item:gap-0 flex flex-1 flex-col [&+[data-slot=item-content]]:flex-none"
|
|
120
110
|
{...props}
|
|
121
111
|
/>
|
|
122
112
|
);
|
|
123
113
|
}
|
|
124
114
|
|
|
125
|
-
function ItemTitle({
|
|
115
|
+
function ItemTitle({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
|
|
126
116
|
return (
|
|
127
117
|
<div
|
|
128
118
|
data-slot="item-title"
|
|
129
|
-
className=
|
|
130
|
-
'gap-2 text-sm leading-snug font-medium underline-offset-4 line-clamp-1 flex w-fit items-center',
|
|
131
|
-
className,
|
|
132
|
-
)}
|
|
119
|
+
className="gap-2 text-sm leading-snug font-medium underline-offset-4 line-clamp-1 flex w-fit items-center"
|
|
133
120
|
{...props}
|
|
134
121
|
/>
|
|
135
122
|
);
|
|
136
123
|
}
|
|
137
124
|
|
|
138
|
-
function ItemDescription({
|
|
125
|
+
function ItemDescription({ ...props }: Omit<React.ComponentProps<'p'>, 'className'>) {
|
|
139
126
|
return (
|
|
140
127
|
<p
|
|
141
128
|
data-slot="item-description"
|
|
142
|
-
className=
|
|
143
|
-
'text-muted-foreground text-left text-sm leading-normal group-data-[size=xs]/item:text-xs [&>a:hover]:text-primary line-clamp-2 font-normal [&>a]:underline [&>a]:underline-offset-4',
|
|
144
|
-
className,
|
|
145
|
-
)}
|
|
129
|
+
className="text-muted-foreground text-left text-sm leading-normal group-data-[size=xs]/item:text-xs [&>a:hover]:text-primary line-clamp-2 font-normal [&>a]:underline [&>a]:underline-offset-4"
|
|
146
130
|
{...props}
|
|
147
131
|
/>
|
|
148
132
|
);
|
|
149
133
|
}
|
|
150
134
|
|
|
151
|
-
function ItemActions({
|
|
152
|
-
return
|
|
153
|
-
<div data-slot="item-actions" className={cn('gap-2 flex items-center', className)} {...props} />
|
|
154
|
-
);
|
|
135
|
+
function ItemActions({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
|
|
136
|
+
return <div data-slot="item-actions" className="gap-2 flex items-center" {...props} />;
|
|
155
137
|
}
|
|
156
138
|
|
|
157
|
-
function ItemHeader({
|
|
139
|
+
function ItemHeader({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
|
|
158
140
|
return (
|
|
159
141
|
<div
|
|
160
142
|
data-slot="item-header"
|
|
161
|
-
className=
|
|
143
|
+
className="gap-2 flex basis-full items-center justify-between"
|
|
162
144
|
{...props}
|
|
163
145
|
/>
|
|
164
146
|
);
|
|
165
147
|
}
|
|
166
148
|
|
|
167
|
-
function ItemFooter({
|
|
149
|
+
function ItemFooter({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
|
|
168
150
|
return (
|
|
169
151
|
<div
|
|
170
152
|
data-slot="item-footer"
|
|
171
|
-
className=
|
|
153
|
+
className="gap-2 flex basis-full items-center justify-between"
|
|
172
154
|
{...props}
|
|
173
155
|
/>
|
|
174
156
|
);
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { Heading } from '../atoms/heading';
|
|
4
|
+
import { Text } from '../atoms/text';
|
|
5
|
+
import { Stack } from '../atoms/stack';
|
|
6
|
+
|
|
7
|
+
interface PageHeaderProps extends Omit<React.ComponentProps<'div'>, 'className'> {
|
|
8
|
+
title: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
/** Additional descriptive text below description */
|
|
11
|
+
meta?: string;
|
|
12
|
+
actions?: React.ReactNode;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function PageHeader({ title, description, meta, actions, children, ...props }: PageHeaderProps) {
|
|
16
|
+
const childArray = React.Children.toArray(children);
|
|
17
|
+
const extractedActionChildren: React.ReactNode[] = [];
|
|
18
|
+
const nonActionChildren: React.ReactNode[] = [];
|
|
19
|
+
|
|
20
|
+
childArray.forEach((child) => {
|
|
21
|
+
if (
|
|
22
|
+
React.isValidElement(child) &&
|
|
23
|
+
(child.type === PageHeaderActions ||
|
|
24
|
+
(typeof child.type === 'function' &&
|
|
25
|
+
(child.type as unknown as { __pageHeaderSlot?: string }).__pageHeaderSlot === 'actions'))
|
|
26
|
+
) {
|
|
27
|
+
extractedActionChildren.push((child.props as { children?: React.ReactNode }).children);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
nonActionChildren.push(child);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const resolvedActions =
|
|
34
|
+
actions ??
|
|
35
|
+
(extractedActionChildren.length > 0 ? extractedActionChildren : undefined);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div data-slot="page-header" className="flex items-start justify-between gap-4" {...props}>
|
|
39
|
+
<Stack gap="1">
|
|
40
|
+
<Heading level="1">{title}</Heading>
|
|
41
|
+
{description && (
|
|
42
|
+
<Text size="sm" variant="muted">
|
|
43
|
+
{description}
|
|
44
|
+
</Text>
|
|
45
|
+
)}
|
|
46
|
+
{meta && (
|
|
47
|
+
<Text size="xs" variant="muted">
|
|
48
|
+
{meta}
|
|
49
|
+
</Text>
|
|
50
|
+
)}
|
|
51
|
+
{nonActionChildren}
|
|
52
|
+
</Stack>
|
|
53
|
+
{resolvedActions &&
|
|
54
|
+
(React.isValidElement(resolvedActions) && resolvedActions.type === PageHeaderActions ? (
|
|
55
|
+
resolvedActions
|
|
56
|
+
) : (
|
|
57
|
+
<PageHeaderActions>{resolvedActions}</PageHeaderActions>
|
|
58
|
+
))}
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function PageHeaderTitle({ ...props }: Omit<React.ComponentProps<typeof Heading>, 'className'>) {
|
|
64
|
+
return <Heading data-slot="page-header-title" level="1" {...props} />;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function PageHeaderDescription({ ...props }: Omit<React.ComponentProps<typeof Text>, 'className'>) {
|
|
68
|
+
return <Text data-slot="page-header-description" size="sm" variant="muted" {...props} />;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function PageHeaderActions({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
|
|
72
|
+
return (
|
|
73
|
+
<div data-slot="page-header-actions" className="flex shrink-0 items-center gap-3" {...props} />
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Mark compound slots so PageHeader can detect them even if module instances differ.
|
|
78
|
+
(PageHeaderActions as unknown as { __pageHeaderSlot?: string }).__pageHeaderSlot = 'actions';
|
|
79
|
+
|
|
80
|
+
export { PageHeader, PageHeaderActions, PageHeaderDescription, PageHeaderTitle };
|
|
@@ -1,33 +1,27 @@
|
|
|
1
1
|
import { Button as ButtonPrimitive } from '@base-ui/react/button';
|
|
2
|
-
import * as React from 'react';
|
|
3
2
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { buttonVariants } from './button';
|
|
3
|
+
import { ChevronLeft, ChevronRight, OverflowMenuHorizontal } from '@carbon/icons-react';
|
|
4
|
+
import { buttonVariants } from '../atoms/button';
|
|
7
5
|
|
|
8
|
-
function Pagination({
|
|
6
|
+
function Pagination({ ...props }: Omit<React.ComponentProps<'nav'>, 'className'>) {
|
|
9
7
|
return (
|
|
10
8
|
<nav
|
|
11
9
|
role="navigation"
|
|
12
10
|
aria-label="pagination"
|
|
13
11
|
data-slot="pagination"
|
|
14
|
-
className=
|
|
12
|
+
className="mx-auto flex w-full justify-center"
|
|
15
13
|
{...props}
|
|
16
14
|
/>
|
|
17
15
|
);
|
|
18
16
|
}
|
|
19
17
|
|
|
20
|
-
function PaginationContent({
|
|
18
|
+
function PaginationContent({ ...props }: Omit<React.ComponentProps<'ul'>, 'className'>) {
|
|
21
19
|
return (
|
|
22
|
-
<ul
|
|
23
|
-
data-slot="pagination-content"
|
|
24
|
-
className={cn('gap-1 flex items-center', className)}
|
|
25
|
-
{...props}
|
|
26
|
-
/>
|
|
20
|
+
<ul data-slot="pagination-content" className="gap-1 flex items-center" {...props} />
|
|
27
21
|
);
|
|
28
22
|
}
|
|
29
23
|
|
|
30
|
-
function PaginationItem({ ...props }: React.ComponentProps<'li'>) {
|
|
24
|
+
function PaginationItem({ ...props }: Omit<React.ComponentProps<'li'>, 'className'>) {
|
|
31
25
|
return <li data-slot="pagination-item" {...props} />;
|
|
32
26
|
}
|
|
33
27
|
|
|
@@ -55,10 +49,10 @@ function PaginationLink({ isActive, size = 'icon', ...props }: PaginationLinkPro
|
|
|
55
49
|
function PaginationPrevious({ ...props }: Omit<PaginationLinkProps, 'size'>) {
|
|
56
50
|
return (
|
|
57
51
|
<ButtonPrimitive
|
|
58
|
-
className={
|
|
52
|
+
className={`${buttonVariants({ variant: 'ghost', size: 'default' })} pl-2`}
|
|
59
53
|
render={<a aria-label="Go to previous page" data-slot="pagination-link" {...props} />}
|
|
60
54
|
>
|
|
61
|
-
<
|
|
55
|
+
<ChevronLeft data-icon="inline-start" />
|
|
62
56
|
<span className="hidden sm:block">Previous</span>
|
|
63
57
|
</ButtonPrimitive>
|
|
64
58
|
);
|
|
@@ -67,27 +61,24 @@ function PaginationPrevious({ ...props }: Omit<PaginationLinkProps, 'size'>) {
|
|
|
67
61
|
function PaginationNext({ ...props }: Omit<PaginationLinkProps, 'size'>) {
|
|
68
62
|
return (
|
|
69
63
|
<ButtonPrimitive
|
|
70
|
-
className={
|
|
64
|
+
className={`${buttonVariants({ variant: 'ghost', size: 'default' })} pr-2`}
|
|
71
65
|
render={<a aria-label="Go to next page" data-slot="pagination-link" {...props} />}
|
|
72
66
|
>
|
|
73
67
|
<span className="hidden sm:block">Next</span>
|
|
74
|
-
<
|
|
68
|
+
<ChevronRight data-icon="inline-end" />
|
|
75
69
|
</ButtonPrimitive>
|
|
76
70
|
);
|
|
77
71
|
}
|
|
78
72
|
|
|
79
|
-
function PaginationEllipsis({
|
|
73
|
+
function PaginationEllipsis({ ...props }: Omit<React.ComponentProps<'span'>, 'className'>) {
|
|
80
74
|
return (
|
|
81
75
|
<span
|
|
82
76
|
aria-hidden
|
|
83
77
|
data-slot="pagination-ellipsis"
|
|
84
|
-
className=
|
|
85
|
-
"size-9 items-center justify-center [&_svg:not([class*='size-'])]:size-4 flex items-center justify-center",
|
|
86
|
-
className,
|
|
87
|
-
)}
|
|
78
|
+
className="size-9 items-center justify-center [&_svg:not([class*='size-'])]:size-4 flex items-center justify-center"
|
|
88
79
|
{...props}
|
|
89
80
|
>
|
|
90
|
-
<
|
|
81
|
+
<OverflowMenuHorizontal />
|
|
91
82
|
<span className="sr-only">More pages</span>
|
|
92
83
|
</span>
|
|
93
84
|
);
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { Popover as PopoverPrimitive } from '@base-ui/react/popover';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
|
|
4
|
+
import { cn } from '../../../lib/utils';
|
|
5
|
+
|
|
4
6
|
function Popover({ ...props }: PopoverPrimitive.Root.Props) {
|
|
5
7
|
return <PopoverPrimitive.Root data-slot="popover" {...props} />;
|
|
6
8
|
}
|
|
7
9
|
|
|
8
|
-
function PopoverTrigger({ ...props }: PopoverPrimitive.Trigger.Props) {
|
|
9
|
-
return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />;
|
|
10
|
+
function PopoverTrigger({ className, ...props }: PopoverPrimitive.Trigger.Props) {
|
|
11
|
+
return <PopoverPrimitive.Trigger data-slot="popover-trigger" className={cn(className)} {...props} />;
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
function PopoverContent({
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Radio as RadioPrimitive } from '@base-ui/react/radio';
|
|
2
|
+
import { RadioGroup as RadioGroupPrimitive } from '@base-ui/react/radio-group';
|
|
3
|
+
|
|
4
|
+
import { CircleFilled } from '@carbon/icons-react';
|
|
5
|
+
|
|
6
|
+
function RadioGroup({ ...props }: Omit<RadioGroupPrimitive.Props, 'className'>) {
|
|
7
|
+
return (
|
|
8
|
+
<RadioGroupPrimitive
|
|
9
|
+
data-slot="radio-group"
|
|
10
|
+
className="grid gap-2 w-full"
|
|
11
|
+
{...props}
|
|
12
|
+
/>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function RadioGroupItem({ ...props }: Omit<RadioPrimitive.Root.Props, 'className'>) {
|
|
17
|
+
return (
|
|
18
|
+
<RadioPrimitive.Root
|
|
19
|
+
data-slot="radio-group-item"
|
|
20
|
+
className="border-input text-primary dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 flex size-4 rounded-full shadow-xs focus-visible:ring-[3px] aria-invalid:ring-[3px] group/radio-group-item peer relative aspect-square shrink-0 border outline-none after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
21
|
+
{...props}
|
|
22
|
+
>
|
|
23
|
+
<RadioPrimitive.Indicator
|
|
24
|
+
data-slot="radio-group-indicator"
|
|
25
|
+
className="group-aria-invalid/radio-group-item:text-destructive text-primary flex size-4 items-center justify-center"
|
|
26
|
+
>
|
|
27
|
+
<CircleFilled className="absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" />
|
|
28
|
+
</RadioPrimitive.Indicator>
|
|
29
|
+
</RadioPrimitive.Root>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { RadioGroup, RadioGroupItem };
|