@bigtablet/design-system 3.0.0 → 3.1.1

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.css CHANGED
@@ -718,6 +718,267 @@
718
718
  color: #fff;
719
719
  }
720
720
 
721
+ /* src/ui/navigation/bottom-nav/style.scss */
722
+ :root {
723
+ --bt-color-brand-primary: #121212;
724
+ --bt-color-brand-on-primary: #FFFFFF;
725
+ --bt-color-brand-primary-container: rgba(0, 0, 0, 0.05);
726
+ --bt-color-text-heading: #121212;
727
+ --bt-color-text-body: #666666;
728
+ --bt-color-text-caption: #888888;
729
+ --bt-color-text-brand: #121212;
730
+ --bt-color-text-inverse: #FFFFFF;
731
+ --bt-color-text-disabled: rgba(26, 26, 26, 0.38);
732
+ --bt-color-bg-solid: #FFFFFF;
733
+ --bt-color-bg-solid-dim: #F4F4F4;
734
+ --bt-color-bg-additive: rgba(0, 0, 0, 0.05);
735
+ --bt-color-bg-disabled: rgba(26, 26, 26, 0.12);
736
+ --bt-color-bg-overlay: rgba(0, 0, 0, 0.5);
737
+ --bt-color-bg-inverse-surface: #333333;
738
+ --bt-color-state-hover-on-light: rgba(0, 0, 0, 0.05);
739
+ --bt-color-state-pressed-on-light: rgba(26, 26, 26, 0.12);
740
+ --bt-color-state-focus-on-light: rgba(0, 0, 0, 0.08);
741
+ --bt-color-state-hover-on-dark: rgba(255, 255, 255, 0.05);
742
+ --bt-color-state-pressed-on-dark: rgba(255, 255, 255, 0.12);
743
+ --bt-color-state-focus-on-dark: rgba(255, 255, 255, 0.08);
744
+ --bt-color-border-default: #E5E5E5;
745
+ --bt-color-border-hover: #B3B3B3;
746
+ --bt-color-border-subtle: rgba(0, 0, 0, 0.08);
747
+ --bt-color-border-focus: #121212;
748
+ --bt-color-border-disabled: #F2F2F2;
749
+ --bt-color-status-error: #EF4444;
750
+ --bt-color-status-success: #10B981;
751
+ --bt-color-status-warning: #F59E0B;
752
+ --bt-color-status-info: #3B82F6;
753
+ --bt-color-accent-subtle: rgba(0, 0, 0, 0.05);
754
+ --bt-color-accent-muted: rgba(26, 26, 26, 0.12);
755
+ --bt-color-accent-light: #B3B3B3;
756
+ --bt-color-accent-default: #121212;
757
+ --bt-color-accent-strong: #121212;
758
+ --bt-color-accent-on-surface: #FFFFFF;
759
+ --bt-color-skeleton-base: #F3F3F3;
760
+ --bt-color-skeleton-wave: #f0f0f0;
761
+ --bt-color-skeleton-highlight: #fafafa;
762
+ }
763
+ [data-theme=dark] {
764
+ color-scheme: dark;
765
+ -webkit-font-smoothing: antialiased;
766
+ -moz-osx-font-smoothing: grayscale;
767
+ --bt-color-text-heading: rgba(255, 255, 255, 0.95);
768
+ --bt-color-text-body: #B4C2CD;
769
+ --bt-color-text-caption: #B3B3B3;
770
+ --bt-color-text-inverse: #121212;
771
+ --bt-color-text-disabled: rgba(255, 255, 255, 0.38);
772
+ --bt-color-bg-solid: #1F2630;
773
+ --bt-color-bg-solid-dim: #303841;
774
+ --bt-color-bg-additive: rgba(255, 255, 255, 0.05);
775
+ --bt-color-bg-disabled: rgba(255, 255, 255, 0.12);
776
+ --bt-color-bg-overlay: rgba(0, 0, 0, 0.7);
777
+ --bt-color-bg-inverse-surface: #F4F4F4;
778
+ --bt-color-state-hover-on-light: rgba(255, 255, 255, 0.08);
779
+ --bt-color-state-pressed-on-light: rgba(255, 255, 255, 0.12);
780
+ --bt-color-state-focus-on-light: rgba(255, 255, 255, 0.12);
781
+ --bt-color-state-hover-on-dark: rgba(0, 0, 0, 0.08);
782
+ --bt-color-state-pressed-on-dark: rgba(26, 26, 26, 0.12);
783
+ --bt-color-state-focus-on-dark: rgba(26, 26, 26, 0.12);
784
+ --bt-color-border-default: #3D4852;
785
+ --bt-color-border-hover: #5A8DCB;
786
+ --bt-color-border-subtle: rgba(255, 255, 255, 0.12);
787
+ --bt-color-border-focus: #FFFFFF;
788
+ --bt-color-border-disabled: #303841;
789
+ --bt-color-brand-primary-container: rgba(255, 255, 255, 0.08);
790
+ --bt-color-accent-subtle: rgba(255, 255, 255, 0.08);
791
+ --bt-color-accent-muted: rgba(255, 255, 255, 0.12);
792
+ --bt-color-accent-light: #999999;
793
+ --bt-color-accent-default: #FFFFFF;
794
+ --bt-color-accent-strong: #FFFFFF;
795
+ --bt-color-accent-on-surface: #121212;
796
+ --bt-color-skeleton-base: #3D4852;
797
+ --bt-color-skeleton-wave: #3D4852;
798
+ --bt-color-skeleton-highlight: #47555E;
799
+ }
800
+ @media (prefers-color-scheme: dark) {
801
+ :root:not([data-theme]) {
802
+ color-scheme: dark;
803
+ -webkit-font-smoothing: antialiased;
804
+ -moz-osx-font-smoothing: grayscale;
805
+ --bt-color-text-heading: rgba(255, 255, 255, 0.95);
806
+ --bt-color-text-body: #B4C2CD;
807
+ --bt-color-text-caption: #B3B3B3;
808
+ --bt-color-text-inverse: #121212;
809
+ --bt-color-text-disabled: rgba(255, 255, 255, 0.38);
810
+ --bt-color-bg-solid: #1F2630;
811
+ --bt-color-bg-solid-dim: #303841;
812
+ --bt-color-bg-additive: rgba(255, 255, 255, 0.05);
813
+ --bt-color-bg-disabled: rgba(255, 255, 255, 0.12);
814
+ --bt-color-bg-overlay: rgba(0, 0, 0, 0.7);
815
+ --bt-color-bg-inverse-surface: #F4F4F4;
816
+ --bt-color-state-hover-on-light: rgba(255, 255, 255, 0.08);
817
+ --bt-color-state-pressed-on-light: rgba(255, 255, 255, 0.12);
818
+ --bt-color-state-focus-on-light: rgba(255, 255, 255, 0.12);
819
+ --bt-color-state-hover-on-dark: rgba(0, 0, 0, 0.08);
820
+ --bt-color-state-pressed-on-dark: rgba(26, 26, 26, 0.12);
821
+ --bt-color-state-focus-on-dark: rgba(26, 26, 26, 0.12);
822
+ --bt-color-border-default: #3D4852;
823
+ --bt-color-border-hover: #5A8DCB;
824
+ --bt-color-border-subtle: rgba(255, 255, 255, 0.12);
825
+ --bt-color-border-focus: #FFFFFF;
826
+ --bt-color-border-disabled: #303841;
827
+ --bt-color-brand-primary-container: rgba(255, 255, 255, 0.08);
828
+ --bt-color-accent-subtle: rgba(255, 255, 255, 0.08);
829
+ --bt-color-accent-muted: rgba(255, 255, 255, 0.12);
830
+ --bt-color-accent-light: #999999;
831
+ --bt-color-accent-default: #FFFFFF;
832
+ --bt-color-accent-strong: #FFFFFF;
833
+ --bt-color-accent-on-surface: #121212;
834
+ --bt-color-skeleton-base: #3D4852;
835
+ --bt-color-skeleton-wave: #3D4852;
836
+ --bt-color-skeleton-highlight: #47555E;
837
+ }
838
+ }
839
+ :root {
840
+ --bt-focus-ring: 0 0 0 3px rgba(0, 0, 0, 0.15);
841
+ --bt-focus-ring-error: 0 0 0 3px rgba(239, 68, 68, 0.30);
842
+ --bt-focus-ring-success: 0 0 0 3px rgba(16, 185, 129, 0.30);
843
+ }
844
+ [data-theme=dark] {
845
+ --bt-focus-ring: 0 0 0 3px rgba(255, 255, 255, 0.35);
846
+ --bt-focus-ring-error: 0 0 0 3px rgba(239, 68, 68, 0.45);
847
+ --bt-focus-ring-success: 0 0 0 3px rgba(16, 185, 129, 0.45);
848
+ }
849
+ @media (prefers-color-scheme: dark) {
850
+ :root:not([data-theme]) {
851
+ --bt-focus-ring: 0 0 0 3px rgba(255, 255, 255, 0.35);
852
+ --bt-focus-ring-error: 0 0 0 3px rgba(239, 68, 68, 0.45);
853
+ --bt-focus-ring-success: 0 0 0 3px rgba(16, 185, 129, 0.45);
854
+ }
855
+ }
856
+ :root {
857
+ --bt-elevation-level1: 0 1px 1px -1px rgba(0, 0, 0, 0.20), 0 3px 3px 0px rgba(0, 0, 0, 0.12);
858
+ --bt-elevation-level2: 0 2px 2px -2px rgba(0, 0, 0, 0.20), 0 6px 6px 0px rgba(0, 0, 0, 0.12);
859
+ --bt-elevation-level3: 0 3px 3px -3px rgba(0, 0, 0, 0.20), 0 9px 9px 0px rgba(0, 0, 0, 0.12);
860
+ --bt-elevation-level4: 0 5px 5px -5px rgba(0, 0, 0, 0.20), 0 15px 15px 0px rgba(0, 0, 0, 0.12);
861
+ --bt-elevation-level5: 0 8px 10px -5px rgba(0, 0, 0, 0.20), 0 20px 20px 0px rgba(0, 0, 0, 0.12);
862
+ }
863
+ [data-theme=dark] {
864
+ --bt-elevation-level1: 0 1px 2px 0 rgba(0, 0, 0, 0.45), 0 3px 6px 0 rgba(0, 0, 0, 0.30);
865
+ --bt-elevation-level2: 0 2px 4px 0 rgba(0, 0, 0, 0.45), 0 6px 12px 0 rgba(0, 0, 0, 0.30);
866
+ --bt-elevation-level3: 0 3px 6px 0 rgba(0, 0, 0, 0.50), 0 9px 18px 0 rgba(0, 0, 0, 0.35);
867
+ --bt-elevation-level4: 0 5px 10px 0 rgba(0, 0, 0, 0.55), 0 15px 30px 0 rgba(0, 0, 0, 0.40);
868
+ --bt-elevation-level5: 0 8px 16px 0 rgba(0, 0, 0, 0.60), 0 20px 40px 0 rgba(0, 0, 0, 0.45);
869
+ }
870
+ @media (prefers-color-scheme: dark) {
871
+ :root:not([data-theme]) {
872
+ --bt-elevation-level1: 0 1px 2px 0 rgba(0, 0, 0, 0.45), 0 3px 6px 0 rgba(0, 0, 0, 0.30);
873
+ --bt-elevation-level2: 0 2px 4px 0 rgba(0, 0, 0, 0.45), 0 6px 12px 0 rgba(0, 0, 0, 0.30);
874
+ --bt-elevation-level3: 0 3px 6px 0 rgba(0, 0, 0, 0.50), 0 9px 18px 0 rgba(0, 0, 0, 0.35);
875
+ --bt-elevation-level4: 0 5px 10px 0 rgba(0, 0, 0, 0.55), 0 15px 30px 0 rgba(0, 0, 0, 0.40);
876
+ --bt-elevation-level5: 0 8px 16px 0 rgba(0, 0, 0, 0.60), 0 20px 40px 0 rgba(0, 0, 0, 0.45);
877
+ }
878
+ }
879
+ @keyframes skeleton_loading {
880
+ 0% {
881
+ background-position: 100% 0;
882
+ }
883
+ 100% {
884
+ background-position: 0 0;
885
+ }
886
+ }
887
+ :root {
888
+ --bt-bottom-nav-height: 56px;
889
+ --bt-bottom-nav-safe-area: env(safe-area-inset-bottom, 0px);
890
+ --bt-bottom-nav-total-height: calc( var(--bt-bottom-nav-height) + var(--bt-bottom-nav-safe-area) );
891
+ }
892
+ .bottom_nav {
893
+ position: fixed;
894
+ left: 0;
895
+ right: 0;
896
+ bottom: 0;
897
+ z-index: 100;
898
+ display: flex;
899
+ align-items: stretch;
900
+ justify-content: space-around;
901
+ height: var(--bt-bottom-nav-total-height);
902
+ padding-bottom: var(--bt-bottom-nav-safe-area);
903
+ background: var(--bt-color-bg-solid);
904
+ border-top: 1px solid var(--bt-color-border-default);
905
+ }
906
+ .bottom_nav_item {
907
+ flex: 1;
908
+ min-width: 0;
909
+ display: inline-flex;
910
+ flex-direction: column;
911
+ align-items: center;
912
+ justify-content: center;
913
+ gap: 2px;
914
+ padding: 8px 4px;
915
+ border: none;
916
+ background: transparent;
917
+ color: var(--bt-color-text-caption);
918
+ cursor: pointer;
919
+ text-decoration: none;
920
+ min-height: 56px;
921
+ transition: color 0.1s cubic-bezier(0.16, 1, 0.3, 1), background 0.1s cubic-bezier(0.16, 1, 0.3, 1);
922
+ }
923
+ .bottom_nav_item:hover:not(.bottom_nav_item_active) {
924
+ background: var(--bt-color-state-hover-on-light);
925
+ color: var(--bt-color-text-heading);
926
+ }
927
+ .bottom_nav_item:active:not(.bottom_nav_item_active) {
928
+ background: var(--bt-color-state-pressed-on-light);
929
+ }
930
+ .bottom_nav_item:focus-visible {
931
+ outline: none;
932
+ box-shadow: var(--bt-focus-ring);
933
+ }
934
+ .bottom_nav_item:disabled,
935
+ .bottom_nav_item_disabled {
936
+ opacity: 0.4;
937
+ cursor: not-allowed;
938
+ pointer-events: none;
939
+ }
940
+ .bottom_nav_item_active {
941
+ color: var(--bt-color-accent-default);
942
+ }
943
+ .bottom_nav_item_active .bottom_nav_item_label {
944
+ font-weight: 600;
945
+ }
946
+ .bottom_nav_item_icon {
947
+ position: relative;
948
+ display: inline-flex;
949
+ align-items: center;
950
+ justify-content: center;
951
+ width: 24px;
952
+ height: 24px;
953
+ }
954
+ .bottom_nav_item_badge {
955
+ position: absolute;
956
+ top: -4px;
957
+ right: -8px;
958
+ pointer-events: none;
959
+ }
960
+ .bottom_nav_item_label {
961
+ font-size: 12px;
962
+ font-weight: 500;
963
+ line-height: 16px;
964
+ letter-spacing: 0px;
965
+ line-height: 1.2;
966
+ text-align: center;
967
+ max-width: 100%;
968
+ overflow: hidden;
969
+ text-overflow: ellipsis;
970
+ white-space: nowrap;
971
+ }
972
+ .bottom_nav_spacer {
973
+ height: var(--bt-bottom-nav-total-height);
974
+ flex-shrink: 0;
975
+ }
976
+ @media (prefers-reduced-motion: reduce) {
977
+ .bottom_nav_item {
978
+ transition: none;
979
+ }
980
+ }
981
+
721
982
  /* src/ui/navigation/breadcrumb/style.scss */
