@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
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Checkbox
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
받기 npx @pantoia/ui add checkbox
|
|
5
|
+
층 ui
|
|
6
|
+
파일 checkbox/index.tsx · checkbox/AGENTS.md
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## 부위
|
|
10
|
+
|
|
11
|
+
| 이름 | props |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `CheckboxGroup` | `CheckboxGroupProps` |
|
|
14
|
+
| `Checkbox` | `CheckboxProps` |
|
|
15
|
+
|
|
16
|
+
## props
|
|
17
|
+
|
|
18
|
+
### `CheckboxGroupProps`
|
|
19
|
+
|
|
20
|
+
| 이름 | 타입 | 필수 |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| `value` | `string[]` | |
|
|
23
|
+
| `defaultValue` | `string[]` | |
|
|
24
|
+
| `onValueChange` | `(value: string[]) => void` | |
|
|
25
|
+
| `disabled` | `boolean` | |
|
|
26
|
+
| `className` | `string` | |
|
|
27
|
+
| `children` | `ReactNode` | |
|
|
28
|
+
|
|
29
|
+
### `CheckboxProps`
|
|
30
|
+
|
|
31
|
+
`Omit<ComponentProps<typeof CheckboxPrimitive.Root>, 'checked' | 'onCheckedChange' | 'value'>` 를 확장한다.
|
|
32
|
+
|
|
33
|
+
| 이름 | 타입 | 필수 |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| `value` | `string` | |
|
|
36
|
+
| `checked` | `boolean` | |
|
|
37
|
+
| `onCheckedChange` | `(checked: boolean) => void` | |
|
|
38
|
+
| `indeterminate` | `boolean` | |
|
|
39
|
+
| `onIndeterminateChange` | `(indeterminate: boolean) => void` | |
|
|
40
|
+
| `color` | `'control' \| 'info' \| 'success' \| 'warning' \| 'danger'` | |
|
|
41
|
+
|
|
42
|
+
## 변형 — `checkbox`
|
|
43
|
+
|
|
44
|
+
| 축 | 값 | 기본 |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| `color` | `control` · `info` · `success` · `warning` · `danger` | `control` |
|
|
47
|
+
|
|
48
|
+
## 함께 받는 것
|
|
49
|
+
|
|
50
|
+
`tv`
|
|
51
|
+
|
|
52
|
+
## npm 의존
|
|
53
|
+
|
|
54
|
+
`lucide-react` · `radix-ui`
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Check, Minus } from 'lucide-react'
|
|
4
|
+
import { Checkbox as CheckboxPrimitive } from 'radix-ui'
|
|
5
|
+
import {
|
|
6
|
+
createContext,
|
|
7
|
+
use,
|
|
8
|
+
useRef,
|
|
9
|
+
useState,
|
|
10
|
+
type ComponentProps,
|
|
11
|
+
type KeyboardEvent as ReactKeyboardEvent,
|
|
12
|
+
type ReactNode,
|
|
13
|
+
} from 'react'
|
|
14
|
+
|
|
15
|
+
import { tv } from '../tv'
|
|
16
|
+
|
|
17
|
+
export const checkbox = tv({
|
|
18
|
+
base: 'size-5 shrink-0 rounded-mark inline-flex items-center justify-center'
|
|
19
|
+
+ ' bg-transparent ring-1 ring-inset ring-line-strong'
|
|
20
|
+
+ ' transition-state press press-shrink'
|
|
21
|
+
+ ' checked:ring-2'
|
|
22
|
+
+ ' invalid:ring-2 invalid:ring-danger'
|
|
23
|
+
+ ' focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring'
|
|
24
|
+
+ ' disabled:ring-line disabled:bg-disabled disabled:cursor-not-allowed'
|
|
25
|
+
+ ' checked:disabled:bg-control-on-disabled checked:disabled:ring-control-on-disabled',
|
|
26
|
+
|
|
27
|
+
variants: {
|
|
28
|
+
color: {
|
|
29
|
+
control: 'checked:bg-control-on checked:ring-control-on',
|
|
30
|
+
info: 'checked:bg-info checked:ring-info',
|
|
31
|
+
success: 'checked:bg-success checked:ring-success',
|
|
32
|
+
warning: 'checked:bg-warning checked:ring-warning',
|
|
33
|
+
danger: 'checked:bg-danger checked:ring-danger',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
defaultVariants: { color: 'control' },
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
interface GroupContext {
|
|
41
|
+
value: string[]
|
|
42
|
+
toggle: (item: string, next: boolean) => void
|
|
43
|
+
disabled?: boolean
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const Group = createContext<GroupContext | null>(null)
|
|
47
|
+
|
|
48
|
+
const ITEMS = '[role="checkbox"]:not([disabled])'
|
|
49
|
+
|
|
50
|
+
export interface CheckboxGroupProps {
|
|
51
|
+
value?: string[]
|
|
52
|
+
defaultValue?: string[]
|
|
53
|
+
onValueChange?: (value: string[]) => void
|
|
54
|
+
disabled?: boolean
|
|
55
|
+
className?: string
|
|
56
|
+
children?: ReactNode
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function CheckboxGroup({
|
|
60
|
+
value,
|
|
61
|
+
defaultValue,
|
|
62
|
+
onValueChange,
|
|
63
|
+
disabled,
|
|
64
|
+
className,
|
|
65
|
+
children,
|
|
66
|
+
}: CheckboxGroupProps) {
|
|
67
|
+
const [own, setOwn] = useState<string[]>(defaultValue ?? [])
|
|
68
|
+
const list = value ?? own
|
|
69
|
+
const root = useRef<HTMLDivElement>(null)
|
|
70
|
+
|
|
71
|
+
const toggle = (item: string, next: boolean) => {
|
|
72
|
+
const after = next ? [...list, item] : list.filter(one => one !== item)
|
|
73
|
+
if (value === undefined) setOwn(after)
|
|
74
|
+
onValueChange?.(after)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const onKeyDown = (e: ReactKeyboardEvent<HTMLDivElement>) => {
|
|
78
|
+
const step = e.key === 'ArrowDown' || e.key === 'ArrowRight'
|
|
79
|
+
? 1
|
|
80
|
+
: e.key === 'ArrowUp' || e.key === 'ArrowLeft' ? -1 : 0
|
|
81
|
+
if (step === 0 || !root.current) return
|
|
82
|
+
|
|
83
|
+
const items = [...root.current.querySelectorAll<HTMLElement>(ITEMS)]
|
|
84
|
+
const at = items.indexOf(document.activeElement as HTMLElement)
|
|
85
|
+
if (at === -1) return
|
|
86
|
+
|
|
87
|
+
e.preventDefault()
|
|
88
|
+
items[(at + step + items.length) % items.length]!.focus()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<Group value={{ value: list, toggle, disabled }}>
|
|
93
|
+
<div
|
|
94
|
+
ref={root}
|
|
95
|
+
role="group"
|
|
96
|
+
onKeyDown={onKeyDown}
|
|
97
|
+
className={tv({ base: 'flex flex-col gap-3' })({ className })}
|
|
98
|
+
>
|
|
99
|
+
{children}
|
|
100
|
+
</div>
|
|
101
|
+
</Group>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface CheckboxProps extends Omit<ComponentProps<typeof CheckboxPrimitive.Root>, 'checked' | 'onCheckedChange' | 'value'> {
|
|
106
|
+
value?: string
|
|
107
|
+
checked?: boolean
|
|
108
|
+
onCheckedChange?: (checked: boolean) => void
|
|
109
|
+
indeterminate?: boolean
|
|
110
|
+
onIndeterminateChange?: (indeterminate: boolean) => void
|
|
111
|
+
color?: 'control' | 'info' | 'success' | 'warning' | 'danger'
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function Checkbox({
|
|
115
|
+
value,
|
|
116
|
+
checked,
|
|
117
|
+
onCheckedChange,
|
|
118
|
+
indeterminate,
|
|
119
|
+
onIndeterminateChange,
|
|
120
|
+
color,
|
|
121
|
+
className,
|
|
122
|
+
disabled,
|
|
123
|
+
...rest
|
|
124
|
+
}: CheckboxProps) {
|
|
125
|
+
const group = use(Group)
|
|
126
|
+
const item = value ?? 'on'
|
|
127
|
+
|
|
128
|
+
const on = group ? group.value.includes(item) : checked ?? false
|
|
129
|
+
const state = indeterminate ? 'indeterminate' as const : on
|
|
130
|
+
|
|
131
|
+
const change = (next: boolean | 'indeterminate') => {
|
|
132
|
+
if (typeof next !== 'boolean') return
|
|
133
|
+
if (group) group.toggle(item, next)
|
|
134
|
+
else onCheckedChange?.(next)
|
|
135
|
+
if (indeterminate) onIndeterminateChange?.(false)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<CheckboxPrimitive.Root
|
|
140
|
+
{...rest}
|
|
141
|
+
checked={state}
|
|
142
|
+
disabled={disabled ?? group?.disabled}
|
|
143
|
+
onCheckedChange={change}
|
|
144
|
+
className={checkbox({ color, className })}
|
|
145
|
+
>
|
|
146
|
+
<CheckboxPrimitive.Indicator className="text-fg-on-control flex">
|
|
147
|
+
{indeterminate ? <Minus className="size-4" /> : <Check className="size-4" />}
|
|
148
|
+
</CheckboxPrimitive.Indicator>
|
|
149
|
+
</CheckboxPrimitive.Root>
|
|
150
|
+
)
|
|
151
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# CodeBlock
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
받기 npx @pantoia/ui add code-block
|
|
5
|
+
층 ui
|
|
6
|
+
파일 code-block/index.tsx · code-block/index.module.css · code-block/AGENTS.md
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## 부위
|
|
10
|
+
|
|
11
|
+
| 이름 | props |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `CodeBlock` | `CodeBlockProps` |
|
|
14
|
+
| `CodeBlockHeader` | — |
|
|
15
|
+
|
|
16
|
+
### `CodeBlockHeader`
|
|
17
|
+
|
|
18
|
+
| 이름 | 타입 | 필수 |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| `className` | `string` | |
|
|
21
|
+
| `children` | `ReactNode` | |
|
|
22
|
+
|
|
23
|
+
## props
|
|
24
|
+
|
|
25
|
+
### `CodeBlockFold`
|
|
26
|
+
|
|
27
|
+
| 이름 | 타입 | 필수 |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `maxLines` | `number` | 예 |
|
|
30
|
+
| `more` | `string` | 예 |
|
|
31
|
+
| `moreCount` | `string` | 예 |
|
|
32
|
+
| `less` | `string` | 예 |
|
|
33
|
+
|
|
34
|
+
### `CodeBlockProps`
|
|
35
|
+
|
|
36
|
+
| 이름 | 타입 | 필수 |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| `code` | `string` | 예 |
|
|
39
|
+
| `wrap` | `boolean` | |
|
|
40
|
+
| `numbers` | `boolean` | |
|
|
41
|
+
| `fold` | `CodeBlockFold` | |
|
|
42
|
+
| `className` | `string` | |
|
|
43
|
+
| `header` | `ReactNode` | |
|
|
44
|
+
| `line` | `(line: string, index: number) => ReactNode` | |
|
|
45
|
+
|
|
46
|
+
## 변형 — `codeBlock`
|
|
47
|
+
|
|
48
|
+
슬롯 `root` · `viewport` · `pre` · `chevron`
|
|
49
|
+
|
|
50
|
+
| 축 | 값 | 기본 |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| `folded` | `false` | — |
|
|
53
|
+
|
|
54
|
+
## 함께 받는 것
|
|
55
|
+
|
|
56
|
+
`tv`
|
|
57
|
+
|
|
58
|
+
## npm 의존
|
|
59
|
+
|
|
60
|
+
`lucide-react`
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
.code {
|
|
2
|
+
margin: 0;
|
|
3
|
+
padding: 1rem;
|
|
4
|
+
counter-reset: ln;
|
|
5
|
+
|
|
6
|
+
--code-block-keyword: var(--purple-700);
|
|
7
|
+
--code-block-name: var(--orange-800);
|
|
8
|
+
--code-block-string: var(--green-800);
|
|
9
|
+
--code-block-comment: var(--gray-500);
|
|
10
|
+
--code-block-value: var(--blue-700);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.code-keyword { color: var(--code-block-keyword); }
|
|
14
|
+
.code-name { color: var(--code-block-name); }
|
|
15
|
+
.code-string { color: var(--code-block-string); }
|
|
16
|
+
.code-comment { color: var(--code-block-comment); }
|
|
17
|
+
.code-value { color: var(--code-block-value); }
|
|
18
|
+
.line { display: block; white-space: pre; min-height: 1lh; }
|
|
19
|
+
.wrap .line { white-space: pre-wrap; word-break: break-all; }
|
|
20
|
+
|
|
21
|
+
.numbers { padding-left: 0; }
|
|
22
|
+
.numbers .line::before {
|
|
23
|
+
counter-increment: ln;
|
|
24
|
+
content: counter(ln);
|
|
25
|
+
display: inline-block;
|
|
26
|
+
width: 3.4em;
|
|
27
|
+
padding-right: 0.9em;
|
|
28
|
+
text-align: right;
|
|
29
|
+
color: var(--color-fg-subtle);
|
|
30
|
+
user-select: none;
|
|
31
|
+
pointer-events: none;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.numbers:not(.wrap) .line::before {
|
|
35
|
+
position: sticky;
|
|
36
|
+
left: 0;
|
|
37
|
+
background: var(--color-well);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.numbers.wrap .line { padding-left: 3.4em; text-indent: -3.4em; }
|
|
41
|
+
|
|
42
|
+
.clip {
|
|
43
|
+
max-height: calc(var(--code-block-max-lines) * 1lh + 2rem);
|
|
44
|
+
overflow-y: hidden;
|
|
45
|
+
mask-image: linear-gradient(to bottom, #000 calc(100% - 2.5rem), transparent);
|
|
46
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { ChevronDown } from 'lucide-react'
|
|
4
|
+
import { useCallback, useState, useSyncExternalStore, type CSSProperties, type ReactNode } from 'react'
|
|
5
|
+
|
|
6
|
+
import { tv } from '../tv'
|
|
7
|
+
|
|
8
|
+
import s from './index.module.css'
|
|
9
|
+
|
|
10
|
+
export const codeToken = {
|
|
11
|
+
keyword: s['code-keyword'],
|
|
12
|
+
name: s['code-name'],
|
|
13
|
+
string: s['code-string'],
|
|
14
|
+
comment: s['code-comment'],
|
|
15
|
+
value: s['code-value'],
|
|
16
|
+
none: '',
|
|
17
|
+
} as const
|
|
18
|
+
|
|
19
|
+
export interface CodeBlockFold {
|
|
20
|
+
maxLines: number
|
|
21
|
+
more: string
|
|
22
|
+
moreCount: string
|
|
23
|
+
less: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const codeBlock = tv({
|
|
27
|
+
slots: {
|
|
28
|
+
root: 'bg-well rounded-well text-value font-mono overflow-hidden',
|
|
29
|
+
viewport: 'scroll-slim overflow-auto'
|
|
30
|
+
+ ' focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring',
|
|
31
|
+
pre: '',
|
|
32
|
+
chevron: 'size-4 transition-transform motion-reduce:transition-none',
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
variants: {
|
|
36
|
+
folded: { false: { chevron: 'rotate-180' } },
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
export interface CodeBlockProps {
|
|
41
|
+
code: string
|
|
42
|
+
wrap?: boolean
|
|
43
|
+
numbers?: boolean
|
|
44
|
+
fold?: CodeBlockFold
|
|
45
|
+
className?: string
|
|
46
|
+
header?: ReactNode
|
|
47
|
+
line?: (line: string, index: number) => ReactNode
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function CodeBlock({
|
|
51
|
+
code,
|
|
52
|
+
wrap = false,
|
|
53
|
+
numbers = false,
|
|
54
|
+
fold,
|
|
55
|
+
className,
|
|
56
|
+
header,
|
|
57
|
+
line,
|
|
58
|
+
}: CodeBlockProps) {
|
|
59
|
+
const [folded, setFolded] = useState(true)
|
|
60
|
+
const [box, setBox] = useState<HTMLDivElement | null>(null)
|
|
61
|
+
|
|
62
|
+
const subscribe = useCallback((onChange: () => void) => {
|
|
63
|
+
if (!box) return () => {}
|
|
64
|
+
const observer = new ResizeObserver(onChange)
|
|
65
|
+
observer.observe(box)
|
|
66
|
+
if (box.firstElementChild) observer.observe(box.firstElementChild)
|
|
67
|
+
return () => observer.disconnect()
|
|
68
|
+
}, [box])
|
|
69
|
+
|
|
70
|
+
const overflows = useSyncExternalStore(
|
|
71
|
+
subscribe,
|
|
72
|
+
() => box !== null && (box.scrollWidth > box.clientWidth || box.scrollHeight > box.clientHeight),
|
|
73
|
+
() => false,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
const lines = code.split('\n')
|
|
77
|
+
const foldable = !!fold && lines.length > fold.maxLines
|
|
78
|
+
const rest = lines.length - (fold?.maxLines ?? 0)
|
|
79
|
+
|
|
80
|
+
const ui = codeBlock({ folded })
|
|
81
|
+
|
|
82
|
+
const pre = [s.code, wrap ? s.wrap : '', numbers ? s.numbers : '', ui.pre()]
|
|
83
|
+
.filter(Boolean).join(' ')
|
|
84
|
+
|
|
85
|
+
const viewport = [ui.viewport(), foldable && folded ? s.clip : '']
|
|
86
|
+
.filter(Boolean).join(' ')
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<div className={ui.root({ className })}>
|
|
90
|
+
{header}
|
|
91
|
+
|
|
92
|
+
<div
|
|
93
|
+
ref={setBox}
|
|
94
|
+
tabIndex={overflows ? 0 : undefined}
|
|
95
|
+
className={viewport}
|
|
96
|
+
style={{ '--code-block-max-lines': fold?.maxLines } as CSSProperties}
|
|
97
|
+
><pre className={pre}>{lines.map((text, i) => (
|
|
98
|
+
<span key={i} className={s.line}>{line ? line(text, i) : text}</span>
|
|
99
|
+
))}</pre></div>
|
|
100
|
+
|
|
101
|
+
{foldable && (
|
|
102
|
+
<button
|
|
103
|
+
type="button"
|
|
104
|
+
className="border-line text-caption text-fg-muted flex w-full items-center justify-center
|
|
105
|
+
gap-tight border-t py-2 transition-state press
|
|
106
|
+
enabled:hover:bg-hover
|
|
107
|
+
focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring"
|
|
108
|
+
onClick={() => setFolded(!folded)}
|
|
109
|
+
>
|
|
110
|
+
<ChevronDown className={ui.chevron()} />
|
|
111
|
+
{folded ? (wrap ? fold.more : fold.moreCount.replace('{count}', String(rest))) : fold.less}
|
|
112
|
+
</button>
|
|
113
|
+
)}
|
|
114
|
+
</div>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function CodeBlockHeader({ className, children }: { className?: string, children?: ReactNode }) {
|
|
119
|
+
return (
|
|
120
|
+
<div
|
|
121
|
+
className={tv({ base: 'border-line text-caption text-fg-muted'
|
|
122
|
+
+ ' flex items-center gap-stack border-b px-4 py-2' })({ className })}
|
|
123
|
+
>
|
|
124
|
+
{children}
|
|
125
|
+
</div>
|
|
126
|
+
)
|
|
127
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Combobox
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
받기 npx @pantoia/ui add combobox
|
|
5
|
+
층 ui
|
|
6
|
+
파일 combobox/index.tsx · combobox/AGENTS.md
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## 부위
|
|
10
|
+
|
|
11
|
+
| 이름 | props |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `Combobox` | `ComboboxProps` |
|
|
14
|
+
| `ComboboxAnchor` | `ComboboxAnchorProps` |
|
|
15
|
+
| `ComboboxInput` | `ComboboxInputProps` |
|
|
16
|
+
| `ComboboxTrigger` | — |
|
|
17
|
+
| `ComboboxCancel` | — |
|
|
18
|
+
| `ComboboxContent` | `ComboboxContentProps` |
|
|
19
|
+
| `ComboboxViewport` | — |
|
|
20
|
+
| `ComboboxItem` | `ComboboxItemProps` |
|
|
21
|
+
| `ComboboxItemText` | — |
|
|
22
|
+
| `ComboboxItemDescription` | — |
|
|
23
|
+
| `ComboboxGroup` | — |
|
|
24
|
+
| `ComboboxEmpty` | — |
|
|
25
|
+
| `ComboboxSeparator` | — |
|
|
26
|
+
|
|
27
|
+
### `ComboboxTrigger`
|
|
28
|
+
|
|
29
|
+
| 이름 | 타입 | 필수 |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| `label` | `string` | |
|
|
32
|
+
| `disabled` | `boolean` | |
|
|
33
|
+
| `className` | `string` | |
|
|
34
|
+
|
|
35
|
+
### `ComboboxCancel`
|
|
36
|
+
|
|
37
|
+
| 이름 | 타입 | 필수 |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `label` | `string` | |
|
|
40
|
+
| `className` | `string` | |
|
|
41
|
+
|
|
42
|
+
### `ComboboxViewport`
|
|
43
|
+
|
|
44
|
+
| 이름 | 타입 | 필수 |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| `label` | `string` | |
|
|
47
|
+
| `className` | `string` | |
|
|
48
|
+
| `children` | `ReactNode` | |
|
|
49
|
+
|
|
50
|
+
### `ComboboxItemText`
|
|
51
|
+
|
|
52
|
+
| 이름 | 타입 | 필수 |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| `className` | `string` | |
|
|
55
|
+
| `children` | `ReactNode` | |
|
|
56
|
+
|
|
57
|
+
### `ComboboxItemDescription`
|
|
58
|
+
|
|
59
|
+
| 이름 | 타입 | 필수 |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| `className` | `string` | |
|
|
62
|
+
| `children` | `ReactNode` | |
|
|
63
|
+
|
|
64
|
+
### `ComboboxGroup`
|
|
65
|
+
|
|
66
|
+
| 이름 | 타입 | 필수 |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| `label` | `string` | |
|
|
69
|
+
| `className` | `string` | |
|
|
70
|
+
| `children` | `ReactNode` | |
|
|
71
|
+
|
|
72
|
+
### `ComboboxEmpty`
|
|
73
|
+
|
|
74
|
+
| 이름 | 타입 | 필수 |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `className` | `string` | |
|
|
77
|
+
| `children` | `ReactNode` | |
|
|
78
|
+
|
|
79
|
+
### `ComboboxSeparator`
|
|
80
|
+
|
|
81
|
+
| 이름 | 타입 | 필수 |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| `className` | `string` | |
|
|
84
|
+
|
|
85
|
+
## 도우미
|
|
86
|
+
|
|
87
|
+
`contains(text: string, search: string)`
|
|
88
|
+
|
|
89
|
+
## props
|
|
90
|
+
|
|
91
|
+
### `ComboboxProps`
|
|
92
|
+
|
|
93
|
+
| 이름 | 타입 | 필수 |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| `value` | `string` | |
|
|
96
|
+
| `defaultValue` | `string` | |
|
|
97
|
+
| `onValueChange` | `(value: string) => void` | |
|
|
98
|
+
| `open` | `boolean` | |
|
|
99
|
+
| `defaultOpen` | `boolean` | |
|
|
100
|
+
| `onOpenChange` | `(open: boolean) => void` | |
|
|
101
|
+
| `ignoreFilter` | `boolean` | |
|
|
102
|
+
| `className` | `string` | |
|
|
103
|
+
| `children` | `ReactNode` | |
|
|
104
|
+
|
|
105
|
+
### `ComboboxAnchorProps`
|
|
106
|
+
|
|
107
|
+
| 이름 | 타입 | 필수 |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| `surface` | `'bare' \| 'well'` | |
|
|
110
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | |
|
|
111
|
+
| `className` | `string` | |
|
|
112
|
+
| `children` | `ReactNode` | |
|
|
113
|
+
|
|
114
|
+
### `ComboboxInputProps`
|
|
115
|
+
|
|
116
|
+
`Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange'>` 를 확장한다.
|
|
117
|
+
|
|
118
|
+
| 이름 | 타입 | 필수 |
|
|
119
|
+
|---|---|---|
|
|
120
|
+
| `value` | `string` | |
|
|
121
|
+
| `onValueChange` | `(value: string) => void` | |
|
|
122
|
+
|
|
123
|
+
### `ComboboxContentProps`
|
|
124
|
+
|
|
125
|
+
`Omit<ComponentProps<typeof PopoverPrimitive.Content>, 'align' | 'sideOffset' | 'onOpenAutoFocus'>`
|
|
126
|
+
|
|
127
|
+
### `ComboboxItemProps`
|
|
128
|
+
|
|
129
|
+
| 이름 | 타입 | 필수 |
|
|
130
|
+
|---|---|---|
|
|
131
|
+
| `value` | `string` | 예 |
|
|
132
|
+
| `textValue` | `string` | |
|
|
133
|
+
| `disabled` | `boolean` | |
|
|
134
|
+
| `className` | `string` | |
|
|
135
|
+
| `children` | `ReactNode` | |
|
|
136
|
+
|
|
137
|
+
## 변형 — `comboboxAnchor`
|
|
138
|
+
|
|
139
|
+
| 축 | 값 | 기본 |
|
|
140
|
+
|---|---|---|
|
|
141
|
+
| `surface` | `bare` · `well` | `bare` |
|
|
142
|
+
| `size` | `sm` · `md` · `lg` | `md` |
|
|
143
|
+
|
|
144
|
+
## 함께 받는 것
|
|
145
|
+
|
|
146
|
+
`tv`
|
|
147
|
+
|
|
148
|
+
## npm 의존
|
|
149
|
+
|
|
150
|
+
`lucide-react` · `radix-ui`
|