@pantoia/ui 0.6.0 → 0.7.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/README.md +4 -8
- package/agents/pantoia-ui.md +7 -0
- package/field/index.tsx +7 -7
- package/package.json +2 -4
- package/radio-group/index.tsx +2 -2
- package/select/index.tsx +10 -6
package/README.md
CHANGED
|
@@ -19,21 +19,17 @@ npx @pantoia/ui diff 복사한 소스와 배포본의 차이
|
|
|
19
19
|
|
|
20
20
|
## 개발
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
Node 20.11 이상이 필요하다.
|
|
23
23
|
|
|
24
24
|
```
|
|
25
25
|
git clone git@github.com:pantoia/pantoia-ui.git
|
|
26
26
|
cd pantoia-ui
|
|
27
|
-
|
|
27
|
+
npm install
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
편집기는 `.devcontainer/devcontainer.json`으로 컨테이너 안에서 연다.
|
|
31
|
-
|
|
32
30
|
```
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
docker compose exec dev npm run verify typecheck, check, lint, test
|
|
36
|
-
docker compose exec dev npm pack tarball
|
|
31
|
+
npm run verify typecheck, check, lint, test
|
|
32
|
+
npm pack tarball
|
|
37
33
|
```
|
|
38
34
|
|
|
39
35
|
## 구조
|
package/agents/pantoia-ui.md
CHANGED
|
@@ -25,6 +25,9 @@ init은 컴포넌트를 저장할 위치와 전역 CSS 파일을 묻고 `compone
|
|
|
25
25
|
다크 모드는 `.dark` 클래스로 켠다. `html`이나 `body`에 `.dark`를 두면 그 안의 토큰이 모두 바뀐다.
|
|
26
26
|
next-themes를 쓸 때는 `attribute="class"`로 둔다.
|
|
27
27
|
|
|
28
|
+
React Router 프레임워크 모드는 `react-router.config.ts`에 `future: { unstable_optimizeDeps: true }`를 더한다.
|
|
29
|
+
없으면 개발 서버에서 처음 여는 route마다 페이지가 새로고침된다.
|
|
30
|
+
|
|
28
31
|
## 쓰는 법
|
|
29
32
|
|
|
30
33
|
컴포넌트는 폴더 이름으로 가져온다.
|
|
@@ -44,6 +47,7 @@ import { Field, FieldLabel, FieldHelp } from '@pantoia/ui/field'
|
|
|
44
47
|
기본은 패키지로 쓰는 것이다. 동작이나 마크업을 프로젝트에 맞게 바꿔야 할 때만 복사한다.
|
|
45
48
|
|
|
46
49
|
제어 속성은 Base UI 규약을 따른다. `value`와 `defaultValue`, `onValueChange`를 짝으로 쓴다.
|
|
50
|
+
고르지 않은 상태는 `null`이다. Select, RadioGroup, Combobox, NumberField가 같다.
|
|
47
51
|
컴포넌트마다의 속성과 이벤트는 https://design.pantoia.com 의 각 페이지에 있다.
|
|
48
52
|
|
|
49
53
|
## 디자인 규칙
|
|
@@ -183,6 +187,9 @@ Tooltip과 Popover 짧은 설명이면 Tooltip, 안에서 조작하면 Po
|
|
|
183
187
|
|
|
184
188
|
Field는 라벨, 도움말, 오류 메시지를 입력 컨트롤에 묶는다. Input, Textarea, NumberField, Select, Combobox와 함께 쓴다.
|
|
185
189
|
|
|
190
|
+
Select는 고른 항목의 `SelectItemText`나 글자를 트리거에 보인다. `SelectItem`을 다른 컴포넌트로 감싸면 `items`로 라벨을 넘긴다.
|
|
191
|
+
Combobox 항목이 `{ value, label }`이 아닌 객체면 `itemToStringLabel`로 `ComboboxInput`에 보일 글자를 정한다.
|
|
192
|
+
|
|
186
193
|
앱에 한 번만 두는 것이 2개다.
|
|
187
194
|
|
|
188
195
|
```tsx
|
package/field/index.tsx
CHANGED
|
@@ -6,13 +6,13 @@ import { Label } from '../label'
|
|
|
6
6
|
import { tv } from '../tv'
|
|
7
7
|
|
|
8
8
|
export const field = tv({
|
|
9
|
-
base: 'group flex
|
|
9
|
+
base: 'group flex gap-x-2 gap-y-tight',
|
|
10
10
|
|
|
11
11
|
variants: {
|
|
12
12
|
layout: {
|
|
13
13
|
stack: 'flex-col',
|
|
14
|
-
inline: 'flex-row items-center',
|
|
15
|
-
split: 'flex-row items-center justify-between',
|
|
14
|
+
inline: 'flex-row flex-wrap content-start items-center',
|
|
15
|
+
split: 'flex-row flex-wrap content-start items-center justify-between',
|
|
16
16
|
},
|
|
17
17
|
},
|
|
18
18
|
|
|
@@ -39,7 +39,7 @@ export function FieldLabel({ render = <Label />, className, ...rest }: FieldLabe
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
const fieldHelp = tv({
|
|
42
|
-
base: '
|
|
42
|
+
base: 'w-full text-caption text-fg-subtle'
|
|
43
43
|
+ ' group-has-disabled:text-fg-disabled',
|
|
44
44
|
})
|
|
45
45
|
|
|
@@ -57,7 +57,7 @@ export type FieldErrorProps = Omit<FieldPrimitive.Error.Props, 'className'> & {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
const fieldError = tv({
|
|
60
|
-
base: '
|
|
60
|
+
base: 'w-full text-caption text-fg-danger'
|
|
61
61
|
+ ' flex items-start gap-tight',
|
|
62
62
|
})
|
|
63
63
|
|
|
@@ -79,7 +79,7 @@ export function FieldError({ items, match = true, render = <p />, className, chi
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
const fieldPending = tv({
|
|
82
|
-
base: '
|
|
82
|
+
base: 'w-full text-caption text-fg-subtle'
|
|
83
83
|
+ ' flex items-start gap-tight',
|
|
84
84
|
})
|
|
85
85
|
|
|
@@ -97,7 +97,7 @@ export function FieldPending({ className, children, ...rest }: FieldPendingProps
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
const fieldSuccess = tv({
|
|
100
|
-
base: '
|
|
100
|
+
base: 'w-full text-caption text-fg-success'
|
|
101
101
|
+ ' flex items-start gap-tight',
|
|
102
102
|
})
|
|
103
103
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pantoia/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Pantoia Design System",
|
|
5
5
|
"homepage": "https://design.pantoia.com",
|
|
6
6
|
"repository": {
|
|
@@ -48,9 +48,7 @@
|
|
|
48
48
|
"check": "tsx scripts/check.ts",
|
|
49
49
|
"lint": "oxlint --deny-warnings",
|
|
50
50
|
"test": "vitest run",
|
|
51
|
-
"verify": "npm run typecheck && npm run check && npm run lint && npm run test"
|
|
52
|
-
"dev:up": "docker compose up -d dev",
|
|
53
|
-
"dev:down": "docker compose down"
|
|
51
|
+
"verify": "npm run typecheck && npm run check && npm run lint && npm run test"
|
|
54
52
|
},
|
|
55
53
|
"dependencies": {
|
|
56
54
|
"@base-ui/react": "^1.8.0",
|
package/radio-group/index.tsx
CHANGED
|
@@ -46,7 +46,7 @@ export const radioGroupItem = tv({
|
|
|
46
46
|
defaultVariants: { color: 'neutral' },
|
|
47
47
|
})
|
|
48
48
|
|
|
49
|
-
export type RadioGroupProps = Omit<RadioGroupPrimitive.Props<string>, 'className'> & {
|
|
49
|
+
export type RadioGroupProps = Omit<RadioGroupPrimitive.Props<string | null>, 'className'> & {
|
|
50
50
|
color?: RadioColor
|
|
51
51
|
className?: string
|
|
52
52
|
}
|
|
@@ -56,7 +56,7 @@ const radioGroup = tv({ base: 'flex flex-col gap-3' })
|
|
|
56
56
|
export function RadioGroup({ color = 'neutral', className, ...rest }: RadioGroupProps) {
|
|
57
57
|
return (
|
|
58
58
|
<ColorContext value={color}>
|
|
59
|
-
<RadioGroupPrimitive<string> {...rest} className={radioGroup({ className })} />
|
|
59
|
+
<RadioGroupPrimitive<string | null> {...rest} className={radioGroup({ className })} />
|
|
60
60
|
</ColorContext>
|
|
61
61
|
)
|
|
62
62
|
}
|
package/select/index.tsx
CHANGED
|
@@ -51,12 +51,16 @@ function findItemText(children: ReactNode): { label: ReactNode } | undefined {
|
|
|
51
51
|
return found
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
function plainText(children: ReactNode) {
|
|
55
|
+
return typeof children === 'string' || typeof children === 'number' ? children : undefined
|
|
56
|
+
}
|
|
57
|
+
|
|
54
58
|
function collectItems(children: ReactNode, items: Record<string, ReactNode>) {
|
|
55
59
|
Children.forEach(children, child => {
|
|
56
60
|
if (!isValidElement<ItemElementProps>(child)) return
|
|
57
61
|
if (child.type === SelectItem && child.props.value !== undefined) {
|
|
58
|
-
const
|
|
59
|
-
if (
|
|
62
|
+
const label = findItemText(child.props.children)?.label ?? plainText(child.props.children)
|
|
63
|
+
if (label !== undefined) items[child.props.value] = label
|
|
60
64
|
return
|
|
61
65
|
}
|
|
62
66
|
collectItems(child.props.children, items)
|
|
@@ -67,9 +71,9 @@ function collectItems(children: ReactNode, items: Record<string, ReactNode>) {
|
|
|
67
71
|
export type SelectProps = Omit<ComponentProps<typeof SelectPrimitive.Trigger>, 'className' | 'value' | 'defaultValue'>
|
|
68
72
|
& Pick<SelectPrimitive.Root.Props<string>, 'open' | 'defaultOpen' | 'onOpenChange' | 'items' | 'name' | 'required' | 'readOnly' | 'modal' | 'form'>
|
|
69
73
|
& {
|
|
70
|
-
value?: string
|
|
71
|
-
defaultValue?: string
|
|
72
|
-
onValueChange?: (value: string, eventDetails: SelectPrimitive.Root.ChangeEventDetails) => void
|
|
74
|
+
value?: string | null
|
|
75
|
+
defaultValue?: string | null
|
|
76
|
+
onValueChange?: (value: string | null, eventDetails: SelectPrimitive.Root.ChangeEventDetails) => void
|
|
73
77
|
placeholder?: string
|
|
74
78
|
surface?: 'bare' | 'well'
|
|
75
79
|
size?: 'sm' | 'md' | 'lg'
|
|
@@ -118,7 +122,7 @@ export function Select({
|
|
|
118
122
|
<SelectPrimitive.Root<string>
|
|
119
123
|
value={value}
|
|
120
124
|
defaultValue={defaultValue}
|
|
121
|
-
onValueChange={
|
|
125
|
+
onValueChange={onValueChange}
|
|
122
126
|
open={open}
|
|
123
127
|
defaultOpen={defaultOpen}
|
|
124
128
|
onOpenChange={(next, eventDetails) => {
|