@mondrianai/runyourai-design-system 0.0.26 → 0.0.27
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 +93 -140
- package/dist/index.d.mts +13 -4
- package/dist/index.d.ts +13 -4
- package/dist/index.js +59 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +59 -20
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,56 +1,33 @@
|
|
|
1
1
|
# @mondrianai/runyourai-design-system
|
|
2
2
|
|
|
3
|
-
RunYourAI Design System —
|
|
3
|
+
RunYourAI Design System — [shadcn/ui](https://ui.shadcn.com/) · [Radix UI](https://www.radix-ui.com/) · [Tailwind CSS v4](https://tailwindcss.com/) 기반 React 컴포넌트 라이브러리입니다.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
| 항목 | 내용 |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| 최종 수정 | 2026-08-11 (Seulki Yun) |
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
- Next.js 15+
|
|
9
|
-
- Tailwind CSS v4
|
|
10
|
-
|
|
11
|
-
## Installation
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
npm install @mondrianai/runyourai-design-system
|
|
15
|
-
# or
|
|
16
|
-
yarn add @mondrianai/runyourai-design-system
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Local Development (yalc)
|
|
20
|
-
|
|
21
|
-
소비 앱에서 로컬 빌드를 테스트할 때는 [yalc](https://github.com/wclr/yalc)를 사용합니다.
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
# 최초 1회: 소비 앱에 yalc 연결
|
|
25
|
-
# (design system 레포에서)
|
|
26
|
-
yalc publish
|
|
27
|
-
# (소비 앱 레포에서)
|
|
28
|
-
yalc add @mondrianai/runyourai-design-system
|
|
29
|
-
|
|
30
|
-
# 이후 변경사항 반영 시 (design system 레포에서)
|
|
31
|
-
npm run build && yalc push
|
|
32
|
-
# 소비 앱 dev 서버 재시작 필요
|
|
33
|
-
```
|
|
9
|
+
---
|
|
34
10
|
|
|
35
|
-
|
|
11
|
+
소비 앱은 `main-frontend`(React 18 · antd 5)와 `core-frontend`(Next.js 16 · React 19 · antd 6) 두 곳입니다. 이 레포는 라이브러리이면서 동시에 **쇼케이스 앱**(`src/app/`)을 겸하고 있어, `yarn dev` 로 모든 컴포넌트를 눈으로 확인할 수 있습니다.
|
|
36
12
|
|
|
37
|
-
|
|
13
|
+
| 항목 | 값 |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| Peer deps | React 18+ · Tailwind CSS v4 |
|
|
16
|
+
| 배포 | Bitbucket Pipelines — `v*` 태그 push 시 npm publish |
|
|
17
|
+
| 로컬 연동 | [yalc](https://github.com/wclr/yalc) |
|
|
38
18
|
|
|
39
|
-
##
|
|
19
|
+
## 소비 앱에 설치하기
|
|
40
20
|
|
|
41
21
|
### 1. 패키지 설치
|
|
42
22
|
|
|
43
23
|
```bash
|
|
44
|
-
npm install @mondrianai/runyourai-design-system tailwindcss
|
|
45
|
-
npm install --save-dev @tailwindcss/postcss
|
|
46
|
-
# or
|
|
47
24
|
yarn add @mondrianai/runyourai-design-system tailwindcss
|
|
48
25
|
yarn add --dev @tailwindcss/postcss
|
|
49
26
|
```
|
|
50
27
|
|
|
51
28
|
### 2. PostCSS 설정
|
|
52
29
|
|
|
53
|
-
프로젝트 루트에 `postcss.config.mjs
|
|
30
|
+
프로젝트 루트에 `postcss.config.mjs` 를 만듭니다.
|
|
54
31
|
|
|
55
32
|
```js
|
|
56
33
|
// postcss.config.mjs
|
|
@@ -65,7 +42,7 @@ export default config;
|
|
|
65
42
|
|
|
66
43
|
### 3. Next.js (Turbopack) 설정
|
|
67
44
|
|
|
68
|
-
Next.js 15+ / 16의 기본 번들러인 Turbopack은 CSS `@import
|
|
45
|
+
Next.js 15+ / 16 의 기본 번들러인 Turbopack 은 CSS `@import` 에서 `package.json` 의 `exports` 서브패스를 해석하지 못합니다. `next.config.ts` 에 `resolveAlias` 를 추가합니다.
|
|
69
46
|
|
|
70
47
|
```ts
|
|
71
48
|
// next.config.ts
|
|
@@ -85,11 +62,11 @@ const nextConfig: NextConfig = {
|
|
|
85
62
|
export default nextConfig;
|
|
86
63
|
```
|
|
87
64
|
|
|
88
|
-
> Turbopack을
|
|
65
|
+
> Turbopack 을 쓰지 않으면(webpack 기반 커스텀 설정 등) 이 단계는 건너뜁니다.
|
|
89
66
|
|
|
90
67
|
### 4. Tailwind CSS 설정
|
|
91
68
|
|
|
92
|
-
`globals.css
|
|
69
|
+
`globals.css` 에 아래를 추가하면 디자인 토큰 · Tailwind 테마 · 애니메이션 · 다크모드가 모두 적용됩니다.
|
|
93
70
|
|
|
94
71
|
```css
|
|
95
72
|
/* globals.css */
|
|
@@ -98,60 +75,53 @@ export default nextConfig;
|
|
|
98
75
|
@import '@mondrianai/runyourai-design-system/tailwind-theme';
|
|
99
76
|
|
|
100
77
|
/* globals.css 위치 기준 node_modules 상대 경로 — 프로젝트 구조에 따라 조정하세요
|
|
101
|
-
*
|
|
102
|
-
*
|
|
78
|
+
* app/globals.css → ../node_modules/...
|
|
79
|
+
* src/app/globals.css → ../../node_modules/...
|
|
103
80
|
*/
|
|
104
81
|
@source "../node_modules/@mondrianai/runyourai-design-system/dist";
|
|
105
82
|
```
|
|
106
83
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
>
|
|
141
|
-
> * {
|
|
142
|
-
> margin: 0;
|
|
143
|
-
> padding: 0;
|
|
144
|
-
> }
|
|
145
|
-
> }
|
|
146
|
-
> ```
|
|
84
|
+
**`@import` 순서** — `url(...)` 형태의 외부 폰트 import 는 `@import 'tailwindcss'` **앞에**, 디자인 시스템 두 줄은 **뒤에** 둡니다.
|
|
85
|
+
|
|
86
|
+
**디자인 토큰 오버라이드** — `:root` 에서 **컴포넌트 표면색 토큰**(`--background` · `--card` · `--popover` 등)을 페이지 배경색 목적으로 바꾸면 안 됩니다. 이 변수들은 `Button` outline · `Card` · `Popover` 의 배경색에 직접 쓰입니다.
|
|
87
|
+
|
|
88
|
+
```css
|
|
89
|
+
/* ❌ Button outline 등 컴포넌트 배경색까지 바뀜 */
|
|
90
|
+
:root {
|
|
91
|
+
--background: #fdfdfd;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* ✅ 페이지 배경만 변경, 컴포넌트 토큰 무영향 */
|
|
95
|
+
html,
|
|
96
|
+
body {
|
|
97
|
+
background-color: #fdfdfd;
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**기존 CSS 리셋** — `* { margin: 0; padding: 0; }` 같은 전역 리셋이 있다면 반드시 `@layer base` 로 감쌉니다. Tailwind v4 에서 레이어 밖 스타일은 `@layer utilities` 보다 우선순위가 높아 `px-*` · `py-*` 가 무효화됩니다.
|
|
102
|
+
|
|
103
|
+
```css
|
|
104
|
+
@layer base {
|
|
105
|
+
*,
|
|
106
|
+
*::before,
|
|
107
|
+
*::after {
|
|
108
|
+
box-sizing: border-box;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
* {
|
|
112
|
+
margin: 0;
|
|
113
|
+
padding: 0;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
147
117
|
|
|
148
118
|
### 5. Provider 설정
|
|
149
119
|
|
|
150
|
-
`Tooltip` 등 Radix
|
|
120
|
+
`Tooltip` · `toast()` 등 Radix · sonner 기반 컴포넌트가 동작하려면 앱 최상단을 `RyaiDsProvider` 로 감쌉니다.
|
|
151
121
|
|
|
152
122
|
```tsx
|
|
153
123
|
// app/layout.tsx
|
|
154
|
-
import {
|
|
124
|
+
import { RyaiDsProvider } from '@mondrianai/runyourai-design-system';
|
|
155
125
|
|
|
156
126
|
export default function RootLayout({
|
|
157
127
|
children,
|
|
@@ -161,19 +131,21 @@ export default function RootLayout({
|
|
|
161
131
|
return (
|
|
162
132
|
<html lang='ko'>
|
|
163
133
|
<body>
|
|
164
|
-
<
|
|
134
|
+
<RyaiDsProvider>{children}</RyaiDsProvider>
|
|
165
135
|
</body>
|
|
166
136
|
</html>
|
|
167
137
|
);
|
|
168
138
|
}
|
|
169
139
|
```
|
|
170
140
|
|
|
171
|
-
|
|
141
|
+
> antd 를 함께 쓴다면 **antd `ConfigProvider` 바깥**에 둡니다.
|
|
142
|
+
|
|
143
|
+
## 사용
|
|
172
144
|
|
|
173
145
|
```tsx
|
|
174
146
|
import { Button, Badge, Card } from '@mondrianai/runyourai-design-system';
|
|
175
147
|
|
|
176
|
-
export
|
|
148
|
+
export function Page() {
|
|
177
149
|
return (
|
|
178
150
|
<Card>
|
|
179
151
|
<Badge text='New' />
|
|
@@ -183,78 +155,59 @@ export default function Page() {
|
|
|
183
155
|
}
|
|
184
156
|
```
|
|
185
157
|
|
|
186
|
-
### Utilities
|
|
187
|
-
|
|
188
158
|
```tsx
|
|
189
159
|
import { cn } from '@mondrianai/runyourai-design-system/utils';
|
|
190
160
|
|
|
191
161
|
<div className={cn('base-class', isActive && 'active-class')} />;
|
|
192
162
|
```
|
|
193
163
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
| Component | Description |
|
|
197
|
-
| ------------------------------------- | ------------------------- |
|
|
198
|
-
| `Accordion` | 펼침/접힘 컨텐츠 영역 |
|
|
199
|
-
| `AlertDialog` | 확인/취소 모달 다이얼로그 |
|
|
200
|
-
| `Alert` | 인라인 알림 메시지 |
|
|
201
|
-
| `Avatar` / `AvatarGroup` | 유저 아바타 및 그룹 |
|
|
202
|
-
| `Badge` | 상태·카테고리 배지 |
|
|
203
|
-
| `Breadcrumb` | 페이지 경로 탐색 |
|
|
204
|
-
| `Button` | 버튼 |
|
|
205
|
-
| `Card` | 카드 컨테이너 |
|
|
206
|
-
| `Checkbox` / `CheckboxCard` | 체크박스 |
|
|
207
|
-
| `ChoiceCardGroup` / `ChoiceCardItem` | 선택형 카드 그룹 |
|
|
208
|
-
| `Collapsible` | 접을 수 있는 컨텐츠 |
|
|
209
|
-
| `Combobox` | 검색 가능한 선택 입력 |
|
|
210
|
-
| `Field` / `FieldSection` / `FieldRow` | 폼 필드 레이아웃 |
|
|
211
|
-
| `GroupedSelect` | 그룹 구분이 있는 셀렉트 |
|
|
212
|
-
| `Header` | 앱 상단 헤더 |
|
|
213
|
-
| `Icon` | Lucide 아이콘 래퍼 |
|
|
214
|
-
| `MarkdownMessage` | 마크다운 렌더링 메시지 |
|
|
215
|
-
| `Menubar` | 메뉴바 |
|
|
216
|
-
| `MessageFooter` | 메시지 하단 액션 영역 |
|
|
217
|
-
| `NumberBadge` | 숫자 배지 |
|
|
218
|
-
| `SegmentedControl` | 세그먼트 탭 컨트롤 |
|
|
219
|
-
| `Select` | 셀렉트 박스 |
|
|
220
|
-
| `Sheet` | 사이드 슬라이드 패널 |
|
|
221
|
-
| `Sidebar` | 앱 사이드바 |
|
|
222
|
-
| `Slider` | 슬라이더 |
|
|
223
|
-
| `Stepper` | 단계 진행 표시 |
|
|
224
|
-
| `Switch` / `SwitchField` | 토글 스위치 |
|
|
225
|
-
| `Tooltip` / `TooltipWithIcon` | 툴팁 |
|
|
226
|
-
| `UserMessageBubble` | 채팅 메시지 말풍선 |
|
|
227
|
-
|
|
228
|
-
## Icons
|
|
229
|
-
|
|
230
|
-
### Custom Icons
|
|
164
|
+
아이콘도 같은 진입점에서 가져옵니다. `size-*` · `text-*` 클래스로 크기와 색을 제어합니다.
|
|
231
165
|
|
|
232
166
|
```tsx
|
|
233
167
|
import {
|
|
234
|
-
SparklesIcon,
|
|
235
|
-
|
|
236
|
-
|
|
168
|
+
SparklesIcon, // 커스텀 아이콘
|
|
169
|
+
ClaudeIcon, // 브랜드 아이콘
|
|
170
|
+
RyaiLogoIcon, // 로고
|
|
237
171
|
} from '@mondrianai/runyourai-design-system';
|
|
238
172
|
|
|
239
173
|
<SparklesIcon className='size-4 text-primary' />;
|
|
240
174
|
```
|
|
241
175
|
|
|
242
|
-
|
|
176
|
+
**제공 컴포넌트 목록**은 여기 적지 않습니다 — 목록은 금방 낡습니다. `src/components/index.ts` 의 export 를 보거나, `yarn dev` 로 쇼케이스 앱을 띄워 확인하세요.
|
|
243
177
|
|
|
244
|
-
|
|
245
|
-
import {
|
|
246
|
-
ClaudeIcon,
|
|
247
|
-
OpenAiIcon,
|
|
248
|
-
GeminiIcon,
|
|
249
|
-
} from '@mondrianai/runyourai-design-system';
|
|
178
|
+
## 로컬 개발
|
|
250
179
|
|
|
251
|
-
|
|
180
|
+
```bash
|
|
181
|
+
yarn install
|
|
182
|
+
yarn dev # 쇼케이스 앱 (port 3999)
|
|
183
|
+
yarn build # 라이브러리 빌드 — tsc --noEmit && tsup
|
|
184
|
+
yarn lint
|
|
252
185
|
```
|
|
253
186
|
|
|
254
|
-
|
|
187
|
+
소비 앱에서 로컬 빌드를 테스트할 때는 yalc 를 씁니다.
|
|
255
188
|
|
|
256
|
-
```
|
|
257
|
-
|
|
189
|
+
```bash
|
|
190
|
+
# 디자인 시스템 레포에서 (최초 1회)
|
|
191
|
+
yarn build && yalc publish
|
|
192
|
+
# 소비 앱에서 (최초 1회)
|
|
193
|
+
yalc add @mondrianai/runyourai-design-system
|
|
258
194
|
|
|
259
|
-
|
|
195
|
+
# 이후 변경마다 — 디자인 시스템 레포에서
|
|
196
|
+
yarn build && yalc push
|
|
260
197
|
```
|
|
198
|
+
|
|
199
|
+
> **소스만 고치면 소비 앱에 반영되지 않습니다.** 소비 앱은 `dist/` 를 보므로 반드시 `yarn build` 를 거쳐야 합니다. 최초 세팅·트러블슈팅은 [개발 사이클](./docs/operations/dev-cycle.md).
|
|
200
|
+
|
|
201
|
+
## 문서
|
|
202
|
+
|
|
203
|
+
| 문서 | 내용 |
|
|
204
|
+
| --- | --- |
|
|
205
|
+
| [`AGENTS.md`](./AGENTS.md) | **작업 규칙** — 코드를 만지기 전에 읽습니다. 디자이너·개발자 갈래가 나뉩니다 |
|
|
206
|
+
| [`docs/`](./docs/README.md) | 문서 인덱스 — 디자인 · 아키텍처 · 운영 · 계획 |
|
|
207
|
+
| [Figma 구현](./docs/design/figma-implementation.md) | 시안을 1px 오차 없이 옮기는 기준 |
|
|
208
|
+
| [디자인 토큰](./docs/design/tokens.md) | `variables.json` → CSS 세 파일 동기화 |
|
|
209
|
+
| [컴포넌트 API](./docs/architecture/component-api.md) | 공개 표면 설계 원칙 |
|
|
210
|
+
| [개발 사이클](./docs/operations/dev-cycle.md) | 구현 후 마무리 · yalc 반영 · 트러블슈팅 |
|
|
211
|
+
| [릴리스](./docs/operations/release.md) | 버전 결정 · 태그 · npm 배포 |
|
|
212
|
+
|
|
213
|
+
디렉터리 구조와 설정 항목은 문서로 옮겨두지 않습니다. `src/` 와 `tsup.config.ts` · `package.json` 을 직접 보세요. 왜 그렇게 되어 있는지는 위 문서에 있습니다.
|
package/dist/index.d.mts
CHANGED
|
@@ -114,7 +114,7 @@ declare function BreadcrumbSeparator({ iconName, className, ...props }: React__d
|
|
|
114
114
|
|
|
115
115
|
declare const buttonVariants: (props?: ({
|
|
116
116
|
variant?: "link" | "primary" | "secondary" | "outline" | "ghost" | "destructive" | "ghostDestructive" | null | undefined;
|
|
117
|
-
size?: "rounded" | "default" | "icon" | "pill" | null | undefined;
|
|
117
|
+
size?: "rounded" | "default" | "sm" | "icon" | "pill" | null | undefined;
|
|
118
118
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
119
119
|
interface ButtonProps extends React$1.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
120
120
|
/** Radix Slot 패턴 — true 이면 자식 요소에 props를 병합 */
|
|
@@ -819,7 +819,7 @@ type SelectProps = SingleSelectProps | MultiSelectProps;
|
|
|
819
819
|
* - 열린 상태에서 listbox에 포커스가 이동하고 `aria-activedescendant`로 활성 옵션을 연결한다.
|
|
820
820
|
* - 키보드: ↑/↓/Home/End 이동, Enter/Space 선택(다중은 토글), Esc 닫힘, 타입어헤드 점프.
|
|
821
821
|
* - 역할: 컨테이너 `role="listbox"`(+다중 시 `aria-multiselectable`), 옵션 `role="option"`+`aria-selected`.
|
|
822
|
-
* 배경·설계 근거: {@link ../../../docs/
|
|
822
|
+
* 배경·설계 근거: {@link ../../../docs/plans/select-a11y-keyboard-aria.md}
|
|
823
823
|
*/
|
|
824
824
|
declare function Select(props: SelectProps): react_jsx_runtime.JSX.Element;
|
|
825
825
|
|
|
@@ -999,7 +999,7 @@ interface TabsProps<T extends string = string> {
|
|
|
999
999
|
*
|
|
1000
1000
|
* NOTE: 콘텐츠 패널은 사용처가 바깥에서 `current`로 렌더하므로 Tabs.Content(및 aria-controls↔
|
|
1001
1001
|
* aria-labelledby 연결)가 없다. 키보드·역할 a11y는 확보했으나 탭↔패널 연결은 미완.
|
|
1002
|
-
* 구 pill 사용처 이관 및 릴리스 체크리스트: docs/
|
|
1002
|
+
* 구 pill 사용처 이관 및 릴리스 체크리스트: docs/plans/tabs-segmented-control.md
|
|
1003
1003
|
*/
|
|
1004
1004
|
declare function Tabs<T extends string = string>({ allTabs, current, onTabChange, block, className, }: TabsProps<T>): react_jsx_runtime.JSX.Element;
|
|
1005
1005
|
|
|
@@ -1396,6 +1396,15 @@ type ToastOptions = {
|
|
|
1396
1396
|
duration?: number;
|
|
1397
1397
|
/** 표시 위치. 미지정 시 `<Toaster />` 기본값(top-center). */
|
|
1398
1398
|
position?: ExternalToast['position'];
|
|
1399
|
+
/**
|
|
1400
|
+
* 기본 너비(356px)에서 줄바꿈하지 않고 문구 길이만큼 토스트를 넓힌다.
|
|
1401
|
+
* 목록처럼 길이가 가변적인 문구(예: 지원하지 않는 파일 형식 나열)에 사용한다.
|
|
1402
|
+
*
|
|
1403
|
+
* 무한정 넓어지지는 않는다. `--toast-nowrap-max`(기본 `min(90vw, 720px)`)에서
|
|
1404
|
+
* 멈추고, 그보다 긴 문구는 그 지점부터 다시 줄바꿈한다. 좁은 뷰포트에서
|
|
1405
|
+
* 토스트가 화면 밖으로 나가지 않도록 하는 안전장치다.
|
|
1406
|
+
*/
|
|
1407
|
+
nowrap?: boolean;
|
|
1399
1408
|
/** 토스트 컨테이너에 추가할 클래스 */
|
|
1400
1409
|
className?: string;
|
|
1401
1410
|
};
|
|
@@ -1443,7 +1452,7 @@ type ToastPromiseMessages<T> = {
|
|
|
1443
1452
|
* error: '실패',
|
|
1444
1453
|
* });
|
|
1445
1454
|
*/
|
|
1446
|
-
declare function promiseToast<T>(promise: Promise<T> | (() => Promise<T>), messages: ToastPromiseMessages<T
|
|
1455
|
+
declare function promiseToast<T>(promise: Promise<T> | (() => Promise<T>), messages: ToastPromiseMessages<T>, options?: ToastMethodOptions): string | number;
|
|
1447
1456
|
/**
|
|
1448
1457
|
* 이미 떠 있는 하나의 토스트를 이후 상태로 갱신하는 핸들.
|
|
1449
1458
|
* `toast.loading()`이 반환하며, `{ id }`를 옵션으로 넘겨 다시 지정할 필요 없이
|
package/dist/index.d.ts
CHANGED
|
@@ -114,7 +114,7 @@ declare function BreadcrumbSeparator({ iconName, className, ...props }: React__d
|
|
|
114
114
|
|
|
115
115
|
declare const buttonVariants: (props?: ({
|
|
116
116
|
variant?: "link" | "primary" | "secondary" | "outline" | "ghost" | "destructive" | "ghostDestructive" | null | undefined;
|
|
117
|
-
size?: "rounded" | "default" | "icon" | "pill" | null | undefined;
|
|
117
|
+
size?: "rounded" | "default" | "sm" | "icon" | "pill" | null | undefined;
|
|
118
118
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
119
119
|
interface ButtonProps extends React$1.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
120
120
|
/** Radix Slot 패턴 — true 이면 자식 요소에 props를 병합 */
|
|
@@ -819,7 +819,7 @@ type SelectProps = SingleSelectProps | MultiSelectProps;
|
|
|
819
819
|
* - 열린 상태에서 listbox에 포커스가 이동하고 `aria-activedescendant`로 활성 옵션을 연결한다.
|
|
820
820
|
* - 키보드: ↑/↓/Home/End 이동, Enter/Space 선택(다중은 토글), Esc 닫힘, 타입어헤드 점프.
|
|
821
821
|
* - 역할: 컨테이너 `role="listbox"`(+다중 시 `aria-multiselectable`), 옵션 `role="option"`+`aria-selected`.
|
|
822
|
-
* 배경·설계 근거: {@link ../../../docs/
|
|
822
|
+
* 배경·설계 근거: {@link ../../../docs/plans/select-a11y-keyboard-aria.md}
|
|
823
823
|
*/
|
|
824
824
|
declare function Select(props: SelectProps): react_jsx_runtime.JSX.Element;
|
|
825
825
|
|
|
@@ -999,7 +999,7 @@ interface TabsProps<T extends string = string> {
|
|
|
999
999
|
*
|
|
1000
1000
|
* NOTE: 콘텐츠 패널은 사용처가 바깥에서 `current`로 렌더하므로 Tabs.Content(및 aria-controls↔
|
|
1001
1001
|
* aria-labelledby 연결)가 없다. 키보드·역할 a11y는 확보했으나 탭↔패널 연결은 미완.
|
|
1002
|
-
* 구 pill 사용처 이관 및 릴리스 체크리스트: docs/
|
|
1002
|
+
* 구 pill 사용처 이관 및 릴리스 체크리스트: docs/plans/tabs-segmented-control.md
|
|
1003
1003
|
*/
|
|
1004
1004
|
declare function Tabs<T extends string = string>({ allTabs, current, onTabChange, block, className, }: TabsProps<T>): react_jsx_runtime.JSX.Element;
|
|
1005
1005
|
|
|
@@ -1396,6 +1396,15 @@ type ToastOptions = {
|
|
|
1396
1396
|
duration?: number;
|
|
1397
1397
|
/** 표시 위치. 미지정 시 `<Toaster />` 기본값(top-center). */
|
|
1398
1398
|
position?: ExternalToast['position'];
|
|
1399
|
+
/**
|
|
1400
|
+
* 기본 너비(356px)에서 줄바꿈하지 않고 문구 길이만큼 토스트를 넓힌다.
|
|
1401
|
+
* 목록처럼 길이가 가변적인 문구(예: 지원하지 않는 파일 형식 나열)에 사용한다.
|
|
1402
|
+
*
|
|
1403
|
+
* 무한정 넓어지지는 않는다. `--toast-nowrap-max`(기본 `min(90vw, 720px)`)에서
|
|
1404
|
+
* 멈추고, 그보다 긴 문구는 그 지점부터 다시 줄바꿈한다. 좁은 뷰포트에서
|
|
1405
|
+
* 토스트가 화면 밖으로 나가지 않도록 하는 안전장치다.
|
|
1406
|
+
*/
|
|
1407
|
+
nowrap?: boolean;
|
|
1399
1408
|
/** 토스트 컨테이너에 추가할 클래스 */
|
|
1400
1409
|
className?: string;
|
|
1401
1410
|
};
|
|
@@ -1443,7 +1452,7 @@ type ToastPromiseMessages<T> = {
|
|
|
1443
1452
|
* error: '실패',
|
|
1444
1453
|
* });
|
|
1445
1454
|
*/
|
|
1446
|
-
declare function promiseToast<T>(promise: Promise<T> | (() => Promise<T>), messages: ToastPromiseMessages<T
|
|
1455
|
+
declare function promiseToast<T>(promise: Promise<T> | (() => Promise<T>), messages: ToastPromiseMessages<T>, options?: ToastMethodOptions): string | number;
|
|
1447
1456
|
/**
|
|
1448
1457
|
* 이미 떠 있는 하나의 토스트를 이후 상태로 갱신하는 핸들.
|
|
1449
1458
|
* `toast.loading()`이 반환하며, `{ id }`를 옵션으로 넘겨 다시 지정할 필요 없이
|
package/dist/index.js
CHANGED
|
@@ -716,12 +716,15 @@ var buttonVariants = (0, import_class_variance_authority.cva)(
|
|
|
716
716
|
/**
|
|
717
717
|
* size — 버튼 크기
|
|
718
718
|
* default h-9 (36px) px-4 텍스트 버튼 표준
|
|
719
|
+
* sm h-8 (32px) px-3 py-1.5 표 안 액션 등 좁은 자리용 (Figma 6320:6085)
|
|
719
720
|
* icon 36×36 정사각형 아이콘 전용, rounded-lg 유지 / hover: opacity-80
|
|
720
721
|
* rounded 36×36 원형 아이콘 전용, rounded-full / hover: opacity-80
|
|
721
722
|
* pill px-3 py-1.5 rounded-full Rounded-text 필셋 스타일
|
|
722
723
|
*/
|
|
723
724
|
size: {
|
|
724
725
|
default: "h-9 px-4 py-2",
|
|
726
|
+
/** Small — Figma node 6320:6085. 높이 32px, 패딩만 default 와 다르다 */
|
|
727
|
+
sm: "h-8 px-3 py-1.5",
|
|
725
728
|
icon: "size-9 p-0 hover:opacity-80",
|
|
726
729
|
rounded: "size-9 p-0 rounded-full hover:opacity-80",
|
|
727
730
|
pill: "px-3 py-1.5 rounded-full"
|
|
@@ -1051,8 +1054,8 @@ function AvatarGroup({ avatars, size = "md", className }) {
|
|
|
1051
1054
|
|
|
1052
1055
|
// src/components/ui/badge.tsx
|
|
1053
1056
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1054
|
-
var TEXT_BADGE_BASE = "inline-flex w-fit shrink-0 items-center justify-center gap-1
|
|
1055
|
-
var BADGE_ICON_BASE = "size-
|
|
1057
|
+
var TEXT_BADGE_BASE = "inline-flex w-fit shrink-0 items-center justify-center gap-1 rounded-full px-2.5 py-1 text-sm font-medium leading-5 tracking-tight whitespace-nowrap";
|
|
1058
|
+
var BADGE_ICON_BASE = "size-4";
|
|
1056
1059
|
var BADGE_VARIANT_STYLES = {
|
|
1057
1060
|
default: "bg-primary text-primary-foreground",
|
|
1058
1061
|
secondary: "bg-secondary text-secondary-foreground",
|
|
@@ -4725,13 +4728,13 @@ MenubarSeparator.displayName = import_radix_ui13.Menubar.Separator.displayName;
|
|
|
4725
4728
|
|
|
4726
4729
|
// src/components/ui/number-badge.tsx
|
|
4727
4730
|
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
4728
|
-
var NUMBER_BADGE_BASE = "inline-flex shrink-0 items-center justify-center rounded-full font-mono text-
|
|
4731
|
+
var NUMBER_BADGE_BASE = "inline-flex h-7 shrink-0 items-center justify-center rounded-full font-mono text-sm font-medium leading-4 whitespace-nowrap";
|
|
4729
4732
|
function NumberBadge({
|
|
4730
4733
|
count,
|
|
4731
4734
|
variant = "default",
|
|
4732
4735
|
className
|
|
4733
4736
|
}) {
|
|
4734
|
-
const sizeClass = count < 10 ? "
|
|
4737
|
+
const sizeClass = count < 10 ? "w-5" : "min-w-5 px-2";
|
|
4735
4738
|
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
4736
4739
|
"span",
|
|
4737
4740
|
{
|
|
@@ -7236,29 +7239,63 @@ var TYPE_TOAST = {
|
|
|
7236
7239
|
info: import_sonner.toast.info,
|
|
7237
7240
|
error: import_sonner.toast.error
|
|
7238
7241
|
};
|
|
7242
|
+
var WIDTH_CLASS = {
|
|
7243
|
+
/**
|
|
7244
|
+
* 내용 크기로 줄이고, --width(356px)를 넘으면 줄바꿈.
|
|
7245
|
+
*
|
|
7246
|
+
* w-fit으로 줄이면 absolute+left/right auto라 컨테이너 좌측 끝에 붙어 왼쪽으로
|
|
7247
|
+
* 치우친다. left/right:0 + margin-inline:auto로 컨테이너 안에서 다시 중앙 정렬한다.
|
|
7248
|
+
* (left:50%로 잡으면 fit-content의 available space가 컨테이너의 절반으로 줄어
|
|
7249
|
+
* 토스트가 178px에서 줄바꿈해 버리므로 이 조합이어야 한다.)
|
|
7250
|
+
*/
|
|
7251
|
+
fit: "!w-fit !max-w-[var(--width)] data-[x-position=center]:!left-0 data-[x-position=center]:!right-0 data-[x-position=center]:!mx-auto",
|
|
7252
|
+
/**
|
|
7253
|
+
* 한 줄로 늘리고, --toast-nowrap-max에서 멈춘 뒤 줄바꿈.
|
|
7254
|
+
*
|
|
7255
|
+
* 컨테이너(--width)보다 넓어질 수 있어서 fit의 중앙 정렬 방식을 쓸 수 없다.
|
|
7256
|
+
* left:0 + right:0 + margin auto는 over-constrained가 되고, CSS 규격상 마진이
|
|
7257
|
+
* 음수가 되어야 하는 경우 ltr에서는 margin-left가 0으로 고정되어 좌측으로 쏠린다.
|
|
7258
|
+
* 대신 left:50% + translate로 잡는다. Tailwind v4의 translate 유틸리티는
|
|
7259
|
+
* `transform`이 아니라 `translate` 속성을 쓰므로, sonner가 스택 애니메이션에
|
|
7260
|
+
* 사용하는 transform과 겹치지 않고 합성된다.
|
|
7261
|
+
*/
|
|
7262
|
+
nowrap: "!w-max !max-w-[var(--toast-nowrap-max)] data-[x-position=center]:!left-1/2 data-[x-position=center]:!right-auto data-[x-position=center]:-translate-x-1/2"
|
|
7263
|
+
};
|
|
7264
|
+
function toSonnerOptions(options) {
|
|
7265
|
+
const _a = options != null ? options : {}, { nowrap, className } = _a, rest = __objRest(_a, ["nowrap", "className"]);
|
|
7266
|
+
return __spreadProps(__spreadValues({}, rest), {
|
|
7267
|
+
className: cn(nowrap ? WIDTH_CLASS.nowrap : WIDTH_CLASS.fit, className)
|
|
7268
|
+
});
|
|
7269
|
+
}
|
|
7239
7270
|
function baseToast(message, options) {
|
|
7240
7271
|
const _a = options != null ? options : {}, { type } = _a, rest = __objRest(_a, ["type"]);
|
|
7241
|
-
|
|
7242
|
-
|
|
7272
|
+
const sonnerOptions = toSonnerOptions(rest);
|
|
7273
|
+
if (type) return TYPE_TOAST[type](message, sonnerOptions);
|
|
7274
|
+
return (0, import_sonner.toast)(message, sonnerOptions);
|
|
7243
7275
|
}
|
|
7244
7276
|
function resolveMessage(message, arg) {
|
|
7245
7277
|
return typeof message === "function" ? message(arg) : message;
|
|
7246
7278
|
}
|
|
7247
|
-
function promiseToast(promise, messages) {
|
|
7279
|
+
function promiseToast(promise, messages, options) {
|
|
7248
7280
|
const running = typeof promise === "function" ? promise() : promise;
|
|
7249
|
-
const
|
|
7281
|
+
const sonnerOptions = toSonnerOptions(options);
|
|
7282
|
+
const id = import_sonner.toast.loading(messages.loading, __spreadProps(__spreadValues({}, sonnerOptions), {
|
|
7250
7283
|
description: messages.description
|
|
7251
|
-
});
|
|
7284
|
+
}));
|
|
7252
7285
|
running.then(
|
|
7253
|
-
(data) => import_sonner.toast.success(resolveMessage(messages.success, data), {
|
|
7286
|
+
(data) => import_sonner.toast.success(resolveMessage(messages.success, data), __spreadProps(__spreadValues({}, sonnerOptions), {
|
|
7287
|
+
id
|
|
7288
|
+
}))
|
|
7254
7289
|
).catch(
|
|
7255
|
-
(error) => import_sonner.toast.error(resolveMessage(messages.error, error), {
|
|
7290
|
+
(error) => import_sonner.toast.error(resolveMessage(messages.error, error), __spreadProps(__spreadValues({}, sonnerOptions), {
|
|
7291
|
+
id
|
|
7292
|
+
}))
|
|
7256
7293
|
);
|
|
7257
7294
|
return id;
|
|
7258
7295
|
}
|
|
7259
7296
|
function makeToastHandle(id) {
|
|
7260
7297
|
const update = (fn) => (message, options) => {
|
|
7261
|
-
fn(message, __spreadProps(__spreadValues({}, options), { id }));
|
|
7298
|
+
fn(message, __spreadProps(__spreadValues({}, toSonnerOptions(options)), { id }));
|
|
7262
7299
|
};
|
|
7263
7300
|
return {
|
|
7264
7301
|
id,
|
|
@@ -7293,7 +7330,7 @@ var toast = Object.assign(baseToast, {
|
|
|
7293
7330
|
* onError: () => t.error('저장 실패'),
|
|
7294
7331
|
* });
|
|
7295
7332
|
*/
|
|
7296
|
-
loading: (message, options) => makeToastHandle(import_sonner.toast.loading(message, options)),
|
|
7333
|
+
loading: (message, options) => makeToastHandle(import_sonner.toast.loading(message, toSonnerOptions(options))),
|
|
7297
7334
|
/** Promise/async 함수를 받아 로딩→성공/실패로 자동 전환. */
|
|
7298
7335
|
promise: promiseToast,
|
|
7299
7336
|
/** id로 특정 토스트를 닫는다. 인자 없으면 전체 닫기. */
|
|
@@ -7321,16 +7358,18 @@ function Toaster(_a) {
|
|
|
7321
7358
|
style: {
|
|
7322
7359
|
"--normal-bg": "var(--popover)",
|
|
7323
7360
|
"--normal-text": "var(--popover-foreground)",
|
|
7324
|
-
"--normal-border": "var(--border)"
|
|
7361
|
+
"--normal-border": "var(--border)",
|
|
7362
|
+
// `nowrap` 토스트의 상한. 뷰포트 밖으로 나가지 않도록 90vw로 막고,
|
|
7363
|
+
// 대화면에서 과도하게 길어지지 않도록 절대값도 함께 건다.
|
|
7364
|
+
"--toast-nowrap-max": "min(90vw, 720px)"
|
|
7325
7365
|
},
|
|
7326
7366
|
toastOptions: {
|
|
7327
7367
|
classNames: {
|
|
7328
|
-
// 세로 패딩만 약간 축소(16→12px).
|
|
7329
|
-
//
|
|
7330
|
-
//
|
|
7331
|
-
//
|
|
7332
|
-
|
|
7333
|
-
toast: "!w-fit !max-w-[var(--width)] !py-3 data-[x-position=center]:!left-0 data-[x-position=center]:!right-0 data-[x-position=center]:!mx-auto",
|
|
7368
|
+
// 세로 패딩만 약간 축소(16→12px).
|
|
7369
|
+
// 너비·중앙 정렬은 토스트마다 달라지므로 여기 두지 않는다. sonner의 내부 cn은
|
|
7370
|
+
// 단순 join이라 전역 클래스와 개별 클래스가 충돌해도 정리해 주지 않는다.
|
|
7371
|
+
// `toast()`에서 cn()(tailwind-merge)으로 합쳐 WIDTH_CLASS로 붙인다.
|
|
7372
|
+
toast: "!py-3",
|
|
7334
7373
|
title: "text-sm font-medium leading-5 tracking-tight break-words",
|
|
7335
7374
|
description: "text-sm leading-5 tracking-tight text-muted-foreground break-words"
|
|
7336
7375
|
}
|