722
983
  :root {
723
984
  --bt-color-brand-primary: #121212;
@@ -2175,6 +2436,102 @@
2175
2436
  padding: 8px;
2176
2437
  gap: 0;
2177
2438
  }
2439
+ @media (max-width: 599px) {
2440
+ :root {
2441
+ --bt-sidebar-height: 56px;
2442
+ --bt-sidebar-safe-area: env(safe-area-inset-bottom, 0px);
2443
+ --bt-sidebar-total-height: calc( var(--bt-sidebar-height) + var(--bt-sidebar-safe-area) );
2444
+ }
2445
+ .sidebar:not(.sidebar_static) {
2446
+ position: fixed;
2447
+ inset: auto 0 0 0;
2448
+ width: 100% !important;
2449
+ height: auto;
2450
+ min-height: 0;
2451
+ flex-direction: row;
2452
+ border-right: none;
2453
+ border-top: 1px solid var(--bt-color-border-default);
2454
+ box-shadow: var(--bt-elevation-level2);
2455
+ z-index: 100;
2456
+ padding-bottom: var(--bt-sidebar-safe-area);
2457
+ }
2458
+ .sidebar:not(.sidebar_static) .sidebar_header,
2459
+ .sidebar:not(.sidebar_static) .sidebar_footer,
2460
+ .sidebar:not(.sidebar_static) .sidebar_collapse_btn,
2461
+ .sidebar:not(.sidebar_static) .sidebar_section_label {
2462
+ display: none;
2463
+ }
2464
+ .sidebar:not(.sidebar_static) .sidebar_nav {
2465
+ flex: 1;
2466
+ flex-direction: row;
2467
+ align-items: stretch;
2468
+ padding: 0;
2469
+ gap: 0;
2470
+ overflow: visible;
2471
+ }
2472
+ .sidebar:not(.sidebar_static) .sidebar_section {
2473
+ display: contents;
2474
+ }
2475
+ .sidebar:not(.sidebar_static) .sidebar_item {
2476
+ flex: 1;
2477
+ flex-direction: column;
2478
+ align-items: center;
2479
+ justify-content: center;
2480
+ gap: 4px;
2481
+ padding: 6px 4px;
2482
+ min-height: 56px;
2483
+ border-radius: 0;
2484
+ color: var(--bt-color-text-body);
2485
+ }
2486
+ .sidebar:not(.sidebar_static) .sidebar_item:hover:not(.sidebar_item_active) {
2487
+ background: var(--bt-color-state-hover-on-light);
2488
+ color: var(--bt-color-text-heading);
2489
+ }
2490
+ .sidebar:not(.sidebar_static) .sidebar_item_active {
2491
+ background: transparent;
2492
+ color: var(--bt-color-text-heading);
2493
+ }
2494
+ .sidebar:not(.sidebar_static) .sidebar_item_active .sidebar_item_icon {
2495
+ background: var(--bt-color-brand-primary);
2496
+ color: var(--bt-color-brand-on-primary);
2497
+ border-radius: 9999px;
2498
+ padding: 0 16px;
2499
+ width: auto;
2500
+ height: 32px;
2501
+ min-width: 56px;
2502
+ }
2503
+ .sidebar:not(.sidebar_static) .sidebar_item_active .sidebar_item_label {
2504
+ font-weight: 600;
2505
+ }
2506
+ .sidebar:not(.sidebar_static) .sidebar_item_icon {
2507
+ width: 24px;
2508
+ height: 24px;
2509
+ transition: background 0.1s ease-in-out, color 0.1s ease-in-out;
2510
+ }
2511
+ .sidebar:not(.sidebar_static) .sidebar_item_label {
2512
+ font-size: 12px;
2513
+ font-weight: 500;
2514
+ line-height: 16px;
2515
+ letter-spacing: 0px;
2516
+ flex: 0 0 auto;
2517
+ line-height: 1.2;
2518
+ text-align: center;
2519
+ max-width: 100%;
2520
+ white-space: nowrap;
2521
+ }
2522
+ .sidebar:not(.sidebar_static) .sidebar_item_trailing {
2523
+ display: none;
2524
+ }
2525
+ .sidebar:not(.sidebar_static).sidebar_collapsed .sidebar_item_label {
2526
+ position: static;
2527
+ width: auto;
2528
+ height: auto;
2529
+ padding: 0;
2530
+ margin: 0;
2531
+ overflow: visible;
2532
+ clip: auto;
2533
+ }
2534
+ }
2178
2535
 
