@pantoia/ui 0.1.0 → 0.2.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 +5 -5
- package/accordion/index.tsx +17 -9
- package/alert-dialog/AGENTS.md +7 -7
- package/alert-dialog/index.tsx +20 -10
- package/badge/AGENTS.md +5 -5
- package/badge/index.tsx +7 -7
- package/button/AGENTS.md +7 -7
- package/button/index.tsx +36 -36
- package/callout/AGENTS.md +4 -5
- package/callout/index.tsx +13 -14
- package/checkbox/AGENTS.md +5 -5
- package/checkbox/index.tsx +15 -13
- package/combobox/AGENTS.md +13 -13
- package/combobox/index.tsx +82 -38
- package/command-palette/AGENTS.md +3 -2
- package/command-palette/index.tsx +4 -2
- package/dialog/AGENTS.md +6 -6
- package/dialog/index.tsx +11 -5
- package/dist/index.js +63 -34
- package/drawer/AGENTS.md +5 -5
- package/drawer/index.tsx +8 -4
- package/dropdown-menu/AGENTS.md +12 -12
- package/dropdown-menu/index.tsx +23 -11
- package/field/AGENTS.md +8 -8
- package/field/index.tsx +27 -9
- package/input/AGENTS.md +4 -4
- package/input/index.tsx +8 -8
- package/kbd/AGENTS.md +1 -1
- package/kbd/index.tsx +6 -2
- package/label/AGENTS.md +1 -1
- package/label/index.tsx +3 -1
- package/number-field/AGENTS.md +6 -6
- package/number-field/index.tsx +22 -16
- package/package.json +3 -6
- package/popover/AGENTS.md +4 -4
- package/popover/index.tsx +14 -4
- package/press.css +7 -7
- package/radio-group/AGENTS.md +8 -8
- package/radio-group/index.tsx +20 -16
- package/registry.json +3 -51
- package/select/AGENTS.md +10 -10
- package/select/index.tsx +41 -23
- package/styles.css +291 -139
- package/switch/AGENTS.md +4 -4
- package/switch/index.tsx +13 -13
- package/table/AGENTS.md +9 -9
- package/table/index.tsx +17 -7
- package/tabs/AGENTS.md +4 -4
- package/tabs/index.module.css +1 -1
- package/tabs/index.tsx +17 -9
- package/textarea/AGENTS.md +4 -4
- package/textarea/index.tsx +8 -8
- package/toast/AGENTS.md +5 -13
- package/toast/index.tsx +31 -37
- package/toggle/AGENTS.md +4 -4
- package/toggle/index.tsx +10 -9
- package/tooltip/AGENTS.md +2 -2
- package/tooltip/index.tsx +14 -4
- package/code-block/AGENTS.md +0 -60
- package/code-block/index.module.css +0 -46
- package/code-block/index.tsx +0 -127
- package/copy-button/AGENTS.md +0 -43
- package/copy-button/index.tsx +0 -119
package/dist/index.js
CHANGED
|
@@ -6,12 +6,12 @@ import { createInterface } from 'node:readline/promises';
|
|
|
6
6
|
const HERE = join(import.meta.dirname, '..');
|
|
7
7
|
const CONFIG = 'components.json';
|
|
8
8
|
const DEFAULT_PATH = 'components/ui';
|
|
9
|
-
const USAGE = `pantoia-ui
|
|
9
|
+
const USAGE = `pantoia-ui <명령어>
|
|
10
10
|
|
|
11
|
-
init [--path
|
|
12
|
-
add <이름...> [--force]
|
|
13
|
-
list
|
|
14
|
-
diff [이름]
|
|
11
|
+
init [--path <경로>] 컴포넌트를 저장할 위치를 정해요
|
|
12
|
+
add <이름...> [--force] 컴포넌트를 추가해요
|
|
13
|
+
list 추가할 수 있는 컴포넌트를 확인해요
|
|
14
|
+
diff [이름] 추가한 파일과 최신 파일을 비교해요`;
|
|
15
15
|
function fail(message) {
|
|
16
16
|
console.error(message);
|
|
17
17
|
exit(1);
|
|
@@ -27,7 +27,7 @@ function registry() {
|
|
|
27
27
|
function find(name) {
|
|
28
28
|
const item = registry().find(candidate => candidate.name === name);
|
|
29
29
|
if (!item)
|
|
30
|
-
fail(
|
|
30
|
+
fail(`목록에 없는 컴포넌트예요: ${name}\npantoia-ui list 로 이름을 확인하세요.`);
|
|
31
31
|
return item;
|
|
32
32
|
}
|
|
33
33
|
function configPath() {
|
|
@@ -59,6 +59,14 @@ function resolve(names) {
|
|
|
59
59
|
}
|
|
60
60
|
return [...seen.values()];
|
|
61
61
|
}
|
|
62
|
+
async function ask(question, fallback) {
|
|
63
|
+
if (!stdin.isTTY)
|
|
64
|
+
return fallback;
|
|
65
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
66
|
+
const answer = await rl.question(`${question} (${fallback}) `);
|
|
67
|
+
rl.close();
|
|
68
|
+
return answer.trim() || fallback;
|
|
69
|
+
}
|
|
62
70
|
async function confirm(question) {
|
|
63
71
|
if (!stdin.isTTY)
|
|
64
72
|
return false;
|
|
@@ -67,59 +75,78 @@ async function confirm(question) {
|
|
|
67
75
|
rl.close();
|
|
68
76
|
return answer.trim().toLowerCase() === 'y';
|
|
69
77
|
}
|
|
70
|
-
async function copy(item,
|
|
78
|
+
async function copy(item, base, force) {
|
|
79
|
+
let skipped = false;
|
|
71
80
|
for (const file of item.files ?? []) {
|
|
72
81
|
const source = readFileSync(join(HERE, file.path), 'utf8');
|
|
73
|
-
const
|
|
82
|
+
const shown = join(base, file.target ?? file.path);
|
|
83
|
+
const target = join(cwd(), shown);
|
|
74
84
|
if (existsSync(target) && readFileSync(target, 'utf8') !== source) {
|
|
75
|
-
if (!force && !await confirm(`${
|
|
76
|
-
console.log(`
|
|
85
|
+
if (!force && !await confirm(`${shown} 파일이 이미 있고 내용이 달라요. 덮어쓸까요?`)) {
|
|
86
|
+
console.log(` ${shown} (건너뜀)`);
|
|
87
|
+
skipped = true;
|
|
77
88
|
continue;
|
|
78
89
|
}
|
|
79
90
|
}
|
|
80
91
|
mkdirSync(dirname(target), { recursive: true });
|
|
81
92
|
writeFileSync(target, source);
|
|
82
|
-
console.log(`
|
|
93
|
+
console.log(` ${shown}`);
|
|
83
94
|
}
|
|
95
|
+
return skipped;
|
|
84
96
|
}
|
|
85
97
|
async function commandInit(args) {
|
|
86
98
|
const existing = readConfig();
|
|
87
99
|
if (existing)
|
|
88
|
-
fail(`${CONFIG} 이 이미
|
|
89
|
-
const path = option(args, '--path')
|
|
100
|
+
fail(`${CONFIG} 이 이미 있어요.`);
|
|
101
|
+
const path = option(args, '--path')
|
|
102
|
+
?? await ask('컴포넌트를 저장할 위치를 입력하세요.', DEFAULT_PATH);
|
|
90
103
|
writeConfig({ path, components: {} });
|
|
91
|
-
console.log(`${CONFIG} 을
|
|
92
|
-
console.log('
|
|
104
|
+
console.log(`${CONFIG} 을 만들었어요. 컴포넌트는 ${path} 에 저장돼요.`);
|
|
105
|
+
console.log('전역 CSS 에 아래 한 줄을 추가하세요.');
|
|
106
|
+
console.log('');
|
|
107
|
+
console.log(' @import "@pantoia/ui/styles.css";');
|
|
93
108
|
}
|
|
94
109
|
async function commandAdd(args) {
|
|
95
110
|
const names = args.filter(arg => !arg.startsWith('--'));
|
|
96
111
|
if (names.length === 0)
|
|
97
|
-
fail('
|
|
112
|
+
fail('추가할 컴포넌트 이름을 입력하세요. 예) pantoia-ui add button');
|
|
98
113
|
const force = args.includes('--force');
|
|
99
|
-
|
|
100
|
-
if (!config)
|
|
101
|
-
|
|
102
|
-
console.log(`${CONFIG} 이 없어 만들었다 — 소스는 ${config.path} 아래로 간다`);
|
|
103
|
-
}
|
|
114
|
+
const config = readConfig();
|
|
115
|
+
if (!config)
|
|
116
|
+
fail(`${CONFIG} 이 없어요. 먼저 pantoia-ui init 을 실행하세요.`);
|
|
104
117
|
const items = resolve(names);
|
|
105
|
-
const
|
|
118
|
+
const asked = items.filter(item => names.includes(item.name));
|
|
106
119
|
const extra = items.filter(item => !names.includes(item.name));
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
for (const item of
|
|
120
|
+
let skipped = false;
|
|
121
|
+
let first = true;
|
|
122
|
+
for (const item of asked) {
|
|
123
|
+
if (!first)
|
|
124
|
+
console.log('');
|
|
125
|
+
first = false;
|
|
110
126
|
console.log(item.name);
|
|
111
|
-
await copy(item,
|
|
127
|
+
skipped = await copy(item, config.path, force) || skipped;
|
|
112
128
|
config.components[item.name] = version();
|
|
113
129
|
}
|
|
130
|
+
if (extra.length > 0) {
|
|
131
|
+
console.log('');
|
|
132
|
+
console.log('함께 필요한 파일도 추가했어요.');
|
|
133
|
+
for (const item of extra) {
|
|
134
|
+
skipped = await copy(item, config.path, force) || skipped;
|
|
135
|
+
config.components[item.name] = version();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
114
138
|
writeConfig(config);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
console.log(
|
|
139
|
+
if (skipped) {
|
|
140
|
+
console.log('');
|
|
141
|
+
console.log('덮어쓰려면 --force 옵션을 붙이세요.');
|
|
142
|
+
}
|
|
118
143
|
}
|
|
119
144
|
function commandList() {
|
|
145
|
+
console.log(`${'이름'.padEnd(16)}${'종류'.padEnd(7)}같이 추가되는 컴포넌트`);
|
|
120
146
|
for (const item of registry()) {
|
|
121
|
-
const
|
|
122
|
-
|
|
147
|
+
const layer = item.meta?.layer ?? (item.type === 'registry:lib' ? 'lib' : '');
|
|
148
|
+
const together = (item.registryDependencies ?? []).join(', ');
|
|
149
|
+
console.log(`${item.name.padEnd(18)}${layer.padEnd(9)}${together}`.trimEnd());
|
|
123
150
|
}
|
|
124
151
|
}
|
|
125
152
|
function lcs(a, b) {
|
|
@@ -154,7 +181,7 @@ function difference(mine, theirs) {
|
|
|
154
181
|
async function commandDiff(args) {
|
|
155
182
|
const config = readConfig();
|
|
156
183
|
if (!config)
|
|
157
|
-
fail(`${CONFIG} 이
|
|
184
|
+
fail(`${CONFIG} 이 없어요. 먼저 pantoia-ui add 로 컴포넌트를 추가하세요.`);
|
|
158
185
|
const names = args.filter(arg => !arg.startsWith('--'));
|
|
159
186
|
const wanted = names.length > 0 ? names : Object.keys(config.components);
|
|
160
187
|
let changed = 0;
|
|
@@ -167,14 +194,16 @@ async function commandDiff(args) {
|
|
|
167
194
|
const theirs = readFileSync(join(HERE, file.path), 'utf8');
|
|
168
195
|
if (mine === theirs)
|
|
169
196
|
continue;
|
|
197
|
+
if (changed === 0)
|
|
198
|
+
console.log('- 는 내 파일, + 는 최신 파일이에요.');
|
|
170
199
|
changed++;
|
|
171
|
-
console.log(`\n${file.path}`);
|
|
200
|
+
console.log(`\n${join(config.path, file.target ?? file.path)}`);
|
|
172
201
|
for (const line of difference(mine.split('\n'), theirs.split('\n')))
|
|
173
202
|
console.log(line);
|
|
174
203
|
}
|
|
175
204
|
}
|
|
176
205
|
if (changed === 0)
|
|
177
|
-
console.log('
|
|
206
|
+
console.log('달라진 파일이 없어요.');
|
|
178
207
|
}
|
|
179
208
|
const [command, ...args] = argv.slice(2);
|
|
180
209
|
if (command === 'init')
|
package/drawer/AGENTS.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
```
|
|
4
4
|
받기 npx @pantoia/ui add drawer
|
|
5
5
|
층 ui
|
|
6
|
-
파일 drawer/index.tsx
|
|
6
|
+
파일 drawer/index.tsx, drawer/index.module.css, drawer/AGENTS.md
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
## 부위
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
| 이름 | props |
|
|
12
12
|
|---|---|
|
|
13
13
|
| `Drawer` | `DrawerProps` |
|
|
14
|
-
| `DrawerTrigger` |
|
|
15
|
-
| `DrawerClose` |
|
|
14
|
+
| `DrawerTrigger` | - |
|
|
15
|
+
| `DrawerClose` | - |
|
|
16
16
|
| `DrawerContent` | `ComponentProps<typeof Dialog.Content>` |
|
|
17
17
|
| `DrawerHeader` | `DrawerHeaderProps` |
|
|
18
18
|
| `DrawerTitle` | `ComponentProps<typeof Dialog.Title>` |
|
|
@@ -51,11 +51,11 @@
|
|
|
51
51
|
| `className` | `string` | |
|
|
52
52
|
| `children` | `ReactNode` | |
|
|
53
53
|
|
|
54
|
-
## 변형
|
|
54
|
+
## 변형 - `drawerContent`
|
|
55
55
|
|
|
56
56
|
| 축 | 값 | 기본 |
|
|
57
57
|
|---|---|---|
|
|
58
|
-
| `side` | `left
|
|
58
|
+
| `side` | `left`, `right` | - |
|
|
59
59
|
|
|
60
60
|
## 함께 받는 것
|
|
61
61
|
|
package/drawer/index.tsx
CHANGED
|
@@ -117,7 +117,7 @@ export function DrawerClose({ children }: { children: ReactNode }) {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
export const drawerContent = tv({
|
|
120
|
-
base: '
|
|
120
|
+
base: 'sheet text-foreground z-overlay fixed inset-y-0'
|
|
121
121
|
+ ' flex w-full flex-col gap-stack overflow-y-auto p-4 sm:w-120'
|
|
122
122
|
+ ' focus-visible:outline-none',
|
|
123
123
|
variants: {
|
|
@@ -137,7 +137,7 @@ export function DrawerContent({
|
|
|
137
137
|
|
|
138
138
|
return (
|
|
139
139
|
<Dialog.Portal>
|
|
140
|
-
<Dialog.Overlay className="bg-overlay z-overlay fixed inset-0 open:animate-fade-in closed:animate-fade-out" />
|
|
140
|
+
<Dialog.Overlay className="bg-background-overlay z-overlay fixed inset-0 open:animate-fade-in closed:animate-fade-out" />
|
|
141
141
|
|
|
142
142
|
<Dialog.Content
|
|
143
143
|
{...rest}
|
|
@@ -187,12 +187,16 @@ export function DrawerHeader({ lead, trail, className, children }: DrawerHeaderP
|
|
|
187
187
|
)
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
const drawerTitle = tv({ base: 'text-section min-w-0' })
|
|
191
|
+
|
|
190
192
|
export function DrawerTitle({ className, ...rest }: ComponentProps<typeof Dialog.Title>) {
|
|
191
|
-
return <Dialog.Title {...rest} className={
|
|
193
|
+
return <Dialog.Title {...rest} className={drawerTitle({ className })} />
|
|
192
194
|
}
|
|
193
195
|
|
|
196
|
+
const drawerDescription = tv({ base: 'text-prose text-foreground-muted' })
|
|
197
|
+
|
|
194
198
|
export function DrawerDescription({ className, ...rest }: ComponentProps<typeof Dialog.Description>) {
|
|
195
199
|
return (
|
|
196
|
-
<Dialog.Description {...rest} className={
|
|
200
|
+
<Dialog.Description {...rest} className={drawerDescription({ className })} />
|
|
197
201
|
)
|
|
198
202
|
}
|
package/dropdown-menu/AGENTS.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
```
|
|
4
4
|
받기 npx @pantoia/ui add dropdown-menu
|
|
5
5
|
층 ui
|
|
6
|
-
파일 dropdown-menu/index.tsx
|
|
6
|
+
파일 dropdown-menu/index.tsx, dropdown-menu/AGENTS.md
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
## 부위
|
|
@@ -11,16 +11,16 @@
|
|
|
11
11
|
| 이름 | props |
|
|
12
12
|
|---|---|
|
|
13
13
|
| `DropdownMenu` | `DropdownMenuProps` |
|
|
14
|
-
| `DropdownMenuTrigger` |
|
|
14
|
+
| `DropdownMenuTrigger` | - |
|
|
15
15
|
| `DropdownMenuContent` | `DropdownMenuContentProps` |
|
|
16
|
-
| `DropdownMenuGroup` |
|
|
16
|
+
| `DropdownMenuGroup` | - |
|
|
17
17
|
| `DropdownMenuItem` | `DropdownMenuItemProps` |
|
|
18
|
-
| `DropdownMenuCheckboxItem` |
|
|
19
|
-
| `DropdownMenuRadioGroup` |
|
|
20
|
-
| `DropdownMenuRadioItem` |
|
|
21
|
-
| `DropdownMenuSeparator` |
|
|
22
|
-
| `DropdownMenuSub` |
|
|
23
|
-
| `DropdownMenuSubTrigger` |
|
|
18
|
+
| `DropdownMenuCheckboxItem` | - |
|
|
19
|
+
| `DropdownMenuRadioGroup` | - |
|
|
20
|
+
| `DropdownMenuRadioItem` | - |
|
|
21
|
+
| `DropdownMenuSeparator` | - |
|
|
22
|
+
| `DropdownMenuSub` | - |
|
|
23
|
+
| `DropdownMenuSubTrigger` | - |
|
|
24
24
|
| `DropdownMenuSubContent` | `ComponentProps<typeof MenuPrimitive.SubContent>` |
|
|
25
25
|
|
|
26
26
|
### `DropdownMenuTrigger`
|
|
@@ -118,11 +118,11 @@
|
|
|
118
118
|
| `lead` | `ReactNode` | |
|
|
119
119
|
| `trail` | `ReactNode` | |
|
|
120
120
|
|
|
121
|
-
## 변형
|
|
121
|
+
## 변형 - `menuItem`
|
|
122
122
|
|
|
123
123
|
| 축 | 값 | 기본 |
|
|
124
124
|
|---|---|---|
|
|
125
|
-
| `color` | `neutral
|
|
125
|
+
| `color` | `neutral`, `danger` | `neutral` |
|
|
126
126
|
|
|
127
127
|
## 함께 받는 것
|
|
128
128
|
|
|
@@ -130,4 +130,4 @@
|
|
|
130
130
|
|
|
131
131
|
## npm 의존
|
|
132
132
|
|
|
133
|
-
`lucide-react
|
|
133
|
+
`lucide-react`, `radix-ui`
|
package/dropdown-menu/index.tsx
CHANGED
|
@@ -14,15 +14,15 @@ export const menuItem = tv({
|
|
|
14
14
|
|
|
15
15
|
variants: {
|
|
16
16
|
color: {
|
|
17
|
-
neutral: 'text-
|
|
18
|
-
danger: 'text-danger data-highlighted:bg-danger-weak data-disabled:text-danger/40',
|
|
17
|
+
neutral: 'text-foreground data-highlighted:bg-bg-neutral-hover data-disabled:text-foreground-disabled',
|
|
18
|
+
danger: 'text-fg-danger data-highlighted:bg-bg-danger-weak data-disabled:text-fg-danger/40',
|
|
19
19
|
},
|
|
20
20
|
},
|
|
21
21
|
|
|
22
22
|
defaultVariants: { color: 'neutral' },
|
|
23
23
|
})
|
|
24
24
|
|
|
25
|
-
const PANEL = 'menu
|
|
25
|
+
const PANEL = 'menu float text-foreground rounded-float z-overlay'
|
|
26
26
|
+ ' flex min-w-44 flex-col gap-px p-1'
|
|
27
27
|
+ ' open:animate-enter closed:animate-exit'
|
|
28
28
|
+ ' focus-visible:outline-none'
|
|
@@ -51,6 +51,8 @@ export interface DropdownMenuContentProps extends Omit<ComponentProps<typeof Men
|
|
|
51
51
|
arrow?: boolean
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
const menuPanel = tv({ base: PANEL })
|
|
55
|
+
|
|
54
56
|
export function DropdownMenuContent({
|
|
55
57
|
align = 'start',
|
|
56
58
|
arrow = true,
|
|
@@ -65,25 +67,33 @@ export function DropdownMenuContent({
|
|
|
65
67
|
align={align}
|
|
66
68
|
sideOffset={8}
|
|
67
69
|
arrowPadding={16}
|
|
68
|
-
className={
|
|
70
|
+
className={menuPanel({ className })}
|
|
69
71
|
>
|
|
70
72
|
{children}
|
|
71
73
|
|
|
72
|
-
{arrow &&
|
|
74
|
+
{arrow && (
|
|
75
|
+
<MenuPrimitive.Arrow asChild width={12} height={6}>
|
|
76
|
+
<svg viewBox="0 0 12 6" preserveAspectRatio="none" className="fill-background-panel -translate-y-px">
|
|
77
|
+
<path d="M0 0L4.58579 4.58579C5.36683 5.36683 6.63316 5.36684 7.41421 4.58579L12 0" className="stroke-line stroke-1" />
|
|
78
|
+
</svg>
|
|
79
|
+
</MenuPrimitive.Arrow>
|
|
80
|
+
)}
|
|
73
81
|
</MenuPrimitive.Content>
|
|
74
82
|
</MenuPrimitive.Portal>
|
|
75
83
|
)
|
|
76
84
|
}
|
|
77
85
|
|
|
86
|
+
const menuGroup = tv({ base: 'flex flex-col gap-px' })
|
|
87
|
+
|
|
78
88
|
export function DropdownMenuGroup({ label, className, children }: {
|
|
79
89
|
label?: string
|
|
80
90
|
className?: string
|
|
81
91
|
children?: ReactNode
|
|
82
92
|
}) {
|
|
83
93
|
return (
|
|
84
|
-
<MenuPrimitive.Group className={
|
|
94
|
+
<MenuPrimitive.Group className={menuGroup({ className })}>
|
|
85
95
|
{label && (
|
|
86
|
-
<MenuPrimitive.Label className="text-caption text-
|
|
96
|
+
<MenuPrimitive.Label className="text-caption text-foreground-subtle px-2 pb-1 pt-2">
|
|
87
97
|
{label}
|
|
88
98
|
</MenuPrimitive.Label>
|
|
89
99
|
)}
|
|
@@ -161,7 +171,7 @@ export function DropdownMenuRadioItem({ value, disabled, className, children }:
|
|
|
161
171
|
<MenuPrimitive.RadioItem value={value} disabled={disabled} className={menuItem({ className })}>
|
|
162
172
|
<span className="menu-lead grid size-4 shrink-0 place-items-center">
|
|
163
173
|
<MenuPrimitive.ItemIndicator>
|
|
164
|
-
<span className="bg-
|
|
174
|
+
<span className="bg-bg-neutral-solid in-disabled:bg-bg-neutral-solid-checked-disabled size-2 rounded-full" />
|
|
165
175
|
</MenuPrimitive.ItemIndicator>
|
|
166
176
|
</span>
|
|
167
177
|
|
|
@@ -170,8 +180,10 @@ export function DropdownMenuRadioItem({ value, disabled, className, children }:
|
|
|
170
180
|
)
|
|
171
181
|
}
|
|
172
182
|
|
|
183
|
+
const menuSeparator = tv({ base: 'bg-line -mx-1 my-1 h-px' })
|
|
184
|
+
|
|
173
185
|
export function DropdownMenuSeparator({ className }: { className?: string }) {
|
|
174
|
-
return <MenuPrimitive.Separator className={
|
|
186
|
+
return <MenuPrimitive.Separator className={menuSeparator({ className })} />
|
|
175
187
|
}
|
|
176
188
|
|
|
177
189
|
export function DropdownMenuSub({ open, defaultOpen, onOpenChange, children }: {
|
|
@@ -203,7 +215,7 @@ export function DropdownMenuSubTrigger({ disabled, lead, className, children }:
|
|
|
203
215
|
|
|
204
216
|
<span className="min-w-0 grow">{children}</span>
|
|
205
217
|
|
|
206
|
-
<ChevronRight className="text-
|
|
218
|
+
<ChevronRight className="text-foreground-subtle size-4 shrink-0" />
|
|
207
219
|
</MenuPrimitive.SubTrigger>
|
|
208
220
|
)
|
|
209
221
|
}
|
|
@@ -214,7 +226,7 @@ export function DropdownMenuSubContent({ className, children, ...rest }: Compone
|
|
|
214
226
|
<MenuPrimitive.SubContent
|
|
215
227
|
{...rest}
|
|
216
228
|
sideOffset={4}
|
|
217
|
-
className={
|
|
229
|
+
className={menuPanel({ className })}
|
|
218
230
|
>
|
|
219
231
|
{children}
|
|
220
232
|
</MenuPrimitive.SubContent>
|
package/field/AGENTS.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
```
|
|
4
4
|
받기 npx @pantoia/ui add field
|
|
5
5
|
층 ui
|
|
6
|
-
파일 field/index.tsx
|
|
6
|
+
파일 field/index.tsx, field/AGENTS.md
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
## 부위
|
|
@@ -11,11 +11,11 @@
|
|
|
11
11
|
| 이름 | props |
|
|
12
12
|
|---|---|
|
|
13
13
|
| `Field` | `FieldProps` |
|
|
14
|
-
| `FieldLabel` |
|
|
15
|
-
| `FieldHelp` |
|
|
14
|
+
| `FieldLabel` | - |
|
|
15
|
+
| `FieldHelp` | - |
|
|
16
16
|
| `FieldError` | `FieldErrorProps` |
|
|
17
|
-
| `FieldPending` |
|
|
18
|
-
| `FieldSuccess` |
|
|
17
|
+
| `FieldPending` | - |
|
|
18
|
+
| `FieldSuccess` | - |
|
|
19
19
|
|
|
20
20
|
### `FieldLabel`
|
|
21
21
|
|
|
@@ -81,15 +81,15 @@
|
|
|
81
81
|
| `className` | `string` | |
|
|
82
82
|
| `children` | `ReactNode` | |
|
|
83
83
|
|
|
84
|
-
## 변형
|
|
84
|
+
## 변형 - `field`
|
|
85
85
|
|
|
86
86
|
| 축 | 값 | 기본 |
|
|
87
87
|
|---|---|---|
|
|
88
|
-
| `layout` | `stack
|
|
88
|
+
| `layout` | `stack`, `inline`, `split` | `stack` |
|
|
89
89
|
|
|
90
90
|
## 함께 받는 것
|
|
91
91
|
|
|
92
|
-
`label
|
|
92
|
+
`label`, `tv`
|
|
93
93
|
|
|
94
94
|
## npm 의존
|
|
95
95
|
|
package/field/index.tsx
CHANGED
|
@@ -66,6 +66,8 @@ export function Field({ invalid = false, layout, className, children }: FieldPro
|
|
|
66
66
|
)
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
const fieldLabel = tv({ base: 'group-has-disabled:text-foreground-disabled' })
|
|
70
|
+
|
|
69
71
|
export function FieldLabel({ className, children }: { className?: string, children?: ReactNode }) {
|
|
70
72
|
const ctx = use(Context)
|
|
71
73
|
|
|
@@ -73,21 +75,25 @@ export function FieldLabel({ className, children }: { className?: string, childr
|
|
|
73
75
|
<Label
|
|
74
76
|
id={ctx?.labelId}
|
|
75
77
|
htmlFor={ctx?.id}
|
|
76
|
-
className={
|
|
78
|
+
className={fieldLabel({ className })}
|
|
77
79
|
>
|
|
78
80
|
{children}
|
|
79
81
|
</Label>
|
|
80
82
|
)
|
|
81
83
|
}
|
|
82
84
|
|
|
85
|
+
const fieldHelp = tv({
|
|
86
|
+
base: 'basis-full text-caption text-foreground-subtle'
|
|
87
|
+
+ ' group-has-disabled:text-foreground-disabled',
|
|
88
|
+
})
|
|
89
|
+
|
|
83
90
|
export function FieldHelp({ className, children }: { className?: string, children?: ReactNode }) {
|
|
84
91
|
const ctx = use(Context)
|
|
85
92
|
|
|
86
93
|
return (
|
|
87
94
|
<p
|
|
88
95
|
id={ctx?.messageId}
|
|
89
|
-
className={
|
|
90
|
-
+ ' group-has-disabled:text-fg-disabled' })({ className })}
|
|
96
|
+
className={fieldHelp({ className })}
|
|
91
97
|
>
|
|
92
98
|
{children}
|
|
93
99
|
</p>
|
|
@@ -100,6 +106,11 @@ export interface FieldErrorProps {
|
|
|
100
106
|
children?: ReactNode
|
|
101
107
|
}
|
|
102
108
|
|
|
109
|
+
const fieldError = tv({
|
|
110
|
+
base: 'basis-full text-caption text-fg-danger'
|
|
111
|
+
+ ' flex items-start gap-tight',
|
|
112
|
+
})
|
|
113
|
+
|
|
103
114
|
export function FieldError({ items, className, children }: FieldErrorProps) {
|
|
104
115
|
const ctx = use(Context)
|
|
105
116
|
const list = items == null ? [] : Array.isArray(items) ? items : [items]
|
|
@@ -107,8 +118,7 @@ export function FieldError({ items, className, children }: FieldErrorProps) {
|
|
|
107
118
|
return (
|
|
108
119
|
<p
|
|
109
120
|
id={ctx?.messageId}
|
|
110
|
-
className={
|
|
111
|
-
+ ' flex items-start gap-tight' })({ className })}
|
|
121
|
+
className={fieldError({ className })}
|
|
112
122
|
>
|
|
113
123
|
<CircleAlert className="mt-0.5 size-4 shrink-0" />
|
|
114
124
|
<span className="flex min-w-0 flex-col gap-0.5">
|
|
@@ -118,14 +128,18 @@ export function FieldError({ items, className, children }: FieldErrorProps) {
|
|
|
118
128
|
)
|
|
119
129
|
}
|
|
120
130
|
|
|
131
|
+
const fieldPending = tv({
|
|
132
|
+
base: 'basis-full text-caption text-foreground-subtle'
|
|
133
|
+
+ ' flex items-start gap-tight',
|
|
134
|
+
})
|
|
135
|
+
|
|
121
136
|
export function FieldPending({ className, children }: { className?: string, children?: ReactNode }) {
|
|
122
137
|
const ctx = use(Context)
|
|
123
138
|
|
|
124
139
|
return (
|
|
125
140
|
<p
|
|
126
141
|
id={ctx?.messageId}
|
|
127
|
-
className={
|
|
128
|
-
+ ' flex items-start gap-tight' })({ className })}
|
|
142
|
+
className={fieldPending({ className })}
|
|
129
143
|
>
|
|
130
144
|
<LoaderCircle className="mt-0.5 size-4 shrink-0 animate-spin motion-reduce:animate-none" />
|
|
131
145
|
<span className="min-w-0">{children}</span>
|
|
@@ -133,14 +147,18 @@ export function FieldPending({ className, children }: { className?: string, chil
|
|
|
133
147
|
)
|
|
134
148
|
}
|
|
135
149
|
|
|
150
|
+
const fieldSuccess = tv({
|
|
151
|
+
base: 'basis-full text-caption text-fg-success'
|
|
152
|
+
+ ' flex items-start gap-tight',
|
|
153
|
+
})
|
|
154
|
+
|
|
136
155
|
export function FieldSuccess({ className, children }: { className?: string, children?: ReactNode }) {
|
|
137
156
|
const ctx = use(Context)
|
|
138
157
|
|
|
139
158
|
return (
|
|
140
159
|
<p
|
|
141
160
|
id={ctx?.messageId}
|
|
142
|
-
className={
|
|
143
|
-
+ ' flex items-start gap-tight' })({ className })}
|
|
161
|
+
className={fieldSuccess({ className })}
|
|
144
162
|
>
|
|
145
163
|
<CircleCheck className="mt-0.5 size-4 shrink-0" />
|
|
146
164
|
<span className="min-w-0">{children}</span>
|
package/input/AGENTS.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
```
|
|
4
4
|
받기 npx @pantoia/ui add input
|
|
5
5
|
층 ui
|
|
6
|
-
파일 input/index.tsx
|
|
6
|
+
파일 input/index.tsx, input/AGENTS.md
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
## 부위
|
|
@@ -23,12 +23,12 @@
|
|
|
23
23
|
| `surface` | `'bare' \| 'well'` | |
|
|
24
24
|
| `size` | `'sm' \| 'md' \| 'lg'` | |
|
|
25
25
|
|
|
26
|
-
## 변형
|
|
26
|
+
## 변형 - `input`
|
|
27
27
|
|
|
28
28
|
| 축 | 값 | 기본 |
|
|
29
29
|
|---|---|---|
|
|
30
|
-
| `surface` | `bare
|
|
31
|
-
| `size` | `sm
|
|
30
|
+
| `surface` | `bare`, `well` | `bare` |
|
|
31
|
+
| `size` | `sm`, `md`, `lg` | `md` |
|
|
32
32
|
|
|
33
33
|
## 함께 받는 것
|
|
34
34
|
|
package/input/index.tsx
CHANGED
|
@@ -3,18 +3,18 @@ import type { InputHTMLAttributes } from 'react'
|
|
|
3
3
|
import { tv } from '../tv'
|
|
4
4
|
|
|
5
5
|
export const input = tv({
|
|
6
|
-
base: 'w-full rounded-r8 text-value text-
|
|
7
|
-
+ ' placeholder:text-
|
|
8
|
-
+ ' invalid:ring-2 invalid:ring-danger'
|
|
9
|
-
+ ' read-only:text-
|
|
6
|
+
base: 'w-full rounded-r8 text-value text-foreground'
|
|
7
|
+
+ ' placeholder:text-foreground-placeholder'
|
|
8
|
+
+ ' invalid:ring-2 invalid:ring-line-danger'
|
|
9
|
+
+ ' read-only:text-foreground-muted'
|
|
10
10
|
+ ' transition-state'
|
|
11
|
-
+ ' focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring'
|
|
12
|
-
+ ' disabled:bg-disabled disabled:text-
|
|
11
|
+
+ ' focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-line-focus-ring'
|
|
12
|
+
+ ' disabled:bg-bg-neutral-disabled disabled:text-foreground-disabled disabled:cursor-not-allowed',
|
|
13
13
|
|
|
14
14
|
variants: {
|
|
15
15
|
surface: {
|
|
16
|
-
bare: 'bg-transparent ring-1 ring-inset ring-line-strong read-only:bg-well',
|
|
17
|
-
well: 'bg-well',
|
|
16
|
+
bare: 'bg-transparent ring-1 ring-inset ring-line-strong read-only:bg-background-well',
|
|
17
|
+
well: 'bg-background-well',
|
|
18
18
|
},
|
|
19
19
|
size: {
|
|
20
20
|
sm: 'h-8 px-3',
|
package/kbd/AGENTS.md
CHANGED
package/kbd/index.tsx
CHANGED
|
@@ -2,12 +2,16 @@ import type { HTMLAttributes } from 'react'
|
|
|
2
2
|
|
|
3
3
|
import { tv } from '../tv'
|
|
4
4
|
|
|
5
|
+
const kbd = tv({
|
|
6
|
+
base: 'bg-background-well text-foreground-muted text-micro rounded-mark inline-flex items-center'
|
|
7
|
+
+ ' border border-line px-1.5 py-0.5 font-mono',
|
|
8
|
+
})
|
|
9
|
+
|
|
5
10
|
export function Kbd({ className, ...rest }: HTMLAttributes<HTMLElement>) {
|
|
6
11
|
return (
|
|
7
12
|
<kbd
|
|
8
13
|
{...rest}
|
|
9
|
-
className={
|
|
10
|
-
+ ' border border-line px-1.5 py-0.5 font-mono' })({ className })}
|
|
14
|
+
className={kbd({ className })}
|
|
11
15
|
/>
|
|
12
16
|
)
|
|
13
17
|
}
|
package/label/AGENTS.md
CHANGED
package/label/index.tsx
CHANGED
|
@@ -3,11 +3,13 @@ import type { ComponentProps } from 'react'
|
|
|
3
3
|
|
|
4
4
|
import { tv } from '../tv'
|
|
5
5
|
|
|
6
|
+
const labelText = tv({ base: 'text-label text-foreground' })
|
|
7
|
+
|
|
6
8
|
export function Label({ className, ...rest }: ComponentProps<typeof LabelPrimitive.Root>) {
|
|
7
9
|
return (
|
|
8
10
|
<LabelPrimitive.Root
|
|
9
11
|
{...rest}
|
|
10
|
-
className={
|
|
12
|
+
className={labelText({ className })}
|
|
11
13
|
/>
|
|
12
14
|
)
|
|
13
15
|
}
|