@pantoia/ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/accordion/AGENTS.md +60 -0
- package/accordion/index.module.css +10 -0
- package/accordion/index.tsx +91 -0
- package/alert-dialog/AGENTS.md +78 -0
- package/alert-dialog/index.tsx +119 -0
- package/badge/AGENTS.md +35 -0
- package/badge/index.tsx +34 -0
- package/button/AGENTS.md +39 -0
- package/button/index.tsx +98 -0
- package/callout/AGENTS.md +42 -0
- package/callout/index.tsx +70 -0
- package/checkbox/AGENTS.md +54 -0
- package/checkbox/index.tsx +151 -0
- package/code-block/AGENTS.md +60 -0
- package/code-block/index.module.css +46 -0
- package/code-block/index.tsx +127 -0
- package/combobox/AGENTS.md +150 -0
- package/combobox/index.tsx +420 -0
- package/command-palette/AGENTS.md +44 -0
- package/command-palette/index.tsx +108 -0
- package/copy-button/AGENTS.md +43 -0
- package/copy-button/index.tsx +119 -0
- package/dialog/AGENTS.md +81 -0
- package/dialog/index.tsx +108 -0
- package/dist/index.js +189 -0
- package/drawer/AGENTS.md +66 -0
- package/drawer/index.module.css +17 -0
- package/drawer/index.tsx +198 -0
- package/dropdown-menu/AGENTS.md +133 -0
- package/dropdown-menu/index.tsx +223 -0
- package/field/AGENTS.md +96 -0
- package/field/index.tsx +149 -0
- package/input/AGENTS.md +35 -0
- package/input/index.tsx +36 -0
- package/kbd/AGENTS.md +17 -0
- package/kbd/index.tsx +13 -0
- package/label/AGENTS.md +21 -0
- package/label/index.tsx +13 -0
- package/number-field/AGENTS.md +53 -0
- package/number-field/index.tsx +227 -0
- package/package.json +73 -0
- package/popover/AGENTS.md +64 -0
- package/popover/index.tsx +66 -0
- package/press.css +37 -0
- package/radio-group/AGENTS.md +81 -0
- package/radio-group/index.tsx +139 -0
- package/registry.json +726 -0
- package/select/AGENTS.md +90 -0
- package/select/index.tsx +169 -0
- package/styles.css +347 -0
- package/switch/AGENTS.md +37 -0
- package/switch/index.tsx +42 -0
- package/table/AGENTS.md +82 -0
- package/table/index.tsx +120 -0
- package/tabs/AGENTS.md +60 -0
- package/tabs/index.module.css +14 -0
- package/tabs/index.tsx +105 -0
- package/test-utils.ts +10 -0
- package/textarea/AGENTS.md +35 -0
- package/textarea/index.tsx +37 -0
- package/toast/AGENTS.md +42 -0
- package/toast/index.tsx +71 -0
- package/toggle/AGENTS.md +37 -0
- package/toggle/index.tsx +36 -0
- package/tooltip/AGENTS.md +50 -0
- package/tooltip/index.tsx +57 -0
- package/tv.ts +23 -0
package/select/AGENTS.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Select
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
받기 npx @pantoia/ui add select
|
|
5
|
+
층 ui
|
|
6
|
+
파일 select/index.tsx · select/AGENTS.md
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## 부위
|
|
10
|
+
|
|
11
|
+
| 이름 | props |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `Select` | `SelectProps` |
|
|
14
|
+
| `SelectGroup` | — |
|
|
15
|
+
| `SelectItem` | — |
|
|
16
|
+
| `SelectItemText` | — |
|
|
17
|
+
| `SelectItemDescription` | — |
|
|
18
|
+
| `SelectSeparator` | — |
|
|
19
|
+
|
|
20
|
+
### `SelectGroup`
|
|
21
|
+
|
|
22
|
+
| 이름 | 타입 | 필수 |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| `label` | `string` | |
|
|
25
|
+
| `className` | `string` | |
|
|
26
|
+
| `children` | `ReactNode` | |
|
|
27
|
+
|
|
28
|
+
### `SelectItem`
|
|
29
|
+
|
|
30
|
+
| 이름 | 타입 | 필수 |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| `value` | `string` | 예 |
|
|
33
|
+
| `disabled` | `boolean` | |
|
|
34
|
+
| `className` | `string` | |
|
|
35
|
+
| `children` | `ReactNode` | |
|
|
36
|
+
|
|
37
|
+
### `SelectItemText`
|
|
38
|
+
|
|
39
|
+
| 이름 | 타입 | 필수 |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| `className` | `string` | |
|
|
42
|
+
| `children` | `ReactNode` | |
|
|
43
|
+
|
|
44
|
+
### `SelectItemDescription`
|
|
45
|
+
|
|
46
|
+
| 이름 | 타입 | 필수 |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| `className` | `string` | |
|
|
49
|
+
| `children` | `ReactNode` | |
|
|
50
|
+
|
|
51
|
+
### `SelectSeparator`
|
|
52
|
+
|
|
53
|
+
| 이름 | 타입 | 필수 |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `className` | `string` | |
|
|
56
|
+
|
|
57
|
+
## props
|
|
58
|
+
|
|
59
|
+
### `SelectProps`
|
|
60
|
+
|
|
61
|
+
`Omit<ComponentProps<typeof SelectPrimitive.Trigger>, 'value' | 'defaultValue' | 'dir'>` 를 확장한다.
|
|
62
|
+
|
|
63
|
+
| 이름 | 타입 | 필수 |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| `value` | `string` | |
|
|
66
|
+
| `defaultValue` | `string` | |
|
|
67
|
+
| `onValueChange` | `(value: string) => void` | |
|
|
68
|
+
| `open` | `boolean` | |
|
|
69
|
+
| `defaultOpen` | `boolean` | |
|
|
70
|
+
| `onOpenChange` | `(open: boolean) => void` | |
|
|
71
|
+
| `placeholder` | `string` | |
|
|
72
|
+
| `surface` | `'bare' \| 'well'` | |
|
|
73
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | |
|
|
74
|
+
| `arrow` | `boolean` | |
|
|
75
|
+
| `children` | `ReactNode` | |
|
|
76
|
+
|
|
77
|
+
## 변형 — `selectTrigger`
|
|
78
|
+
|
|
79
|
+
| 축 | 값 | 기본 |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| `surface` | `bare` · `well` | `bare` |
|
|
82
|
+
| `size` | `sm` · `md` · `lg` | `md` |
|
|
83
|
+
|
|
84
|
+
## 함께 받는 것
|
|
85
|
+
|
|
86
|
+
`tv`
|
|
87
|
+
|
|
88
|
+
## npm 의존
|
|
89
|
+
|
|
90
|
+
`lucide-react` · `radix-ui`
|
package/select/index.tsx
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Check, ChevronDown, ChevronUp } from 'lucide-react'
|
|
4
|
+
import { Select as SelectPrimitive } from 'radix-ui'
|
|
5
|
+
import type { ComponentProps, ReactNode } from 'react'
|
|
6
|
+
|
|
7
|
+
import { tv } from '../tv'
|
|
8
|
+
|
|
9
|
+
export const selectTrigger = tv({
|
|
10
|
+
base: 'flex w-full items-center justify-between gap-tight rounded-r8'
|
|
11
|
+
+ ' text-value text-fg'
|
|
12
|
+
+ ' transition-state'
|
|
13
|
+
+ ' invalid:ring-2 invalid:ring-danger'
|
|
14
|
+
+ ' focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring'
|
|
15
|
+
+ ' disabled:bg-disabled disabled:text-fg-disabled disabled:cursor-not-allowed'
|
|
16
|
+
+ ' unset:text-fg-placeholder',
|
|
17
|
+
|
|
18
|
+
variants: {
|
|
19
|
+
surface: {
|
|
20
|
+
bare: 'bg-transparent ring-1 ring-inset ring-line-strong',
|
|
21
|
+
well: 'bg-well',
|
|
22
|
+
},
|
|
23
|
+
size: {
|
|
24
|
+
sm: 'h-8 px-3',
|
|
25
|
+
md: 'h-9 px-3',
|
|
26
|
+
lg: 'h-10 px-3 text-t16',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
defaultVariants: { surface: 'bare', size: 'md' },
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
export interface SelectProps extends Omit<ComponentProps<typeof SelectPrimitive.Trigger>, 'value' | 'defaultValue' | 'dir'> {
|
|
34
|
+
value?: string
|
|
35
|
+
defaultValue?: string
|
|
36
|
+
onValueChange?: (value: string) => void
|
|
37
|
+
open?: boolean
|
|
38
|
+
defaultOpen?: boolean
|
|
39
|
+
onOpenChange?: (open: boolean) => void
|
|
40
|
+
placeholder?: string
|
|
41
|
+
surface?: 'bare' | 'well'
|
|
42
|
+
size?: 'sm' | 'md' | 'lg'
|
|
43
|
+
arrow?: boolean
|
|
44
|
+
children?: ReactNode
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function Select({
|
|
48
|
+
value,
|
|
49
|
+
defaultValue,
|
|
50
|
+
onValueChange,
|
|
51
|
+
open,
|
|
52
|
+
defaultOpen,
|
|
53
|
+
onOpenChange,
|
|
54
|
+
placeholder,
|
|
55
|
+
surface,
|
|
56
|
+
size,
|
|
57
|
+
arrow = false,
|
|
58
|
+
className,
|
|
59
|
+
children,
|
|
60
|
+
...rest
|
|
61
|
+
}: SelectProps) {
|
|
62
|
+
return (
|
|
63
|
+
<SelectPrimitive.Root
|
|
64
|
+
value={value}
|
|
65
|
+
defaultValue={defaultValue}
|
|
66
|
+
onValueChange={onValueChange}
|
|
67
|
+
open={open}
|
|
68
|
+
defaultOpen={defaultOpen}
|
|
69
|
+
onOpenChange={onOpenChange}
|
|
70
|
+
>
|
|
71
|
+
<SelectPrimitive.Trigger {...rest} className={selectTrigger({ surface, size, className })}>
|
|
72
|
+
<span className="min-w-0 truncate">
|
|
73
|
+
<SelectPrimitive.Value placeholder={placeholder} />
|
|
74
|
+
</span>
|
|
75
|
+
<SelectPrimitive.Icon className="text-fg-subtle">
|
|
76
|
+
<ChevronDown className="size-4" />
|
|
77
|
+
</SelectPrimitive.Icon>
|
|
78
|
+
</SelectPrimitive.Trigger>
|
|
79
|
+
|
|
80
|
+
<SelectPrimitive.Portal>
|
|
81
|
+
<SelectPrimitive.Content
|
|
82
|
+
className="bg-float text-fg rounded-float shadow-s3 z-overlay
|
|
83
|
+
min-w-(--radix-select-trigger-width) p-1
|
|
84
|
+
open:animate-enter closed:animate-exit"
|
|
85
|
+
position="popper"
|
|
86
|
+
sideOffset={8}
|
|
87
|
+
arrowPadding={16}
|
|
88
|
+
>
|
|
89
|
+
<SelectPrimitive.ScrollUpButton className="text-fg-subtle flex h-6 cursor-default items-center justify-center">
|
|
90
|
+
<ChevronUp className="size-4" />
|
|
91
|
+
</SelectPrimitive.ScrollUpButton>
|
|
92
|
+
|
|
93
|
+
<SelectPrimitive.Viewport className="flex flex-col gap-px">
|
|
94
|
+
{children}
|
|
95
|
+
</SelectPrimitive.Viewport>
|
|
96
|
+
|
|
97
|
+
<SelectPrimitive.ScrollDownButton className="text-fg-subtle flex h-6 cursor-default items-center justify-center">
|
|
98
|
+
<ChevronDown className="size-4" />
|
|
99
|
+
</SelectPrimitive.ScrollDownButton>
|
|
100
|
+
|
|
101
|
+
{arrow && <SelectPrimitive.Arrow className="fill-float" width={12} height={6} />}
|
|
102
|
+
</SelectPrimitive.Content>
|
|
103
|
+
</SelectPrimitive.Portal>
|
|
104
|
+
</SelectPrimitive.Root>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function SelectGroup({ label, className, children }: {
|
|
109
|
+
label?: string
|
|
110
|
+
className?: string
|
|
111
|
+
children?: ReactNode
|
|
112
|
+
}) {
|
|
113
|
+
return (
|
|
114
|
+
<SelectPrimitive.Group className={tv({ base: 'flex flex-col gap-px' })({ className })}>
|
|
115
|
+
{label && (
|
|
116
|
+
<SelectPrimitive.Label className="text-caption text-fg-subtle px-2 pb-1 pt-2">
|
|
117
|
+
{label}
|
|
118
|
+
</SelectPrimitive.Label>
|
|
119
|
+
)}
|
|
120
|
+
{children}
|
|
121
|
+
</SelectPrimitive.Group>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function SelectItem({ value, disabled, className, children }: {
|
|
126
|
+
value: string
|
|
127
|
+
disabled?: boolean
|
|
128
|
+
className?: string
|
|
129
|
+
children?: ReactNode
|
|
130
|
+
}) {
|
|
131
|
+
return (
|
|
132
|
+
<SelectPrimitive.Item
|
|
133
|
+
value={value}
|
|
134
|
+
disabled={disabled}
|
|
135
|
+
className={tv({ base: 'flex min-h-9 cursor-default items-center justify-between gap-stack'
|
|
136
|
+
+ ' rounded-r8 px-2 py-1 transition-state'
|
|
137
|
+
+ ' data-highlighted:bg-hover data-highlighted:outline-none'
|
|
138
|
+
+ ' data-disabled:text-fg-disabled data-disabled:pointer-events-none' })({ className })}
|
|
139
|
+
>
|
|
140
|
+
<span className="flex min-w-0 flex-col">
|
|
141
|
+
{children}
|
|
142
|
+
</span>
|
|
143
|
+
|
|
144
|
+
<SelectPrimitive.ItemIndicator className="text-fg-muted flex shrink-0">
|
|
145
|
+
<Check className="size-4" />
|
|
146
|
+
</SelectPrimitive.ItemIndicator>
|
|
147
|
+
</SelectPrimitive.Item>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function SelectItemText({ className, children }: { className?: string, children?: ReactNode }) {
|
|
152
|
+
return (
|
|
153
|
+
<SelectPrimitive.ItemText className={tv({ base: 'text-label text-fg' })({ className })}>
|
|
154
|
+
{children}
|
|
155
|
+
</SelectPrimitive.ItemText>
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function SelectItemDescription({ className, children }: { className?: string, children?: ReactNode }) {
|
|
160
|
+
return (
|
|
161
|
+
<span className={tv({ base: 'text-caption text-fg-subtle' })({ className })}>
|
|
162
|
+
{children}
|
|
163
|
+
</span>
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function SelectSeparator({ className }: { className?: string }) {
|
|
168
|
+
return <SelectPrimitive.Separator className={tv({ base: 'bg-line -mx-1 my-1 h-px' })({ className })} />
|
|
169
|
+
}
|
package/styles.css
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
@import "pretendard/dist/web/variable/pretendardvariable-dynamic-subset.css";
|
|
2
|
+
@import "./press.css";
|
|
3
|
+
|
|
4
|
+
@custom-variant dark (&:where(.dark, .dark *):not(:where(.light, .light *)));
|
|
5
|
+
|
|
6
|
+
@custom-variant current (&[aria-current="page"]);
|
|
7
|
+
|
|
8
|
+
@custom-variant invalid (&[aria-invalid="true"]);
|
|
9
|
+
|
|
10
|
+
@custom-variant invalid-within (&:has([aria-invalid="true"]));
|
|
11
|
+
|
|
12
|
+
@custom-variant in-invalid (&:where([aria-invalid="true"] *));
|
|
13
|
+
|
|
14
|
+
@custom-variant described (&:has([data-description]));
|
|
15
|
+
|
|
16
|
+
@custom-variant checked (&:where([data-state="checked"], [data-state="indeterminate"]));
|
|
17
|
+
@custom-variant open (&[data-state="open"]);
|
|
18
|
+
@custom-variant closed (&[data-state="closed"]);
|
|
19
|
+
@custom-variant on (&[data-state="on"]);
|
|
20
|
+
@custom-variant selected (&[data-selected]:not([data-selected="false"]));
|
|
21
|
+
@custom-variant unset (&[data-placeholder]);
|
|
22
|
+
|
|
23
|
+
@custom-variant menu-gutter (.menu:has(.menu-lead) &:not(:has(.menu-lead)));
|
|
24
|
+
|
|
25
|
+
:root, .light {
|
|
26
|
+
--gray-50:#f9fafb; --gray-100:#f2f4f6; --gray-200:#e5e8eb; --gray-300:#d1d6db;
|
|
27
|
+
--gray-400:#b0b8c1; --gray-500:#8b95a1; --gray-600:#6b7684; --gray-700:#4e5968;
|
|
28
|
+
--gray-800:#333d4b; --gray-900:#191f28;
|
|
29
|
+
|
|
30
|
+
--purple-50:#f5f1fd; --purple-100:#e4d6ff; --purple-200:#ceb0ff; --purple-300:#bb8ffa;
|
|
31
|
+
--purple-400:#aa74f2; --purple-500:#9c63e4; --purple-600:#8750c9; --purple-700:#7543b0;
|
|
32
|
+
--purple-800:#633997; --purple-900:#54317f;
|
|
33
|
+
|
|
34
|
+
--blue-50:#e8f3ff; --blue-100:#c9e2ff; --blue-200:#90c2ff; --blue-300:#64a8ff;
|
|
35
|
+
--blue-400:#4593fc; --blue-500:#3182f6; --blue-600:#2272eb; --blue-700:#1b64da;
|
|
36
|
+
--blue-800:#1957c2; --blue-900:#194aa6;
|
|
37
|
+
|
|
38
|
+
--green-50:#f0faf6; --green-100:#aeefd5; --green-200:#76e4b8; --green-300:#3fd599;
|
|
39
|
+
--green-400:#15c47e; --green-500:#03b26c; --green-600:#02a262; --green-700:#029359;
|
|
40
|
+
--green-800:#028450; --green-900:#027648;
|
|
41
|
+
|
|
42
|
+
--orange-50:#fff3e0; --orange-100:#ffe0b0; --orange-200:#ffcd80; --orange-300:#ffbd51;
|
|
43
|
+
--orange-400:#ffa927; --orange-500:#fe9800; --orange-600:#fb8800; --orange-700:#f57800;
|
|
44
|
+
--orange-800:#ed6700; --orange-900:#e45600;
|
|
45
|
+
|
|
46
|
+
--red-50:#ffeeee; --red-100:#ffd4d6; --red-200:#feafb4; --red-300:#fb8890;
|
|
47
|
+
--red-400:#f66570; --red-500:#f04452; --red-600:#e42939; --red-700:#d22030;
|
|
48
|
+
--red-800:#bc1b2a; --red-900:#a51926;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.dark {
|
|
52
|
+
--gray-50:#202027; --gray-100:#2c2c35; --gray-200:#3c3c47; --gray-300:#4d4d59;
|
|
53
|
+
--gray-400:#62626d; --gray-500:#7e7e87; --gray-600:#9e9ea4; --gray-700:#c3c3c6;
|
|
54
|
+
--gray-800:#e4e4e5; --gray-900:#ffffff;
|
|
55
|
+
|
|
56
|
+
--purple-50:#362949; --purple-100:#462e66; --purple-200:#583682; --purple-300:#6b3fa0;
|
|
57
|
+
--purple-400:#8451c4; --purple-500:#9c63e4; --purple-600:#ad75f7; --purple-700:#c094ff;
|
|
58
|
+
--purple-800:#d6baff; --purple-900:#eadeff;
|
|
59
|
+
|
|
60
|
+
--blue-50:#202c4d; --blue-100:#23386a; --blue-200:#25478c; --blue-300:#265ab3;
|
|
61
|
+
--blue-400:#2970d9; --blue-500:#3485fa; --blue-600:#449bff; --blue-700:#61b0ff;
|
|
62
|
+
--blue-800:#8fcdff; --blue-900:#c8e7ff;
|
|
63
|
+
|
|
64
|
+
--green-50:#153729; --green-100:#135338; --green-200:#136d47; --green-300:#138a59;
|
|
65
|
+
--green-400:#13a065; --green-500:#16bb76; --green-600:#26cf88; --green-700:#4ee4a6;
|
|
66
|
+
--green-800:#82f6c5; --green-900:#ccffea;
|
|
67
|
+
|
|
68
|
+
--orange-50:#3d2500; --orange-100:#563200; --orange-200:#804600; --orange-300:#a85f00;
|
|
69
|
+
--orange-400:#cf7200; --orange-500:#f18600; --orange-600:#fd9528; --orange-700:#ffa861;
|
|
70
|
+
--orange-800:#ffc39e; --orange-900:#ffe4d6;
|
|
71
|
+
|
|
72
|
+
--red-50:#3c2020; --red-100:#562025; --red-200:#7a242d; --red-300:#9e2733;
|
|
73
|
+
--red-400:#ca2f3d; --red-500:#f04251; --red-600:#fa616d; --red-700:#fe818b;
|
|
74
|
+
--red-800:#ffa8ad; --red-900:#ffd1d3;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@theme {
|
|
78
|
+
--color-*: initial;
|
|
79
|
+
--text-*: initial;
|
|
80
|
+
--leading-*: initial;
|
|
81
|
+
--font-weight-*: initial;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@theme {
|
|
85
|
+
--font-weight-normal: 400;
|
|
86
|
+
--font-weight-medium: 500;
|
|
87
|
+
--font-weight-bold: 700;
|
|
88
|
+
|
|
89
|
+
--font-sans: "Pretendard Variable", "Pretendard", -apple-system, system-ui, sans-serif;
|
|
90
|
+
--font-mono: "JetBrains Mono", "Pretendard Variable", "Pretendard", ui-monospace, monospace;
|
|
91
|
+
|
|
92
|
+
--text-display: 28px; --text-display--line-height: 38px; --text-display--font-weight: 700;
|
|
93
|
+
--text-title: 24px; --text-title--line-height: 32px; --text-title--font-weight: 700;
|
|
94
|
+
--text-section: 20px; --text-section--line-height: 27px; --text-section--font-weight: 700;
|
|
95
|
+
--text-subsection: 16px; --text-subsection--line-height: 22px; --text-subsection--font-weight: 700;
|
|
96
|
+
--text-label: 14px; --text-label--line-height: 19px; --text-label--font-weight: 500;
|
|
97
|
+
--text-caption: 13px; --text-caption--line-height: 18px; --text-caption--font-weight: 400;
|
|
98
|
+
--text-micro: 11px; --text-micro--line-height: 15px; --text-micro--font-weight: 500;
|
|
99
|
+
|
|
100
|
+
--text-t10: 10px; --text-t10--line-height: 14px; --text-t10--font-weight: 400;
|
|
101
|
+
--text-t11: 11px; --text-t11--line-height: 15px; --text-t11--font-weight: 400;
|
|
102
|
+
--text-t12: 12px; --text-t12--line-height: 16px; --text-t12--font-weight: 400;
|
|
103
|
+
--text-t13: 13px; --text-t13--line-height: 18px; --text-t13--font-weight: 400;
|
|
104
|
+
--text-t14: 14px; --text-t14--line-height: 19px; --text-t14--font-weight: 400;
|
|
105
|
+
--text-t16: 16px; --text-t16--line-height: 22px; --text-t16--font-weight: 400;
|
|
106
|
+
--text-t18: 18px; --text-t18--line-height: 24px; --text-t18--font-weight: 400;
|
|
107
|
+
--text-t20: 20px; --text-t20--line-height: 27px; --text-t20--font-weight: 400;
|
|
108
|
+
--text-t24: 24px; --text-t24--line-height: 32px; --text-t24--font-weight: 400;
|
|
109
|
+
--text-t28: 28px; --text-t28--line-height: 38px; --text-t28--font-weight: 400;
|
|
110
|
+
--text-t32: 32px; --text-t32--line-height: 43px; --text-t32--font-weight: 400;
|
|
111
|
+
--text-t40: 40px; --text-t40--line-height: 54px; --text-t40--font-weight: 400;
|
|
112
|
+
--text-t48: 48px; --text-t48--line-height: 65px; --text-t48--font-weight: 400;
|
|
113
|
+
--text-t56: 56px; --text-t56--line-height: 76px; --text-t56--font-weight: 400;
|
|
114
|
+
--text-t64: 64px; --text-t64--line-height: 86px; --text-t64--font-weight: 400;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@theme inline {
|
|
118
|
+
--text-prose: var(--ui-prose);
|
|
119
|
+
--text-prose--line-height: var(--ui-prose-lh);
|
|
120
|
+
--text-prose--font-weight: 400;
|
|
121
|
+
|
|
122
|
+
--text-value: var(--ui-value);
|
|
123
|
+
--text-value--line-height: var(--ui-value-lh);
|
|
124
|
+
--text-value--font-weight: 400;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
:root {
|
|
128
|
+
--ui-prose: 16px; --ui-prose-lh: 24px;
|
|
129
|
+
--ui-value: 16px; --ui-value-lh: 22px;
|
|
130
|
+
|
|
131
|
+
@variant sm { --ui-prose: 14px; --ui-prose-lh: 22px; }
|
|
132
|
+
@variant sm { --ui-value: 14px; --ui-value-lh: 19px; }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@font-face {
|
|
136
|
+
font-family: "JetBrains Mono";
|
|
137
|
+
src: url("@fontsource/jetbrains-mono/files/jetbrains-mono-latin-400-normal.woff2") format("woff2");
|
|
138
|
+
font-weight: 400;
|
|
139
|
+
font-display: swap;
|
|
140
|
+
/* [TODO] size-adjust 를 지운다 — 시각 기준 이미지가 서고 나서 */
|
|
141
|
+
size-adjust: 96%;
|
|
142
|
+
unicode-range: U+0000-00FF, U+2000-206F;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@layer base {
|
|
146
|
+
:root { color-scheme: light; }
|
|
147
|
+
.dark { color-scheme: dark; }
|
|
148
|
+
|
|
149
|
+
body {
|
|
150
|
+
background-color: var(--color-canvas);
|
|
151
|
+
color: var(--color-fg);
|
|
152
|
+
font-family: var(--font-sans);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
code, pre, kbd, samp { font-family: var(--font-mono); }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
@theme inline {
|
|
159
|
+
--color-white:#ffffff;
|
|
160
|
+
--color-black:#000000;
|
|
161
|
+
|
|
162
|
+
--color-canvas: var(--ui-canvas);
|
|
163
|
+
--color-well: var(--ui-well);
|
|
164
|
+
--color-panel: var(--ui-panel);
|
|
165
|
+
--color-float: var(--ui-float);
|
|
166
|
+
--color-disabled: var(--gray-200);
|
|
167
|
+
|
|
168
|
+
--color-hover: var(--ui-hover);
|
|
169
|
+
--color-selected: var(--ui-selected);
|
|
170
|
+
--color-selected-hover: var(--ui-selected-hover);
|
|
171
|
+
|
|
172
|
+
--color-control-on: var(--gray-900);
|
|
173
|
+
--color-control-on-hover: var(--gray-800);
|
|
174
|
+
--color-control-on-disabled: var(--gray-600);
|
|
175
|
+
--color-fg-on-control: var(--gray-50);
|
|
176
|
+
|
|
177
|
+
--color-fg: var(--gray-900);
|
|
178
|
+
--color-fg-muted: var(--gray-700);
|
|
179
|
+
--color-fg-subtle: var(--gray-500);
|
|
180
|
+
--color-fg-placeholder: var(--gray-400);
|
|
181
|
+
--color-fg-disabled: var(--gray-600);
|
|
182
|
+
--color-fg-on-solid:#ffffff;
|
|
183
|
+
|
|
184
|
+
--color-line: var(--gray-200);
|
|
185
|
+
--color-line-strong: var(--gray-300);
|
|
186
|
+
--color-ring: var(--blue-500);
|
|
187
|
+
|
|
188
|
+
--color-neutral-solid: var(--gray-900);
|
|
189
|
+
--color-neutral-solid-hover: var(--gray-800);
|
|
190
|
+
|
|
191
|
+
--color-primary-solid: var(--purple-500);
|
|
192
|
+
--color-primary-solid-hover: var(--purple-600);
|
|
193
|
+
--color-primary-weak: var(--purple-50);
|
|
194
|
+
--color-primary-on-weak: var(--purple-700);
|
|
195
|
+
--color-primary-stroke: var(--purple-200);
|
|
196
|
+
|
|
197
|
+
--color-info: var(--blue-500);
|
|
198
|
+
--color-info-weak: var(--blue-50);
|
|
199
|
+
--color-info-on-weak: var(--blue-700);
|
|
200
|
+
--color-info-stroke: var(--blue-200);
|
|
201
|
+
|
|
202
|
+
--color-success: var(--green-500);
|
|
203
|
+
--color-success-weak: var(--green-50);
|
|
204
|
+
--color-success-on-weak: var(--green-700);
|
|
205
|
+
--color-success-stroke: var(--green-200);
|
|
206
|
+
|
|
207
|
+
--color-warning: var(--orange-500);
|
|
208
|
+
--color-warning-weak: var(--orange-50);
|
|
209
|
+
--color-warning-on-weak: var(--orange-700);
|
|
210
|
+
--color-warning-stroke: var(--orange-200);
|
|
211
|
+
|
|
212
|
+
--color-danger: var(--red-500);
|
|
213
|
+
--color-danger-weak: var(--red-50);
|
|
214
|
+
--color-danger-on-weak: var(--red-700);
|
|
215
|
+
--color-danger-stroke: var(--red-200);
|
|
216
|
+
--color-danger-solid: var(--red-500);
|
|
217
|
+
--color-danger-solid-hover: var(--red-600);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
:root, .light {
|
|
221
|
+
--ui-canvas:#ffffff;
|
|
222
|
+
--ui-well:#f2f4f6;
|
|
223
|
+
--ui-panel:#ffffff;
|
|
224
|
+
--ui-float:#ffffff;
|
|
225
|
+
--ui-hover: rgba(0,23,51,0.02);
|
|
226
|
+
--ui-selected: rgba(2,32,71,0.05);
|
|
227
|
+
--ui-selected-hover: rgba(0,27,55,0.1);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.dark {
|
|
231
|
+
--ui-canvas:#17171c;
|
|
232
|
+
--ui-well:#101013;
|
|
233
|
+
--ui-panel:#202027;
|
|
234
|
+
--ui-float:#2c2c35;
|
|
235
|
+
--ui-hover: rgba(209,209,253,0.05);
|
|
236
|
+
--ui-selected: rgba(217,217,255,0.11);
|
|
237
|
+
--ui-selected-hover: rgba(222,222,255,0.19);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
@theme {
|
|
241
|
+
--spacing-tight: 4px;
|
|
242
|
+
--spacing-stack: 16px;
|
|
243
|
+
--spacing-block: 32px;
|
|
244
|
+
--spacing-section: 48px;
|
|
245
|
+
--spacing-gutter: 16px;
|
|
246
|
+
|
|
247
|
+
--container-app: 1280px;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
@theme {
|
|
251
|
+
--radius-*: initial;
|
|
252
|
+
|
|
253
|
+
--radius-mark: 4px;
|
|
254
|
+
--radius-well: 8px;
|
|
255
|
+
--radius-panel: 12px;
|
|
256
|
+
--radius-float: 16px;
|
|
257
|
+
|
|
258
|
+
--radius-r2: 2px; --radius-r4: 4px; --radius-r6: 6px;
|
|
259
|
+
--radius-r8: 8px; --radius-r10: 10px; --radius-r12: 12px;
|
|
260
|
+
--radius-r14: 14px; --radius-r16: 16px; --radius-r20: 20px;
|
|
261
|
+
--radius-r24: 24px;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
@theme {
|
|
265
|
+
--ease-*: initial;
|
|
266
|
+
|
|
267
|
+
--ease-state: cubic-bezier(.35, 0, .65, 1);
|
|
268
|
+
--ease-enter: cubic-bezier(0, 0, .15, 1);
|
|
269
|
+
--ease-exit: cubic-bezier(.35, 0, 1, 1);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
:root {
|
|
273
|
+
--dur-state: 150ms;
|
|
274
|
+
--dur-enter: 200ms;
|
|
275
|
+
--dur-exit: 150ms;
|
|
276
|
+
--enter-scale: 0.95;
|
|
277
|
+
--draw-len: 24;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
@utility transition-state {
|
|
281
|
+
transition-property: color, background-color, box-shadow, scale;
|
|
282
|
+
transition-duration: var(--dur-state);
|
|
283
|
+
transition-timing-function: var(--ease-state);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
@keyframes enter { from { opacity: 0; scale: var(--enter-scale); } }
|
|
287
|
+
@keyframes exit { to { opacity: 0; scale: var(--enter-scale); } }
|
|
288
|
+
|
|
289
|
+
@utility animate-enter { animation: enter var(--dur-enter) var(--ease-enter); }
|
|
290
|
+
@utility animate-exit { animation: exit var(--dur-exit) var(--ease-exit); }
|
|
291
|
+
|
|
292
|
+
@keyframes draw { from { stroke-dashoffset: var(--draw-len); } }
|
|
293
|
+
|
|
294
|
+
@utility animate-draw {
|
|
295
|
+
stroke-dasharray: var(--draw-len);
|
|
296
|
+
animation: draw var(--dur-enter) var(--ease-enter);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
@keyframes fade-in { from { opacity: 0; } }
|
|
300
|
+
@keyframes fade-out { to { opacity: 0; } }
|
|
301
|
+
|
|
302
|
+
@utility animate-fade-in { animation: fade-in var(--dur-enter) var(--ease-enter); }
|
|
303
|
+
@utility animate-fade-out { animation: fade-out var(--dur-exit) var(--ease-exit); }
|
|
304
|
+
|
|
305
|
+
@media (prefers-reduced-motion: reduce) {
|
|
306
|
+
:root { --enter-scale: 1; }
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
@theme {
|
|
310
|
+
--shadow-*: initial;
|
|
311
|
+
|
|
312
|
+
--shadow-s1: 0 1px 4px 0 var(--shade-1);
|
|
313
|
+
--shadow-s2: 0 2px 10px 0 var(--shade-2);
|
|
314
|
+
--shadow-s3: 0 4px 16px 0 var(--shade-3);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
:root, .light { --shade-1:#00000014; --shade-2:#0000001a; --shade-3:#0000001f; }
|
|
318
|
+
.dark { --shade-1:#00000080; --shade-2:#000000ad; --shade-3:#000000cc; }
|
|
319
|
+
|
|
320
|
+
@theme inline {
|
|
321
|
+
--color-overlay: var(--ui-overlay);
|
|
322
|
+
}
|
|
323
|
+
:root, .light { --ui-overlay: rgba(0,0,0,0.2); }
|
|
324
|
+
.dark { --ui-overlay: rgba(0,0,0,0.56); }
|
|
325
|
+
|
|
326
|
+
@utility z-sticky { z-index: 10; }
|
|
327
|
+
@utility z-overlay { z-index: 50; }
|
|
328
|
+
@utility z-toast { z-index: 60; }
|
|
329
|
+
|
|
330
|
+
:root { --header-h: 56px; }
|
|
331
|
+
|
|
332
|
+
@utility side-rail {
|
|
333
|
+
position: sticky;
|
|
334
|
+
top: var(--header-h);
|
|
335
|
+
max-height: calc(100dvh - var(--header-h));
|
|
336
|
+
overflow-y: auto;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
@utility scroll-slim {
|
|
340
|
+
scrollbar-width: thin;
|
|
341
|
+
scrollbar-color: var(--color-line-strong) transparent;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
@utility scroll-hidden {
|
|
345
|
+
scrollbar-width: none;
|
|
346
|
+
&::-webkit-scrollbar { display: none; }
|
|
347
|
+
}
|
package/switch/AGENTS.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Switch
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
받기 npx @pantoia/ui add switch
|
|
5
|
+
층 ui
|
|
6
|
+
파일 switch/index.tsx · switch/AGENTS.md
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## 부위
|
|
10
|
+
|
|
11
|
+
| 이름 | props |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `Switch` | `SwitchProps` |
|
|
14
|
+
|
|
15
|
+
## props
|
|
16
|
+
|
|
17
|
+
### `SwitchProps`
|
|
18
|
+
|
|
19
|
+
`ComponentProps<typeof SwitchPrimitive.Root>` 를 확장한다.
|
|
20
|
+
|
|
21
|
+
| 이름 | 타입 | 필수 |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| `color` | `'control' \| 'info' \| 'success' \| 'warning' \| 'danger'` | |
|
|
24
|
+
|
|
25
|
+
## 변형 — `uiSwitch`
|
|
26
|
+
|
|
27
|
+
| 축 | 값 | 기본 |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `color` | `control` · `info` · `success` · `warning` · `danger` | `control` |
|
|
30
|
+
|
|
31
|
+
## 함께 받는 것
|
|
32
|
+
|
|
33
|
+
`tv`
|
|
34
|
+
|
|
35
|
+
## npm 의존
|
|
36
|
+
|
|
37
|
+
`radix-ui`
|
package/switch/index.tsx
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Switch as SwitchPrimitive } from 'radix-ui'
|
|
4
|
+
import type { ComponentProps } from 'react'
|
|
5
|
+
|
|
6
|
+
import { tv } from '../tv'
|
|
7
|
+
|
|
8
|
+
export const uiSwitch = tv({
|
|
9
|
+
base: 'h-6 w-10 shrink-0 rounded-full p-0.5 transition-state press press-shrink'
|
|
10
|
+
+ ' bg-well ring-1 ring-inset ring-line-strong'
|
|
11
|
+
+ ' invalid:ring-2 invalid:ring-danger'
|
|
12
|
+
+ ' focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring'
|
|
13
|
+
+ ' disabled:bg-disabled disabled:cursor-not-allowed'
|
|
14
|
+
+ ' checked:disabled:bg-control-on-disabled checked:disabled:ring-control-on-disabled',
|
|
15
|
+
|
|
16
|
+
variants: {
|
|
17
|
+
color: {
|
|
18
|
+
control: 'checked:bg-control-on checked:ring-control-on',
|
|
19
|
+
info: 'checked:bg-info checked:ring-info',
|
|
20
|
+
success: 'checked:bg-success checked:ring-success',
|
|
21
|
+
warning: 'checked:bg-warning checked:ring-warning',
|
|
22
|
+
danger: 'checked:bg-danger checked:ring-danger',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
defaultVariants: { color: 'control' },
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
export interface SwitchProps extends ComponentProps<typeof SwitchPrimitive.Root> {
|
|
30
|
+
color?: 'control' | 'info' | 'success' | 'warning' | 'danger'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function Switch({ color, className, ...rest }: SwitchProps) {
|
|
34
|
+
return (
|
|
35
|
+
<SwitchPrimitive.Root {...rest} className={uiSwitch({ color, className })}>
|
|
36
|
+
<SwitchPrimitive.Thumb
|
|
37
|
+
className="bg-float block size-5 rounded-full checked:translate-x-4
|
|
38
|
+
transition-transform ease-state motion-reduce:transition-none"
|
|
39
|
+
/>
|
|
40
|
+
</SwitchPrimitive.Root>
|
|
41
|
+
)
|
|
42
|
+
}
|