@bigtablet/design-system 1.14.1 → 1.14.3

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 CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  <img width="1800" height="300" alt="Image" src="https://github.com/user-attachments/assets/420a15cc-5be3-447f-9c64-068e946cb118" /> <br>
4
4
 
5
- </div>
6
-
7
5
  # Bigtablet Design System
8
6
 
9
7
  [![npm version](https://img.shields.io/npm/v/@bigtablet/design-system.svg)](https://www.npmjs.com/package/@bigtablet/design-system)
@@ -11,9 +9,9 @@
11
9
 
12
10
  Bigtablet의 공식 디자인 시스템으로, Foundation(디자인 토큰)과 Components(UI 컴포넌트)로 구성된 통합 UI 라이브러리입니다.
13
11
 
14
- > **현재 버전**: v1.12.0 | **최근 업데이트**: 2025년 1월 15일
12
+ [GitHub](https://github.com/Bigtablet/bigtablet-design-system) · [NPM](https://www.npmjs.com/package/@bigtablet/design-system) · [Storybook](https://bigtablet.github.io/bigtablet-design-system)
15
13
 
16
- [깃허브 링크](https://github.com/Bigtablet/bigtablet-design-system) | [📦 NPM 패키지](https://www.npmjs.com/package/@bigtablet/design-system)
14
+ </div>
17
15
 
18
16
  ---
19
17
 
@@ -22,93 +20,73 @@ Bigtablet의 공식 디자인 시스템으로, Foundation(디자인 토큰)과 C
22
20
  - [주요 특징](#주요-특징)
23
21
  - [설치](#설치)
24
22
  - [빠른 시작](#빠른-시작)
25
- - [프로젝트 구조](#프로젝트-구조)
23
+ - [컴포넌트](#컴포넌트)
26
24
  - [Foundation (디자인 토큰)](#foundation-디자인-토큰)
27
- - [Components (UI 컴포넌트)](#components-ui-컴포넌트)
28
25
  - [개발 가이드](#개발-가이드)
29
26
  - [기여하기](#기여하기)
30
- - [라이센스](#라이센스)
31
27
 
32
28
  ---
33
29
 
34
30
  ## 주요 특징
35
31
 
36
- **체계적인 구조**
37
- - Foundation과 Components의 명확한 분리
38
- - TypeScript 기반 타입 안정성
39
- - SCSS 기반 스타일 토큰 관리
40
-
41
- **개발자 경험**
42
- - Storybook 8 기반 인터랙티브 문서화
43
- - Chromatic 시각적 회귀 테스트
44
- - Pure React / Next.js 전용 번들 제공
45
-
46
- **최신 기술 스택**
47
- - React 19 지원
48
- - pnpm 워크스페이스
49
- - GitHub Actions 자동 배포
50
- - Changesets 기반 버전 관리
51
-
52
- **디자인 시스템**
53
- - 일관된 디자인 토큰 (색상, 타이포그래피, 간격 등)
54
- - 접근성(a11y) 기본 지원
55
- - 다크모드 준비중
32
+ - **React 19 지원** - 최신 React 버전 완벽 지원
33
+ - **TypeScript** - 완전한 타입 안정성
34
+ - **Pure React / Next.js** - 프레임워크별 최적화된 번들 제공
35
+ - **디자인 토큰** - 일관된 색상, 타이포그래피, 간격 시스템
36
+ - **접근성(a11y)** - 키보드 네비게이션, 스크린 리더 호환
37
+ - **Storybook** - 인터랙티브 문서화
56
38
 
57
39
  ---
58
40
 
59
41
  ## 설치
60
42
 
61
- ### npm
62
43
  ```bash
44
+ # npm
63
45
  npm install @bigtablet/design-system
64
- ```
65
46
 
66
- ### yarn
67
- ```bash
47
+ # yarn
68
48
  yarn add @bigtablet/design-system
49
+
50
+ # pnpm
51
+ pnpm add @bigtablet/design-system
69
52
  ```
70
53
 
71
- ### pnpm
54
+ ### Peer Dependencies
55
+
72
56
  ```bash
73
- pnpm add @bigtablet/design-system
57
+ npm install react react-dom lucide-react react-toastify
74
58
  ```
75
59
 
76
60
  ---
77
61
 
78
62
  ## 빠른 시작
79
63
 
80
- ### Pure React 프로젝트
64
+ ### Pure React
81
65
 
82
66
  ```tsx
83
67
  import { Button, TextField } from '@bigtablet/design-system';
84
- import '@bigtablet/design-system/styles';
68
+ import '@bigtablet/design-system/style.css';
85
69
 
86
70
  function App() {
87
71
  return (
88
72
  <div>
89
- <TextField
90
- label="이메일"
91
- type="email"
92
- placeholder="email@example.com"
93
- />
94
- <Button variant="primary" size="md">
95
- 제출하기
96
- </Button>
73
+ <TextField label="이메일" placeholder="email@example.com" />
74
+ <Button variant="primary">제출</Button>
97
75
  </div>
98
76
  );
99
77
  }
100
78
  ```
101
79
 
102
- ### Next.js 프로젝트
80
+ ### Next.js
103
81
 
104
82
  ```tsx
105
83
  import { Sidebar, Button } from '@bigtablet/design-system/next';
106
- import '@bigtablet/design-system/styles';
84
+ import '@bigtablet/design-system/style.css';
107
85
 
108
86
  export default function Layout({ children }) {
109
87
  return (
110
88
  <div>
111
- <Sidebar
89
+ <Sidebar
112
90
  items={[
113
91
  { label: '홈', href: '/' },
114
92
  { label: '대시보드', href: '/dashboard' }
@@ -120,502 +98,582 @@ export default function Layout({ children }) {
120
98
  }
121
99
  ```
122
100
 
123
- ### Foundation 토큰 사용
124
-
125
- ```tsx
126
- import { colors, spacing, typography } from '@bigtablet/design-system/foundation';
127
-
128
- const StyledComponent = styled.div`
129
- color: ${colors.brand.primary};
130
- padding: ${spacing.md};
131
- font-size: ${typography.body.fontSize};
132
- `;
133
- ```
134
-
135
101
  ---
136
102
 
137
- ## 프로젝트 구조
103
+ ## 컴포넌트
138
104
 
139
- ```
140
- bigtablet-design-system/
141
- ├── .changeset/ # 버전 관리 설정
142
- ├── .github/
143
- │ └── workflows/ # CI/CD 파이프라인
144
- ├── .storybook/ # Storybook 설정
145
- ├── public/ # 정적 리소스
146
- ├── scripts/ # 빌드 및 배포 스크립트
147
- ├── src/
148
- │ ├── styles/
149
- │ │ ├── ts/ # TypeScript 디자인 토큰
150
- │ │ │ ├── colors.ts
151
- │ │ │ ├── spacing.ts
152
- │ │ │ ├── typography.ts
153
- │ │ │ ├── radius.ts
154
- │ │ │ ├── shadows.ts
155
- │ │ │ ├── motion.ts
156
- │ │ │ ├── z-index.ts
157
- │ │ │ ├── breakpoints.ts
158
- │ │ │ └── a11y.ts
159
- │ │ └── scss/ # SCSS 믹스인 및 변수
160
- │ ├── ui/
161
- │ │ ├── form/ # 입력 컴포넌트
162
- │ │ │ ├── Button/
163
- │ │ │ ├── TextField/
164
- │ │ │ ├── Checkbox/
165
- │ │ │ ├── Radio/
166
- │ │ │ ├── Switch/
167
- │ │ │ ├── Select/
168
- │ │ │ └── FileInput/
169
- │ │ ├── feedback/ # 피드백 컴포넌트
170
- │ │ │ ├── Alert/
171
- │ │ │ ├── Toast/
172
- │ │ │ └── Loading/
173
- │ │ ├── navigation/ # 네비게이션 컴포넌트
174
- │ │ │ ├── Pagination/
175
- │ │ │ └── Sidebar/
176
- │ │ ├── overlay/ # 오버레이 컴포넌트
177
- │ │ │ └── Modal/
178
- │ │ ├── display/ # 표시 컴포넌트
179
- │ │ │ └── Card/
180
- │ │ └── skeleton/ # 스켈레톤 컴포넌트
181
- │ │ ├── SkeletonCard/
182
- │ │ └── SkeletonList/
183
- │ ├── index.ts # Pure React 진입점
184
- │ └── next.ts # Next.js 진입점
185
- ├── package.json
186
- ├── tsconfig.json
187
- ├── tsup.config.ts
188
- └── README.md
189
- ```
105
+ ### General
190
106
 
191
- ---
107
+ #### Button
192
108
 
193
- ## Foundation (디자인 토큰)
109
+ ```tsx
110
+ import { Button } from '@bigtablet/design-system';
194
111
 
195
- Foundation은 **모든 컴포넌트의 기반이 되는 디자인 규칙**입니다. 컴포넌트 구현 시 임의의 값을 사용하지 않고 반드시 Foundation 토큰을 사용해야 합니다.
112
+ // 기본 사용
113
+ <Button>클릭</Button>
196
114
 
197
- ### 1. Colors (색상)
115
+ // Variants
116
+ <Button variant="primary">Primary</Button>
117
+ <Button variant="secondary">Secondary</Button>
118
+ <Button variant="ghost">Ghost</Button>
119
+ <Button variant="danger">Danger</Button>
198
120
 
199
- 브랜드 색상, 배경색, 텍스트 색상, 상태 색상을 정의합니다.
121
+ // Sizes
122
+ <Button size="sm">Small</Button>
123
+ <Button size="md">Medium</Button>
124
+ <Button size="lg">Large</Button>
200
125
 
201
- ```typescript
202
- // src/styles/ts/colors.ts
203
- export const colors = {
204
- brand: {
205
- primary: '#0066FF',
206
- secondary: '#00C896',
207
- },
208
- background: {
209
- primary: '#FFFFFF',
210
- secondary: '#F5F5F5',
211
- },
212
- text: {
213
- primary: '#1A1A1A',
214
- secondary: '#666666',
215
- },
216
- status: {
217
- success: '#22C55E',
218
- error: '#EF4444',
219
- warning: '#F59E0B',
220
- }
221
- };
126
+ // 너비 조절
127
+ <Button width="200px">고정 너비</Button>
128
+ <Button width="100%">전체 너비</Button>
222
129
  ```
223
130
 
224
- **사용 예시:**
225
- ```tsx
226
- <Button style={{ backgroundColor: colors.brand.primary }}>
227
- 클릭
228
- </Button>
229
- ```
131
+ | Prop | Type | Default | Description |
132
+ |------|------|---------|-------------|
133
+ | `variant` | `'primary' \| 'secondary' \| 'ghost' \| 'danger'` | `'primary'` | 버튼 스타일 |
134
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 버튼 크기 |
135
+ | `width` | `string` | `'100%'` | 버튼 너비 |
136
+ | `disabled` | `boolean` | `false` | 비활성화 |
230
137
 
231
- ### 2. Spacing (간격)
138
+ #### Select
232
139
 
233
- 일관된 레이아웃을 위한 margin, padding, gap 기준을 정의합니다.
140
+ ```tsx
141
+ import { Select } from '@bigtablet/design-system';
234
142
 
235
- ```typescript
236
- // src/styles/ts/spacing.ts
237
- export const spacing = {
238
- xs: '4px',
239
- sm: '8px',
240
- md: '16px',
241
- lg: '24px',
242
- xl: '32px',
243
- xxl: '48px',
244
- };
245
- ```
143
+ const options = [
144
+ { value: 'apple', label: '사과' },
145
+ { value: 'banana', label: '바나나' },
146
+ { value: 'orange', label: '오렌지', disabled: true },
147
+ ];
246
148
 
247
- ### 3. Typography (타이포그래피)
149
+ // 기본 사용
150
+ <Select
151
+ label="과일 선택"
152
+ options={options}
153
+ placeholder="선택하세요"
154
+ onChange={(value, option) => console.log(value, option)}
155
+ />
248
156
 
249
- 폰트 패밀리, 크기, 굵기, 줄 간격을 정의합니다.
157
+ // Controlled
158
+ const [fruit, setFruit] = useState<string | null>(null);
159
+ <Select
160
+ options={options}
161
+ value={fruit}
162
+ onChange={(value) => setFruit(value)}
163
+ />
250
164
 
251
- ```typescript
252
- // src/styles/ts/typography.ts
253
- export const typography = {
254
- fontFamily: {
255
- base: 'Pretendard, -apple-system, sans-serif',
256
- mono: 'Fira Code, monospace',
257
- },
258
- heading: {
259
- h1: { fontSize: '32px', lineHeight: '40px', fontWeight: 700 },
260
- h2: { fontSize: '24px', lineHeight: '32px', fontWeight: 700 },
261
- h3: { fontSize: '20px', lineHeight: '28px', fontWeight: 600 },
262
- },
263
- body: {
264
- large: { fontSize: '16px', lineHeight: '24px', fontWeight: 400 },
265
- medium: { fontSize: '14px', lineHeight: '20px', fontWeight: 400 },
266
- small: { fontSize: '12px', lineHeight: '16px', fontWeight: 400 },
267
- }
268
- };
165
+ // Variants & Sizes
166
+ <Select options={options} variant="outline" size="md" />
167
+ <Select options={options} variant="filled" size="lg" />
168
+ <Select options={options} variant="ghost" size="sm" />
269
169
  ```
270
170
 
271
- ### 4. Radius (둥근 모서리)
272
-
273
- 컴포넌트의 모서리 둥글기 기준을 정의합니다.
171
+ | Prop | Type | Default | Description |
172
+ |------|------|---------|-------------|
173
+ | `options` | `SelectOption[]` | required | 옵션 목록 |
174
+ | `value` | `string \| null` | - | 선택된 값 (controlled) |
175
+ | `defaultValue` | `string \| null` | `null` | 기본 선택값 |
176
+ | `onChange` | `(value, option) => void` | - | 변경 핸들러 |
177
+ | `placeholder` | `string` | `'Select…'` | 플레이스홀더 |
178
+ | `variant` | `'outline' \| 'filled' \| 'ghost'` | `'outline'` | 스타일 |
179
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 크기 |
180
+ | `fullWidth` | `boolean` | `false` | 전체 너비 |
181
+ | `disabled` | `boolean` | `false` | 비활성화 |
274
182
 
275
- ```typescript
276
- // src/styles/ts/radius.ts
277
- export const radius = {
278
- none: '0',
279
- sm: '4px',
280
- md: '8px',
281
- lg: '12px',
282
- full: '9999px',
283
- };
284
- ```
285
-
286
- ### 5. Shadows (그림자)
183
+ ---
287
184
 
288
- elevation 레벨에 따른 그림자를 정의합니다.
185
+ ### Form
289
186
 
290
- ```typescript
291
- // src/styles/ts/shadows.ts
292
- export const shadows = {
293
- sm: '0 1px 2px rgba(0, 0, 0, 0.05)',
294
- md: '0 4px 6px rgba(0, 0, 0, 0.1)',
295
- lg: '0 10px 15px rgba(0, 0, 0, 0.1)',
296
- xl: '0 20px 25px rgba(0, 0, 0, 0.15)',
297
- };
298
- ```
187
+ #### TextField
299
188
 
300
- ### 6. Motion (애니메이션)
189
+ ```tsx
190
+ import { TextField } from '@bigtablet/design-system';
301
191
 
302
- transition duration과 easing curve를 정의합니다.
192
+ // 기본 사용
193
+ <TextField label="이름" placeholder="이름을 입력하세요" />
303
194
 
304
- ```typescript
305
- // src/styles/ts/motion.ts
306
- export const motion = {
307
- duration: {
308
- fast: '150ms',
309
- normal: '300ms',
310
- slow: '500ms',
311
- },
312
- easing: {
313
- ease: 'ease',
314
- easeIn: 'ease-in',
315
- easeOut: 'ease-out',
316
- easeInOut: 'ease-in-out',
317
- }
318
- };
319
- ```
195
+ // 상태 표시
196
+ <TextField label="이메일" error helperText="유효하지 않은 이메일입니다" />
197
+ <TextField label="이메일" success helperText="사용 가능한 이메일입니다" />
320
198
 
321
- ### 7. Z-Index (레이어 우선순위)
199
+ // 아이콘
200
+ import { Search, Eye } from 'lucide-react';
201
+ <TextField leftIcon={<Search size={16} />} placeholder="검색..." />
202
+ <TextField rightIcon={<Eye size={16} />} type="password" />
322
203
 
323
- 컴포넌트 레이어의 우선순위를 정의합니다.
204
+ // Variants
205
+ <TextField variant="outline" label="Outline" />
206
+ <TextField variant="filled" label="Filled" />
207
+ <TextField variant="ghost" label="Ghost" />
324
208
 
325
- ```typescript
326
- // src/styles/ts/z-index.ts
327
- export const zIndex = {
328
- base: 0,
329
- dropdown: 1000,
330
- sticky: 1100,
331
- modal: 1300,
332
- toast: 1400,
333
- tooltip: 1500,
334
- };
209
+ // 값 변환 (자동 포맷팅)
210
+ <TextField
211
+ label="전화번호"
212
+ transformValue={(v) => v.replace(/\D/g, '').slice(0, 11)}
213
+ onChangeAction={(value) => console.log(value)}
214
+ />
335
215
  ```
336
216
 
337
- ### 8. Breakpoints (반응형 기준)
217
+ | Prop | Type | Default | Description |
218
+ |------|------|---------|-------------|
219
+ | `label` | `string` | - | 라벨 |
220
+ | `helperText` | `string` | - | 도움말 텍스트 |
221
+ | `error` | `boolean` | `false` | 에러 상태 |
222
+ | `success` | `boolean` | `false` | 성공 상태 |
223
+ | `variant` | `'outline' \| 'filled' \| 'ghost'` | `'outline'` | 스타일 |
224
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 크기 |
225
+ | `leftIcon` | `ReactNode` | - | 왼쪽 아이콘 |
226
+ | `rightIcon` | `ReactNode` | - | 오른쪽 아이콘 |
227
+ | `fullWidth` | `boolean` | `false` | 전체 너비 |
228
+ | `onChangeAction` | `(value: string) => void` | - | 값 변경 콜백 |
229
+ | `transformValue` | `(value: string) => string` | - | 값 변환 함수 |
338
230
 
339
- 반응형 디자인을 위한 화면 크기 기준을 정의합니다.
231
+ #### DatePicker
340
232
 
341
- ```typescript
342
- // src/styles/ts/breakpoints.ts
343
- export const breakpoints = {
344
- mobile: '320px',
345
- tablet: '768px',
346
- laptop: '1024px',
347
- desktop: '1440px',
348
- };
349
- ```
233
+ ```tsx
234
+ import { DatePicker } from '@bigtablet/design-system';
235
+
236
+ // 기본 사용 (연-월-일)
237
+ const [date, setDate] = useState('');
238
+ <DatePicker
239
+ label="생년월일"
240
+ value={date}
241
+ onChange={setDate}
242
+ />
350
243
 
351
- ### 9. Accessibility (접근성)
244
+ // 연-월 모드
245
+ <DatePicker
246
+ label="시작 월"
247
+ mode="year-month"
248
+ value={date}
249
+ onChange={setDate}
250
+ />
352
251
 
353
- 접근성을 위한 기준을 정의합니다.
252
+ // 범위 제한
253
+ <DatePicker
254
+ label="예약일"
255
+ startYear={2020}
256
+ endYear={2030}
257
+ selectableRange="until-today" // 오늘까지만 선택 가능
258
+ value={date}
259
+ onChange={setDate}
260
+ />
354
261
 
355
- ```typescript
356
- // src/styles/ts/a11y.ts
357
- export const a11y = {
358
- focusRing: '0 0 0 3px rgba(0, 102, 255, 0.3)',
359
- focusRingError: '0 0 0 3px rgba(239, 68, 68, 0.3)',
360
- tapMinSize: '44px', // 최소 터치 영역
361
- };
262
+ // 너비 조절
263
+ <DatePicker label="날짜" width={300} onChange={setDate} />
264
+ <DatePicker label="날짜" width="50%" onChange={setDate} />
362
265
  ```
363
266
 
364
- ---
365
-
366
- ## Components (UI 컴포넌트)
267
+ | Prop | Type | Default | Description |
268
+ |------|------|---------|-------------|
269
+ | `label` | `string` | - | 라벨 |
270
+ | `value` | `string` | - | 선택된 날짜 (`'YYYY-MM-DD'` 또는 `'YYYY-MM'`) |
271
+ | `onChange` | `(value: string) => void` | required | 변경 핸들러 |
272
+ | `mode` | `'year-month' \| 'year-month-day'` | `'year-month-day'` | 날짜 선택 모드 |
273
+ | `startYear` | `number` | `1950` | 시작 연도 |
274
+ | `endYear` | `number` | `현재년도 + 10` | 종료 연도 |
275
+ | `selectableRange` | `'all' \| 'until-today'` | `'all'` | 선택 가능 범위 |
276
+ | `minDate` | `string` | - | 최소 날짜 |
277
+ | `width` | `number \| string` | `'100%'` | 너비 |
278
+ | `disabled` | `boolean` | `false` | 비활성화 |
367
279
 
368
- Components는 Foundation 토큰을 기반으로 구현된 실제 UI 요소입니다.
369
-
370
- ### Form (입력 컴포넌트)
371
-
372
- #### Button
280
+ #### Checkbox
373
281
 
374
282
  ```tsx
375
- <Button variant="primary" size="md" onClick={() => alert('클릭!')}>
376
- 기본 버튼
377
- </Button>
378
- ```
283
+ import { Checkbox } from '@bigtablet/design-system';
379
284
 
380
- **Props:**
381
- - `variant`: `primary` | `secondary` | `ghost` | `danger`
382
- - `size`: `sm` | `md` | `lg`
383
- - `disabled`: `boolean`
384
- - `loading`: `boolean`
285
+ // 기본 사용
286
+ <Checkbox label="동의합니다" />
385
287
 
386
- #### TextField
387
-
388
- ```tsx
389
- <TextField
390
- label="이메일"
391
- type="email"
392
- placeholder="email@example.com"
393
- helperText="이메일을 입력해주세요"
394
- error={false}
395
- leftIcon={<EmailIcon />}
288
+ // Controlled
289
+ const [checked, setChecked] = useState(false);
290
+ <Checkbox
291
+ label="알림 받기"
292
+ checked={checked}
293
+ onChange={(e) => setChecked(e.target.checked)}
396
294
  />
397
- ```
398
-
399
- **Props:**
400
- - `label`: `string`
401
- - `type`: `text` | `email` | `password` | `number`
402
- - `error`: `boolean`
403
- - `helperText`: `string`
404
- - `leftIcon` / `rightIcon`: `ReactNode`
405
295
 
406
- #### Checkbox
296
+ // Indeterminate (부분 선택)
297
+ <Checkbox label="전체 선택" indeterminate />
407
298
 
408
- ```tsx
409
- <Checkbox
410
- checked={isChecked}
411
- onChange={(e) => setIsChecked(e.target.checked)}
412
- label="동의합니다"
413
- indeterminate={false}
414
- />
299
+ // Sizes
300
+ <Checkbox size="sm" label="Small" />
301
+ <Checkbox size="md" label="Medium" />
302
+ <Checkbox size="lg" label="Large" />
415
303
  ```
416
304
 
305
+ | Prop | Type | Default | Description |
306
+ |------|------|---------|-------------|
307
+ | `label` | `ReactNode` | - | 라벨 |
308
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 크기 |
309
+ | `indeterminate` | `boolean` | `false` | 부분 선택 상태 |
310
+ | `checked` | `boolean` | - | 체크 상태 |
311
+ | `disabled` | `boolean` | `false` | 비활성화 |
312
+
417
313
  #### Radio
418
314
 
419
315
  ```tsx
316
+ import { Radio } from '@bigtablet/design-system';
317
+
318
+ const [selected, setSelected] = useState('option1');
319
+
420
320
  <Radio
421
- name="option"
422
- value="1"
423
- checked={selected === '1'}
424
- onChange={(e) => setSelected(e.target.value)}
321
+ name="options"
322
+ value="option1"
425
323
  label="옵션 1"
324
+ checked={selected === 'option1'}
325
+ onChange={(e) => setSelected(e.target.value)}
326
+ />
327
+ <Radio
328
+ name="options"
329
+ value="option2"
330
+ label="옵션 2"
331
+ checked={selected === 'option2'}
332
+ onChange={(e) => setSelected(e.target.value)}
426
333
  />
427
334
  ```
428
335
 
429
336
  #### Switch
430
337
 
431
338
  ```tsx
432
- <Switch
433
- checked={isOn}
434
- onChange={(checked) => setIsOn(checked)}
435
- label="알림 받기"
436
- />
437
- ```
339
+ import { Switch } from '@bigtablet/design-system';
438
340
 
439
- #### Select
341
+ // 기본 사용
342
+ <Switch onChange={(checked) => console.log(checked)} />
440
343
 
441
- ```tsx
442
- <Select
443
- options={[
444
- { value: '1', label: '옵션 1' },
445
- { value: '2', label: '옵션 2' },
446
- ]}
447
- value={selected}
448
- onChange={(value) => setSelected(value)}
449
- placeholder="선택하세요"
450
- />
344
+ // Controlled
345
+ const [isOn, setIsOn] = useState(false);
346
+ <Switch checked={isOn} onChange={setIsOn} />
347
+
348
+ // Sizes
349
+ <Switch size="sm" />
350
+ <Switch size="md" />
351
+ <Switch size="lg" />
451
352
  ```
452
353
 
354
+ | Prop | Type | Default | Description |
355
+ |------|------|---------|-------------|
356
+ | `checked` | `boolean` | - | 켜짐 상태 (controlled) |
357
+ | `defaultChecked` | `boolean` | `false` | 기본 상태 |
358
+ | `onChange` | `(checked: boolean) => void` | - | 변경 핸들러 |
359
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 크기 |
360
+ | `disabled` | `boolean` | `false` | 비활성화 |
361
+
453
362
  #### FileInput
454
363
 
455
364
  ```tsx
365
+ import { FileInput } from '@bigtablet/design-system';
366
+
456
367
  <FileInput
457
- accept="image/*"
458
- onChange={(file) => console.log(file)}
459
368
  label="파일 선택"
369
+ accept="image/*"
370
+ onFiles={(files) => console.log(files)}
371
+ />
372
+
373
+ // 여러 파일
374
+ <FileInput
375
+ label="이미지 업로드"
376
+ accept="image/*"
377
+ multiple
378
+ onFiles={(files) => console.log(files)}
460
379
  />
461
380
  ```
462
381
 
382
+ | Prop | Type | Default | Description |
383
+ |------|------|---------|-------------|
384
+ | `label` | `string` | `'파일 선택'` | 버튼 라벨 |
385
+ | `accept` | `string` | - | 허용 파일 타입 |
386
+ | `onFiles` | `(files: FileList \| null) => void` | - | 파일 선택 핸들러 |
387
+ | `multiple` | `boolean` | `false` | 다중 선택 |
388
+ | `disabled` | `boolean` | `false` | 비활성화 |
389
+
463
390
  ---
464
391
 
465
- ### Feedback (피드백 컴포넌트)
392
+ ### Feedback
466
393
 
467
394
  #### Alert
468
395
 
469
396
  ```tsx
470
- import { useAlert } from '@bigtablet/design-system';
471
-
472
- function Component() {
473
- const alert = useAlert();
474
-
475
- const handleClick = () => {
476
- alert.show({
477
- title: '알림',
478
- message: '작업이 완료되었습니다.',
479
- variant: 'success',
480
- onConfirm: () => console.log('확인'),
481
- onCancel: () => console.log('취소'),
397
+ import { AlertProvider, useAlert } from '@bigtablet/design-system';
398
+
399
+ // App에 Provider 추가
400
+ function App() {
401
+ return (
402
+ <AlertProvider>
403
+ <YourComponent />
404
+ </AlertProvider>
405
+ );
406
+ }
407
+
408
+ // 사용
409
+ function YourComponent() {
410
+ const { showAlert } = useAlert();
411
+
412
+ const handleDelete = () => {
413
+ showAlert({
414
+ variant: 'warning',
415
+ title: '삭제 확인',
416
+ message: '정말 삭제하시겠습니까?',
417
+ showCancel: true,
418
+ confirmText: '삭제',
419
+ cancelText: '취소',
420
+ onConfirm: () => console.log('삭제됨'),
421
+ onCancel: () => console.log('취소됨'),
482
422
  });
483
423
  };
424
+
425
+ return <button onClick={handleDelete}>삭제</button>;
484
426
  }
485
427
  ```
486
428
 
487
- **Variants:** `info` | `success` | `warning` | `error`
429
+ | Option | Type | Default | Description |
430
+ |--------|------|---------|-------------|
431
+ | `variant` | `'info' \| 'success' \| 'warning' \| 'error'` | `'info'` | 알림 타입 |
432
+ | `title` | `ReactNode` | - | 제목 |
433
+ | `message` | `ReactNode` | - | 메시지 |
434
+ | `confirmText` | `string` | `'확인'` | 확인 버튼 텍스트 |
435
+ | `cancelText` | `string` | `'취소'` | 취소 버튼 텍스트 |
436
+ | `showCancel` | `boolean` | `false` | 취소 버튼 표시 |
437
+ | `actionsAlign` | `'left' \| 'center' \| 'right'` | `'right'` | 버튼 정렬 |
438
+ | `onConfirm` | `() => void` | - | 확인 핸들러 |
439
+ | `onCancel` | `() => void` | - | 취소 핸들러 |
488
440
 
489
441
  #### Toast
490
442
 
491
443
  ```tsx
492
444
  import { ToastProvider, useToast } from '@bigtablet/design-system';
493
445
 
446
+ // App에 Provider 추가
494
447
  function App() {
495
448
  return (
496
- <ToastProvider>
449
+ <>
450
+ <ToastProvider />
497
451
  <YourComponent />
498
- </ToastProvider>
452
+ </>
499
453
  );
500
454
  }
501
455
 
456
+ // 사용
502
457
  function YourComponent() {
503
458
  const toast = useToast();
504
-
459
+
505
460
  return (
506
- <button onClick={() => toast.success('저장되었습니다')}>
507
- 저장
508
- </button>
461
+ <div>
462
+ <button onClick={() => toast.success('저장되었습니다')}>성공</button>
463
+ <button onClick={() => toast.error('오류가 발생했습니다')}>에러</button>
464
+ <button onClick={() => toast.warning('주의가 필요합니다')}>경고</button>
465
+ <button onClick={() => toast.info('참고 정보입니다')}>정보</button>
466
+ <button onClick={() => toast.message('일반 메시지')}>메시지</button>
467
+ </div>
509
468
  );
510
469
  }
511
470
  ```
512
471
 
513
- **메서드:**
514
- - `toast.success(message)`
515
- - `toast.error(message)`
516
- - `toast.warning(message)`
517
- - `toast.info(message)`
518
- - `toast.message(message)`
472
+ #### Spinner
519
473
 
520
- #### Loading
474
+ ```tsx
475
+ import { Spinner } from '@bigtablet/design-system';
476
+
477
+ <Spinner /> // 기본 (24px)
478
+ <Spinner size={16} /> // 작은 크기
479
+ <Spinner size={48} /> // 큰 크기
480
+ ```
481
+
482
+ #### TopLoading
521
483
 
522
484
  ```tsx
523
- <Loading size="md" />
485
+ import { TopLoading } from '@bigtablet/design-system';
486
+
487
+ // Indeterminate (무한 로딩)
488
+ <TopLoading isLoading />
489
+
490
+ // Progress (진행률 표시)
491
+ <TopLoading isLoading progress={65} />
492
+
493
+ // 커스텀 스타일
494
+ <TopLoading isLoading color="#ff0000" height={5} />
495
+
496
+ // 숨기기
497
+ <TopLoading isLoading={false} />
524
498
  ```
525
499
 
526
- **Props:**
527
- - `size`: `sm` | `md` | `lg`
500
+ | Prop | Type | Default | Description |
501
+ |------|------|---------|-------------|
502
+ | `isLoading` | `boolean` | `true` | 표시 여부 |
503
+ | `progress` | `number` | - | 진행률 (0-100), 없으면 indeterminate |
504
+ | `color` | `string` | primary | 로딩바 색상 |
505
+ | `height` | `number` | `3` | 로딩바 높이 (px) |
528
506
 
529
507
  ---
530
508
 
531
- ### Navigation (네비게이션 컴포넌트)
509
+ ### Navigation
532
510
 
533
511
  #### Pagination
534
512
 
535
513
  ```tsx
514
+ import { Pagination } from '@bigtablet/design-system';
515
+
516
+ const [page, setPage] = useState(1);
517
+
536
518
  <Pagination
537
- currentPage={page}
538
- totalPages={10}
539
- onPageChange={(newPage) => setPage(newPage)}
519
+ page={page}
520
+ totalPages={20}
521
+ onChange={setPage}
540
522
  />
541
523
  ```
542
524
 
543
- #### Sidebar (Next.js 전용)
525
+ | Prop | Type | Default | Description |
526
+ |------|------|---------|-------------|
527
+ | `page` | `number` | required | 현재 페이지 |
528
+ | `totalPages` | `number` | required | 전체 페이지 수 |
529
+ | `onChange` | `(page: number) => void` | required | 페이지 변경 핸들러 |
530
+
531
+ #### Sidebar (Next.js)
544
532
 
545
533
  ```tsx
546
534
  import { Sidebar } from '@bigtablet/design-system/next';
535
+ import { Home, Settings, Users } from 'lucide-react';
536
+
537
+ const items = [
538
+ { href: '/', label: '홈', icon: Home },
539
+ { href: '/users', label: '사용자', icon: Users },
540
+ {
541
+ type: 'group',
542
+ id: 'settings',
543
+ label: '설정',
544
+ icon: Settings,
545
+ children: [
546
+ { href: '/settings/profile', label: '프로필' },
547
+ { href: '/settings/security', label: '보안' },
548
+ ],
549
+ },
550
+ ];
547
551
 
548
552
  <Sidebar
549
- items={[
550
- { label: '홈', href: '/', icon: <HomeIcon /> },
551
- { label: '대시보드', href: '/dashboard', icon: <DashboardIcon /> },
552
- ]}
553
- matchMode="startsWith" // 'startsWith' | 'exact'
553
+ items={items}
554
+ activePath={pathname}
555
+ match="startsWith"
556
+ brandHref="/main"
554
557
  />
555
558
  ```
556
559
 
560
+ | Prop | Type | Default | Description |
561
+ |------|------|---------|-------------|
562
+ | `items` | `SidebarItem[]` | `[]` | 메뉴 아이템 |
563
+ | `activePath` | `string` | - | 현재 활성 경로 |
564
+ | `match` | `'startsWith' \| 'exact'` | `'startsWith'` | 경로 매칭 방식 |
565
+ | `brandHref` | `string` | `'/main'` | 로고 클릭 시 이동 경로 |
566
+ | `onItemSelect` | `(href: string) => void` | - | 아이템 선택 핸들러 |
567
+
557
568
  ---
558
569
 
559
- ### Overlay (오버레이 컴포넌트)
570
+ ### Overlay
560
571
 
561
572
  #### Modal
562
573
 
563
574
  ```tsx
575
+ import { Modal } from '@bigtablet/design-system';
576
+
577
+ const [isOpen, setIsOpen] = useState(false);
578
+
579
+ <button onClick={() => setIsOpen(true)}>모달 열기</button>
580
+
564
581
  <Modal
565
- isOpen={isOpen}
582
+ open={isOpen}
566
583
  onClose={() => setIsOpen(false)}
567
584
  title="모달 제목"
585
+ width={600}
568
586
  >
569
- <p>모달 내용</p>
587
+ <p>모달 내용입니다.</p>
570
588
  </Modal>
571
589
  ```
572
590
 
573
- **Props:**
574
- - `isOpen`: `boolean`
575
- - `onClose`: `() => void`
576
- - `title`: `string` (optional)
577
- - `closeOnOverlayClick`: `boolean` (default: `true`)
578
- - `closeOnEsc`: `boolean` (default: `true`)
591
+ | Prop | Type | Default | Description |
592
+ |------|------|---------|-------------|
593
+ | `open` | `boolean` | required | 열림 상태 |
594
+ | `onClose` | `() => void` | - | 닫기 핸들러 |
595
+ | `title` | `ReactNode` | - | 제목 |
596
+ | `width` | `number \| string` | `520` | 모달 너비 |
597
+ | `closeOnOverlay` | `boolean` | `true` | 오버레이 클릭 시 닫기 |
579
598
 
580
599
  ---
581
600
 
582
- ### Display (표시 컴포넌트)
601
+ ### Display
583
602
 
584
603
  #### Card
585
604
 
586
605
  ```tsx
587
- <Card elevation="md" padding="lg">
588
- <h3>카드 제목</h3>
589
- <p>카드 내용</p>
606
+ import { Card } from '@bigtablet/design-system';
607
+
608
+ <Card heading="카드 제목">
609
+ <p>카드 내용입니다.</p>
610
+ </Card>
611
+
612
+ // 스타일 옵션
613
+ <Card heading="제목" shadow="lg" padding="lg" bordered>
614
+ 내용
590
615
  </Card>
591
616
  ```
592
617
 
593
- **Props:**
594
- - `elevation`: `sm` | `md` | `lg` | `xl`
595
- - `padding`: `sm` | `md` | `lg` | `xl`
618
+ | Prop | Type | Default | Description |
619
+ |------|------|---------|-------------|
620
+ | `heading` | `ReactNode` | - | 카드 제목 |
621
+ | `shadow` | `'none' \| 'sm' \| 'md' \| 'lg'` | `'sm'` | 그림자 |
622
+ | `padding` | `'none' \| 'sm' \| 'md' \| 'lg'` | `'md'` | 내부 여백 |
623
+ | `bordered` | `boolean` | `false` | 테두리 표시 |
596
624
 
597
625
  ---
598
626
 
599
- ### Skeleton (로딩 상태 컴포넌트)
627
+ ## Foundation (디자인 토큰)
600
628
 
601
- #### SkeletonCard
629
+ ### SCSS 토큰 사용
602
630
 
603
- ```tsx
604
- <SkeletonCard />
631
+ ```scss
632
+ @use "@bigtablet/design-system/scss/token" as token;
633
+
634
+ .my-component {
635
+ color: token.$color_primary;
636
+ padding: token.$spacing_md;
637
+ border-radius: token.$radius_sm;
638
+ font-size: token.$font_size_base;
639
+ }
605
640
  ```
606
641
 
607
- #### SkeletonList
642
+ ### 주요 토큰
643
+
644
+ | Category | Examples |
645
+ |----------|----------|
646
+ | **Colors** | `$color_primary`, `$color_error`, `$color_text_primary` |
647
+ | **Spacing** | `$spacing_xs` (4px), `$spacing_sm` (8px), `$spacing_md` (16px) |
648
+ | **Typography** | `$font_size_sm`, `$font_size_base`, `$font_weight_medium` |
649
+ | **Radius** | `$radius_sm` (4px), `$radius_md` (8px), `$radius_lg` (12px) |
650
+ | **Shadows** | `$shadow_sm`, `$shadow_md`, `$shadow_lg` |
651
+ | **Z-Index** | `$z_dropdown`, `$z_modal`, `$z_toast` |
652
+
653
+ ---
654
+
655
+ ## 프로젝트 구조
608
656
 
609
- ```tsx
610
- <SkeletonList count={5} />
657
+ ```
658
+ src/
659
+ ├── styles/
660
+ │ ├── ts/ # TypeScript 디자인 토큰
661
+ │ └── scss/ # SCSS 토큰 및 믹스인
662
+ ├── ui/
663
+ │ ├── general/ # Button, Select
664
+ │ ├── form/ # TextField, Checkbox, Radio, Switch, DatePicker, FileInput
665
+ │ ├── feedback/ # Alert, Toast, Spinner, TopLoading
666
+ │ ├── navigation/ # Pagination, Sidebar
667
+ │ ├── overlay/ # Modal
668
+ │ └── display/ # Card
669
+ ├── index.ts # Pure React 진입점
670
+ └── next.ts # Next.js 진입점
611
671
  ```
612
672
 
613
673
  ---
614
674
 
615
675
  ## 개발 가이드
616
676
 
617
- ### 로컬 개발 환경 설정
618
-
619
677
  ```bash
620
678
  # 저장소 클론
621
679
  git clone https://github.com/Bigtablet/bigtablet-design-system.git
@@ -629,151 +687,39 @@ pnpm storybook
629
687
 
630
688
  # 빌드
631
689
  pnpm build
632
-
633
- # 테스트
634
- pnpm test
635
690
  ```
636
691
 
637
- ### Storybook 가이드라인
638
-
639
- 1. **Title 규칙**
640
- - Foundation: `foundation/Colors`, `foundation/Typography`
641
- - Components: `components/Button`, `components/TextField`
642
-
643
- 2. **Story 작성 원칙**
644
- - 기본 상태(Default) 필수 포함
645
- - 모든 variant와 size 예시 제공
646
- - 실제 사용 사례 중심으로 작성
647
- - 명확한 설명과 문서화
648
-
649
- 3. **예시**
650
-
651
- ```tsx
652
- // Button.stories.tsx
653
- import type { Meta, StoryObj } from '@storybook/react';
654
- import { Button } from './Button';
655
-
656
- const meta: Meta<typeof Button> = {
657
- title: 'components/Button',
658
- component: Button,
659
- tags: ['autodocs'],
660
- };
661
-
662
- export default meta;
663
- type Story = StoryObj<typeof Button>;
664
-
665
- export const Default: Story = {
666
- args: {
667
- children: '기본 버튼',
668
- variant: 'primary',
669
- size: 'md',
670
- },
671
- };
672
-
673
- export const Variants: Story = {
674
- render: () => (
675
- <>
676
- <Button variant="primary">Primary</Button>
677
- <Button variant="secondary">Secondary</Button>
678
- <Button variant="ghost">Ghost</Button>
679
- <Button variant="danger">Danger</Button>
680
- </>
681
- ),
682
- };
683
- ```
684
-
685
- ### 컴포넌트 개발 원칙
686
-
687
- 1. **Foundation 토큰 사용 필수**
688
- - 직접적인 색상/크기 값 사용 금지
689
- - 모든 스타일은 토큰을 통해 정의
690
-
691
- 2. **접근성(a11y) 고려**
692
- - 키보드 네비게이션 지원
693
- - 스크린 리더 호환
694
- - 적절한 ARIA 속성 사용
695
-
696
- 3. **상태 관리**
697
- - hover, active, disabled, error 등 명확히 정의
698
- - loading 상태 제공
699
-
700
- 4. **TypeScript 타입**
701
- - Props 타입 명확히 정의
702
- - Generic 타입 적절히 활용
703
-
704
692
  ---
705
693
 
706
694
  ## 기여하기
707
695
 
708
- 기여는 언제나 환영합니다! 다음 절차를 따라주세요:
709
-
710
696
  1. Fork the repository
711
- 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
712
- 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
713
- 4. Push to the branch (`git push origin feature/amazing-feature`)
714
- 5. Open a Pull Request
697
+ 2. Create your feature branch (`git checkout -b feat/amazing-feature`)
698
+ 3. Commit your changes (`git commit -m 'feat: add amazing feature'`)
699
+ 4. Push to the branch (`git push origin feat/amazing-feature`)
700
+ 5. Open a Pull Request to `develop` branch
715
701
 
716
702
  ### Commit Convention
717
703
 
718
- ```
719
- feat: 새로운 기능 추가
720
- fix: 버그 수정
721
- docs: 문서 수정
722
- style: 코드 포맷팅, 세미콜론 누락
723
- refactor: 코드 리팩토링
724
- test: 테스트 코드
725
- chore: 빌드 업무, 패키지 매니저 설정 등
726
- ```
727
-
728
- ---
729
-
730
- ## 버전 관리
731
-
732
- 이 프로젝트는 [Changesets](https://github.com/changesets/changesets)를 사용하여 버전을 관리합니다.
733
-
734
- ### 변경사항 추가
735
-
736
- ```bash
737
- pnpm changeset
738
- ```
739
-
740
- ---
741
-
742
- ## 기술 스택
743
-
744
- | Category | Technology |
745
- |----------|------------|
746
- | **Framework** | React 19 |
747
- | **Language** | TypeScript |
748
- | **Styling** | SCSS |
749
- | **Documentation** | Storybook 8 |
750
- | **Visual Test** | Chromatic |
751
- | **Build** | tsup |
752
- | **Package Manager** | pnpm |
753
- | **CI/CD** | GitHub Actions |
754
- | **Version Management** | Changesets |
755
-
756
- ---
757
-
758
- ## 브라우저 지원
759
-
760
- - Chrome (최신 2개 버전)
761
- - Firefox (최신 2개 버전)
762
- - Safari (최신 2개 버전)
763
- - Edge (최신 2개 버전)
704
+ | Label | Description |
705
+ |-------|-------------|
706
+ | `feat` | 새로운 기능 |
707
+ | `fix` | 버그/코드 수정 |
708
+ | `docs` | 문서 수정 |
709
+ | `style` | 코드 스타일 변경 |
710
+ | `config` | 설정 파일 수정 |
764
711
 
765
712
  ---
766
713
 
767
714
  ## 라이센스
768
715
 
769
- [Bigtablet License](https://github.com/Bigtablet/.github/blob/main/BIGTABLET_LICENSE.md) 를 참고해주세요
716
+ [Bigtablet License](https://github.com/Bigtablet/.github/blob/main/BIGTABLET_LICENSE.md)
770
717
 
771
718
  ---
772
719
 
773
720
  ## 링크
774
721
 
775
- - [Github Link](https://bigtablet.github.io/bigtablet-design-system)
776
- - [NPM 패키지](https://www.npmjs.com/package/@bigtablet/design-system)
777
- - [이슈 트래커](https://github.com/Bigtablet/bigtablet-design-system/issues)
778
- - [토론](https://github.com/Bigtablet/bigtablet-design-system/discussions)
779
-
722
+ - [GitHub](https://github.com/Bigtablet/bigtablet-design-system)
723
+ - [NPM](https://www.npmjs.com/package/@bigtablet/design-system)
724
+ - [Storybook](https://bigtablet.github.io/bigtablet-design-system)
725
+ - [Issues](https://github.com/Bigtablet/bigtablet-design-system/issues)
package/dist/index.css CHANGED
@@ -377,6 +377,23 @@
377
377
  opacity: 0.6;
378
378
  cursor: not-allowed;
379
379
  }
380
+ .checkbox_input:disabled:not(:checked):not(:indeterminate) + .checkbox_box::before,
381
+ .checkbox_input:disabled:not(:checked):not(:indeterminate) + .checkbox_box::after {
382
+ content: "";
383
+ position: absolute;
384
+ left: 50%;
385
+ top: 50%;
386
+ width: 0.7rem;
387
+ height: 2px;
388
+ background: #9ca3af;
389
+ border-radius: 9999px;
390
+ }
391
+ .checkbox_input:disabled:not(:checked):not(:indeterminate) + .checkbox_box::before {
392
+ transform: translate(-50%, -50%) rotate(45deg);
393
+ }
394
+ .checkbox_input:disabled:not(:checked):not(:indeterminate) + .checkbox_box::after {
395
+ transform: translate(-50%, -50%) rotate(-45deg);
396
+ }
380
397
  .checkbox_input:checked + .checkbox_box,
381
398
  .checkbox_input:indeterminate + .checkbox_box {
382
399
  background: #000000;
package/dist/index.d.ts CHANGED
@@ -151,12 +151,7 @@ interface DatePickerProps {
151
151
  minDate?: string;
152
152
  selectableRange?: SelectableRange;
153
153
  disabled?: boolean;
154
- width?: {
155
- container?: number | string;
156
- year?: number | string;
157
- month?: number | string;
158
- day?: number | string;
159
- };
154
+ width?: number | string;
160
155
  }
161
156
  declare const DatePicker: ({ label, value, onChange, mode, startYear, endYear, minDate, selectableRange, disabled, width, }: DatePickerProps) => react_jsx_runtime.JSX.Element;
162
157
 
package/dist/index.js CHANGED
@@ -679,18 +679,18 @@ var DatePicker = ({
679
679
  const safeDay = clampDay(yy, mm, dd ?? 1);
680
680
  onChange(`${yy}-${pad(mm)}-${pad(safeDay)}`);
681
681
  };
682
- return /* @__PURE__ */ jsxs("div", { className: "date_picker", style: { width: normalizeWidth(width?.container) }, children: [
682
+ const containerStyle = width ? { width: normalizeWidth(width) } : void 0;
683
+ return /* @__PURE__ */ jsxs("div", { className: "date_picker date_picker_full", style: containerStyle, children: [
683
684
  label && /* @__PURE__ */ jsx("label", { className: "date_picker_label", children: label }),
684
685
  /* @__PURE__ */ jsxs("div", { className: "date_picker_fields", children: [
685
686
  /* @__PURE__ */ jsxs(
686
687
  "select",
687
688
  {
688
- style: { width: normalizeWidth(width?.year) },
689
689
  value: year,
690
690
  disabled,
691
691
  onChange: (e) => emit(Number(e.target.value), month || minMonth, day || minDay),
692
692
  children: [
693
- /* @__PURE__ */ jsx("option", { value: "", disabled: true }),
693
+ /* @__PURE__ */ jsx("option", { value: "", children: "\uC5F0\uB3C4" }),
694
694
  Array.from(
695
695
  { length: maxYear - startYear + 1 },
696
696
  (_, i) => startYear + i
@@ -701,12 +701,11 @@ var DatePicker = ({
701
701
  /* @__PURE__ */ jsxs(
702
702
  "select",
703
703
  {
704
- style: { width: normalizeWidth(width?.month) },
705
704
  value: month,
706
705
  disabled: disabled || !year,
707
706
  onChange: (e) => emit(year, Number(e.target.value), day || minDay),
708
707
  children: [
709
- /* @__PURE__ */ jsx("option", { value: "", disabled: true }),
708
+ /* @__PURE__ */ jsx("option", { value: "", children: "\uC6D4" }),
710
709
  Array.from({ length: maxMonth - minMonth + 1 }, (_, i) => minMonth + i).map(
711
710
  (m2) => /* @__PURE__ */ jsx("option", { value: m2, children: pad(m2) }, m2)
712
711
  )
@@ -716,12 +715,11 @@ var DatePicker = ({
716
715
  mode === "year-month-day" && /* @__PURE__ */ jsxs(
717
716
  "select",
718
717
  {
719
- style: { width: normalizeWidth(width?.day) },
720
718
  value: day,
721
719
  disabled: disabled || !month,
722
720
  onChange: (e) => emit(year, month, Number(e.target.value)),
723
721
  children: [
724
- /* @__PURE__ */ jsx("option", { value: "", disabled: true }),
722
+ /* @__PURE__ */ jsx("option", { value: "", children: "\uC77C" }),
725
723
  Array.from(
726
724
  { length: days - minDay + 1 },
727
725
  (_, i) => minDay + i
@@ -741,12 +739,23 @@ var getPaginationItems = (page, totalPages) => {
741
739
  if (totalPages <= 7) return range(1, totalPages);
742
740
  const items = [];
743
741
  const last = totalPages;
744
- const start = Math.max(2, page - 1);
745
- const end = Math.min(last - 1, page + 1);
742
+ const sibling = 2;
743
+ if (page <= sibling + 2) {
744
+ for (const p of range(1, sibling + 3)) items.push(p);
745
+ items.push("ellipsis");
746
+ items.push(last);
747
+ return items;
748
+ }
749
+ if (page >= last - sibling - 1) {
750
+ items.push(1);
751
+ items.push("ellipsis");
752
+ for (const p of range(last - sibling - 2, last)) items.push(p);
753
+ return items;
754
+ }
746
755
  items.push(1);
747
- if (start > 2) items.push("ellipsis");
748
- for (const p of range(start, end)) items.push(p);
749
- if (end < last - 1) items.push("ellipsis");
756
+ items.push("ellipsis");
757
+ for (const p of range(page - sibling, page + sibling)) items.push(p);
758
+ items.push("ellipsis");
750
759
  items.push(last);
751
760
  return items;
752
761
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bigtablet/design-system",
3
- "version": "1.14.1",
3
+ "version": "1.14.3",
4
4
  "description": "Bigtablet Design System UI Components",
5
5
  "type": "module",
6
6
  "types": "dist/index.d.ts",