@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trycompai/design-system",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Design system for Comp AI - shadcn-style components with Tailwind CSS",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"default": "./lib/utils.ts"
|
|
24
24
|
},
|
|
25
25
|
"./globals.css": "./src/styles/globals.css",
|
|
26
|
+
"./fonts/*": "./src/fonts/*",
|
|
26
27
|
"./tailwind.config": {
|
|
27
28
|
"types": "./tailwind.config.ts",
|
|
28
29
|
"import": "./tailwind.config.ts",
|
|
@@ -67,13 +68,14 @@
|
|
|
67
68
|
},
|
|
68
69
|
"dependencies": {
|
|
69
70
|
"@base-ui/react": "^1.0.0",
|
|
71
|
+
"@carbon/icons-react": "^11.72.0",
|
|
72
|
+
"@fontsource-variable/plus-jakarta-sans": "^5.2.8",
|
|
70
73
|
"class-variance-authority": "^0.7.1",
|
|
71
74
|
"clsx": "^2.1.1",
|
|
72
75
|
"cmdk": "^1.1.1",
|
|
73
76
|
"date-fns": "^4.1.0",
|
|
74
77
|
"embla-carousel-react": "^8.6.0",
|
|
75
78
|
"input-otp": "^1.4.2",
|
|
76
|
-
"lucide-react": "^0.562.0",
|
|
77
79
|
"next-themes": "^0.4.6",
|
|
78
80
|
"react-day-picker": "^9.13.0",
|
|
79
81
|
"react-resizable-panels": "^4.2.0",
|
|
@@ -90,6 +92,7 @@
|
|
|
90
92
|
"tailwindcss": "^4.0.0"
|
|
91
93
|
},
|
|
92
94
|
"devDependencies": {
|
|
95
|
+
"@repo/typescript-config": "workspace:*",
|
|
93
96
|
"@tailwindcss/postcss": "^4.1.10",
|
|
94
97
|
"@types/node": "^22.18.0",
|
|
95
98
|
"@types/react": "^19.2.7",
|
|
@@ -3,20 +3,21 @@ import { useRender } from '@base-ui/react/use-render';
|
|
|
3
3
|
import { cva, type VariantProps } from 'class-variance-authority';
|
|
4
4
|
|
|
5
5
|
const badgeVariants = cva(
|
|
6
|
-
'
|
|
6
|
+
'gap-1 rounded-sm px-1.5 py-1 text-[10px] font-semibold uppercase tracking-wider leading-none transition-colors has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 [&>svg]:size-2.5! inline-flex items-center justify-center w-fit whitespace-nowrap shrink-0 [&>svg]:pointer-events-none focus-visible:ring-ring/50 focus-visible:ring-[3px] overflow-hidden antialiased select-none',
|
|
7
7
|
{
|
|
8
8
|
variants: {
|
|
9
9
|
variant: {
|
|
10
|
-
default:
|
|
10
|
+
default:
|
|
11
|
+
'bg-primary text-primary-foreground [a]:hover:bg-primary/90',
|
|
11
12
|
secondary:
|
|
12
|
-
'
|
|
13
|
+
'bg-muted text-muted-foreground [a]:hover:bg-muted/80',
|
|
13
14
|
destructive:
|
|
14
|
-
'
|
|
15
|
+
'bg-destructive/10 text-destructive [a]:hover:bg-destructive/15 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/15',
|
|
15
16
|
outline:
|
|
16
|
-
'border-border bg-transparent text-foreground [a]:hover:bg-muted
|
|
17
|
+
'border border-border/50 bg-transparent text-foreground [a]:hover:bg-muted/30',
|
|
17
18
|
ghost:
|
|
18
|
-
'
|
|
19
|
-
link: '
|
|
19
|
+
'bg-transparent text-muted-foreground hover:bg-muted/50 hover:text-foreground',
|
|
20
|
+
link: 'bg-transparent text-primary underline-offset-4 hover:underline',
|
|
20
21
|
},
|
|
21
22
|
},
|
|
22
23
|
defaultVariants: {
|
|
@@ -5,7 +5,7 @@ import * as React from 'react';
|
|
|
5
5
|
import { Spinner } from './spinner';
|
|
6
6
|
|
|
7
7
|
const buttonVariants = cva(
|
|
8
|
-
"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 rounded-lg border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
|
|
8
|
+
"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 rounded-lg border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all duration-150 ease-out active:scale-[0.98] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none cursor-pointer",
|
|
9
9
|
{
|
|
10
10
|
variants: {
|
|
11
11
|
variant: {
|
|
@@ -36,6 +36,11 @@ const buttonVariants = cva(
|
|
|
36
36
|
'icon-sm':
|
|
37
37
|
'size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg',
|
|
38
38
|
'icon-lg': 'size-9',
|
|
39
|
+
// Round icon buttons - for avatar triggers and circular icons
|
|
40
|
+
'icon-round': 'size-8 rounded-full',
|
|
41
|
+
'icon-round-xs': "size-6 rounded-full [&_svg:not([class*='size-'])]:size-3",
|
|
42
|
+
'icon-round-sm': 'size-7 rounded-full',
|
|
43
|
+
'icon-round-lg': 'size-9 rounded-full',
|
|
39
44
|
// Calendar day button - special size for calendar day cells
|
|
40
45
|
'calendar-day': [
|
|
41
46
|
// Base sizing
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { Checkbox as CheckboxPrimitive } from '@base-ui/react/checkbox';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { Checkmark } from '@carbon/icons-react';
|
|
4
4
|
|
|
5
5
|
function Checkbox({ ...props }: Omit<CheckboxPrimitive.Root.Props, 'className'>) {
|
|
6
6
|
return (
|
|
7
7
|
<CheckboxPrimitive.Root
|
|
8
8
|
data-slot="checkbox"
|
|
9
|
-
className="border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 flex size-4 items-center justify-center rounded-[4px] border transition-colors group-has-disabled/field:opacity-50 focus-visible:ring-[3px] aria-invalid:ring-[3px] peer relative shrink-0 outline-none after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
9
|
+
className="border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 flex size-4 items-center justify-center rounded-[4px] border transition-colors group-has-disabled/field:opacity-50 focus-visible:ring-[3px] aria-invalid:ring-[3px] peer relative shrink-0 outline-none after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50 cursor-pointer"
|
|
10
10
|
{...props}
|
|
11
11
|
>
|
|
12
12
|
<CheckboxPrimitive.Indicator
|
|
13
13
|
data-slot="checkbox-indicator"
|
|
14
14
|
className="[&>svg]:size-3.5 grid place-content-center text-current transition-none"
|
|
15
15
|
>
|
|
16
|
-
<
|
|
16
|
+
<Checkmark />
|
|
17
17
|
</CheckboxPrimitive.Indicator>
|
|
18
18
|
</CheckboxPrimitive.Root>
|
|
19
19
|
);
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { cva, type VariantProps } from 'class-variance-authority';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
|
|
4
|
-
const headingVariants = cva('font-
|
|
4
|
+
const headingVariants = cva('font-medium', {
|
|
5
5
|
variants: {
|
|
6
6
|
level: {
|
|
7
|
-
'1': 'text-
|
|
7
|
+
'1': 'text-4xl tracking-tight',
|
|
8
8
|
'2': 'text-xl',
|
|
9
|
-
'3': 'text-lg
|
|
10
|
-
'4': 'text-base
|
|
11
|
-
'5': 'text-sm
|
|
12
|
-
'6': 'text-sm
|
|
9
|
+
'3': 'text-lg ',
|
|
10
|
+
'4': 'text-base ',
|
|
11
|
+
'5': 'text-sm ',
|
|
12
|
+
'6': 'text-sm ',
|
|
13
13
|
},
|
|
14
14
|
variant: {
|
|
15
15
|
default: 'text-foreground',
|
|
@@ -8,11 +8,13 @@ export * from './heading';
|
|
|
8
8
|
export * from './input';
|
|
9
9
|
export * from './kbd';
|
|
10
10
|
export * from './label';
|
|
11
|
+
export * from './logo';
|
|
11
12
|
export * from './progress';
|
|
12
13
|
export * from './separator';
|
|
13
14
|
export * from './skeleton';
|
|
14
15
|
export * from './slider';
|
|
15
16
|
export * from './spinner';
|
|
17
|
+
export * from './stack';
|
|
16
18
|
export * from './switch';
|
|
17
19
|
export * from './text';
|
|
18
20
|
export * from './textarea';
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
interface LogoProps extends Omit<React.SVGProps<SVGSVGElement>, 'className'> {
|
|
6
|
+
/** Logo variant - 'dark' shows dark logo (for light backgrounds), 'light' shows white logo (for dark backgrounds) */
|
|
7
|
+
variant?: 'dark' | 'light';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function Logo({ variant = 'dark', ...props }: LogoProps) {
|
|
11
|
+
const fill = variant === 'light' ? '#FFFFFF' : '#16171B';
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<svg
|
|
15
|
+
viewBox="0 0 1119 272"
|
|
16
|
+
fill="none"
|
|
17
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
18
|
+
{...props}
|
|
19
|
+
>
|
|
20
|
+
<path
|
|
21
|
+
d="M204 52.885L195.465 46.2399L136.011 0L0 105.77V166.217L136.011 272L272 166.217V105.77L204 52.885ZM136.011 26.631L178.349 59.5553L156.598 76.4509L154.653 77.9583L136.011 63.4621L81.6113 105.77L100.253 120.266L117.369 133.594L136.011 148.091L190.4 105.758L171.781 91.2613L173.725 89.7538L195.476 72.8583L237.791 105.77L216.04 122.691L136.023 184.934L93.6851 152.01L76.5692 138.707L55.9827 122.703L34.2431 105.783L136.011 26.631Z"
|
|
22
|
+
fill={fill}
|
|
23
|
+
/>
|
|
24
|
+
<path
|
|
25
|
+
d="M423.8 210.2C411.133 210.2 400 207.267 390.4 201.4C380.8 195.4 373.333 186.867 368 175.8C362.667 164.733 360 151.533 360 136.2C360 120.867 362.667 107.667 368 96.6C373.333 85.4 380.8 76.8 390.4 70.8C400 64.8 411.133 61.8 423.8 61.8C441 61.8 454.333 66.1333 463.8 74.8C473.267 83.4667 479.467 95.2667 482.4 110.2L464.2 111.4C462.2 101.267 457.933 93.2667 451.4 87.4C444.867 81.5333 435.667 78.6 423.8 78.6C414.733 78.6 406.733 80.9333 399.8 85.6C393 90.1333 387.667 96.7333 383.8 105.4C379.933 113.933 378 124.2 378 136.2C378 148.2 379.933 158.467 383.8 167C387.667 175.533 393 182.067 399.8 186.6C406.733 191.133 414.733 193.4 423.8 193.4C436.2 193.4 445.8 190.2 452.6 183.8C459.4 177.267 463.667 168.467 465.4 157.4L483.6 158.6C481.867 168.867 478.467 177.867 473.4 185.6C468.333 193.333 461.6 199.4 453.2 203.8C444.933 208.067 435.133 210.2 423.8 210.2ZM547.463 209.4C537.729 209.4 529.196 207.133 521.863 202.6C514.529 198.067 508.863 191.667 504.863 183.4C500.863 175 498.863 165.2 498.863 154C498.863 142.667 500.863 132.867 504.863 124.6C508.863 116.333 514.529 109.933 521.863 105.4C529.196 100.867 537.729 98.6 547.463 98.6C557.196 98.6 565.663 100.867 572.863 105.4C580.196 109.933 585.863 116.333 589.863 124.6C593.863 132.867 595.863 142.667 595.863 154C595.863 165.2 593.863 175 589.863 183.4C585.863 191.667 580.196 198.067 572.863 202.6C565.663 207.133 557.196 209.4 547.463 209.4ZM547.463 193.4C557.196 193.4 564.729 189.933 570.063 183C575.529 175.933 578.263 166.267 578.263 154C578.263 141.733 575.529 132.133 570.063 125.2C564.729 118.133 557.196 114.6 547.463 114.6C537.729 114.6 530.129 118.133 524.663 125.2C519.196 132.133 516.462 141.733 516.462 154C516.462 166.267 519.196 175.933 524.663 183C530.129 189.933 537.729 193.4 547.463 193.4ZM618.758 207V101H634.158L634.758 127.8L632.358 127C633.558 121.133 635.624 116.133 638.558 112C641.624 107.733 645.358 104.467 649.758 102.2C654.158 99.8 659.024 98.6 664.358 98.6C673.558 98.6 681.024 101.133 686.758 106.2C692.624 111.267 696.291 118.333 697.758 127.4L694.558 127.2C695.624 121.067 697.558 115.933 700.358 111.8C703.291 107.533 707.024 104.267 711.558 102C716.091 99.7333 721.291 98.6 727.158 98.6C734.758 98.6 741.158 100.2 746.358 103.4C751.558 106.467 755.491 111 758.158 117C760.824 123 762.158 130.267 762.158 138.8V207H745.358V142.4C745.358 132.8 743.491 125.6 739.758 120.8C736.024 115.867 730.491 113.4 723.158 113.4C718.224 113.4 713.891 114.6 710.158 117C706.558 119.4 703.758 122.867 701.758 127.4C699.758 131.8 698.758 137.2 698.758 143.6V207H681.958V143.6C681.958 134.133 680.291 126.733 676.958 121.4C673.624 116.067 668.024 113.4 660.158 113.4C655.224 113.4 650.891 114.6 647.158 117C643.558 119.4 640.691 122.867 638.558 127.4C636.558 131.933 635.558 137.333 635.558 143.6V207H618.758ZM790.242 237V101H805.842L806.242 123.8L803.842 122.6C806.509 114.733 810.909 108.8 817.042 104.8C823.309 100.667 830.509 98.6 838.642 98.6C849.176 98.6 857.776 101.2 864.442 106.4C871.242 111.6 876.242 118.4 879.442 126.8C882.776 135.2 884.442 144.267 884.442 154C884.442 163.733 882.776 172.8 879.442 181.2C876.242 189.6 871.242 196.4 864.442 201.6C857.776 206.8 849.176 209.4 838.642 209.4C833.176 209.4 828.042 208.467 823.242 206.6C818.576 204.733 814.576 202.133 811.242 198.8C808.042 195.467 805.842 191.533 804.642 187L807.042 184.2V237H790.242ZM837.042 193.4C846.242 193.4 853.509 189.933 858.842 183C864.176 176.067 866.842 166.4 866.842 154C866.842 141.6 864.176 131.933 858.842 125C853.509 118.067 846.242 114.6 837.042 114.6C830.909 114.6 825.576 116.067 821.042 119C816.642 121.933 813.176 126.333 810.642 132.2C808.242 138.067 807.042 145.333 807.042 154C807.042 162.667 808.242 169.933 810.642 175.8C813.042 181.667 816.509 186.067 821.042 189C825.576 191.933 830.909 193.4 837.042 193.4ZM939.188 207L990.388 65H1013.59L1064.79 207H1045.99L1031.79 166.6H972.188L957.988 207H939.188ZM977.988 149.8H1025.99L1001.99 80L977.988 149.8ZM1083.28 207V65H1100.48V207H1083.28Z"
|
|
26
|
+
fill={fill}
|
|
27
|
+
/>
|
|
28
|
+
</svg>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Icon-only version of the logo (just the shield/chevron mark) */
|
|
33
|
+
function LogoIcon({ variant = 'dark', ...props }: LogoProps) {
|
|
34
|
+
const fill = variant === 'light' ? '#FFFFFF' : '#16171B';
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<svg
|
|
38
|
+
viewBox="0 0 272 272"
|
|
39
|
+
fill="none"
|
|
40
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
41
|
+
{...props}
|
|
42
|
+
>
|
|
43
|
+
<path
|
|
44
|
+
d="M204 52.885L195.465 46.2399L136.011 0L0 105.77V166.217L136.011 272L272 166.217V105.77L204 52.885ZM136.011 26.631L178.349 59.5553L156.598 76.4509L154.653 77.9583L136.011 63.4621L81.6113 105.77L100.253 120.266L117.369 133.594L136.011 148.091L190.4 105.758L171.781 91.2613L173.725 89.7538L195.476 72.8583L237.791 105.77L216.04 122.691L136.023 184.934L93.6851 152.01L76.5692 138.707L55.9827 122.703L34.2431 105.783L136.011 26.631Z"
|
|
45
|
+
fill={fill}
|
|
46
|
+
/>
|
|
47
|
+
</svg>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { Logo, LogoIcon };
|
|
52
|
+
export type { LogoProps };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Renew } from '@carbon/icons-react';
|
|
2
2
|
|
|
3
|
-
function Spinner({ ...props }: Omit<React.ComponentProps<
|
|
3
|
+
function Spinner({ ...props }: Omit<React.ComponentProps<typeof Renew>, 'className'>) {
|
|
4
4
|
return (
|
|
5
|
-
<
|
|
5
|
+
<Renew
|
|
6
6
|
role="status"
|
|
7
7
|
aria-label="Loading"
|
|
8
8
|
className="size-4 shrink-0 animate-spin"
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
const stackVariants = cva('flex', {
|
|
5
|
+
variants: {
|
|
6
|
+
direction: {
|
|
7
|
+
row: 'flex-row',
|
|
8
|
+
column: 'flex-col',
|
|
9
|
+
'row-reverse': 'flex-row-reverse',
|
|
10
|
+
'column-reverse': 'flex-col-reverse',
|
|
11
|
+
},
|
|
12
|
+
gap: {
|
|
13
|
+
none: 'gap-0',
|
|
14
|
+
xs: 'gap-1',
|
|
15
|
+
sm: 'gap-2',
|
|
16
|
+
md: 'gap-4',
|
|
17
|
+
lg: 'gap-6',
|
|
18
|
+
xl: 'gap-8',
|
|
19
|
+
// Numeric values for backward compatibility
|
|
20
|
+
'0': 'gap-0',
|
|
21
|
+
'1': 'gap-1',
|
|
22
|
+
'2': 'gap-2',
|
|
23
|
+
'3': 'gap-3',
|
|
24
|
+
'4': 'gap-4',
|
|
25
|
+
'6': 'gap-6',
|
|
26
|
+
'8': 'gap-8',
|
|
27
|
+
},
|
|
28
|
+
align: {
|
|
29
|
+
start: 'items-start',
|
|
30
|
+
center: 'items-center',
|
|
31
|
+
end: 'items-end',
|
|
32
|
+
stretch: 'items-stretch',
|
|
33
|
+
baseline: 'items-baseline',
|
|
34
|
+
},
|
|
35
|
+
justify: {
|
|
36
|
+
start: 'justify-start',
|
|
37
|
+
center: 'justify-center',
|
|
38
|
+
end: 'justify-end',
|
|
39
|
+
between: 'justify-between',
|
|
40
|
+
around: 'justify-around',
|
|
41
|
+
evenly: 'justify-evenly',
|
|
42
|
+
},
|
|
43
|
+
wrap: {
|
|
44
|
+
nowrap: 'flex-nowrap',
|
|
45
|
+
wrap: 'flex-wrap',
|
|
46
|
+
'wrap-reverse': 'flex-wrap-reverse',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
defaultVariants: {
|
|
50
|
+
direction: 'column',
|
|
51
|
+
gap: 'md',
|
|
52
|
+
align: 'stretch',
|
|
53
|
+
justify: 'start',
|
|
54
|
+
wrap: 'nowrap',
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
type StackElement = 'div' | 'section' | 'nav' | 'ul' | 'ol' | 'main' | 'article' | 'aside' | 'header' | 'footer';
|
|
59
|
+
|
|
60
|
+
interface StackProps
|
|
61
|
+
extends Omit<React.HTMLAttributes<HTMLElement>, 'className'>,
|
|
62
|
+
VariantProps<typeof stackVariants> {
|
|
63
|
+
as?: StackElement;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function Stack({
|
|
67
|
+
as: Component = 'div',
|
|
68
|
+
direction,
|
|
69
|
+
gap,
|
|
70
|
+
align,
|
|
71
|
+
justify,
|
|
72
|
+
wrap,
|
|
73
|
+
...props
|
|
74
|
+
}: StackProps) {
|
|
75
|
+
return (
|
|
76
|
+
<Component
|
|
77
|
+
data-slot="stack"
|
|
78
|
+
className={stackVariants({ direction, gap, align, justify, wrap })}
|
|
79
|
+
{...props}
|
|
80
|
+
/>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
type VStackProps = Omit<StackProps, 'direction'>;
|
|
85
|
+
|
|
86
|
+
function VStack({ gap, align, justify, wrap, ...props }: VStackProps) {
|
|
87
|
+
return <Stack direction="column" gap={gap} align={align} justify={justify} wrap={wrap} {...props} />;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
type HStackProps = Omit<StackProps, 'direction'>;
|
|
91
|
+
|
|
92
|
+
function HStack({ gap, align, justify, wrap, ...props }: HStackProps) {
|
|
93
|
+
return <Stack direction="row" gap={gap} align={align} justify={justify} wrap={wrap} {...props} />;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export { Stack, VStack, HStack, stackVariants };
|
|
97
|
+
export type { StackProps, VStackProps, HStackProps };
|
|
@@ -12,7 +12,7 @@ function Switch({
|
|
|
12
12
|
<SwitchPrimitive.Root
|
|
13
13
|
data-slot="switch"
|
|
14
14
|
data-size={size}
|
|
15
|
-
className="data-checked:bg-primary data-unchecked:bg-input 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 dark:data-unchecked:bg-input/80 shrink-0 rounded-full border border-transparent shadow-xs focus-visible:ring-[3px] aria-invalid:ring-[3px] data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] peer group/switch relative inline-flex items-center transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 data-disabled:cursor-not-allowed data-disabled:opacity-50"
|
|
15
|
+
className="data-checked:bg-primary data-unchecked:bg-input 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 dark:data-unchecked:bg-input/80 shrink-0 rounded-full border border-transparent shadow-xs focus-visible:ring-[3px] aria-invalid:ring-[3px] data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] peer group/switch relative inline-flex items-center transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 data-disabled:cursor-not-allowed data-disabled:opacity-50 cursor-pointer"
|
|
16
16
|
{...props}
|
|
17
17
|
>
|
|
18
18
|
<SwitchPrimitive.Thumb
|
|
@@ -47,10 +47,14 @@ interface TextProps
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
function Text({ as: Component = 'p', size, variant, weight, leading, font, ...props }: TextProps) {
|
|
50
|
+
const resolvedSize = size ?? 'base';
|
|
51
|
+
const isDefaultSize = size === undefined;
|
|
50
52
|
return (
|
|
51
53
|
<Component
|
|
52
54
|
data-slot="text"
|
|
53
|
-
|
|
55
|
+
data-size={resolvedSize}
|
|
56
|
+
data-default-size={isDefaultSize ? 'true' : undefined}
|
|
57
|
+
className={textVariants({ size: resolvedSize, variant, weight, leading, font })}
|
|
54
58
|
{...props}
|
|
55
59
|
/>
|
|
56
60
|
);
|
|
@@ -2,7 +2,7 @@ import { Toggle as TogglePrimitive } from '@base-ui/react/toggle';
|
|
|
2
2
|
import { cva, type VariantProps } from 'class-variance-authority';
|
|
3
3
|
|
|
4
4
|
const toggleVariants = cva(
|
|
5
|
-
"hover:text-foreground aria-pressed:bg-muted focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive gap-1 rounded-md text-sm font-medium transition-[color,box-shadow] [&_svg:not([class*='size-'])]:size-4 group/toggle hover:bg-muted inline-flex items-center justify-center whitespace-nowrap outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
5
|
+
"hover:text-foreground aria-pressed:bg-muted focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive gap-1 rounded-md text-sm font-medium transition-[color,box-shadow] [&_svg:not([class*='size-'])]:size-4 group/toggle hover:bg-muted inline-flex items-center justify-center whitespace-nowrap outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 cursor-pointer",
|
|
6
6
|
{
|
|
7
7
|
variants: {
|
|
8
8
|
variant: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { Accordion as AccordionPrimitive } from '@base-ui/react/accordion';
|
|
4
|
-
import {
|
|
4
|
+
import { ChevronDown, ChevronUp } from '@carbon/icons-react';
|
|
5
5
|
|
|
6
6
|
function Accordion({ ...props }: Omit<AccordionPrimitive.Root.Props, 'className'>) {
|
|
7
7
|
return (
|
|
@@ -27,11 +27,11 @@ function AccordionTrigger({
|
|
|
27
27
|
{...props}
|
|
28
28
|
>
|
|
29
29
|
{children}
|
|
30
|
-
<
|
|
30
|
+
<ChevronDown
|
|
31
31
|
data-slot="accordion-trigger-icon"
|
|
32
32
|
className="pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden"
|
|
33
33
|
/>
|
|
34
|
-
<
|
|
34
|
+
<ChevronUp
|
|
35
35
|
data-slot="accordion-trigger-icon"
|
|
36
36
|
className="pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline"
|
|
37
37
|
/>
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
4
|
+
import { Close, MagicWand, Send } from '@carbon/icons-react';
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
|
|
7
|
+
const aiChatTriggerVariants = cva(
|
|
8
|
+
'fixed bottom-6 right-6 z-50 flex items-center justify-center rounded-full transition-all duration-200 cursor-pointer',
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
size: {
|
|
12
|
+
default: 'size-14',
|
|
13
|
+
sm: 'size-12',
|
|
14
|
+
lg: 'size-16',
|
|
15
|
+
},
|
|
16
|
+
variant: {
|
|
17
|
+
default: 'bg-primary text-primary-foreground hover:bg-primary/90 active:scale-95',
|
|
18
|
+
secondary: 'bg-foreground text-background hover:bg-foreground/90 active:scale-95',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: {
|
|
22
|
+
size: 'default',
|
|
23
|
+
variant: 'default',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const aiChatPanelVariants = cva(
|
|
29
|
+
'fixed bottom-24 right-6 z-50 flex flex-col bg-background border border-border/40 rounded-2xl overflow-hidden transition-all duration-200 origin-bottom-right',
|
|
30
|
+
{
|
|
31
|
+
variants: {
|
|
32
|
+
size: {
|
|
33
|
+
default: 'w-96 h-[500px]',
|
|
34
|
+
sm: 'w-80 h-[400px]',
|
|
35
|
+
lg: 'w-[450px] h-[600px]',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
defaultVariants: {
|
|
39
|
+
size: 'default',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
interface AIChatProps extends VariantProps<typeof aiChatTriggerVariants> {
|
|
45
|
+
/** Whether the chat panel is open */
|
|
46
|
+
open?: boolean;
|
|
47
|
+
/** Default open state (uncontrolled) */
|
|
48
|
+
defaultOpen?: boolean;
|
|
49
|
+
/** Callback when open state changes */
|
|
50
|
+
onOpenChange?: (open: boolean) => void;
|
|
51
|
+
/** Custom trigger icon */
|
|
52
|
+
triggerIcon?: React.ReactNode;
|
|
53
|
+
/** Panel size */
|
|
54
|
+
panelSize?: 'sm' | 'default' | 'lg';
|
|
55
|
+
/** Content to render inside the chat panel */
|
|
56
|
+
children?: React.ReactNode;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function AIChat({
|
|
60
|
+
open: openProp,
|
|
61
|
+
defaultOpen = false,
|
|
62
|
+
onOpenChange,
|
|
63
|
+
triggerIcon,
|
|
64
|
+
size = 'default',
|
|
65
|
+
variant = 'default',
|
|
66
|
+
panelSize = 'default',
|
|
67
|
+
children,
|
|
68
|
+
}: AIChatProps) {
|
|
69
|
+
const [internalOpen, setInternalOpen] = React.useState(defaultOpen);
|
|
70
|
+
const isOpen = openProp ?? internalOpen;
|
|
71
|
+
|
|
72
|
+
const handleToggle = () => {
|
|
73
|
+
const newValue = !isOpen;
|
|
74
|
+
if (openProp === undefined) {
|
|
75
|
+
setInternalOpen(newValue);
|
|
76
|
+
}
|
|
77
|
+
onOpenChange?.(newValue);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<>
|
|
82
|
+
{/* Chat Panel */}
|
|
83
|
+
<div
|
|
84
|
+
data-slot="ai-chat-panel"
|
|
85
|
+
className={`${aiChatPanelVariants({ size: panelSize })} ${
|
|
86
|
+
isOpen ? 'scale-100 opacity-100' : 'scale-95 opacity-0 pointer-events-none'
|
|
87
|
+
}`}
|
|
88
|
+
style={{
|
|
89
|
+
boxShadow: '0 8px 32px -4px rgb(0 0 0 / 0.12), 0 4px 16px -2px rgb(0 0 0 / 0.08)',
|
|
90
|
+
}}
|
|
91
|
+
>
|
|
92
|
+
{children || <AIChatDefaultContent onClose={handleToggle} />}
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
{/* Floating Trigger Button */}
|
|
96
|
+
<button
|
|
97
|
+
type="button"
|
|
98
|
+
data-slot="ai-chat-trigger"
|
|
99
|
+
onClick={handleToggle}
|
|
100
|
+
className={aiChatTriggerVariants({ size, variant })}
|
|
101
|
+
style={{
|
|
102
|
+
boxShadow: '0 4px 16px -2px rgb(0 0 0 / 0.15), 0 2px 8px -2px rgb(0 0 0 / 0.1)',
|
|
103
|
+
}}
|
|
104
|
+
aria-label={isOpen ? 'Close chat' : 'Open chat'}
|
|
105
|
+
>
|
|
106
|
+
<span
|
|
107
|
+
className={`absolute transition-all duration-200 ${
|
|
108
|
+
isOpen ? 'scale-0 opacity-0 rotate-90' : 'scale-100 opacity-100 rotate-0'
|
|
109
|
+
}`}
|
|
110
|
+
>
|
|
111
|
+
{triggerIcon || <MagicWand className="size-6" />}
|
|
112
|
+
</span>
|
|
113
|
+
<span
|
|
114
|
+
className={`absolute transition-all duration-200 ${
|
|
115
|
+
isOpen ? 'scale-100 opacity-100 rotate-0' : 'scale-0 opacity-0 -rotate-90'
|
|
116
|
+
}`}
|
|
117
|
+
>
|
|
118
|
+
<Close className="size-6" />
|
|
119
|
+
</span>
|
|
120
|
+
</button>
|
|
121
|
+
</>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Default content when no children provided
|
|
126
|
+
function AIChatDefaultContent({ onClose }: { onClose: () => void }) {
|
|
127
|
+
return (
|
|
128
|
+
<>
|
|
129
|
+
{/* Header */}
|
|
130
|
+
<div className="flex items-center justify-between px-4 py-3 border-b border-border/40">
|
|
131
|
+
<div className="flex items-center gap-2">
|
|
132
|
+
<span className="flex size-8 items-center justify-center rounded-full bg-primary/10 text-primary">
|
|
133
|
+
<MagicWand className="size-4" />
|
|
134
|
+
</span>
|
|
135
|
+
<div>
|
|
136
|
+
<div className="font-semibold text-sm">AI Assistant</div>
|
|
137
|
+
<div className="text-xs text-muted-foreground">Ask me anything</div>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
<button
|
|
141
|
+
type="button"
|
|
142
|
+
onClick={onClose}
|
|
143
|
+
className="size-8 flex items-center justify-center rounded-md hover:bg-muted/50 text-muted-foreground hover:text-foreground transition-colors"
|
|
144
|
+
>
|
|
145
|
+
<Close className="size-4" />
|
|
146
|
+
</button>
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
{/* Messages Area */}
|
|
150
|
+
<div className="flex-1 overflow-auto p-4">
|
|
151
|
+
<div className="flex flex-col gap-4">
|
|
152
|
+
{/* AI Welcome Message */}
|
|
153
|
+
<div className="flex gap-3">
|
|
154
|
+
<span className="flex size-7 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary">
|
|
155
|
+
<MagicWand className="size-3.5" />
|
|
156
|
+
</span>
|
|
157
|
+
<div className="flex-1 rounded-2xl rounded-tl-sm bg-muted/50 dark:bg-muted px-3 py-2 text-sm">
|
|
158
|
+
Hi! I'm your AI assistant. How can I help you today?
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
{/* Input Area */}
|
|
165
|
+
<div className="p-3 border-t border-border/40">
|
|
166
|
+
<div className="flex items-center gap-2 rounded-xl bg-muted/50 dark:bg-muted px-3 py-2">
|
|
167
|
+
<input
|
|
168
|
+
type="text"
|
|
169
|
+
placeholder="Ask a question..."
|
|
170
|
+
className="flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
|
|
171
|
+
/>
|
|
172
|
+
<button
|
|
173
|
+
type="button"
|
|
174
|
+
className="flex size-8 items-center justify-center rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 transition-colors"
|
|
175
|
+
>
|
|
176
|
+
<Send className="size-4" />
|
|
177
|
+
</button>
|
|
178
|
+
</div>
|
|
179
|
+
<div className="mt-2 text-center text-xs text-muted-foreground">
|
|
180
|
+
Powered by AI
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
183
|
+
</>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Compound components for custom content
|
|
188
|
+
function AIChatHeader({ children, ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
|
|
189
|
+
return (
|
|
190
|
+
<div
|
|
191
|
+
data-slot="ai-chat-header"
|
|
192
|
+
className="flex items-center justify-between px-4 py-3 border-b border-border/40"
|
|
193
|
+
{...props}
|
|
194
|
+
>
|
|
195
|
+
{children}
|
|
196
|
+
</div>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function AIChatBody({ children, ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
|
|
201
|
+
return (
|
|
202
|
+
<div data-slot="ai-chat-body" className="flex-1 overflow-auto p-4" {...props}>
|
|
203
|
+
{children}
|
|
204
|
+
</div>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function AIChatFooter({ children, ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
|
|
209
|
+
return (
|
|
210
|
+
<div data-slot="ai-chat-footer" className="p-3 border-t border-border/40" {...props}>
|
|
211
|
+
{children}
|
|
212
|
+
</div>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export { AIChat, AIChatHeader, AIChatBody, AIChatFooter, aiChatTriggerVariants, aiChatPanelVariants };
|
|
217
|
+
export type { AIChatProps };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cva, type VariantProps } from 'class-variance-authority';
|
|
2
|
-
import {
|
|
2
|
+
import { CheckmarkFilled, Information, Misuse, Warning } from '@carbon/icons-react';
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
|
|
5
5
|
const alertVariants = cva(
|
|
@@ -25,10 +25,10 @@ const alertVariants = cva(
|
|
|
25
25
|
|
|
26
26
|
const variantIcons = {
|
|
27
27
|
default: null,
|
|
28
|
-
info:
|
|
29
|
-
success:
|
|
30
|
-
warning:
|
|
31
|
-
destructive:
|
|
28
|
+
info: Information,
|
|
29
|
+
success: CheckmarkFilled,
|
|
30
|
+
warning: Warning,
|
|
31
|
+
destructive: Misuse,
|
|
32
32
|
} as const;
|
|
33
33
|
|
|
34
34
|
type AlertVariant = NonNullable<VariantProps<typeof alertVariants>['variant']>;
|