sparkdesign 0.0.1 → 0.1.10
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 +188 -4
- package/dist/commands/add.js +93 -0
- package/dist/commands/diff.js +54 -0
- package/dist/commands/init.js +96 -0
- package/dist/commands/list.js +25 -0
- package/dist/index.js +37 -0
- package/dist/utils/config.js +53 -0
- package/dist/utils/registry.js +34 -0
- package/dist/utils/tokens.js +176 -0
- package/dist/utils/transform.js +19 -0
- package/package.json +33 -10
- package/registry/__tests__/basic/button.test.tsx +333 -0
- package/registry/__tests__/chat/markdown.test.tsx +387 -0
- package/registry/__tests__/chat/thinking-indicator.test.tsx +244 -0
- package/registry/__tests__/chat/tool-invocation-card.test.tsx +346 -0
- package/registry/basic/alert-dialog.tsx +180 -0
- package/registry/basic/avatar.tsx +120 -0
- package/registry/basic/button.tsx +100 -0
- package/registry/basic/collapse.tsx +94 -0
- package/registry/basic/collapsible-card.tsx +230 -0
- package/registry/basic/collapsible.tsx +21 -0
- package/registry/basic/dropdown-menu.tsx +254 -0
- package/registry/basic/icon-button.tsx +66 -0
- package/registry/basic/icons-inline.tsx +206 -0
- package/registry/basic/kbd.tsx +50 -0
- package/registry/basic/option-list.tsx +125 -0
- package/registry/basic/pagination.tsx +132 -0
- package/registry/basic/progress.tsx +42 -0
- package/registry/basic/radio-group.tsx +69 -0
- package/registry/basic/resizable.tsx +67 -0
- package/registry/basic/scrollbar.tsx +114 -0
- package/registry/basic/select.tsx +177 -0
- package/registry/basic/shimmering-text.tsx +115 -0
- package/registry/basic/sidebar-menu.tsx +177 -0
- package/registry/basic/skeleton.tsx +33 -0
- package/registry/basic/slider.tsx +55 -0
- package/registry/basic/sonner.tsx +104 -0
- package/registry/basic/spinner.tsx +17 -0
- package/registry/basic/switch.tsx +49 -0
- package/registry/basic/table.tsx +117 -0
- package/registry/basic/tabs.tsx +85 -0
- package/registry/basic/tag.tsx +161 -0
- package/registry/basic/theme-from-document.ts +10 -0
- package/registry/basic/toggle.tsx +223 -0
- package/registry/basic/tooltip.tsx +80 -0
- package/registry/basic/typography.tsx +201 -0
- package/registry/chat/ask-user-part.tsx +70 -0
- package/registry/chat/browser-action-part.tsx +166 -0
- package/registry/chat/chat-input/chat-input-folder-selector.tsx +185 -0
- package/registry/chat/chat-input/chat-input-model-switcher.tsx +131 -0
- package/registry/chat/chat-input/chat-input-textarea.tsx +67 -0
- package/registry/chat/chat-input/compound.tsx +334 -0
- package/registry/chat/chat-input/context.tsx +189 -0
- package/registry/chat/chat-input/folder-permission-dialog.tsx +61 -0
- package/registry/chat/chat-input/index.tsx +123 -0
- package/registry/chat/chat-input/types.ts +77 -0
- package/registry/chat/chat-input/useAutoResizeTextarea.ts +20 -0
- package/registry/chat/code-block-part.tsx +151 -0
- package/registry/chat/file-attachment.tsx +44 -0
- package/registry/chat/file-card.tsx +68 -0
- package/registry/chat/file-review-part.tsx +259 -0
- package/registry/chat/folder-button.tsx +169 -0
- package/registry/chat/generated-images-grid.tsx +56 -0
- package/registry/chat/generation-status-bar.tsx +72 -0
- package/registry/chat/hint-banner.tsx +165 -0
- package/registry/chat/image-attachment.tsx +166 -0
- package/registry/chat/image-generating.tsx +281 -0
- package/registry/chat/markdown.tsx +146 -0
- package/registry/chat/mermaid-part.tsx +90 -0
- package/registry/chat/permission-card.tsx +178 -0
- package/registry/chat/plan-part.tsx +168 -0
- package/registry/chat/queue-indicator.tsx +234 -0
- package/registry/chat/reasoning-step/compound.tsx +336 -0
- package/registry/chat/reasoning-step/context.tsx +114 -0
- package/registry/chat/reasoning-step/index.tsx +45 -0
- package/registry/chat/reasoning-step/types.ts +109 -0
- package/registry/chat/related-prompts.tsx +91 -0
- package/registry/chat/response/compound.tsx +210 -0
- package/registry/chat/response/context.tsx +200 -0
- package/registry/chat/response/index.tsx +87 -0
- package/registry/chat/response/types.ts +123 -0
- package/registry/chat/send-button.tsx +94 -0
- package/registry/chat/streaming-markdown-block.tsx +111 -0
- package/registry/chat/task-part.tsx +109 -0
- package/registry/chat/terminal-code-block-part.tsx +69 -0
- package/registry/chat/thinking-indicator.tsx +91 -0
- package/registry/chat/tool-invocation-card.tsx +132 -0
- package/registry/chat/user-message.tsx +38 -0
- package/registry/chat/user-question/UserQuestionCard.tsx +198 -0
- package/registry/chat/user-question/UserQuestionFooter.tsx +66 -0
- package/registry/chat/user-question/UserQuestionHeader.tsx +64 -0
- package/registry/chat/user-question/compound.tsx +324 -0
- package/registry/chat/user-question/context.tsx +456 -0
- package/registry/chat/user-question/index.tsx +95 -0
- package/registry/chat/user-question/types.ts +61 -0
- package/registry/chat/user-question/useUserQuestionKeyboard.ts +126 -0
- package/registry/chat/user-question/useUserQuestionState.ts +165 -0
- package/registry/chat/user-question-answer.tsx +62 -0
- package/registry/lib/file-icon-maps.ts +150 -0
- package/registry/lib/use-mermaid-render.ts +76 -0
- package/registry/lib/utils.ts +6 -0
- package/registry/meta.json +1 -0
- package/registry/tokens/index.css +31 -0
- package/registry/tokens/scale/computed.css +103 -0
- package/registry/tokens/scale/config.css +110 -0
- package/registry/tokens/scale/index.css +30 -0
- package/registry/tokens/scale/presets/compact.css +30 -0
- package/registry/tokens/scale/presets/dense.css +64 -0
- package/registry/tokens/scale/presets/sharp.css +40 -0
- package/registry/tokens/scale/presets/soft.css +16 -0
- package/registry/tokens/scale.css +13 -0
- package/registry/tokens/scrollbar-utility.css +35 -0
- package/registry/tokens/theme.css +633 -0
- package/registry/tokens/themes/dark-parchment.css +132 -0
- package/registry/tokens/themes/dark-qoder.css +132 -0
- package/registry/tokens/themes/light-parchment.css +123 -0
- package/registry/tokens/themes/light-qoder.css +131 -0
- package/index.js +0 -5
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import * as SwitchPrimitives from '@radix-ui/react-switch'
|
|
3
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
4
|
+
import { cn } from '@/lib/utils'
|
|
5
|
+
|
|
6
|
+
const rootVariants = cva(
|
|
7
|
+
'peer inline-flex shrink-0 cursor-pointer items-center rounded-full overflow-hidden border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-border focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-fill-secondary',
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
size: { md: 'h-5 w-9', lg: 'h-6 w-11' },
|
|
11
|
+
},
|
|
12
|
+
defaultVariants: { size: 'md' },
|
|
13
|
+
}
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
const thumbVariants = cva(
|
|
17
|
+
'pointer-events-none block rounded-full bg-text-on-primary ring-0 transition-transform data-[state=unchecked]:translate-x-0',
|
|
18
|
+
{
|
|
19
|
+
variants: {
|
|
20
|
+
size: {
|
|
21
|
+
md: 'h-4 w-4 data-[state=checked]:translate-x-4',
|
|
22
|
+
lg: 'h-5 w-5 data-[state=checked]:translate-x-4.5',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
defaultVariants: { size: 'md' },
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
export interface SwitchProps
|
|
30
|
+
extends Omit<React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>, 'onChange'>,
|
|
31
|
+
VariantProps<typeof rootVariants> {
|
|
32
|
+
size?: 'md' | 'lg'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const Switch = React.forwardRef<HTMLButtonElement, SwitchProps>(
|
|
36
|
+
({ className, size = 'md', disabled = false, ...rest }, ref) => (
|
|
37
|
+
<SwitchPrimitives.Root
|
|
38
|
+
ref={ref}
|
|
39
|
+
className={cn(rootVariants({ size }), className)}
|
|
40
|
+
disabled={disabled}
|
|
41
|
+
{...rest}
|
|
42
|
+
>
|
|
43
|
+
<SwitchPrimitives.Thumb className={thumbVariants({ size })} />
|
|
44
|
+
</SwitchPrimitives.Root>
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
Switch.displayName = 'Switch'
|
|
48
|
+
|
|
49
|
+
export { Switch }
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { forwardRef } from 'react'
|
|
2
|
+
import type { HTMLAttributes, TdHTMLAttributes, ThHTMLAttributes } from 'react'
|
|
3
|
+
import { cn } from '@/lib/utils'
|
|
4
|
+
|
|
5
|
+
const Table = forwardRef<HTMLTableElement, HTMLAttributes<HTMLTableElement>>(
|
|
6
|
+
({ className, ...props }, ref) => (
|
|
7
|
+
<div className="relative w-full overflow-auto scrollbar-auto">
|
|
8
|
+
<table ref={ref} className={cn('w-full caption-bottom text-sm', className)} {...props} />
|
|
9
|
+
</div>
|
|
10
|
+
)
|
|
11
|
+
)
|
|
12
|
+
Table.displayName = 'Table'
|
|
13
|
+
|
|
14
|
+
const TableHeader = forwardRef<
|
|
15
|
+
HTMLTableSectionElement,
|
|
16
|
+
HTMLAttributes<HTMLTableSectionElement>
|
|
17
|
+
>(({ className, ...props }, ref) => (
|
|
18
|
+
<thead
|
|
19
|
+
ref={ref}
|
|
20
|
+
className={cn('[&_tr]:border-b border-border-tertiary', className)}
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
))
|
|
24
|
+
TableHeader.displayName = 'TableHeader'
|
|
25
|
+
|
|
26
|
+
const TableBody = forwardRef<
|
|
27
|
+
HTMLTableSectionElement,
|
|
28
|
+
HTMLAttributes<HTMLTableSectionElement>
|
|
29
|
+
>(({ className, ...props }, ref) => (
|
|
30
|
+
<tbody ref={ref} className={cn('[&_tr:last-child]:border-0', className)} {...props} />
|
|
31
|
+
))
|
|
32
|
+
TableBody.displayName = 'TableBody'
|
|
33
|
+
|
|
34
|
+
const TableFooter = forwardRef<
|
|
35
|
+
HTMLTableSectionElement,
|
|
36
|
+
HTMLAttributes<HTMLTableSectionElement>
|
|
37
|
+
>(({ className, ...props }, ref) => (
|
|
38
|
+
<tfoot
|
|
39
|
+
ref={ref}
|
|
40
|
+
className={cn(
|
|
41
|
+
'border-t border-border-tertiary bg-fill-tertiary font-medium [&>tr]:last:border-b-0',
|
|
42
|
+
className
|
|
43
|
+
)}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
))
|
|
47
|
+
TableFooter.displayName = 'TableFooter'
|
|
48
|
+
|
|
49
|
+
const TableRow = forwardRef<
|
|
50
|
+
HTMLTableRowElement,
|
|
51
|
+
HTMLAttributes<HTMLTableRowElement>
|
|
52
|
+
>(({ className, ...props }, ref) => (
|
|
53
|
+
<tr
|
|
54
|
+
ref={ref}
|
|
55
|
+
className={cn(
|
|
56
|
+
'border-b border-border-tertiary transition-colors hover:bg-fill-tertiary data-[state=selected]:bg-fill-tertiary',
|
|
57
|
+
className
|
|
58
|
+
)}
|
|
59
|
+
{...props}
|
|
60
|
+
/>
|
|
61
|
+
))
|
|
62
|
+
TableRow.displayName = 'TableRow'
|
|
63
|
+
|
|
64
|
+
const TableHead = forwardRef<
|
|
65
|
+
HTMLTableCellElement,
|
|
66
|
+
ThHTMLAttributes<HTMLTableCellElement>
|
|
67
|
+
>(({ className, ...props }, ref) => (
|
|
68
|
+
<th
|
|
69
|
+
ref={ref}
|
|
70
|
+
className={cn(
|
|
71
|
+
'h-10 px-4 text-left align-middle font-medium text-text-secondary [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-0.5',
|
|
72
|
+
className
|
|
73
|
+
)}
|
|
74
|
+
{...props}
|
|
75
|
+
/>
|
|
76
|
+
))
|
|
77
|
+
TableHead.displayName = 'TableHead'
|
|
78
|
+
|
|
79
|
+
const TableCell = forwardRef<
|
|
80
|
+
HTMLTableCellElement,
|
|
81
|
+
TdHTMLAttributes<HTMLTableCellElement>
|
|
82
|
+
>(({ className, ...props }, ref) => (
|
|
83
|
+
<td
|
|
84
|
+
ref={ref}
|
|
85
|
+
className={cn('p-4 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-0.5', className)}
|
|
86
|
+
{...props}
|
|
87
|
+
/>
|
|
88
|
+
))
|
|
89
|
+
TableCell.displayName = 'TableCell'
|
|
90
|
+
|
|
91
|
+
const TableCaption = forwardRef<
|
|
92
|
+
HTMLTableCaptionElement,
|
|
93
|
+
HTMLAttributes<HTMLTableCaptionElement>
|
|
94
|
+
>(({ className, ...props }, ref) => (
|
|
95
|
+
<caption ref={ref} className={cn('mt-4 text-sm text-text-secondary', className)} {...props} />
|
|
96
|
+
))
|
|
97
|
+
TableCaption.displayName = 'TableCaption'
|
|
98
|
+
|
|
99
|
+
export type TableProps = HTMLAttributes<HTMLTableElement>
|
|
100
|
+
export type TableHeaderProps = HTMLAttributes<HTMLTableSectionElement>
|
|
101
|
+
export type TableBodyProps = HTMLAttributes<HTMLTableSectionElement>
|
|
102
|
+
export type TableFooterProps = HTMLAttributes<HTMLTableSectionElement>
|
|
103
|
+
export type TableRowProps = HTMLAttributes<HTMLTableRowElement>
|
|
104
|
+
export type TableHeadProps = ThHTMLAttributes<HTMLTableCellElement>
|
|
105
|
+
export type TableCellProps = TdHTMLAttributes<HTMLTableCellElement>
|
|
106
|
+
export type TableCaptionProps = HTMLAttributes<HTMLTableCaptionElement>
|
|
107
|
+
|
|
108
|
+
export {
|
|
109
|
+
Table,
|
|
110
|
+
TableHeader,
|
|
111
|
+
TableBody,
|
|
112
|
+
TableFooter,
|
|
113
|
+
TableHead,
|
|
114
|
+
TableRow,
|
|
115
|
+
TableCell,
|
|
116
|
+
TableCaption,
|
|
117
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import * as TabsPrimitive from '@radix-ui/react-tabs'
|
|
3
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
4
|
+
import { cn } from '@/lib/utils'
|
|
5
|
+
|
|
6
|
+
const Tabs = TabsPrimitive.Root
|
|
7
|
+
|
|
8
|
+
const tabsListVariants = cva(
|
|
9
|
+
'inline-flex items-center justify-center text-text-secondary data-[orientation=horizontal]:flex-row data-[orientation=vertical]:flex-col data-[orientation=vertical]:items-stretch',
|
|
10
|
+
{
|
|
11
|
+
variants: {
|
|
12
|
+
variant: {
|
|
13
|
+
default: 'rounded-lg bg-fill-secondary p-0.5 gap-0.5',
|
|
14
|
+
line: 'gap-6 border-b border-border-tertiary bg-transparent p-0',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
defaultVariants: { variant: 'default' },
|
|
18
|
+
}
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
const tabsTriggerVariants = cva(
|
|
22
|
+
'inline-flex items-center justify-center gap-1.5 whitespace-nowrap text-sm font-medium transition-colors outline-none focus-visible:ring-2 focus-visible:ring-primary-border focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base disabled:pointer-events-none disabled:opacity-50 data-[state=active]:text-text cursor-pointer data-[orientation=vertical]:justify-start data-[orientation=vertical]:rounded-md data-[orientation=vertical]:px-3 data-[orientation=vertical]:py-2',
|
|
23
|
+
{
|
|
24
|
+
variants: {
|
|
25
|
+
variant: {
|
|
26
|
+
default: 'rounded-md px-3 py-1.5 data-[state=active]:bg-bg-container data-[state=active]:shadow-sm',
|
|
27
|
+
line: 'rounded-none border-b-2 border-transparent px-1 pb-2 pt-0 data-[state=active]:border-primary data-[state=active]:shadow-none',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
defaultVariants: { variant: 'default' },
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
export interface TabsListProps
|
|
35
|
+
extends React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>,
|
|
36
|
+
VariantProps<typeof tabsListVariants> {}
|
|
37
|
+
|
|
38
|
+
export interface TabsTriggerProps
|
|
39
|
+
extends React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>,
|
|
40
|
+
VariantProps<typeof tabsTriggerVariants> {}
|
|
41
|
+
|
|
42
|
+
const TabsList = React.forwardRef<
|
|
43
|
+
React.ElementRef<typeof TabsPrimitive.List>,
|
|
44
|
+
TabsListProps
|
|
45
|
+
>(({ className, variant = 'default', ...props }, ref) => (
|
|
46
|
+
<TabsPrimitive.List
|
|
47
|
+
ref={ref}
|
|
48
|
+
className={cn(tabsListVariants({ variant }), className)}
|
|
49
|
+
{...props}
|
|
50
|
+
/>
|
|
51
|
+
))
|
|
52
|
+
TabsList.displayName = TabsPrimitive.List.displayName
|
|
53
|
+
|
|
54
|
+
const TabsTrigger = React.forwardRef<
|
|
55
|
+
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
|
56
|
+
TabsTriggerProps
|
|
57
|
+
>(({ className, variant = 'default', ...props }, ref) => (
|
|
58
|
+
<TabsPrimitive.Trigger
|
|
59
|
+
ref={ref}
|
|
60
|
+
className={cn(tabsTriggerVariants({ variant }), className)}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
))
|
|
64
|
+
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
|
|
65
|
+
|
|
66
|
+
const TabsContent = React.forwardRef<
|
|
67
|
+
React.ElementRef<typeof TabsPrimitive.Content>,
|
|
68
|
+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
|
69
|
+
>(({ className, ...props }, ref) => (
|
|
70
|
+
<TabsPrimitive.Content
|
|
71
|
+
ref={ref}
|
|
72
|
+
className={cn(
|
|
73
|
+
'mt-2 outline-none ring-0 data-[state=inactive]:hidden',
|
|
74
|
+
'data-[orientation=vertical]:mt-0 data-[orientation=vertical]:ml-2',
|
|
75
|
+
className
|
|
76
|
+
)}
|
|
77
|
+
{...props}
|
|
78
|
+
/>
|
|
79
|
+
))
|
|
80
|
+
TabsContent.displayName = TabsPrimitive.Content.displayName
|
|
81
|
+
|
|
82
|
+
export type TabsContentProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
|
83
|
+
export type TabsProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.Root>
|
|
84
|
+
|
|
85
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants, tabsTriggerVariants }
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { motion } from 'framer-motion'
|
|
3
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
4
|
+
import { cn } from '@/lib/utils'
|
|
5
|
+
import { CloseLargeLine } from './icons-inline'
|
|
6
|
+
|
|
7
|
+
const tagVariants = cva(
|
|
8
|
+
'inline-flex items-center justify-center gap-1.5 rounded-md border px-2.5 py-0.5 text-xs font-medium transition-colors whitespace-nowrap cursor-pointer group',
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
appearance: { filled: '', outline: 'bg-transparent' },
|
|
12
|
+
color: {
|
|
13
|
+
primary: '',
|
|
14
|
+
success: '',
|
|
15
|
+
error: '',
|
|
16
|
+
warning: '',
|
|
17
|
+
info: '',
|
|
18
|
+
link: '',
|
|
19
|
+
pink: '',
|
|
20
|
+
purple: '',
|
|
21
|
+
yellow: '',
|
|
22
|
+
orange: '',
|
|
23
|
+
teal: '',
|
|
24
|
+
blue: '',
|
|
25
|
+
mauve: '',
|
|
26
|
+
slate: '',
|
|
27
|
+
lavender: '',
|
|
28
|
+
sage: '',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
compoundVariants: [
|
|
32
|
+
{ appearance: 'filled', color: 'primary', className: 'border-transparent bg-primary text-text-on-primary hover:bg-primary-hover' },
|
|
33
|
+
{ appearance: 'filled', color: 'success', className: 'border-transparent bg-success-bg text-success border-success-border hover:bg-success-bg-hover' },
|
|
34
|
+
{ appearance: 'filled', color: 'error', className: 'border-transparent bg-error-bg text-error border-error-border hover:bg-error-bg-hover' },
|
|
35
|
+
{ appearance: 'filled', color: 'warning', className: 'border-transparent bg-warning-bg text-warning border-warning-border hover:bg-warning-bg-hover' },
|
|
36
|
+
{ appearance: 'filled', color: 'info', className: 'border-transparent bg-info-bg text-info border-info-border hover:bg-info-bg-hover' },
|
|
37
|
+
{ appearance: 'filled', color: 'link', className: 'border-transparent bg-transparent text-link underline-offset-4 hover:underline hover:text-link' },
|
|
38
|
+
{ appearance: 'filled', color: 'pink', className: 'border-transparent bg-pink-bg text-pink hover:bg-pink-bg-hover' },
|
|
39
|
+
{ appearance: 'filled', color: 'purple', className: 'border-transparent bg-purple-bg text-purple hover:bg-purple-bg-hover' },
|
|
40
|
+
{ appearance: 'filled', color: 'yellow', className: 'border-transparent bg-yellow-bg text-yellow hover:bg-yellow-bg-hover' },
|
|
41
|
+
{ appearance: 'filled', color: 'orange', className: 'border-transparent bg-orange-bg text-orange hover:bg-orange-bg-hover' },
|
|
42
|
+
{ appearance: 'filled', color: 'teal', className: 'border-transparent bg-teal-bg text-teal hover:bg-teal-bg-hover' },
|
|
43
|
+
{ appearance: 'filled', color: 'blue', className: 'border-transparent bg-blue-bg text-blue hover:bg-blue-bg-hover' },
|
|
44
|
+
{ appearance: 'filled', color: 'mauve', className: 'border-transparent bg-mauve-bg text-mauve hover:bg-mauve-bg-hover' },
|
|
45
|
+
{ appearance: 'filled', color: 'slate', className: 'border-transparent bg-slate-bg text-slate hover:bg-slate-bg-hover' },
|
|
46
|
+
{ appearance: 'filled', color: 'lavender', className: 'border-transparent bg-lavender-bg text-lavender hover:bg-lavender-bg-hover' },
|
|
47
|
+
{ appearance: 'filled', color: 'sage', className: 'border-transparent bg-sage-bg text-sage hover:bg-sage-bg-hover' },
|
|
48
|
+
{ appearance: 'outline', color: 'primary', className: 'border-primary-border text-primary hover:bg-primary-bg' },
|
|
49
|
+
{ appearance: 'outline', color: 'success', className: 'border-success-border text-success hover:bg-success-bg' },
|
|
50
|
+
{ appearance: 'outline', color: 'error', className: 'border-error-border text-error hover:bg-error-bg' },
|
|
51
|
+
{ appearance: 'outline', color: 'warning', className: 'border-warning-border text-warning hover:bg-warning-bg' },
|
|
52
|
+
{ appearance: 'outline', color: 'info', className: 'border-info-border text-info hover:bg-info-bg' },
|
|
53
|
+
{ appearance: 'outline', color: 'link', className: 'border-transparent text-link underline-offset-4 hover:underline hover:text-link' },
|
|
54
|
+
{ appearance: 'outline', color: 'pink', className: 'border-pink text-pink hover:bg-pink-bg' },
|
|
55
|
+
{ appearance: 'outline', color: 'purple', className: 'border-purple text-purple hover:bg-purple-bg' },
|
|
56
|
+
{ appearance: 'outline', color: 'yellow', className: 'border-yellow text-yellow hover:bg-yellow-bg' },
|
|
57
|
+
{ appearance: 'outline', color: 'orange', className: 'border-orange text-orange hover:bg-orange-bg' },
|
|
58
|
+
{ appearance: 'outline', color: 'teal', className: 'border-teal text-teal hover:bg-teal-bg' },
|
|
59
|
+
{ appearance: 'outline', color: 'blue', className: 'border-blue text-blue hover:bg-blue-bg' },
|
|
60
|
+
{ appearance: 'outline', color: 'mauve', className: 'border-mauve text-mauve hover:bg-mauve-bg' },
|
|
61
|
+
{ appearance: 'outline', color: 'slate', className: 'border-slate text-slate hover:bg-slate-bg' },
|
|
62
|
+
{ appearance: 'outline', color: 'lavender', className: 'border-lavender text-lavender hover:bg-lavender-bg' },
|
|
63
|
+
{ appearance: 'outline', color: 'sage', className: 'border-sage text-sage hover:bg-sage-bg' },
|
|
64
|
+
],
|
|
65
|
+
defaultVariants: { appearance: 'filled', color: 'primary' },
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
export interface TagProps
|
|
70
|
+
extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'color'>,
|
|
71
|
+
VariantProps<typeof tagVariants> {
|
|
72
|
+
closable?: boolean
|
|
73
|
+
onClose?: (event: React.MouseEvent<HTMLButtonElement> | React.KeyboardEvent<HTMLButtonElement>) => void
|
|
74
|
+
closeAriaLabel?: string
|
|
75
|
+
closeIcon?: React.ReactNode
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const Tag = React.forwardRef<HTMLSpanElement, TagProps>(
|
|
79
|
+
(
|
|
80
|
+
{
|
|
81
|
+
className,
|
|
82
|
+
appearance = 'filled',
|
|
83
|
+
color = 'primary',
|
|
84
|
+
closable = false,
|
|
85
|
+
onClose,
|
|
86
|
+
closeAriaLabel = 'Close tag',
|
|
87
|
+
closeIcon,
|
|
88
|
+
children,
|
|
89
|
+
onMouseEnter: onMouseEnterProp,
|
|
90
|
+
onMouseLeave: onMouseLeaveProp,
|
|
91
|
+
...props
|
|
92
|
+
},
|
|
93
|
+
ref
|
|
94
|
+
) => {
|
|
95
|
+
const [isHovered, setIsHovered] = React.useState(false)
|
|
96
|
+
const showClose = closable && typeof onClose === 'function'
|
|
97
|
+
|
|
98
|
+
const handleMouseEnter = (e: React.MouseEvent<HTMLSpanElement>) => {
|
|
99
|
+
if (showClose) setIsHovered(true)
|
|
100
|
+
onMouseEnterProp?.(e)
|
|
101
|
+
}
|
|
102
|
+
const handleMouseLeave = (e: React.MouseEvent<HTMLSpanElement>) => {
|
|
103
|
+
if (showClose) setIsHovered(false)
|
|
104
|
+
onMouseLeaveProp?.(e)
|
|
105
|
+
}
|
|
106
|
+
const handleCloseClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
107
|
+
e.preventDefault()
|
|
108
|
+
e.stopPropagation()
|
|
109
|
+
onClose?.(e)
|
|
110
|
+
}
|
|
111
|
+
const handleCloseKeyDown = (e: React.KeyboardEvent<HTMLButtonElement>) => {
|
|
112
|
+
if (e.key !== 'Enter' && e.key !== ' ') return
|
|
113
|
+
e.preventDefault()
|
|
114
|
+
e.stopPropagation()
|
|
115
|
+
onClose?.(e)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<span
|
|
120
|
+
ref={ref}
|
|
121
|
+
className={cn(
|
|
122
|
+
tagVariants({ appearance, color }),
|
|
123
|
+
showClose && 'gap-0 pr-1.5',
|
|
124
|
+
className
|
|
125
|
+
)}
|
|
126
|
+
onMouseEnter={handleMouseEnter}
|
|
127
|
+
onMouseLeave={handleMouseLeave}
|
|
128
|
+
{...props}
|
|
129
|
+
>
|
|
130
|
+
{children}
|
|
131
|
+
{showClose && (
|
|
132
|
+
<motion.div
|
|
133
|
+
aria-hidden
|
|
134
|
+
initial={false}
|
|
135
|
+
animate={{
|
|
136
|
+
width: isHovered ? 16 : 0,
|
|
137
|
+
marginLeft: isHovered ? 2 : 0,
|
|
138
|
+
opacity: isHovered ? 1 : 0,
|
|
139
|
+
}}
|
|
140
|
+
transition={{ duration: 0.2, ease: [0.4, 0, 0.2, 1] }}
|
|
141
|
+
className="flex min-w-0 shrink-0 overflow-hidden"
|
|
142
|
+
style={{ pointerEvents: isHovered ? 'auto' : 'none' }}
|
|
143
|
+
>
|
|
144
|
+
<button
|
|
145
|
+
type="button"
|
|
146
|
+
aria-label={closeAriaLabel}
|
|
147
|
+
className="inline-flex h-4 w-4 cursor-pointer items-center justify-center rounded-sm text-current/80 hover:text-current focus:outline-none"
|
|
148
|
+
onClick={handleCloseClick}
|
|
149
|
+
onKeyDown={handleCloseKeyDown}
|
|
150
|
+
>
|
|
151
|
+
{closeIcon ?? <CloseLargeLine className="h-3 w-3" />}
|
|
152
|
+
</button>
|
|
153
|
+
</motion.div>
|
|
154
|
+
)}
|
|
155
|
+
</span>
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
)
|
|
159
|
+
Tag.displayName = 'Tag'
|
|
160
|
+
|
|
161
|
+
export { Tag, tagVariants }
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function getThemeFromDocument(): Record<string, string> {
|
|
2
|
+
if (typeof document === 'undefined') return {}
|
|
3
|
+
const el = document.documentElement
|
|
4
|
+
const out: Record<string, string> = {}
|
|
5
|
+
const theme = el.getAttribute('data-theme')
|
|
6
|
+
const style = el.getAttribute('data-style')
|
|
7
|
+
if (theme) out['data-theme'] = theme
|
|
8
|
+
if (style) out['data-style'] = style
|
|
9
|
+
return out
|
|
10
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { createContext, forwardRef, useContext } from 'react'
|
|
2
|
+
import type { ComponentPropsWithoutRef, ReactNode } from 'react'
|
|
3
|
+
import * as TogglePrimitive from '@radix-ui/react-toggle'
|
|
4
|
+
import * as ToggleGroupPrimitive from '@radix-ui/react-toggle-group'
|
|
5
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
6
|
+
import { cn } from '@/lib/utils'
|
|
7
|
+
|
|
8
|
+
const toggleVariants = cva(
|
|
9
|
+
'inline-flex items-center justify-center gap-1.5 font-medium transition-colors duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-border focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base disabled:pointer-events-none disabled:opacity-50 cursor-pointer data-[state=on]:bg-fill data-[state=on]:text-text',
|
|
10
|
+
{
|
|
11
|
+
variants: {
|
|
12
|
+
variant: {
|
|
13
|
+
default:
|
|
14
|
+
'bg-transparent text-text-secondary hover:bg-fill-secondary hover:text-text data-[state=on]:bg-fill data-[state=on]:text-text',
|
|
15
|
+
outline:
|
|
16
|
+
'border border-border-tertiary bg-transparent text-text-secondary hover:bg-fill-secondary hover:text-text data-[state=on]:border-border-secondary data-[state=on]:bg-fill data-[state=on]:text-text',
|
|
17
|
+
},
|
|
18
|
+
size: {
|
|
19
|
+
sm: 'h-7 px-2 text-xs',
|
|
20
|
+
md: 'h-9 px-3 text-sm',
|
|
21
|
+
lg: 'h-11 px-4 text-base',
|
|
22
|
+
},
|
|
23
|
+
rounded: {
|
|
24
|
+
square: 'rounded',
|
|
25
|
+
pill: 'rounded-full',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
defaultVariants: { variant: 'default', size: 'md', rounded: 'square' },
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
export interface ToggleProps
|
|
33
|
+
extends Omit<React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root>, 'asChild'>,
|
|
34
|
+
VariantProps<typeof toggleVariants> {
|
|
35
|
+
children?: ReactNode
|
|
36
|
+
prefixIcon?: ReactNode
|
|
37
|
+
suffixIcon?: ReactNode
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const Toggle = forwardRef<
|
|
41
|
+
React.ElementRef<typeof TogglePrimitive.Root>,
|
|
42
|
+
ToggleProps
|
|
43
|
+
>(
|
|
44
|
+
(
|
|
45
|
+
{
|
|
46
|
+
variant = 'default',
|
|
47
|
+
size = 'md',
|
|
48
|
+
rounded = 'square',
|
|
49
|
+
className,
|
|
50
|
+
prefixIcon,
|
|
51
|
+
suffixIcon,
|
|
52
|
+
children,
|
|
53
|
+
...props
|
|
54
|
+
},
|
|
55
|
+
ref
|
|
56
|
+
) => (
|
|
57
|
+
<TogglePrimitive.Root
|
|
58
|
+
ref={ref}
|
|
59
|
+
className={toggleVariants({ variant, size, rounded, className })}
|
|
60
|
+
{...props}
|
|
61
|
+
>
|
|
62
|
+
{prefixIcon && (
|
|
63
|
+
<span className="inline-flex shrink-0 items-center justify-center [&>*]:block [&>*]:leading-none">
|
|
64
|
+
{prefixIcon}
|
|
65
|
+
</span>
|
|
66
|
+
)}
|
|
67
|
+
{children}
|
|
68
|
+
{suffixIcon && (
|
|
69
|
+
<span className="inline-flex shrink-0 items-center justify-center [&>*]:block [&>*]:leading-none">
|
|
70
|
+
{suffixIcon}
|
|
71
|
+
</span>
|
|
72
|
+
)}
|
|
73
|
+
</TogglePrimitive.Root>
|
|
74
|
+
)
|
|
75
|
+
)
|
|
76
|
+
Toggle.displayName = 'Toggle'
|
|
77
|
+
|
|
78
|
+
type ToggleGroupContextValue = VariantProps<typeof toggleVariants> & {
|
|
79
|
+
spacing?: 'none' | 'sm' | 'md'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const ToggleGroupContext = createContext<ToggleGroupContextValue | null>(null)
|
|
83
|
+
const spacingClasses = { none: 'gap-0', sm: 'gap-1', md: 'gap-2' } as const
|
|
84
|
+
|
|
85
|
+
const toggleGroupItemGroupClasses = [
|
|
86
|
+
'shrink-0',
|
|
87
|
+
'group-data-[spacing=0]/toggle-group:rounded-none',
|
|
88
|
+
'group-data-[spacing=0]/toggle-group:group-data-[orientation=horizontal]/toggle-group:first:rounded-l',
|
|
89
|
+
'group-data-[spacing=0]/toggle-group:group-data-[orientation=horizontal]/toggle-group:last:rounded-r',
|
|
90
|
+
'group-data-[spacing=0]/toggle-group:group-data-[orientation=vertical]/toggle-group:first:rounded-t',
|
|
91
|
+
'group-data-[spacing=0]/toggle-group:group-data-[orientation=vertical]/toggle-group:last:rounded-b',
|
|
92
|
+
'group-data-[spacing=0]/toggle-group:data-[variant=outline]:group-data-[orientation=horizontal]/toggle-group:[&:not(:first-child)]:border-l-0',
|
|
93
|
+
'group-data-[spacing=0]/toggle-group:data-[variant=outline]:group-data-[orientation=vertical]/toggle-group:[&:not(:first-child)]:border-t-0',
|
|
94
|
+
].join(' ')
|
|
95
|
+
|
|
96
|
+
type ToggleGroupRootProps = React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root>
|
|
97
|
+
|
|
98
|
+
export type ToggleGroupProps = ToggleGroupRootProps &
|
|
99
|
+
VariantProps<typeof toggleVariants> & {
|
|
100
|
+
spacing?: 'none' | 'sm' | 'md'
|
|
101
|
+
children?: ReactNode
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const ToggleGroup = forwardRef<
|
|
105
|
+
React.ElementRef<typeof ToggleGroupPrimitive.Root>,
|
|
106
|
+
ToggleGroupProps
|
|
107
|
+
>(
|
|
108
|
+
(
|
|
109
|
+
{
|
|
110
|
+
type = 'single',
|
|
111
|
+
variant = 'default',
|
|
112
|
+
size = 'md',
|
|
113
|
+
rounded = 'square',
|
|
114
|
+
spacing = 'none',
|
|
115
|
+
className,
|
|
116
|
+
orientation = 'horizontal',
|
|
117
|
+
children,
|
|
118
|
+
...props
|
|
119
|
+
},
|
|
120
|
+
ref
|
|
121
|
+
) => {
|
|
122
|
+
const rootClassName = cn(
|
|
123
|
+
'group/toggle-group inline-flex w-fit',
|
|
124
|
+
orientation === 'horizontal' ? 'flex-row' : 'flex-col',
|
|
125
|
+
orientation === 'vertical' && 'items-stretch',
|
|
126
|
+
spacingClasses[spacing],
|
|
127
|
+
className
|
|
128
|
+
)
|
|
129
|
+
const commonProps = {
|
|
130
|
+
ref,
|
|
131
|
+
orientation,
|
|
132
|
+
'data-spacing': spacing === 'none' ? '0' : spacing,
|
|
133
|
+
'data-orientation': orientation,
|
|
134
|
+
'data-variant': variant,
|
|
135
|
+
'data-size': size,
|
|
136
|
+
className: rootClassName,
|
|
137
|
+
} as const
|
|
138
|
+
return (
|
|
139
|
+
<ToggleGroupContext.Provider value={{ variant, size, rounded, spacing }}>
|
|
140
|
+
{type === 'multiple' ? (
|
|
141
|
+
<ToggleGroupPrimitive.Root
|
|
142
|
+
{...commonProps}
|
|
143
|
+
{...(props as ToggleGroupPrimitive.ToggleGroupMultipleProps)}
|
|
144
|
+
type="multiple"
|
|
145
|
+
>
|
|
146
|
+
{children}
|
|
147
|
+
</ToggleGroupPrimitive.Root>
|
|
148
|
+
) : (
|
|
149
|
+
<ToggleGroupPrimitive.Root
|
|
150
|
+
{...commonProps}
|
|
151
|
+
{...(props as ToggleGroupPrimitive.ToggleGroupSingleProps)}
|
|
152
|
+
type="single"
|
|
153
|
+
>
|
|
154
|
+
{children}
|
|
155
|
+
</ToggleGroupPrimitive.Root>
|
|
156
|
+
)}
|
|
157
|
+
</ToggleGroupContext.Provider>
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
)
|
|
161
|
+
ToggleGroup.displayName = 'ToggleGroup'
|
|
162
|
+
|
|
163
|
+
export interface ToggleGroupItemProps
|
|
164
|
+
extends Omit<React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item>, 'asChild'>,
|
|
165
|
+
VariantProps<typeof toggleVariants> {
|
|
166
|
+
value: string
|
|
167
|
+
children?: ReactNode
|
|
168
|
+
prefixIcon?: ReactNode
|
|
169
|
+
suffixIcon?: ReactNode
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const ToggleGroupItem = forwardRef<
|
|
173
|
+
React.ElementRef<typeof ToggleGroupPrimitive.Item>,
|
|
174
|
+
ToggleGroupItemProps
|
|
175
|
+
>(
|
|
176
|
+
(
|
|
177
|
+
{
|
|
178
|
+
variant: variantProp,
|
|
179
|
+
size: sizeProp,
|
|
180
|
+
rounded: roundedProp,
|
|
181
|
+
className,
|
|
182
|
+
prefixIcon,
|
|
183
|
+
suffixIcon,
|
|
184
|
+
children,
|
|
185
|
+
...props
|
|
186
|
+
},
|
|
187
|
+
ref
|
|
188
|
+
) => {
|
|
189
|
+
const ctx = useContext(ToggleGroupContext)
|
|
190
|
+
const variant = variantProp ?? ctx?.variant ?? 'default'
|
|
191
|
+
const size = sizeProp ?? ctx?.size ?? 'md'
|
|
192
|
+
const rounded = roundedProp ?? ctx?.rounded ?? 'square'
|
|
193
|
+
return (
|
|
194
|
+
<ToggleGroupPrimitive.Item
|
|
195
|
+
ref={ref}
|
|
196
|
+
data-variant={variant}
|
|
197
|
+
data-size={size}
|
|
198
|
+
className={cn(
|
|
199
|
+
toggleVariants({ variant, size, rounded }),
|
|
200
|
+
ctx && toggleGroupItemGroupClasses,
|
|
201
|
+
'focus-visible:z-10',
|
|
202
|
+
className
|
|
203
|
+
)}
|
|
204
|
+
{...props}
|
|
205
|
+
>
|
|
206
|
+
{prefixIcon && (
|
|
207
|
+
<span className="inline-flex shrink-0 items-center justify-center [&>*]:block [&>*]:leading-none">
|
|
208
|
+
{prefixIcon}
|
|
209
|
+
</span>
|
|
210
|
+
)}
|
|
211
|
+
{children}
|
|
212
|
+
{suffixIcon && (
|
|
213
|
+
<span className="inline-flex shrink-0 items-center justify-center [&>*]:block [&>*]:leading-none">
|
|
214
|
+
{suffixIcon}
|
|
215
|
+
</span>
|
|
216
|
+
)}
|
|
217
|
+
</ToggleGroupPrimitive.Item>
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
)
|
|
221
|
+
ToggleGroupItem.displayName = 'ToggleGroupItem'
|
|
222
|
+
|
|
223
|
+
export { ToggleGroup, ToggleGroupItem, toggleVariants }
|