2179
2536
  /* src/ui/navigation/tabs/style.scss */
2180
2537
  :root {
package/dist/index.d.ts CHANGED
@@ -171,6 +171,62 @@ interface BadgeProps extends React$1.HTMLAttributes<HTMLSpanElement> {
171
171
  */
172
172
  declare const Badge: ({ variant, shape, size, count, max, className, children, ...props }: BadgeProps) => react_jsx_runtime.JSX.Element;
173
173
 
174
+ interface BottomNavProps extends Omit<React$1.HTMLAttributes<HTMLElement>, "onChange"> {
175
+ /** 스크린 리더 레이블 (기본 "주요 메뉴") */
176
+ ariaLabel?: string;
177
+ /** 2–5 개의 `BottomNavItem` */
178
+ children: React$1.ReactNode;
179
+ }
180
+ /**
181
+ * 모바일 하단 네비게이션 바.
182
+ *
183
+ * `position: fixed; bottom: 0` 으로 viewport 하단 고정. iOS 홈 인디케이터 영역
184
+ * (`env(safe-area-inset-bottom)`) 자동 패딩. 본문이 가려지지 않게 페이지 끝에
185
+ * `<BottomNavSpacer />` 를 깔거나 `--bt-bottom-nav-height` CSS 변수로 padding 계산.
186
+ *
187
+ * @example
188
+ * ```tsx
189
+ * <BottomNav>
190
+ * <BottomNavItem icon={<Home />} label="주문" active />
191
+ * <BottomNavItem icon={<Menu />} label="메뉴" />
192
+ * <BottomNavItem icon={<Chart />} label="매출" />
193
+ * </BottomNav>
194
+ * <BottomNavSpacer />
195
+ * ```
196
+ */
197
+ declare const BottomNav: ({ ariaLabel, className, children, ...props }: BottomNavProps) => react_jsx_runtime.JSX.Element;
198
+ interface BottomNavItemCommon {
199
+ /** 아이콘 (필수) */
200
+ icon: React$1.ReactNode;
201
+ /** 라벨 텍스트 (필수, 짧게 — 2–4자) */
202
+ label: string;
203
+ /** 활성 상태 */
204
+ active?: boolean;
205
+ /** 아이콘 우상단 dot/카운트 (Badge 등) */
206
+ badge?: React$1.ReactNode;
207
+ }
208
+ type BottomNavItemButton = BottomNavItemCommon & {
209
+ as?: "button";
210
+ href?: never;
211
+ } & Omit<React$1.ButtonHTMLAttributes<HTMLButtonElement>, "children">;
212
+ type BottomNavItemAnchor = BottomNavItemCommon & {
213
+ as: "a";
214
+ href: string;
215
+ /** anchor 에 native `disabled` 없음 — `aria-disabled` + `preventDefault` 로 처리. */
216
+ disabled?: boolean;
217
+ } & Omit<React$1.AnchorHTMLAttributes<HTMLAnchorElement>, "children" | "type">;
218
+ type BottomNavItemProps = BottomNavItemButton | BottomNavItemAnchor;
219
+ /**
220
+ * `BottomNav` 의 항목. icon + label 수직 스택.
221
+ * active 시 `aria-current="page"` 자동 부여.
222
+ */
223
+ declare const BottomNavItem: (props: BottomNavItemProps) => react_jsx_runtime.JSX.Element;
224
+ /**
225
+ * 페이지 본문 끝에 두면 `BottomNav` 가 콘텐츠를 가리지 않게 빈 공간 확보.
226
+ * `--bt-bottom-nav-height` (+ safe-area) 만큼 height.
227
+ */
228
+ declare const BottomNavSpacer: ({ className, ...props }: React$1.HTMLAttributes<HTMLDivElement>) => react_jsx_runtime.JSX.Element;
229
+
174
230
  interface BreadcrumbItem {
175
231
  /** 표시 텍스트 */
176
232
  label: React$1.ReactNode;
@@ -326,6 +382,7 @@ interface NavLinkProps extends React$1.AnchorHTMLAttributes<HTMLAnchorElement> {
326
382
  }
327
383
  declare const NavLink: ({ active, className, children, ...props }: NavLinkProps) => react_jsx_runtime.JSX.Element;
328
384
 
385
+ type SidebarMode = "auto" | "static";
329
386
  interface SidebarProps extends Omit<React$1.HTMLAttributes<HTMLElement>, "onChange"> {
330
387
  /** 상단 brand 영역 (펼친 상태 로고) */
331
388
  header?: React$1.ReactNode;
@@ -347,6 +404,12 @@ interface SidebarProps extends Omit<React$1.HTMLAttributes<HTMLElement>, "onChan
347
404
  width?: number;
348
405
  /** collapsed 너비 (기본 64px) */
349
406
  collapsedWidth?: number;
407
+ /**
408
+ * 반응형 모드 (기본 `"auto"`).
409
+ * - `"auto"`: viewport `< 600px` 에서 자동으로 하단 bar 로 변신.
410
+ * - `"static"`: 어떤 viewport 에서도 좌측 rail 유지 (admin desktop-only).
411
+ */
412
+ mode?: SidebarMode;
350
413
  }
351
414
  /**
352
415
  * admin/dashboard 좌측 네비게이션.
@@ -363,20 +426,25 @@ interface SidebarProps extends Omit<React$1.HTMLAttributes<HTMLElement>, "onChan
363
426
  * </Sidebar>
364
427
  * ```
365
428
  */
366
- declare const Sidebar: ({ header, headerCollapsed, footer, collapsed: collapsedProp, defaultCollapsed, onCollapsedChange, collapsible, toggleLabel, width, collapsedWidth, className, children, style, ...props }: SidebarProps) => react_jsx_runtime.JSX.Element;
367
- interface SidebarItemProps extends React$1.ButtonHTMLAttributes<HTMLButtonElement> {
429
+ declare const Sidebar: ({ header, headerCollapsed, footer, collapsed: collapsedProp, defaultCollapsed, onCollapsedChange, collapsible, toggleLabel, width, collapsedWidth, mode, className, children, style, ...props }: SidebarProps) => react_jsx_runtime.JSX.Element;
430
+ interface SidebarItemCommon {
368
431
  /** 왼쪽 아이콘 */
369
432
  icon?: React$1.ReactNode;
370
433
  /** 현재 활성 상태 */
371
434
  active?: boolean;
372
435
  /** 오른쪽 trailing (Badge 등) */
373
436
  trailing?: React$1.ReactNode;
374
- /** 링크 컴포넌트 (Next Link 등). `asChild` 패턴 — `as="a" href="..."` */
375
- as?: "button" | "a";
376
- /** as="a"일 때 사용 */
377
- href?: string;
378
437
  }
379
- declare const SidebarItem: ({ icon, active, trailing, as, href, className, children, type, ...props }: SidebarItemProps) => react_jsx_runtime.JSX.Element;
438
+ type SidebarItemButton = SidebarItemCommon & {
439
+ as?: "button";
440
+ href?: never;
441
+ } & React$1.ButtonHTMLAttributes<HTMLButtonElement>;
442
+ type SidebarItemAnchor = SidebarItemCommon & {
443
+ as: "a";
444
+ href: string;
445
+ } & Omit<React$1.AnchorHTMLAttributes<HTMLAnchorElement>, "type">;
446
+ type SidebarItemProps = SidebarItemButton | SidebarItemAnchor;
447
+ declare const SidebarItem: (props: SidebarItemProps) => react_jsx_runtime.JSX.Element;
380
448
  interface SidebarSectionProps extends React$1.HTMLAttributes<HTMLDivElement> {
381
449
  /** 섹션 라벨 (collapsed 상태에선 hidden) */
382
450
  label?: string;
@@ -1866,4 +1934,4 @@ interface GridProps extends React$1.HTMLAttributes<HTMLDivElement> {
1866
1934
  */
1867
1935
  declare const Grid: ({ cols, minColWidth, gap, rowGap, colGap, singleColOnMobile, as: Tag, className, children, style, ...props }: GridProps) => react_jsx_runtime.JSX.Element;
1868
1936
 
1869
- export { Accordion, type AccordionItem, type AccordionProps, AlertProvider, Avatar, type AvatarProps, type AvatarShape, type AvatarSize, Badge, type BadgeProps, type BadgeShape, type BadgeSize, type BadgeVariant, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, Button, type ButtonProps, Card, type CardProps, Checkbox, type CheckboxProps, Chip, type ChipProps, type ChipSize, type ChipTone, type ChipType, Container, type ContainerProps, type ContainerSize, DatePicker, type DatePickerProps, Divider, type DividerProps, Dropdown, type DropdownOption, type DropdownProps, type DropdownSize, EmptyState, type EmptyStateProps, FileInput, type FileInputProps, Grid, type GridCols, type GridGap, type GridProps, Hero, type HeroAction, type HeroAlign, type HeroHeight, type HeroOverlay, type HeroProps, Icon, IconButton, type IconButtonProps, type IconButtonSize, type IconButtonVariant, type IconProps, LinearProgress, type LinearProgressProps, ListItem, type ListItemProps, MediaCard, type MediaCardImage, type MediaCardImagePosition, type MediaCardProps, type MediaCardShadow, Menu, type MenuItem, type MenuProps, Modal, type ModalProps, NavBar, type NavBarLayout, type NavBarLocaleConfig, type NavBarLocaleOption, type NavBarProps, type NavBarVariant, NavLink, type NavLinkProps, OtpInput, type OtpInputProps, Pagination, type PaginationProps, Radio, type RadioProps, type ResolvedTheme, Section, type SectionBg, type SectionProps, type SectionSpacing, Sidebar, SidebarItem, type SidebarItemProps, type SidebarProps, SidebarSection, type SidebarSectionProps, Skeleton, type SkeletonProps, type SkeletonVariant, Spinner, type SpinnerProps, Stack, type StackAlign, type StackDirection, type StackGap, type StackJustify, type StackProps, type StackWrap, Tab, TabList, type TabListProps, TabPanel, type TabPanelProps, type TabProps, Table, type TableColumn, type TableProps, type TableSize, Tabs, type TabsProps, type TabsSize, type TabsVariant, TextField, type TextFieldProps, type ThemeMode, ThemeProvider, type ThemeProviderProps, ToastProvider, Toggle, type ToggleProps, Tooltip, type TooltipPlacement, type TooltipProps, TopLoading, type TopLoadingProps, a11y, baseBorderWidth, baseColors, baseTypography, borderWidth, breakpoints, cn, colors, elevation, motion, opacity, radius, skeleton, spacing, typography, useAlert, useFocusTrap, useSpringHover, useSpringPresence, useTheme, useToast, zIndex };
1937
+ export { Accordion, type AccordionItem, type AccordionProps, AlertProvider, Avatar, type AvatarProps, type AvatarShape, type AvatarSize, Badge, type BadgeProps, type BadgeShape, type BadgeSize, type BadgeVariant, BottomNav, BottomNavItem, type BottomNavItemProps, type BottomNavProps, BottomNavSpacer, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, Button, type ButtonProps, Card, type CardProps, Checkbox, type CheckboxProps, Chip, type ChipProps, type ChipSize, type ChipTone, type ChipType, Container, type ContainerProps, type ContainerSize, DatePicker, type DatePickerProps, Divider, type DividerProps, Dropdown, type DropdownOption, type DropdownProps, type DropdownSize, EmptyState, type EmptyStateProps, FileInput, type FileInputProps, Grid, type GridCols, type GridGap, type GridProps, Hero, type HeroAction, type HeroAlign, type HeroHeight, type HeroOverlay, type HeroProps, Icon, IconButton, type IconButtonProps, type IconButtonSize, type IconButtonVariant, type IconProps, LinearProgress, type LinearProgressProps, ListItem, type ListItemProps, MediaCard, type MediaCardImage, type MediaCardImagePosition, type MediaCardProps, type MediaCardShadow, Menu, type MenuItem, type MenuProps, Modal, type ModalProps, NavBar, type NavBarLayout, type NavBarLocaleConfig, type NavBarLocaleOption, type NavBarProps, type NavBarVariant, NavLink, type NavLinkProps, OtpInput, type OtpInputProps, Pagination, type PaginationProps, Radio, type RadioProps, type ResolvedTheme, Section, type SectionBg, type SectionProps, type SectionSpacing, Sidebar, SidebarItem, type SidebarItemProps, type SidebarMode, type SidebarProps, SidebarSection, type SidebarSectionProps, Skeleton, type SkeletonProps, type SkeletonVariant, Spinner, type SpinnerProps, Stack, type StackAlign, type StackDirection, type StackGap, type StackJustify, type StackProps, type StackWrap, Tab, TabList, type TabListProps, TabPanel, type TabPanelProps, type TabProps, Table, type TableColumn, type TableProps, type TableSize, Tabs, type TabsProps, type TabsSize, type TabsVariant, TextField, type TextFieldProps, type ThemeMode, ThemeProvider, type ThemeProviderProps, ToastProvider, Toggle, type ToggleProps, Tooltip, type TooltipPlacement, type TooltipProps, TopLoading, type TopLoadingProps, a11y, baseBorderWidth, baseColors, baseTypography, borderWidth, breakpoints, cn, colors, elevation, motion, opacity, radius, skeleton, spacing, typography, useAlert, useFocusTrap, useSpringHover, useSpringPresence, useTheme, useToast, zIndex };
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
  import './index.css';
3
- import * as React16 from 'react';
3
+ import * as React17 from 'react';
4
4
  import { createContext, useRef, useState, useLayoutEffect, useId, useEffect, useContext, useCallback, useMemo, Fragment } from 'react';
5
5
  import { useSpring, animated } from '@react-spring/web';
6
6
  import { ChevronDown, Globe, ChevronRight, ChevronLeft, Check, Image, X, XCircle, AlertTriangle, CheckCircle2, Info, Bell } from 'lucide-react';
7
- import { jsx, jsxs } from 'react/jsx-runtime';
7
+ import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
8
8
  import { createPortal } from 'react-dom';
9
9
 
10
10
  // src/utils/cn.ts
@@ -38,8 +38,8 @@ var FOCUSABLE_SELECTORS = [
38
38
  '[tabindex]:not([tabindex="-1"])'
39
39
  ].join(", ");
40
40
  function useFocusTrap(containerRef, isActive) {
41
- const previousActiveElement = React16.useRef(null);
42
- React16.useEffect(() => {
41
+ const previousActiveElement = React17.useRef(null);
42
+ React17.useEffect(() => {
43
43
  if (!isActive) return;
44
44
  const container = containerRef.current;
45
45
  if (!container) return;
@@ -91,7 +91,7 @@ function useSpringHover({
91
91
  scale = 1.02,
92
92
  lift = -2
93
93
  } = {}) {
94
- const [hovered, setHovered] = React16.useState(false);
94
+ const [hovered, setHovered] = React17.useState(false);
95
95
  const style = useSpring({
96
96
  transform: hovered ? `translateY(${lift}px) scale(${scale})` : "translateY(0px) scale(1)",
97
97
  config: { tension: 320, friction: 22 }
@@ -139,7 +139,7 @@ var Accordion = ({
139
139
  ...props
140
140
  }) => {
141
141
  const isControlled = controlledKeys !== void 0;
142
- const [internalKeys, setInternalKeys] = React16.useState(defaultOpenKeys);
142
+ const [internalKeys, setInternalKeys] = React17.useState(defaultOpenKeys);
143
143
  const open = isControlled ? controlledKeys ?? [] : internalKeys;
144
144
  const toggle = (key) => {
145
145
  const isOpen = open.includes(key);
@@ -205,7 +205,7 @@ var Avatar = ({
205
205
  style,
206
206
  ...props
207
207
  }) => {
208
- const [imgFailed, setImgFailed] = React16.useState(false);
208
+ const [imgFailed, setImgFailed] = React17.useState(false);
209
209
  const showImage = src && !imgFailed;
210
210
  const initials = getInitials(name);
211
211
  return /* @__PURE__ */ jsx(
@@ -263,6 +263,89 @@ var Badge = ({
263
263
  }
264
264
  );
265
265
  };
266
+ var BottomNav = ({
267
+ ariaLabel = "\uC8FC\uC694 \uBA54\uB274",
268
+ className,
269
+ children,
270
+ ...props
271
+ }) => {
272
+ return /* @__PURE__ */ jsx(
273
+ "nav",
274
+ {
275
+ className: cn("bottom_nav", className),
276
+ "aria-label": ariaLabel,
277
+ ...props,
278
+ children
279
+ }
280
+ );
281
+ };
282
+ var BottomNavItem = (props) => {
283
+ const {
284
+ icon,
285
+ label,
286
+ active,
287
+ badge,
288
+ as = "button",
289
+ className,
290
+ disabled,
291
+ ...rest
292
+ } = props;
293
+ const classes = cn(
294
+ "bottom_nav_item",
295
+ active && "bottom_nav_item_active",
296
+ disabled && "bottom_nav_item_disabled",
297
+ className
298
+ );
299
+ const ariaCurrent = active ? "page" : void 0;
300
+ const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [
301
+ /* @__PURE__ */ jsxs("span", { className: "bottom_nav_item_icon", "aria-hidden": "true", children: [
302
+ icon,
303
+ badge && /* @__PURE__ */ jsx("span", { className: "bottom_nav_item_badge", children: badge })
304
+ ] }),
305
+ /* @__PURE__ */ jsx("span", { className: "bottom_nav_item_label", children: label })
306
+ ] });
307
+ if (as === "a") {
308
+ const { href, onClick: onClick2, ...anchorRest } = rest;
309
+ return (
310
+ // biome-ignore lint/a11y/useValidAnchor: navigation link with optional active state
311
+ /* @__PURE__ */ jsx(
312
+ "a",
313
+ {
314
+ className: classes,
315
+ href,
316
+ "aria-current": ariaCurrent,
317
+ "aria-disabled": disabled ? "true" : void 0,
318
+ tabIndex: disabled ? -1 : void 0,
319
+ onClick: (e) => {
320
+ if (disabled) {
321
+ e.preventDefault();
322
+ return;
323
+ }
324
+ onClick2?.(e);
325
+ },
326
+ ...anchorRest,
327
+ children: content
328
+ }
329
+ )
330
+ );
331
+ }
332
+ const { type, onClick, ...buttonRest } = rest;
333
+ return /* @__PURE__ */ jsx(
334
+ "button",
335
+ {
336
+ type: type ?? "button",
337
+ className: classes,
338
+ disabled,
339
+ "aria-current": ariaCurrent,
340
+ onClick,
341
+ ...buttonRest,
342
+ children: content
343
+ }
344
+ );
345
+ };
346
+ var BottomNavSpacer = ({ className, ...props }) => {
347
+ return /* @__PURE__ */ jsx("div", { className: cn("bottom_nav_spacer", className), "aria-hidden": "true", ...props });
348
+ };
266
349
  var Breadcrumb = ({
267
350
  items,
268
351
  separator,
@@ -309,11 +392,11 @@ var EmptyState = ({
309
392
  ] });
310
393
  };
311
394
  var Menu = ({ items, trigger, align = "start" }) => {
312
- const [open, setOpen] = React16.useState(false);
313
- const wrapperRef = React16.useRef(null);
314
- const menuId = React16.useId();
395
+ const [open, setOpen] = React17.useState(false);
396
+ const wrapperRef = React17.useRef(null);
397
+ const menuId = React17.useId();
315
398
  const style = useSpringPresence({ visible: open, from: "translateY(-4px)" });
316
- React16.useEffect(() => {
399
+ React17.useEffect(() => {
317
400
  if (!open) return;
318
401
  const handleClick = (e) => {
319
402
  if (!wrapperRef.current?.contains(e.target)) setOpen(false);
@@ -328,7 +411,7 @@ var Menu = ({ items, trigger, align = "start" }) => {
328
411
  document.removeEventListener("keydown", handleEsc);
329
412
  };
330
413
  }, [open]);
331
- const triggerWithProps = React16.cloneElement(
414
+ const triggerWithProps = React17.cloneElement(
332
415
  trigger,
333
416
  {
334
417
  onClick: () => setOpen((o) => !o),
@@ -540,23 +623,30 @@ var Sidebar = ({
540
623
  toggleLabel = "\uC0AC\uC774\uB4DC\uBC14 \uD1A0\uAE00",
541
624
  width = 240,
542
625
  collapsedWidth = 64,
626
+ mode = "auto",
543
627
  className,
544
628
  children,
545
629
  style,
546
630
  ...props
547
631
  }) => {
548
632
  const isControlled = collapsedProp !== void 0;
549
- const [internalCollapsed, setInternalCollapsed] = React16.useState(defaultCollapsed);
633
+ const [internalCollapsed, setInternalCollapsed] = React17.useState(defaultCollapsed);
550
634
  const collapsed = isControlled ? collapsedProp : internalCollapsed;
551
- const toggle = React16.useCallback(() => {
635
+ const toggle = React17.useCallback(() => {
552
636
  const next = !collapsed;
553
637
  if (!isControlled) setInternalCollapsed(next);
554
638
  onCollapsedChange?.(next);
555
639
  }, [collapsed, isControlled, onCollapsedChange]);
640
+ const isStatic = mode === "static";
556
641
  return /* @__PURE__ */ jsxs(
557
642
  "aside",
558
643
  {
559
- className: cn("sidebar", collapsed && "sidebar_collapsed", className),
644
+ className: cn(
645
+ "sidebar",
646
+ collapsed && "sidebar_collapsed",
647
+ isStatic && "sidebar_static",
648
+ className
649
+ ),
560
650
  style: { width: collapsed ? collapsedWidth : width, ...style },
561
651
  ...props,
562
652
  children: [
@@ -581,41 +671,31 @@ var Sidebar = ({
581
671
  }
582
672
  );
583
673
  };
584
- var SidebarItem = ({
585
- icon,
586
- active,
587
- trailing,
588
- as = "button",
589
- href,
590
- className,
591
- children,
592
- type,
593
- ...props
594
- }) => {
674
+ var SidebarItem = (props) => {
675
+ const { icon, active, trailing, as = "button", className, children, ...rest } = props;
595
676
  const classes = cn("sidebar_item", active && "sidebar_item_active", className);
596
677
  const ariaCurrent = active ? "page" : void 0;
597
- if (as === "a" && href) {
678
+ const inner = /* @__PURE__ */ jsxs(Fragment$1, { children: [
679
+ icon && /* @__PURE__ */ jsx("span", { className: "sidebar_item_icon", "aria-hidden": "true", children: icon }),
680
+ /* @__PURE__ */ jsx("span", { className: "sidebar_item_label", children }),
681
+ trailing && /* @__PURE__ */ jsx("span", { className: "sidebar_item_trailing", children: trailing })
682
+ ] });
683
+ if (as === "a") {
684
+ const { href, ...anchorRest } = rest;
598
685
  return (
599
686
  // biome-ignore lint/a11y/useValidAnchor: navigation link with optional active state
600
- /* @__PURE__ */ jsxs("a", { className: classes, href, "aria-current": ariaCurrent, ...props, children: [
601
- icon && /* @__PURE__ */ jsx("span", { className: "sidebar_item_icon", "aria-hidden": "true", children: icon }),
602
- /* @__PURE__ */ jsx("span", { className: "sidebar_item_label", children }),
603
- trailing && /* @__PURE__ */ jsx("span", { className: "sidebar_item_trailing", children: trailing })
604
- ] })
687
+ /* @__PURE__ */ jsx("a", { className: classes, href, "aria-current": ariaCurrent, ...anchorRest, children: inner })
605
688
  );
606
689
  }
607
- return /* @__PURE__ */ jsxs(
690
+ const { type, ...buttonRest } = rest;
691
+ return /* @__PURE__ */ jsx(
608
692
  "button",
609
693
  {
610
694
  type: type ?? "button",
611
695
  className: classes,
612
696
  "aria-current": ariaCurrent,
613
- ...props,
614
- children: [
615
- icon && /* @__PURE__ */ jsx("span", { className: "sidebar_item_icon", "aria-hidden": "true", children: icon }),
616
- /* @__PURE__ */ jsx("span", { className: "sidebar_item_label", children }),
617
- trailing && /* @__PURE__ */ jsx("span", { className: "sidebar_item_trailing", children: trailing })
618
- ]
697
+ ...buttonRest,
698
+ children: inner
619
699
  }
620
700
  );
621
701
  };
@@ -625,9 +705,9 @@ var SidebarSection = ({ label, className, children, ...props }) => {
625
705
  children
626
706
  ] });
627
707
  };
628
- var TabsContext = React16.createContext(null);
708
+ var TabsContext = React17.createContext(null);
629
709
  function useTabsContext() {
630
- const ctx = React16.useContext(TabsContext);
710
+ const ctx = React17.useContext(TabsContext);
631
711
  if (!ctx) {
632
712
  throw new Error(
633
713
  '[Bigtablet DS] <Tab>, <TabList>, <TabPanel>\uC740 \uBC18\uB4DC\uC2DC <Tabs> \uC548\uC5D0 \uC788\uC5B4\uC57C \uD569\uB2C8\uB2E4.\n\n\uC62C\uBC14\uB978 \uC0AC\uC6A9 \uC608:\n <Tabs defaultValue="a">\n <TabList>\n <Tab value="a">A</Tab>\n </TabList>\n <TabPanel value="a">...</TabPanel>\n </Tabs>'
@@ -646,17 +726,17 @@ var Tabs = ({
646
726
  ...props
647
727
  }) => {
648
728
  const isControlled = controlledValue !== void 0;
649
- const [internalValue, setInternalValue] = React16.useState(defaultValue);
729
+ const [internalValue, setInternalValue] = React17.useState(defaultValue);
650
730
  const value = isControlled ? controlledValue : internalValue;
651
- const setValue = React16.useCallback(
731
+ const setValue = React17.useCallback(
652
732
  (v) => {
653
733
  if (!isControlled) setInternalValue(v);
654
734
  onValueChange?.(v);
655
735
  },
656
736
  [isControlled, onValueChange]
657
737
  );
658
- const idPrefix = React16.useId();
659
- const ctx = React16.useMemo(
738
+ const idPrefix = React17.useId();
739
+ const ctx = React17.useMemo(
660
740
  () => ({ value, setValue, variant, size, idPrefix }),
661
741
  [value, setValue, variant, size, idPrefix]
662
742
  );
@@ -664,10 +744,10 @@ var Tabs = ({
664
744
  };
665
745
  var TabList = ({ ariaLabel, className, children, ...props }) => {
666
746
  const { variant } = useTabsContext();
667
- const listRef = React16.useRef(null);
668
- const [indicator, setIndicator] = React16.useState(null);
669
- const [hasMounted, setHasMounted] = React16.useState(false);
670
- React16.useLayoutEffect(() => {
747
+ const listRef = React17.useRef(null);
748
+ const [indicator, setIndicator] = React17.useState(null);
749
+ const [hasMounted, setHasMounted] = React17.useState(false);
750
+ React17.useLayoutEffect(() => {
671
751
  const list = listRef.current;
672
752
  if (!list) return;
673
753
  const update = () => {
@@ -802,18 +882,18 @@ var Tooltip = ({
802
882
  disabled = false,
803
883
  children
804
884
  }) => {
805
- const [open, setOpen] = React16.useState(false);
806
- const timerRef = React16.useRef(null);
807
- const tooltipId = React16.useId();
808
- const show = React16.useCallback(() => {
885
+ const [open, setOpen] = React17.useState(false);
886
+ const timerRef = React17.useRef(null);
887
+ const tooltipId = React17.useId();
888
+ const show = React17.useCallback(() => {
809
889
  if (timerRef.current) clearTimeout(timerRef.current);
810
890
  timerRef.current = setTimeout(() => setOpen(true), delay);
811
891
  }, [delay]);
812
- const hide = React16.useCallback(() => {
892
+ const hide = React17.useCallback(() => {
813
893
  if (timerRef.current) clearTimeout(timerRef.current);
814
894
  setOpen(false);
815
895
  }, []);
816
- React16.useEffect(() => {
896
+ React17.useEffect(() => {
817
897
  return () => {
818
898
  if (timerRef.current) clearTimeout(timerRef.current);
819
899
  };
@@ -831,7 +911,7 @@ var Tooltip = ({
831
911
  }
832
912
  })();
833
913
  const style = useSpringPresence({ visible: open, from: fromTransform });
834
- const trigger = React16.cloneElement(children, {
914
+ const trigger = React17.cloneElement(children, {
835
915
  onMouseEnter: show,
836
916
  onMouseLeave: hide,
837
917
  onFocus: show,
@@ -1425,12 +1505,12 @@ var AlertModal = ({
1425
1505
  onCancel,
1426
1506
  onClose
1427
1507
  }) => {
1428
- const panelRef = React16.useRef(null);
1429
- const titleId = React16.useId();
1430
- const messageId = React16.useId();
1508
+ const panelRef = React17.useRef(null);
1509
+ const titleId = React17.useId();
1510
+ const messageId = React17.useId();
1431
1511
  const [shouldRender, setShouldRender] = useState(isOpen);
1432
1512
  useFocusTrap(panelRef, isOpen);
1433
- React16.useEffect(() => {
1513
+ React17.useEffect(() => {
1434
1514
  if (isOpen) setShouldRender(true);
1435
1515
  }, [isOpen]);
1436
1516
  const overlayStyle = useSpring({
@@ -1533,12 +1613,12 @@ var Checkbox = ({
1533
1613
  ref,
1534
1614
  ...props
1535
1615
  }) => {
1536
- const inputRef = React16.useRef(null);
1537
- React16.useImperativeHandle(
1616
+ const inputRef = React17.useRef(null);
1617
+ React17.useImperativeHandle(
1538
1618
  ref,
1539
1619
  () => inputRef.current
1540
1620
  );
1541
- React16.useEffect(() => {
1621
+ React17.useEffect(() => {
1542
1622
  if (!inputRef.current) return;
1543
1623
  inputRef.current.indeterminate = Boolean(indeterminate);
1544
1624
  }, [indeterminate]);
@@ -1943,9 +2023,9 @@ var DatePicker = ({
1943
2023
  minDateSrFormat = "Minimum date: {date}",
1944
2024
  selectableRangeUntilTodaySrText = "Selectable up to today"
1945
2025
  }) => {
1946
- const groupId = React16.useId();
1947
- const constraintId = React16.useId();
1948
- const { todayYear, todayMonth, todayDay } = React16.useMemo(() => {
2026
+ const groupId = React17.useId();
2027
+ const constraintId = React17.useId();
2028
+ const { todayYear, todayMonth, todayDay } = React17.useMemo(() => {
1949
2029
  const now = /* @__PURE__ */ new Date();
1950
2030
  return {
1951
2031
  todayYear: now.getFullYear(),
@@ -1954,7 +2034,7 @@ var DatePicker = ({
1954
2034
  };
1955
2035
  }, []);
1956
2036
  const endYear = endYearProp ?? todayYear + 10;
1957
- const parsed = React16.useMemo(() => {
2037
+ const parsed = React17.useMemo(() => {
1958
2038
  if (!value) return { year: 0, month: 0, day: 0 };
1959
2039
  const [y, m, d] = value.split("-").map(Number);
1960
2040
  return {
@@ -1963,7 +2043,7 @@ var DatePicker = ({
1963
2043
  day: d || 0
1964
2044
  };
1965
2045
  }, [value]);
1966
- const min = React16.useMemo(() => {
2046
+ const min = React17.useMemo(() => {
1967
2047
  if (!minDate) return { year: 0, month: 0, day: 0 };
1968
2048
  const [y, m, d] = minDate.split("-").map(Number);
1969
2049
  return {
@@ -1983,7 +2063,7 @@ var DatePicker = ({
1983
2063
  min.year > 0 && min.month > 0 && year === min.year && month === min.month ? min.day : 1
1984
2064
  )
1985
2065
  );
1986
- const maxDay = React16.useMemo(() => {
2066
+ const maxDay = React17.useMemo(() => {
1987
2067
  if (!year || !month) return 31;
1988
2068
  const daysInMonth = getDaysInMonth(year, month);
1989
2069
  if (selectableRange === "until-today" && year === todayYear && month === todayMonth) {
@@ -1991,28 +2071,28 @@ var DatePicker = ({
1991
2071
  }
1992
2072
  return daysInMonth;
1993
2073
  }, [year, month, selectableRange, todayYear, todayMonth, todayDay]);
1994
- const yearOptions = React16.useMemo(
2074
+ const yearOptions = React17.useMemo(
1995
2075
  () => range(startYear, maxYear).map((y) => ({
1996
2076
  value: String(y),
1997
2077
  label: String(y)
1998
2078
  })),
1999
2079
  [startYear, maxYear]
2000
2080
  );
2001
- const monthOptions = React16.useMemo(
2081
+ const monthOptions = React17.useMemo(
2002
2082
  () => range(minMonth, Math.max(minMonth, maxMonth)).map((m) => ({
2003
2083
  value: String(m),
2004
2084
  label: pad(m)
2005
2085
  })),
2006
2086
  [minMonth, maxMonth]
2007
2087
  );
2008
- const dayOptions = React16.useMemo(
2088
+ const dayOptions = React17.useMemo(
2009
2089
  () => range(minDay, Math.max(minDay, maxDay)).map((d) => ({
2010
2090
  value: String(d),
2011
2091
  label: pad(d)
2012
2092
  })),
2013
2093
  [minDay, maxDay]
2014
2094
  );
2015
- const emit = React16.useCallback(
2095
+ const emit = React17.useCallback(
2016
2096
  (yy, mm, dd) => {
2017
2097
  if (mode === "year-month") {
2018
2098
  onChange(`${yy}-${pad(mm)}`);
@@ -2023,7 +2103,7 @@ var DatePicker = ({
2023
2103
  },
2024
2104
  [mode, onChange]
2025
2105
  );
2026
- const handleYearChange = React16.useCallback(
2106
+ const handleYearChange = React17.useCallback(
2027
2107
  (raw) => {
2028
2108
  if (!raw) return;
2029
2109
  const newYear = Number(raw);
@@ -2040,14 +2120,14 @@ var DatePicker = ({
2040
2120
  },
2041
2121
  [month, day, min.year, min.month, selectableRange, todayYear, todayMonth, emit]
2042
2122
  );
2043
- const handleMonthChange = React16.useCallback(
2123
+ const handleMonthChange = React17.useCallback(
2044
2124
  (raw) => {
2045
2125
  if (!raw || !year) return;
2046
2126
  emit(year, Number(raw), day || void 0);
2047
2127
  },
2048
2128
  [year, day, emit]
2049
2129
  );
2050
- const handleDayChange = React16.useCallback(
2130
+ const handleDayChange = React17.useCallback(
2051
2131
  (raw) => {
2052
2132
  if (!raw || !year || !month) return;
2053
2133
  emit(year, month, Number(raw));
@@ -2135,10 +2215,10 @@ var FileInput = ({
2135
2215
  onChange,
2136
2216
  ...props
2137
2217
  }) => {
2138
- const inputId = React16.useId();
2139
- const helperId = React16.useId();
2140
- const inputRef = React16.useRef(null);
2141
- const [previewUrls, setPreviewUrls] = React16.useState([]);
2218
+ const inputId = React17.useId();
2219
+ const helperId = React17.useId();
2220
+ const inputRef = React17.useRef(null);
2221
+ const [previewUrls, setPreviewUrls] = React17.useState([]);
2142
2222
  const isPreviewVariant = variant === "preview";
2143
2223
  const showPreview = isPreviewVariant || preview;
2144
2224
  const handleChange = (e) => {
@@ -2176,7 +2256,7 @@ var FileInput = ({
2176
2256
  }
2177
2257
  onFiles?.(null);
2178
2258
  };
2179
- React16.useEffect(() => {
2259
+ React17.useEffect(() => {
2180
2260
  return () => {
2181
2261
  for (const url of previewUrls) {
2182
2262
  URL.revokeObjectURL(url);
@@ -2473,11 +2553,11 @@ var Modal = ({
2473
2553
  ariaLabel,
2474
2554
  ...props
2475
2555
  }) => {
2476
- const panelRef = React16.useRef(null);
2477
- const titleId = React16.useId();
2478
- const [shouldRender, setShouldRender] = React16.useState(open);
2556
+ const panelRef = React17.useRef(null);
2557
+ const titleId = React17.useId();
2558
+ const [shouldRender, setShouldRender] = React17.useState(open);
2479
2559
  useFocusTrap(panelRef, open);
2480
- React16.useEffect(() => {
2560
+ React17.useEffect(() => {
2481
2561
  if (open) setShouldRender(true);
2482
2562
  }, [open]);
2483
2563
  const overlayStyle = useSpring({
@@ -2492,15 +2572,15 @@ var Modal = ({
2492
2572
  transform: open ? "scale(1) translateY(0px)" : "scale(0.96) translateY(-4px)",
2493
2573
  config: { tension: 280, friction: 28, clamp: !open }
2494
2574
  });
2495
- const handleEscape = React16.useEffectEvent((e) => {
2575
+ const handleEscape = React17.useEffectEvent((e) => {
2496
2576
  if (e.key === "Escape") onClose?.();
2497
2577
  });
2498
- React16.useEffect(() => {
2578
+ React17.useEffect(() => {
2499
2579
  if (!open) return;
2500
2580
  document.addEventListener("keydown", handleEscape);
2501
2581
  return () => document.removeEventListener("keydown", handleEscape);
2502
2582
  }, [open]);
2503
- React16.useEffect(() => {
2583
+ React17.useEffect(() => {
2504
2584
  if (!open) return;
2505
2585
  const body = document.body;
2506
2586
  const openModals = parseInt(body.dataset.openModals || "0", 10);
@@ -2580,12 +2660,12 @@ var OtpInput = ({
2580
2660
  ariaLabel = "OTP \uC785\uB825",
2581
2661
  className
2582
2662
  }) => {
2583
- const inputsRef = React16.useRef([]);
2584
- const isTypingRef = React16.useRef(false);
2585
- React16.useEffect(() => {
2663
+ const inputsRef = React17.useRef([]);
2664
+ const isTypingRef = React17.useRef(false);
2665
+ React17.useEffect(() => {
2586
2666
  if (autoFocus) inputsRef.current[0]?.focus();
2587
2667
  }, [autoFocus]);
2588
- const digits = React16.useMemo(() => {
2668
+ const digits = React17.useMemo(() => {
2589
2669
  const chars = value.split("").slice(0, length);
2590
2670
  while (chars.length < length) chars.push("");
2591
2671
  return chars;
@@ -2724,7 +2804,7 @@ var Pagination = ({
2724
2804
  }) => {
2725
2805
  const prevDisabled = page <= 1;
2726
2806
  const nextDisabled = page >= totalPages;
2727
- const items = React16.useMemo(() => getPaginationItems(page, totalPages), [page, totalPages]);
2807
+ const items = React17.useMemo(() => getPaginationItems(page, totalPages), [page, totalPages]);
2728
2808
  return /* @__PURE__ */ jsxs("nav", { className: "pagination", "aria-label": "Pagination", children: [
2729
2809
  /* @__PURE__ */ jsx(
2730
2810
  "button",
@@ -2906,9 +2986,9 @@ var Table = ({
2906
2986
  isEmpty && /* @__PURE__ */ jsx("div", { className: "table_empty", role: "status", children: emptyMessage })
2907
2987
  ] });
2908
2988
  };
2909
- var ThemeContext = React16.createContext(null);
2989
+ var ThemeContext = React17.createContext(null);
2910
2990
  var useTheme = () => {
2911
- const ctx = React16.useContext(ThemeContext);
2991
+ const ctx = React17.useContext(ThemeContext);
2912
2992
  if (!ctx) {
2913
2993
  throw new Error(
2914
2994
  '[Bigtablet DS] useTheme\uB294 <ThemeProvider> \uC548\uC5D0\uC11C\uB9CC \uC0AC\uC6A9 \uAC00\uB2A5\uD569\uB2C8\uB2E4.\n\n\uC571 \uCD5C\uC0C1\uB2E8\uC5D0 <ThemeProvider>\uB85C \uAC10\uC2F8\uC8FC\uC138\uC694:\n <ThemeProvider mode="system">\n <YourApp />\n </ThemeProvider>'
@@ -2932,14 +3012,14 @@ var ThemeProvider = ({
2932
3012
  targetSelector,
2933
3013
  children
2934
3014
  }) => {
2935
- const [mode, setModeState] = React16.useState(() => {
3015
+ const [mode, setModeState] = React17.useState(() => {
2936
3016
  return readStored(storageKey) ?? defaultMode;
2937
3017
  });
2938
- const [systemDark, setSystemDark] = React16.useState(() => {
3018
+ const [systemDark, setSystemDark] = React17.useState(() => {
2939
3019
  if (!isClient) return false;
2940
3020
  return window.matchMedia("(prefers-color-scheme: dark)").matches;
2941
3021
  });
2942
- React16.useEffect(() => {
3022
+ React17.useEffect(() => {
2943
3023
  if (!isClient) return;
2944
3024
  const mq = window.matchMedia("(prefers-color-scheme: dark)");
2945
3025
  const handler = (e) => setSystemDark(e.matches);
@@ -2947,7 +3027,7 @@ var ThemeProvider = ({
2947
3027
  return () => mq.removeEventListener("change", handler);
2948
3028
  }, []);
2949
3029
  const resolved = mode === "system" ? systemDark ? "dark" : "light" : mode;
2950
- React16.useEffect(() => {
3030
+ React17.useEffect(() => {
2951
3031
  if (!isClient) return;
2952
3032
  const target = targetSelector ? document.querySelector(targetSelector) : document.documentElement;
2953
3033
  if (!target) return;
@@ -2957,7 +3037,7 @@ var ThemeProvider = ({
2957
3037
  target.setAttribute("data-theme", mode);
2958
3038
  }
2959
3039
  }, [mode, targetSelector]);
2960
- const setMode = React16.useCallback(
3040
+ const setMode = React17.useCallback(
2961
3041
  (next) => {
2962
3042
  setModeState(next);
2963
3043
  if (storageKey && isClient) {
@@ -2969,7 +3049,7 @@ var ThemeProvider = ({
2969
3049
  },
2970
3050
  [storageKey]
2971
3051
  );
2972
- const value = React16.useMemo(
3052
+ const value = React17.useMemo(
2973
3053
  () => ({ mode, resolved, setMode }),
2974
3054
  [mode, resolved, setMode]
2975
3055
  );
@@ -3075,7 +3155,7 @@ var TextField = ({
3075
3155
  ] });
3076
3156
  };
3077
3157
  TextField.displayName = "TextField";
3078
- var ToastContext = React16.createContext(null);
3158
+ var ToastContext = React17.createContext(null);
3079
3159
  var VARIANT_ICONS = {
3080
3160
  success: /* @__PURE__ */ jsx(CheckCircle2, { size: 18 }),
3081
3161
  error: /* @__PURE__ */ jsx(XCircle, { size: 18 }),
@@ -3084,15 +3164,15 @@ var VARIANT_ICONS = {
3084
3164
  default: /* @__PURE__ */ jsx(Bell, { size: 18 })
3085
3165
  };
3086
3166
  var ToastItemComponent = ({ item, onRemove, closeAriaLabel }) => {
3087
- const [visible, setVisible] = React16.useState(true);
3088
- const closingRef = React16.useRef(false);
3167
+ const [visible, setVisible] = React17.useState(true);
3168
+ const closingRef = React17.useRef(false);
3089
3169
  const style = useSpringPresence({
3090
3170
  visible,
3091
3171
  from: "translateX(20px)",
3092
3172
  // 우측에서 슬라이드 인
3093
3173
  onExitComplete: () => onRemove(item.id)
3094
3174
  });
3095
- const close = React16.useCallback(() => {
3175
+ const close = React17.useCallback(() => {
3096
3176
  if (closingRef.current) return;
3097
3177
  closingRef.current = true;
3098
3178
  setVisible(false);
@@ -3125,19 +3205,19 @@ var ToastProvider = ({
3125
3205
  maxCount = 5,
3126
3206
  closeAriaLabel = "Close"
3127
3207
  }) => {
3128
- const [toasts, setToasts] = React16.useState([]);
3129
- const [isMounted, setIsMounted] = React16.useState(false);
3130
- React16.useEffect(() => {
3208
+ const [toasts, setToasts] = React17.useState([]);
3209
+ const [isMounted, setIsMounted] = React17.useState(false);
3210
+ React17.useEffect(() => {
3131
3211
  setIsMounted(true);
3132
3212
  }, []);
3133
- const addToast = React16.useCallback(
3213
+ const addToast = React17.useCallback(
3134
3214
  (message, variant, duration = 3e3) => {
3135
3215
  const id = crypto.randomUUID();
3136
3216
  setToasts((prev) => [{ id, message, variant, duration }, ...prev].slice(0, maxCount));
3137
3217
  },
3138
3218
  [maxCount]
3139
3219
  );
3140
- const removeToast = React16.useCallback((id) => {
3220
+ const removeToast = React17.useCallback((id) => {
3141
3221
  setToasts((prev) => prev.filter((t) => t.id !== id));
3142
3222
  }, []);
3143
3223
  return /* @__PURE__ */ jsxs(ToastContext.Provider, { value: { addToast }, children: [
@@ -3199,7 +3279,7 @@ var Toggle = ({
3199
3279
  ...props
3200
3280
  }) => {
3201
3281
  const isControlled = checked !== void 0;
3202
- const [innerChecked, setInnerChecked] = React16.useState(!!defaultChecked);
3282
+ const [innerChecked, setInnerChecked] = React17.useState(!!defaultChecked);
3203
3283
  const isOn = isControlled ? !!checked : innerChecked;
3204
3284
  const handleToggle = () => {
3205
3285
  if (disabled) return;
@@ -3361,4 +3441,4 @@ var Grid = ({
3361
3441
  );
3362
3442
  };
3363
3443
 
3364
- export { Accordion, AlertProvider, Avatar, Badge, Breadcrumb, Button, Card, Checkbox, Chip, Container, DatePicker, Divider, Dropdown, EmptyState, FileInput, Grid, Hero, Icon, IconButton, LinearProgress, ListItem, MediaCard, Menu, Modal, NavBar, NavLink, OtpInput, Pagination, Radio, Section, Sidebar, SidebarItem, SidebarSection, Skeleton, Spinner, Stack, Tab, TabList, TabPanel, Table, Tabs, TextField, ThemeProvider, ToastProvider, Toggle, Tooltip, TopLoading, a11y, baseBorderWidth, baseColors, baseTypography, borderWidth, breakpoints, cn, colors, elevation, motion, opacity, radius, skeleton, spacing, typography, useAlert, useFocusTrap, useSpringHover, useSpringPresence, useTheme, useToast, zIndex };
3444
+ export { Accordion, AlertProvider, Avatar, Badge, BottomNav, BottomNavItem, BottomNavSpacer, Breadcrumb, Button, Card, Checkbox, Chip, Container, DatePicker, Divider, Dropdown, EmptyState, FileInput, Grid, Hero, Icon, IconButton, LinearProgress, ListItem, MediaCard, Menu, Modal, NavBar, NavLink, OtpInput, Pagination, Radio, Section, Sidebar, SidebarItem, SidebarSection, Skeleton, Spinner, Stack, Tab, TabList, TabPanel, Table, Tabs, TextField, ThemeProvider, ToastProvider, Toggle, Tooltip, TopLoading, a11y, baseBorderWidth, baseColors, baseTypography, borderWidth, breakpoints, cn, colors, elevation, motion, opacity, radius, skeleton, spacing, typography, useAlert, useFocusTrap, useSpringHover, useSpringPresence, useTheme, useToast, zIndex };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bigtablet/design-system",
3
- "version": "3.0.0",
3
+ "version": "3.1.1",
4
4
  "description": "Bigtablet Design System UI Components",
5
5
  "type": "module",
6
6
  "types": "dist/index.d.ts",