@bigtablet/design-system 2.2.4 → 2.4.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.css CHANGED
@@ -625,7 +625,7 @@
625
625
  cursor: not-allowed;
626
626
  }
627
627
 
628
- /* src/ui/select/style.scss */
628
+ /* src/ui/dropdown/style.scss */
629
629
  @keyframes skeleton_loading {
630
630
  0% {
631
631
  background-position: 100% 0;
@@ -634,164 +634,125 @@
634
634
  background-position: 0 0;
635
635
  }
636
636
  }
637
- .select {
637
+ .dropdown {
638
638
  position: relative;
639
639
  display: inline-flex;
640
640
  flex-direction: column;
641
- width: 100%;
642
641
  font-family: "Pretendard", sans-serif;
643
642
  }
644
- .select_fieldset {
645
- padding: 0;
646
- margin: 0;
647
- min-inline-size: 0;
648
- border-radius: 12px;
649
- background-color: #FFFFFF;
650
- transition: border-color 0.2s ease-in-out, background-color 0.2s ease-in-out;
651
- }
652
- .select_variant_outline {
653
- border: 1px solid #E5E5E5;
654
- }
655
- .select_variant_outline:hover:not(.is_disabled) {
656
- border-color: #B3B3B3;
657
- }
658
- .select_variant_outline.is_open {
659
- border-color: #121212;
643
+ .dropdown_size_sm .dropdown_fieldset {
644
+ min-height: 40px;
660
645
  }
661
- .select_variant_filled {
662
- background: #F4F4F4;
663
- border: 1px solid transparent;
646
+ .dropdown_size_md .dropdown_fieldset,
647
+ .dropdown_size_lg .dropdown_fieldset {
648
+ min-height: 56px;
664
649
  }
665
- .select_variant_filled:hover:not(.is_disabled) {
666
- background: rgb(251.7, 251.7, 251.7);
650
+ .dropdown_size_sm .dropdown_control {
651
+ padding: 8px 16px;
667
652
  }
668
- .select_variant_filled.is_open {
669
- background: #FFFFFF;
670
- border-color: #121212;
653
+ .dropdown_size_sm .dropdown_value,
654
+ .dropdown_size_sm .dropdown_placeholder {
655
+ font-size: 12px;
656
+ font-weight: 400;
657
+ line-height: 16px;
658
+ letter-spacing: 0px;
671
659
  }
672
- .select_variant_ghost {
673
- background: transparent;
674
- border: 1px solid transparent;
660
+ .dropdown_fieldset {
661
+ position: relative;
662
+ border: 1px solid #E5E5E5;
663
+ border-radius: 12px;
664
+ background-color: #FFFFFF;
665
+ transition: border-color 0.2s ease-in-out, background-color 0.2s ease-in-out;
675
666
  }
676
- .select_variant_ghost:hover:not(.is_disabled) {
677
- background: rgba(0, 0, 0, 0.05);
667
+ .dropdown_fieldset:hover:not(.is_open):not(.is_disabled) {
668
+ border-color: #B3B3B3;
678
669
  }
679
- .select_variant_ghost.is_open {
680
- background: #FFFFFF;
670
+ .dropdown_fieldset.is_open {
681
671
  border-color: #121212;
682
672
  }
683
- .select_fieldset.is_disabled {
673
+ .dropdown_fieldset.is_disabled {
684
674
  background-color: #F4F4F4;
675
+ border-color: rgba(26, 26, 26, 0.12);
685
676
  }
686
- .select_inner {
687
- display: flex;
688
- align-items: stretch;
689
- padding: 4px 0;
690
- }
691
- .select_size_sm .select_inner {
692
- min-height: 40px;
693
- }
694
- .select_size_md .select_inner {
695
- min-height: 52px;
696
- }
697
- .select_size_lg .select_inner {
698
- min-height: 52px;
699
- }
700
- .select_label {
701
- margin-inline-start: 12px;
677
+ .dropdown_label {
678
+ position: absolute;
679
+ top: -8px;
680
+ left: 12px;
702
681
  padding: 0 4px;
703
- color: #121212;
704
- transition: color 0.2s ease-in-out;
705
- }
706
- .select_label > label {
707
- display: block;
708
- font-family: "Pretendard", sans-serif;
682
+ background: var(--dropdown-surface, #FFFFFF);
709
683
  font-size: 12px;
710
684
  font-weight: 400;
711
685
  line-height: 16px;
686
+ letter-spacing: 0px;
712
687
  letter-spacing: 0.32px;
713
- color: inherit;
688
+ color: #121212;
689
+ white-space: nowrap;
714
690
  pointer-events: none;
715
- cursor: default;
691
+ opacity: 0;
692
+ transition: opacity 0.2s ease-in-out, color 0.2s ease-in-out;
693
+ }
694
+ .dropdown_label.is_visible {
695
+ opacity: 1;
716
696
  }
717
- .select_fieldset.is_disabled .select_label {
697
+ .dropdown_fieldset.is_disabled .dropdown_label {
718
698
  color: rgba(26, 26, 26, 0.38);
719
699
  }
720
- .select_control {
721
- flex: 1;
700
+ .dropdown_control {
701
+ width: 100%;
722
702
  display: inline-flex;
723
703
  align-items: center;
724
- justify-content: space-between;
725
- gap: 8px;
704
+ gap: 4px;
705
+ padding: 12px 16px;
726
706
  cursor: pointer;
727
707
  background: transparent;
728
708
  border: none;
729
709
  outline: none;
730
710
  color: #121212;
731
711
  border-radius: 12px;
712
+ text-align: start;
732
713
  }
733
- .select_control:focus-visible {
714
+ .dropdown_control:focus-visible {
734
715
  box-shadow: 0 0 0 3px rgba(18, 18, 18, 0.15);
735
716
  }
736
- .select_control:disabled,
737
- .select_control.is_disabled {
717
+ .dropdown_control:disabled,
718
+ .dropdown_control.is_disabled {
738
719
  cursor: not-allowed;
739
720
  opacity: 0.38;
740
721
  color: #888888;
741
722
  }
742
- .select_size_sm .select_control {
743
- padding: 8px 16px;
744
- font-size: 12px;
745
- font-weight: 400;
746
- line-height: 16px;
747
- letter-spacing: 0px;
748
- }
749
- .select_size_md .select_control {
750
- padding: 12px 20px;
751
- font-size: 15px;
752
- font-weight: 400;
753
- line-height: 22.5px;
754
- letter-spacing: 0px;
755
- }
756
- .select_size_lg .select_control {
757
- padding: 16px 24px;
758
- font-size: 16px;
759
- font-weight: 400;
760
- line-height: 24px;
761
- letter-spacing: 0px;
762
- }
763
- .select_value {
723
+ .dropdown_value {
764
724
  flex: 1 1 auto;
765
725
  min-width: 0;
766
726
  overflow: hidden;
767
727
  text-overflow: ellipsis;
768
728
  white-space: nowrap;
769
729
  text-align: start;
770
- font-size: 15px;
730
+ font-size: 14px;
771
731
  font-weight: 400;
772
- line-height: 22.5px;
732
+ line-height: 20px;
773
733
  letter-spacing: 0px;
774
734
  }
775
- .select_placeholder {
735
+ .dropdown_placeholder {
776
736
  flex: 1 1 auto;
777
737
  min-width: 0;
778
738
  overflow: hidden;
779
739
  text-overflow: ellipsis;
780
740
  white-space: nowrap;
781
741
  text-align: start;
782
- font-size: 15px;
742
+ font-size: 14px;
783
743
  font-weight: 400;
784
- line-height: 22.5px;
744
+ line-height: 20px;
785
745
  letter-spacing: 0px;
786
- color: #888888;
746
+ color: #666666;
787
747
  }
788
- .select_icon {
748
+ .dropdown_icon {
789
749
  display: inline-flex;
790
750
  align-items: center;
791
751
  justify-content: center;
752
+ flex-shrink: 0;
792
753
  color: #666666;
793
754
  }
794
- .select_list {
755
+ .dropdown_list {
795
756
  position: absolute;
796
757
  z-index: 1000;
797
758
  top: 100%;
@@ -802,51 +763,88 @@
802
763
  margin-top: 4px;
803
764
  background: #FFFFFF;
804
765
  border: 1px solid #E5E5E5;
805
- border-radius: 8px;
766
+ border-radius: 4px;
806
767
  box-shadow: 0 2px 2px -2px rgba(0, 0, 0, 0.2), 0 6px 6px 0px rgba(0, 0, 0, 0.12);
807
768
  max-height: 18rem;
808
769
  overflow-y: auto;
809
770
  overflow-x: hidden;
810
771
  padding: 4px 0;
811
772
  }
812
- .select_list_up {
773
+ .dropdown_list_up {
813
774
  top: auto;
814
775
  bottom: 100%;
815
776
  margin-top: 0;
816
777
  margin-bottom: 4px;
817
778
  }
818
- .select_option {
779
+ .dropdown_option {
819
780
  width: 100%;
820
781
  box-sizing: border-box;
821
- display: grid;
822
- grid-template-columns: 1fr auto;
782
+ display: flex;
823
783
  align-items: center;
824
- gap: 8px;
784
+ gap: 12px;
825
785
  padding: 8px 16px;
786
+ min-height: 56px;
826
787
  cursor: pointer;
827
- color: #121212;
828
788
  background: transparent;
829
- font-size: 15px;
830
- font-weight: 400;
831
- line-height: 22.5px;
832
- letter-spacing: 0px;
789
+ color: #121212;
790
+ }
791
+ .dropdown_option:hover {
792
+ background: rgba(0, 0, 0, 0.05);
793
+ }
794
+ .dropdown_option.is_active {
795
+ background: rgba(0, 0, 0, 0.08);
796
+ }
797
+ .dropdown_option.is_disabled {
798
+ cursor: not-allowed;
799
+ color: #888888;
833
800
  }
834
- .select_option > span:first-child {
801
+ .dropdown_option_icon {
802
+ display: inline-flex;
803
+ align-items: center;
804
+ justify-content: center;
805
+ flex-shrink: 0;
806
+ color: #666666;
807
+ }
808
+ .dropdown_option_content {
809
+ flex: 1;
835
810
  min-width: 0;
811
+ display: flex;
812
+ flex-direction: column;
813
+ }
814
+ .dropdown_option_label {
836
815
  overflow: hidden;
837
816
  text-overflow: ellipsis;
838
817
  white-space: nowrap;
818
+ font-size: 14px;
819
+ font-weight: 500;
820
+ line-height: 20px;
821
+ letter-spacing: 0px;
839
822
  }
840
- .select_option:hover,
841
- .select_option.is_active {
842
- background: #F4F4F4;
843
- }
844
- .select_option.is_selected {
823
+ .is_selected .dropdown_option_label {
845
824
  font-weight: 500;
846
825
  }
847
- .select_option.is_disabled {
848
- cursor: not-allowed;
849
- color: #888888;
826
+ .dropdown_option_supporting {
827
+ overflow: hidden;
828
+ text-overflow: ellipsis;
829
+ white-space: nowrap;
830
+ font-size: 14px;
831
+ font-weight: 400;
832
+ line-height: 20px;
833
+ letter-spacing: 0px;
834
+ color: #666666;
835
+ }
836
+ .dropdown_option_trailing {
837
+ display: inline-flex;
838
+ align-items: center;
839
+ justify-content: center;
840
+ flex-shrink: 0;
841
+ color: #666666;
842
+ }
843
+ .dropdown_option_divider {
844
+ height: 1px;
845
+ background: #E5E5E5;
846
+ border: none;
847
+ margin: 0;
850
848
  }
851
849
 
852
850
  /* src/ui/date-picker/style.scss */
package/dist/index.d.ts CHANGED
@@ -36,8 +36,9 @@ declare const baseColors: {
36
36
  readonly neutral300: "#999999";
37
37
  readonly neutral400: "#B3B3B3";
38
38
  readonly neutral500: "#888888";
39
- readonly neutral600: "#6B6B6B";
39
+ readonly neutral600: "#777777";
40
40
  readonly neutral700: "#666666";
41
+ readonly neutral800: "#333333";
41
42
  readonly neutral900: "#121212";
42
43
  readonly statusError: "#EF4444";
43
44
  readonly statusSuccess: "#10B981";
@@ -71,6 +72,7 @@ declare const colors: {
71
72
  readonly additive: "rgba(0, 0, 0, 0.05)";
72
73
  readonly disabled: "rgba(26, 26, 26, 0.12)";
73
74
  readonly overlay: "rgba(0, 0, 0, 0.50)";
75
+ readonly inverseSurface: "#333333";
74
76
  };
75
77
  readonly state: {
76
78
  readonly hoverOnLight: "rgba(0, 0, 0, 0.05)";
@@ -609,6 +611,60 @@ interface DatePickerProps {
609
611
  */
610
612
  declare const DatePicker: ({ label, value, onChange, mode, startYear, endYear: endYearProp, minDate, selectableRange, disabled, fullWidth, width, yearLabel, monthLabel, dayLabel, minDateSrFormat, selectableRangeUntilTodaySrText, }: DatePickerProps) => react_jsx_runtime.JSX.Element;
611
613
 
614
+ type DropdownSize = "sm" | "md" | "lg";
615
+ interface DropdownOption {
616
+ value: string;
617
+ label: string;
618
+ disabled?: boolean;
619
+ /** 옵션 아래에 표시할 보조 텍스트 */
620
+ supportingText?: string;
621
+ /** 옵션 왼쪽 아이콘 */
622
+ leadingIcon?: React$1.ReactNode;
623
+ /** 옵션 오른쪽 아이콘 (미지정 시 선택 상태에 체크 아이콘 자동 표시) */
624
+ trailingIcon?: React$1.ReactNode;
625
+ /** 아이템 아래 구분선 표시 여부 */
626
+ showDivider?: boolean;
627
+ }
628
+ interface DropdownProps {
629
+ /** 드롭다운 요소의 id (미입력 시 자동 생성) */
630
+ id?: string;
631
+ /** 드롭다운 위에 표시할 플로팅 라벨 텍스트 */
632
+ label?: string;
633
+ /** 선택 전 표시할 플레이스홀더 (기본값: "Select…") */
634
+ placeholder?: string;
635
+ /** 표시할 옵션 목록 */
636
+ options: DropdownOption[];
637
+ /** 제어형 선택 값 */
638
+ value?: string | null;
639
+ /** 값 변경 시 호출되는 콜백. 선택된 값과 전체 옵션 객체를 인자로 전달합니다. */
640
+ onChange?: (value: string | null, option?: DropdownOption | null) => void;
641
+ /** 비제어형 초기 선택 값 */
642
+ defaultValue?: string | null;
643
+ /** 비활성화 여부 */
644
+ disabled?: boolean;
645
+ /** 드롭다운 크기 (기본값: "md") */
646
+ size?: DropdownSize;
647
+ /** 컨테이너 전체 너비 차지 여부 */
648
+ fullWidth?: boolean;
649
+ /** 루트 요소에 추가할 className */
650
+ className?: string;
651
+ /**
652
+ * @deprecated variant는 더 이상 지원되지 않습니다. Dropdown은 outline 스타일만 사용합니다.
653
+ */
654
+ variant?: "outline" | "filled" | "ghost";
655
+ /**
656
+ * @deprecated textAlign은 더 이상 지원되지 않습니다.
657
+ */
658
+ textAlign?: "left" | "center";
659
+ }
660
+ /**
661
+ * 드롭다운 컴포넌트를 렌더링한다.
662
+ * 제어형/비제어형 상태를 지원하며, 키보드/마우스 상호작용과 플로팅 라벨을 관리한다.
663
+ * @param props 드롭다운 속성
664
+ * @returns 렌더링된 드롭다운 UI
665
+ */
666
+ declare const Dropdown: ({ id, label, placeholder, options, value, onChange, defaultValue, disabled, size, fullWidth, className, }: DropdownProps) => react_jsx_runtime.JSX.Element;
667
+
612
668
  interface DividerProps extends React$1.HTMLAttributes<HTMLHRElement> {
613
669
  /** 구분선 두께 (기본값: "standard") */
614
670
  weight?: "standard" | "heavy";
@@ -840,49 +896,6 @@ declare const Radio: {
840
896
  displayName: string;
841
897
  };
842
898
 
843
- type SelectSize = "sm" | "md" | "lg";
844
- type SelectVariant = "outline" | "filled" | "ghost";
845
- interface SelectOption {
846
- value: string;
847
- label: string;
848
- disabled?: boolean;
849
- }
850
- interface SelectProps {
851
- /** 셀렉트 요소의 id (미입력 시 자동 생성) */
852
- id?: string;
853
- /** 셀렉트 위에 표시할 라벨 텍스트 */
854
- label?: string;
855
- /** 선택 전 표시할 플레이스홀더 (기본값: "Select…") */
856
- placeholder?: string;
857
- /** 표시할 옵션 목록 */
858
- options: SelectOption[];
859
- /** 제어형 선택 값 */
860
- value?: string | null;
861
- /** 값 변경 시 호출되는 콜백. 선택된 값과 전체 옵션 객체를 인자로 전달합니다. */
862
- onChange?: (value: string | null, option?: SelectOption | null) => void;
863
- /** 비제어형 초기 선택 값 */
864
- defaultValue?: string | null;
865
- /** 비활성화 여부 */
866
- disabled?: boolean;
867
- /** 셀렉트 크기 (기본값: "md") */
868
- size?: SelectSize;
869
- /** 셀렉트 스타일 변형 (기본값: "outline") */
870
- variant?: SelectVariant;
871
- /** 컨테이너 전체 너비 차지 여부 */
872
- fullWidth?: boolean;
873
- /** 루트 요소에 추가할 className */
874
- className?: string;
875
- /** 선택된 값 텍스트 정렬 (기본값: "left") */
876
- textAlign?: "left" | "center";
877
- }
878
- /**
879
- * 셀렉트 컴포넌트를 렌더링한다.
880
- * 제어형/비제어형 상태를 정리하고, 키보드/마우스 상호작용과 드롭다운 표시를 관리한다.
881
- * @param props 셀렉트 속성
882
- * @returns 렌더링된 셀렉트 UI
883
- */
884
- declare const Select: ({ id, label, placeholder, options, value, onChange, defaultValue, disabled, size, variant, fullWidth, className, textAlign, }: SelectProps) => react_jsx_runtime.JSX.Element;
885
-
886
899
  interface SpinnerProps {
887
900
  /** 스피너 크기(px) (기본값: 24) */
888
901
  size?: number;
@@ -1021,4 +1034,4 @@ interface TopLoadingProps {
1021
1034
  */
1022
1035
  declare const TopLoading: ({ progress, color, height, isLoading, ariaLabel, }: TopLoadingProps) => react_jsx_runtime.JSX.Element | null;
1023
1036
 
1024
- export { AlertProvider, Button, type ButtonProps, Card, type CardProps, Checkbox, type CheckboxProps, Chip, type ChipProps, type ChipType, DatePicker, type DatePickerProps, Divider, type DividerProps, FAB, type FABProps, type FABVariant, FileInput, type FileInputProps, Icon, IconButton, type IconButtonProps, type IconButtonSize, type IconButtonVariant, type IconName, type IconProps, type IconWeight, LinearProgress, type LinearProgressProps, ListItem, type ListItemProps, Modal, type ModalProps, OtpInput, type OtpInputProps, Pagination, type PaginationProps, Radio, type RadioProps, Select, type SelectOption, type SelectProps, Spinner, type SpinnerProps, TextField, type TextFieldProps, ToastProvider, Toggle, type ToggleProps, TopLoading, type TopLoadingProps, a11y, baseBorderWidth, baseColors, baseTypography, borderWidth, breakpoints, colors, elevation, motion, opacity, radius, skeleton, spacing, typography, useAlert, useToast, zIndex };
1037
+ export { AlertProvider, Button, type ButtonProps, Card, type CardProps, Checkbox, type CheckboxProps, Chip, type ChipProps, type ChipType, DatePicker, type DatePickerProps, Divider, type DividerProps, Dropdown, type DropdownOption, type DropdownProps, type DropdownSize, FAB, type FABProps, type FABVariant, FileInput, type FileInputProps, Icon, IconButton, type IconButtonProps, type IconButtonSize, type IconButtonVariant, type IconName, type IconProps, type IconWeight, LinearProgress, type LinearProgressProps, ListItem, type ListItemProps, Modal, type ModalProps, OtpInput, type OtpInputProps, Pagination, type PaginationProps, Radio, type RadioProps, Dropdown as Select, type DropdownOption as SelectOption, type DropdownProps as SelectProps, Spinner, type SpinnerProps, TextField, type TextFieldProps, ToastProvider, Toggle, type ToggleProps, TopLoading, type TopLoadingProps, a11y, baseBorderWidth, baseColors, baseTypography, borderWidth, breakpoints, colors, elevation, motion, opacity, radius, skeleton, spacing, typography, useAlert, useToast, zIndex };
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import * as React5 from 'react';
4
4
  import { createContext, useContext, useState, useCallback } from 'react';
5
5
  import { createPortal } from 'react-dom';
6
6
  import { jsxs, jsx } from 'react/jsx-runtime';
7
- import { ChevronDown, Check, X, Bell, Info, AlertTriangle, XCircle, CheckCircle2 } from 'lucide-react';
7
+ import { X, ChevronDown, Check, Bell, Info, AlertTriangle, XCircle, CheckCircle2 } from 'lucide-react';
8
8
 
9
9
  // src/styles/a11y/index.ts
10
10
  var a11y = {
@@ -48,8 +48,9 @@ var baseColors = {
48
48
  neutral300: "#999999",
49
49
  neutral400: "#B3B3B3",
50
50
  neutral500: "#888888",
51
- neutral600: "#6B6B6B",
51
+ neutral600: "#777777",
52
52
  neutral700: "#666666",
53
+ neutral800: "#333333",
53
54
  neutral900: "#121212",
54
55
  statusError: "#EF4444",
55
56
  statusSuccess: "#10B981",
@@ -82,7 +83,8 @@ var colors = {
82
83
  solidDim: baseColors.neutral50,
83
84
  additive: baseColors.alphaBlack5,
84
85
  disabled: baseColors.alphaBlack12,
85
- overlay: baseColors.alphaBlack50
86
+ overlay: baseColors.alphaBlack50,
87
+ inverseSurface: baseColors.neutral800
86
88
  },
87
89
  state: {
88
90
  hoverOnLight: baseColors.alphaBlack5,
@@ -803,7 +805,7 @@ var Chip = ({
803
805
  )
804
806
  ] });
805
807
  };
806
- var Select = ({
808
+ var Dropdown = ({
807
809
  id,
808
810
  label,
809
811
  placeholder = "Select\u2026",
@@ -813,13 +815,11 @@ var Select = ({
813
815
  defaultValue = null,
814
816
  disabled,
815
817
  size = "md",
816
- variant = "outline",
817
818
  fullWidth,
818
- className,
819
- textAlign = "left"
819
+ className
820
820
  }) => {
821
821
  const internalId = React5.useId();
822
- const selectId = id ?? internalId;
822
+ const dropdownId = id ?? internalId;
823
823
  const isControlled = value !== void 0;
824
824
  const [internalValue, setInternalValue] = React5.useState(defaultValue);
825
825
  const currentValue = isControlled ? value ?? null : internalValue;
@@ -832,6 +832,7 @@ var Select = ({
832
832
  () => options.find((o) => o.value === currentValue) ?? null,
833
833
  [options, currentValue]
834
834
  );
835
+ const isLabelVisible = isOpen || !!currentOption;
835
836
  const setValue = React5.useCallback(
836
837
  (next) => {
837
838
  const option = options.find((o) => o.value === next) ?? null;
@@ -840,16 +841,17 @@ var Select = ({
840
841
  },
841
842
  [isControlled, onChange, options]
842
843
  );
843
- const handleOutsideClick = React5.useEffectEvent((e) => {
844
- if (!wrapperRef.current?.contains(e.target)) {
845
- setIsOpen(false);
846
- }
847
- });
848
844
  React5.useEffect(() => {
845
+ const handleOutsideClick = (e) => {
846
+ if (!wrapperRef.current?.contains(e.target)) {
847
+ setIsOpen(false);
848
+ }
849
+ };
849
850
  document.addEventListener("mousedown", handleOutsideClick);
850
851
  return () => document.removeEventListener("mousedown", handleOutsideClick);
851
852
  }, []);
852
853
  const moveActive = (dir) => {
854
+ if (options.length === 0) return;
853
855
  if (!isOpen) {
854
856
  setIsOpen(true);
855
857
  return;
@@ -914,28 +916,20 @@ var Select = ({
914
916
  if (!isOpen) return;
915
917
  const idx = options.findIndex((o) => o.value === currentValue && !o.disabled);
916
918
  setActiveIndex(
917
- idx >= 0 ? idx : Math.max(
918
- 0,
919
- options.findIndex((o) => !o.disabled)
920
- )
919
+ idx >= 0 ? idx : Math.max(0, options.findIndex((o) => !o.disabled))
921
920
  );
922
921
  }, [isOpen, options, currentValue]);
923
922
  React5.useLayoutEffect(() => {
924
923
  if (!isOpen || !controlRef.current) return;
925
924
  const rect = controlRef.current.getBoundingClientRect();
926
- const listHeight = Math.min(options.length * 40, 288);
925
+ const listHeight = Math.min(options.length * 56, 288);
927
926
  const spaceBelow = window.innerHeight - rect.bottom;
928
927
  const spaceAbove = rect.top;
929
928
  setDropUp(spaceBelow < listHeight && spaceAbove > spaceBelow);
930
929
  }, [isOpen, options.length]);
931
- const rootClassName = cn("select", { select_has_label: !!label }, className);
932
- const fieldsetClassName = cn(
933
- "select_fieldset",
934
- `select_variant_${variant}`,
935
- `select_size_${size}`,
936
- { is_open: isOpen, is_disabled: disabled }
937
- );
938
- const listClassName = cn("select_list", { select_list_up: dropUp });
930
+ const rootClassName = cn("dropdown", `dropdown_size_${size}`, className);
931
+ const fieldsetClassName = cn("dropdown_fieldset", { is_open: isOpen, is_disabled: disabled });
932
+ const listClassName = cn("dropdown_list", { dropdown_list_up: dropUp });
939
933
  return /* @__PURE__ */ jsxs(
940
934
  "div",
941
935
  {
@@ -943,73 +937,79 @@ var Select = ({
943
937
  className: rootClassName,
944
938
  style: fullWidth ? { width: "100%" } : void 0,
945
939
  children: [
946
- /* @__PURE__ */ jsxs("fieldset", { className: fieldsetClassName, children: [
947
- label && /* @__PURE__ */ jsx("legend", { className: "select_label", children: /* @__PURE__ */ jsx("label", { htmlFor: selectId, children: label }) }),
948
- /* @__PURE__ */ jsx("div", { className: "select_inner", children: /* @__PURE__ */ jsxs(
940
+ /* @__PURE__ */ jsxs("div", { className: fieldsetClassName, children: [
941
+ label && /* @__PURE__ */ jsx(
942
+ "span",
943
+ {
944
+ className: cn("dropdown_label", { is_visible: isLabelVisible }),
945
+ "aria-hidden": "true",
946
+ children: label
947
+ }
948
+ ),
949
+ /* @__PURE__ */ jsxs(
949
950
  "button",
950
951
  {
951
952
  ref: controlRef,
952
- id: selectId,
953
+ id: dropdownId,
953
954
  type: "button",
954
- className: cn("select_control", { is_disabled: disabled }),
955
+ className: cn("dropdown_control", { is_disabled: disabled }),
955
956
  "aria-haspopup": "listbox",
956
957
  "aria-expanded": isOpen,
957
- "aria-controls": `${selectId}_listbox`,
958
+ "aria-controls": `${dropdownId}_listbox`,
959
+ "aria-label": label,
958
960
  onClick: () => !disabled && setIsOpen((o) => !o),
959
961
  onKeyDown,
960
962
  disabled,
961
963
  children: [
962
- /* @__PURE__ */ jsx(
963
- "span",
964
- {
965
- className: currentOption ? "select_value" : "select_placeholder",
966
- style: textAlign === "left" ? { textAlign: "start" } : void 0,
967
- children: currentOption ? currentOption.label : placeholder
968
- }
969
- ),
970
- /* @__PURE__ */ jsx("span", { className: "select_icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(ChevronDown, { size: 16 }) })
964
+ /* @__PURE__ */ jsx("span", { className: currentOption ? "dropdown_value" : "dropdown_placeholder", children: currentOption ? currentOption.label : placeholder }),
965
+ /* @__PURE__ */ jsx("span", { className: "dropdown_icon", "aria-hidden": "true", children: isOpen ? /* @__PURE__ */ jsx(X, { size: 24 }) : /* @__PURE__ */ jsx(ChevronDown, { size: 24 }) })
971
966
  ]
972
967
  }
973
- ) })
968
+ )
974
969
  ] }),
975
- isOpen && /* @__PURE__ */ jsx("div", { id: `${selectId}_listbox`, role: "listbox", className: listClassName, children: options.map((opt, i) => {
976
- const selected = currentValue === opt.value;
977
- const active = i === activeIndex;
978
- const optionClassName = cn("select_option", {
979
- is_selected: selected,
980
- is_active: active,
981
- is_disabled: opt.disabled
982
- });
983
- return /* @__PURE__ */ jsxs(
984
- "div",
985
- {
986
- role: "option",
987
- tabIndex: -1,
988
- "aria-selected": selected,
989
- "aria-disabled": opt.disabled ? true : void 0,
990
- className: optionClassName,
991
- onMouseEnter: () => !opt.disabled && setActiveIndex(i),
992
- onClick: () => {
993
- if (opt.disabled) return;
994
- setValue(opt.value);
995
- setIsOpen(false);
996
- },
997
- onKeyDown: (e) => {
998
- if (opt.disabled) return;
999
- if (e.key === "Enter" || e.key === " ") {
1000
- e.preventDefault();
1001
- setValue(opt.value);
1002
- setIsOpen(false);
1003
- }
1004
- },
1005
- children: [
1006
- /* @__PURE__ */ jsx("span", { children: opt.label }),
1007
- selected && /* @__PURE__ */ jsx(Check, { size: 16, "aria-hidden": "true" })
1008
- ]
1009
- },
1010
- opt.value
1011
- );
1012
- }) })
970
+ isOpen && /* @__PURE__ */ jsx(
971
+ "div",
972
+ {
973
+ id: `${dropdownId}_listbox`,
974
+ role: "listbox",
975
+ className: listClassName,
976
+ children: options.map((opt, i) => {
977
+ const selected = currentValue === opt.value;
978
+ const active = i === activeIndex;
979
+ return /* @__PURE__ */ jsxs(React5.Fragment, { children: [
980
+ /* @__PURE__ */ jsxs(
981
+ "div",
982
+ {
983
+ role: "option",
984
+ tabIndex: -1,
985
+ "aria-selected": selected,
986
+ "aria-disabled": opt.disabled ? true : void 0,
987
+ className: cn("dropdown_option", {
988
+ is_selected: selected,
989
+ is_active: active,
990
+ is_disabled: opt.disabled
991
+ }),
992
+ onMouseEnter: () => !opt.disabled && setActiveIndex(i),
993
+ onClick: () => {
994
+ if (opt.disabled) return;
995
+ setValue(opt.value);
996
+ setIsOpen(false);
997
+ },
998
+ children: [
999
+ opt.leadingIcon && /* @__PURE__ */ jsx("span", { className: "dropdown_option_icon", children: opt.leadingIcon }),
1000
+ /* @__PURE__ */ jsxs("span", { className: "dropdown_option_content", children: [
1001
+ /* @__PURE__ */ jsx("span", { className: "dropdown_option_label", children: opt.label }),
1002
+ opt.supportingText && /* @__PURE__ */ jsx("span", { className: "dropdown_option_supporting", children: opt.supportingText })
1003
+ ] }),
1004
+ (selected || opt.trailingIcon) && /* @__PURE__ */ jsx("span", { className: "dropdown_option_trailing", children: opt.trailingIcon ?? /* @__PURE__ */ jsx(Check, { size: 16, "aria-hidden": "true" }) })
1005
+ ]
1006
+ }
1007
+ ),
1008
+ opt.showDivider && /* @__PURE__ */ jsx("div", { className: "dropdown_option_divider", role: "separator" })
1009
+ ] }, opt.value);
1010
+ })
1011
+ }
1012
+ )
1013
1013
  ]
1014
1014
  }
1015
1015
  );
@@ -1166,7 +1166,7 @@ var DatePicker = ({
1166
1166
  "aria-describedby": constraintDesc ? constraintId : void 0,
1167
1167
  children: [
1168
1168
  /* @__PURE__ */ jsx(
1169
- Select,
1169
+ Dropdown,
1170
1170
  {
1171
1171
  size: "sm",
1172
1172
  fullWidth: true,
@@ -1179,7 +1179,7 @@ var DatePicker = ({
1179
1179
  }
1180
1180
  ),
1181
1181
  /* @__PURE__ */ jsx(
1182
- Select,
1182
+ Dropdown,
1183
1183
  {
1184
1184
  size: "sm",
1185
1185
  fullWidth: true,
@@ -1192,7 +1192,7 @@ var DatePicker = ({
1192
1192
  }
1193
1193
  ),
1194
1194
  mode === "year-month-day" && /* @__PURE__ */ jsx(
1195
- Select,
1195
+ Dropdown,
1196
1196
  {
1197
1197
  size: "sm",
1198
1198
  fullWidth: true,
@@ -2542,4 +2542,4 @@ var TopLoading = ({
2542
2542
  );
2543
2543
  };
2544
2544
 
2545
- export { AlertProvider, Button, Card, Checkbox, Chip, DatePicker, Divider, FAB, FileInput, Icon, IconButton, LinearProgress, ListItem, Modal, OtpInput, Pagination, Radio, Select, Spinner, TextField, ToastProvider, Toggle, TopLoading, a11y, baseBorderWidth, baseColors, baseTypography, borderWidth, breakpoints, colors, elevation, motion, opacity, radius, skeleton, spacing, typography, useAlert, useToast, zIndex };
2545
+ export { AlertProvider, Button, Card, Checkbox, Chip, DatePicker, Divider, Dropdown, FAB, FileInput, Icon, IconButton, LinearProgress, ListItem, Modal, OtpInput, Pagination, Radio, Dropdown as Select, Spinner, TextField, ToastProvider, Toggle, TopLoading, a11y, baseBorderWidth, baseColors, baseTypography, borderWidth, breakpoints, colors, elevation, motion, opacity, radius, skeleton, spacing, typography, useAlert, useToast, zIndex };
@@ -8,8 +8,9 @@ $color_base_neutral_200: #E5E5E5;
8
8
  $color_base_neutral_300: #999999;
9
9
  $color_base_neutral_400: #B3B3B3;
10
10
  $color_base_neutral_500: #888888;
11
- $color_base_neutral_600: #6B6B6B;
11
+ $color_base_neutral_600: #777777;
12
12
  $color_base_neutral_700: #666666;
13
+ $color_base_neutral_800: #333333;
13
14
  $color_base_neutral_900: #121212;
14
15
  $color_base_status_error: #EF4444;
15
16
  $color_base_status_success: #10B981;
@@ -45,6 +46,7 @@ $color_bg_solid_dim: $color_base_neutral_50;
45
46
  $color_bg_additive: $color_base_alpha_black_5;
46
47
  $color_bg_disabled: $color_base_alpha_black_12;
47
48
  $color_bg_overlay: $color_base_alpha_black_50;
49
+ $color_bg_inverse_surface: $color_base_neutral_800;
48
50
 
49
51
  // ── Semantic: State ───────────────────────────────────────────────────────────
50
52
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bigtablet/design-system",
3
- "version": "2.2.4",
3
+ "version": "2.4.0",
4
4
  "description": "Bigtablet Design System UI Components",
5
5
  "type": "module",
6
6
  "types": "dist/index.d.ts",
@@ -116,7 +116,7 @@
116
116
  "storybook": "^10.3.3",
117
117
  "tsup": "^8.5.0",
118
118
  "tsx": "^4.21.0",
119
- "typescript": "^6.0.2",
119
+ "typescript": "^6.0.3",
120
120
  "vite": "^6.4.2",
121
121
  "vitest": "^4.1.2"
122
122
  },