@bigtablet/design-system 1.19.3 โ 1.20.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 +10 -2
- package/dist/index.d.ts +15 -3
- package/dist/index.js +176 -143
- package/dist/next.js +5 -3
- package/dist/styles/scss/_colors.scss +4 -0
- package/package.json +27 -8
package/README.md
CHANGED
|
@@ -7,11 +7,16 @@
|
|
|
7
7
|
[](https://www.npmjs.com/package/@bigtablet/design-system)
|
|
8
8
|
[](LICENSE)
|
|
9
9
|
[](https://github.com/Bigtablet/bigtablet-design-system/actions)
|
|
10
|
+
[](https://github.com/Bigtablet/bigtablet-design-system/actions/workflows/ci.yml)
|
|
11
|
+
[](https://bigtablet.github.io/bigtablet-design-system)
|
|
10
12
|
|
|
11
13
|
[๐ฐ๐ท ํ๊ตญ์ด](./README_KR.md) ยท ๐บ๐ธ English
|
|
12
14
|
|
|
13
15
|
The official design system of Bigtablet โ a unified UI library composed of Foundation (design tokens) and UI Components.
|
|
14
16
|
|
|
17
|
+
> **Note**: This is Bigtablet's in-house design system, open-sourced for community reference.
|
|
18
|
+
> External use is welcome, but minor versions may include breaking changes without prior notice.
|
|
19
|
+
|
|
15
20
|
[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)
|
|
16
21
|
|
|
17
22
|
</div>
|
|
@@ -30,6 +35,7 @@ The official design system of Bigtablet โ a unified UI library composed of Fou
|
|
|
30
35
|
| โฟ **Accessibility** | Keyboard navigation, screen reader support, full ARIA attributes |
|
|
31
36
|
| ๐งช **86% Coverage** | Stable test coverage powered by Vitest |
|
|
32
37
|
| ๐ญ **Storybook** | Interactive documentation for all components |
|
|
38
|
+
| ๐ฏ **Zero Dependencies** | No bundled runtime dependencies โ peer deps only |
|
|
33
39
|
|
|
34
40
|
---
|
|
35
41
|
|
|
@@ -49,15 +55,17 @@ pnpm add @bigtablet/design-system
|
|
|
49
55
|
**Peer Dependencies**
|
|
50
56
|
|
|
51
57
|
```bash
|
|
52
|
-
npm install react react-dom lucide-react
|
|
58
|
+
npm install react@^19 react-dom@^19 "lucide-react@>=0.552.0"
|
|
53
59
|
```
|
|
54
60
|
|
|
55
|
-
>
|
|
61
|
+
> Requires **React 19** and **lucide-react โฅ 0.552.0**. Compatible with **Next.js 13+**.
|
|
56
62
|
|
|
57
63
|
---
|
|
58
64
|
|
|
59
65
|
## Quick Start
|
|
60
66
|
|
|
67
|
+
> โ ๏ธ **Alert** and **Toast** require Providers at the root of your app โ see [Provider Setup](#provider-setup) below.
|
|
68
|
+
|
|
61
69
|
### Pure React
|
|
62
70
|
|
|
63
71
|
```tsx
|
package/dist/index.d.ts
CHANGED
|
@@ -99,6 +99,8 @@ interface ToastProviderProps {
|
|
|
99
99
|
children: React.ReactNode;
|
|
100
100
|
/** ์ต๋ ๋์ ํ์ ํ ์คํธ ์ (๊ธฐ๋ณธ๊ฐ: 5) */
|
|
101
101
|
maxCount?: number;
|
|
102
|
+
/** ํ ์คํธ ๋ซ๊ธฐ ๋ฒํผ์ aria-label (๊ธฐ๋ณธ๊ฐ: "Close") */
|
|
103
|
+
closeAriaLabel?: string;
|
|
102
104
|
}
|
|
103
105
|
/**
|
|
104
106
|
* ํ ์คํธ ์ปจํ
์คํธ๋ฅผ ์ ๊ณตํ๋ Provider๋ฅผ ๋ ๋๋งํ๋ค.
|
|
@@ -106,7 +108,7 @@ interface ToastProviderProps {
|
|
|
106
108
|
* @param props Provider ์์ฑ
|
|
107
109
|
* @returns ๋ ๋๋ง๋ Provider์ ํ ์คํธ ์ปจํ
์ด๋
|
|
108
110
|
*/
|
|
109
|
-
declare const ToastProvider: ({ children, maxCount }: ToastProviderProps) => react_jsx_runtime.JSX.Element;
|
|
111
|
+
declare const ToastProvider: ({ children, maxCount, closeAriaLabel }: ToastProviderProps) => react_jsx_runtime.JSX.Element;
|
|
110
112
|
|
|
111
113
|
/**
|
|
112
114
|
* ํ ์คํธ ๋ฉ์์ง๋ฅผ ํ์ํ๋ ํ
.
|
|
@@ -326,6 +328,12 @@ interface DatePickerProps {
|
|
|
326
328
|
* @deprecated `fullWidth` ์ฌ์ฉ ๋๋ CSS๋ก ์ฒ๋ฆฌ
|
|
327
329
|
*/
|
|
328
330
|
width?: number | string;
|
|
331
|
+
/** ์ฐ๋ select์ aria-label ๋ฐ ๋น ์ต์
ํ
์คํธ (๊ธฐ๋ณธ๊ฐ: "Year") */
|
|
332
|
+
yearLabel?: string;
|
|
333
|
+
/** ์ select์ aria-label ๋ฐ ๋น ์ต์
ํ
์คํธ (๊ธฐ๋ณธ๊ฐ: "Month") */
|
|
334
|
+
monthLabel?: string;
|
|
335
|
+
/** ์ผ select์ aria-label ๋ฐ ๋น ์ต์
ํ
์คํธ (๊ธฐ๋ณธ๊ฐ: "Day") */
|
|
336
|
+
dayLabel?: string;
|
|
329
337
|
}
|
|
330
338
|
/**
|
|
331
339
|
* ์ฐ/์/์ผ ์ ํํ ๋ฐ์ดํธ ํผ์ปค๋ฅผ ๋ ๋๋งํ๋ค.
|
|
@@ -333,7 +341,7 @@ interface DatePickerProps {
|
|
|
333
341
|
* @param props ๋ฐ์ดํธ ํผ์ปค ์์ฑ
|
|
334
342
|
* @returns ๋ ๋๋ง๋ ๋ฐ์ดํธ ํผ์ปค UI
|
|
335
343
|
*/
|
|
336
|
-
declare const DatePicker: ({ label, value, onChange, mode, startYear, endYear, minDate, selectableRange, disabled, fullWidth, width, }: DatePickerProps) => react_jsx_runtime.JSX.Element;
|
|
344
|
+
declare const DatePicker: ({ label, value, onChange, mode, startYear, endYear, minDate, selectableRange, disabled, fullWidth, width, yearLabel, monthLabel, dayLabel, }: DatePickerProps) => react_jsx_runtime.JSX.Element;
|
|
337
345
|
|
|
338
346
|
interface PaginationProps {
|
|
339
347
|
/** ํ์ฌ ํ์ด์ง ๋ฒํธ (1-based) */
|
|
@@ -342,6 +350,10 @@ interface PaginationProps {
|
|
|
342
350
|
totalPages: number;
|
|
343
351
|
/** ํ์ด์ง ๋ณ๊ฒฝ ์ ํธ์ถ๋๋ ์ฝ๋ฐฑ */
|
|
344
352
|
onChange: (page: number) => void;
|
|
353
|
+
/** ์ด์ ํ์ด์ง ๋ฒํผ aria-label (๊ธฐ๋ณธ๊ฐ: "Previous page") */
|
|
354
|
+
prevLabel?: string;
|
|
355
|
+
/** ๋ค์ ํ์ด์ง ๋ฒํผ aria-label (๊ธฐ๋ณธ๊ฐ: "Next page") */
|
|
356
|
+
nextLabel?: string;
|
|
345
357
|
}
|
|
346
358
|
/**
|
|
347
359
|
* ํ์ด์ง๋ค์ด์
์ ๋ ๋๋งํ๋ค.
|
|
@@ -349,7 +361,7 @@ interface PaginationProps {
|
|
|
349
361
|
* @param props ํ์ด์ง๋ค์ด์
์์ฑ
|
|
350
362
|
* @returns ๋ ๋๋ง๋ ํ์ด์ง๋ค์ด์
UI
|
|
351
363
|
*/
|
|
352
|
-
declare const Pagination: ({ page, totalPages, onChange }: PaginationProps) => react_jsx_runtime.JSX.Element;
|
|
364
|
+
declare const Pagination: ({ page, totalPages, onChange, prevLabel, nextLabel }: PaginationProps) => react_jsx_runtime.JSX.Element;
|
|
353
365
|
|
|
354
366
|
interface ModalProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
|
|
355
367
|
/** ๋ชจ๋ฌ ์ด๋ฆผ ์ฌ๋ถ */
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import './index.css';
|
|
3
|
-
import * as
|
|
3
|
+
import * as React7 from 'react';
|
|
4
4
|
import { createContext, useContext, useState, useCallback } from 'react';
|
|
5
5
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
6
|
import { createPortal } from 'react-dom';
|
|
@@ -37,8 +37,8 @@ var FOCUSABLE_SELECTORS = [
|
|
|
37
37
|
'[tabindex]:not([tabindex="-1"])'
|
|
38
38
|
].join(", ");
|
|
39
39
|
function useFocusTrap(containerRef, isActive) {
|
|
40
|
-
const previousActiveElement =
|
|
41
|
-
|
|
40
|
+
const previousActiveElement = React7.useRef(null);
|
|
41
|
+
React7.useEffect(() => {
|
|
42
42
|
if (!isActive) return;
|
|
43
43
|
const container = containerRef.current;
|
|
44
44
|
if (!container) return;
|
|
@@ -168,6 +168,8 @@ var AlertModal = ({
|
|
|
168
168
|
onCancel,
|
|
169
169
|
onClose
|
|
170
170
|
}) => {
|
|
171
|
+
const panelRef = React7.useRef(null);
|
|
172
|
+
useFocusTrap(panelRef, true);
|
|
171
173
|
const modalClassName = [
|
|
172
174
|
"alert_modal",
|
|
173
175
|
`alert_variant_${variant}`
|
|
@@ -176,41 +178,50 @@ var AlertModal = ({
|
|
|
176
178
|
"alert_actions",
|
|
177
179
|
`alert_actions_${actionsAlign}`
|
|
178
180
|
].filter(Boolean).join(" ");
|
|
179
|
-
return /* @__PURE__ */ jsx(
|
|
181
|
+
return /* @__PURE__ */ jsx(
|
|
180
182
|
"div",
|
|
181
183
|
{
|
|
182
|
-
className:
|
|
183
|
-
onClick:
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
184
|
+
className: "alert_overlay",
|
|
185
|
+
onClick: onClose,
|
|
186
|
+
onKeyDown: (e) => e.key === "Escape" && onClose(),
|
|
187
|
+
children: /* @__PURE__ */ jsxs(
|
|
188
|
+
"div",
|
|
189
|
+
{
|
|
190
|
+
ref: panelRef,
|
|
191
|
+
className: modalClassName,
|
|
192
|
+
onClick: (e) => e.stopPropagation(),
|
|
193
|
+
role: "alertdialog",
|
|
194
|
+
"aria-modal": "true",
|
|
195
|
+
"aria-labelledby": "alert_title",
|
|
196
|
+
"aria-describedby": "alert_message",
|
|
197
|
+
children: [
|
|
198
|
+
title && /* @__PURE__ */ jsx("div", { className: "alert_title", id: "alert_title", children: title }),
|
|
199
|
+
message && /* @__PURE__ */ jsx("div", { className: "alert_message", id: "alert_message", children: message }),
|
|
200
|
+
/* @__PURE__ */ jsxs("div", { className: actionsClassName, children: [
|
|
201
|
+
showCancel && /* @__PURE__ */ jsx(
|
|
202
|
+
"button",
|
|
203
|
+
{
|
|
204
|
+
type: "button",
|
|
205
|
+
className: "alert_button alert_button_cancel",
|
|
206
|
+
onClick: onCancel,
|
|
207
|
+
children: cancelText
|
|
208
|
+
}
|
|
209
|
+
),
|
|
210
|
+
/* @__PURE__ */ jsx(
|
|
211
|
+
"button",
|
|
212
|
+
{
|
|
213
|
+
type: "button",
|
|
214
|
+
className: "alert_button alert_button_confirm",
|
|
215
|
+
onClick: onConfirm,
|
|
216
|
+
children: confirmText
|
|
217
|
+
}
|
|
218
|
+
)
|
|
219
|
+
] })
|
|
220
|
+
]
|
|
221
|
+
}
|
|
222
|
+
)
|
|
212
223
|
}
|
|
213
|
-
)
|
|
224
|
+
);
|
|
214
225
|
};
|
|
215
226
|
var Spinner = ({ size = 24, ariaLabel = "Loading" }) => {
|
|
216
227
|
return /* @__PURE__ */ jsx(
|
|
@@ -258,7 +269,7 @@ var TopLoading = ({
|
|
|
258
269
|
}
|
|
259
270
|
);
|
|
260
271
|
};
|
|
261
|
-
var ToastContext =
|
|
272
|
+
var ToastContext = React7.createContext(null);
|
|
262
273
|
var VARIANT_ICONS = {
|
|
263
274
|
success: /* @__PURE__ */ jsx(CheckCircle2, { size: 18 }),
|
|
264
275
|
error: /* @__PURE__ */ jsx(XCircle, { size: 18 }),
|
|
@@ -266,10 +277,10 @@ var VARIANT_ICONS = {
|
|
|
266
277
|
info: /* @__PURE__ */ jsx(Info, { size: 18 }),
|
|
267
278
|
default: /* @__PURE__ */ jsx(Bell, { size: 18 })
|
|
268
279
|
};
|
|
269
|
-
var ToastItemComponent = ({ item, onRemove }) => {
|
|
270
|
-
const [exiting, setExiting] =
|
|
271
|
-
const closingRef =
|
|
272
|
-
const close =
|
|
280
|
+
var ToastItemComponent = ({ item, onRemove, closeAriaLabel }) => {
|
|
281
|
+
const [exiting, setExiting] = React7.useState(false);
|
|
282
|
+
const closingRef = React7.useRef(false);
|
|
283
|
+
const close = React7.useCallback(() => {
|
|
273
284
|
if (closingRef.current) return;
|
|
274
285
|
closingRef.current = true;
|
|
275
286
|
setExiting(true);
|
|
@@ -283,7 +294,7 @@ var ToastItemComponent = ({ item, onRemove }) => {
|
|
|
283
294
|
"div",
|
|
284
295
|
{
|
|
285
296
|
className: itemClassName,
|
|
286
|
-
role: "alert",
|
|
297
|
+
role: item.variant === "error" ? "alert" : "status",
|
|
287
298
|
children: [
|
|
288
299
|
/* @__PURE__ */ jsx("span", { className: `toast_icon toast_icon_${item.variant}`, "aria-hidden": "true", children: VARIANT_ICONS[item.variant] }),
|
|
289
300
|
/* @__PURE__ */ jsx("span", { className: "toast_message", children: item.message }),
|
|
@@ -293,7 +304,7 @@ var ToastItemComponent = ({ item, onRemove }) => {
|
|
|
293
304
|
type: "button",
|
|
294
305
|
className: "toast_close",
|
|
295
306
|
onClick: close,
|
|
296
|
-
"aria-label":
|
|
307
|
+
"aria-label": closeAriaLabel,
|
|
297
308
|
children: /* @__PURE__ */ jsx(X, { size: 14 })
|
|
298
309
|
}
|
|
299
310
|
),
|
|
@@ -310,33 +321,43 @@ var ToastItemComponent = ({ item, onRemove }) => {
|
|
|
310
321
|
}
|
|
311
322
|
);
|
|
312
323
|
};
|
|
313
|
-
var ToastProvider = ({ children, maxCount = 5 }) => {
|
|
314
|
-
const [toasts, setToasts] =
|
|
315
|
-
const [isMounted, setIsMounted] =
|
|
316
|
-
|
|
324
|
+
var ToastProvider = ({ children, maxCount = 5, closeAriaLabel = "Close" }) => {
|
|
325
|
+
const [toasts, setToasts] = React7.useState([]);
|
|
326
|
+
const [isMounted, setIsMounted] = React7.useState(false);
|
|
327
|
+
React7.useEffect(() => {
|
|
317
328
|
setIsMounted(true);
|
|
318
329
|
}, []);
|
|
319
|
-
const addToast =
|
|
330
|
+
const addToast = React7.useCallback(
|
|
320
331
|
(message, variant, duration = 3e3) => {
|
|
321
332
|
const id = crypto.randomUUID();
|
|
322
333
|
setToasts((prev) => [{ id, message, variant, duration }, ...prev].slice(0, maxCount));
|
|
323
334
|
},
|
|
324
335
|
[maxCount]
|
|
325
336
|
);
|
|
326
|
-
const removeToast =
|
|
337
|
+
const removeToast = React7.useCallback((id) => {
|
|
327
338
|
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
328
339
|
}, []);
|
|
329
340
|
return /* @__PURE__ */ jsxs(ToastContext.Provider, { value: { addToast }, children: [
|
|
330
341
|
children,
|
|
331
342
|
isMounted && createPortal(
|
|
332
|
-
/* @__PURE__ */ jsx(
|
|
333
|
-
|
|
343
|
+
/* @__PURE__ */ jsx(
|
|
344
|
+
"div",
|
|
334
345
|
{
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
346
|
+
className: "toast_container",
|
|
347
|
+
"aria-live": "polite",
|
|
348
|
+
"aria-atomic": "false",
|
|
349
|
+
"aria-label": "Notifications",
|
|
350
|
+
children: toasts.map((item) => /* @__PURE__ */ jsx(
|
|
351
|
+
ToastItemComponent,
|
|
352
|
+
{
|
|
353
|
+
item,
|
|
354
|
+
onRemove: removeToast,
|
|
355
|
+
closeAriaLabel
|
|
356
|
+
},
|
|
357
|
+
item.id
|
|
358
|
+
))
|
|
359
|
+
}
|
|
360
|
+
),
|
|
340
361
|
document.body
|
|
341
362
|
)
|
|
342
363
|
] });
|
|
@@ -378,11 +399,11 @@ var Button = ({
|
|
|
378
399
|
const buttonStyle = width ? { ...style, width } : style;
|
|
379
400
|
return /* @__PURE__ */ jsx("button", { className: buttonClassName, style: buttonStyle, ...props });
|
|
380
401
|
};
|
|
381
|
-
var Checkbox =
|
|
402
|
+
var Checkbox = React7.forwardRef(
|
|
382
403
|
({ label, size = "md", indeterminate, className, ...props }, ref) => {
|
|
383
|
-
const inputRef =
|
|
384
|
-
|
|
385
|
-
|
|
404
|
+
const inputRef = React7.useRef(null);
|
|
405
|
+
React7.useImperativeHandle(ref, () => inputRef.current);
|
|
406
|
+
React7.useEffect(() => {
|
|
386
407
|
if (!inputRef.current) return;
|
|
387
408
|
inputRef.current.indeterminate = Boolean(indeterminate);
|
|
388
409
|
}, [indeterminate]);
|
|
@@ -408,19 +429,19 @@ var Checkbox = React6.forwardRef(
|
|
|
408
429
|
);
|
|
409
430
|
Checkbox.displayName = "Checkbox";
|
|
410
431
|
var FileInput = ({
|
|
411
|
-
label = "
|
|
432
|
+
label = "Choose file",
|
|
412
433
|
onFiles,
|
|
413
434
|
className,
|
|
414
435
|
disabled,
|
|
415
436
|
...props
|
|
416
437
|
}) => {
|
|
417
|
-
const inputId =
|
|
438
|
+
const inputId = React7.useId();
|
|
418
439
|
const rootClassName = [
|
|
419
440
|
"file_input",
|
|
420
441
|
disabled && "file_input_disabled",
|
|
421
442
|
className ?? ""
|
|
422
443
|
].filter(Boolean).join(" ");
|
|
423
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
444
|
+
return /* @__PURE__ */ jsxs("div", { className: rootClassName, children: [
|
|
424
445
|
/* @__PURE__ */ jsx(
|
|
425
446
|
"input",
|
|
426
447
|
{
|
|
@@ -435,7 +456,7 @@ var FileInput = ({
|
|
|
435
456
|
/* @__PURE__ */ jsx("label", { htmlFor: inputId, className: "file_input_label", children: label })
|
|
436
457
|
] });
|
|
437
458
|
};
|
|
438
|
-
var Radio =
|
|
459
|
+
var Radio = React7.forwardRef(
|
|
439
460
|
({ label, size = "md", className, ...props }, ref) => {
|
|
440
461
|
const rootClassName = cn(
|
|
441
462
|
"radio",
|
|
@@ -465,21 +486,21 @@ var Select = ({
|
|
|
465
486
|
className,
|
|
466
487
|
textAlign = "left"
|
|
467
488
|
}) => {
|
|
468
|
-
const internalId =
|
|
489
|
+
const internalId = React7.useId();
|
|
469
490
|
const selectId = id ?? internalId;
|
|
470
491
|
const isControlled = value !== void 0;
|
|
471
|
-
const [internalValue, setInternalValue] =
|
|
492
|
+
const [internalValue, setInternalValue] = React7.useState(defaultValue);
|
|
472
493
|
const currentValue = isControlled ? value ?? null : internalValue;
|
|
473
|
-
const [isOpen, setIsOpen] =
|
|
474
|
-
const [activeIndex, setActiveIndex] =
|
|
475
|
-
const [dropUp, setDropUp] =
|
|
476
|
-
const wrapperRef =
|
|
477
|
-
const controlRef =
|
|
478
|
-
const currentOption =
|
|
494
|
+
const [isOpen, setIsOpen] = React7.useState(false);
|
|
495
|
+
const [activeIndex, setActiveIndex] = React7.useState(-1);
|
|
496
|
+
const [dropUp, setDropUp] = React7.useState(false);
|
|
497
|
+
const wrapperRef = React7.useRef(null);
|
|
498
|
+
const controlRef = React7.useRef(null);
|
|
499
|
+
const currentOption = React7.useMemo(
|
|
479
500
|
() => options.find((o) => o.value === currentValue) ?? null,
|
|
480
501
|
[options, currentValue]
|
|
481
502
|
);
|
|
482
|
-
const setValue =
|
|
503
|
+
const setValue = React7.useCallback(
|
|
483
504
|
(next) => {
|
|
484
505
|
const option = options.find((o) => o.value === next) ?? null;
|
|
485
506
|
if (!isControlled) setInternalValue(next);
|
|
@@ -487,12 +508,12 @@ var Select = ({
|
|
|
487
508
|
},
|
|
488
509
|
[isControlled, onChange, options]
|
|
489
510
|
);
|
|
490
|
-
const handleOutsideClick =
|
|
511
|
+
const handleOutsideClick = React7.useEffectEvent((e) => {
|
|
491
512
|
if (!wrapperRef.current?.contains(e.target)) {
|
|
492
513
|
setIsOpen(false);
|
|
493
514
|
}
|
|
494
515
|
});
|
|
495
|
-
|
|
516
|
+
React7.useEffect(() => {
|
|
496
517
|
document.addEventListener("mousedown", handleOutsideClick);
|
|
497
518
|
return () => document.removeEventListener("mousedown", handleOutsideClick);
|
|
498
519
|
}, []);
|
|
@@ -557,12 +578,12 @@ var Select = ({
|
|
|
557
578
|
break;
|
|
558
579
|
}
|
|
559
580
|
};
|
|
560
|
-
|
|
581
|
+
React7.useEffect(() => {
|
|
561
582
|
if (!isOpen) return;
|
|
562
583
|
const idx = options.findIndex((o) => o.value === currentValue && !o.disabled);
|
|
563
584
|
setActiveIndex(idx >= 0 ? idx : Math.max(0, options.findIndex((o) => !o.disabled)));
|
|
564
585
|
}, [isOpen, options, currentValue]);
|
|
565
|
-
|
|
586
|
+
React7.useLayoutEffect(() => {
|
|
566
587
|
if (!isOpen || !controlRef.current) return;
|
|
567
588
|
const rect = controlRef.current.getBoundingClientRect();
|
|
568
589
|
const listHeight = Math.min(options.length * 40, 288);
|
|
@@ -644,7 +665,7 @@ var Select = ({
|
|
|
644
665
|
)
|
|
645
666
|
] });
|
|
646
667
|
};
|
|
647
|
-
var Switch =
|
|
668
|
+
var Switch = React7.forwardRef(
|
|
648
669
|
({
|
|
649
670
|
checked,
|
|
650
671
|
defaultChecked,
|
|
@@ -656,7 +677,7 @@ var Switch = React6.forwardRef(
|
|
|
656
677
|
...props
|
|
657
678
|
}, ref) => {
|
|
658
679
|
const isControlled = checked !== void 0;
|
|
659
|
-
const [innerChecked, setInnerChecked] =
|
|
680
|
+
const [innerChecked, setInnerChecked] = React7.useState(!!defaultChecked);
|
|
660
681
|
const isOn = isControlled ? !!checked : innerChecked;
|
|
661
682
|
const handleToggle = () => {
|
|
662
683
|
if (disabled) return;
|
|
@@ -688,7 +709,7 @@ var Switch = React6.forwardRef(
|
|
|
688
709
|
}
|
|
689
710
|
);
|
|
690
711
|
Switch.displayName = "Switch";
|
|
691
|
-
var TextField =
|
|
712
|
+
var TextField = React7.forwardRef(
|
|
692
713
|
({
|
|
693
714
|
id,
|
|
694
715
|
label,
|
|
@@ -707,15 +728,15 @@ var TextField = React6.forwardRef(
|
|
|
707
728
|
transformValue,
|
|
708
729
|
...props
|
|
709
730
|
}, ref) => {
|
|
710
|
-
const inputId = id ??
|
|
731
|
+
const inputId = id ?? React7.useId();
|
|
711
732
|
const helperId = helperText ? `${inputId}-help` : void 0;
|
|
712
733
|
const isControlled = value !== void 0;
|
|
713
734
|
const applyTransform = (nextValue) => transformValue ? transformValue(nextValue) : nextValue;
|
|
714
|
-
const [innerValue, setInnerValue] =
|
|
735
|
+
const [innerValue, setInnerValue] = React7.useState(
|
|
715
736
|
() => applyTransform(value ?? defaultValue ?? "")
|
|
716
737
|
);
|
|
717
|
-
const isComposingRef =
|
|
718
|
-
|
|
738
|
+
const isComposingRef = React7.useRef(false);
|
|
739
|
+
React7.useEffect(() => {
|
|
719
740
|
if (!isControlled) return;
|
|
720
741
|
setInnerValue(applyTransform(value ?? ""));
|
|
721
742
|
}, [isControlled, value, transformValue]);
|
|
@@ -799,8 +820,12 @@ var DatePicker = ({
|
|
|
799
820
|
selectableRange = "all",
|
|
800
821
|
disabled,
|
|
801
822
|
fullWidth = true,
|
|
802
|
-
width
|
|
823
|
+
width,
|
|
824
|
+
yearLabel = "Year",
|
|
825
|
+
monthLabel = "Month",
|
|
826
|
+
dayLabel = "Day"
|
|
803
827
|
}) => {
|
|
828
|
+
const groupId = React7.useId();
|
|
804
829
|
const today = /* @__PURE__ */ new Date();
|
|
805
830
|
const todayYear = today.getFullYear();
|
|
806
831
|
const todayMonth = today.getMonth() + 1;
|
|
@@ -828,56 +853,64 @@ var DatePicker = ({
|
|
|
828
853
|
const containerStyle = width ? { width: normalizeWidth(width) } : void 0;
|
|
829
854
|
const rootClassName = cn("date_picker", { date_picker_full_width: fullWidth && !width });
|
|
830
855
|
return /* @__PURE__ */ jsxs("div", { className: rootClassName, style: containerStyle, children: [
|
|
831
|
-
label && /* @__PURE__ */ jsx("label", { className: "date_picker_label", children: label }),
|
|
832
|
-
/* @__PURE__ */ jsxs(
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
856
|
+
label && /* @__PURE__ */ jsx("label", { className: "date_picker_label", id: groupId, children: label }),
|
|
857
|
+
/* @__PURE__ */ jsxs(
|
|
858
|
+
"div",
|
|
859
|
+
{
|
|
860
|
+
className: "date_picker_fields",
|
|
861
|
+
role: "group",
|
|
862
|
+
"aria-labelledby": label ? groupId : void 0,
|
|
863
|
+
children: [
|
|
864
|
+
/* @__PURE__ */ jsxs(
|
|
865
|
+
"select",
|
|
866
|
+
{
|
|
867
|
+
"aria-label": yearLabel,
|
|
868
|
+
value: year,
|
|
869
|
+
disabled,
|
|
870
|
+
onChange: (e) => emit(Number(e.target.value), month || minMonth, day || minDay),
|
|
871
|
+
children: [
|
|
872
|
+
/* @__PURE__ */ jsx("option", { value: "", children: yearLabel }),
|
|
873
|
+
Array.from(
|
|
874
|
+
{ length: maxYear - startYear + 1 },
|
|
875
|
+
(_, i) => startYear + i
|
|
876
|
+
).map((y2) => /* @__PURE__ */ jsx("option", { value: y2, children: y2 }, y2))
|
|
877
|
+
]
|
|
878
|
+
}
|
|
879
|
+
),
|
|
880
|
+
/* @__PURE__ */ jsxs(
|
|
881
|
+
"select",
|
|
882
|
+
{
|
|
883
|
+
"aria-label": monthLabel,
|
|
884
|
+
value: month,
|
|
885
|
+
disabled: disabled || !year,
|
|
886
|
+
onChange: (e) => emit(year, Number(e.target.value), day || minDay),
|
|
887
|
+
children: [
|
|
888
|
+
/* @__PURE__ */ jsx("option", { value: "", children: monthLabel }),
|
|
889
|
+
Array.from({ length: maxMonth - minMonth + 1 }, (_, i) => minMonth + i).map(
|
|
890
|
+
(m2) => /* @__PURE__ */ jsx("option", { value: m2, children: pad(m2) }, m2)
|
|
891
|
+
)
|
|
892
|
+
]
|
|
893
|
+
}
|
|
894
|
+
),
|
|
895
|
+
mode === "year-month-day" && /* @__PURE__ */ jsxs(
|
|
896
|
+
"select",
|
|
897
|
+
{
|
|
898
|
+
"aria-label": dayLabel,
|
|
899
|
+
value: day,
|
|
900
|
+
disabled: disabled || !month,
|
|
901
|
+
onChange: (e) => emit(year, month, Number(e.target.value)),
|
|
902
|
+
children: [
|
|
903
|
+
/* @__PURE__ */ jsx("option", { value: "", children: dayLabel }),
|
|
904
|
+
Array.from(
|
|
905
|
+
{ length: days - minDay + 1 },
|
|
906
|
+
(_, i) => minDay + i
|
|
907
|
+
).map((d2) => /* @__PURE__ */ jsx("option", { value: d2, children: pad(d2) }, d2))
|
|
908
|
+
]
|
|
909
|
+
}
|
|
910
|
+
)
|
|
911
|
+
]
|
|
912
|
+
}
|
|
913
|
+
)
|
|
881
914
|
] });
|
|
882
915
|
};
|
|
883
916
|
var range = (start, end) => {
|
|
@@ -909,10 +942,10 @@ var getPaginationItems = (page, totalPages) => {
|
|
|
909
942
|
items.push(last);
|
|
910
943
|
return items;
|
|
911
944
|
};
|
|
912
|
-
var Pagination = ({ page, totalPages, onChange }) => {
|
|
945
|
+
var Pagination = ({ page, totalPages, onChange, prevLabel = "Previous page", nextLabel = "Next page" }) => {
|
|
913
946
|
const prevDisabled = page <= 1;
|
|
914
947
|
const nextDisabled = page >= totalPages;
|
|
915
|
-
const items =
|
|
948
|
+
const items = React7.useMemo(
|
|
916
949
|
() => getPaginationItems(page, totalPages),
|
|
917
950
|
[page, totalPages]
|
|
918
951
|
);
|
|
@@ -923,7 +956,7 @@ var Pagination = ({ page, totalPages, onChange }) => {
|
|
|
923
956
|
className: "pagination_item",
|
|
924
957
|
onClick: () => onChange(page - 1),
|
|
925
958
|
disabled: prevDisabled,
|
|
926
|
-
"aria-label":
|
|
959
|
+
"aria-label": prevLabel,
|
|
927
960
|
children: "\u2039"
|
|
928
961
|
}
|
|
929
962
|
),
|
|
@@ -953,7 +986,7 @@ var Pagination = ({ page, totalPages, onChange }) => {
|
|
|
953
986
|
className: "pagination_item",
|
|
954
987
|
onClick: () => onChange(page + 1),
|
|
955
988
|
disabled: nextDisabled,
|
|
956
|
-
"aria-label":
|
|
989
|
+
"aria-label": nextLabel,
|
|
957
990
|
children: "\u203A"
|
|
958
991
|
}
|
|
959
992
|
)
|
|
@@ -970,18 +1003,18 @@ var Modal = ({
|
|
|
970
1003
|
ariaLabel,
|
|
971
1004
|
...props
|
|
972
1005
|
}) => {
|
|
973
|
-
const panelRef =
|
|
974
|
-
const titleId =
|
|
1006
|
+
const panelRef = React7.useRef(null);
|
|
1007
|
+
const titleId = React7.useId();
|
|
975
1008
|
useFocusTrap(panelRef, open);
|
|
976
|
-
const handleEscape =
|
|
1009
|
+
const handleEscape = React7.useEffectEvent((e) => {
|
|
977
1010
|
if (e.key === "Escape") onClose?.();
|
|
978
1011
|
});
|
|
979
|
-
|
|
1012
|
+
React7.useEffect(() => {
|
|
980
1013
|
if (!open) return;
|
|
981
1014
|
document.addEventListener("keydown", handleEscape);
|
|
982
1015
|
return () => document.removeEventListener("keydown", handleEscape);
|
|
983
1016
|
}, [open]);
|
|
984
|
-
|
|
1017
|
+
React7.useEffect(() => {
|
|
985
1018
|
if (!open) return;
|
|
986
1019
|
const body = document.body;
|
|
987
1020
|
const openModals = parseInt(body.dataset.openModals || "0", 10);
|
package/dist/next.js
CHANGED
|
@@ -75,7 +75,7 @@ var Sidebar = ({
|
|
|
75
75
|
Image,
|
|
76
76
|
{
|
|
77
77
|
src: "/images/sidebar/arrow-close.svg",
|
|
78
|
-
alt: "
|
|
78
|
+
alt: "\uC0AC\uC774\uB4DC\uBC14 \uB2EB\uAE30",
|
|
79
79
|
width: 24,
|
|
80
80
|
height: 24
|
|
81
81
|
}
|
|
@@ -83,7 +83,7 @@ var Sidebar = ({
|
|
|
83
83
|
}
|
|
84
84
|
)
|
|
85
85
|
] }),
|
|
86
|
-
/* @__PURE__ */ jsx("nav", { className: "sidebar_nav", children: items.map((item) => {
|
|
86
|
+
/* @__PURE__ */ jsx("nav", { className: "sidebar_nav", "aria-label": "\uBA54\uC778 \uBA54\uB274", children: items.map((item) => {
|
|
87
87
|
if (item.type === "group") {
|
|
88
88
|
const open = openGroups.includes(item.id);
|
|
89
89
|
const subClassName = [
|
|
@@ -130,6 +130,7 @@ var Sidebar = ({
|
|
|
130
130
|
{
|
|
131
131
|
href: child.href,
|
|
132
132
|
className: subItemClassName,
|
|
133
|
+
"aria-current": active2 ? "page" : void 0,
|
|
133
134
|
onClick: () => onItemSelect?.(
|
|
134
135
|
child.href
|
|
135
136
|
),
|
|
@@ -158,6 +159,7 @@ var Sidebar = ({
|
|
|
158
159
|
{
|
|
159
160
|
href: item.href,
|
|
160
161
|
className: itemClassName,
|
|
162
|
+
"aria-current": active ? "page" : void 0,
|
|
161
163
|
onClick: () => onItemSelect?.(item.href),
|
|
162
164
|
children: /* @__PURE__ */ jsxs("div", { className: "sidebar_item_left", children: [
|
|
163
165
|
item.icon && /* @__PURE__ */ jsx("span", { className: "sidebar_icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(item.icon, { size: 16 }) }),
|
|
@@ -177,7 +179,7 @@ var Sidebar = ({
|
|
|
177
179
|
Image,
|
|
178
180
|
{
|
|
179
181
|
src: "/images/sidebar/menu.svg",
|
|
180
|
-
alt: "
|
|
182
|
+
alt: "\uC0AC\uC774\uB4DC\uBC14 \uC5F4\uAE30",
|
|
181
183
|
width: 24,
|
|
182
184
|
height: 24
|
|
183
185
|
}
|
|
@@ -40,6 +40,10 @@ $color_accent: #3B82F6;
|
|
|
40
40
|
$color_accent_light: #60A5FA;
|
|
41
41
|
$color_accent_dark: #2563EB;
|
|
42
42
|
|
|
43
|
+
/* Hover */
|
|
44
|
+
$color_hover_subtle: rgba(0, 0, 0, 0.03);
|
|
45
|
+
$color_hover_light: rgba(0, 0, 0, 0.05);
|
|
46
|
+
|
|
43
47
|
/* Overlay */
|
|
44
48
|
$color_overlay: rgba(0, 0, 0, 0.5);
|
|
45
49
|
$color_overlay_light: rgba(0, 0, 0, 0.3);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bigtablet/design-system",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.0",
|
|
4
4
|
"description": "Bigtablet Design System UI Components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -50,7 +50,12 @@
|
|
|
50
50
|
"test": "vitest run --project unit",
|
|
51
51
|
"test:watch": "vitest --project unit",
|
|
52
52
|
"test:coverage": "vitest run --project unit --coverage",
|
|
53
|
-
"chromatic": "npx chromatic --project-token
|
|
53
|
+
"chromatic": "npx chromatic --project-token=$CHROMATIC_TOKEN --build-script-name=build:sb",
|
|
54
|
+
"figma:test": "node scripts/figma-connect.mjs",
|
|
55
|
+
"figma:snapshot": "node scripts/figma-snapshot.mjs",
|
|
56
|
+
"figma:diff": "node scripts/figma-diff.mjs",
|
|
57
|
+
"figma:apply": "node scripts/figma-apply.mjs",
|
|
58
|
+
"size": "size-limit"
|
|
54
59
|
},
|
|
55
60
|
"keywords": [
|
|
56
61
|
"design-system",
|
|
@@ -80,10 +85,11 @@
|
|
|
80
85
|
"@semantic-release/git": "^10.0.1",
|
|
81
86
|
"@semantic-release/github": "^12.0.1",
|
|
82
87
|
"@semantic-release/npm": "^13.1.1",
|
|
83
|
-
"@
|
|
84
|
-
"@storybook/addon-
|
|
85
|
-
"@storybook/
|
|
86
|
-
"@storybook/react
|
|
88
|
+
"@size-limit/preset-small-lib": "^12.0.1",
|
|
89
|
+
"@storybook/addon-docs": "^10.2.17",
|
|
90
|
+
"@storybook/addon-vitest": "10.2.17",
|
|
91
|
+
"@storybook/react": "10.2.17",
|
|
92
|
+
"@storybook/react-vite": "10.2.17",
|
|
87
93
|
"@testing-library/dom": "^10.4.1",
|
|
88
94
|
"@testing-library/jest-dom": "^6.9.1",
|
|
89
95
|
"@testing-library/react": "^16.3.2",
|
|
@@ -103,7 +109,8 @@
|
|
|
103
109
|
"react-dom": "19.2.0",
|
|
104
110
|
"sass-embedded": "^1.93.3",
|
|
105
111
|
"semantic-release": "^25.0.1",
|
|
106
|
-
"
|
|
112
|
+
"size-limit": "^12.0.1",
|
|
113
|
+
"storybook": "10.2.17",
|
|
107
114
|
"tsup": "^8.5.0",
|
|
108
115
|
"typescript": "^5",
|
|
109
116
|
"vite": "^5",
|
|
@@ -113,11 +120,23 @@
|
|
|
113
120
|
"access": "public",
|
|
114
121
|
"registry": "https://registry.npmjs.org/"
|
|
115
122
|
},
|
|
123
|
+
"size-limit": [
|
|
124
|
+
{
|
|
125
|
+
"path": "dist/index.js",
|
|
126
|
+
"limit": "12 kB"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"path": "dist/vanilla/bigtablet.min.js",
|
|
130
|
+
"limit": "5 kB"
|
|
131
|
+
}
|
|
132
|
+
],
|
|
116
133
|
"pnpm": {
|
|
117
134
|
"overrides": {
|
|
118
135
|
"rollup": "^4.59.0",
|
|
119
136
|
"lodash": "^4.17.23",
|
|
120
|
-
"lodash-es": "^4.17.23"
|
|
137
|
+
"lodash-es": "^4.17.23",
|
|
138
|
+
"immutable": "^5.1.5",
|
|
139
|
+
"minimatch": "^10.2.4"
|
|
121
140
|
}
|
|
122
141
|
}
|
|
123
142
|
}
|