@bigtablet/design-system 1.24.1 → 1.25.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/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  "use client";
2
2
  import './index.css';
3
- import * as React9 from 'react';
3
+ import * as React4 from 'react';
4
4
  import { createContext, useContext, useState, useCallback } from 'react';
5
- import { jsxs, jsx } from 'react/jsx-runtime';
6
5
  import { createPortal } from 'react-dom';
6
+ import { jsxs, jsx } from 'react/jsx-runtime';
7
7
  import { ChevronDown, Check, X, Bell, Info, AlertTriangle, XCircle, CheckCircle2 } from 'lucide-react';
8
8
 
9
- // src/styles/ts/a11y.ts
9
+ // src/styles/a11y/index.ts
10
10
  var a11y = {
11
11
  focusRing: "0 0 0 3px rgba(0, 0, 0, 0.15)",
12
12
  focusRingError: "0 0 0 3px rgba(239, 68, 68, 0.15)",
@@ -14,7 +14,7 @@ var a11y = {
14
14
  tapMinSize: "44px"
15
15
  };
16
16
 
17
- // src/styles/ts/border-width.ts
17
+ // src/styles/border-width/index.ts
18
18
  var baseBorderWidth = {
19
19
  "0": "0px",
20
20
  "1": "1px",
@@ -26,7 +26,7 @@ var borderWidth = {
26
26
  indicator: baseBorderWidth["2"]
27
27
  };
28
28
 
29
- // src/styles/ts/breakpoints.ts
29
+ // src/styles/breakpoints/index.ts
30
30
  var breakpoints = {
31
31
  compact: 0,
32
32
  // mobile
@@ -38,7 +38,7 @@ var breakpoints = {
38
38
  // desktop
39
39
  };
40
40
 
41
- // src/styles/ts/colors.ts
41
+ // src/styles/colors/index.ts
42
42
  var baseColors = {
43
43
  brandPrimary: "#121212",
44
44
  blue600: "#1A73E8",
@@ -48,6 +48,7 @@ var baseColors = {
48
48
  neutral300: "#999999",
49
49
  neutral400: "#B3B3B3",
50
50
  neutral500: "#888888",
51
+ neutral600: "#6B6B6B",
51
52
  neutral700: "#666666",
52
53
  neutral900: "#121212",
53
54
  statusError: "#EF4444",
@@ -104,7 +105,7 @@ var colors = {
104
105
  }
105
106
  };
106
107
 
107
- // src/styles/ts/motion.ts
108
+ // src/styles/motion/index.ts
108
109
  var motion = {
109
110
  transition: {
110
111
  fast: "0.1s ease-in-out",
@@ -119,7 +120,7 @@ var motion = {
119
120
  }
120
121
  };
121
122
 
122
- // src/styles/ts/opacity.ts
123
+ // src/styles/opacity/index.ts
123
124
  var opacity = {
124
125
  "0": 0,
125
126
  "5": 0.05,
@@ -133,7 +134,7 @@ var opacity = {
133
134
  "100": 1
134
135
  };
135
136
 
136
- // src/styles/ts/radius.ts
137
+ // src/styles/radius/index.ts
137
138
  var radius = {
138
139
  none: "0px",
139
140
  xs: "4px",
@@ -144,8 +145,8 @@ var radius = {
144
145
  full: "9999px"
145
146
  };
146
147
 
147
- // src/styles/ts/shadows.ts
148
- var shadows = {
148
+ // src/styles/elevation/index.ts
149
+ var elevation = {
149
150
  level1: "0 1px 1px -1px rgba(0, 0, 0, 0.20), 0 3px 3px 0px rgba(0, 0, 0, 0.12)",
150
151
  level2: "0 2px 2px -2px rgba(0, 0, 0, 0.20), 0 6px 6px 0px rgba(0, 0, 0, 0.12)",
151
152
  level3: "0 3px 3px -3px rgba(0, 0, 0, 0.20), 0 9px 9px 0px rgba(0, 0, 0, 0.12)",
@@ -153,7 +154,7 @@ var shadows = {
153
154
  level5: "0 8px 10px -5px rgba(0, 0, 0, 0.20), 0 20px 20px 0px rgba(0, 0, 0, 0.12)"
154
155
  };
155
156
 
156
- // src/styles/ts/spacing.ts
157
+ // src/styles/spacing/index.ts
157
158
  var spacing = {
158
159
  "0": "0px",
159
160
  "1": "1px",
@@ -171,7 +172,7 @@ var spacing = {
171
172
  "48": "48px"
172
173
  };
173
174
 
174
- // src/styles/ts/skeleton.ts
175
+ // src/styles/skeleton/index.ts
175
176
  var skeleton = {
176
177
  color: {
177
178
  base: baseColors.neutral50,
@@ -197,7 +198,7 @@ var skeleton = {
197
198
  }
198
199
  };
199
200
 
200
- // src/styles/ts/typography.ts
201
+ // src/styles/typography/index.ts
201
202
  var baseTypography = {
202
203
  fontFamily: {
203
204
  primary: "Pretendard"
@@ -217,8 +218,15 @@ var baseTypography = {
217
218
  "48": "48px"
218
219
  },
219
220
  fontWeight: {
221
+ thin: "Thin",
222
+ extraLight: "ExtraLight",
223
+ light: "Light",
220
224
  regular: "Regular",
221
- medium: "Medium"
225
+ medium: "Medium",
226
+ semiBold: "SemiBold",
227
+ bold: "Bold",
228
+ extraBold: "ExtraBold",
229
+ black: "Black"
222
230
  },
223
231
  lineHeight: {
224
232
  "16": "16px",
@@ -434,7 +442,7 @@ var typography = {
434
442
  }
435
443
  };
436
444
 
437
- // src/styles/ts/z-index.ts
445
+ // src/styles/z-index/index.ts
438
446
  var zIndex = {
439
447
  level0: 0,
440
448
  level1: 10,
@@ -475,8 +483,8 @@ var FOCUSABLE_SELECTORS = [
475
483
  '[tabindex]:not([tabindex="-1"])'
476
484
  ].join(", ");
477
485
  function useFocusTrap(containerRef, isActive) {
478
- const previousActiveElement = React9.useRef(null);
479
- React9.useEffect(() => {
486
+ const previousActiveElement = React4.useRef(null);
487
+ React4.useEffect(() => {
480
488
  if (!isActive) return;
481
489
  const container = containerRef.current;
482
490
  if (!container) return;
@@ -524,28 +532,6 @@ function useFocusTrap(containerRef, isActive) {
524
532
  };
525
533
  }, [isActive, containerRef]);
526
534
  }
527
- var Card = ({
528
- heading,
529
- headingAs: HeadingTag = "h3",
530
- shadow = "sm",
531
- padding = "md",
532
- bordered = false,
533
- className,
534
- children,
535
- ...props
536
- }) => {
537
- const cardClassName = cn(
538
- "card",
539
- `card_shadow_${shadow}`,
540
- `card_p_${padding}`,
541
- { card_bordered: bordered },
542
- className
543
- );
544
- return /* @__PURE__ */ jsxs("div", { className: cardClassName, ...props, children: [
545
- heading ? /* @__PURE__ */ jsx(HeadingTag, { className: "card_title", children: heading }) : null,
546
- /* @__PURE__ */ jsx("div", { className: "card_body", children })
547
- ] });
548
- };
549
535
  var AlertContext = createContext(null);
550
536
  var useAlert = () => {
551
537
  const context = useContext(AlertContext);
@@ -603,10 +589,12 @@ var AlertModal = ({
603
589
  onCancel,
604
590
  onClose
605
591
  }) => {
606
- const panelRef = React9.useRef(null);
592
+ const panelRef = React4.useRef(null);
593
+ const titleId = React4.useId();
594
+ const messageId = React4.useId();
607
595
  useFocusTrap(panelRef, true);
608
- const modalClassName = ["alert_modal", `alert_variant_${variant}`].filter(Boolean).join(" ");
609
- const actionsClassName = ["alert_actions", `alert_actions_${actionsAlign}`].filter(Boolean).join(" ");
596
+ const modalClassName = cn("alert_modal", `alert_variant_${variant}`);
597
+ const actionsClassName = cn("alert_actions", `alert_actions_${actionsAlign}`);
610
598
  return /* @__PURE__ */ jsx(
611
599
  "div",
612
600
  {
@@ -625,11 +613,11 @@ var AlertModal = ({
625
613
  },
626
614
  role: "alertdialog",
627
615
  "aria-modal": "true",
628
- "aria-labelledby": "alert_title",
629
- "aria-describedby": "alert_message",
616
+ "aria-labelledby": titleId,
617
+ "aria-describedby": messageId,
630
618
  children: [
631
- title && /* @__PURE__ */ jsx("div", { className: "alert_title", id: "alert_title", children: title }),
632
- message && /* @__PURE__ */ jsx("div", { className: "alert_message", id: "alert_message", children: message }),
619
+ title && /* @__PURE__ */ jsx("div", { className: "alert_title", id: titleId, children: title }),
620
+ message && /* @__PURE__ */ jsx("div", { className: "alert_message", id: messageId, children: message }),
633
621
  /* @__PURE__ */ jsxs("div", { className: actionsClassName, children: [
634
622
  showCancel && /* @__PURE__ */ jsx("button", { type: "button", className: "alert_button alert_button_cancel", onClick: onCancel, children: cancelText }),
635
623
  /* @__PURE__ */ jsx("button", { type: "button", className: "alert_button alert_button_confirm", onClick: onConfirm, children: confirmText })
@@ -640,178 +628,156 @@ var AlertModal = ({
640
628
  }
641
629
  );
642
630
  };
643
- var Spinner = ({ size = 24, ariaLabel = "Loading" }) => {
644
- return /* @__PURE__ */ jsx(
645
- "span",
646
- {
647
- className: "spinner",
648
- style: { width: size, height: size },
649
- role: "status",
650
- "aria-label": ariaLabel
651
- }
652
- );
653
- };
654
- var ToastContext = React9.createContext(null);
655
- var VARIANT_ICONS = {
656
- success: /* @__PURE__ */ jsx(CheckCircle2, { size: 18 }),
657
- error: /* @__PURE__ */ jsx(XCircle, { size: 18 }),
658
- warning: /* @__PURE__ */ jsx(AlertTriangle, { size: 18 }),
659
- info: /* @__PURE__ */ jsx(Info, { size: 18 }),
660
- default: /* @__PURE__ */ jsx(Bell, { size: 18 })
661
- };
662
- var ToastItemComponent = ({ item, onRemove, closeAriaLabel }) => {
663
- const [exiting, setExiting] = React9.useState(false);
664
- const closingRef = React9.useRef(false);
665
- const close = React9.useCallback(() => {
666
- if (closingRef.current) return;
667
- closingRef.current = true;
668
- setExiting(true);
669
- setTimeout(() => onRemove(item.id), 260);
670
- }, [item.id, onRemove]);
671
- const itemClassName = ["toast_item", exiting && "toast_item_exiting"].filter(Boolean).join(" ");
672
- return /* @__PURE__ */ jsxs("div", { className: itemClassName, role: item.variant === "error" ? "alert" : "status", children: [
673
- /* @__PURE__ */ jsx("span", { className: `toast_icon toast_icon_${item.variant}`, "aria-hidden": "true", children: VARIANT_ICONS[item.variant] }),
674
- /* @__PURE__ */ jsx("span", { className: "toast_message", children: item.message }),
675
- /* @__PURE__ */ jsx("button", { type: "button", className: "toast_close", onClick: close, "aria-label": closeAriaLabel, children: /* @__PURE__ */ jsx(X, { size: 14 }) }),
676
- /* @__PURE__ */ jsx(
677
- "div",
678
- {
679
- className: `toast_progress toast_progress_${item.variant}`,
680
- style: { "--toast-duration": `${item.duration}ms` },
681
- onAnimationEnd: close,
682
- "aria-hidden": "true"
683
- }
684
- )
685
- ] });
686
- };
687
- var ToastProvider = ({
631
+ var Button = ({
632
+ variant = "filled",
633
+ size = "md",
634
+ leadingIcon,
635
+ trailingIcon,
636
+ fullWidth = false,
637
+ radius: radius2,
638
+ className,
688
639
  children,
689
- maxCount = 5,
690
- closeAriaLabel = "Close"
640
+ ...props
691
641
  }) => {
692
- const [toasts, setToasts] = React9.useState([]);
693
- const [isMounted, setIsMounted] = React9.useState(false);
694
- React9.useEffect(() => {
695
- setIsMounted(true);
696
- }, []);
697
- const addToast = React9.useCallback(
698
- (message, variant, duration = 3e3) => {
699
- const id = crypto.randomUUID();
700
- setToasts((prev) => [{ id, message, variant, duration }, ...prev].slice(0, maxCount));
701
- },
702
- [maxCount]
642
+ const buttonClassName = cn(
643
+ "button",
644
+ `button_variant_${variant}`,
645
+ `button_size_${size}`,
646
+ fullWidth && "button_full_width",
647
+ radius2 && `button_radius_${radius2}`,
648
+ className
703
649
  );
704
- const removeToast = React9.useCallback((id) => {
705
- setToasts((prev) => prev.filter((t) => t.id !== id));
706
- }, []);
707
- return /* @__PURE__ */ jsxs(ToastContext.Provider, { value: { addToast }, children: [
708
- children,
709
- isMounted && createPortal(
710
- /* @__PURE__ */ jsx(
711
- "div",
712
- {
713
- className: "toast_container",
714
- role: "region",
715
- "aria-live": "polite",
716
- "aria-atomic": "false",
717
- "aria-label": "Notifications",
718
- children: toasts.map((item) => /* @__PURE__ */ jsx(
719
- ToastItemComponent,
720
- {
721
- item,
722
- onRemove: removeToast,
723
- closeAriaLabel
724
- },
725
- item.id
726
- ))
727
- }
728
- ),
729
- document.body
730
- )
650
+ return /* @__PURE__ */ jsxs("button", { className: buttonClassName, ...props, children: [
651
+ leadingIcon && /* @__PURE__ */ jsx("span", { className: "button_icon", "aria-hidden": "true", children: leadingIcon }),
652
+ children && /* @__PURE__ */ jsx("span", { className: "button_label", children }),
653
+ trailingIcon && /* @__PURE__ */ jsx("span", { className: "button_icon", "aria-hidden": "true", children: trailingIcon })
731
654
  ] });
732
655
  };
733
- var useToast = () => {
734
- const ctx = useContext(ToastContext);
735
- if (!ctx) {
736
- throw new Error("useToast must be used within ToastProvider");
737
- }
738
- return {
739
- /** 성공 메시지를 표시한다 */
740
- success: (message, duration) => ctx.addToast(message, "success", duration),
741
- /** 오류 메시지를 표시한다 */
742
- error: (message, duration) => ctx.addToast(message, "error", duration),
743
- /** 경고 메시지를 표시한다 */
744
- warning: (message, duration) => ctx.addToast(message, "warning", duration),
745
- /** 정보 메시지를 표시한다 */
746
- info: (message, duration) => ctx.addToast(message, "info", duration),
747
- /** 기본 메시지를 표시한다 */
748
- message: (message, duration) => ctx.addToast(message, "default", duration)
749
- };
750
- };
751
- var TopLoading = ({
752
- progress,
753
- color,
754
- height = 3,
755
- isLoading = true,
756
- ariaLabel = "Page loading"
656
+ var Card = ({
657
+ heading,
658
+ headingAs: HeadingTag = "h3",
659
+ shadow = "sm",
660
+ padding = "md",
661
+ bordered = false,
662
+ className,
663
+ children,
664
+ ...props
757
665
  }) => {
758
- if (!isLoading) return null;
759
- const isIndeterminate = progress === void 0;
760
- return /* @__PURE__ */ jsx(
761
- "div",
762
- {
763
- className: "top_loading",
764
- style: { height },
765
- role: "progressbar",
766
- "aria-valuemin": 0,
767
- "aria-valuemax": 100,
768
- "aria-valuenow": isIndeterminate ? void 0 : progress,
769
- "aria-label": ariaLabel,
770
- children: /* @__PURE__ */ jsx(
771
- "div",
772
- {
773
- className: ["top_loading_bar", isIndeterminate && "top_loading_indeterminate"].filter(Boolean).join(" "),
774
- style: {
775
- width: isIndeterminate ? void 0 : `${progress}%`,
776
- backgroundColor: color
777
- }
778
- }
779
- )
780
- }
666
+ const cardClassName = cn(
667
+ "card",
668
+ `card_shadow_${shadow}`,
669
+ `card_p_${padding}`,
670
+ { card_bordered: bordered },
671
+ className
781
672
  );
673
+ return /* @__PURE__ */ jsxs("div", { className: cardClassName, ...props, children: [
674
+ heading ? /* @__PURE__ */ jsx(HeadingTag, { className: "card_title", children: heading }) : null,
675
+ /* @__PURE__ */ jsx("div", { className: "card_body", children })
676
+ ] });
782
677
  };
783
678
  var Checkbox = ({
784
679
  label,
785
- size = "md",
786
680
  indeterminate,
681
+ error,
787
682
  className,
788
683
  ref,
789
684
  ...props
790
685
  }) => {
791
- const inputRef = React9.useRef(null);
792
- React9.useImperativeHandle(ref, () => inputRef.current);
793
- React9.useEffect(() => {
686
+ const inputRef = React4.useRef(null);
687
+ React4.useImperativeHandle(
688
+ ref,
689
+ () => inputRef.current
690
+ );
691
+ React4.useEffect(() => {
794
692
  if (!inputRef.current) return;
795
693
  inputRef.current.indeterminate = Boolean(indeterminate);
796
694
  }, [indeterminate]);
797
- const rootClassName = cn("checkbox", `checkbox_size_${size}`, className);
695
+ const rootClassName = cn(
696
+ "checkbox",
697
+ error && "checkbox_error",
698
+ className
699
+ );
798
700
  return /* @__PURE__ */ jsxs("label", { className: rootClassName, children: [
799
- /* @__PURE__ */ jsx("input", { ref: inputRef, type: "checkbox", className: "checkbox_input", ...props }),
800
- /* @__PURE__ */ jsx("span", { className: "checkbox_box", "aria-hidden": "true" }),
701
+ /* @__PURE__ */ jsx(
702
+ "input",
703
+ {
704
+ ref: inputRef,
705
+ type: "checkbox",
706
+ className: "checkbox_input",
707
+ ...props
708
+ }
709
+ ),
710
+ /* @__PURE__ */ jsx("span", { className: "checkbox_state_layer", "aria-hidden": "true", children: /* @__PURE__ */ jsx("span", { className: "checkbox_icon" }) }),
801
711
  label ? /* @__PURE__ */ jsx("span", { className: "checkbox_label", children: label }) : null
802
712
  ] });
803
713
  };
804
714
  Checkbox.displayName = "Checkbox";
715
+ var CheckIcon = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("polyline", { points: "4 10 8 14 16 6" }) });
716
+ var CloseIcon = () => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: [
717
+ /* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "14", y2: "14" }),
718
+ /* @__PURE__ */ jsx("line", { x1: "14", y1: "6", x2: "6", y2: "14" })
719
+ ] });
720
+ var ChevronDownIcon = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("polyline", { points: "5 8 10 13 15 8" }) });
721
+ var Chip = ({
722
+ type = "basic",
723
+ label,
724
+ selected,
725
+ removable,
726
+ disabled,
727
+ onClick,
728
+ onRemove,
729
+ className,
730
+ ...props
731
+ }) => {
732
+ const hasLeading = selected;
733
+ const hasTrailing = type === "input" && removable || type === "filter";
734
+ const chipClassName = cn(
735
+ "chip",
736
+ `chip_type_${type}`,
737
+ selected && "chip_selected",
738
+ hasLeading && "chip_has_leading",
739
+ hasTrailing && "chip_has_trailing",
740
+ disabled && "chip_disabled",
741
+ className
742
+ );
743
+ return /* @__PURE__ */ jsxs("div", { className: chipClassName, ...props, children: [
744
+ /* @__PURE__ */ jsxs(
745
+ "button",
746
+ {
747
+ type: "button",
748
+ className: "chip_content",
749
+ disabled,
750
+ onClick,
751
+ children: [
752
+ hasLeading && /* @__PURE__ */ jsx("span", { className: "chip_icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(CheckIcon, {}) }),
753
+ /* @__PURE__ */ jsx("span", { className: "chip_label", children: label })
754
+ ]
755
+ }
756
+ ),
757
+ hasTrailing && /* @__PURE__ */ jsx(
758
+ "button",
759
+ {
760
+ type: "button",
761
+ className: "chip_trailing",
762
+ disabled,
763
+ onClick: type === "input" && removable ? onRemove : onClick,
764
+ "aria-label": type === "input" && removable ? "Remove" : "Toggle dropdown",
765
+ children: /* @__PURE__ */ jsx("span", { className: "chip_icon", "aria-hidden": "true", children: type === "input" && removable ? /* @__PURE__ */ jsx(CloseIcon, {}) : /* @__PURE__ */ jsx(ChevronDownIcon, {}) })
766
+ }
767
+ )
768
+ ] });
769
+ };
805
770
  var pad = (n) => String(n).padStart(2, "0");
806
771
  var getDaysInMonth = (year, month) => new Date(year, month, 0).getDate();
807
772
  var normalizeWidth = (v) => typeof v === "number" ? `${v}px` : v;
773
+ var range = (start, end) => Array.from({ length: end - start + 1 }, (_, i) => start + i);
808
774
  var DatePicker = ({
809
775
  label,
810
776
  value,
811
777
  onChange,
812
778
  mode = "year-month-day",
813
779
  startYear = 1950,
814
- endYear = (/* @__PURE__ */ new Date()).getFullYear() + 10,
780
+ endYear: endYearProp,
815
781
  minDate,
816
782
  selectableRange = "all",
817
783
  disabled,
@@ -823,37 +789,108 @@ var DatePicker = ({
823
789
  minDateSrFormat = "Minimum date: {date}",
824
790
  selectableRangeUntilTodaySrText = "Selectable up to today"
825
791
  }) => {
826
- const groupId = React9.useId();
827
- const constraintId = React9.useId();
828
- const today = /* @__PURE__ */ new Date();
829
- const todayYear = today.getFullYear();
830
- const todayMonth = today.getMonth() + 1;
831
- const todayDay = today.getDate();
832
- const [y, m, d] = value?.split("-").map(Number) ?? [];
833
- const [minY, minM, minD] = minDate?.split("-").map(Number) ?? [];
834
- const year = y ?? "";
835
- const month = m ?? "";
836
- const day = d ?? "";
792
+ const groupId = React4.useId();
793
+ const constraintId = React4.useId();
794
+ const { todayYear, todayMonth, todayDay } = React4.useMemo(() => {
795
+ const now = /* @__PURE__ */ new Date();
796
+ return {
797
+ todayYear: now.getFullYear(),
798
+ todayMonth: now.getMonth() + 1,
799
+ todayDay: now.getDate()
800
+ };
801
+ }, []);
802
+ const endYear = endYearProp ?? todayYear + 10;
803
+ const parsed = React4.useMemo(() => {
804
+ if (!value) return { year: 0, month: 0, day: 0 };
805
+ const [y, m, d] = value.split("-").map(Number);
806
+ return {
807
+ year: y || 0,
808
+ month: m || 0,
809
+ day: d || 0
810
+ };
811
+ }, [value]);
812
+ const min = React4.useMemo(() => {
813
+ if (!minDate) return { year: 0, month: 0, day: 0 };
814
+ const [y, m, d] = minDate.split("-").map(Number);
815
+ return {
816
+ year: y || 0,
817
+ month: m || 0,
818
+ day: d || 0
819
+ };
820
+ }, [minDate]);
821
+ const { year, month, day } = parsed;
837
822
  const maxYear = selectableRange === "until-today" ? todayYear : endYear;
838
- const minMonth = minY && year === minY ? minM : 1;
823
+ const minMonth = min.year > 0 && year === min.year ? min.month : 1;
839
824
  const maxMonth = selectableRange === "until-today" && year === todayYear ? todayMonth : 12;
840
- const minDay = minY && minM && year === minY && month === minM ? minD : 1;
841
- const maxDay = selectableRange === "until-today" && year === todayYear && month === todayMonth ? todayDay : getDaysInMonth(year || todayYear, month || 1);
842
- const days = year && month ? Math.min(getDaysInMonth(year, month), maxDay) : 31;
843
- const clampDay = (year2, month2, day2) => Math.min(day2, getDaysInMonth(year2, month2));
844
- const emit = (yy, mm, dd) => {
845
- if (mode === "year-month") {
846
- onChange(`${yy}-${pad(mm)}`);
847
- return;
825
+ const minDay = min.year > 0 && min.month > 0 && year === min.year && month === min.month ? min.day : 1;
826
+ const maxDay = React4.useMemo(() => {
827
+ if (!year || !month) return 31;
828
+ const daysInMonth = getDaysInMonth(year, month);
829
+ if (selectableRange === "until-today" && year === todayYear && month === todayMonth) {
830
+ return Math.min(daysInMonth, todayDay);
848
831
  }
849
- const safeDay = clampDay(yy, mm, dd ?? 1);
850
- onChange(`${yy}-${pad(mm)}-${pad(safeDay)}`);
851
- };
832
+ return daysInMonth;
833
+ }, [year, month, selectableRange, todayYear, todayMonth, todayDay]);
834
+ const yearOptions = React4.useMemo(
835
+ () => range(startYear, maxYear),
836
+ [startYear, maxYear]
837
+ );
838
+ const monthOptions = React4.useMemo(
839
+ () => range(minMonth, Math.max(minMonth, maxMonth)),
840
+ [minMonth, maxMonth]
841
+ );
842
+ const dayOptions = React4.useMemo(
843
+ () => range(minDay, Math.max(minDay, maxDay)),
844
+ [minDay, maxDay]
845
+ );
846
+ const emit = React4.useCallback(
847
+ (yy, mm, dd) => {
848
+ if (mode === "year-month") {
849
+ onChange(`${yy}-${pad(mm)}`);
850
+ return;
851
+ }
852
+ const safeDay = Math.min(dd ?? 1, getDaysInMonth(yy, mm));
853
+ onChange(`${yy}-${pad(mm)}-${pad(safeDay)}`);
854
+ },
855
+ [mode, onChange]
856
+ );
857
+ const handleYearChange = React4.useCallback(
858
+ (newYear) => {
859
+ let newMonth = month;
860
+ const newMinMonth = min.year > 0 && newYear === min.year ? min.month : 1;
861
+ const newMaxMonth = selectableRange === "until-today" && newYear === todayYear ? todayMonth : 12;
862
+ if (newMonth > 0 && newMonth < newMinMonth) newMonth = newMinMonth;
863
+ if (newMonth > newMaxMonth) newMonth = newMaxMonth;
864
+ if (newMonth > 0) {
865
+ emit(newYear, newMonth, day || void 0);
866
+ } else {
867
+ emit(newYear, newMinMonth, day || void 0);
868
+ }
869
+ },
870
+ [month, day, min.year, min.month, selectableRange, todayYear, todayMonth, emit]
871
+ );
872
+ const handleMonthChange = React4.useCallback(
873
+ (newMonth) => {
874
+ if (!year) return;
875
+ emit(year, newMonth, day || void 0);
876
+ },
877
+ [year, day, emit]
878
+ );
879
+ const handleDayChange = React4.useCallback(
880
+ (newDay) => {
881
+ if (!year || !month) return;
882
+ emit(year, month, newDay);
883
+ },
884
+ [year, month, emit]
885
+ );
852
886
  const containerStyle = width ? { width: normalizeWidth(width) } : void 0;
853
- const rootClassName = cn("date_picker", { date_picker_full_width: fullWidth && !width });
887
+ const rootClassName = cn("date_picker", {
888
+ date_picker_full_width: fullWidth && !width
889
+ });
854
890
  const constraintParts = [];
855
891
  if (minDate) constraintParts.push(minDateSrFormat.replace("{date}", minDate));
856
- if (selectableRange === "until-today") constraintParts.push(selectableRangeUntilTodaySrText);
892
+ if (selectableRange === "until-today")
893
+ constraintParts.push(selectableRangeUntilTodaySrText);
857
894
  const constraintDesc = constraintParts.join(". ");
858
895
  return /* @__PURE__ */ jsxs("div", { className: rootClassName, style: containerStyle, children: [
859
896
  label && /* @__PURE__ */ jsx("span", { className: "date_picker_label", id: groupId, children: label }),
@@ -870,12 +907,12 @@ var DatePicker = ({
870
907
  "select",
871
908
  {
872
909
  "aria-label": yearLabel,
873
- value: year,
910
+ value: year || "",
874
911
  disabled,
875
- onChange: (e) => emit(Number(e.target.value), month || minMonth, day || minDay),
912
+ onChange: (e) => handleYearChange(Number(e.target.value)),
876
913
  children: [
877
914
  /* @__PURE__ */ jsx("option", { value: "", children: yearLabel }),
878
- Array.from({ length: maxYear - startYear + 1 }, (_, i) => startYear + i).map((y2) => /* @__PURE__ */ jsx("option", { value: y2, children: y2 }, y2))
915
+ yearOptions.map((y) => /* @__PURE__ */ jsx("option", { value: y, children: y }, y))
879
916
  ]
880
917
  }
881
918
  ),
@@ -883,12 +920,12 @@ var DatePicker = ({
883
920
  "select",
884
921
  {
885
922
  "aria-label": monthLabel,
886
- value: month,
923
+ value: month || "",
887
924
  disabled: disabled || !year,
888
- onChange: (e) => emit(year, Number(e.target.value), day || minDay),
925
+ onChange: (e) => handleMonthChange(Number(e.target.value)),
889
926
  children: [
890
927
  /* @__PURE__ */ jsx("option", { value: "", children: monthLabel }),
891
- Array.from({ length: maxMonth - minMonth + 1 }, (_, i) => minMonth + i).map((m2) => /* @__PURE__ */ jsx("option", { value: m2, children: pad(m2) }, m2))
928
+ monthOptions.map((m) => /* @__PURE__ */ jsx("option", { value: m, children: pad(m) }, m))
892
929
  ]
893
930
  }
894
931
  ),
@@ -896,12 +933,12 @@ var DatePicker = ({
896
933
  "select",
897
934
  {
898
935
  "aria-label": dayLabel,
899
- value: day,
936
+ value: day || "",
900
937
  disabled: disabled || !month,
901
- onChange: (e) => emit(year, month, Number(e.target.value)),
938
+ onChange: (e) => handleDayChange(Number(e.target.value)),
902
939
  children: [
903
940
  /* @__PURE__ */ jsx("option", { value: "", children: dayLabel }),
904
- Array.from({ length: days - minDay + 1 }, (_, i) => minDay + i).map((d2) => /* @__PURE__ */ jsx("option", { value: d2, children: pad(d2) }, d2))
941
+ dayOptions.map((d) => /* @__PURE__ */ jsx("option", { value: d, children: pad(d) }, d))
905
942
  ]
906
943
  }
907
944
  )
@@ -910,17 +947,66 @@ var DatePicker = ({
910
947
  )
911
948
  ] });
912
949
  };
950
+ var Divider = ({
951
+ weight = "standard",
952
+ className,
953
+ ...props
954
+ }) => {
955
+ const dividerClassName = cn(
956
+ "divider",
957
+ `divider_weight_${weight}`,
958
+ className
959
+ );
960
+ return /* @__PURE__ */ jsx("hr", { className: dividerClassName, ...props });
961
+ };
962
+ var FAB = ({
963
+ variant = "primary",
964
+ icon,
965
+ className,
966
+ ...props
967
+ }) => {
968
+ const fabClassName = cn(
969
+ "fab",
970
+ `fab_variant_${variant}`,
971
+ className
972
+ );
973
+ return /* @__PURE__ */ jsx("button", { className: fabClassName, ...props, children: /* @__PURE__ */ jsx("span", { className: "fab_icon", "aria-hidden": "true", children: icon }) });
974
+ };
913
975
  var FileInput = ({
914
976
  label = "Choose file",
915
977
  onFiles,
916
- helperText,
978
+ supportingText,
979
+ preview = false,
917
980
  className,
918
981
  disabled,
919
982
  ...props
920
983
  }) => {
921
- const inputId = React9.useId();
922
- const helperId = React9.useId();
923
- const rootClassName = ["file_input", disabled && "file_input_disabled", className ?? ""].filter(Boolean).join(" ");
984
+ const inputId = React4.useId();
985
+ const helperId = React4.useId();
986
+ const [previewUrls, setPreviewUrls] = React4.useState([]);
987
+ const handleChange = (e) => {
988
+ const files = e.currentTarget.files;
989
+ onFiles?.(files);
990
+ if (preview && files) {
991
+ previewUrls.forEach((url) => URL.revokeObjectURL(url));
992
+ const urls = [];
993
+ for (let i = 0; i < files.length; i++) {
994
+ if (files[i].type.startsWith("image/")) {
995
+ urls.push(URL.createObjectURL(files[i]));
996
+ }
997
+ }
998
+ setPreviewUrls(urls);
999
+ } else {
1000
+ previewUrls.forEach((url) => URL.revokeObjectURL(url));
1001
+ setPreviewUrls([]);
1002
+ }
1003
+ };
1004
+ React4.useEffect(() => {
1005
+ return () => {
1006
+ previewUrls.forEach((url) => URL.revokeObjectURL(url));
1007
+ };
1008
+ }, [previewUrls]);
1009
+ const rootClassName = cn("file_input", disabled && "file_input_disabled", className);
924
1010
  return /* @__PURE__ */ jsxs("div", { className: rootClassName, children: [
925
1011
  /* @__PURE__ */ jsx(
926
1012
  "input",
@@ -929,259 +1015,357 @@ var FileInput = ({
929
1015
  type: "file",
930
1016
  className: "file_input_control",
931
1017
  disabled,
932
- "aria-describedby": helperText ? helperId : void 0,
933
- onChange: (e) => onFiles?.(e.currentTarget.files),
1018
+ "aria-describedby": supportingText ? helperId : void 0,
1019
+ onChange: handleChange,
934
1020
  ...props
935
1021
  }
936
1022
  ),
937
1023
  /* @__PURE__ */ jsx("label", { htmlFor: inputId, className: "file_input_label", children: label }),
938
- helperText && /* @__PURE__ */ jsx("span", { id: helperId, className: "file_input_helper", children: helperText })
1024
+ supportingText && /* @__PURE__ */ jsx("span", { id: helperId, className: "file_input_helper", children: supportingText }),
1025
+ previewUrls.length > 0 && /* @__PURE__ */ jsx("div", { className: "file_input_preview", children: previewUrls.map((url) => /* @__PURE__ */ jsx("img", { src: url, alt: "", className: "file_input_preview_img" }, url)) })
939
1026
  ] });
940
1027
  };
941
- var Radio = ({ label, size = "md", className, ref, ...props }) => {
942
- const rootClassName = cn("radio", `radio_size_${size}`, className);
943
- return /* @__PURE__ */ jsxs("label", { className: rootClassName, children: [
944
- /* @__PURE__ */ jsx("input", { ref, type: "radio", className: "radio_input", ...props }),
945
- /* @__PURE__ */ jsx("span", { className: "radio_dot", "aria-hidden": "true" }),
946
- label ? /* @__PURE__ */ jsx("span", { className: "radio_label", children: label }) : null
947
- ] });
948
- };
949
- Radio.displayName = "Radio";
950
- var Switch = ({
951
- checked,
952
- defaultChecked,
953
- onChange,
1028
+ var IconButton = ({
1029
+ variant = "standard",
954
1030
  size = "md",
955
- disabled,
1031
+ icon,
956
1032
  className,
957
- ariaLabel,
958
- ref,
959
1033
  ...props
960
1034
  }) => {
961
- const isControlled = checked !== void 0;
962
- const [innerChecked, setInnerChecked] = React9.useState(!!defaultChecked);
963
- const isOn = isControlled ? !!checked : innerChecked;
964
- const handleToggle = () => {
965
- if (disabled) return;
966
- const next = !isOn;
967
- if (!isControlled) setInnerChecked(next);
968
- onChange?.(next);
969
- };
970
- const rootClassName = cn(
971
- "switch",
972
- `switch_size_${size}`,
973
- { switch_on: isOn, switch_disabled: disabled },
1035
+ const buttonClassName = cn(
1036
+ "icon_button",
1037
+ `icon_button_variant_${variant}`,
1038
+ `icon_button_size_${size}`,
974
1039
  className
975
1040
  );
1041
+ return /* @__PURE__ */ jsx("button", { className: buttonClassName, ...props, children: /* @__PURE__ */ jsx("span", { className: "icon_button_icon", "aria-hidden": "true", children: icon }) });
1042
+ };
1043
+ var LinearProgress = ({
1044
+ totalSteps,
1045
+ currentStep,
1046
+ className,
1047
+ ...props
1048
+ }) => {
1049
+ const percent = totalSteps > 0 ? Math.min(Math.max(currentStep / totalSteps, 0), 1) * 100 : 0;
976
1050
  return /* @__PURE__ */ jsx(
977
- "button",
1051
+ "div",
978
1052
  {
979
- ref,
980
- type: "button",
981
- role: "switch",
982
- "aria-checked": isOn,
983
- "aria-label": ariaLabel,
984
- disabled,
985
- onClick: handleToggle,
986
- className: rootClassName,
1053
+ className: cn("linear_progress", className),
1054
+ role: "progressbar",
1055
+ "aria-valuenow": currentStep,
1056
+ "aria-valuemin": 0,
1057
+ "aria-valuemax": totalSteps,
987
1058
  ...props,
988
- children: /* @__PURE__ */ jsx("span", { className: "switch_thumb" })
1059
+ children: /* @__PURE__ */ jsx(
1060
+ "div",
1061
+ {
1062
+ className: "linear_progress_indicator",
1063
+ style: { width: `${percent}%` }
1064
+ }
1065
+ )
989
1066
  }
990
1067
  );
991
1068
  };
992
- Switch.displayName = "Switch";
993
- var TextField = ({
994
- id,
1069
+ var ListItem = ({
1070
+ overline,
995
1071
  label,
996
- helperText,
997
- error,
998
- success,
999
- variant = "outline",
1000
- size = "md",
1001
- leftIcon,
1002
- rightIcon,
1003
- fullWidth,
1072
+ supportingText,
1073
+ metadata,
1074
+ leadingElement,
1075
+ trailingElement,
1076
+ alignment = "top",
1077
+ disabled,
1078
+ onClick,
1004
1079
  className,
1005
- onChangeAction,
1006
- value,
1007
- defaultValue,
1008
- transformValue,
1009
- ref,
1010
1080
  ...props
1011
1081
  }) => {
1012
- const generatedId = React9.useId();
1013
- const inputId = id ?? generatedId;
1014
- const helperId = helperText ? `${inputId}-help` : void 0;
1015
- const isControlled = value !== void 0;
1016
- const applyTransform = (nextValue) => transformValue ? transformValue(nextValue) : nextValue;
1017
- const [innerValue, setInnerValue] = React9.useState(
1018
- () => applyTransform(value ?? defaultValue ?? "")
1082
+ const rootClassName = cn(
1083
+ "list_item",
1084
+ `list_item_align_${alignment}`,
1085
+ disabled && "list_item_disabled",
1086
+ onClick && "list_item_interactive",
1087
+ className
1019
1088
  );
1020
- const isComposingRef = React9.useRef(false);
1021
- React9.useEffect(() => {
1022
- if (!isControlled) return;
1023
- const nextValue = value ?? "";
1024
- setInnerValue(transformValue ? transformValue(nextValue) : nextValue);
1025
- }, [isControlled, value, transformValue]);
1026
- const rootClassName = cn("text_field", { text_field_full_width: fullWidth }, className);
1027
- const inputClassName = cn(
1028
- "text_field_input",
1029
- `text_field_variant_${variant}`,
1030
- `text_field_size_${size}`,
1089
+ return /* @__PURE__ */ jsx(
1090
+ "div",
1031
1091
  {
1032
- text_field_with_left: !!leftIcon,
1033
- text_field_with_right: !!rightIcon,
1034
- text_field_error: !!error,
1035
- text_field_success: !!success
1092
+ className: rootClassName,
1093
+ onClick: disabled ? void 0 : onClick,
1094
+ onKeyDown: (e) => {
1095
+ if (disabled || !onClick) return;
1096
+ if (e.key === "Enter" || e.key === " ") {
1097
+ e.preventDefault();
1098
+ onClick(e);
1099
+ }
1100
+ },
1101
+ role: onClick ? "button" : void 0,
1102
+ tabIndex: onClick && !disabled ? 0 : void 0,
1103
+ "aria-disabled": disabled || void 0,
1104
+ ...props,
1105
+ children: /* @__PURE__ */ jsxs("div", { className: "list_item_state_layer", children: [
1106
+ leadingElement && /* @__PURE__ */ jsx("div", { className: "list_item_leading", children: leadingElement }),
1107
+ /* @__PURE__ */ jsxs("div", { className: "list_item_content", children: [
1108
+ overline && /* @__PURE__ */ jsx("span", { className: "list_item_overline", children: overline }),
1109
+ /* @__PURE__ */ jsx("span", { className: "list_item_label", children: label }),
1110
+ supportingText && /* @__PURE__ */ jsx("span", { className: "list_item_supporting", children: supportingText }),
1111
+ metadata && /* @__PURE__ */ jsx("span", { className: "list_item_metadata", children: metadata })
1112
+ ] }),
1113
+ trailingElement && /* @__PURE__ */ jsx("div", { className: "list_item_trailing", children: trailingElement })
1114
+ ] })
1036
1115
  }
1037
1116
  );
1038
- const helperClassName = cn("text_field_helper", {
1039
- text_field_helper_error: error,
1040
- text_field_helper_success: success
1041
- });
1042
- return /* @__PURE__ */ jsxs("div", { className: rootClassName, children: [
1043
- label ? /* @__PURE__ */ jsx("label", { className: "text_field_label", htmlFor: inputId, children: label }) : null,
1044
- /* @__PURE__ */ jsxs("div", { className: "text_field_wrap", children: [
1045
- leftIcon ? /* @__PURE__ */ jsx("span", { className: "text_field_icon text_field_icon_left", "aria-hidden": "true", children: leftIcon }) : null,
1046
- /* @__PURE__ */ jsx(
1047
- "input",
1048
- {
1049
- id: inputId,
1050
- ref,
1051
- className: inputClassName,
1052
- "aria-invalid": !!error,
1053
- "aria-describedby": helperId,
1054
- ...props,
1055
- value: innerValue,
1056
- onCompositionStart: () => {
1057
- isComposingRef.current = true;
1058
- },
1059
- onCompositionEnd: (event) => {
1060
- isComposingRef.current = false;
1061
- const rawValue = event.currentTarget.value;
1062
- const nextValue = applyTransform(rawValue);
1063
- setInnerValue(nextValue);
1064
- onChangeAction?.(nextValue);
1065
- },
1066
- onChange: (event) => {
1067
- const rawValue = event.target.value;
1068
- if (isComposingRef.current) {
1069
- setInnerValue(rawValue);
1070
- return;
1071
- }
1072
- const nextValue = applyTransform(rawValue);
1073
- setInnerValue(nextValue);
1074
- onChangeAction?.(nextValue);
1075
- }
1076
- }
1077
- ),
1078
- rightIcon ? /* @__PURE__ */ jsx("span", { className: "text_field_icon text_field_icon_right", "aria-hidden": "true", children: rightIcon }) : null
1079
- ] }),
1080
- helperText ? /* @__PURE__ */ jsx("div", { id: helperId, className: helperClassName, children: helperText }) : null
1081
- ] });
1082
1117
  };
1083
- TextField.displayName = "TextField";
1084
- var Button = ({
1085
- variant = "primary",
1086
- size = "md",
1087
- fullWidth = true,
1088
- width,
1118
+ var Modal = ({
1119
+ open,
1120
+ onClose,
1121
+ closeOnOverlay = true,
1122
+ width = 520,
1123
+ title,
1124
+ children,
1089
1125
  className,
1090
- style,
1126
+ ariaLabel,
1091
1127
  ...props
1092
1128
  }) => {
1093
- const buttonClassName = cn(
1094
- "button",
1095
- `button_variant_${variant}`,
1096
- `button_size_${size}`,
1097
- fullWidth && !width && "button_full_width",
1098
- className
1099
- );
1100
- const buttonStyle = width ? { ...style, width } : style;
1101
- return /* @__PURE__ */ jsx("button", { className: buttonClassName, style: buttonStyle, ...props });
1102
- };
1103
- var Select = ({
1104
- id,
1105
- label,
1106
- placeholder = "Select\u2026",
1107
- options,
1108
- value,
1109
- onChange,
1110
- defaultValue = null,
1111
- disabled,
1112
- size = "md",
1113
- variant = "outline",
1114
- fullWidth,
1115
- className,
1116
- textAlign = "left"
1117
- }) => {
1118
- const internalId = React9.useId();
1119
- const selectId = id ?? internalId;
1120
- const isControlled = value !== void 0;
1121
- const [internalValue, setInternalValue] = React9.useState(defaultValue);
1122
- const currentValue = isControlled ? value ?? null : internalValue;
1123
- const [isOpen, setIsOpen] = React9.useState(false);
1124
- const [activeIndex, setActiveIndex] = React9.useState(-1);
1125
- const [dropUp, setDropUp] = React9.useState(false);
1126
- const wrapperRef = React9.useRef(null);
1127
- const controlRef = React9.useRef(null);
1128
- const currentOption = React9.useMemo(
1129
- () => options.find((o) => o.value === currentValue) ?? null,
1130
- [options, currentValue]
1131
- );
1132
- const setValue = React9.useCallback(
1133
- (next) => {
1134
- const option = options.find((o) => o.value === next) ?? null;
1135
- if (!isControlled) setInternalValue(next);
1136
- onChange?.(next, option);
1137
- },
1138
- [isControlled, onChange, options]
1139
- );
1140
- const handleOutsideClick = React9.useEffectEvent((e) => {
1141
- if (!wrapperRef.current?.contains(e.target)) {
1142
- setIsOpen(false);
1143
- }
1129
+ const panelRef = React4.useRef(null);
1130
+ const titleId = React4.useId();
1131
+ useFocusTrap(panelRef, open);
1132
+ const handleEscape = React4.useEffectEvent((e) => {
1133
+ if (e.key === "Escape") onClose?.();
1144
1134
  });
1145
- React9.useEffect(() => {
1146
- document.addEventListener("mousedown", handleOutsideClick);
1147
- return () => document.removeEventListener("mousedown", handleOutsideClick);
1148
- }, []);
1149
- const moveActive = (dir) => {
1150
- if (!isOpen) {
1151
- setIsOpen(true);
1152
- return;
1135
+ React4.useEffect(() => {
1136
+ if (!open) return;
1137
+ document.addEventListener("keydown", handleEscape);
1138
+ return () => document.removeEventListener("keydown", handleEscape);
1139
+ }, [open]);
1140
+ React4.useEffect(() => {
1141
+ if (!open) return;
1142
+ const body = document.body;
1143
+ const openModals = parseInt(body.dataset.openModals || "0", 10);
1144
+ if (openModals === 0) {
1145
+ body.dataset.originalOverflow = window.getComputedStyle(body).overflow;
1146
+ body.style.overflow = "hidden";
1153
1147
  }
1154
- let i = activeIndex;
1155
- const len = options.length;
1156
- for (let step = 0; step < len; step++) {
1157
- i = (i + dir + len) % len;
1158
- if (!options[i].disabled) {
1159
- setActiveIndex(i);
1160
- break;
1148
+ body.dataset.openModals = String(openModals + 1);
1149
+ return () => {
1150
+ const currentOpenModals = parseInt(body.dataset.openModals || "1", 10);
1151
+ const nextOpenModals = currentOpenModals - 1;
1152
+ if (nextOpenModals === 0) {
1153
+ body.style.overflow = body.dataset.originalOverflow || "";
1154
+ delete body.dataset.openModals;
1155
+ delete body.dataset.originalOverflow;
1156
+ } else {
1157
+ body.dataset.openModals = String(nextOpenModals);
1161
1158
  }
1159
+ };
1160
+ }, [open]);
1161
+ if (!open) return null;
1162
+ const panelClassName = cn("modal_panel", className);
1163
+ const hasTitle = !!title;
1164
+ return /* @__PURE__ */ jsx(
1165
+ "div",
1166
+ {
1167
+ className: "modal",
1168
+ role: "dialog",
1169
+ "aria-modal": "true",
1170
+ "aria-labelledby": hasTitle && !ariaLabel ? titleId : void 0,
1171
+ "aria-label": !hasTitle ? ariaLabel ?? "Dialog" : ariaLabel,
1172
+ onClick: () => closeOnOverlay && onClose?.(),
1173
+ onKeyDown: (e) => {
1174
+ if (e.key === "Escape") onClose?.();
1175
+ },
1176
+ children: /* @__PURE__ */ jsxs(
1177
+ "div",
1178
+ {
1179
+ ref: panelRef,
1180
+ className: panelClassName,
1181
+ style: { width },
1182
+ role: "document",
1183
+ onClick: (e) => e.stopPropagation(),
1184
+ onKeyDown: (e) => {
1185
+ if (e.key !== "Escape") e.stopPropagation();
1186
+ },
1187
+ ...props,
1188
+ children: [
1189
+ title && /* @__PURE__ */ jsx("div", { id: titleId, className: "modal_header", children: title }),
1190
+ /* @__PURE__ */ jsx("div", { className: "modal_body", children })
1191
+ ]
1192
+ }
1193
+ )
1162
1194
  }
1163
- };
1164
- const commitActive = () => {
1165
- if (activeIndex < 0 || activeIndex >= options.length) return;
1166
- const opt = options[activeIndex];
1167
- if (!opt.disabled) {
1168
- setValue(opt.value);
1169
- setIsOpen(false);
1170
- }
1171
- };
1172
- const onKeyDown = (e) => {
1173
- if (disabled) return;
1174
- switch (e.key) {
1175
- case " ":
1176
- case "Enter":
1177
- e.preventDefault();
1178
- if (!isOpen) setIsOpen(true);
1179
- else commitActive();
1180
- break;
1181
- case "ArrowDown":
1182
- e.preventDefault();
1183
- moveActive(1);
1184
- break;
1195
+ );
1196
+ };
1197
+ var range2 = (start, end) => {
1198
+ const out = [];
1199
+ for (let i = start; i <= end; i += 1) out.push(i);
1200
+ return out;
1201
+ };
1202
+ var getPaginationItems = (page, totalPages) => {
1203
+ if (totalPages <= 7) return range2(1, totalPages);
1204
+ const items = [];
1205
+ const last = totalPages;
1206
+ const sibling = 2;
1207
+ if (page <= sibling + 2) {
1208
+ for (const p of range2(1, sibling + 3)) items.push(p);
1209
+ items.push("ellipsis");
1210
+ items.push(last);
1211
+ return items;
1212
+ }
1213
+ if (page >= last - sibling - 1) {
1214
+ items.push(1);
1215
+ items.push("ellipsis");
1216
+ for (const p of range2(last - sibling - 2, last)) items.push(p);
1217
+ return items;
1218
+ }
1219
+ items.push(1);
1220
+ items.push("ellipsis");
1221
+ for (const p of range2(page - sibling, page + sibling)) items.push(p);
1222
+ items.push("ellipsis");
1223
+ items.push(last);
1224
+ return items;
1225
+ };
1226
+ var Pagination = ({
1227
+ page,
1228
+ totalPages,
1229
+ onChange,
1230
+ prevLabel = "Previous page",
1231
+ nextLabel = "Next page"
1232
+ }) => {
1233
+ const prevDisabled = page <= 1;
1234
+ const nextDisabled = page >= totalPages;
1235
+ const items = React4.useMemo(() => getPaginationItems(page, totalPages), [page, totalPages]);
1236
+ return /* @__PURE__ */ jsxs("nav", { className: "pagination", "aria-label": "Pagination", children: [
1237
+ /* @__PURE__ */ jsx(
1238
+ "button",
1239
+ {
1240
+ type: "button",
1241
+ className: "pagination_item",
1242
+ onClick: () => onChange(page - 1),
1243
+ disabled: prevDisabled,
1244
+ "aria-label": prevLabel,
1245
+ children: "\u2039"
1246
+ }
1247
+ ),
1248
+ /* @__PURE__ */ jsx("ul", { className: "pagination_pages", children: items.map((it, idx) => {
1249
+ if (it === "ellipsis") {
1250
+ return /* @__PURE__ */ jsx("li", { className: "pagination_ellipsis", "aria-hidden": "true", children: "\u2026" }, `e-${idx}`);
1251
+ }
1252
+ const isActive = it === page;
1253
+ const buttonClassName = cn("pagination_page_button", { pagination_active: isActive });
1254
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
1255
+ "button",
1256
+ {
1257
+ type: "button",
1258
+ className: buttonClassName,
1259
+ onClick: () => onChange(it),
1260
+ "aria-current": isActive ? "page" : void 0,
1261
+ children: it
1262
+ }
1263
+ ) }, it);
1264
+ }) }),
1265
+ /* @__PURE__ */ jsx(
1266
+ "button",
1267
+ {
1268
+ type: "button",
1269
+ className: "pagination_item",
1270
+ onClick: () => onChange(page + 1),
1271
+ disabled: nextDisabled,
1272
+ "aria-label": nextLabel,
1273
+ children: "\u203A"
1274
+ }
1275
+ )
1276
+ ] });
1277
+ };
1278
+ var Radio = ({ label, size = "md", className, ref, ...props }) => {
1279
+ const rootClassName = cn("radio", `radio_size_${size}`, className);
1280
+ return /* @__PURE__ */ jsxs("label", { className: rootClassName, children: [
1281
+ /* @__PURE__ */ jsx("input", { ref, type: "radio", className: "radio_input", ...props }),
1282
+ /* @__PURE__ */ jsx("span", { className: "radio_state_layer", "aria-hidden": "true", children: /* @__PURE__ */ jsx("span", { className: "radio_dot" }) }),
1283
+ label ? /* @__PURE__ */ jsx("span", { className: "radio_label", children: label }) : null
1284
+ ] });
1285
+ };
1286
+ Radio.displayName = "Radio";
1287
+ var Select = ({
1288
+ id,
1289
+ label,
1290
+ placeholder = "Select\u2026",
1291
+ options,
1292
+ value,
1293
+ onChange,
1294
+ defaultValue = null,
1295
+ disabled,
1296
+ size = "md",
1297
+ variant = "outline",
1298
+ fullWidth,
1299
+ className,
1300
+ textAlign = "left"
1301
+ }) => {
1302
+ const internalId = React4.useId();
1303
+ const selectId = id ?? internalId;
1304
+ const isControlled = value !== void 0;
1305
+ const [internalValue, setInternalValue] = React4.useState(defaultValue);
1306
+ const currentValue = isControlled ? value ?? null : internalValue;
1307
+ const [isOpen, setIsOpen] = React4.useState(false);
1308
+ const [activeIndex, setActiveIndex] = React4.useState(-1);
1309
+ const [dropUp, setDropUp] = React4.useState(false);
1310
+ const wrapperRef = React4.useRef(null);
1311
+ const controlRef = React4.useRef(null);
1312
+ const currentOption = React4.useMemo(
1313
+ () => options.find((o) => o.value === currentValue) ?? null,
1314
+ [options, currentValue]
1315
+ );
1316
+ const setValue = React4.useCallback(
1317
+ (next) => {
1318
+ const option = options.find((o) => o.value === next) ?? null;
1319
+ if (!isControlled) setInternalValue(next);
1320
+ onChange?.(next, option);
1321
+ },
1322
+ [isControlled, onChange, options]
1323
+ );
1324
+ const handleOutsideClick = React4.useEffectEvent((e) => {
1325
+ if (!wrapperRef.current?.contains(e.target)) {
1326
+ setIsOpen(false);
1327
+ }
1328
+ });
1329
+ React4.useEffect(() => {
1330
+ document.addEventListener("mousedown", handleOutsideClick);
1331
+ return () => document.removeEventListener("mousedown", handleOutsideClick);
1332
+ }, []);
1333
+ const moveActive = (dir) => {
1334
+ if (!isOpen) {
1335
+ setIsOpen(true);
1336
+ return;
1337
+ }
1338
+ let i = activeIndex;
1339
+ const len = options.length;
1340
+ for (let step = 0; step < len; step++) {
1341
+ i = (i + dir + len) % len;
1342
+ if (!options[i].disabled) {
1343
+ setActiveIndex(i);
1344
+ break;
1345
+ }
1346
+ }
1347
+ };
1348
+ const commitActive = () => {
1349
+ if (activeIndex < 0 || activeIndex >= options.length) return;
1350
+ const opt = options[activeIndex];
1351
+ if (!opt.disabled) {
1352
+ setValue(opt.value);
1353
+ setIsOpen(false);
1354
+ }
1355
+ };
1356
+ const onKeyDown = (e) => {
1357
+ if (disabled) return;
1358
+ switch (e.key) {
1359
+ case " ":
1360
+ case "Enter":
1361
+ e.preventDefault();
1362
+ if (!isOpen) setIsOpen(true);
1363
+ else commitActive();
1364
+ break;
1365
+ case "ArrowDown":
1366
+ e.preventDefault();
1367
+ moveActive(1);
1368
+ break;
1185
1369
  case "ArrowUp":
1186
1370
  e.preventDefault();
1187
1371
  moveActive(-1);
@@ -1207,7 +1391,7 @@ var Select = ({
1207
1391
  break;
1208
1392
  }
1209
1393
  };
1210
- React9.useEffect(() => {
1394
+ React4.useEffect(() => {
1211
1395
  if (!isOpen) return;
1212
1396
  const idx = options.findIndex((o) => o.value === currentValue && !o.disabled);
1213
1397
  setActiveIndex(
@@ -1217,7 +1401,7 @@ var Select = ({
1217
1401
  )
1218
1402
  );
1219
1403
  }, [isOpen, options, currentValue]);
1220
- React9.useLayoutEffect(() => {
1404
+ React4.useLayoutEffect(() => {
1221
1405
  if (!isOpen || !controlRef.current) return;
1222
1406
  const rect = controlRef.current.getBoundingClientRect();
1223
1407
  const listHeight = Math.min(options.length * 40, 288);
@@ -1309,165 +1493,269 @@ var Select = ({
1309
1493
  }
1310
1494
  );
1311
1495
  };
1312
- var range = (start, end) => {
1313
- const out = [];
1314
- for (let i = start; i <= end; i += 1) out.push(i);
1315
- return out;
1316
- };
1317
- var getPaginationItems = (page, totalPages) => {
1318
- if (totalPages <= 7) return range(1, totalPages);
1319
- const items = [];
1320
- const last = totalPages;
1321
- const sibling = 2;
1322
- if (page <= sibling + 2) {
1323
- for (const p of range(1, sibling + 3)) items.push(p);
1324
- items.push("ellipsis");
1325
- items.push(last);
1326
- return items;
1327
- }
1328
- if (page >= last - sibling - 1) {
1329
- items.push(1);
1330
- items.push("ellipsis");
1331
- for (const p of range(last - sibling - 2, last)) items.push(p);
1332
- return items;
1333
- }
1334
- items.push(1);
1335
- items.push("ellipsis");
1336
- for (const p of range(page - sibling, page + sibling)) items.push(p);
1337
- items.push("ellipsis");
1338
- items.push(last);
1339
- return items;
1496
+ var Spinner = ({ size = 24, ariaLabel = "Loading" }) => {
1497
+ return /* @__PURE__ */ jsx(
1498
+ "span",
1499
+ {
1500
+ className: "spinner",
1501
+ style: { width: size, height: size },
1502
+ role: "status",
1503
+ "aria-label": ariaLabel
1504
+ }
1505
+ );
1340
1506
  };
1341
- var Pagination = ({
1342
- page,
1343
- totalPages,
1507
+ var Switch = ({
1508
+ checked,
1509
+ defaultChecked,
1344
1510
  onChange,
1345
- prevLabel = "Previous page",
1346
- nextLabel = "Next page"
1511
+ size = "md",
1512
+ disabled,
1513
+ className,
1514
+ ariaLabel,
1515
+ ref,
1516
+ ...props
1347
1517
  }) => {
1348
- const prevDisabled = page <= 1;
1349
- const nextDisabled = page >= totalPages;
1350
- const items = React9.useMemo(() => getPaginationItems(page, totalPages), [page, totalPages]);
1351
- return /* @__PURE__ */ jsxs("nav", { className: "pagination", "aria-label": "Pagination", children: [
1352
- /* @__PURE__ */ jsx(
1353
- "button",
1354
- {
1355
- type: "button",
1356
- className: "pagination_item",
1357
- onClick: () => onChange(page - 1),
1358
- disabled: prevDisabled,
1359
- "aria-label": prevLabel,
1360
- children: "\u2039"
1361
- }
1362
- ),
1363
- /* @__PURE__ */ jsx("ul", { className: "pagination_pages", children: items.map((it, idx) => {
1364
- if (it === "ellipsis") {
1365
- return /* @__PURE__ */ jsx("li", { className: "pagination_ellipsis", "aria-hidden": "true", children: "\u2026" }, `e-${idx}`);
1366
- }
1367
- const isActive = it === page;
1368
- const buttonClassName = cn("pagination_page_button", { pagination_active: isActive });
1369
- return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
1370
- "button",
1518
+ const isControlled = checked !== void 0;
1519
+ const [innerChecked, setInnerChecked] = React4.useState(!!defaultChecked);
1520
+ const isOn = isControlled ? !!checked : innerChecked;
1521
+ const handleToggle = () => {
1522
+ if (disabled) return;
1523
+ const next = !isOn;
1524
+ if (!isControlled) setInnerChecked(next);
1525
+ onChange?.(next);
1526
+ };
1527
+ const rootClassName = cn(
1528
+ "switch",
1529
+ `switch_size_${size}`,
1530
+ { switch_on: isOn, switch_disabled: disabled },
1531
+ className
1532
+ );
1533
+ return /* @__PURE__ */ jsx(
1534
+ "button",
1535
+ {
1536
+ ref,
1537
+ type: "button",
1538
+ role: "switch",
1539
+ "aria-checked": isOn,
1540
+ "aria-label": ariaLabel,
1541
+ disabled,
1542
+ onClick: handleToggle,
1543
+ className: rootClassName,
1544
+ ...props,
1545
+ children: /* @__PURE__ */ jsx("span", { className: "switch_thumb" })
1546
+ }
1547
+ );
1548
+ };
1549
+ Switch.displayName = "Switch";
1550
+ var TextField = ({
1551
+ id,
1552
+ label,
1553
+ showLabel = true,
1554
+ supportingText,
1555
+ error,
1556
+ leadingIcon,
1557
+ trailingIcon,
1558
+ fullWidth,
1559
+ className,
1560
+ onChangeAction,
1561
+ value,
1562
+ defaultValue,
1563
+ transformValue,
1564
+ ref,
1565
+ ...props
1566
+ }) => {
1567
+ const generatedId = React4.useId();
1568
+ const inputId = id ?? generatedId;
1569
+ const helperId = supportingText ? `${inputId}-help` : void 0;
1570
+ const isControlled = value !== void 0;
1571
+ const applyTransform = (nextValue) => transformValue ? transformValue(nextValue) : nextValue;
1572
+ const [innerValue, setInnerValue] = React4.useState(
1573
+ () => applyTransform(value ?? defaultValue ?? "")
1574
+ );
1575
+ const isComposingRef = React4.useRef(false);
1576
+ React4.useEffect(() => {
1577
+ if (!isControlled) return;
1578
+ const nextValue = value ?? "";
1579
+ setInnerValue(transformValue ? transformValue(nextValue) : nextValue);
1580
+ }, [isControlled, value, transformValue]);
1581
+ const rootClassName = cn(
1582
+ "text_field",
1583
+ fullWidth && "text_field_full_width",
1584
+ error && "text_field_error",
1585
+ props.disabled && "text_field_disabled",
1586
+ className
1587
+ );
1588
+ return /* @__PURE__ */ jsxs("div", { className: rootClassName, children: [
1589
+ /* @__PURE__ */ jsxs("div", { className: "text_field_container", children: [
1590
+ leadingIcon && /* @__PURE__ */ jsx("span", { className: "text_field_icon", "aria-hidden": "true", children: leadingIcon }),
1591
+ /* @__PURE__ */ jsx("div", { className: "text_field_input_wrap", children: /* @__PURE__ */ jsx(
1592
+ "input",
1371
1593
  {
1372
- type: "button",
1373
- className: buttonClassName,
1374
- onClick: () => onChange(it),
1375
- "aria-current": isActive ? "page" : void 0,
1376
- children: it
1594
+ id: inputId,
1595
+ ref,
1596
+ className: "text_field_input",
1597
+ "aria-invalid": !!error,
1598
+ "aria-describedby": helperId,
1599
+ "aria-label": !showLabel ? label : void 0,
1600
+ ...props,
1601
+ value: innerValue,
1602
+ onCompositionStart: () => {
1603
+ isComposingRef.current = true;
1604
+ },
1605
+ onCompositionEnd: (event) => {
1606
+ isComposingRef.current = false;
1607
+ const rawValue = event.currentTarget.value;
1608
+ const nextValue = applyTransform(rawValue);
1609
+ setInnerValue(nextValue);
1610
+ onChangeAction?.(nextValue);
1611
+ },
1612
+ onChange: (event) => {
1613
+ const rawValue = event.target.value;
1614
+ if (isComposingRef.current) {
1615
+ setInnerValue(rawValue);
1616
+ return;
1617
+ }
1618
+ const nextValue = applyTransform(rawValue);
1619
+ setInnerValue(nextValue);
1620
+ onChangeAction?.(nextValue);
1621
+ }
1377
1622
  }
1378
- ) }, it);
1379
- }) }),
1623
+ ) }),
1624
+ trailingIcon && /* @__PURE__ */ jsx("span", { className: "text_field_icon", "aria-hidden": "true", children: trailingIcon }),
1625
+ label && showLabel && /* @__PURE__ */ jsx("label", { className: "text_field_label", htmlFor: inputId, children: label })
1626
+ ] }),
1627
+ supportingText && /* @__PURE__ */ jsx("div", { id: helperId, className: "text_field_helper", children: supportingText })
1628
+ ] });
1629
+ };
1630
+ TextField.displayName = "TextField";
1631
+ var ToastContext = React4.createContext(null);
1632
+ var VARIANT_ICONS = {
1633
+ success: /* @__PURE__ */ jsx(CheckCircle2, { size: 18 }),
1634
+ error: /* @__PURE__ */ jsx(XCircle, { size: 18 }),
1635
+ warning: /* @__PURE__ */ jsx(AlertTriangle, { size: 18 }),
1636
+ info: /* @__PURE__ */ jsx(Info, { size: 18 }),
1637
+ default: /* @__PURE__ */ jsx(Bell, { size: 18 })
1638
+ };
1639
+ var ToastItemComponent = ({ item, onRemove, closeAriaLabel }) => {
1640
+ const [exiting, setExiting] = React4.useState(false);
1641
+ const closingRef = React4.useRef(false);
1642
+ const close = React4.useCallback(() => {
1643
+ if (closingRef.current) return;
1644
+ closingRef.current = true;
1645
+ setExiting(true);
1646
+ setTimeout(() => onRemove(item.id), 260);
1647
+ }, [item.id, onRemove]);
1648
+ const itemClassName = cn("toast_item", exiting && "toast_item_exiting");
1649
+ return /* @__PURE__ */ jsxs("div", { className: itemClassName, role: item.variant === "error" ? "alert" : "status", children: [
1650
+ /* @__PURE__ */ jsx("span", { className: `toast_icon toast_icon_${item.variant}`, "aria-hidden": "true", children: VARIANT_ICONS[item.variant] }),
1651
+ /* @__PURE__ */ jsx("span", { className: "toast_message", children: item.message }),
1652
+ /* @__PURE__ */ jsx("button", { type: "button", className: "toast_close", onClick: close, "aria-label": closeAriaLabel, children: /* @__PURE__ */ jsx(X, { size: 14 }) }),
1380
1653
  /* @__PURE__ */ jsx(
1381
- "button",
1654
+ "div",
1382
1655
  {
1383
- type: "button",
1384
- className: "pagination_item",
1385
- onClick: () => onChange(page + 1),
1386
- disabled: nextDisabled,
1387
- "aria-label": nextLabel,
1388
- children: "\u203A"
1656
+ className: `toast_progress toast_progress_${item.variant}`,
1657
+ style: { "--toast-duration": `${item.duration}ms` },
1658
+ onAnimationEnd: close,
1659
+ "aria-hidden": "true"
1389
1660
  }
1390
1661
  )
1391
1662
  ] });
1392
1663
  };
1393
- var Modal = ({
1394
- open,
1395
- onClose,
1396
- closeOnOverlay = true,
1397
- width = 520,
1398
- title,
1664
+ var ToastProvider = ({
1399
1665
  children,
1400
- className,
1401
- ariaLabel,
1402
- ...props
1666
+ maxCount = 5,
1667
+ closeAriaLabel = "Close"
1403
1668
  }) => {
1404
- const panelRef = React9.useRef(null);
1405
- const titleId = React9.useId();
1406
- useFocusTrap(panelRef, open);
1407
- const handleEscape = React9.useEffectEvent((e) => {
1408
- if (e.key === "Escape") onClose?.();
1409
- });
1410
- React9.useEffect(() => {
1411
- if (!open) return;
1412
- document.addEventListener("keydown", handleEscape);
1413
- return () => document.removeEventListener("keydown", handleEscape);
1414
- }, [open]);
1415
- React9.useEffect(() => {
1416
- if (!open) return;
1417
- const body = document.body;
1418
- const openModals = parseInt(body.dataset.openModals || "0", 10);
1419
- if (openModals === 0) {
1420
- body.dataset.originalOverflow = window.getComputedStyle(body).overflow;
1421
- body.style.overflow = "hidden";
1422
- }
1423
- body.dataset.openModals = String(openModals + 1);
1424
- return () => {
1425
- const currentOpenModals = parseInt(body.dataset.openModals || "1", 10);
1426
- const nextOpenModals = currentOpenModals - 1;
1427
- if (nextOpenModals === 0) {
1428
- body.style.overflow = body.dataset.originalOverflow || "";
1429
- delete body.dataset.openModals;
1430
- delete body.dataset.originalOverflow;
1431
- } else {
1432
- body.dataset.openModals = String(nextOpenModals);
1433
- }
1434
- };
1435
- }, [open]);
1436
- if (!open) return null;
1437
- const panelClassName = cn("modal_panel", className);
1438
- const hasTitle = !!title;
1669
+ const [toasts, setToasts] = React4.useState([]);
1670
+ const [isMounted, setIsMounted] = React4.useState(false);
1671
+ React4.useEffect(() => {
1672
+ setIsMounted(true);
1673
+ }, []);
1674
+ const addToast = React4.useCallback(
1675
+ (message, variant, duration = 3e3) => {
1676
+ const id = crypto.randomUUID();
1677
+ setToasts((prev) => [{ id, message, variant, duration }, ...prev].slice(0, maxCount));
1678
+ },
1679
+ [maxCount]
1680
+ );
1681
+ const removeToast = React4.useCallback((id) => {
1682
+ setToasts((prev) => prev.filter((t) => t.id !== id));
1683
+ }, []);
1684
+ return /* @__PURE__ */ jsxs(ToastContext.Provider, { value: { addToast }, children: [
1685
+ children,
1686
+ isMounted && createPortal(
1687
+ /* @__PURE__ */ jsx(
1688
+ "div",
1689
+ {
1690
+ className: "toast_container",
1691
+ role: "region",
1692
+ "aria-live": "polite",
1693
+ "aria-atomic": "false",
1694
+ "aria-label": "Notifications",
1695
+ children: toasts.map((item) => /* @__PURE__ */ jsx(
1696
+ ToastItemComponent,
1697
+ {
1698
+ item,
1699
+ onRemove: removeToast,
1700
+ closeAriaLabel
1701
+ },
1702
+ item.id
1703
+ ))
1704
+ }
1705
+ ),
1706
+ document.body
1707
+ )
1708
+ ] });
1709
+ };
1710
+ var useToast = () => {
1711
+ const ctx = useContext(ToastContext);
1712
+ if (!ctx) {
1713
+ throw new Error("useToast must be used within ToastProvider");
1714
+ }
1715
+ return {
1716
+ /** 성공 메시지를 표시한다 */
1717
+ success: (message, duration) => ctx.addToast(message, "success", duration),
1718
+ /** 오류 메시지를 표시한다 */
1719
+ error: (message, duration) => ctx.addToast(message, "error", duration),
1720
+ /** 경고 메시지를 표시한다 */
1721
+ warning: (message, duration) => ctx.addToast(message, "warning", duration),
1722
+ /** 정보 메시지를 표시한다 */
1723
+ info: (message, duration) => ctx.addToast(message, "info", duration),
1724
+ /** 기본 메시지를 표시한다 */
1725
+ message: (message, duration) => ctx.addToast(message, "default", duration)
1726
+ };
1727
+ };
1728
+ var TopLoading = ({
1729
+ progress,
1730
+ color,
1731
+ height = 3,
1732
+ isLoading = true,
1733
+ ariaLabel = "Page loading"
1734
+ }) => {
1735
+ if (!isLoading) return null;
1736
+ const isIndeterminate = progress === void 0;
1439
1737
  return /* @__PURE__ */ jsx(
1440
1738
  "div",
1441
1739
  {
1442
- className: "modal",
1443
- role: "dialog",
1444
- "aria-modal": "true",
1445
- "aria-labelledby": hasTitle && !ariaLabel ? titleId : void 0,
1446
- "aria-label": !hasTitle ? ariaLabel ?? "Dialog" : ariaLabel,
1447
- onClick: () => closeOnOverlay && onClose?.(),
1448
- onKeyDown: (e) => {
1449
- if (e.key === "Escape") onClose?.();
1450
- },
1451
- children: /* @__PURE__ */ jsxs(
1740
+ className: "top_loading",
1741
+ style: { height },
1742
+ role: "progressbar",
1743
+ "aria-valuemin": 0,
1744
+ "aria-valuemax": 100,
1745
+ "aria-valuenow": isIndeterminate ? void 0 : progress,
1746
+ "aria-label": ariaLabel,
1747
+ children: /* @__PURE__ */ jsx(
1452
1748
  "div",
1453
1749
  {
1454
- ref: panelRef,
1455
- className: panelClassName,
1456
- style: { width },
1457
- role: "document",
1458
- onClick: (e) => e.stopPropagation(),
1459
- onKeyDown: (e) => {
1460
- if (e.key !== "Escape") e.stopPropagation();
1461
- },
1462
- ...props,
1463
- children: [
1464
- title && /* @__PURE__ */ jsx("div", { id: titleId, className: "modal_header", children: title }),
1465
- /* @__PURE__ */ jsx("div", { className: "modal_body", children })
1466
- ]
1750
+ className: cn("top_loading_bar", isIndeterminate && "top_loading_indeterminate"),
1751
+ style: {
1752
+ width: isIndeterminate ? void 0 : `${progress}%`,
1753
+ backgroundColor: color
1754
+ }
1467
1755
  }
1468
1756
  )
1469
1757
  }
1470
1758
  );
1471
1759
  };
1472
1760
 
1473
- export { AlertProvider, Button, Card, Checkbox, DatePicker, FileInput, Modal, Pagination, Radio, Select, Spinner, Switch, TextField, ToastProvider, TopLoading, a11y, baseBorderWidth, baseColors, baseTypography, borderWidth, breakpoints, colors, motion, opacity, radius, shadows, skeleton, spacing, typography, useAlert, useToast, zIndex };
1761
+ export { AlertProvider, Button, Card, Checkbox, Chip, DatePicker, Divider, FAB, FileInput, IconButton, LinearProgress, ListItem, Modal, Pagination, Radio, Select, Spinner, Switch, TextField, ToastProvider, TopLoading, a11y, baseBorderWidth, baseColors, baseTypography, borderWidth, breakpoints, colors, elevation, motion, opacity, radius, skeleton, spacing, typography, useAlert, useToast, zIndex };