@loword/loword-design-system 0.0.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/README.md +20 -0
- package/dist/components/Avatar/Avatar.d.ts +11 -0
- package/dist/components/Avatar/Avatar.d.ts.map +1 -0
- package/dist/components/Avatar/Avatar.js +24 -0
- package/dist/components/Avatar/AvatarBase.d.ts +32 -0
- package/dist/components/Avatar/AvatarBase.d.ts.map +1 -0
- package/dist/components/Avatar/AvatarBase.js +49 -0
- package/dist/components/Badge/Badge.d.ts +25 -0
- package/dist/components/Badge/Badge.d.ts.map +1 -0
- package/dist/components/Badge/Badge.js +36 -0
- package/dist/components/Badge/BadgeBase.d.ts +10 -0
- package/dist/components/Badge/BadgeBase.d.ts.map +1 -0
- package/dist/components/Badge/BadgeBase.js +9 -0
- package/dist/components/Button/Button.d.ts +26 -0
- package/dist/components/Button/Button.d.ts.map +1 -0
- package/dist/components/Button/Button.js +102 -0
- package/dist/components/Button/ButtonBase.d.ts +5 -0
- package/dist/components/Button/ButtonBase.d.ts.map +1 -0
- package/dist/components/Button/ButtonBase.js +10 -0
- package/dist/components/Button/ButtonSpinner.d.ts +9 -0
- package/dist/components/Button/ButtonSpinner.d.ts.map +1 -0
- package/dist/components/Button/ButtonSpinner.js +14 -0
- package/dist/components/Calendar/Calendar.d.ts +16 -0
- package/dist/components/Calendar/Calendar.d.ts.map +1 -0
- package/dist/components/Calendar/Calendar.js +75 -0
- package/dist/components/Carousel/Carousel.d.ts +50 -0
- package/dist/components/Carousel/Carousel.d.ts.map +1 -0
- package/dist/components/Carousel/Carousel.js +22 -0
- package/dist/components/Carousel/CarouselBase.d.ts +98 -0
- package/dist/components/Carousel/CarouselBase.d.ts.map +1 -0
- package/dist/components/Carousel/CarouselBase.js +144 -0
- package/dist/components/Checkbox/Checkbox.d.ts +27 -0
- package/dist/components/Checkbox/Checkbox.d.ts.map +1 -0
- package/dist/components/Checkbox/Checkbox.js +30 -0
- package/dist/components/Checkbox/CheckboxBase.d.ts +40 -0
- package/dist/components/Checkbox/CheckboxBase.d.ts.map +1 -0
- package/dist/components/Checkbox/CheckboxBase.js +57 -0
- package/dist/components/Checkbox/CheckboxGroup.d.ts +22 -0
- package/dist/components/Checkbox/CheckboxGroup.d.ts.map +1 -0
- package/dist/components/Checkbox/CheckboxGroup.js +13 -0
- package/dist/components/Checkbox/FormCheckbox.d.ts +14 -0
- package/dist/components/Checkbox/FormCheckbox.d.ts.map +1 -0
- package/dist/components/Checkbox/FormCheckbox.js +9 -0
- package/dist/components/Checkbox/FormCheckboxGroup.d.ts +13 -0
- package/dist/components/Checkbox/FormCheckboxGroup.d.ts.map +1 -0
- package/dist/components/Checkbox/FormCheckboxGroup.js +9 -0
- package/dist/components/DataCalendar/DataCalendar.d.ts +12 -0
- package/dist/components/DataCalendar/DataCalendar.d.ts.map +1 -0
- package/dist/components/DataCalendar/DataCalendar.js +13 -0
- package/dist/components/DataCalendar/DataCalendarBase.d.ts +45 -0
- package/dist/components/DataCalendar/DataCalendarBase.d.ts.map +1 -0
- package/dist/components/DataCalendar/DataCalendarBase.js +57 -0
- package/dist/components/DatePicker/DatePicker.d.ts +34 -0
- package/dist/components/DatePicker/DatePicker.d.ts.map +1 -0
- package/dist/components/DatePicker/DatePicker.js +44 -0
- package/dist/components/DatePicker/FormDatePicker.d.ts +12 -0
- package/dist/components/DatePicker/FormDatePicker.d.ts.map +1 -0
- package/dist/components/DatePicker/FormDatePicker.js +12 -0
- package/dist/components/DateRangePicker/DateRangePicker.d.ts +49 -0
- package/dist/components/DateRangePicker/DateRangePicker.d.ts.map +1 -0
- package/dist/components/DateRangePicker/DateRangePicker.js +197 -0
- package/dist/components/DateRangePicker/FormDateRangePicker.d.ts +12 -0
- package/dist/components/DateRangePicker/FormDateRangePicker.d.ts.map +1 -0
- package/dist/components/DateRangePicker/FormDateRangePicker.js +10 -0
- package/dist/components/Drawer/Drawer.d.ts +51 -0
- package/dist/components/Drawer/Drawer.d.ts.map +1 -0
- package/dist/components/Drawer/Drawer.js +30 -0
- package/dist/components/Drawer/DrawerBase.d.ts +52 -0
- package/dist/components/Drawer/DrawerBase.d.ts.map +1 -0
- package/dist/components/Drawer/DrawerBase.js +38 -0
- package/dist/components/Editor/Editor.d.ts +12 -0
- package/dist/components/Editor/Editor.d.ts.map +1 -0
- package/dist/components/Editor/Editor.js +162 -0
- package/dist/components/Editor/EditorBase.d.ts +52 -0
- package/dist/components/Editor/EditorBase.d.ts.map +1 -0
- package/dist/components/Editor/EditorBase.js +89 -0
- package/dist/components/Editor/Viewer.d.ts +7 -0
- package/dist/components/Editor/Viewer.d.ts.map +1 -0
- package/dist/components/Editor/Viewer.js +9 -0
- package/dist/components/Editor/extensions.d.ts +6 -0
- package/dist/components/Editor/extensions.d.ts.map +1 -0
- package/dist/components/Editor/extensions.js +38 -0
- package/dist/components/FileUpload/FileUploadBase.d.ts +50 -0
- package/dist/components/FileUpload/FileUploadBase.d.ts.map +1 -0
- package/dist/components/FileUpload/FileUploadBase.js +102 -0
- package/dist/components/Form/Form.d.ts +21 -0
- package/dist/components/Form/Form.d.ts.map +1 -0
- package/dist/components/Form/Form.js +18 -0
- package/dist/components/Form/FormErrorMessage.d.ts +9 -0
- package/dist/components/Form/FormErrorMessage.d.ts.map +1 -0
- package/dist/components/Form/FormErrorMessage.js +6 -0
- package/dist/components/Form/FormField.d.ts +55 -0
- package/dist/components/Form/FormField.d.ts.map +1 -0
- package/dist/components/Form/FormField.js +43 -0
- package/dist/components/Icon/LucideIcon.d.ts +11 -0
- package/dist/components/Icon/LucideIcon.d.ts.map +1 -0
- package/dist/components/Icon/LucideIcon.js +41 -0
- package/dist/components/Input/FormInput.d.ts +15 -0
- package/dist/components/Input/FormInput.d.ts.map +1 -0
- package/dist/components/Input/FormInput.js +19 -0
- package/dist/components/Input/Input.d.ts +24 -0
- package/dist/components/Input/Input.d.ts.map +1 -0
- package/dist/components/Input/Input.js +71 -0
- package/dist/components/Input/InputBase.d.ts +22 -0
- package/dist/components/Input/InputBase.d.ts.map +1 -0
- package/dist/components/Input/InputBase.js +17 -0
- package/dist/components/LNB/LNBBase.d.ts +82 -0
- package/dist/components/LNB/LNBBase.d.ts.map +1 -0
- package/dist/components/LNB/LNBBase.js +108 -0
- package/dist/components/LoadingDots/LoadingDots.d.ts +8 -0
- package/dist/components/LoadingDots/LoadingDots.d.ts.map +1 -0
- package/dist/components/LoadingDots/LoadingDots.js +29 -0
- package/dist/components/MenuBar/MenuBar.d.ts +21 -0
- package/dist/components/MenuBar/MenuBar.d.ts.map +1 -0
- package/dist/components/MenuBar/MenuBar.js +33 -0
- package/dist/components/MenuBar/MenuBarBase.d.ts +43 -0
- package/dist/components/MenuBar/MenuBarBase.d.ts.map +1 -0
- package/dist/components/MenuBar/MenuBarBase.js +69 -0
- package/dist/components/Modal/Modal.d.ts +51 -0
- package/dist/components/Modal/Modal.d.ts.map +1 -0
- package/dist/components/Modal/Modal.js +72 -0
- package/dist/components/Modal/ModalBase.d.ts +42 -0
- package/dist/components/Modal/ModalBase.d.ts.map +1 -0
- package/dist/components/Modal/ModalBase.js +30 -0
- package/dist/components/Modal/openModal.d.ts +24 -0
- package/dist/components/Modal/openModal.d.ts.map +1 -0
- package/dist/components/Modal/openModal.js +52 -0
- package/dist/components/OTPInput/OTPInput.d.ts +46 -0
- package/dist/components/OTPInput/OTPInput.d.ts.map +1 -0
- package/dist/components/OTPInput/OTPInput.js +87 -0
- package/dist/components/Pagination/Pagination.d.ts +12 -0
- package/dist/components/Pagination/Pagination.d.ts.map +1 -0
- package/dist/components/Pagination/Pagination.js +5 -0
- package/dist/components/Pagination/PaginationBase.d.ts +36 -0
- package/dist/components/Pagination/PaginationBase.d.ts.map +1 -0
- package/dist/components/Pagination/PaginationBase.js +44 -0
- package/dist/components/Pagination/paginationUtils.d.ts +2 -0
- package/dist/components/Pagination/paginationUtils.d.ts.map +1 -0
- package/dist/components/Pagination/paginationUtils.js +18 -0
- package/dist/components/Popover/Popover.d.ts +42 -0
- package/dist/components/Popover/Popover.d.ts.map +1 -0
- package/dist/components/Popover/Popover.js +31 -0
- package/dist/components/Popover/PopoverBase.d.ts +31 -0
- package/dist/components/Popover/PopoverBase.d.ts.map +1 -0
- package/dist/components/Popover/PopoverBase.js +18 -0
- package/dist/components/Radio/FormRadioGroup.d.ts +13 -0
- package/dist/components/Radio/FormRadioGroup.d.ts.map +1 -0
- package/dist/components/Radio/FormRadioGroup.js +9 -0
- package/dist/components/Radio/Radio.d.ts +24 -0
- package/dist/components/Radio/Radio.d.ts.map +1 -0
- package/dist/components/Radio/Radio.js +39 -0
- package/dist/components/Radio/RadioBase.d.ts +38 -0
- package/dist/components/Radio/RadioBase.d.ts.map +1 -0
- package/dist/components/Radio/RadioBase.js +46 -0
- package/dist/components/Radio/RadioGroup.d.ts +22 -0
- package/dist/components/Radio/RadioGroup.d.ts.map +1 -0
- package/dist/components/Radio/RadioGroup.js +7 -0
- package/dist/components/ScrollArea/ScrollArea.d.ts +15 -0
- package/dist/components/ScrollArea/ScrollArea.d.ts.map +1 -0
- package/dist/components/ScrollArea/ScrollArea.js +10 -0
- package/dist/components/Select/FormMultiSelect.d.ts +13 -0
- package/dist/components/Select/FormMultiSelect.d.ts.map +1 -0
- package/dist/components/Select/FormMultiSelect.js +14 -0
- package/dist/components/Select/FormSelect.d.ts +13 -0
- package/dist/components/Select/FormSelect.d.ts.map +1 -0
- package/dist/components/Select/FormSelect.js +10 -0
- package/dist/components/Select/MultiSelect.d.ts +31 -0
- package/dist/components/Select/MultiSelect.d.ts.map +1 -0
- package/dist/components/Select/MultiSelect.js +59 -0
- package/dist/components/Select/Select.d.ts +41 -0
- package/dist/components/Select/Select.d.ts.map +1 -0
- package/dist/components/Select/Select.js +55 -0
- package/dist/components/Select/SelectBase.d.ts +59 -0
- package/dist/components/Select/SelectBase.d.ts.map +1 -0
- package/dist/components/Select/SelectBase.js +211 -0
- package/dist/components/Skeleton/Skeleton.d.ts +10 -0
- package/dist/components/Skeleton/Skeleton.d.ts.map +1 -0
- package/dist/components/Skeleton/Skeleton.js +8 -0
- package/dist/components/Slider/Slider.d.ts +23 -0
- package/dist/components/Slider/Slider.d.ts.map +1 -0
- package/dist/components/Slider/Slider.js +195 -0
- package/dist/components/Spinner/Spinner.d.ts +25 -0
- package/dist/components/Spinner/Spinner.d.ts.map +1 -0
- package/dist/components/Spinner/Spinner.js +20 -0
- package/dist/components/Table/Table.d.ts +34 -0
- package/dist/components/Table/Table.d.ts.map +1 -0
- package/dist/components/Table/Table.js +29 -0
- package/dist/components/Table/TableBase.d.ts +37 -0
- package/dist/components/Table/TableBase.d.ts.map +1 -0
- package/dist/components/Table/TableBase.js +39 -0
- package/dist/components/Table/TableField.d.ts +64 -0
- package/dist/components/Table/TableField.d.ts.map +1 -0
- package/dist/components/Table/TableField.js +87 -0
- package/dist/components/Table/TanstackTable.d.ts +23 -0
- package/dist/components/Table/TanstackTable.d.ts.map +1 -0
- package/dist/components/Table/TanstackTable.js +35 -0
- package/dist/components/TimePicker/FormTimePicker.d.ts +12 -0
- package/dist/components/TimePicker/FormTimePicker.d.ts.map +1 -0
- package/dist/components/TimePicker/FormTimePicker.js +12 -0
- package/dist/components/TimePicker/TimePicker.d.ts +27 -0
- package/dist/components/TimePicker/TimePicker.d.ts.map +1 -0
- package/dist/components/TimePicker/TimePicker.js +120 -0
- package/dist/components/Toast/Toast.d.ts +18 -0
- package/dist/components/Toast/Toast.d.ts.map +1 -0
- package/dist/components/Toast/Toast.js +42 -0
- package/dist/components/Toast/Toaster.d.ts +7 -0
- package/dist/components/Toast/Toaster.d.ts.map +1 -0
- package/dist/components/Toast/Toaster.js +7 -0
- package/dist/components/ToggleButton/FormToggleButton.d.ts +12 -0
- package/dist/components/ToggleButton/FormToggleButton.d.ts.map +1 -0
- package/dist/components/ToggleButton/FormToggleButton.js +9 -0
- package/dist/components/ToggleButton/ToggleButton.d.ts +31 -0
- package/dist/components/ToggleButton/ToggleButton.d.ts.map +1 -0
- package/dist/components/ToggleButton/ToggleButton.js +42 -0
- package/dist/components/ToggleButton/ToggleButtonBase.d.ts +41 -0
- package/dist/components/ToggleButton/ToggleButtonBase.d.ts.map +1 -0
- package/dist/components/ToggleButton/ToggleButtonBase.js +59 -0
- package/dist/components/Tooltip/Tooltip.d.ts +18 -0
- package/dist/components/Tooltip/Tooltip.d.ts.map +1 -0
- package/dist/components/Tooltip/Tooltip.js +13 -0
- package/dist/components/Tooltip/TooltipBase.d.ts +39 -0
- package/dist/components/Tooltip/TooltipBase.d.ts.map +1 -0
- package/dist/components/Tooltip/TooltipBase.js +46 -0
- package/dist/constants/file.d.ts +42 -0
- package/dist/constants/file.d.ts.map +1 -0
- package/dist/constants/file.js +47 -0
- package/dist/index.css +4061 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +88 -0
- package/dist/types/polymorphic.d.ts +19 -0
- package/dist/types/polymorphic.d.ts.map +1 -0
- package/dist/types/polymorphic.js +1 -0
- package/dist/utils/cn.d.ts +4 -0
- package/dist/utils/cn.d.ts.map +1 -0
- package/dist/utils/cn.js +6 -0
- package/dist/utils/file.d.ts +16 -0
- package/dist/utils/file.d.ts.map +1 -0
- package/dist/utils/file.js +58 -0
- package/dist/utils/renderIcon.d.ts +4 -0
- package/dist/utils/renderIcon.d.ts.map +1 -0
- package/dist/utils/renderIcon.js +11 -0
- package/dist/utils/utils.d.ts +2 -0
- package/dist/utils/utils.d.ts.map +1 -0
- package/dist/utils/utils.js +5 -0
- package/package.json +239 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import SelectBase from './SelectBase.js';
|
|
3
|
+
import LucideIcon from '../Icon/LucideIcon.js';
|
|
4
|
+
import cn from '../../utils/cn.js';
|
|
5
|
+
// ── variant × color 조합 ──
|
|
6
|
+
const variantColorClasses = {
|
|
7
|
+
filled: {
|
|
8
|
+
neutral: 'bg-neutral5 text-neutral100 border-[0.5px] border-mono200 data-[state=open]:border-lb500',
|
|
9
|
+
point: 'bg-neutral5 text-neutral100 border-[0.5px] border-mono200 data-[state=open]:border-lb500',
|
|
10
|
+
danger: 'bg-tintRed text-neutral100 border-[0.5px] border-transparent data-[state=open]:border-red500',
|
|
11
|
+
},
|
|
12
|
+
outline: {
|
|
13
|
+
neutral: 'bg-transparent text-neutral100 border border-neutral10 data-[state=open]:border-lb500',
|
|
14
|
+
point: 'bg-transparent text-neutral100 border border-neutral10 data-[state=open]:border-lb500',
|
|
15
|
+
danger: 'bg-transparent text-neutral100 border border-danger100 data-[state=open]:border-red500',
|
|
16
|
+
},
|
|
17
|
+
ghost: {
|
|
18
|
+
neutral: 'bg-transparent text-neutral100 border-[0.5px] border-transparent data-[state=open]:border-lb500 hover:bg-neutral5',
|
|
19
|
+
point: 'bg-transparent text-neutral100 border-[0.5px] border-transparent data-[state=open]:border-lb500 hover:bg-neutral5',
|
|
20
|
+
danger: 'bg-transparent text-danger500 border-[0.5px] border-transparent data-[state=open]:border-red500 hover:bg-tintRed',
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
const disabledVariantClasses = {
|
|
24
|
+
filled: 'bg-mono20 text-neutral20',
|
|
25
|
+
outline: 'bg-transparent text-neutral20 border border-mono20',
|
|
26
|
+
ghost: 'bg-transparent text-neutral20',
|
|
27
|
+
};
|
|
28
|
+
const sizeClasses = {
|
|
29
|
+
xs: 'min-h-[38px] display14',
|
|
30
|
+
sm: 'min-h-10 display14',
|
|
31
|
+
md: 'min-h-[56px] display16',
|
|
32
|
+
lg: 'min-h-[62px] display18',
|
|
33
|
+
};
|
|
34
|
+
const iconSizes = {
|
|
35
|
+
xs: 15,
|
|
36
|
+
sm: 15,
|
|
37
|
+
md: 16,
|
|
38
|
+
lg: 16,
|
|
39
|
+
};
|
|
40
|
+
const MultiSelect = ({ items, placeholder = '선택하세요', value = [], onChange, disabled = false, name, variant, color = 'neutral', size, maxDisplay = 2, truncate = true, className, style, contentClassName, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedby, }) => {
|
|
41
|
+
const iconSize = iconSizes[size ?? 'sm'];
|
|
42
|
+
return (_jsxs(SelectBase.Root, { multiple: true, values: value, onMultiChange: onChange, disabled: disabled, name: name, items: items, maxDisplay: maxDisplay, children: [_jsx(SelectBase.Trigger, { style: style, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, className: cn('group rounded-[5px] ld-transition outline-none cursor-pointer', variant &&
|
|
43
|
+
(disabled
|
|
44
|
+
? disabledVariantClasses[variant]
|
|
45
|
+
: variantColorClasses[variant][color]), size && sizeClasses[size], disabled && 'cursor-not-allowed disabled:cursor-not-allowed', className), children: truncate ? (_jsxs("div", { className: "flex gap-sm items-center w-full justify-center px-3 py-1.5", children: [_jsx(SelectBase.Value, { placeholder: placeholder, className: "flex flex-1 bold display14 justify-center text-neutral900", placeholderClassName: "text-neutral200" }), _jsx(LucideIcon, { name: "chevronDown", size: iconSize, className: "text-neutral500 shrink-0" })] })) : (_jsxs("div", { className: "flex gap-sm items-center w-full justify-between px-3 py-1.5", children: [_jsx("div", { className: "flex flex-1 flex-wrap gap-1.5 items-center justify-start text-left", children: value.length === 0 ? (_jsx("span", { className: "text-neutral900", children: placeholder })) : (value.map((v) => {
|
|
46
|
+
const label = items.find((i) => i.value === v)?.label || v;
|
|
47
|
+
return (_jsxs("span", { className: "flex items-center justify-between px-2 py-1 bg-white rounded-[4px] display12 text-neutral900 shadow-sm", children: [label, _jsx("div", { role: "button", "aria-label": `${label} 삭제`, onClick: (e) => {
|
|
48
|
+
e.stopPropagation();
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
if (!disabled) {
|
|
51
|
+
onChange?.(value.filter((val) => val !== v));
|
|
52
|
+
}
|
|
53
|
+
}, className: "ml-1 text-neutral900 hover:text-neutral800 cursor-pointer flex items-center justify-center", children: _jsx(LucideIcon, { name: "x", size: 14 }) })] }, v));
|
|
54
|
+
})) }), _jsx(LucideIcon, { name: "chevronDown", size: iconSize, className: "text-neutral40 shrink-0" })] })) }), _jsx(SelectBase.Content, { className: cn('rounded-[5px] bg-mono5 border border-mono200 p-2 flex flex-col gap-[2px]', 'origin-top overflow-hidden ld-transition', contentClassName), openClassName: "max-h-[230px] opacity-100 translate-y-0 overflow-y-auto scrollbar-none", closedClassName: "max-h-0 opacity-0 -translate-y-1 pointer-events-none", children: items.map((opt) => {
|
|
55
|
+
const isSelected = value.includes(opt.value);
|
|
56
|
+
return (_jsxs(SelectBase.Item, { value: opt.value, className: cn('cursor-pointer hover:bg-neutral5 py-xs px-sm rounded-[5px] display14 regular transition-all duration-200 outline-none', 'focus:bg-neutral5 focus:ring-1 focus:ring-inset focus:ring-lb500', 'flex items-center gap-2', isSelected && 'bg-neutral5'), children: [_jsx("div", { className: cn('w-4 h-4 rounded border flex items-center justify-center transition-all', isSelected ? 'bg-lb500 border-lb500' : 'border-neutral20'), children: isSelected && (_jsx(LucideIcon, { name: "check", size: 12, className: "text-white" })) }), _jsx(SelectBase.ItemText, { children: opt.label })] }, opt.value));
|
|
57
|
+
}) })] }));
|
|
58
|
+
};
|
|
59
|
+
export default MultiSelect;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { CSSProperties } from 'react';
|
|
2
|
+
import { type SelectOption } from './SelectBase.js';
|
|
3
|
+
type SelectVariant = 'filled' | 'outline' | 'ghost';
|
|
4
|
+
type SelectColor = 'neutral' | 'point' | 'danger';
|
|
5
|
+
type SelectSize = 'xs' | 'sm' | 'md' | 'lg';
|
|
6
|
+
export interface SelectProps {
|
|
7
|
+
/** 선택 옵션 목록 ({ value, label }) */
|
|
8
|
+
items: SelectOption[];
|
|
9
|
+
/** 미선택 시 표시할 텍스트 */
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
/** 현재 선택된 값 (controlled) */
|
|
12
|
+
value?: string;
|
|
13
|
+
/** 초기 선택 값 (uncontrolled) */
|
|
14
|
+
defaultValue?: string;
|
|
15
|
+
/** 값 변경 시 호출되는 콜백 */
|
|
16
|
+
onChange?: (value: string) => void;
|
|
17
|
+
/** 비활성화 여부 */
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
/** 폼 필드 이름 */
|
|
20
|
+
name?: string;
|
|
21
|
+
/** 드롭다운 스타일 유형 */
|
|
22
|
+
variant?: SelectVariant;
|
|
23
|
+
/** 색상 테마 */
|
|
24
|
+
color?: SelectColor;
|
|
25
|
+
/** 드롭다운 높이 및 폰트 크기 프리셋 */
|
|
26
|
+
size?: SelectSize;
|
|
27
|
+
/** 추가 CSS 클래스 */
|
|
28
|
+
className?: string;
|
|
29
|
+
/** 인라인 스타일 */
|
|
30
|
+
style?: CSSProperties;
|
|
31
|
+
/** 드롭다운 콘텐츠 영역 className */
|
|
32
|
+
contentClassName?: string;
|
|
33
|
+
/** 에러 상태 속성 */
|
|
34
|
+
'aria-invalid'?: boolean | 'true' | 'false';
|
|
35
|
+
/** 설명 요소 ID 연결 */
|
|
36
|
+
'aria-describedby'?: string;
|
|
37
|
+
}
|
|
38
|
+
export type { SelectOption };
|
|
39
|
+
declare const Select: ({ items, placeholder, value, defaultValue, onChange, disabled, name, variant, color, size, className, style, contentClassName, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, }: SelectProps) => import("react/jsx-runtime").JSX.Element;
|
|
40
|
+
export default Select;
|
|
41
|
+
//# sourceMappingURL=Select.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Select.d.ts","sourceRoot":"","sources":["../../../src/components/Select/Select.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAmB,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAK7D,KAAK,aAAa,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AACpD,KAAK,WAAW,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;AAClD,KAAK,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAwD5C,MAAM,WAAW,WAAW;IAC1B,kCAAkC;IAClC,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,oBAAoB;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4BAA4B;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,cAAc;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kBAAkB;IAClB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,YAAY;IACZ,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,0BAA0B;IAC1B,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,iBAAiB;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc;IACd,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,4BAA4B;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe;IACf,cAAc,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IAC5C,kBAAkB;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,YAAY,EAAE,YAAY,EAAE,CAAC;AAE7B,QAAA,MAAM,MAAM,GAAI,oMAgBb,WAAW,4CAyEb,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import SelectBase from './SelectBase.js';
|
|
3
|
+
import LucideIcon from '../Icon/LucideIcon.js';
|
|
4
|
+
import cn from '../../utils/cn.js';
|
|
5
|
+
// ── variant × color 조합 (LDDropdown 기준) ──
|
|
6
|
+
const variantColorClasses = {
|
|
7
|
+
filled: {
|
|
8
|
+
neutral: 'bg-neutral50 text-neutral900 border-[0.5px] border-transparent data-[state=open]:border-lb500',
|
|
9
|
+
point: 'bg-neutral50 text-neutral900 border-[0.5px] border-transparent data-[state=open]:border-lb500',
|
|
10
|
+
danger: 'bg-tintRed text-neutral900 border-[0.5px] border-transparent data-[state=open]:border-red500',
|
|
11
|
+
},
|
|
12
|
+
outline: {
|
|
13
|
+
neutral: 'bg-transparent text-neutral900 border border-neutral100 data-[state=open]:border-lb500',
|
|
14
|
+
point: 'bg-transparent text-neutral900 border border-neutral100 data-[state=open]:border-lb500',
|
|
15
|
+
danger: 'bg-transparent text-neutral900 border border-danger100 data-[state=open]:border-red500',
|
|
16
|
+
},
|
|
17
|
+
ghost: {
|
|
18
|
+
neutral: 'bg-transparent text-neutral900 border-[0.5px] border-transparent data-[state=open]:border-lb500 hover:bg-neutral50',
|
|
19
|
+
point: 'bg-transparent text-neutral900 border-[0.5px] border-transparent data-[state=open]:border-lb500 hover:bg-neutral50',
|
|
20
|
+
danger: 'bg-transparent text-danger500 border-[0.5px] border-transparent data-[state=open]:border-red500 hover:bg-tintRed',
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
// ── disabled 시 variant별 스타일 ──
|
|
24
|
+
const disabledVariantClasses = {
|
|
25
|
+
filled: 'bg-mono300 text-neutral300',
|
|
26
|
+
outline: 'bg-transparent text-neutral300 border border-mono300',
|
|
27
|
+
ghost: 'bg-transparent text-neutral300',
|
|
28
|
+
};
|
|
29
|
+
// ── size (LDDropdown: display14, h-[38px] 기준) ──
|
|
30
|
+
const sizeClasses = {
|
|
31
|
+
xs: 'h-[38px] display14',
|
|
32
|
+
sm: 'h-10 display14',
|
|
33
|
+
md: 'h-[56px] display16',
|
|
34
|
+
lg: 'h-[62px] display18',
|
|
35
|
+
};
|
|
36
|
+
const iconSizes = {
|
|
37
|
+
xs: 15,
|
|
38
|
+
sm: 15,
|
|
39
|
+
md: 16,
|
|
40
|
+
lg: 16,
|
|
41
|
+
};
|
|
42
|
+
const Select = ({ items, placeholder = '선택하세요', value, defaultValue, onChange, disabled = false, name, variant, color = 'neutral', size, className, style, contentClassName, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedby, }) => {
|
|
43
|
+
const iconSize = iconSizes[size ?? 'sm'];
|
|
44
|
+
// value가 items에 매칭되지 않으면 undefined (Value가 placeholder 표시)
|
|
45
|
+
const resolvedValue = items.some((item) => item.value === value)
|
|
46
|
+
? value
|
|
47
|
+
: undefined;
|
|
48
|
+
return (_jsxs(SelectBase.Root, { value: resolvedValue, defaultValue: defaultValue, onChange: onChange, disabled: disabled, name: name, items: items, children: [_jsx(SelectBase.Trigger, { style: style, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, className: cn('group rounded-[5px] ld-transition outline-none cursor-pointer', variant &&
|
|
49
|
+
(disabled
|
|
50
|
+
? disabledVariantClasses[variant]
|
|
51
|
+
: variantColorClasses[variant][color]), size && sizeClasses[size], disabled && 'cursor-not-allowed disabled:cursor-not-allowed', className), children: _jsxs("div", { className: "flex gap-sm items-center w-full justify-center px-3", children: [_jsx(SelectBase.Value, { placeholder: placeholder, className: "flex flex-1 bold justify-center" }), _jsx(LucideIcon, { name: "chevronDown", size: iconSize, className: "text-neutral500" })] }) }), _jsx(SelectBase.Content, { className: cn('rounded-[5px] bg-white border border-mono100 p-2 flex flex-col gap-3xs', 'origin-top overflow-hidden ld-transition', contentClassName), openClassName: "max-h-[230px] opacity-100 translate-y-0 overflow-y-auto scrollbar-none", closedClassName: "max-h-0 opacity-0 -translate-y-1 pointer-events-none", children: items.map((opt) => (_jsx(SelectBase.Item, { value: opt.value, className: cn('w-full text-left px-3 py-2 rounded-[5px] display14 transition-all cursor-pointer outline-none', 'hover:bg-lb50 hover:text-lb500', 'focus:bg-lb50 focus:text-lb500 focus:ring-1 focus:ring-inset focus:ring-lb500', resolvedValue === opt.value
|
|
52
|
+
? 'bg-neutral10 font-bold text-neutral900'
|
|
53
|
+
: 'text-neutral800'), children: _jsx(SelectBase.ItemText, { children: opt.label }) }, opt.value))) })] }));
|
|
54
|
+
};
|
|
55
|
+
export default Select;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type CSSProperties, type ReactNode } from 'react';
|
|
2
|
+
interface RootProps {
|
|
3
|
+
multiple?: boolean;
|
|
4
|
+
value?: string;
|
|
5
|
+
defaultValue?: string;
|
|
6
|
+
values?: string[];
|
|
7
|
+
defaultValues?: string[];
|
|
8
|
+
onChange?: (value: string) => void;
|
|
9
|
+
onMultiChange?: (value: string[]) => void;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
name?: string;
|
|
12
|
+
items?: SelectOption[];
|
|
13
|
+
maxDisplay?: number;
|
|
14
|
+
children?: ReactNode;
|
|
15
|
+
}
|
|
16
|
+
interface TriggerProps {
|
|
17
|
+
className?: string;
|
|
18
|
+
style?: CSSProperties;
|
|
19
|
+
children?: ReactNode;
|
|
20
|
+
'aria-invalid'?: boolean | 'true' | 'false';
|
|
21
|
+
'aria-describedby'?: string;
|
|
22
|
+
}
|
|
23
|
+
interface ValueProps {
|
|
24
|
+
placeholder?: string;
|
|
25
|
+
className?: string;
|
|
26
|
+
placeholderClassName?: string;
|
|
27
|
+
}
|
|
28
|
+
interface ContentProps {
|
|
29
|
+
className?: string;
|
|
30
|
+
openClassName?: string;
|
|
31
|
+
closedClassName?: string;
|
|
32
|
+
children?: ReactNode;
|
|
33
|
+
}
|
|
34
|
+
interface ItemProps {
|
|
35
|
+
value: string;
|
|
36
|
+
className?: string;
|
|
37
|
+
children?: ReactNode;
|
|
38
|
+
}
|
|
39
|
+
interface ItemIndicatorProps {
|
|
40
|
+
className?: string;
|
|
41
|
+
children?: ReactNode;
|
|
42
|
+
}
|
|
43
|
+
export interface SelectOption {
|
|
44
|
+
label: string;
|
|
45
|
+
value: string;
|
|
46
|
+
}
|
|
47
|
+
export declare const SelectBase: {
|
|
48
|
+
Root: ({ multiple, value, defaultValue, values, defaultValues, onChange, onMultiChange, disabled, name, items, maxDisplay, children, }: RootProps) => import("react/jsx-runtime").JSX.Element;
|
|
49
|
+
Trigger: ({ className, style, children, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, }: TriggerProps) => import("react/jsx-runtime").JSX.Element;
|
|
50
|
+
Value: ({ placeholder, className, placeholderClassName, }: ValueProps) => import("react/jsx-runtime").JSX.Element;
|
|
51
|
+
Content: ({ className, openClassName, closedClassName, children, }: ContentProps) => import("react/jsx-runtime").JSX.Element;
|
|
52
|
+
Item: ({ value: itemValue, className, children }: ItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
53
|
+
ItemText: ({ children }: {
|
|
54
|
+
children?: ReactNode;
|
|
55
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
56
|
+
ItemIndicator: ({ className, children }: ItemIndicatorProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
57
|
+
};
|
|
58
|
+
export default SelectBase;
|
|
59
|
+
//# sourceMappingURL=SelectBase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SelectBase.d.ts","sourceRoot":"","sources":["../../../src/components/Select/SelectBase.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,SAAS,EAQf,MAAM,OAAO,CAAC;AAsCf,UAAU,SAAS;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAsGD,UAAU,YAAY;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IAC5C,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAyDD,UAAU,UAAU;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AA8CD,UAAU,YAAY;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AA4DD,UAAU,SAAS;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AA4FD,UAAU,kBAAkB;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAeD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,UAAU;4IAhYpB,SAAS;iHAqGT,YAAY;+DA2DZ,UAAU;wEAoDV,YAAY;sDA2D0C,SAAS;6BAqFlC;QAAE,QAAQ,CAAC,EAAE,SAAS,CAAA;KAAE;6CAUR,kBAAkB;CA0BjE,CAAC;AAEF,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext, useState, useEffect, useRef, useId, useCallback, } from 'react';
|
|
4
|
+
import cn from '../../utils/cn.js';
|
|
5
|
+
const SelectContext = createContext(null);
|
|
6
|
+
const useSelectContext = () => {
|
|
7
|
+
const ctx = useContext(SelectContext);
|
|
8
|
+
if (!ctx)
|
|
9
|
+
throw new Error('SelectBase 하위 컴포넌트는 Root 내부에서 사용해야 합니다.');
|
|
10
|
+
return ctx;
|
|
11
|
+
};
|
|
12
|
+
// ── Item Context (Item → ItemIndicator 간 value 공유) ──
|
|
13
|
+
const ItemContext = createContext(null);
|
|
14
|
+
const useItemContext = () => {
|
|
15
|
+
return useContext(ItemContext);
|
|
16
|
+
};
|
|
17
|
+
const Root = ({ multiple = false, value, defaultValue, values, defaultValues = [], onChange, onMultiChange, disabled = false, name, items, maxDisplay = 2, children, }) => {
|
|
18
|
+
const [open, setOpen] = useState(false);
|
|
19
|
+
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
20
|
+
const [internalValues, setInternalValues] = useState(defaultValues);
|
|
21
|
+
const rootRef = useRef(null);
|
|
22
|
+
const listboxId = useId();
|
|
23
|
+
const isControlled = value !== undefined;
|
|
24
|
+
const isMultiControlled = values !== undefined;
|
|
25
|
+
const resolvedValue = isControlled ? value : internalValue;
|
|
26
|
+
const resolvedValues = isMultiControlled ? values : internalValues;
|
|
27
|
+
const handleChange = useCallback((val) => {
|
|
28
|
+
if (!isControlled)
|
|
29
|
+
setInternalValue(val);
|
|
30
|
+
onChange?.(val);
|
|
31
|
+
}, [isControlled, onChange]);
|
|
32
|
+
const handleMultiChange = useCallback((vals) => {
|
|
33
|
+
if (!isMultiControlled)
|
|
34
|
+
setInternalValues(vals);
|
|
35
|
+
onMultiChange?.(vals);
|
|
36
|
+
}, [isMultiControlled, onMultiChange]);
|
|
37
|
+
const toggleValue = useCallback((val) => {
|
|
38
|
+
if (disabled)
|
|
39
|
+
return;
|
|
40
|
+
const newValues = resolvedValues.includes(val)
|
|
41
|
+
? resolvedValues.filter((v) => v !== val)
|
|
42
|
+
: [...resolvedValues, val];
|
|
43
|
+
handleMultiChange(newValues);
|
|
44
|
+
}, [disabled, resolvedValues, handleMultiChange]);
|
|
45
|
+
// click-outside (LDDropdown 방식: capture phase)
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const handler = (e) => {
|
|
48
|
+
if (!rootRef.current?.contains(e.target)) {
|
|
49
|
+
setOpen(false);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
document.addEventListener('mousedown', handler, { capture: true });
|
|
53
|
+
return () => document.removeEventListener('mousedown', handler, { capture: true });
|
|
54
|
+
}, []);
|
|
55
|
+
return (_jsx(SelectContext.Provider, { value: {
|
|
56
|
+
open,
|
|
57
|
+
setOpen,
|
|
58
|
+
multiple,
|
|
59
|
+
value: resolvedValue,
|
|
60
|
+
values: resolvedValues,
|
|
61
|
+
onChange: handleChange,
|
|
62
|
+
onMultiChange: handleMultiChange,
|
|
63
|
+
toggleValue,
|
|
64
|
+
disabled,
|
|
65
|
+
listboxId,
|
|
66
|
+
items,
|
|
67
|
+
maxDisplay,
|
|
68
|
+
}, children: _jsxs("div", { ref: rootRef, className: "relative w-full", children: [name && (_jsx("input", { type: "hidden", name: name, value: multiple ? JSON.stringify(resolvedValues) : (resolvedValue ?? '') })), children] }) }));
|
|
69
|
+
};
|
|
70
|
+
const Trigger = ({ className, style, children, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedby, }) => {
|
|
71
|
+
const { open, setOpen, disabled, listboxId } = useSelectContext();
|
|
72
|
+
return (_jsx("button", { type: "button", "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": listboxId, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, disabled: disabled, "data-state": open ? 'open' : 'closed', onClick: () => {
|
|
73
|
+
if (!disabled)
|
|
74
|
+
setOpen(!open);
|
|
75
|
+
}, onKeyDown: (e) => {
|
|
76
|
+
if (disabled)
|
|
77
|
+
return;
|
|
78
|
+
if (e.key === 'Enter' ||
|
|
79
|
+
e.key === ' ' ||
|
|
80
|
+
e.key === 'ArrowDown' ||
|
|
81
|
+
e.key === 'ArrowUp') {
|
|
82
|
+
e.preventDefault();
|
|
83
|
+
if (!open) {
|
|
84
|
+
setOpen(true);
|
|
85
|
+
}
|
|
86
|
+
else if (e.key === 'ArrowDown') {
|
|
87
|
+
const listbox = document.getElementById(listboxId);
|
|
88
|
+
const firstOption = listbox?.querySelector('[role="option"]');
|
|
89
|
+
firstOption?.focus();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else if (e.key === 'Escape') {
|
|
93
|
+
setOpen(false);
|
|
94
|
+
// 닫힐 때 트리거로 포커스 이동을 위해 버튼을 찾음
|
|
95
|
+
e.currentTarget.focus();
|
|
96
|
+
}
|
|
97
|
+
}, style: style, className: cn('w-full flex justify-center items-center', className), children: children }));
|
|
98
|
+
};
|
|
99
|
+
const Value = ({ placeholder, className, placeholderClassName, }) => {
|
|
100
|
+
const { multiple, value, values, items, maxDisplay } = useSelectContext();
|
|
101
|
+
// 단일 선택
|
|
102
|
+
if (!multiple) {
|
|
103
|
+
const selectedLabel = items?.find((item) => item.value === value)?.label;
|
|
104
|
+
return (_jsx("span", { className: cn(className, selectedLabel == null && placeholderClassName), children: selectedLabel ?? placeholder }));
|
|
105
|
+
}
|
|
106
|
+
// 다중 선택
|
|
107
|
+
if (values.length === 0) {
|
|
108
|
+
return (_jsx("span", { className: cn(className, placeholderClassName), children: placeholder }));
|
|
109
|
+
}
|
|
110
|
+
const selectedLabels = items
|
|
111
|
+
?.filter((item) => values.includes(item.value))
|
|
112
|
+
.map((item) => item.label) ?? [];
|
|
113
|
+
if (selectedLabels.length <= maxDisplay) {
|
|
114
|
+
return _jsx("span", { className: className, children: selectedLabels.join(', ') });
|
|
115
|
+
}
|
|
116
|
+
return (_jsxs("span", { className: className, children: [selectedLabels.slice(0, maxDisplay).join(', '), " \uC678", ' ', selectedLabels.length - maxDisplay, "\uAC1C"] }));
|
|
117
|
+
};
|
|
118
|
+
const Content = ({ className, openClassName, closedClassName, children, }) => {
|
|
119
|
+
const { open, multiple, listboxId } = useSelectContext();
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
if (open) {
|
|
122
|
+
const focusAndScroll = () => {
|
|
123
|
+
const listbox = document.getElementById(listboxId);
|
|
124
|
+
if (!listbox)
|
|
125
|
+
return;
|
|
126
|
+
let targetOption = listbox.querySelector('[role="option"][aria-selected="true"]');
|
|
127
|
+
if (!targetOption) {
|
|
128
|
+
targetOption = listbox.querySelector('[role="option"]');
|
|
129
|
+
}
|
|
130
|
+
if (targetOption) {
|
|
131
|
+
targetOption.focus();
|
|
132
|
+
targetOption.scrollIntoView({ block: 'nearest' });
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
// 즉시 포커스 (키보드 대응)
|
|
136
|
+
setTimeout(focusAndScroll, 0);
|
|
137
|
+
// 트랜지션(약 200ms) 후 스크롤 재조정 (max-height가 0일 때 scrollIntoView가 씹히는 문제 방지)
|
|
138
|
+
setTimeout(focusAndScroll, 200);
|
|
139
|
+
}
|
|
140
|
+
}, [open, listboxId]);
|
|
141
|
+
return (_jsx("ul", { id: listboxId, role: "listbox", tabIndex: -1, "aria-hidden": !open, "aria-multiselectable": multiple || undefined, "data-state": open ? 'open' : 'closed', className: cn('absolute z-50 w-full mt-1', className, open ? openClassName : closedClassName), children: children }));
|
|
142
|
+
};
|
|
143
|
+
const Item = ({ value: itemValue, className, children }) => {
|
|
144
|
+
const { multiple, value, values, onChange, toggleValue, setOpen, disabled, listboxId, } = useSelectContext();
|
|
145
|
+
const [highlighted, setHighlighted] = useState(false);
|
|
146
|
+
const isSelected = multiple
|
|
147
|
+
? values.includes(itemValue)
|
|
148
|
+
: value === itemValue;
|
|
149
|
+
const handleClick = useCallback(() => {
|
|
150
|
+
if (disabled)
|
|
151
|
+
return;
|
|
152
|
+
if (multiple) {
|
|
153
|
+
toggleValue(itemValue);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
onChange?.(itemValue);
|
|
157
|
+
setOpen(false);
|
|
158
|
+
}
|
|
159
|
+
}, [disabled, multiple, onChange, toggleValue, itemValue, setOpen]);
|
|
160
|
+
return (_jsx(ItemContext.Provider, { value: itemValue, children: _jsx("li", { role: "option", tabIndex: 0, "aria-selected": isSelected, "data-state": isSelected ? 'checked' : 'unchecked', "data-highlighted": highlighted || undefined, onMouseDown: (e) => e.preventDefault(), onClick: handleClick, onMouseEnter: () => setHighlighted(true), onMouseLeave: () => setHighlighted(false), onFocus: () => setHighlighted(true), onBlur: () => setHighlighted(false), onKeyDown: (e) => {
|
|
161
|
+
switch (e.key) {
|
|
162
|
+
case 'Enter':
|
|
163
|
+
case ' ':
|
|
164
|
+
e.preventDefault();
|
|
165
|
+
handleClick();
|
|
166
|
+
// 단일 선택 시 닫히고 트리거로 포커스 복귀
|
|
167
|
+
if (!multiple) {
|
|
168
|
+
const trigger = document.querySelector(`[aria-controls="${listboxId}"]`);
|
|
169
|
+
trigger?.focus();
|
|
170
|
+
}
|
|
171
|
+
break;
|
|
172
|
+
case 'Escape':
|
|
173
|
+
e.preventDefault();
|
|
174
|
+
setOpen(false);
|
|
175
|
+
document.querySelector(`[aria-controls="${listboxId}"]`)?.focus();
|
|
176
|
+
break;
|
|
177
|
+
case 'ArrowDown':
|
|
178
|
+
e.preventDefault();
|
|
179
|
+
e.currentTarget.nextElementSibling?.focus();
|
|
180
|
+
break;
|
|
181
|
+
case 'ArrowUp':
|
|
182
|
+
e.preventDefault();
|
|
183
|
+
e.currentTarget.previousElementSibling?.focus();
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}, className: cn('relative flex items-center outline-none cursor-pointer', className), children: children }) }));
|
|
187
|
+
};
|
|
188
|
+
// ── ItemText ──
|
|
189
|
+
const ItemText = ({ children }) => {
|
|
190
|
+
return _jsx("span", { children: children });
|
|
191
|
+
};
|
|
192
|
+
const ItemIndicator = ({ className, children }) => {
|
|
193
|
+
const { multiple, value, values } = useSelectContext();
|
|
194
|
+
const itemValue = useItemContext();
|
|
195
|
+
const isSelected = multiple
|
|
196
|
+
? itemValue != null && values.includes(itemValue)
|
|
197
|
+
: value === itemValue;
|
|
198
|
+
if (!isSelected)
|
|
199
|
+
return null;
|
|
200
|
+
return _jsx("span", { className: cn('ml-auto', className), children: children });
|
|
201
|
+
};
|
|
202
|
+
export const SelectBase = {
|
|
203
|
+
Root,
|
|
204
|
+
Trigger,
|
|
205
|
+
Value,
|
|
206
|
+
Content,
|
|
207
|
+
Item,
|
|
208
|
+
ItemText,
|
|
209
|
+
ItemIndicator,
|
|
210
|
+
};
|
|
211
|
+
export default SelectBase;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export interface SkeletonProps {
|
|
3
|
+
/** false이면 children을 그대로 렌더링합니다 */
|
|
4
|
+
loading?: boolean;
|
|
5
|
+
className?: string;
|
|
6
|
+
children?: ReactNode;
|
|
7
|
+
}
|
|
8
|
+
declare const Skeleton: ({ loading, className, children }: SkeletonProps) => import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export default Skeleton;
|
|
10
|
+
//# sourceMappingURL=Skeleton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Skeleton.d.ts","sourceRoot":"","sources":["../../../src/components/Skeleton/Skeleton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,WAAW,aAAa;IAC5B,mCAAmC;IACnC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,QAAA,MAAM,QAAQ,GAAI,kCAAyC,aAAa,4CAgBvE,CAAC;AAEF,eAAe,QAAQ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import cn from '../../utils/cn.js';
|
|
3
|
+
const Skeleton = ({ loading = true, className, children }) => {
|
|
4
|
+
if (!loading)
|
|
5
|
+
return _jsx(_Fragment, { children: children });
|
|
6
|
+
return (_jsxs("span", { "aria-busy": "true", "aria-label": "\uB85C\uB529 \uC911", className: cn('relative inline-block overflow-hidden rounded-md', className), children: [_jsx("span", { className: "invisible", children: children }), _jsx("span", { className: "absolute inset-0 rounded-[inherit] bg-mono200 animate-pulse" })] }));
|
|
7
|
+
};
|
|
8
|
+
export default Skeleton;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
type SliderSingleProps = {
|
|
2
|
+
value?: number;
|
|
3
|
+
defaultValue?: number;
|
|
4
|
+
onChange?: (value: number) => void;
|
|
5
|
+
};
|
|
6
|
+
type SliderRangeProps = {
|
|
7
|
+
value?: [number, number];
|
|
8
|
+
defaultValue?: [number, number];
|
|
9
|
+
onChange?: (value: [number, number]) => void;
|
|
10
|
+
};
|
|
11
|
+
export type SliderProps = (SliderSingleProps | SliderRangeProps) & {
|
|
12
|
+
min?: number;
|
|
13
|
+
max?: number;
|
|
14
|
+
step?: number;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
orientation?: 'horizontal' | 'vertical';
|
|
17
|
+
showLabel?: boolean;
|
|
18
|
+
className?: string;
|
|
19
|
+
style?: React.CSSProperties;
|
|
20
|
+
};
|
|
21
|
+
declare const Slider: (props: SliderProps) => import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
export default Slider;
|
|
23
|
+
//# sourceMappingURL=Slider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Slider.d.ts","sourceRoot":"","sources":["../../../src/components/Slider/Slider.tsx"],"names":[],"mappings":"AAKA,KAAK,iBAAiB,GAAG;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzB,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,CAAC,iBAAiB,GAAG,gBAAgB,CAAC,GAAG;IACjE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IACxC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B,CAAC;AAiCF,QAAA,MAAM,MAAM,GAAI,OAAO,WAAW,4CAgUjC,CAAC;AAEF,eAAe,MAAM,CAAC"}
|