@designbasekorea/ui 0.1.21 → 0.1.23
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 +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +100 -197
- package/dist/index.esm.css +1 -1
- package/dist/index.esm.css.map +1 -1
- package/dist/index.esm.js +1337 -936
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1339 -934
- package/dist/index.js.map +1 -1
- package/dist/index.umd.css +1 -1
- package/dist/index.umd.css.map +1 -1
- package/dist/index.umd.js +1339 -934
- package/dist/index.umd.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1455,7 +1455,7 @@ const Accordion = ({ items, style = 'default', size = 'm', allowMultiple = false
|
|
|
1455
1455
|
const itemType = item.itemType || defaultItemType;
|
|
1456
1456
|
switch (itemType) {
|
|
1457
1457
|
case 'icon':
|
|
1458
|
-
return item.icon ? (jsxRuntimeExports.jsx("div", { className: "designbase-accordion__icon", children: jsxRuntimeExports.jsx(item.icon, { size: iconSize
|
|
1458
|
+
return item.icon ? (jsxRuntimeExports.jsx("div", { className: "designbase-accordion__icon", children: jsxRuntimeExports.jsx(item.icon, { size: iconSize }) })) : null;
|
|
1459
1459
|
case 'number':
|
|
1460
1460
|
return (jsxRuntimeExports.jsxs("div", { className: "designbase-accordion__number", children: [index + 1, "."] }));
|
|
1461
1461
|
case 'question':
|
|
@@ -1471,7 +1471,7 @@ const Accordion = ({ items, style = 'default', size = 'm', allowMultiple = false
|
|
|
1471
1471
|
'designbase-accordion__item--expanded': expanded,
|
|
1472
1472
|
'designbase-accordion__item--disabled': item.disabled,
|
|
1473
1473
|
});
|
|
1474
|
-
return (jsxRuntimeExports.jsxs("div", { className: itemClasses, children: [jsxRuntimeExports.jsxs("button", { type: "button", className: "designbase-accordion__trigger", onClick: () => !item.disabled && handleItemToggle(item.id), disabled: item.disabled, "aria-expanded": expanded, "aria-controls": `accordion-content-${item.id}`, children: [renderItemPrefix(item, index), jsxRuntimeExports.jsx("span", { className: "designbase-accordion__title", children: item.title }), jsxRuntimeExports.jsx("div", { className: "designbase-accordion__chevron", children: jsxRuntimeExports.jsx(icons.ChevronDownIcon, { size: iconSize
|
|
1474
|
+
return (jsxRuntimeExports.jsxs("div", { className: itemClasses, children: [jsxRuntimeExports.jsxs("button", { type: "button", className: "designbase-accordion__trigger", onClick: () => !item.disabled && handleItemToggle(item.id), disabled: item.disabled, "aria-expanded": expanded, "aria-controls": `accordion-content-${item.id}`, children: [renderItemPrefix(item, index), jsxRuntimeExports.jsx("span", { className: "designbase-accordion__title", children: item.title }), jsxRuntimeExports.jsx("div", { className: "designbase-accordion__chevron", children: jsxRuntimeExports.jsx(icons.ChevronDownIcon, { size: iconSize }) })] }), jsxRuntimeExports.jsx("div", { id: `accordion-content-${item.id}`, className: "designbase-accordion__content", "aria-hidden": !expanded, children: jsxRuntimeExports.jsx("div", { className: "designbase-accordion__content-inner", children: item.content }) })] }, item.id));
|
|
1475
1475
|
}) }));
|
|
1476
1476
|
};
|
|
1477
1477
|
Accordion.displayName = 'Accordion';
|
|
@@ -3638,7 +3638,7 @@ const Button = React.forwardRef(({ variant = 'primary', size = 'm', radius, full
|
|
|
3638
3638
|
});
|
|
3639
3639
|
Button.displayName = 'Button';
|
|
3640
3640
|
|
|
3641
|
-
const Alert = ({ title, children, variant = 'info', size = 'm', showIcon = true, closable =
|
|
3641
|
+
const Alert = ({ title, children, variant = 'info', size = 'm', showIcon = true, closable = true, actions, actionButtons, onClose, className, ...props }) => {
|
|
3642
3642
|
const [isVisible, setIsVisible] = React.useState(true);
|
|
3643
3643
|
const handleClose = () => {
|
|
3644
3644
|
setIsVisible(false);
|
|
@@ -3673,7 +3673,7 @@ const Alert = ({ title, children, variant = 'info', size = 'm', showIcon = true,
|
|
|
3673
3673
|
'designbase-alert--with-icon': showIcon,
|
|
3674
3674
|
'designbase-alert--closable': closable,
|
|
3675
3675
|
}, className);
|
|
3676
|
-
return (jsxRuntimeExports.jsxs("div", { className: classes, role: "alert", "aria-live": "polite", ...props, children: [jsxRuntimeExports.jsxs("div", { className: "designbase-alert__content", children: [showIcon && (jsxRuntimeExports.jsx("div", { className: "designbase-alert__icon", children: getIcon() })), jsxRuntimeExports.jsxs("div", { className: "designbase-alert__body", children: [title && (jsxRuntimeExports.jsx("div", { className: "designbase-alert__title", children: title })), jsxRuntimeExports.jsx("div", { className: "designbase-alert__message", children: children })] }), closable && (jsxRuntimeExports.jsx("button", { type: "button", className: "designbase-alert__close", onClick: handleClose, "aria-label": "\uC54C\uB9BC \uB2EB\uAE30", children: jsxRuntimeExports.jsx(icons.CloseIcon, { size: iconSize }) }))] }), (actions || actionButtons) && (jsxRuntimeExports.jsx("div", { className: "designbase-alert__actions", children: actionButtons ? (jsxRuntimeExports.jsx("div", { className: "designbase-alert__action-buttons", children: actionButtons.map((action, index) => (jsxRuntimeExports.jsx(Button, { size: action.size || size, variant: action.variant || '
|
|
3676
|
+
return (jsxRuntimeExports.jsxs("div", { className: classes, role: "alert", "aria-live": "polite", ...props, children: [jsxRuntimeExports.jsxs("div", { className: "designbase-alert__content", children: [showIcon && (jsxRuntimeExports.jsx("div", { className: "designbase-alert__icon", children: getIcon() })), jsxRuntimeExports.jsxs("div", { className: "designbase-alert__body", children: [title && (jsxRuntimeExports.jsx("div", { className: "designbase-alert__title", children: title })), jsxRuntimeExports.jsx("div", { className: "designbase-alert__message", children: children })] }), closable && (jsxRuntimeExports.jsx("button", { type: "button", className: "designbase-alert__close", onClick: handleClose, "aria-label": "\uC54C\uB9BC \uB2EB\uAE30", children: jsxRuntimeExports.jsx(icons.CloseIcon, { size: iconSize }) }))] }), (actions || actionButtons) && (jsxRuntimeExports.jsx("div", { className: "designbase-alert__actions", children: actionButtons ? (jsxRuntimeExports.jsx("div", { className: "designbase-alert__action-buttons", children: actionButtons.map((action, index) => (jsxRuntimeExports.jsx(Button, { size: action.size || size, variant: action.variant || 'tertiary', onClick: action.onClick, disabled: action.disabled, children: action.label }, index))) })) : (actions) }))] }));
|
|
3677
3677
|
};
|
|
3678
3678
|
Alert.displayName = 'Alert';
|
|
3679
3679
|
|
|
@@ -4492,64 +4492,32 @@ const Backdrop = React.forwardRef(({ open = false, onClick, disableBackdropClick
|
|
|
4492
4492
|
});
|
|
4493
4493
|
Backdrop.displayName = 'Backdrop';
|
|
4494
4494
|
|
|
4495
|
-
const Banner = ({ title, description, icon, image, imageAlt, actions = [],
|
|
4496
|
-
const [isVisible, setIsVisible] = React.useState(true);
|
|
4495
|
+
const Banner = ({ title, description, icon, image, imageAlt, actions = [], size = 'm', variant = 'primary', backgroundImage, className, }) => {
|
|
4497
4496
|
// 아이콘 크기 계산 (m이 기본값)
|
|
4498
4497
|
const iconSize = size === 's' ? 16 : size === 'l' ? 24 : 20;
|
|
4499
|
-
// 자동 닫기
|
|
4500
|
-
React.useEffect(() => {
|
|
4501
|
-
if (autoDismiss && autoDismiss > 0) {
|
|
4502
|
-
const timer = setTimeout(() => {
|
|
4503
|
-
handleDismiss();
|
|
4504
|
-
}, autoDismiss);
|
|
4505
|
-
return () => clearTimeout(timer);
|
|
4506
|
-
}
|
|
4507
|
-
}, [autoDismiss]);
|
|
4508
|
-
const handleDismiss = () => {
|
|
4509
|
-
setIsVisible(false);
|
|
4510
|
-
onDismiss?.();
|
|
4511
|
-
};
|
|
4512
4498
|
const renderActions = () => {
|
|
4513
4499
|
if (actions.length === 0)
|
|
4514
4500
|
return null;
|
|
4515
4501
|
return (jsxRuntimeExports.jsx("div", { className: "designbase-banner__actions", children: actions.map((action, index) => (jsxRuntimeExports.jsx(Button, { href: action.href, variant: action.variant || 'primary', size: action.size || 'sm', onClick: action.onClick, target: action.external ? '_blank' : undefined, rel: action.external ? 'noopener noreferrer' : undefined, icon: action.icon, children: action.text }, index))) }));
|
|
4516
4502
|
};
|
|
4517
4503
|
const renderBackground = () => {
|
|
4518
|
-
if (!backgroundImage)
|
|
4504
|
+
if (variant !== 'image' || !backgroundImage)
|
|
4519
4505
|
return null;
|
|
4520
|
-
return (jsxRuntimeExports.jsx("div", { className: "designbase-banner__background", style: {
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
};
|
|
4527
|
-
const
|
|
4528
|
-
|
|
4529
|
-
return null;
|
|
4530
|
-
return (jsxRuntimeExports.jsx("div", { className: "designbase-banner__overlay", style: {
|
|
4531
|
-
backgroundColor: overlayColor,
|
|
4532
|
-
opacity: overlayOpacity
|
|
4533
|
-
} }));
|
|
4534
|
-
};
|
|
4535
|
-
const classes = clsx('designbase-banner', `designbase-banner--size-${size}`, `designbase-banner--variant-${variant}`, `designbase-banner--style-${style}`, `designbase-banner--position-${position}`, `designbase-banner--alignment-${alignment}`, {
|
|
4536
|
-
'designbase-banner--animated': animated,
|
|
4537
|
-
'designbase-banner--full-width': fullWidth,
|
|
4538
|
-
'designbase-banner--shadow': shadow,
|
|
4539
|
-
'designbase-banner--rounded': rounded,
|
|
4540
|
-
'designbase-banner--dismissible': dismissible,
|
|
4541
|
-
'designbase-banner--visible': isVisible,
|
|
4542
|
-
'designbase-banner--hidden': !isVisible,
|
|
4543
|
-
}, className);
|
|
4544
|
-
if (!isVisible)
|
|
4545
|
-
return null;
|
|
4546
|
-
return (jsxRuntimeExports.jsxs("div", { className: classes, children: [renderBackground(), renderOverlay(), jsxRuntimeExports.jsxs("div", { className: "designbase-banner__container", children: [icon && (jsxRuntimeExports.jsx("div", { className: "designbase-banner__icon", children: React.isValidElement(icon) ? (React.cloneElement(icon, {
|
|
4506
|
+
return (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("div", { className: "designbase-banner__background", style: {
|
|
4507
|
+
backgroundImage: `url(${backgroundImage})`,
|
|
4508
|
+
backgroundSize: 'cover',
|
|
4509
|
+
backgroundPosition: 'center',
|
|
4510
|
+
backgroundRepeat: 'no-repeat'
|
|
4511
|
+
} }), jsxRuntimeExports.jsx("div", { className: "designbase-banner__overlay" })] }));
|
|
4512
|
+
};
|
|
4513
|
+
const classes = clsx('designbase-banner', `designbase-banner--${size}`, `designbase-banner--${variant}`, className);
|
|
4514
|
+
return (jsxRuntimeExports.jsxs("div", { className: classes, children: [renderBackground(), jsxRuntimeExports.jsxs("div", { className: "designbase-banner__container", children: [icon && (jsxRuntimeExports.jsx("div", { className: "designbase-banner__icon", children: React.isValidElement(icon) ? (React.cloneElement(icon, {
|
|
4547
4515
|
size: iconSize,
|
|
4548
4516
|
color: 'currentColor'
|
|
4549
|
-
})) : icon })), image && (jsxRuntimeExports.jsx("div", { className: "designbase-banner__image", children: jsxRuntimeExports.jsx("img", { src: image, alt: imageAlt || title || 'Banner image' }) })), jsxRuntimeExports.jsxs("div", { className: "designbase-banner__content", children: [title && (jsxRuntimeExports.jsx("h3", { className: "designbase-banner__title", children: title })), description && (jsxRuntimeExports.jsx("p", { className: "designbase-banner__description", children: description }))] }), renderActions()
|
|
4517
|
+
})) : icon })), image && (jsxRuntimeExports.jsx("div", { className: "designbase-banner__image", children: jsxRuntimeExports.jsx("img", { src: image, alt: imageAlt || title || 'Banner image' }) })), jsxRuntimeExports.jsxs("div", { className: "designbase-banner__content", children: [title && (jsxRuntimeExports.jsx("h3", { className: "designbase-banner__title", children: title })), description && (jsxRuntimeExports.jsx("p", { className: "designbase-banner__description", children: description }))] }), renderActions()] })] }));
|
|
4550
4518
|
};
|
|
4551
4519
|
|
|
4552
|
-
const BottomSheet = React.forwardRef(({ open = false, onClose, title, subtitle, size = 'm', variant = 'default', disableBackdropClick = false, disableEscapeKeyDown = false, disableDrag = false, maxHeight = 80, animation = 'slide', animationDuration = 300, zIndex = 1000,
|
|
4520
|
+
const BottomSheet = React.forwardRef(({ open = false, onClose, title, subtitle, size = 'm', variant = 'default', disableBackdropClick = false, disableEscapeKeyDown = false, disableDrag = false, maxHeight = 80, animation = 'slide', animationDuration = 300, zIndex = 1000, showBackdrop = true, backdropBlur = false, backdropOpacity = 0.5, stickyHeader = false, stickyFooter = false, className, style, header, footer, children, ...props }, forwardedRef) => {
|
|
4553
4521
|
const [sheetHeight, setSheetHeight] = React.useState(maxHeight);
|
|
4554
4522
|
const [isDragging, setIsDragging] = React.useState(false);
|
|
4555
4523
|
const [dragStartY, setDragStartY] = React.useState(0);
|
|
@@ -4673,7 +4641,7 @@ const BottomSheet = React.forwardRef(({ open = false, onClose, title, subtitle,
|
|
|
4673
4641
|
'designbase-backdrop--blur': backdropBlur,
|
|
4674
4642
|
});
|
|
4675
4643
|
// Portal을 사용하여 body에 렌더링
|
|
4676
|
-
return $4AOtR$reactdom.createPortal(jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("div", { ref: backdropRef, className: backdropClasses, style: backdropStyle, onClick: handleBackdropClick, role: "presentation", "aria-hidden": !open }), jsxRuntimeExports.jsxs("div", { ref: forwardedRef, className: classes, style: sheetStyle, role: "dialog", "aria-modal": "true", "aria-labelledby": title ? 'bottom-sheet-title' : undefined, "aria-describedby": subtitle ? 'bottom-sheet-subtitle' : undefined, ...props, children: [!disableDrag && (jsxRuntimeExports.jsx("div", { className: "designbase-bottom-sheet__handle", onMouseDown: handleDragStart, onTouchStart: handleDragStart })), (title || header) && (jsxRuntimeExports.jsx("div", { ref: headerRef, className: "designbase-bottom-sheet__header", children: header || (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [title && (jsxRuntimeExports.jsx("h2", { id: "bottom-sheet-title", className: "designbase-bottom-sheet__title", children: title })), subtitle && (jsxRuntimeExports.jsx("p", { id: "bottom-sheet-subtitle", className: "designbase-bottom-sheet__subtitle", children: subtitle }))] })) })), jsxRuntimeExports.jsx("div", { className: "designbase-bottom-sheet__content", children: children }), footer && (jsxRuntimeExports.jsx("div", { className: "designbase-bottom-sheet__footer", children: footer }))] })] }), document.body);
|
|
4644
|
+
return $4AOtR$reactdom.createPortal(jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [showBackdrop && (jsxRuntimeExports.jsx("div", { ref: backdropRef, className: backdropClasses, style: backdropStyle, onClick: handleBackdropClick, role: "presentation", "aria-hidden": !open })), jsxRuntimeExports.jsxs("div", { ref: forwardedRef, className: classes, style: sheetStyle, role: "dialog", "aria-modal": "true", "aria-labelledby": title ? 'bottom-sheet-title' : undefined, "aria-describedby": subtitle ? 'bottom-sheet-subtitle' : undefined, ...props, children: [!disableDrag && (jsxRuntimeExports.jsx("div", { className: "designbase-bottom-sheet__handle", onMouseDown: handleDragStart, onTouchStart: handleDragStart })), (title || header) && (jsxRuntimeExports.jsx("div", { ref: headerRef, className: "designbase-bottom-sheet__header", children: header || (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [title && (jsxRuntimeExports.jsx("h2", { id: "bottom-sheet-title", className: "designbase-bottom-sheet__title", children: title })), subtitle && (jsxRuntimeExports.jsx("p", { id: "bottom-sheet-subtitle", className: "designbase-bottom-sheet__subtitle", children: subtitle }))] })) })), jsxRuntimeExports.jsx("div", { className: "designbase-bottom-sheet__content", children: children }), footer && (jsxRuntimeExports.jsx("div", { className: "designbase-bottom-sheet__footer", children: footer }))] })] }), document.body);
|
|
4677
4645
|
});
|
|
4678
4646
|
BottomSheet.displayName = 'BottomSheet';
|
|
4679
4647
|
|
|
@@ -4894,16 +4862,27 @@ const Input = React.forwardRef(({ type = 'text', label, placeholder, defaultValu
|
|
|
4894
4862
|
const inputId = React.useId();
|
|
4895
4863
|
const helperTextId = React.useId();
|
|
4896
4864
|
const errorMessageId = React.useId();
|
|
4865
|
+
const [showPassword, setShowPassword] = React.useState(false);
|
|
4897
4866
|
const handleChange = (e) => {
|
|
4898
4867
|
onChange?.(e.target.value);
|
|
4899
4868
|
};
|
|
4869
|
+
const handlePasswordToggle = () => {
|
|
4870
|
+
setShowPassword(!showPassword);
|
|
4871
|
+
};
|
|
4872
|
+
// 비밀번호 타입인 경우 토글 아이콘 사용
|
|
4873
|
+
const isPassword = type === 'password';
|
|
4874
|
+
const actualType = isPassword ? (showPassword ? 'text' : 'password') : type;
|
|
4875
|
+
const PasswordIcon = showPassword ? icons.HideIcon : icons.ShowIcon;
|
|
4876
|
+
// 검색 타입인 경우 자동으로 검색 아이콘 사용
|
|
4877
|
+
const isSearch = type === 'search';
|
|
4878
|
+
const actualStartIcon = isSearch ? icons.SearchIcon : StartIcon;
|
|
4900
4879
|
const classes = clsx('designbase-input', `designbase-input--${size}`, {
|
|
4901
4880
|
'designbase-input--error': error,
|
|
4902
4881
|
'designbase-input--disabled': disabled,
|
|
4903
4882
|
'designbase-input--readonly': readOnly,
|
|
4904
4883
|
'designbase-input--full-width': fullWidth,
|
|
4905
|
-
'designbase-input--with-start-icon':
|
|
4906
|
-
'designbase-input--with-end-icon': EndIcon,
|
|
4884
|
+
'designbase-input--with-start-icon': actualStartIcon,
|
|
4885
|
+
'designbase-input--with-end-icon': EndIcon || isPassword,
|
|
4907
4886
|
}, className);
|
|
4908
4887
|
const inputClasses = clsx('designbase-input__field', inputClassName);
|
|
4909
4888
|
const iconSizeMap = {
|
|
@@ -4912,7 +4891,7 @@ const Input = React.forwardRef(({ type = 'text', label, placeholder, defaultValu
|
|
|
4912
4891
|
lg: 18,
|
|
4913
4892
|
};
|
|
4914
4893
|
const iconSize = iconSizeMap[size];
|
|
4915
|
-
return (jsxRuntimeExports.jsxs("div", { className: classes, children: [label && (jsxRuntimeExports.jsx(Label, { htmlFor: inputId, required: required, error: error, disabled: disabled, size: size === 's' ? 'xs' : size === 'm' ? 's' : 'm', children: label })), jsxRuntimeExports.jsxs("div", { className: "designbase-input__wrapper", children: [
|
|
4894
|
+
return (jsxRuntimeExports.jsxs("div", { className: classes, children: [label && (jsxRuntimeExports.jsx(Label, { htmlFor: inputId, required: required, error: error, disabled: disabled, size: size === 's' ? 'xs' : size === 'm' ? 's' : 'm', children: label })), jsxRuntimeExports.jsxs("div", { className: "designbase-input__wrapper", children: [actualStartIcon && (jsxRuntimeExports.jsx("div", { className: "designbase-input__start-icon", children: React.createElement(actualStartIcon, { size: iconSize }) })), jsxRuntimeExports.jsx("input", { ...props, ref: forwardedRef, id: inputId, type: actualType, value: value, defaultValue: defaultValue, placeholder: placeholder, disabled: disabled, readOnly: readOnly, required: required, className: inputClasses, onChange: handleChange, onFocus: onFocus, onBlur: onBlur, "aria-describedby": clsx(helperText && helperTextId, error && errorMessage && errorMessageId), "aria-invalid": error }), isPassword && (jsxRuntimeExports.jsx("button", { type: "button", className: "designbase-input__password-toggle", onClick: handlePasswordToggle, disabled: disabled, "aria-label": showPassword ? '비밀번호 숨기기' : '비밀번호 보기', children: React.createElement(PasswordIcon, { size: iconSize }) })), EndIcon && !isPassword && (jsxRuntimeExports.jsx("div", { className: "designbase-input__end-icon", children: React.createElement(EndIcon, { size: iconSize }) }))] }), helperText && !error && (jsxRuntimeExports.jsx("p", { id: helperTextId, className: "designbase-input__helper-text", children: helperText })), error && errorMessage && (jsxRuntimeExports.jsx("p", { id: errorMessageId, className: "designbase-input__error-message", children: errorMessage }))] }));
|
|
4916
4895
|
});
|
|
4917
4896
|
Input.displayName = 'Input';
|
|
4918
4897
|
|
|
@@ -5563,7 +5542,7 @@ const Calendar = ({ currentDate = new Date(), view = 'month', events = [], onEve
|
|
|
5563
5542
|
}, title: event.title, children: event.title }, event.id));
|
|
5564
5543
|
}) }, index));
|
|
5565
5544
|
}) }, i));
|
|
5566
|
-
}) })] })] }))] }), jsxRuntimeExports.jsxs(Modal, { isOpen: showEventModal, onClose: handleCloseModal, title: isEditing ? '일정 편집' : '새 일정 추가', size: "
|
|
5545
|
+
}) })] })] }))] }), jsxRuntimeExports.jsxs(Modal, { isOpen: showEventModal, onClose: handleCloseModal, title: isEditing ? '일정 편집' : '새 일정 추가', size: "m", children: [jsxRuntimeExports.jsx(ModalBody, { children: jsxRuntimeExports.jsxs("div", { className: "designbase-calendar__event-form", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-calendar__form-row", children: [jsxRuntimeExports.jsx("label", { children: "\uC81C\uBAA9 *" }), jsxRuntimeExports.jsx(Input, { value: eventForm.title, onChange: (value) => setEventForm(prev => ({ ...prev, title: value })), placeholder: "\uC77C\uC815 \uC81C\uBAA9\uC744 \uC785\uB825\uD558\uC138\uC694" })] }), jsxRuntimeExports.jsxs("div", { className: "designbase-calendar__form-row", children: [jsxRuntimeExports.jsx("label", { children: "\uC124\uBA85" }), jsxRuntimeExports.jsx(Input, { value: eventForm.description, onChange: (value) => setEventForm(prev => ({ ...prev, description: value })), placeholder: "\uC77C\uC815 \uC124\uBA85\uC744 \uC785\uB825\uD558\uC138\uC694" })] }), jsxRuntimeExports.jsxs("div", { className: "designbase-calendar__form-row", children: [jsxRuntimeExports.jsx("label", { children: "\uB0A0\uC9DC" }), jsxRuntimeExports.jsx(Input, { type: "date", value: selectedDate.toISOString().split('T')[0], onChange: (value) => {
|
|
5567
5546
|
const newDate = new Date(value);
|
|
5568
5547
|
setSelectedDate(newDate);
|
|
5569
5548
|
} })] }), jsxRuntimeExports.jsxs("div", { className: "designbase-calendar__form-row", children: [jsxRuntimeExports.jsx("label", { children: "\uC77C\uC815 \uD0C0\uC785" }), jsxRuntimeExports.jsx(Select, { value: eventForm.type, onChange: (value) => {
|
|
@@ -5682,23 +5661,20 @@ const Image$1 = ({ src, alt = '', ratio = 'auto', fit = 'cover', loading = 'lazy
|
|
|
5682
5661
|
return (jsxRuntimeExports.jsxs("div", { className: classes, style: containerStyle, onClick: onClick, role: onClick ? 'button' : undefined, tabIndex: onClick ? 0 : undefined, children: [showPlaceholder && (jsxRuntimeExports.jsxs("div", { className: placeholderClasses, children: [placeholder === 'skeleton' && (jsxRuntimeExports.jsx("div", { className: "designbase-image__skeleton" })), placeholder === 'blur' && (jsxRuntimeExports.jsx("div", { className: "designbase-image__blur" }))] })), imageState === 'error' && (jsxRuntimeExports.jsxs("div", { className: "designbase-image__error", children: [jsxRuntimeExports.jsx(icons.GalleryIcon, { size: errorIconSize, color: "currentColor" }), jsxRuntimeExports.jsx("span", { className: "designbase-image__error-text", children: alt || '이미지를 불러올 수 없습니다' })] })), jsxRuntimeExports.jsx("img", { ref: imageRef, src: currentSrc, alt: alt, loading: loading, className: imageClasses, onLoad: handleLoad, onError: handleError, draggable: false })] }));
|
|
5683
5662
|
};
|
|
5684
5663
|
|
|
5685
|
-
const Card = React.forwardRef(({ title, subtitle, description, children, image, icon: Icon, actions = [], tags = [], meta, variant = 'default', size = 'm', layout = 'vertical', imagePosition = 'top', fullWidth = false, clickable = false, hoverable = false, selectable = false, selected = false, disabled = false, loading = false, className, style, onClick, onSelect, onAction, }, ref)
|
|
5686
|
-
// 아이콘 크기
|
|
5687
|
-
const iconSize = size === 's' ? 16 : size === 'l' ?
|
|
5664
|
+
const Card = React.forwardRef(function Card({ title, subtitle, description, children, image, icon: Icon, actions = [], tags = [], meta, variant = 'default', size = 'm', layout = 'vertical', imagePosition = 'top', fullWidth = false, clickable = false, hoverable = false, selectable = false, selected = false, disabled = false, loading = false, className, style, onClick, onSelect, onAction, }, ref) {
|
|
5665
|
+
// 아이콘 크기(과도하지 않게 정규화)
|
|
5666
|
+
const iconSize = size === 's' ? 16 : size === 'l' ? 22 : size === 'xl' ? 24 : 18;
|
|
5688
5667
|
const handleClick = () => {
|
|
5689
|
-
if (!disabled && !loading && onClick)
|
|
5668
|
+
if (!disabled && !loading && onClick)
|
|
5690
5669
|
onClick();
|
|
5691
|
-
}
|
|
5692
5670
|
};
|
|
5693
5671
|
const handleSelect = () => {
|
|
5694
|
-
if (!disabled && !loading && selectable && onSelect)
|
|
5672
|
+
if (!disabled && !loading && selectable && onSelect)
|
|
5695
5673
|
onSelect(!selected);
|
|
5696
|
-
}
|
|
5697
5674
|
};
|
|
5698
5675
|
const handleActionClick = (action, index) => {
|
|
5699
|
-
if (!disabled && !loading && action.onClick)
|
|
5676
|
+
if (!disabled && !loading && action.onClick)
|
|
5700
5677
|
action.onClick();
|
|
5701
|
-
}
|
|
5702
5678
|
onAction?.(action, index);
|
|
5703
5679
|
};
|
|
5704
5680
|
const classes = clsx('designbase-card', `designbase-card--${variant}`, `designbase-card--${size}`, `designbase-card--${layout}`, {
|
|
@@ -5709,51 +5685,63 @@ const Card = React.forwardRef(({ title, subtitle, description, children, image,
|
|
|
5709
5685
|
'designbase-card--selected': selected,
|
|
5710
5686
|
'designbase-card--disabled': disabled,
|
|
5711
5687
|
'designbase-card--loading': loading,
|
|
5712
|
-
'designbase-card--with-image': image,
|
|
5713
|
-
[`designbase-card--image-${imagePosition}`]: image,
|
|
5688
|
+
'designbase-card--with-image': !!image,
|
|
5689
|
+
[`designbase-card--image-${imagePosition}`]: !!image,
|
|
5714
5690
|
}, className);
|
|
5715
|
-
// 이미지 렌더링
|
|
5716
5691
|
const renderImage = () => {
|
|
5717
5692
|
if (!image)
|
|
5718
5693
|
return null;
|
|
5719
5694
|
const imageClasses = clsx('designbase-card__image', `designbase-card__image--${imagePosition}`);
|
|
5720
5695
|
return (jsxRuntimeExports.jsx("div", { className: imageClasses, children: jsxRuntimeExports.jsx(Image$1, { src: image.src, alt: image.alt || title || 'Card image', ratio: image.ratio, fit: image.fit, loading: image.loading, placeholder: image.placeholder, onClick: image.onClick, className: "designbase-card__image-element" }) }));
|
|
5721
5696
|
};
|
|
5722
|
-
// 아이콘 렌더링
|
|
5723
5697
|
const renderIcon = () => {
|
|
5724
5698
|
if (!Icon)
|
|
5725
5699
|
return null;
|
|
5726
|
-
return (jsxRuntimeExports.jsx("div", { className: "designbase-card__icon", children: jsxRuntimeExports.jsx(Icon, { size: iconSize, color: "currentColor", className: "designbase-card__icon-element" }) }));
|
|
5700
|
+
return (jsxRuntimeExports.jsx("div", { className: "designbase-card__icon", "aria-hidden": "true", children: jsxRuntimeExports.jsx(Icon, { size: iconSize, color: "currentColor", className: "designbase-card__icon-element" }) }));
|
|
5727
5701
|
};
|
|
5728
|
-
// 태그 렌더링
|
|
5729
5702
|
const renderTags = () => {
|
|
5730
|
-
if (tags
|
|
5703
|
+
if (!tags?.length)
|
|
5731
5704
|
return null;
|
|
5732
|
-
return (jsxRuntimeExports.jsx("div", { className: "designbase-card__tags", children: tags.map((tag,
|
|
5705
|
+
return (jsxRuntimeExports.jsx("div", { className: "designbase-card__tags", children: tags.map((tag, i) => (jsxRuntimeExports.jsx("span", { className: "designbase-card__tag", children: tag }, i))) }));
|
|
5733
5706
|
};
|
|
5734
|
-
// 메타 정보 렌더링
|
|
5735
5707
|
const renderMeta = () => {
|
|
5736
5708
|
if (!meta)
|
|
5737
5709
|
return null;
|
|
5738
|
-
|
|
5710
|
+
const items = [];
|
|
5711
|
+
if (meta.author)
|
|
5712
|
+
items.push(['작성자', meta.author]);
|
|
5713
|
+
if (meta.date)
|
|
5714
|
+
items.push(['날짜', meta.date]);
|
|
5715
|
+
if (typeof meta.views === 'number')
|
|
5716
|
+
items.push(['조회수', meta.views]);
|
|
5717
|
+
if (typeof meta.likes === 'number')
|
|
5718
|
+
items.push(['좋아요', meta.likes]);
|
|
5719
|
+
if (typeof meta.comments === 'number')
|
|
5720
|
+
items.push(['댓글', meta.comments]);
|
|
5721
|
+
if (meta.custom) {
|
|
5722
|
+
Object.entries(meta.custom).forEach(([key, value]) => {
|
|
5723
|
+
items.push([key, value]);
|
|
5724
|
+
});
|
|
5725
|
+
}
|
|
5726
|
+
if (!items.length)
|
|
5727
|
+
return null;
|
|
5728
|
+
return (jsxRuntimeExports.jsx("div", { className: "designbase-card__meta", children: items.map(([label, value], idx) => (jsxRuntimeExports.jsxs("span", { className: "designbase-card__meta-item", children: [jsxRuntimeExports.jsx("span", { className: "designbase-card__meta-label", children: label }), jsxRuntimeExports.jsx("span", { className: "designbase-card__meta-sep", children: "\u00B7" }), jsxRuntimeExports.jsx("span", { className: "designbase-card__meta-value", children: value })] }, `${label}-${idx}`))) }));
|
|
5739
5729
|
};
|
|
5740
|
-
// 액션 버튼들 렌더링
|
|
5741
5730
|
const renderActions = () => {
|
|
5742
|
-
if (actions
|
|
5731
|
+
if (!actions?.length)
|
|
5743
5732
|
return null;
|
|
5744
5733
|
return (jsxRuntimeExports.jsx("div", { className: "designbase-card__actions", children: actions.map((action, index) => {
|
|
5745
5734
|
const ActionIcon = action.icon;
|
|
5746
|
-
return (jsxRuntimeExports.jsx(Button, { variant: action.variant || 'primary', size: action.size || 's', onClick: () => handleActionClick(action, index), disabled: action.disabled || disabled, loading: action.loading, icon: ActionIcon, children: action.label }, index));
|
|
5735
|
+
return (jsxRuntimeExports.jsx(Button, { variant: action.variant || 'primary', size: action.size || 's', onClick: () => handleActionClick(action, index), disabled: action.disabled || disabled, loading: !!action.loading, icon: ActionIcon, children: action.label }, index));
|
|
5747
5736
|
}) }));
|
|
5748
5737
|
};
|
|
5749
|
-
|
|
5750
|
-
const renderContent = () => (jsxRuntimeExports.jsxs("div", { className: "designbase-card__content", children: [renderIcon(), (title || subtitle) && (jsxRuntimeExports.jsxs("div", { className: "designbase-card__header", children: [title && jsxRuntimeExports.jsx("h3", { className: "designbase-card__title", children: title }), subtitle && jsxRuntimeExports.jsx("h4", { className: "designbase-card__subtitle", children: subtitle })] })), description && (jsxRuntimeExports.jsx("p", { className: "designbase-card__description", children: description })), children && (jsxRuntimeExports.jsx("div", { className: "designbase-card__body", children: children })), renderTags(), renderMeta(), renderActions()] }));
|
|
5738
|
+
const renderContent = () => (jsxRuntimeExports.jsxs("div", { className: "designbase-card__content", children: [renderIcon(), (title || subtitle) && (jsxRuntimeExports.jsxs("div", { className: "designbase-card__header", children: [title && jsxRuntimeExports.jsx("h3", { className: "designbase-card__title", children: title }), subtitle && jsxRuntimeExports.jsx("p", { className: "designbase-card__subtitle", children: subtitle })] })), description && jsxRuntimeExports.jsx("p", { className: "designbase-card__description", children: description }), children && jsxRuntimeExports.jsx("div", { className: "designbase-card__body", children: children }), renderTags(), renderMeta(), renderActions()] }));
|
|
5751
5739
|
return (jsxRuntimeExports.jsxs("div", { ref: ref, className: classes, style: style, onClick: clickable ? handleClick : undefined, onKeyDown: (e) => {
|
|
5752
5740
|
if (clickable && (e.key === 'Enter' || e.key === ' ')) {
|
|
5753
5741
|
e.preventDefault();
|
|
5754
5742
|
handleClick();
|
|
5755
5743
|
}
|
|
5756
|
-
}, tabIndex: clickable ? 0 : undefined, role: clickable ? 'button' : undefined, "aria-label": clickable ? title : undefined, children: [selectable && (jsxRuntimeExports.jsx("div", { className: "designbase-card__select-overlay", onClick: handleSelect, children: jsxRuntimeExports.jsx("input", { type: "checkbox", checked: selected, onChange: handleSelect, className: "designbase-card__select-input" }) })), loading && (jsxRuntimeExports.jsx("div", { className: "designbase-card__loading-overlay", children: jsxRuntimeExports.jsx("div", { className: "designbase-card__loading-spinner" }) })), imagePosition === 'background' ? (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [renderImage(), jsxRuntimeExports.jsx("div", { className: "designbase-card__overlay", children: renderContent() })] })) : (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [imagePosition === 'top'
|
|
5744
|
+
}, tabIndex: clickable ? 0 : undefined, role: clickable ? 'button' : undefined, "aria-label": clickable ? title : undefined, "aria-disabled": disabled || loading ? true : undefined, children: [selectable && (jsxRuntimeExports.jsx("div", { className: "designbase-card__select-overlay", onClick: handleSelect, children: jsxRuntimeExports.jsx("input", { type: "checkbox", checked: selected, onChange: handleSelect, className: "designbase-card__select-input", "aria-label": "\uCE74\uB4DC \uC120\uD0DD" }) })), loading && (jsxRuntimeExports.jsx("div", { className: "designbase-card__loading-overlay", "aria-live": "polite", "aria-busy": "true", children: jsxRuntimeExports.jsx("div", { className: "designbase-card__loading-spinner" }) })), imagePosition === 'background' ? (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [renderImage(), jsxRuntimeExports.jsx("div", { className: "designbase-card__overlay", children: renderContent() })] })) : (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [(imagePosition === 'top' || imagePosition === 'left') && renderImage(), renderContent(), (imagePosition === 'bottom' || imagePosition === 'right') && renderImage()] }))] }));
|
|
5757
5745
|
});
|
|
5758
5746
|
Card.displayName = 'Card';
|
|
5759
5747
|
|
|
@@ -5915,28 +5903,26 @@ const Carousel = ({ items, size = 'm', variant = 'default', theme = 'light', tra
|
|
|
5915
5903
|
const handleTouchMove = React.useCallback((event) => {
|
|
5916
5904
|
if (!isDragging || !enableTouch || disabled || readonly || !touchStartRef.current)
|
|
5917
5905
|
return;
|
|
5918
|
-
event.preventDefault();
|
|
5919
5906
|
const clientX = 'touches' in event ? event.touches[0].clientX : event.clientX;
|
|
5920
5907
|
const clientY = 'touches' in event ? event.touches[0].clientY : event.clientY;
|
|
5921
5908
|
const deltaX = clientX - touchStartRef.current.x;
|
|
5922
5909
|
const deltaY = clientY - touchStartRef.current.y;
|
|
5923
5910
|
// 수평 스와이프만 처리 (수직 스크롤 방지)
|
|
5924
5911
|
if (Math.abs(deltaX) > Math.abs(deltaY)) {
|
|
5912
|
+
event.preventDefault();
|
|
5925
5913
|
setDragOffset(deltaX);
|
|
5926
5914
|
}
|
|
5927
5915
|
}, [isDragging, enableTouch, disabled, readonly]);
|
|
5928
5916
|
// 터치/드래그 종료
|
|
5929
5917
|
const handleTouchEnd = React.useCallback(() => {
|
|
5930
|
-
if (!
|
|
5918
|
+
if (!enableTouch || disabled || readonly || !touchStartRef.current)
|
|
5931
5919
|
return;
|
|
5932
5920
|
const endTime = Date.now();
|
|
5933
5921
|
const duration = endTime - touchStartRef.current.time;
|
|
5934
|
-
const velocity = Math.abs(dragOffset) / duration;
|
|
5935
|
-
setIsDragging(false);
|
|
5936
|
-
touchStartRef.current = null;
|
|
5937
|
-
// 속도나 거리에 따라 슬라이드 변경
|
|
5922
|
+
const velocity = Math.abs(dragOffset) / Math.max(duration, 1);
|
|
5938
5923
|
const threshold = swipeSensitivity;
|
|
5939
5924
|
const velocityThreshold = 0.5;
|
|
5925
|
+
// 속도나 거리에 따라 슬라이드 변경
|
|
5940
5926
|
if (Math.abs(dragOffset) > threshold || velocity > velocityThreshold) {
|
|
5941
5927
|
if (dragOffset > 0) {
|
|
5942
5928
|
goToPrevious();
|
|
@@ -5945,8 +5931,11 @@ const Carousel = ({ items, size = 'm', variant = 'default', theme = 'light', tra
|
|
|
5945
5931
|
goToNext();
|
|
5946
5932
|
}
|
|
5947
5933
|
}
|
|
5934
|
+
// 상태 초기화
|
|
5935
|
+
setIsDragging(false);
|
|
5948
5936
|
setDragOffset(0);
|
|
5949
|
-
|
|
5937
|
+
touchStartRef.current = null;
|
|
5938
|
+
}, [enableTouch, disabled, readonly, dragOffset, swipeSensitivity, goToPrevious, goToNext]);
|
|
5950
5939
|
// 마우스 휠 핸들러
|
|
5951
5940
|
const handleWheel = React.useCallback((event) => {
|
|
5952
5941
|
if (!enableWheel || disabled || readonly)
|
|
@@ -6015,11 +6004,13 @@ const Carousel = ({ items, size = 'm', variant = 'default', theme = 'light', tra
|
|
|
6015
6004
|
const currentItem = items[currentIndex];
|
|
6016
6005
|
// 슬라이드 스타일 계산
|
|
6017
6006
|
const getSlideStyle = () => {
|
|
6018
|
-
const
|
|
6007
|
+
const containerWidth = containerRef.current?.offsetWidth || 1;
|
|
6008
|
+
const translateX = -(currentIndex * (100 / itemsPerView)) + (dragOffset / containerWidth) * (100 / itemsPerView);
|
|
6019
6009
|
return {
|
|
6020
6010
|
transform: `translateX(${translateX}%)`,
|
|
6021
6011
|
gap: `${gap}px`,
|
|
6022
|
-
transition: isDragging ? 'none' : `transform ${transitionDuration}ms
|
|
6012
|
+
transition: isDragging ? 'none' : `transform ${transitionDuration}ms cubic-bezier(0.4, 0.0, 0.2, 1)`,
|
|
6013
|
+
willChange: isDragging ? 'transform' : 'auto',
|
|
6023
6014
|
};
|
|
6024
6015
|
};
|
|
6025
6016
|
// 인디케이터 활성 상태 확인
|
|
@@ -6040,7 +6031,7 @@ const Carousel = ({ items, size = 'm', variant = 'default', theme = 'light', tra
|
|
|
6040
6031
|
'designbase-carousel--dragging': isDragging,
|
|
6041
6032
|
'designbase-carousel--fullscreen': isFullscreen,
|
|
6042
6033
|
'designbase-carousel--transitioning': isTransitioning,
|
|
6043
|
-
}, className), children: [jsxRuntimeExports.jsxs("div", { ref: containerRef, className: "designbase-carousel__container", onWheel: handleWheel, onMouseDown: handleMouseDown, onMouseMove: handleMouseMove, onMouseUp: handleMouseUp, onMouseLeave: handleMouseUp, onTouchStart: handleTouchStartEvent, onTouchMove: handleTouchMoveEvent, onTouchEnd: handleTouchEndEvent, children: [jsxRuntimeExports.jsx("div", { ref: trackRef, className: "designbase-carousel__track", style: getSlideStyle(), children: items.map((item, index) => (jsxRuntimeExports.jsxs("div", { className: clsx('designbase-carousel__slide', {
|
|
6034
|
+
}, className), children: [jsxRuntimeExports.jsxs("div", { ref: containerRef, className: "designbase-carousel__container", onWheel: handleWheel, onMouseDown: handleMouseDown, onMouseMove: handleMouseMove, onMouseUp: handleMouseUp, onMouseLeave: handleMouseUp, onTouchStart: handleTouchStartEvent, onTouchMove: handleTouchMoveEvent, onTouchEnd: handleTouchEndEvent, onTouchCancel: handleTouchEndEvent, children: [jsxRuntimeExports.jsx("div", { ref: trackRef, className: "designbase-carousel__track", style: getSlideStyle(), children: items.map((item, index) => (jsxRuntimeExports.jsxs("div", { className: clsx('designbase-carousel__slide', {
|
|
6044
6035
|
'designbase-carousel__slide--active': index === currentIndex,
|
|
6045
6036
|
}), style: {
|
|
6046
6037
|
width: `${100 / itemsPerView}%`,
|
|
@@ -6115,7 +6106,278 @@ const Chip = ({ label, size = 'm', variant = 'default', color = 'primary', delet
|
|
|
6115
6106
|
};
|
|
6116
6107
|
Chip.displayName = 'Chip';
|
|
6117
6108
|
|
|
6118
|
-
const
|
|
6109
|
+
const ColorPicker$1 = ({ size = 'm', type = 'dropdown', value, defaultValue = '#006FFF', showInput = true, showAlpha = false, showFormatSelector = true, showCopyButton = true, disabled = false, readonly = false, onChange, className, }) => {
|
|
6110
|
+
const [selectedColor, setSelectedColor] = React.useState(value || defaultValue);
|
|
6111
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
6112
|
+
const [hue, setHue] = React.useState(211);
|
|
6113
|
+
const [saturation, setSaturation] = React.useState(100);
|
|
6114
|
+
const [lightness, setLightness] = React.useState(50);
|
|
6115
|
+
const [alpha, setAlpha] = React.useState(100);
|
|
6116
|
+
const [colorFormat, setColorFormat] = React.useState('hex');
|
|
6117
|
+
const [isCopied, setIsCopied] = React.useState(false);
|
|
6118
|
+
const [inputValue, setInputValue] = React.useState(defaultValue);
|
|
6119
|
+
const pickerRef = React.useRef(null);
|
|
6120
|
+
// value prop이 변경되면 업데이트
|
|
6121
|
+
React.useEffect(() => {
|
|
6122
|
+
if (value) {
|
|
6123
|
+
setSelectedColor(value);
|
|
6124
|
+
updateHSLFromHex(value);
|
|
6125
|
+
}
|
|
6126
|
+
}, [value]);
|
|
6127
|
+
// HEX를 HSL로 변환
|
|
6128
|
+
const updateHSLFromHex = (hex) => {
|
|
6129
|
+
const rgb = hexToRgb(hex);
|
|
6130
|
+
if (rgb) {
|
|
6131
|
+
const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
|
|
6132
|
+
setHue(hsl.h);
|
|
6133
|
+
setSaturation(hsl.s);
|
|
6134
|
+
setLightness(hsl.l);
|
|
6135
|
+
}
|
|
6136
|
+
};
|
|
6137
|
+
// HEX를 RGB로 변환
|
|
6138
|
+
const hexToRgb = (hex) => {
|
|
6139
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
6140
|
+
return result ? {
|
|
6141
|
+
r: parseInt(result[1], 16),
|
|
6142
|
+
g: parseInt(result[2], 16),
|
|
6143
|
+
b: parseInt(result[3], 16)
|
|
6144
|
+
} : null;
|
|
6145
|
+
};
|
|
6146
|
+
// RGB를 HSL로 변환
|
|
6147
|
+
const rgbToHsl = (r, g, b) => {
|
|
6148
|
+
r /= 255;
|
|
6149
|
+
g /= 255;
|
|
6150
|
+
b /= 255;
|
|
6151
|
+
const max = Math.max(r, g, b), min = Math.min(r, g, b);
|
|
6152
|
+
let h = 0, s = 0, l = (max + min) / 2;
|
|
6153
|
+
if (max !== min) {
|
|
6154
|
+
const d = max - min;
|
|
6155
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
6156
|
+
switch (max) {
|
|
6157
|
+
case r:
|
|
6158
|
+
h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
6159
|
+
break;
|
|
6160
|
+
case g:
|
|
6161
|
+
h = ((b - r) / d + 2) / 6;
|
|
6162
|
+
break;
|
|
6163
|
+
case b:
|
|
6164
|
+
h = ((r - g) / d + 4) / 6;
|
|
6165
|
+
break;
|
|
6166
|
+
}
|
|
6167
|
+
}
|
|
6168
|
+
return { h: Math.round(h * 360), s: Math.round(s * 100), l: Math.round(l * 100) };
|
|
6169
|
+
};
|
|
6170
|
+
// HSL을 RGB로 변환
|
|
6171
|
+
const hslToRgb = (h, s, l) => {
|
|
6172
|
+
l /= 100;
|
|
6173
|
+
const a = s * Math.min(l, 1 - l) / 100;
|
|
6174
|
+
const f = (n) => {
|
|
6175
|
+
const k = (n + h / 30) % 12;
|
|
6176
|
+
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
|
|
6177
|
+
return Math.round(255 * color);
|
|
6178
|
+
};
|
|
6179
|
+
return { r: f(0), g: f(8), b: f(4) };
|
|
6180
|
+
};
|
|
6181
|
+
// HSL을 HEX로 변환
|
|
6182
|
+
const hslToHex = (h, s, l) => {
|
|
6183
|
+
const rgb = hslToRgb(h, s, l);
|
|
6184
|
+
const toHex = (n) => n.toString(16).padStart(2, '0');
|
|
6185
|
+
return `#${toHex(rgb.r)}${toHex(rgb.g)}${toHex(rgb.b)}`.toUpperCase();
|
|
6186
|
+
};
|
|
6187
|
+
// 현재 포맷에 따른 색상 문자열 반환
|
|
6188
|
+
const getFormattedColor = () => {
|
|
6189
|
+
const rgb = hslToRgb(hue, saturation, lightness);
|
|
6190
|
+
const alphaValue = alpha / 100;
|
|
6191
|
+
switch (colorFormat) {
|
|
6192
|
+
case 'hex':
|
|
6193
|
+
return hslToHex(hue, saturation, lightness);
|
|
6194
|
+
case 'rgb':
|
|
6195
|
+
return `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`;
|
|
6196
|
+
case 'rgba':
|
|
6197
|
+
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alphaValue.toFixed(2)})`;
|
|
6198
|
+
case 'hsl':
|
|
6199
|
+
return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
|
|
6200
|
+
case 'hsla':
|
|
6201
|
+
return `hsla(${hue}, ${saturation}%, ${lightness}%, ${alphaValue.toFixed(2)})`;
|
|
6202
|
+
default:
|
|
6203
|
+
return hslToHex(hue, saturation, lightness);
|
|
6204
|
+
}
|
|
6205
|
+
};
|
|
6206
|
+
// 색상 변경 핸들러
|
|
6207
|
+
const handleColorChange = (newColor) => {
|
|
6208
|
+
setSelectedColor(newColor);
|
|
6209
|
+
onChange?.(newColor);
|
|
6210
|
+
};
|
|
6211
|
+
// Hue 슬라이더 변경
|
|
6212
|
+
const handleHueChange = (e) => {
|
|
6213
|
+
const newHue = parseInt(e.target.value);
|
|
6214
|
+
setHue(newHue);
|
|
6215
|
+
const newColor = hslToHex(newHue, saturation, lightness);
|
|
6216
|
+
handleColorChange(newColor);
|
|
6217
|
+
};
|
|
6218
|
+
// Saturation 슬라이더 변경
|
|
6219
|
+
const handleSaturationChange = (e) => {
|
|
6220
|
+
const newSaturation = parseInt(e.target.value);
|
|
6221
|
+
setSaturation(newSaturation);
|
|
6222
|
+
const newColor = hslToHex(hue, newSaturation, lightness);
|
|
6223
|
+
handleColorChange(newColor);
|
|
6224
|
+
};
|
|
6225
|
+
// Lightness 슬라이더 변경
|
|
6226
|
+
const handleLightnessChange = (e) => {
|
|
6227
|
+
const newLightness = parseInt(e.target.value);
|
|
6228
|
+
setLightness(newLightness);
|
|
6229
|
+
const newColor = hslToHex(hue, saturation, newLightness);
|
|
6230
|
+
handleColorChange(newColor);
|
|
6231
|
+
};
|
|
6232
|
+
// Alpha 슬라이더 변경
|
|
6233
|
+
const handleAlphaChange = (e) => {
|
|
6234
|
+
const newAlpha = parseInt(e.target.value);
|
|
6235
|
+
setAlpha(newAlpha);
|
|
6236
|
+
const newColor = getFormattedColorWithAlpha(newAlpha);
|
|
6237
|
+
handleColorChange(newColor);
|
|
6238
|
+
};
|
|
6239
|
+
// Alpha 값을 포함한 색상 문자열 반환
|
|
6240
|
+
const getFormattedColorWithAlpha = (alphaVal) => {
|
|
6241
|
+
const rgb = hslToRgb(hue, saturation, lightness);
|
|
6242
|
+
const alphaValue = alphaVal / 100;
|
|
6243
|
+
if (colorFormat === 'rgba') {
|
|
6244
|
+
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alphaValue.toFixed(2)})`;
|
|
6245
|
+
}
|
|
6246
|
+
else if (colorFormat === 'hsla') {
|
|
6247
|
+
return `hsla(${hue}, ${saturation}%, ${lightness}%, ${alphaValue.toFixed(2)})`;
|
|
6248
|
+
}
|
|
6249
|
+
return hslToHex(hue, saturation, lightness);
|
|
6250
|
+
};
|
|
6251
|
+
// 입력 필드 변경
|
|
6252
|
+
const handleInputChange = (e) => {
|
|
6253
|
+
const newValue = e.target.value.trim();
|
|
6254
|
+
setInputValue(newValue);
|
|
6255
|
+
};
|
|
6256
|
+
// 입력 필드에서 포커스 아웃 시 검증 및 적용
|
|
6257
|
+
const handleInputBlur = () => {
|
|
6258
|
+
let isValid = false;
|
|
6259
|
+
// HEX 검증
|
|
6260
|
+
if (/^#[0-9A-F]{6}$/i.test(inputValue)) {
|
|
6261
|
+
handleColorChange(inputValue.toUpperCase());
|
|
6262
|
+
updateHSLFromHex(inputValue);
|
|
6263
|
+
isValid = true;
|
|
6264
|
+
}
|
|
6265
|
+
// RGB 검증
|
|
6266
|
+
else if (/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i.test(inputValue)) {
|
|
6267
|
+
const match = inputValue.match(/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i);
|
|
6268
|
+
if (match) {
|
|
6269
|
+
const r = parseInt(match[1]);
|
|
6270
|
+
const g = parseInt(match[2]);
|
|
6271
|
+
const b = parseInt(match[3]);
|
|
6272
|
+
if (r <= 255 && g <= 255 && b <= 255) {
|
|
6273
|
+
const hsl = rgbToHsl(r, g, b);
|
|
6274
|
+
setHue(hsl.h);
|
|
6275
|
+
setSaturation(hsl.s);
|
|
6276
|
+
setLightness(hsl.l);
|
|
6277
|
+
const hex = hslToHex(hsl.h, hsl.s, hsl.l);
|
|
6278
|
+
handleColorChange(hex);
|
|
6279
|
+
isValid = true;
|
|
6280
|
+
}
|
|
6281
|
+
}
|
|
6282
|
+
}
|
|
6283
|
+
// RGBA 검증
|
|
6284
|
+
else if (/^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d.]+)\s*\)$/i.test(inputValue)) {
|
|
6285
|
+
const match = inputValue.match(/^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d.]+)\s*\)$/i);
|
|
6286
|
+
if (match) {
|
|
6287
|
+
const r = parseInt(match[1]);
|
|
6288
|
+
const g = parseInt(match[2]);
|
|
6289
|
+
const b = parseInt(match[3]);
|
|
6290
|
+
const a = parseFloat(match[4]);
|
|
6291
|
+
if (r <= 255 && g <= 255 && b <= 255 && a >= 0 && a <= 1) {
|
|
6292
|
+
const hsl = rgbToHsl(r, g, b);
|
|
6293
|
+
setHue(hsl.h);
|
|
6294
|
+
setSaturation(hsl.s);
|
|
6295
|
+
setLightness(hsl.l);
|
|
6296
|
+
setAlpha(Math.round(a * 100));
|
|
6297
|
+
const hex = hslToHex(hsl.h, hsl.s, hsl.l);
|
|
6298
|
+
handleColorChange(hex);
|
|
6299
|
+
isValid = true;
|
|
6300
|
+
}
|
|
6301
|
+
}
|
|
6302
|
+
}
|
|
6303
|
+
// 유효하지 않으면 이전 값으로 복원
|
|
6304
|
+
if (!isValid) {
|
|
6305
|
+
setInputValue(getFormattedColor());
|
|
6306
|
+
}
|
|
6307
|
+
};
|
|
6308
|
+
// 복사 기능
|
|
6309
|
+
const handleCopy = async () => {
|
|
6310
|
+
try {
|
|
6311
|
+
await navigator.clipboard.writeText(getFormattedColor());
|
|
6312
|
+
setIsCopied(true);
|
|
6313
|
+
setTimeout(() => setIsCopied(false), 2000);
|
|
6314
|
+
}
|
|
6315
|
+
catch (err) {
|
|
6316
|
+
console.error('Failed to copy:', err);
|
|
6317
|
+
}
|
|
6318
|
+
};
|
|
6319
|
+
// 포맷 변경 시 inputValue 업데이트
|
|
6320
|
+
React.useEffect(() => {
|
|
6321
|
+
setInputValue(getFormattedColor());
|
|
6322
|
+
}, [colorFormat, hue, saturation, lightness, alpha]);
|
|
6323
|
+
// 외부 클릭 감지
|
|
6324
|
+
React.useEffect(() => {
|
|
6325
|
+
const handleClickOutside = (event) => {
|
|
6326
|
+
if (pickerRef.current && !pickerRef.current.contains(event.target)) {
|
|
6327
|
+
setIsOpen(false);
|
|
6328
|
+
}
|
|
6329
|
+
};
|
|
6330
|
+
if (isOpen && type === 'dropdown') {
|
|
6331
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
6332
|
+
}
|
|
6333
|
+
return () => {
|
|
6334
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
6335
|
+
};
|
|
6336
|
+
}, [isOpen, type]);
|
|
6337
|
+
const togglePicker = () => {
|
|
6338
|
+
if (disabled || readonly)
|
|
6339
|
+
return;
|
|
6340
|
+
setIsOpen(!isOpen);
|
|
6341
|
+
};
|
|
6342
|
+
const classes = clsx('designbase-color-picker', `designbase-color-picker--${size}`, {
|
|
6343
|
+
'designbase-color-picker--disabled': disabled,
|
|
6344
|
+
'designbase-color-picker--readonly': readonly,
|
|
6345
|
+
'designbase-color-picker--open': isOpen,
|
|
6346
|
+
'designbase-color-picker--no-input': !showInput,
|
|
6347
|
+
}, className);
|
|
6348
|
+
// 컬러 선택 UI
|
|
6349
|
+
const renderColorSelector = () => (jsxRuntimeExports.jsxs("div", { className: "designbase-color-picker__selector", children: [showFormatSelector && (jsxRuntimeExports.jsx("div", { className: "designbase-color-picker__format-selector", children: ['hex', 'rgb', 'rgba', 'hsl', 'hsla'].map((format) => (jsxRuntimeExports.jsx("button", { type: "button", className: clsx('designbase-color-picker__format-button', { 'designbase-color-picker__format-button--active': colorFormat === format }), onClick: () => setColorFormat(format), children: format.toUpperCase() }, format))) })), jsxRuntimeExports.jsxs("div", { className: "designbase-color-picker__sliders", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-color-picker__slider-group", children: [jsxRuntimeExports.jsxs("label", { className: "designbase-color-picker__slider-label", children: ["Hue ", jsxRuntimeExports.jsxs("span", { className: "designbase-color-picker__slider-value", children: [hue, "\u00B0"] })] }), jsxRuntimeExports.jsx("input", { type: "range", min: "0", max: "360", value: hue, onChange: handleHueChange, className: "designbase-color-picker__slider designbase-color-picker__slider--hue" })] }), jsxRuntimeExports.jsxs("div", { className: "designbase-color-picker__slider-group", children: [jsxRuntimeExports.jsxs("label", { className: "designbase-color-picker__slider-label", children: ["Saturation ", jsxRuntimeExports.jsxs("span", { className: "designbase-color-picker__slider-value", children: [saturation, "%"] })] }), jsxRuntimeExports.jsx("input", { type: "range", min: "0", max: "100", value: saturation, onChange: handleSaturationChange, className: "designbase-color-picker__slider designbase-color-picker__slider--saturation", style: {
|
|
6350
|
+
background: `linear-gradient(to right,
|
|
6351
|
+
hsl(${hue}, 0%, ${lightness}%),
|
|
6352
|
+
hsl(${hue}, 100%, ${lightness}%))`
|
|
6353
|
+
} })] }), jsxRuntimeExports.jsxs("div", { className: "designbase-color-picker__slider-group", children: [jsxRuntimeExports.jsxs("label", { className: "designbase-color-picker__slider-label", children: ["Lightness ", jsxRuntimeExports.jsxs("span", { className: "designbase-color-picker__slider-value", children: [lightness, "%"] })] }), jsxRuntimeExports.jsx("input", { type: "range", min: "0", max: "100", value: lightness, onChange: handleLightnessChange, className: "designbase-color-picker__slider designbase-color-picker__slider--lightness", style: {
|
|
6354
|
+
background: `linear-gradient(to right,
|
|
6355
|
+
hsl(${hue}, ${saturation}%, 0%),
|
|
6356
|
+
hsl(${hue}, ${saturation}%, 50%),
|
|
6357
|
+
hsl(${hue}, ${saturation}%, 100%))`
|
|
6358
|
+
} })] }), showAlpha && (jsxRuntimeExports.jsxs("div", { className: "designbase-color-picker__slider-group", children: [jsxRuntimeExports.jsxs("label", { className: "designbase-color-picker__slider-label", children: ["Alpha ", jsxRuntimeExports.jsxs("span", { className: "designbase-color-picker__slider-value", children: [alpha, "%"] })] }), jsxRuntimeExports.jsx("input", { type: "range", min: "0", max: "100", value: alpha, onChange: handleAlphaChange, className: "designbase-color-picker__slider designbase-color-picker__slider--alpha", style: {
|
|
6359
|
+
background: `linear-gradient(to right,
|
|
6360
|
+
transparent,
|
|
6361
|
+
hsl(${hue}, ${saturation}%, ${lightness}%))`
|
|
6362
|
+
} })] }))] }), jsxRuntimeExports.jsxs("div", { className: "designbase-color-picker__preview", children: [jsxRuntimeExports.jsx("div", { className: "designbase-color-picker__preview-box", style: {
|
|
6363
|
+
backgroundColor: showAlpha
|
|
6364
|
+
? `hsla(${hue}, ${saturation}%, ${lightness}%, ${alpha / 100})`
|
|
6365
|
+
: `hsl(${hue}, ${saturation}%, ${lightness}%)`
|
|
6366
|
+
} }), jsxRuntimeExports.jsxs("div", { className: "designbase-color-picker__preview-info", children: [jsxRuntimeExports.jsx("div", { className: "designbase-color-picker__preview-value", children: getFormattedColor() }), showCopyButton && (jsxRuntimeExports.jsx("button", { type: "button", className: "designbase-color-picker__copy-button", onClick: handleCopy, "aria-label": "Copy color value", children: isCopied ? jsxRuntimeExports.jsx(icons.DoneIcon, { size: 14 }) : jsxRuntimeExports.jsx(icons.CopyIcon, { size: 14 }) }))] })] })] }));
|
|
6367
|
+
return (jsxRuntimeExports.jsxs("div", { ref: pickerRef, className: classes, children: [jsxRuntimeExports.jsxs("div", { className: "designbase-color-picker__trigger", children: [jsxRuntimeExports.jsx("div", { className: "designbase-color-picker__color-display", onClick: togglePicker, children: jsxRuntimeExports.jsx("div", { className: "designbase-color-picker__color-box", style: {
|
|
6368
|
+
backgroundColor: showAlpha
|
|
6369
|
+
? `hsla(${hue}, ${saturation}%, ${lightness}%, ${alpha / 100})`
|
|
6370
|
+
: `hsl(${hue}, ${saturation}%, ${lightness}%)`
|
|
6371
|
+
} }) }), showInput && (jsxRuntimeExports.jsx("input", { type: "text", value: inputValue, onChange: handleInputChange, onBlur: handleInputBlur, onClick: (e) => e.stopPropagation(), disabled: disabled, readOnly: readonly, className: "designbase-color-picker__input", placeholder: "#000000" })), showInput && showCopyButton && (jsxRuntimeExports.jsx("button", { type: "button", className: "designbase-color-picker__copy-button-inline", onClick: (e) => {
|
|
6372
|
+
e.stopPropagation();
|
|
6373
|
+
handleCopy();
|
|
6374
|
+
}, disabled: disabled, "aria-label": "Copy color value", children: isCopied ? jsxRuntimeExports.jsx(icons.DoneIcon, { size: 14 }) : jsxRuntimeExports.jsx(icons.CopyIcon, { size: 14 }) })), jsxRuntimeExports.jsx("button", { type: "button", className: "designbase-color-picker__toggle", onClick: togglePicker, disabled: disabled, "aria-label": "Toggle color picker", children: jsxRuntimeExports.jsx(icons.ChevronDownIcon, { size: 16 }) })] }), type === 'dropdown' && isOpen && (jsxRuntimeExports.jsx("div", { className: "designbase-color-picker__dropdown", children: renderColorSelector() })), type === 'modal' && (jsxRuntimeExports.jsx(Modal, { isOpen: isOpen, onClose: () => setIsOpen(false), title: "\uC0C9\uC0C1 \uC120\uD0DD", size: "s", children: renderColorSelector() }))] }));
|
|
6375
|
+
};
|
|
6376
|
+
ColorPicker$1.displayName = 'ColorPicker';
|
|
6377
|
+
|
|
6378
|
+
ColorPicker;
|
|
6379
|
+
|
|
6380
|
+
const Confirm = ({ open, title, children, confirmText = '확인', cancelText = '취소', confirmVariant = 'primary', variant = 'info', size = 's', showIcon = true, confirmDisabled = false, cancelDisabled = false, onConfirm, onCancel, onClose, closeOnEscape = true, closeOnOverlayClick = true, className, ...props }) => {
|
|
6119
6381
|
const modalRef = React.useRef(null);
|
|
6120
6382
|
// ESC 키 처리
|
|
6121
6383
|
React.useEffect(() => {
|
|
@@ -6285,7 +6547,7 @@ const Container = ({ size = 'l', maxWidth, variant = 'plain', padding = 'm', mar
|
|
|
6285
6547
|
return (jsxRuntimeExports.jsx("div", { className: classes, style: style, children: children }));
|
|
6286
6548
|
};
|
|
6287
6549
|
|
|
6288
|
-
const MenuItem = ({ id, label, href, icon: Icon, active = false, disabled = false, badge, badgeColor = 'primary', variant = 'default', type = '
|
|
6550
|
+
const MenuItem = ({ id, label, href, icon: Icon, active = false, disabled = false, badge, badgeColor = 'primary', variant = 'default', type = 'block', size = 'm', style = 'accordion', subItems, expanded = false, expandable = false, depth = 0, onClick, onChildClick, className, }) => {
|
|
6289
6551
|
const [internalExpanded, setInternalExpanded] = React.useState(expanded);
|
|
6290
6552
|
// expanded prop이 변경될 때 internalExpanded 업데이트
|
|
6291
6553
|
React.useEffect(() => {
|
|
@@ -6340,7 +6602,7 @@ const MenuItem = ({ id, label, href, icon: Icon, active = false, disabled = fals
|
|
|
6340
6602
|
return (jsxRuntimeExports.jsxs("div", { className: classes, children: [jsxRuntimeExports.jsxs("div", { className: contentClasses, onClick: handleClick, children: [Icon && (jsxRuntimeExports.jsx("div", { className: clsx('designbase-menu-item__icon', `designbase-menu-item__icon--${variant}`, {
|
|
6341
6603
|
'designbase-menu-item__icon--active': active,
|
|
6342
6604
|
'designbase-menu-item__icon--disabled': disabled,
|
|
6343
|
-
}), children: jsxRuntimeExports.jsx(Icon, { size: size === 's' ? 16 : size === 'l' ? 24 : 20 }) })), jsxRuntimeExports.jsx("span", { className: "designbase-menu-item__label", children: label }), badge && (jsxRuntimeExports.jsx(Badge, { count: typeof badge === 'string' ? parseInt(badge) : badge, variant: badgeColor === 'neutral' ? 'secondary' : badgeColor, size: "s", style: "number" })), hasChildren && (jsxRuntimeExports.jsx("div", { className: "designbase-menu-item__expand-icon", children: isExpanded ? (jsxRuntimeExports.jsx(icons.ChevronUpIcon, { size: 16 })) : (jsxRuntimeExports.jsx(icons.ChevronDownIcon, { size: 16 })) }))] }), hasChildren && isExpanded && (jsxRuntimeExports.jsx("div", { className: clsx('designbase-menu-item__children', `designbase-menu-item__children--${style}`), children: subItems.map((child) => (jsxRuntimeExports.jsx(MenuItem, { id: child.id, label: child.label, href: child.href, icon: child.icon, active: child.active, disabled: child.disabled, badge: child.badge, badgeColor: child.badgeColor, variant: child.variant, type: child.type, size: size, style: child.style || style, subItems: child.subItems, depth: depth + 1, onClick: () => handleChildClick(child), onChildClick: onChildClick }, child.id))) }))] }));
|
|
6605
|
+
}), children: jsxRuntimeExports.jsx(Icon, { size: size === 's' ? 16 : size === 'l' ? 24 : 20 }) })), jsxRuntimeExports.jsx("span", { className: "designbase-menu-item__label", children: label }), badge && (jsxRuntimeExports.jsx(Badge, { count: typeof badge === 'string' ? parseInt(badge) : badge, variant: badgeColor === 'neutral' ? 'secondary' : badgeColor, size: "s", style: "number" })), hasChildren && (jsxRuntimeExports.jsx("div", { className: "designbase-menu-item__expand-icon", children: isExpanded ? (jsxRuntimeExports.jsx(icons.ChevronUpIcon, { size: 16 })) : (jsxRuntimeExports.jsx(icons.ChevronDownIcon, { size: 16 })) }))] }), hasChildren && isExpanded && (jsxRuntimeExports.jsx("div", { className: clsx('designbase-menu-item__children', `designbase-menu-item__children--${style}`), children: subItems.map((child) => (jsxRuntimeExports.jsx(MenuItem, { id: child.id, label: child.label, href: child.href, icon: child.icon, active: child.active, disabled: child.disabled, badge: child.badge, badgeColor: child.badgeColor, variant: child.variant, type: child.type || (style === 'accordion' ? 'block' : 'inline'), size: size, style: child.style || style, subItems: child.subItems, depth: depth + 1, onClick: () => handleChildClick(child), onChildClick: onChildClick }, child.id))) }))] }));
|
|
6344
6606
|
};
|
|
6345
6607
|
MenuItem.displayName = 'MenuItem';
|
|
6346
6608
|
|
|
@@ -6476,50 +6738,72 @@ const ContextMenu = ({ items, open, x, y, onClose, className, }) => {
|
|
|
6476
6738
|
};
|
|
6477
6739
|
ContextMenu.displayName = 'ContextMenu';
|
|
6478
6740
|
|
|
6479
|
-
const DatePicker = ({ mode = 'single', value, onChange, minDate, maxDate, events = [], showOutsideDays = true, startOfWeek = 'sunday', size = 'm', variant = 'default', highlightWeekends = true, highlightHolidays = false, today = new Date(), locale = 'ko-KR', format = 'yyyy-MM-dd', className, disabled = false, readonly = false,
|
|
6480
|
-
// 드롭다운 관련 props
|
|
6481
|
-
isOpen: controlledIsOpen, onOpenChange, trigger, placement = 'bottom',
|
|
6741
|
+
const DatePicker = ({ mode = 'single', type = 'dropdown', value, defaultValue, onChange, minDate, maxDate, events = [], showOutsideDays = true, startOfWeek = 'sunday', size = 'm', variant = 'default', highlightWeekends = true, highlightHolidays = false, today = new Date(), locale = 'ko-KR', format = 'yyyy-MM-dd', className, disabled = false, readonly = false,
|
|
6482
6742
|
// 인라인 모드
|
|
6483
6743
|
inline = false, }) => {
|
|
6484
6744
|
// 아이콘 크기 계산 (m이 기본값)
|
|
6485
6745
|
const iconSize = size === 's' ? 14 : size === 'l' ? 18 : 16;
|
|
6486
6746
|
const [currentDate, setCurrentDate] = React.useState(new Date());
|
|
6487
6747
|
const [hoveredDate, setHoveredDate] = React.useState(null);
|
|
6488
|
-
const [
|
|
6748
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
6489
6749
|
const [rangeStart, setRangeStart] = React.useState(null);
|
|
6490
|
-
const
|
|
6491
|
-
|
|
6492
|
-
const
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6750
|
+
const [inputValue, setInputValue] = React.useState('');
|
|
6751
|
+
const [selectedValue, setSelectedValue] = React.useState(value || defaultValue);
|
|
6752
|
+
const pickerRef = React.useRef(null);
|
|
6753
|
+
// 모달용 임시 상태 (적용 전까지는 변경되지 않음)
|
|
6754
|
+
const [tempValue, setTempValue] = React.useState(value || defaultValue);
|
|
6755
|
+
// value prop이 변경되면 업데이트
|
|
6756
|
+
React.useEffect(() => {
|
|
6757
|
+
if (value !== undefined) {
|
|
6758
|
+
setSelectedValue(value);
|
|
6759
|
+
// formatDateValue 함수가 정의되기 전이므로 직접 포맷팅
|
|
6760
|
+
if (value instanceof Date) {
|
|
6761
|
+
setInputValue(value.toLocaleDateString(locale));
|
|
6762
|
+
}
|
|
6763
|
+
else if (Array.isArray(value)) {
|
|
6764
|
+
setInputValue(value.map(date => date.toLocaleDateString(locale)).join(', '));
|
|
6765
|
+
}
|
|
6766
|
+
else if (value && 'start' in value && 'end' in value) {
|
|
6767
|
+
setInputValue(`${value.start.toLocaleDateString(locale)} ~ ${value.end.toLocaleDateString(locale)}`);
|
|
6768
|
+
}
|
|
6769
|
+
else {
|
|
6770
|
+
setInputValue('');
|
|
6771
|
+
}
|
|
6496
6772
|
}
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6773
|
+
}, [value, locale]);
|
|
6774
|
+
// 모달이 열릴 때 현재 값을 임시 상태로 복사
|
|
6775
|
+
React.useEffect(() => {
|
|
6776
|
+
if (isOpen && type === 'modal') {
|
|
6777
|
+
setTempValue(selectedValue);
|
|
6778
|
+
}
|
|
6779
|
+
}, [isOpen, type, selectedValue]);
|
|
6780
|
+
// 외부 클릭 감지 (드롭다운만)
|
|
6500
6781
|
React.useEffect(() => {
|
|
6501
|
-
if (!isOpen || inline)
|
|
6782
|
+
if (!isOpen || inline || type !== 'dropdown')
|
|
6502
6783
|
return;
|
|
6503
6784
|
const handleClickOutside = (event) => {
|
|
6504
|
-
if (
|
|
6785
|
+
if (pickerRef.current && !pickerRef.current.contains(event.target)) {
|
|
6505
6786
|
setIsOpen(false);
|
|
6506
6787
|
}
|
|
6507
6788
|
};
|
|
6508
6789
|
document.addEventListener('mousedown', handleClickOutside);
|
|
6509
6790
|
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
6510
|
-
}, [isOpen, inline]);
|
|
6511
|
-
//
|
|
6512
|
-
React.
|
|
6513
|
-
if (!
|
|
6514
|
-
return;
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6791
|
+
}, [isOpen, inline, type]);
|
|
6792
|
+
// 날짜 포맷팅 함수
|
|
6793
|
+
const formatDateValue = React.useCallback((dateValue) => {
|
|
6794
|
+
if (!dateValue)
|
|
6795
|
+
return '';
|
|
6796
|
+
if (dateValue instanceof Date) {
|
|
6797
|
+
return dateValue.toLocaleDateString(locale);
|
|
6798
|
+
}
|
|
6799
|
+
if (Array.isArray(dateValue)) {
|
|
6800
|
+
return dateValue.map(date => date.toLocaleDateString(locale)).join(', ');
|
|
6801
|
+
}
|
|
6802
|
+
if ('start' in dateValue && 'end' in dateValue) {
|
|
6803
|
+
return `${dateValue.start.toLocaleDateString(locale)} ~ ${dateValue.end.toLocaleDateString(locale)}`;
|
|
6804
|
+
}
|
|
6805
|
+
return '';
|
|
6806
|
+
}, [locale]);
|
|
6523
6807
|
// 날짜 유효성 검사
|
|
6524
6808
|
const isDateValid = React.useCallback((date) => {
|
|
6525
6809
|
if (minDate && date < minDate)
|
|
@@ -6528,21 +6812,70 @@ inline = false, }) => {
|
|
|
6528
6812
|
return false;
|
|
6529
6813
|
return true;
|
|
6530
6814
|
}, [minDate, maxDate]);
|
|
6815
|
+
// 토글 핸들러
|
|
6816
|
+
const togglePicker = () => {
|
|
6817
|
+
if (disabled || readonly)
|
|
6818
|
+
return;
|
|
6819
|
+
setIsOpen((o) => !o);
|
|
6820
|
+
};
|
|
6821
|
+
// 모달용 핸들러들
|
|
6822
|
+
const handleModalDateChange = (newValue) => {
|
|
6823
|
+
setTempValue(newValue);
|
|
6824
|
+
};
|
|
6825
|
+
const handleModalApply = () => {
|
|
6826
|
+
// 임시 값을 실제 값으로 적용
|
|
6827
|
+
setSelectedValue(tempValue);
|
|
6828
|
+
setInputValue(formatDateValue(tempValue));
|
|
6829
|
+
onChange?.(tempValue);
|
|
6830
|
+
// 모달 닫기
|
|
6831
|
+
setIsOpen(false);
|
|
6832
|
+
};
|
|
6833
|
+
const handleModalCancel = () => {
|
|
6834
|
+
// 임시 값을 원래 값으로 복원
|
|
6835
|
+
setTempValue(selectedValue);
|
|
6836
|
+
// 모달 닫기
|
|
6837
|
+
setIsOpen(false);
|
|
6838
|
+
};
|
|
6839
|
+
// 입력 필드 변경
|
|
6840
|
+
const handleInputChange = (e) => {
|
|
6841
|
+
setInputValue(e.target.value);
|
|
6842
|
+
};
|
|
6843
|
+
// 입력 필드 블러 시 검증
|
|
6844
|
+
const handleInputBlur = () => {
|
|
6845
|
+
// 간단한 날짜 파싱 (YYYY-MM-DD 형식)
|
|
6846
|
+
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
|
|
6847
|
+
if (dateRegex.test(inputValue)) {
|
|
6848
|
+
const parsedDate = new Date(inputValue);
|
|
6849
|
+
if (!isNaN(parsedDate.getTime()) && isDateValid(parsedDate)) {
|
|
6850
|
+
setSelectedValue(parsedDate);
|
|
6851
|
+
onChange?.(parsedDate);
|
|
6852
|
+
return;
|
|
6853
|
+
}
|
|
6854
|
+
}
|
|
6855
|
+
// 유효하지 않으면 이전 값으로 복원
|
|
6856
|
+
setInputValue(formatDateValue(selectedValue));
|
|
6857
|
+
};
|
|
6531
6858
|
// 날짜 선택 처리
|
|
6532
6859
|
const handleDateClick = React.useCallback((date) => {
|
|
6533
6860
|
if (disabled || readonly || !isDateValid(date))
|
|
6534
6861
|
return;
|
|
6862
|
+
const currentValue = type === 'modal' ? tempValue : selectedValue;
|
|
6863
|
+
const handleChange = type === 'modal' ? handleModalDateChange : (newValue) => {
|
|
6864
|
+
setSelectedValue(newValue);
|
|
6865
|
+
setInputValue(formatDateValue(newValue));
|
|
6866
|
+
onChange?.(newValue);
|
|
6867
|
+
};
|
|
6535
6868
|
switch (mode) {
|
|
6536
6869
|
case 'single':
|
|
6537
|
-
|
|
6538
|
-
if (!inline)
|
|
6870
|
+
handleChange(date);
|
|
6871
|
+
if (!inline && type === 'dropdown')
|
|
6539
6872
|
setIsOpen(false);
|
|
6540
6873
|
break;
|
|
6541
6874
|
case 'range':
|
|
6542
6875
|
// 기존 범위가 있고, 새로운 날짜를 클릭한 경우
|
|
6543
|
-
if (
|
|
6876
|
+
if (currentValue && 'start' in currentValue && !rangeStart) {
|
|
6544
6877
|
// 기존 범위를 해제하고 새로운 범위 시작
|
|
6545
|
-
|
|
6878
|
+
handleChange(undefined);
|
|
6546
6879
|
setRangeStart(date);
|
|
6547
6880
|
return;
|
|
6548
6881
|
}
|
|
@@ -6556,29 +6889,29 @@ inline = false, }) => {
|
|
|
6556
6889
|
const end = date;
|
|
6557
6890
|
if (start > end) {
|
|
6558
6891
|
// 시작일이 끝일보다 늦으면 순서 바꿈
|
|
6559
|
-
|
|
6892
|
+
handleChange({ start: end, end: start });
|
|
6560
6893
|
}
|
|
6561
6894
|
else {
|
|
6562
|
-
|
|
6895
|
+
handleChange({ start, end });
|
|
6563
6896
|
}
|
|
6564
6897
|
setRangeStart(null);
|
|
6565
|
-
if (!inline)
|
|
6898
|
+
if (!inline && type === 'dropdown')
|
|
6566
6899
|
setIsOpen(false);
|
|
6567
6900
|
}
|
|
6568
6901
|
break;
|
|
6569
6902
|
case 'multiple':
|
|
6570
|
-
const currentDates = Array.isArray(
|
|
6903
|
+
const currentDates = Array.isArray(currentValue) ? currentValue : [];
|
|
6571
6904
|
const dateStr = date.toISOString().split('T')[0];
|
|
6572
6905
|
const isSelected = currentDates.some(d => d.toISOString().split('T')[0] === dateStr);
|
|
6573
6906
|
if (isSelected) {
|
|
6574
|
-
|
|
6907
|
+
handleChange(currentDates.filter(d => d.toISOString().split('T')[0] !== dateStr));
|
|
6575
6908
|
}
|
|
6576
6909
|
else {
|
|
6577
|
-
|
|
6910
|
+
handleChange([...currentDates, date]);
|
|
6578
6911
|
}
|
|
6579
6912
|
break;
|
|
6580
6913
|
}
|
|
6581
|
-
}, [mode,
|
|
6914
|
+
}, [mode, onChange, disabled, readonly, isDateValid, rangeStart, inline, type, tempValue, selectedValue, formatDateValue, handleModalDateChange]);
|
|
6582
6915
|
// 날짜 호버 처리
|
|
6583
6916
|
const handleDateHover = React.useCallback((date) => {
|
|
6584
6917
|
if (mode === 'range') {
|
|
@@ -6596,6 +6929,7 @@ inline = false, }) => {
|
|
|
6596
6929
|
const isInRange = React.useCallback((date) => {
|
|
6597
6930
|
if (mode !== 'range')
|
|
6598
6931
|
return false;
|
|
6932
|
+
const currentValue = type === 'modal' ? tempValue : selectedValue;
|
|
6599
6933
|
// 현재 선택된 범위 확인
|
|
6600
6934
|
let start = null;
|
|
6601
6935
|
let end = null;
|
|
@@ -6604,10 +6938,10 @@ inline = false, }) => {
|
|
|
6604
6938
|
start = rangeStart;
|
|
6605
6939
|
end = hoveredDate;
|
|
6606
6940
|
}
|
|
6607
|
-
else if (
|
|
6941
|
+
else if (currentValue && 'start' in currentValue) {
|
|
6608
6942
|
// 완성된 범위
|
|
6609
|
-
start =
|
|
6610
|
-
end =
|
|
6943
|
+
start = currentValue.start;
|
|
6944
|
+
end = currentValue.end;
|
|
6611
6945
|
}
|
|
6612
6946
|
if (!start)
|
|
6613
6947
|
return false;
|
|
@@ -6615,28 +6949,29 @@ inline = false, }) => {
|
|
|
6615
6949
|
const startStr = start.toISOString().split('T')[0];
|
|
6616
6950
|
const endStr = end ? end.toISOString().split('T')[0] : startStr;
|
|
6617
6951
|
return dateStr >= startStr && dateStr <= endStr;
|
|
6618
|
-
}, [mode,
|
|
6952
|
+
}, [mode, rangeStart, hoveredDate, type, tempValue, selectedValue]);
|
|
6619
6953
|
// 날짜가 선택되었는지 확인
|
|
6620
6954
|
const isDateSelected = React.useCallback((date) => {
|
|
6621
6955
|
const dateStr = date.toISOString().split('T')[0];
|
|
6956
|
+
const currentValue = type === 'modal' ? tempValue : selectedValue;
|
|
6622
6957
|
switch (mode) {
|
|
6623
6958
|
case 'single':
|
|
6624
|
-
return
|
|
6959
|
+
return currentValue && currentValue instanceof Date && currentValue.toISOString().split('T')[0] === dateStr;
|
|
6625
6960
|
case 'range':
|
|
6626
6961
|
if (rangeStart && rangeStart.toISOString().split('T')[0] === dateStr) {
|
|
6627
6962
|
return true;
|
|
6628
6963
|
}
|
|
6629
|
-
if (!
|
|
6964
|
+
if (!currentValue || !('start' in currentValue))
|
|
6630
6965
|
return false;
|
|
6631
|
-
const range =
|
|
6966
|
+
const range = currentValue;
|
|
6632
6967
|
return range.start.toISOString().split('T')[0] === dateStr ||
|
|
6633
6968
|
range.end.toISOString().split('T')[0] === dateStr;
|
|
6634
6969
|
case 'multiple':
|
|
6635
|
-
return Array.isArray(
|
|
6970
|
+
return Array.isArray(currentValue) && currentValue.some(d => d.toISOString().split('T')[0] === dateStr);
|
|
6636
6971
|
default:
|
|
6637
6972
|
return false;
|
|
6638
6973
|
}
|
|
6639
|
-
}, [mode,
|
|
6974
|
+
}, [mode, rangeStart, type, tempValue, selectedValue]);
|
|
6640
6975
|
// 월 이동
|
|
6641
6976
|
const goToPreviousMonth = React.useCallback(() => {
|
|
6642
6977
|
setCurrentDate(prev => {
|
|
@@ -6684,22 +7019,23 @@ inline = false, }) => {
|
|
|
6684
7019
|
return weekdays;
|
|
6685
7020
|
}, [startOfWeek]);
|
|
6686
7021
|
// 날짜 셀 렌더링
|
|
6687
|
-
const renderDateCell = React.useCallback((date) => {
|
|
7022
|
+
const renderDateCell = React.useCallback((date, isModal = false) => {
|
|
6688
7023
|
const isCurrentMonth = date.getMonth() === currentDate.getMonth();
|
|
6689
7024
|
const isToday = date.toDateString() === today.toDateString();
|
|
6690
7025
|
const isSelected = isDateSelected(date);
|
|
6691
7026
|
const isInRangeDate = isInRange(date);
|
|
6692
7027
|
const isWeekend = date.getDay() === 0 || date.getDay() === 6;
|
|
6693
7028
|
const dateEvents = getEventsForDate(date);
|
|
7029
|
+
const currentValue = isModal ? tempValue : selectedValue;
|
|
6694
7030
|
const cellClass = clsx('designbase-date-picker__date', {
|
|
6695
7031
|
'designbase-date-picker__date--outside': !isCurrentMonth && !showOutsideDays,
|
|
6696
7032
|
'designbase-date-picker__date--today': isToday,
|
|
6697
7033
|
'designbase-date-picker__date--selected': isSelected,
|
|
6698
7034
|
'designbase-date-picker__date--in-range': isInRangeDate && !isSelected,
|
|
6699
7035
|
'designbase-date-picker__date--range-start': mode === 'range' && ((rangeStart && rangeStart.toDateString() === date.toDateString()) ||
|
|
6700
|
-
(
|
|
6701
|
-
'designbase-date-picker__date--range-end': mode === 'range' &&
|
|
6702
|
-
|
|
7036
|
+
(currentValue && 'start' in currentValue && currentValue.start.toDateString() === date.toDateString())),
|
|
7037
|
+
'designbase-date-picker__date--range-end': mode === 'range' && currentValue && 'end' in currentValue &&
|
|
7038
|
+
currentValue.end.toDateString() === date.toDateString(),
|
|
6703
7039
|
'designbase-date-picker__date--weekend': isWeekend && highlightWeekends,
|
|
6704
7040
|
'designbase-date-picker__date--disabled': !isDateValid(date),
|
|
6705
7041
|
'designbase-date-picker__date--has-events': dateEvents.length > 0,
|
|
@@ -6711,7 +7047,6 @@ inline = false, }) => {
|
|
|
6711
7047
|
isDateSelected,
|
|
6712
7048
|
isInRange,
|
|
6713
7049
|
mode,
|
|
6714
|
-
value,
|
|
6715
7050
|
rangeStart,
|
|
6716
7051
|
highlightWeekends,
|
|
6717
7052
|
getEventsForDate,
|
|
@@ -6721,21 +7056,29 @@ inline = false, }) => {
|
|
|
6721
7056
|
readonly,
|
|
6722
7057
|
handleDateClick,
|
|
6723
7058
|
handleDateHover,
|
|
7059
|
+
tempValue,
|
|
7060
|
+
selectedValue,
|
|
6724
7061
|
]);
|
|
6725
7062
|
const calendarGrid = generateCalendarGrid();
|
|
6726
7063
|
const weekdays = generateWeekdays();
|
|
6727
|
-
//
|
|
6728
|
-
|
|
6729
|
-
return (jsxRuntimeExports.jsxs("div", {
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
7064
|
+
// 캘린더 렌더링 함수
|
|
7065
|
+
const renderCalendar = (isModal = false) => {
|
|
7066
|
+
return (jsxRuntimeExports.jsxs("div", { className: "designbase-date-picker__selector", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-date-picker__header", children: [jsxRuntimeExports.jsx("button", { className: "designbase-date-picker__nav-button", onClick: goToPreviousMonth, disabled: disabled || readonly, type: "button", "aria-label": "\uC774\uC804 \uB2EC", children: jsxRuntimeExports.jsx(icons.ChevronLeftIcon, { size: iconSize, color: "currentColor" }) }), jsxRuntimeExports.jsx("div", { className: "designbase-date-picker__current-month", children: currentDate.toLocaleDateString(locale, { year: 'numeric', month: 'long' }) }), jsxRuntimeExports.jsx("button", { className: "designbase-date-picker__nav-button", onClick: goToNextMonth, disabled: disabled || readonly, type: "button", "aria-label": "\uB2E4\uC74C \uB2EC", children: jsxRuntimeExports.jsx(icons.ChevronRightIcon, { size: iconSize, color: "currentColor" }) })] }), jsxRuntimeExports.jsx("div", { className: "designbase-date-picker__weekdays", children: weekdays.map(day => (jsxRuntimeExports.jsx("div", { className: "designbase-date-picker__weekday", children: day }, day))) }), jsxRuntimeExports.jsx("div", { className: "designbase-date-picker__grid", children: calendarGrid.map(date => renderDateCell(date, isModal)) }), jsxRuntimeExports.jsx("div", { className: "designbase-date-picker__footer", children: jsxRuntimeExports.jsx("button", { className: "designbase-date-picker__today-button", onClick: goToToday, disabled: disabled || readonly, type: "button", children: "\uC624\uB298" }) }), isModal && (jsxRuntimeExports.jsx(ModalFooter, { children: jsxRuntimeExports.jsxs("div", { className: "designbase-date-picker__modal-footer", children: [jsxRuntimeExports.jsx(Button, { variant: "secondary", size: "m", onPress: handleModalCancel, children: "\uCDE8\uC18C" }), jsxRuntimeExports.jsx(Button, { variant: "primary", size: "m", onPress: handleModalApply, children: "\uC801\uC6A9" })] }) }))] }));
|
|
7067
|
+
};
|
|
7068
|
+
// 인라인 모드일 때
|
|
7069
|
+
if (inline) {
|
|
7070
|
+
return (jsxRuntimeExports.jsx("div", { className: clsx('designbase-date-picker', `designbase-date-picker--size-${size}`, `designbase-date-picker--variant-${variant}`, {
|
|
7071
|
+
'designbase-date-picker--disabled': disabled,
|
|
7072
|
+
'designbase-date-picker--readonly': readonly,
|
|
7073
|
+
}, className), children: renderCalendar(false) }));
|
|
6733
7074
|
}
|
|
6734
|
-
//
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
|
|
7075
|
+
// 드롭다운/모달 모드일 때
|
|
7076
|
+
const classes = clsx('designbase-date-picker', `designbase-date-picker--${size}`, {
|
|
7077
|
+
'designbase-date-picker--disabled': disabled,
|
|
7078
|
+
'designbase-date-picker--readonly': readonly,
|
|
7079
|
+
'designbase-date-picker--open': isOpen,
|
|
7080
|
+
}, className);
|
|
7081
|
+
return (jsxRuntimeExports.jsxs("div", { ref: pickerRef, className: classes, children: [jsxRuntimeExports.jsxs("div", { className: "designbase-date-picker__trigger", children: [jsxRuntimeExports.jsx("button", { type: "button", className: "designbase-date-picker__icon-display", onClick: togglePicker, disabled: disabled, "aria-label": "\uB0A0\uC9DC \uC120\uD0DD \uC5F4\uAE30", children: jsxRuntimeExports.jsx(icons.CalendarIcon, { size: 16 }) }), jsxRuntimeExports.jsx("input", { type: "text", value: inputValue, onChange: handleInputChange, onBlur: handleInputBlur, onClick: (e) => e.stopPropagation(), disabled: disabled, readOnly: readonly, className: "designbase-date-picker__input", placeholder: "\uB0A0\uC9DC\uB97C \uC120\uD0DD\uD558\uC138\uC694", "aria-label": "\uB0A0\uC9DC \uC785\uB825" }), jsxRuntimeExports.jsx("button", { type: "button", className: "designbase-date-picker__toggle", onClick: togglePicker, disabled: disabled, "aria-label": "\uB0A0\uC9DC \uC120\uD0DD \uC5F4\uAE30", children: jsxRuntimeExports.jsx(icons.ChevronDownIcon, { size: 16 }) })] }), type === 'dropdown' && isOpen && (jsxRuntimeExports.jsx("div", { className: "designbase-date-picker__dropdown", role: "dialog", "aria-modal": "false", children: renderCalendar(false) })), type === 'modal' && (jsxRuntimeExports.jsx(Modal, { isOpen: isOpen, onClose: handleModalCancel, title: "\uB0A0\uC9DC \uC120\uD0DD", size: "s", children: renderCalendar(true) }))] }));
|
|
6739
7082
|
};
|
|
6740
7083
|
|
|
6741
7084
|
const Drawer = ({ isOpen, onClose, title, children, position = 'right', size = 'm', showCloseButton = true, closeOnBackdropClick = true, closeOnEscape = true, animationDuration = 300, showOverlay = true, overlayClosable = true, className, style, zIndex = 1000, trapFocus = true, id, ...props }) => {
|
|
@@ -7091,12 +7434,10 @@ const EmptyState = ({ variant = 'no-data', size = 'm', title, description, icon:
|
|
|
7091
7434
|
const getDefaultIcon = () => {
|
|
7092
7435
|
const defaultIconSize = iconSize || (() => {
|
|
7093
7436
|
switch (size) {
|
|
7094
|
-
case '
|
|
7095
|
-
case '
|
|
7096
|
-
case '
|
|
7097
|
-
|
|
7098
|
-
case 'xl': return 128;
|
|
7099
|
-
default: return 64;
|
|
7437
|
+
case 's': return 24;
|
|
7438
|
+
case 'm': return 32;
|
|
7439
|
+
case 'l': return 48;
|
|
7440
|
+
default: return 32;
|
|
7100
7441
|
}
|
|
7101
7442
|
})();
|
|
7102
7443
|
if (CustomIcon) {
|
|
@@ -7129,6 +7470,7 @@ const Progressbar = ({ value, max = 100, min = 0, size = 'm', variant = 'default
|
|
|
7129
7470
|
const classes = clsx('designbase-progressbar', `designbase-progressbar--${size}`, `designbase-progressbar--${variant}`, `designbase-progressbar--${style}`, {
|
|
7130
7471
|
'designbase-progressbar--full-width': fullWidth,
|
|
7131
7472
|
'designbase-progressbar--disabled': disabled,
|
|
7473
|
+
'designbase-progressbar--completed': percentage === 100,
|
|
7132
7474
|
}, className);
|
|
7133
7475
|
const containerClasses = clsx('designbase-progressbar__container', `designbase-progressbar__container--${labelPosition}`);
|
|
7134
7476
|
const trackClasses = clsx('designbase-progressbar__track', {
|
|
@@ -7244,13 +7586,13 @@ const FileUploader = ({ size = 'm', variant = 'default', accept, maxSize, multip
|
|
|
7244
7586
|
const getStatusIcon = React.useCallback((status) => {
|
|
7245
7587
|
switch (status) {
|
|
7246
7588
|
case 'pending':
|
|
7247
|
-
return
|
|
7589
|
+
return jsxRuntimeExports.jsx(icons.ClockIcon, { size: 20 });
|
|
7248
7590
|
case 'uploading':
|
|
7249
7591
|
return jsxRuntimeExports.jsx(Spinner, { type: "circular", size: "s" });
|
|
7250
7592
|
case 'success':
|
|
7251
|
-
return
|
|
7593
|
+
return jsxRuntimeExports.jsx(icons.DoneIcon, { size: 20 });
|
|
7252
7594
|
case 'error':
|
|
7253
|
-
return
|
|
7595
|
+
return jsxRuntimeExports.jsx(icons.ErrorIcon, { size: 20 });
|
|
7254
7596
|
default:
|
|
7255
7597
|
return null;
|
|
7256
7598
|
}
|
|
@@ -7261,11 +7603,12 @@ const FileUploader = ({ size = 'm', variant = 'default', accept, maxSize, multip
|
|
|
7261
7603
|
}, className), children: [jsxRuntimeExports.jsx(Dropzone, { size: size, variant: variant, accept: accept, maxSize: maxSize, multiple: multiple, disabled: disabled, readonly: readonly, onFileSelect: handleFileSelect }), showFileList && uploadedFiles.length > 0 && (jsxRuntimeExports.jsxs("div", { className: "designbase-file-uploader__file-list", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-file-uploader__file-list-title", children: ["\uC5C5\uB85C\uB4DC\uB41C \uD30C\uC77C\uB4E4 (", uploadedFiles.length, "\uAC1C)"] }), jsxRuntimeExports.jsx("div", { className: "designbase-file-uploader__file-items", children: uploadedFiles.map((uploadFile) => (jsxRuntimeExports.jsxs("div", { className: clsx('designbase-file-uploader__file-item', `designbase-file-uploader__file-item--${uploadFile.status}`), children: [jsxRuntimeExports.jsxs("div", { className: "designbase-file-uploader__file-info", children: [jsxRuntimeExports.jsx("div", { className: "designbase-file-uploader__file-icon", children: getStatusIcon(uploadFile.status) }), jsxRuntimeExports.jsxs("div", { className: "designbase-file-uploader__file-details", children: [jsxRuntimeExports.jsx("div", { className: "designbase-file-uploader__file-name", children: uploadFile.file.name }), jsxRuntimeExports.jsx("div", { className: "designbase-file-uploader__file-size", children: formatFileSize(uploadFile.file.size) }), uploadFile.error && (jsxRuntimeExports.jsx("div", { className: "designbase-file-uploader__file-error", children: uploadFile.error }))] })] }), showProgress && uploadFile.status === 'uploading' && (jsxRuntimeExports.jsx("div", { className: "designbase-file-uploader__progress", children: jsxRuntimeExports.jsx(Progressbar, { value: uploadFile.progress || 0, size: "s", variant: "primary", style: "solid", showLabel: true, labelPosition: "inside", fullWidth: true }) })), jsxRuntimeExports.jsxs("div", { className: "designbase-file-uploader__file-actions", children: [uploadFile.status === 'error' && (jsxRuntimeExports.jsx("button", { className: "designbase-file-uploader__retry-button", onClick: () => handleRetry(uploadFile.id), disabled: disabled || readonly, type: "button", children: "\uC7AC\uC2DC\uB3C4" })), jsxRuntimeExports.jsx("button", { className: "designbase-file-uploader__remove-button", onClick: () => handleRemove(uploadFile.id), disabled: disabled || readonly, type: "button", children: "\uC0AD\uC81C" })] })] }, uploadFile.id))) })] }))] }));
|
|
7262
7604
|
};
|
|
7263
7605
|
|
|
7264
|
-
const FloatingActionButton = React.forwardRef(({ size = 'm', variant = 'primary', icon, disabled = false, onClick, className, style, ...props }, forwardedRef) => {
|
|
7606
|
+
const FloatingActionButton = React.forwardRef(({ size = 'm', variant = 'primary', icon, loading = false, disabled = false, onClick, className, style, ...props }, forwardedRef) => {
|
|
7265
7607
|
const classes = clsx('designbase-fab', `designbase-fab--${size}`, `designbase-fab--${variant}`, {
|
|
7266
7608
|
'designbase-fab--disabled': disabled,
|
|
7609
|
+
'designbase-fab--loading': loading,
|
|
7267
7610
|
}, className);
|
|
7268
|
-
return (jsxRuntimeExports.jsx("button", { ref: forwardedRef, className: classes, style: style, onClick: onClick, disabled: disabled, "aria-label": "Floating action button", ...props, children: icon && (jsxRuntimeExports.jsx("div", { className: "designbase-fab__icon", children: icon })) }));
|
|
7611
|
+
return (jsxRuntimeExports.jsx("button", { ref: forwardedRef, className: classes, style: style, onClick: onClick, disabled: disabled || loading, "aria-label": "Floating action button", "aria-busy": loading, ...props, children: loading ? (jsxRuntimeExports.jsx("div", { className: "designbase-fab__icon", children: jsxRuntimeExports.jsx(Spinner, { type: "circular", size: "s" }) })) : icon && (jsxRuntimeExports.jsx("div", { className: "designbase-fab__icon", children: icon })) }));
|
|
7269
7612
|
});
|
|
7270
7613
|
FloatingActionButton.displayName = 'FloatingActionButton';
|
|
7271
7614
|
|
|
@@ -7337,7 +7680,251 @@ const Textarea = React.forwardRef(({ label, placeholder, defaultValue, value, si
|
|
|
7337
7680
|
});
|
|
7338
7681
|
Textarea.displayName = 'Textarea';
|
|
7339
7682
|
|
|
7340
|
-
const
|
|
7683
|
+
const ITEM_HEIGHT = 40;
|
|
7684
|
+
const TimePicker$1 = ({ size = 'm', type = 'dropdown', value, defaultValue = '12:00', format = '24h', minuteStep = 1, disabled = false, readonly = false, onChange, className, }) => {
|
|
7685
|
+
const [selectedTime, setSelectedTime] = React.useState(value || defaultValue);
|
|
7686
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
7687
|
+
const [inputValue, setInputValue] = React.useState(value || defaultValue);
|
|
7688
|
+
const [hours, setHours] = React.useState(12);
|
|
7689
|
+
const [minutes, setMinutes] = React.useState(0);
|
|
7690
|
+
const [seconds, setSeconds] = React.useState(0);
|
|
7691
|
+
const [period, setPeriod] = React.useState('PM');
|
|
7692
|
+
// 모달용 임시 상태 (적용 전까지는 변경되지 않음)
|
|
7693
|
+
const [tempHours, setTempHours] = React.useState(12);
|
|
7694
|
+
const [tempMinutes, setTempMinutes] = React.useState(0);
|
|
7695
|
+
const [tempSeconds, setTempSeconds] = React.useState(0);
|
|
7696
|
+
const [tempPeriod, setTempPeriod] = React.useState('PM');
|
|
7697
|
+
const pickerRef = React.useRef(null);
|
|
7698
|
+
// 외부 클릭 감지 (드롭다운만)
|
|
7699
|
+
React.useEffect(() => {
|
|
7700
|
+
if (!isOpen || type !== 'dropdown')
|
|
7701
|
+
return;
|
|
7702
|
+
const handleClickOutside = (event) => {
|
|
7703
|
+
if (pickerRef.current && !pickerRef.current.contains(event.target)) {
|
|
7704
|
+
setIsOpen(false);
|
|
7705
|
+
}
|
|
7706
|
+
};
|
|
7707
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
7708
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
7709
|
+
}, [isOpen, type]);
|
|
7710
|
+
const is12h = format === '12h' || format === '12h-with-seconds';
|
|
7711
|
+
const showSeconds = format === '12h-with-seconds' || format === '24h-with-seconds';
|
|
7712
|
+
React.useEffect(() => {
|
|
7713
|
+
if (!value)
|
|
7714
|
+
return;
|
|
7715
|
+
setSelectedTime(value);
|
|
7716
|
+
setInputValue(value);
|
|
7717
|
+
parseTimeString(value);
|
|
7718
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
7719
|
+
}, [value]);
|
|
7720
|
+
// 모달이 열릴 때 현재 값을 임시 상태로 복사
|
|
7721
|
+
React.useEffect(() => {
|
|
7722
|
+
if (isOpen && type === 'modal') {
|
|
7723
|
+
setTempHours(hours);
|
|
7724
|
+
setTempMinutes(minutes);
|
|
7725
|
+
setTempSeconds(seconds);
|
|
7726
|
+
setTempPeriod(period);
|
|
7727
|
+
}
|
|
7728
|
+
}, [isOpen, type, hours, minutes, seconds, period]);
|
|
7729
|
+
const parseTimeString = (timeStr) => {
|
|
7730
|
+
const s = timeStr.trim();
|
|
7731
|
+
let h = 0, m = 0, sec = 0, p = 'AM';
|
|
7732
|
+
if (is12h) {
|
|
7733
|
+
const re = showSeconds
|
|
7734
|
+
? /^(0?[1-9]|1[0-2]):([0-5]\d):([0-5]\d)\s*(AM|PM)$/i
|
|
7735
|
+
: /^(0?[1-9]|1[0-2]):([0-5]\d)\s*(AM|PM)$/i;
|
|
7736
|
+
const match = s.match(re);
|
|
7737
|
+
if (match) {
|
|
7738
|
+
h = parseInt(match[1], 10);
|
|
7739
|
+
m = parseInt(match[2], 10);
|
|
7740
|
+
sec = showSeconds ? parseInt(match[3], 10) : 0;
|
|
7741
|
+
p = (showSeconds ? match[4] : match[3]).toUpperCase();
|
|
7742
|
+
setPeriod(p);
|
|
7743
|
+
setHours(h);
|
|
7744
|
+
setMinutes(m);
|
|
7745
|
+
setSeconds(sec);
|
|
7746
|
+
return;
|
|
7747
|
+
}
|
|
7748
|
+
}
|
|
7749
|
+
else {
|
|
7750
|
+
const re = showSeconds
|
|
7751
|
+
? /^([01]?\d|2[0-3]):([0-5]\d):([0-5]\d)$/
|
|
7752
|
+
: /^([01]?\d|2[0-3]):([0-5]\d)$/;
|
|
7753
|
+
const match = s.match(re);
|
|
7754
|
+
if (match) {
|
|
7755
|
+
h = parseInt(match[1], 10);
|
|
7756
|
+
m = parseInt(match[2], 10);
|
|
7757
|
+
sec = showSeconds ? parseInt(match[3], 10) : 0;
|
|
7758
|
+
setHours(h);
|
|
7759
|
+
setMinutes(m);
|
|
7760
|
+
setSeconds(sec);
|
|
7761
|
+
return;
|
|
7762
|
+
}
|
|
7763
|
+
}
|
|
7764
|
+
};
|
|
7765
|
+
const to24Hour = (h12, p) => (p === 'AM' ? (h12 === 12 ? 0 : h12) : (h12 === 12 ? 12 : h12 + 12));
|
|
7766
|
+
const formatTimeString = (h, m, s, p) => {
|
|
7767
|
+
const HH = String(h).padStart(2, '0');
|
|
7768
|
+
const MM = String(m).padStart(2, '0');
|
|
7769
|
+
const SS = String(s).padStart(2, '0');
|
|
7770
|
+
if (is12h)
|
|
7771
|
+
return `${String(h).padStart(2, '0')}:${MM}${showSeconds ? `:${SS}` : ''} ${p || period}`;
|
|
7772
|
+
return `${HH}:${MM}${showSeconds ? `:${SS}` : ''}`;
|
|
7773
|
+
};
|
|
7774
|
+
const emitChange24 = (h, m, s, p) => {
|
|
7775
|
+
const h24 = is12h ? to24Hour(h, p || period) : h;
|
|
7776
|
+
onChange?.(`${String(h24).padStart(2, '0')}:${String(m).padStart(2, '0')}${showSeconds ? `:${String(s).padStart(2, '0')}` : ''}`);
|
|
7777
|
+
};
|
|
7778
|
+
const handleTimeChange = (h, m, s = seconds, p) => {
|
|
7779
|
+
setHours(h);
|
|
7780
|
+
setMinutes(m);
|
|
7781
|
+
setSeconds(s);
|
|
7782
|
+
if (p)
|
|
7783
|
+
setPeriod(p);
|
|
7784
|
+
const display = formatTimeString(h, m, s, p);
|
|
7785
|
+
setSelectedTime(display);
|
|
7786
|
+
setInputValue(display);
|
|
7787
|
+
emitChange24(h, m, s, p);
|
|
7788
|
+
};
|
|
7789
|
+
const handleInputBlur = () => {
|
|
7790
|
+
const s = inputValue.trim();
|
|
7791
|
+
let ok = false;
|
|
7792
|
+
if (is12h) {
|
|
7793
|
+
const re = showSeconds
|
|
7794
|
+
? /^(0?[1-9]|1[0-2]):([0-5]\d):([0-5]\d)\s*(AM|PM)$/i
|
|
7795
|
+
: /^(0?[1-9]|1[0-2]):([0-5]\d)\s*(AM|PM)$/i;
|
|
7796
|
+
const m = s.match(re);
|
|
7797
|
+
if (m) {
|
|
7798
|
+
const h = parseInt(m[1], 10), mi = parseInt(m[2], 10);
|
|
7799
|
+
const sec = showSeconds ? parseInt(m[3], 10) : 0;
|
|
7800
|
+
const p = (showSeconds ? m[4] : m[3]).toUpperCase();
|
|
7801
|
+
handleTimeChange(h, mi, sec, p);
|
|
7802
|
+
ok = true;
|
|
7803
|
+
}
|
|
7804
|
+
}
|
|
7805
|
+
else {
|
|
7806
|
+
const re = showSeconds
|
|
7807
|
+
? /^([01]?\d|2[0-3]):([0-5]\d):([0-5]\d)$/
|
|
7808
|
+
: /^([01]?\d|2[0-3]):([0-5]\d)$/;
|
|
7809
|
+
const m = s.match(re);
|
|
7810
|
+
if (m) {
|
|
7811
|
+
const h = parseInt(m[1], 10), mi = parseInt(m[2], 10);
|
|
7812
|
+
const sec = showSeconds ? parseInt(m[3], 10) : 0;
|
|
7813
|
+
handleTimeChange(h, mi, sec);
|
|
7814
|
+
ok = true;
|
|
7815
|
+
}
|
|
7816
|
+
}
|
|
7817
|
+
if (!ok)
|
|
7818
|
+
setInputValue(selectedTime);
|
|
7819
|
+
};
|
|
7820
|
+
const togglePicker = () => {
|
|
7821
|
+
if (disabled || readonly)
|
|
7822
|
+
return;
|
|
7823
|
+
setIsOpen((o) => !o);
|
|
7824
|
+
};
|
|
7825
|
+
// 모달용 핸들러들
|
|
7826
|
+
const handleModalTimeChange = (h, m, s = tempSeconds, p) => {
|
|
7827
|
+
setTempHours(h);
|
|
7828
|
+
setTempMinutes(m);
|
|
7829
|
+
setTempSeconds(s);
|
|
7830
|
+
if (p)
|
|
7831
|
+
setTempPeriod(p);
|
|
7832
|
+
};
|
|
7833
|
+
const handleModalApply = () => {
|
|
7834
|
+
// 임시 값을 실제 값으로 적용
|
|
7835
|
+
setHours(tempHours);
|
|
7836
|
+
setMinutes(tempMinutes);
|
|
7837
|
+
setSeconds(tempSeconds);
|
|
7838
|
+
setPeriod(tempPeriod);
|
|
7839
|
+
// 시간 변경 처리
|
|
7840
|
+
handleTimeChange(tempHours, tempMinutes, tempSeconds, tempPeriod);
|
|
7841
|
+
// 모달 닫기
|
|
7842
|
+
setIsOpen(false);
|
|
7843
|
+
};
|
|
7844
|
+
const handleModalCancel = () => {
|
|
7845
|
+
// 임시 값을 원래 값으로 복원
|
|
7846
|
+
setTempHours(hours);
|
|
7847
|
+
setTempMinutes(minutes);
|
|
7848
|
+
setTempSeconds(seconds);
|
|
7849
|
+
setTempPeriod(period);
|
|
7850
|
+
// 모달 닫기
|
|
7851
|
+
setIsOpen(false);
|
|
7852
|
+
};
|
|
7853
|
+
const pad2 = (n) => String(n).padStart(2, '0');
|
|
7854
|
+
/** ★ 변경 핵심: viewport(고정 레이어) ─ wheel(스크롤 영역) 분리 */
|
|
7855
|
+
const WheelPicker = ({ label, items, value, onChange, formatter }) => {
|
|
7856
|
+
const wheelRef = React.useRef(null);
|
|
7857
|
+
const scrollTimeout = React.useRef(null);
|
|
7858
|
+
const isProgrammatic = React.useRef(false);
|
|
7859
|
+
const indexOfValue = React.useCallback((v) => items.findIndex((x) => x === v), [items]);
|
|
7860
|
+
const scrollToIndex = React.useCallback((idx, smooth = true) => {
|
|
7861
|
+
if (!wheelRef.current)
|
|
7862
|
+
return;
|
|
7863
|
+
const top = idx * ITEM_HEIGHT; // 상/하 패딩은 CSS로 처리
|
|
7864
|
+
isProgrammatic.current = true;
|
|
7865
|
+
wheelRef.current.scrollTo({ top, behavior: smooth ? 'smooth' : 'auto' });
|
|
7866
|
+
window.setTimeout(() => { isProgrammatic.current = false; }, 150);
|
|
7867
|
+
}, []);
|
|
7868
|
+
React.useEffect(() => {
|
|
7869
|
+
const i = indexOfValue(value);
|
|
7870
|
+
if (i >= 0)
|
|
7871
|
+
scrollToIndex(i, false);
|
|
7872
|
+
}, [value, indexOfValue, scrollToIndex]);
|
|
7873
|
+
React.useEffect(() => {
|
|
7874
|
+
const i = indexOfValue(value);
|
|
7875
|
+
if (i >= 0)
|
|
7876
|
+
scrollToIndex(i, false);
|
|
7877
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
7878
|
+
}, []);
|
|
7879
|
+
const handleScroll = () => {
|
|
7880
|
+
if (isProgrammatic.current)
|
|
7881
|
+
return;
|
|
7882
|
+
if (!wheelRef.current)
|
|
7883
|
+
return;
|
|
7884
|
+
if (scrollTimeout.current)
|
|
7885
|
+
window.clearTimeout(scrollTimeout.current);
|
|
7886
|
+
scrollTimeout.current = window.setTimeout(() => {
|
|
7887
|
+
if (!wheelRef.current)
|
|
7888
|
+
return;
|
|
7889
|
+
const top = wheelRef.current.scrollTop;
|
|
7890
|
+
const idx = Math.round(top / ITEM_HEIGHT);
|
|
7891
|
+
const clamped = Math.max(0, Math.min(items.length - 1, idx));
|
|
7892
|
+
const v = items[clamped];
|
|
7893
|
+
scrollToIndex(clamped, true);
|
|
7894
|
+
if (v !== value)
|
|
7895
|
+
onChange(v);
|
|
7896
|
+
}, 120);
|
|
7897
|
+
};
|
|
7898
|
+
const fmt = (v) => (formatter ? formatter(v) : (typeof v === 'number' ? pad2(v) : String(v)));
|
|
7899
|
+
return (jsxRuntimeExports.jsxs("div", { className: "designbase-time-picker__wheel-container", children: [jsxRuntimeExports.jsx("div", { className: "designbase-time-picker__wheel-label", children: label }), jsxRuntimeExports.jsxs("div", { className: "designbase-time-picker__wheel-viewport", children: [jsxRuntimeExports.jsx("div", { ref: wheelRef, className: "designbase-time-picker__wheel", onScroll: handleScroll, role: "listbox", "aria-label": label, children: items.map((item, i) => (jsxRuntimeExports.jsx("div", { className: clsx('designbase-time-picker__wheel-item', item === value && 'designbase-time-picker__wheel-item--selected'), role: "option", "aria-selected": item === value, onClick: () => {
|
|
7900
|
+
const idx = indexOfValue(item);
|
|
7901
|
+
scrollToIndex(idx, true);
|
|
7902
|
+
if (item !== value)
|
|
7903
|
+
onChange(item);
|
|
7904
|
+
}, children: fmt(item) }, `${label}-${item}-${i}`))) }), jsxRuntimeExports.jsx("div", { "aria-hidden": true, className: "designbase-time-picker__wheel-center-indicator" }), jsxRuntimeExports.jsx("div", { "aria-hidden": true, className: "designbase-time-picker__wheel-fade wheel-fade--top" }), jsxRuntimeExports.jsx("div", { "aria-hidden": true, className: "designbase-time-picker__wheel-fade wheel-fade--bottom" })] })] }));
|
|
7905
|
+
};
|
|
7906
|
+
const renderTimeSelector = (isModal = false) => {
|
|
7907
|
+
const hourItems = is12h ? Array.from({ length: 12 }, (_, i) => i + 1) : Array.from({ length: 24 }, (_, i) => i);
|
|
7908
|
+
const minuteItems = Array.from({ length: Math.floor(60 / minuteStep) }, (_, i) => i * minuteStep);
|
|
7909
|
+
const secondItems = Array.from({ length: 60 }, (_, i) => i);
|
|
7910
|
+
const periodItems = ['AM', 'PM'];
|
|
7911
|
+
// 모달인 경우 임시 값 사용, 드롭다운인 경우 실제 값 사용
|
|
7912
|
+
const currentHours = isModal ? tempHours : hours;
|
|
7913
|
+
const currentMinutes = isModal ? tempMinutes : minutes;
|
|
7914
|
+
const currentSeconds = isModal ? tempSeconds : seconds;
|
|
7915
|
+
const currentPeriod = isModal ? tempPeriod : period;
|
|
7916
|
+
const handleChange = isModal ? handleModalTimeChange : handleTimeChange;
|
|
7917
|
+
return (jsxRuntimeExports.jsxs("div", { className: "designbase-time-picker__selector", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-time-picker__picker-wheels", children: [jsxRuntimeExports.jsx(WheelPicker, { label: "\uC2DC\uAC04", items: hourItems, value: currentHours, onChange: (v) => handleChange(v, currentMinutes, currentSeconds, currentPeriod) }), jsxRuntimeExports.jsx(WheelPicker, { label: "\uBD84", items: minuteItems, value: currentMinutes, onChange: (v) => handleChange(currentHours, v, currentSeconds, currentPeriod) }), showSeconds && (jsxRuntimeExports.jsx(WheelPicker, { label: "\uCD08", items: secondItems, value: currentSeconds, onChange: (v) => handleChange(currentHours, currentMinutes, v, currentPeriod) })), is12h && (jsxRuntimeExports.jsx(WheelPicker, { label: "AM/PM", items: periodItems, value: currentPeriod, onChange: (v) => handleChange(currentHours, currentMinutes, currentSeconds, v), formatter: (x) => String(x) }))] }), jsxRuntimeExports.jsxs("div", { className: "designbase-time-picker__preview", "aria-live": "polite", children: [jsxRuntimeExports.jsx(icons.ClockIcon, { size: 20 }), jsxRuntimeExports.jsx("div", { className: "designbase-time-picker__preview-time", children: formatTimeString(currentHours, currentMinutes, currentSeconds, currentPeriod) })] }), !isModal && (jsxRuntimeExports.jsx("div", { className: "designbase-time-picker__dropdown-actions", children: jsxRuntimeExports.jsx(Button, { variant: "primary", size: "m", onPress: () => {
|
|
7918
|
+
handleTimeChange(currentHours, currentMinutes, currentSeconds, currentPeriod);
|
|
7919
|
+
setIsOpen(false);
|
|
7920
|
+
}, children: "\uC120\uD0DD" }) }))] }));
|
|
7921
|
+
};
|
|
7922
|
+
const classes = clsx('designbase-time-picker', `designbase-time-picker--${size}`, { 'designbase-time-picker--disabled': disabled, 'designbase-time-picker--readonly': readonly, 'designbase-time-picker--open': isOpen }, className);
|
|
7923
|
+
return (jsxRuntimeExports.jsxs("div", { ref: pickerRef, className: classes, children: [jsxRuntimeExports.jsxs("div", { className: "designbase-time-picker__trigger", children: [jsxRuntimeExports.jsx("button", { type: "button", className: "designbase-time-picker__icon-display", onClick: togglePicker, disabled: disabled, "aria-label": "\uC2DC\uAC04 \uC120\uD0DD \uC5F4\uAE30", children: jsxRuntimeExports.jsx(icons.ClockIcon, { size: 16 }) }), jsxRuntimeExports.jsx("input", { type: "text", value: inputValue, onChange: (e) => setInputValue(e.target.value), onBlur: handleInputBlur, onClick: (e) => e.stopPropagation(), disabled: disabled, readOnly: readonly, className: "designbase-time-picker__input", placeholder: is12h ? (showSeconds ? '12:00:00 PM' : '12:00 PM') : (showSeconds ? '12:00:00' : '12:00'), "aria-label": "\uC2DC\uAC04 \uC785\uB825" }), jsxRuntimeExports.jsx("button", { type: "button", className: "designbase-time-picker__toggle", onClick: togglePicker, disabled: disabled, "aria-label": "\uC2DC\uAC04 \uC120\uD0DD \uC5F4\uAE30", children: jsxRuntimeExports.jsx(icons.ChevronDownIcon, { size: 16 }) })] }), type === 'dropdown' && isOpen && (jsxRuntimeExports.jsx("div", { className: "designbase-time-picker__dropdown", role: "dialog", "aria-modal": "false", children: renderTimeSelector(false) })), type === 'modal' && (jsxRuntimeExports.jsxs(Modal, { isOpen: isOpen, onClose: handleModalCancel, title: "\uC2DC\uAC04 \uC120\uD0DD", size: "s", children: [renderTimeSelector(true), jsxRuntimeExports.jsx(ModalFooter, { children: jsxRuntimeExports.jsxs("div", { className: "designbase-time-picker__modal-footer", children: [jsxRuntimeExports.jsx(Button, { variant: "secondary", size: "m", onPress: handleModalCancel, children: "\uCDE8\uC18C" }), jsxRuntimeExports.jsx(Button, { variant: "primary", size: "m", onPress: handleModalApply, children: "\uC801\uC6A9" })] }) })] }))] }));
|
|
7924
|
+
};
|
|
7925
|
+
TimePicker$1.displayName = 'TimePicker';
|
|
7926
|
+
|
|
7927
|
+
const Form = ({ fields, columns = 1, size = 'm', variant = 'default', onSubmit, onChange, initialValues = {}, submitText = '제출', submitLoading = false, submitDisabled = false, showReset = false, resetText = '리셋', title, description, className, style, }) => {
|
|
7341
7928
|
const [values, setValues] = React.useState(initialValues);
|
|
7342
7929
|
const [errors, setErrors] = React.useState({});
|
|
7343
7930
|
const [touched, setTouched] = React.useState({});
|
|
@@ -7497,20 +8084,20 @@ const Form = ({ fields, layout = 'vertical', size = 'm', variant = 'default', on
|
|
|
7497
8084
|
}
|
|
7498
8085
|
return (jsxRuntimeExports.jsx(Checkbox, { ...commonProps, isSelected: fieldValue || false, isDisabled: field.disabled, children: field.label }));
|
|
7499
8086
|
case 'file':
|
|
7500
|
-
return (jsxRuntimeExports.jsx(
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
8087
|
+
return (jsxRuntimeExports.jsx(FileUploader, { size: size, accept: field.fileUpload?.accept, maxSize: field.fileUpload?.maxSize, multiple: field.fileUpload?.multiple, disabled: field.disabled, readonly: field.readOnly, onUpload: (files) => {
|
|
8088
|
+
handleFieldChange(field.name, files);
|
|
8089
|
+
} }));
|
|
8090
|
+
case 'date':
|
|
8091
|
+
return (jsxRuntimeExports.jsx(DatePicker, { size: size, value: fieldValue, disabled: field.disabled, readonly: field.readOnly, mode: "single", type: "dropdown", onChange: (date) => handleFieldChange(field.name, date) }));
|
|
8092
|
+
case 'color':
|
|
8093
|
+
return (jsxRuntimeExports.jsx(ColorPicker$1, { size: size, value: fieldValue, disabled: field.disabled, readonly: field.readOnly, showAlpha: true, showFormatSelector: true, showCopyButton: true, onChange: (color) => handleFieldChange(field.name, color) }));
|
|
8094
|
+
case 'timepicker':
|
|
8095
|
+
return (jsxRuntimeExports.jsx(TimePicker$1, { size: size, value: fieldValue, disabled: field.disabled, readonly: field.readOnly, format: "24h", type: "dropdown", minuteStep: 1, onChange: (time) => handleFieldChange(field.name, time) }));
|
|
7509
8096
|
default:
|
|
7510
8097
|
return (jsxRuntimeExports.jsx(Input, { ...commonProps, type: field.type, min: field.min, max: field.max, minLength: field.minLength, maxLength: field.maxLength, pattern: field.pattern }));
|
|
7511
8098
|
}
|
|
7512
8099
|
}, [values, errors, touched, handleFieldChange, handleFieldBlur, size]);
|
|
7513
|
-
const classes = clsx('designbase-form', `designbase-form
|
|
8100
|
+
const classes = clsx('designbase-form', `designbase-form--columns-${columns}`, `designbase-form--${size}`, `designbase-form--${variant}`, className);
|
|
7514
8101
|
return (jsxRuntimeExports.jsxs("form", { ref: formRef, className: classes, style: style, onSubmit: handleSubmit, children: [(title || description) && (jsxRuntimeExports.jsxs("div", { className: "designbase-form__header", children: [title && jsxRuntimeExports.jsx("h3", { className: "designbase-form__title", children: title }), description && jsxRuntimeExports.jsx("p", { className: "designbase-form__description", children: description })] })), jsxRuntimeExports.jsx("div", { className: "designbase-form__fields", children: fields.map(field => (jsxRuntimeExports.jsxs("div", { className: clsx('designbase-form__field-wrapper', `designbase-form__field-wrapper--${field.type}`), children: [field.label && (jsxRuntimeExports.jsxs("label", { className: "designbase-form__label", htmlFor: field.name, children: [field.label, field.required && jsxRuntimeExports.jsx("span", { className: "designbase-form__required", children: "*" })] })), renderField(field), touched[field.name] && errors[field.name] && (jsxRuntimeExports.jsx("div", { className: "designbase-form__error", children: errors[field.name] })), field.helpText && !errors[field.name] && (jsxRuntimeExports.jsx("div", { className: "designbase-form__help", children: field.helpText }))] }, field.name))) }), jsxRuntimeExports.jsxs("div", { className: "designbase-form__actions", children: [jsxRuntimeExports.jsx(Button, { type: "submit", variant: "primary", size: size, loading: submitLoading, disabled: submitDisabled, children: submitText }), showReset && (jsxRuntimeExports.jsx(Button, { type: "button", variant: "secondary", size: size, onClick: handleReset, children: resetText }))] })] }));
|
|
7515
8102
|
};
|
|
7516
8103
|
|
|
@@ -7647,7 +8234,7 @@ const Stat = ({ value, label, icon, iconPosition = 'left', size = 'm', variant =
|
|
|
7647
8234
|
return;
|
|
7648
8235
|
onClick?.();
|
|
7649
8236
|
};
|
|
7650
|
-
return (jsxRuntimeExports.jsxs("div", { className: classes, style: customStyle, onClick: handleClick, role: clickable ? 'button' : undefined, tabIndex: clickable ? 0 : undefined, children: [icon && (jsxRuntimeExports.jsx("div", { className: clsx('designbase-stat__icon', `designbase-stat__icon--${iconPosition}`), children: icon })), jsxRuntimeExports.jsxs("div", { className: "designbase-stat__content", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-stat__main", children: [jsxRuntimeExports.jsx("div", { className: "designbase-stat__value", children: value }), jsxRuntimeExports.jsx("div", { className: "designbase-stat__label", children: label })] }), showChange && change && (jsxRuntimeExports.jsx("div", { className: clsx('designbase-stat__change', getChangeClass()), children: getChangeText() })), showDescription && description && (jsxRuntimeExports.jsx("div", { className: "designbase-stat__description", children: description }))] }), showProgress && progress
|
|
8237
|
+
return (jsxRuntimeExports.jsxs("div", { className: classes, style: customStyle, onClick: handleClick, role: clickable ? 'button' : undefined, tabIndex: clickable ? 0 : undefined, children: [icon && (jsxRuntimeExports.jsx("div", { className: clsx('designbase-stat__icon', `designbase-stat__icon--${iconPosition}`), children: icon })), jsxRuntimeExports.jsxs("div", { className: "designbase-stat__content", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-stat__main", children: [jsxRuntimeExports.jsx("div", { className: "designbase-stat__value", children: value }), jsxRuntimeExports.jsx("div", { className: "designbase-stat__label", children: label })] }), showChange && change && (jsxRuntimeExports.jsx("div", { className: clsx('designbase-stat__change', getChangeClass()), children: getChangeText() })), showDescription && description && (jsxRuntimeExports.jsx("div", { className: "designbase-stat__description", children: description }))] }), showProgress && typeof progress === 'number' && (jsxRuntimeExports.jsx("div", { className: "designbase-stat__progress", children: jsxRuntimeExports.jsx(Progressbar, { value: Math.min(Math.max(progress, 0), 100), size: "s", variant: color === 'primary' ? 'primary' : color === 'success' ? 'success' : color === 'warning' ? 'warning' : color === 'error' ? 'danger' : 'default' }) }))] }));
|
|
7651
8238
|
};
|
|
7652
8239
|
|
|
7653
8240
|
const HeroFeature = ({ title, subtitle, description, image, imageAlt, backgroundImage, backgroundVideo, overlayColor, overlayOpacity = 0.5, buttons = [], badge, stats = [], icon, size = 'l', variant = 'default', theme = 'light', alignment = 'left', animated = false, parallax = false, fullHeight = false, minHeight, maxHeight, className, }) => {
|
|
@@ -7728,7 +8315,7 @@ const HeroFeature = ({ title, subtitle, description, image, imageAlt, background
|
|
|
7728
8315
|
return (jsxRuntimeExports.jsxs("section", { className: classes, style: containerStyle, children: [renderBackground(), renderOverlay(), jsxRuntimeExports.jsxs("div", { className: "designbase-hero-feature__container", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-hero-feature__content", children: [icon && (jsxRuntimeExports.jsx("div", { className: "designbase-hero-feature__icon", children: icon })), renderBadge(), jsxRuntimeExports.jsx("h1", { className: "designbase-hero-feature__title", children: title }), subtitle && (jsxRuntimeExports.jsx("h2", { className: "designbase-hero-feature__subtitle", children: subtitle })), description && (jsxRuntimeExports.jsx("p", { className: "designbase-hero-feature__description", children: description })), renderButtons(), renderStats()] }), variant === 'split' && renderImage()] }), variant !== 'split' && renderImage()] }));
|
|
7729
8316
|
};
|
|
7730
8317
|
|
|
7731
|
-
const Lightbox = ({ images, currentIndex = 0, size = 'l',
|
|
8318
|
+
const Lightbox = ({ images, currentIndex = 0, size = 'l', isOpen, onOpenChange, onImageChange, enableZoom = true, enableRotate = true, enableDownload = true, enableFullscreen = true, enableKeyboard = true, enableWheelZoom = true, showThumbnails = true, showCounter = true, showCloseButton = true, showNavigationButtons = true, showToolbar = true, closeOnBackdropClick = true, closeOnEscape = true, className, }) => {
|
|
7732
8319
|
const [internalCurrentIndex, setInternalCurrentIndex] = React.useState(currentIndex);
|
|
7733
8320
|
const [zoomLevel, setZoomLevel] = React.useState(1);
|
|
7734
8321
|
const [rotation, setRotation] = React.useState(0);
|
|
@@ -7789,32 +8376,32 @@ const Lightbox = ({ images, currentIndex = 0, size = 'l', variant = 'default', t
|
|
|
7789
8376
|
}, [onOpenChange]);
|
|
7790
8377
|
// 줌 인
|
|
7791
8378
|
const handleZoomIn = React.useCallback(() => {
|
|
7792
|
-
if (!enableZoom
|
|
8379
|
+
if (!enableZoom)
|
|
7793
8380
|
return;
|
|
7794
8381
|
setZoomLevel(prev => Math.min(prev * 1.5, 5));
|
|
7795
|
-
}, [enableZoom
|
|
8382
|
+
}, [enableZoom]);
|
|
7796
8383
|
// 줌 아웃
|
|
7797
8384
|
const handleZoomOut = React.useCallback(() => {
|
|
7798
|
-
if (!enableZoom
|
|
8385
|
+
if (!enableZoom)
|
|
7799
8386
|
return;
|
|
7800
8387
|
setZoomLevel(prev => Math.max(prev / 1.5, 0.1));
|
|
7801
|
-
}, [enableZoom
|
|
8388
|
+
}, [enableZoom]);
|
|
7802
8389
|
// 줌 리셋
|
|
7803
8390
|
const handleZoomReset = React.useCallback(() => {
|
|
7804
|
-
if (!enableZoom
|
|
8391
|
+
if (!enableZoom)
|
|
7805
8392
|
return;
|
|
7806
8393
|
setZoomLevel(1);
|
|
7807
8394
|
setImagePosition({ x: 0, y: 0 });
|
|
7808
|
-
}, [enableZoom
|
|
8395
|
+
}, [enableZoom]);
|
|
7809
8396
|
// 회전
|
|
7810
8397
|
const handleRotate = React.useCallback(() => {
|
|
7811
|
-
if (!enableRotate
|
|
8398
|
+
if (!enableRotate)
|
|
7812
8399
|
return;
|
|
7813
8400
|
setRotation(prev => (prev + 90) % 360);
|
|
7814
|
-
}, [enableRotate
|
|
8401
|
+
}, [enableRotate]);
|
|
7815
8402
|
// 다운로드
|
|
7816
8403
|
const handleDownload = React.useCallback(() => {
|
|
7817
|
-
if (!enableDownload
|
|
8404
|
+
if (!enableDownload)
|
|
7818
8405
|
return;
|
|
7819
8406
|
const currentImage = images[internalCurrentIndex];
|
|
7820
8407
|
if (currentImage) {
|
|
@@ -7851,13 +8438,13 @@ const Lightbox = ({ images, currentIndex = 0, size = 'l', variant = 'default', t
|
|
|
7851
8438
|
document.body.removeChild(link);
|
|
7852
8439
|
});
|
|
7853
8440
|
}
|
|
7854
|
-
}, [enableDownload,
|
|
8441
|
+
}, [enableDownload, images, internalCurrentIndex]);
|
|
7855
8442
|
// 전체화면 토글
|
|
7856
8443
|
const handleFullscreenToggle = React.useCallback(() => {
|
|
7857
|
-
if (!enableFullscreen
|
|
8444
|
+
if (!enableFullscreen)
|
|
7858
8445
|
return;
|
|
7859
8446
|
setIsFullscreen(prev => !prev);
|
|
7860
|
-
}, [enableFullscreen
|
|
8447
|
+
}, [enableFullscreen]);
|
|
7861
8448
|
// 이미지 로드 핸들러
|
|
7862
8449
|
const handleImageLoad = React.useCallback(() => {
|
|
7863
8450
|
setIsImageLoaded(true);
|
|
@@ -7881,7 +8468,7 @@ const Lightbox = ({ images, currentIndex = 0, size = 'l', variant = 'default', t
|
|
|
7881
8468
|
}, [isOpen, internalCurrentIndex, images]);
|
|
7882
8469
|
// 마우스 휠 줌 핸들러
|
|
7883
8470
|
const handleWheel = React.useCallback((event) => {
|
|
7884
|
-
if (!enableWheelZoom
|
|
8471
|
+
if (!enableWheelZoom)
|
|
7885
8472
|
return;
|
|
7886
8473
|
event.preventDefault();
|
|
7887
8474
|
if (event.deltaY < 0) {
|
|
@@ -7890,44 +8477,40 @@ const Lightbox = ({ images, currentIndex = 0, size = 'l', variant = 'default', t
|
|
|
7890
8477
|
else {
|
|
7891
8478
|
handleZoomOut();
|
|
7892
8479
|
}
|
|
7893
|
-
}, [enableWheelZoom,
|
|
8480
|
+
}, [enableWheelZoom, handleZoomIn, handleZoomOut]);
|
|
7894
8481
|
// 드래그 시작 핸들러
|
|
7895
8482
|
const handleMouseDown = React.useCallback((event) => {
|
|
7896
|
-
if (zoomLevel <= 1
|
|
8483
|
+
if (zoomLevel <= 1)
|
|
7897
8484
|
return;
|
|
7898
8485
|
event.preventDefault();
|
|
7899
8486
|
setIsDragging(true);
|
|
7900
8487
|
setDragStart({ x: event.clientX - imagePosition.x, y: event.clientY - imagePosition.y });
|
|
7901
|
-
}, [zoomLevel,
|
|
8488
|
+
}, [zoomLevel, imagePosition]);
|
|
7902
8489
|
// 드래그 중 핸들러
|
|
7903
8490
|
const handleMouseMove = React.useCallback((event) => {
|
|
7904
|
-
if (!isDragging || zoomLevel <= 1
|
|
8491
|
+
if (!isDragging || zoomLevel <= 1)
|
|
7905
8492
|
return;
|
|
7906
8493
|
event.preventDefault();
|
|
7907
8494
|
setImagePosition({
|
|
7908
8495
|
x: event.clientX - dragStart.x,
|
|
7909
8496
|
y: event.clientY - dragStart.y,
|
|
7910
8497
|
});
|
|
7911
|
-
}, [isDragging, zoomLevel,
|
|
8498
|
+
}, [isDragging, zoomLevel, dragStart]);
|
|
7912
8499
|
// 드래그 종료 핸들러
|
|
7913
8500
|
const handleMouseUp = React.useCallback(() => {
|
|
7914
8501
|
setIsDragging(false);
|
|
7915
8502
|
}, []);
|
|
7916
8503
|
// 터치 시작 핸들러
|
|
7917
8504
|
const handleTouchStart = React.useCallback((event) => {
|
|
7918
|
-
if (disabled || readonly)
|
|
7919
|
-
return;
|
|
7920
8505
|
const touch = event.touches[0];
|
|
7921
8506
|
setTouchStart({ x: touch.clientX, y: touch.clientY });
|
|
7922
|
-
}, [
|
|
8507
|
+
}, []);
|
|
7923
8508
|
// 터치 종료 핸들러
|
|
7924
8509
|
const handleTouchEnd = React.useCallback((event) => {
|
|
7925
|
-
if (disabled || readonly)
|
|
7926
|
-
return;
|
|
7927
8510
|
const touch = event.changedTouches[0];
|
|
7928
8511
|
setTouchEnd({ x: touch.clientX, y: touch.clientY });
|
|
7929
8512
|
handleSwipe();
|
|
7930
|
-
}, [
|
|
8513
|
+
}, []);
|
|
7931
8514
|
// 스와이프 처리
|
|
7932
8515
|
const handleSwipe = React.useCallback(() => {
|
|
7933
8516
|
const deltaX = touchStart.x - touchEnd.x;
|
|
@@ -7947,7 +8530,7 @@ const Lightbox = ({ images, currentIndex = 0, size = 'l', variant = 'default', t
|
|
|
7947
8530
|
}, [touchStart, touchEnd, goToNext, goToPrevious]);
|
|
7948
8531
|
// 키보드 이벤트 핸들러
|
|
7949
8532
|
React.useEffect(() => {
|
|
7950
|
-
if (!enableKeyboard || !isOpen
|
|
8533
|
+
if (!enableKeyboard || !isOpen)
|
|
7951
8534
|
return;
|
|
7952
8535
|
const handleKeyDown = (event) => {
|
|
7953
8536
|
switch (event.key) {
|
|
@@ -7988,8 +8571,6 @@ const Lightbox = ({ images, currentIndex = 0, size = 'l', variant = 'default', t
|
|
|
7988
8571
|
}, [
|
|
7989
8572
|
enableKeyboard,
|
|
7990
8573
|
isOpen,
|
|
7991
|
-
disabled,
|
|
7992
|
-
readonly,
|
|
7993
8574
|
closeOnEscape,
|
|
7994
8575
|
handleClose,
|
|
7995
8576
|
goToPrevious,
|
|
@@ -8023,11 +8604,9 @@ const Lightbox = ({ images, currentIndex = 0, size = 'l', variant = 'default', t
|
|
|
8023
8604
|
const currentImage = images[internalCurrentIndex];
|
|
8024
8605
|
if (!isOpen || !currentImage)
|
|
8025
8606
|
return null;
|
|
8026
|
-
return (jsxRuntimeExports.jsx("div", { className: clsx('designbase-lightbox', `designbase-lightbox--size-${size}`,
|
|
8607
|
+
return (jsxRuntimeExports.jsx("div", { className: clsx('designbase-lightbox', `designbase-lightbox--size-${size}`, {
|
|
8027
8608
|
'designbase-lightbox--fullscreen': isFullscreen,
|
|
8028
|
-
|
|
8029
|
-
'designbase-lightbox--readonly': readonly,
|
|
8030
|
-
}, className), children: jsxRuntimeExports.jsx("div", { className: "designbase-lightbox__overlay", children: jsxRuntimeExports.jsxs("div", { ref: modalRef, className: "designbase-lightbox__modal", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-lightbox__header", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-lightbox__title", children: [currentImage.title && (jsxRuntimeExports.jsx("h3", { className: "designbase-lightbox__title-text", children: currentImage.title })), showCounter && (jsxRuntimeExports.jsxs("span", { className: "designbase-lightbox__counter", children: [internalCurrentIndex + 1, " / ", images.length] }))] }), jsxRuntimeExports.jsxs("div", { className: "designbase-lightbox__header-actions", children: [showToolbar && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [enableZoom && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("button", { className: "designbase-lightbox__toolbar-button", onClick: handleZoomOut, disabled: disabled || readonly, title: "\uC90C \uC544\uC6C3", type: "button", children: jsxRuntimeExports.jsx(icons.MinusIcon, { size: 16 }) }), jsxRuntimeExports.jsxs("button", { className: "designbase-lightbox__toolbar-button", onClick: handleZoomReset, disabled: disabled || readonly, title: "\uC90C \uB9AC\uC14B", type: "button", children: [Math.round(zoomLevel * 100), "%"] }), jsxRuntimeExports.jsx("button", { className: "designbase-lightbox__toolbar-button", onClick: handleZoomIn, disabled: disabled || readonly, title: "\uC90C \uC778", type: "button", children: jsxRuntimeExports.jsx(icons.PlusIcon, { size: 16 }) })] })), enableRotate && (jsxRuntimeExports.jsx("button", { className: "designbase-lightbox__toolbar-button", onClick: handleRotate, disabled: disabled || readonly, title: "\uD68C\uC804", type: "button", children: jsxRuntimeExports.jsx(icons.RefreshIcon, { size: 16 }) })), enableDownload && (jsxRuntimeExports.jsx("button", { className: "designbase-lightbox__toolbar-button", onClick: handleDownload, disabled: disabled || readonly, title: "\uB2E4\uC6B4\uB85C\uB4DC", type: "button", children: jsxRuntimeExports.jsx(icons.DownloadIcon, { size: 16 }) })), enableFullscreen && (jsxRuntimeExports.jsx("button", { className: "designbase-lightbox__toolbar-button", onClick: handleFullscreenToggle, disabled: disabled || readonly, title: isFullscreen ? "전체화면 해제" : "전체화면", type: "button", children: isFullscreen ? jsxRuntimeExports.jsx(icons.ShrinkIcon, { size: 16 }) : jsxRuntimeExports.jsx(icons.ExpandIcon, { size: 16 }) }))] })), showCloseButton && (jsxRuntimeExports.jsx("button", { className: "designbase-lightbox__close-button", onClick: handleClose, disabled: disabled, title: "\uB2EB\uAE30", type: "button", children: jsxRuntimeExports.jsx(icons.CloseIcon, { size: 20 }) }))] })] }), jsxRuntimeExports.jsxs("div", { className: "designbase-lightbox__content", children: [showNavigationButtons && images.length > 1 && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("button", { className: "designbase-lightbox__nav-button designbase-lightbox__nav-button--prev", onClick: goToPrevious, disabled: disabled || readonly, title: "\uC774\uC804 \uC774\uBBF8\uC9C0", type: "button", children: jsxRuntimeExports.jsx(icons.ChevronLeftIcon, { size: 24 }) }), jsxRuntimeExports.jsx("button", { className: "designbase-lightbox__nav-button designbase-lightbox__nav-button--next", onClick: goToNext, disabled: disabled || readonly, title: "\uB2E4\uC74C \uC774\uBBF8\uC9C0", type: "button", children: jsxRuntimeExports.jsx(icons.ChevronRightIcon, { size: 24 }) })] })), jsxRuntimeExports.jsxs("div", { ref: containerRef, className: "designbase-lightbox__image-container", onWheel: handleWheel, onMouseDown: handleMouseDown, onMouseMove: handleMouseMove, onMouseUp: handleMouseUp, onMouseLeave: handleMouseUp, onTouchStart: handleTouchStart, onTouchEnd: handleTouchEnd, children: [isImageError ? (jsxRuntimeExports.jsxs("div", { className: "designbase-lightbox__error", children: [jsxRuntimeExports.jsx("div", { className: "designbase-lightbox__error-icon", children: "\u26A0\uFE0F" }), jsxRuntimeExports.jsx("p", { className: "designbase-lightbox__error-text", children: "\uC774\uBBF8\uC9C0\uB97C \uBD88\uB7EC\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4." })] })) : (jsxRuntimeExports.jsx("img", { ref: imageRef, src: currentImage.src, alt: currentImage.alt || currentImage.title || `이미지 ${internalCurrentIndex + 1}`, className: clsx('designbase-lightbox__image', {
|
|
8609
|
+
}, className), children: jsxRuntimeExports.jsx("div", { className: "designbase-lightbox__overlay", children: jsxRuntimeExports.jsxs("div", { ref: modalRef, className: "designbase-lightbox__modal", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-lightbox__header", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-lightbox__title", children: [currentImage.title && (jsxRuntimeExports.jsx("h3", { className: "designbase-lightbox__title-text", children: currentImage.title })), showCounter && (jsxRuntimeExports.jsxs("span", { className: "designbase-lightbox__counter", children: [internalCurrentIndex + 1, " / ", images.length] }))] }), jsxRuntimeExports.jsxs("div", { className: "designbase-lightbox__header-actions", children: [showToolbar && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [enableZoom && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("button", { className: "designbase-lightbox__toolbar-button", onClick: handleZoomOut, title: "\uC90C \uC544\uC6C3", type: "button", children: jsxRuntimeExports.jsx(icons.MinusIcon, { size: 16 }) }), jsxRuntimeExports.jsxs("button", { className: "designbase-lightbox__toolbar-button", onClick: handleZoomReset, title: "\uC90C \uB9AC\uC14B", type: "button", children: [Math.round(zoomLevel * 100), "%"] }), jsxRuntimeExports.jsx("button", { className: "designbase-lightbox__toolbar-button", onClick: handleZoomIn, title: "\uC90C \uC778", type: "button", children: jsxRuntimeExports.jsx(icons.PlusIcon, { size: 16 }) })] })), enableRotate && (jsxRuntimeExports.jsx("button", { className: "designbase-lightbox__toolbar-button", onClick: handleRotate, title: "\uD68C\uC804", type: "button", children: jsxRuntimeExports.jsx(icons.RefreshIcon, { size: 16 }) })), enableDownload && (jsxRuntimeExports.jsx("button", { className: "designbase-lightbox__toolbar-button", onClick: handleDownload, title: "\uB2E4\uC6B4\uB85C\uB4DC", type: "button", children: jsxRuntimeExports.jsx(icons.DownloadIcon, { size: 16 }) })), enableFullscreen && (jsxRuntimeExports.jsx("button", { className: "designbase-lightbox__toolbar-button", onClick: handleFullscreenToggle, title: isFullscreen ? "전체화면 해제" : "전체화면", type: "button", children: isFullscreen ? jsxRuntimeExports.jsx(icons.ShrinkIcon, { size: 16 }) : jsxRuntimeExports.jsx(icons.ExpandIcon, { size: 16 }) }))] })), showCloseButton && (jsxRuntimeExports.jsx("button", { className: "designbase-lightbox__close-button", onClick: handleClose, title: "\uB2EB\uAE30", type: "button", children: jsxRuntimeExports.jsx(icons.CloseIcon, { size: 20 }) }))] })] }), jsxRuntimeExports.jsxs("div", { className: "designbase-lightbox__content", children: [showNavigationButtons && images.length > 1 && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("button", { className: "designbase-lightbox__nav-button designbase-lightbox__nav-button--prev", onClick: goToPrevious, title: "\uC774\uC804 \uC774\uBBF8\uC9C0", type: "button", children: jsxRuntimeExports.jsx(icons.ChevronLeftIcon, { size: 24 }) }), jsxRuntimeExports.jsx("button", { className: "designbase-lightbox__nav-button designbase-lightbox__nav-button--next", onClick: goToNext, title: "\uB2E4\uC74C \uC774\uBBF8\uC9C0", type: "button", children: jsxRuntimeExports.jsx(icons.ChevronRightIcon, { size: 24 }) })] })), jsxRuntimeExports.jsxs("div", { ref: containerRef, className: "designbase-lightbox__image-container", onWheel: handleWheel, onMouseDown: handleMouseDown, onMouseMove: handleMouseMove, onMouseUp: handleMouseUp, onMouseLeave: handleMouseUp, onTouchStart: handleTouchStart, onTouchEnd: handleTouchEnd, children: [isImageError ? (jsxRuntimeExports.jsxs("div", { className: "designbase-lightbox__error", children: [jsxRuntimeExports.jsx("div", { className: "designbase-lightbox__error-icon", children: "\u26A0\uFE0F" }), jsxRuntimeExports.jsx("p", { className: "designbase-lightbox__error-text", children: "\uC774\uBBF8\uC9C0\uB97C \uBD88\uB7EC\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4." })] })) : (jsxRuntimeExports.jsx("img", { ref: imageRef, src: currentImage.src, alt: currentImage.alt || currentImage.title || `이미지 ${internalCurrentIndex + 1}`, className: clsx('designbase-lightbox__image', {
|
|
8031
8610
|
'designbase-lightbox__image--loaded': isImageLoaded,
|
|
8032
8611
|
'designbase-lightbox__image--dragging': isDragging,
|
|
8033
8612
|
}), style: {
|
|
@@ -8035,10 +8614,10 @@ const Lightbox = ({ images, currentIndex = 0, size = 'l', variant = 'default', t
|
|
|
8035
8614
|
cursor: zoomLevel > 1 ? (isDragging ? 'grabbing' : 'grab') : 'default',
|
|
8036
8615
|
}, onLoad: handleImageLoad, onError: handleImageError })), !isImageLoaded && !isImageError && (jsxRuntimeExports.jsxs("div", { className: "designbase-lightbox__loading", children: [jsxRuntimeExports.jsx("div", { className: "designbase-lightbox__spinner" }), jsxRuntimeExports.jsx("p", { className: "designbase-lightbox__loading-text", children: "\uC774\uBBF8\uC9C0 \uB85C\uB529 \uC911..." })] }))] }), currentImage.description && (jsxRuntimeExports.jsx("div", { className: "designbase-lightbox__description", children: jsxRuntimeExports.jsx("p", { className: "designbase-lightbox__description-text", children: currentImage.description }) }))] }), showThumbnails && images.length > 1 && (jsxRuntimeExports.jsx("div", { className: "designbase-lightbox__thumbnails", children: jsxRuntimeExports.jsx("div", { className: "designbase-lightbox__thumbnails-container", children: images.map((image, index) => (jsxRuntimeExports.jsx("button", { className: clsx('designbase-lightbox__thumbnail', {
|
|
8037
8616
|
'designbase-lightbox__thumbnail--active': index === internalCurrentIndex,
|
|
8038
|
-
}), onClick: () => handleImageChange(index),
|
|
8617
|
+
}), onClick: () => handleImageChange(index), title: image.title || `이미지 ${index + 1}`, type: "button", children: jsxRuntimeExports.jsx("img", { src: image.thumbnail || image.src, alt: image.alt || image.title || `썸네일 ${index + 1}`, className: "designbase-lightbox__thumbnail-image" }) }, image.id))) }) }))] }) }) }));
|
|
8039
8618
|
};
|
|
8040
8619
|
|
|
8041
|
-
const ImageList = ({ images, layout = 'grid', columns = 3, spacing = '
|
|
8620
|
+
const ImageList = ({ images, layout = 'grid', columns = 3, spacing = 'm', ratio = '16:9', fit = 'cover', rounded = false, shadow = false, hover = true, clickable = true, lightbox = true, onImageClick, onImageLoad, onImageError, loading = 'lazy', responsive = true, className, style, }) => {
|
|
8042
8621
|
const [selectedImageIndex, setSelectedImageIndex] = React.useState(null);
|
|
8043
8622
|
// 이미지 클릭 핸들러
|
|
8044
8623
|
const handleImageClick = React.useCallback((image, index) => {
|
|
@@ -8136,8 +8715,7 @@ const List = ({ items, size = 'm', variant = 'default', itemType = 'default', la
|
|
|
8136
8715
|
const renderBadge = (badge) => {
|
|
8137
8716
|
if (!badge)
|
|
8138
8717
|
return null;
|
|
8139
|
-
|
|
8140
|
-
return (jsxRuntimeExports.jsx("span", { className: badgeClasses, children: badge.text }));
|
|
8718
|
+
return (jsxRuntimeExports.jsx(Badge, { variant: badge.variant || 'primary', style: badge.style || 'text', size: "s", children: badge.text }));
|
|
8141
8719
|
};
|
|
8142
8720
|
const renderMeta = (meta) => {
|
|
8143
8721
|
if (!meta || meta.length === 0)
|
|
@@ -8550,7 +9128,7 @@ onFileUpload, onChange, onSave, onFocus, onBlur, className, }) => {
|
|
|
8550
9128
|
}, className: "designbase-markdown-editor__textarea" }) })), (currentMode === 'preview' || currentMode === 'split') && (jsxRuntimeExports.jsx("div", { className: "designbase-markdown-editor__preview-panel", children: jsxRuntimeExports.jsx("div", { ref: previewRef, className: "designbase-markdown-editor__preview", dangerouslySetInnerHTML: { __html: markdownToHtml(internalValue) } }) })), currentMode === 'code' && (jsxRuntimeExports.jsx("div", { className: "designbase-markdown-editor__code-panel", children: jsxRuntimeExports.jsx("pre", { className: "designbase-markdown-editor__code-view", children: jsxRuntimeExports.jsx("code", { children: internalValue }) }) }))] }), renderStatus()] }));
|
|
8551
9129
|
};
|
|
8552
9130
|
|
|
8553
|
-
const Masonry = ({ images, columns = 3, spacing = '
|
|
9131
|
+
const Masonry = ({ images, columns = 3, spacing = 'm', ratio = 'auto', fit = 'cover', rounded = false, shadow = false, hover = true, clickable = true, lightbox = true, animation = 'fade', animationDelay = 100, onImageClick, onImageLoad, onImageError, loading = 'lazy', responsive = true, infiniteScroll = false, infiniteScrollThreshold = 100, onLoadMore, className, style, }) => {
|
|
8554
9132
|
const [selectedImageIndex, setSelectedImageIndex] = React.useState(null);
|
|
8555
9133
|
const [columnHeights, setColumnHeights] = React.useState([]);
|
|
8556
9134
|
const [visibleImages, setVisibleImages] = React.useState([]);
|
|
@@ -8567,7 +9145,7 @@ const Masonry = ({ images, columns = 3, spacing = 'md', ratio = 'auto', fit = 'c
|
|
|
8567
9145
|
const imageHeight = columnWidth * aspectRatio;
|
|
8568
9146
|
// 가장 짧은 컬럼에 이미지 추가
|
|
8569
9147
|
const shortestColumn = heights.indexOf(Math.min(...heights));
|
|
8570
|
-
heights[shortestColumn] += imageHeight + (spacing === 'xs' ? 4 : spacing === '
|
|
9148
|
+
heights[shortestColumn] += imageHeight + (spacing === 'xs' ? 4 : spacing === 's' ? 8 : spacing === 'm' ? 16 : spacing === 'l' ? 24 : 32);
|
|
8571
9149
|
}
|
|
8572
9150
|
});
|
|
8573
9151
|
setColumnHeights(heights);
|
|
@@ -8728,10 +9306,12 @@ const SearchBar = ({ value, defaultValue = '', placeholder = '검색...', size =
|
|
|
8728
9306
|
};
|
|
8729
9307
|
SearchBar.displayName = 'SearchBar';
|
|
8730
9308
|
|
|
8731
|
-
const Navbar = ({ size = 'm', variant = 'default', position = 'static', logo, onLogoClick, items = [], onItemClick, userMenuItems = [], onUserMenuItemClick, userProfile,
|
|
9309
|
+
const Navbar = ({ size = 'm', variant = 'default', position = 'static', logo, onLogoClick, items = [], onItemClick, userMenuItems = [], onUserMenuItemClick, userProfile, onLoginClick, onLogoutClick, isAuthenticated = false, showSearch = false, onSearch, searchPlaceholder = '검색...', fullWidth = false, shadow = false, className, ...props }) => {
|
|
8732
9310
|
const [isMobileMenuOpen, setIsMobileMenuOpen] = React.useState(false);
|
|
8733
9311
|
const [isUserMenuOpen, setIsUserMenuOpen] = React.useState(false);
|
|
8734
9312
|
const [searchQuery, setSearchQuery] = React.useState('');
|
|
9313
|
+
const [expandedItems, setExpandedItems] = React.useState({});
|
|
9314
|
+
const navbarRef = React.useRef(null);
|
|
8735
9315
|
// 아이콘 크기 계산 (m이 기본값)
|
|
8736
9316
|
const iconSize = size === 's' ? 16 : size === 'l' ? 24 : 20;
|
|
8737
9317
|
const handleMobileMenuToggle = () => {
|
|
@@ -8740,14 +9320,26 @@ const Navbar = ({ size = 'm', variant = 'default', position = 'static', logo, on
|
|
|
8740
9320
|
const handleUserMenuToggle = () => {
|
|
8741
9321
|
setIsUserMenuOpen(!isUserMenuOpen);
|
|
8742
9322
|
};
|
|
8743
|
-
const handleItemClick = (item) => {
|
|
9323
|
+
const handleItemClick = (item, isMobile = false) => {
|
|
8744
9324
|
if (item.disabled)
|
|
8745
9325
|
return;
|
|
9326
|
+
// 모바일/태블릿에서만 하위 메뉴 확장/축소 토글
|
|
9327
|
+
if (isMobile && item.children && item.children.length > 0) {
|
|
9328
|
+
setExpandedItems(prev => ({
|
|
9329
|
+
...prev,
|
|
9330
|
+
[item.id]: !prev[item.id]
|
|
9331
|
+
}));
|
|
9332
|
+
// 하위 메뉴가 있는 경우 메뉴를 닫지 않음
|
|
9333
|
+
return;
|
|
9334
|
+
}
|
|
9335
|
+
if (item.onClick) {
|
|
9336
|
+
item.onClick(item);
|
|
9337
|
+
}
|
|
8746
9338
|
if (onItemClick) {
|
|
8747
9339
|
onItemClick(item);
|
|
8748
9340
|
}
|
|
8749
|
-
// 모바일 메뉴 자동 닫기
|
|
8750
|
-
if (isMobileMenuOpen) {
|
|
9341
|
+
// 모바일 메뉴 자동 닫기 (하위 메뉴가 없는 경우에만)
|
|
9342
|
+
if (isMobileMenuOpen && (!item.children || item.children.length === 0)) {
|
|
8751
9343
|
setIsMobileMenuOpen(false);
|
|
8752
9344
|
}
|
|
8753
9345
|
};
|
|
@@ -8759,107 +9351,103 @@ const Navbar = ({ size = 'm', variant = 'default', position = 'static', logo, on
|
|
|
8759
9351
|
}
|
|
8760
9352
|
setIsUserMenuOpen(false);
|
|
8761
9353
|
};
|
|
9354
|
+
const handleSubItemClick = (item) => {
|
|
9355
|
+
if (item.disabled)
|
|
9356
|
+
return;
|
|
9357
|
+
if (item.onClick) {
|
|
9358
|
+
item.onClick(item);
|
|
9359
|
+
}
|
|
9360
|
+
if (onItemClick) {
|
|
9361
|
+
onItemClick(item);
|
|
9362
|
+
}
|
|
9363
|
+
// 하위 메뉴 아이템 클릭 시 모바일 메뉴 닫기
|
|
9364
|
+
if (isMobileMenuOpen) {
|
|
9365
|
+
setIsMobileMenuOpen(false);
|
|
9366
|
+
}
|
|
9367
|
+
};
|
|
9368
|
+
// 외부 클릭 시 메뉴 닫기
|
|
9369
|
+
React.useEffect(() => {
|
|
9370
|
+
const handleClickOutside = (event) => {
|
|
9371
|
+
if (navbarRef.current && !navbarRef.current.contains(event.target)) {
|
|
9372
|
+
setIsUserMenuOpen(false);
|
|
9373
|
+
}
|
|
9374
|
+
};
|
|
9375
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
9376
|
+
return () => {
|
|
9377
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
9378
|
+
};
|
|
9379
|
+
}, []);
|
|
8762
9380
|
const classes = clsx('designbase-navbar', `designbase-navbar--${size}`, `designbase-navbar--${variant}`, `designbase-navbar--${position}`, {
|
|
8763
9381
|
'designbase-navbar--full-width': fullWidth,
|
|
8764
9382
|
'designbase-navbar--shadow': shadow,
|
|
8765
|
-
'designbase-navbar--mobile-open': isMobileMenuOpen,
|
|
8766
9383
|
}, className);
|
|
8767
9384
|
const containerClasses = clsx('designbase-navbar__container', {
|
|
8768
9385
|
'designbase-navbar__container--full-width': fullWidth,
|
|
8769
9386
|
});
|
|
8770
|
-
//
|
|
8771
|
-
const renderMenuItem = (item,
|
|
9387
|
+
// 메뉴 아이템을 렌더링하는 함수
|
|
9388
|
+
const renderMenuItem = (item, isMobile = false) => {
|
|
8772
9389
|
const hasChildren = item.children && item.children.length > 0;
|
|
8773
|
-
const
|
|
8774
|
-
|
|
8775
|
-
|
|
8776
|
-
|
|
8777
|
-
|
|
8778
|
-
|
|
8779
|
-
|
|
8780
|
-
|
|
8781
|
-
|
|
8782
|
-
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
|
|
8786
|
-
|
|
8787
|
-
|
|
8788
|
-
|
|
8789
|
-
|
|
8790
|
-
|
|
8791
|
-
|
|
8792
|
-
|
|
8793
|
-
|
|
8794
|
-
|
|
8795
|
-
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
|
|
8799
|
-
|
|
8800
|
-
|
|
8801
|
-
|
|
8802
|
-
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
8811
|
-
}
|
|
8812
|
-
|
|
8813
|
-
|
|
8814
|
-
|
|
8815
|
-
|
|
8816
|
-
|
|
8817
|
-
|
|
8818
|
-
|
|
8819
|
-
|
|
8820
|
-
|
|
8821
|
-
|
|
8822
|
-
|
|
8823
|
-
|
|
8824
|
-
|
|
8825
|
-
|
|
8826
|
-
|
|
8827
|
-
const renderNavItem = (item) => renderMenuItem(item, 0);
|
|
8828
|
-
return (jsxRuntimeExports.jsxs("nav", { className: classes, role: "navigation", "aria-label": "\uBA54\uC778 \uB124\uBE44\uAC8C\uC774\uC158", ...props, children: [jsxRuntimeExports.jsxs("div", { className: containerClasses, children: [jsxRuntimeExports.jsx("div", { className: "designbase-navbar__brand", children: jsxRuntimeExports.jsx("div", { className: "designbase-navbar__logo", onClick: onLogoClick, role: onLogoClick ? 'button' : undefined, tabIndex: onLogoClick ? 0 : undefined, children: logo || jsxRuntimeExports.jsx(Logo, { size: size === 's' ? 's' : size === 'l' ? 'l' : 'm' }) }) }), jsxRuntimeExports.jsx("div", { className: "designbase-navbar__nav", children: jsxRuntimeExports.jsx("ul", { className: "designbase-navbar__nav-list", children: items.map(renderNavItem) }) }), showSearch && (jsxRuntimeExports.jsx("div", { className: "designbase-navbar__search", children: jsxRuntimeExports.jsx(SearchBar, { placeholder: searchPlaceholder, value: searchQuery, onChange: setSearchQuery, onSearch: onSearch, size: size === 's' ? 's' : size === 'l' ? 'l' : 'm', variant: "outlined" }) })), jsxRuntimeExports.jsx("div", { className: "designbase-navbar__user-menu", children: isAuthenticated && userProfile ? (jsxRuntimeExports.jsxs("div", { className: "designbase-navbar__user-dropdown", children: [jsxRuntimeExports.jsxs("button", { className: "designbase-navbar__user-toggle", onClick: handleUserMenuToggle, "aria-expanded": isUserMenuOpen, children: [jsxRuntimeExports.jsx(Avatar, { src: userProfile.avatar, alt: userProfile.name, initials: userProfile.name, size: size === 's' ? 's' : size === 'l' ? 'l' : 'm', badge: userProfile.badge, badgeVariant: userProfile.badgeVariant, badgeStyle: userProfile.badgeStyle, badgeText: userProfile.badgeText }), jsxRuntimeExports.jsx("span", { className: "designbase-navbar__user-name", children: userProfile.name }), jsxRuntimeExports.jsx(icons.ChevronDownIcon, { size: 12 })] }), isUserMenuOpen && (jsxRuntimeExports.jsxs("ul", { className: "designbase-navbar__user-dropdown-menu", children: [userProfile.email && (jsxRuntimeExports.jsx("li", { className: "designbase-navbar__user-info", children: jsxRuntimeExports.jsx("span", { className: "designbase-navbar__user-email", children: userProfile.email }) })), userMenuItems.map((item) => (jsxRuntimeExports.jsx("li", { children: jsxRuntimeExports.jsxs("a", { href: item.href, className: clsx('designbase-navbar__user-dropdown-item', {
|
|
8829
|
-
'designbase-navbar__user-dropdown-item--disabled': item.disabled,
|
|
8830
|
-
}), onClick: (e) => {
|
|
8831
|
-
if (item.disabled) {
|
|
8832
|
-
e.preventDefault();
|
|
8833
|
-
return;
|
|
8834
|
-
}
|
|
8835
|
-
handleUserMenuItemClick(item);
|
|
8836
|
-
}, children: [item.icon && React.createElement(item.icon, { size: iconSize, color: 'currentColor' }), item.label] }) }, item.id)))] }))] })) : (jsxRuntimeExports.jsx("button", { className: "designbase-navbar__auth-button", onClick: onAuthClick, children: "\uB85C\uADF8\uC778" })) }), jsxRuntimeExports.jsxs("button", { className: "designbase-navbar__mobile-toggle", onClick: handleMobileMenuToggle, "aria-expanded": isMobileMenuOpen, "aria-label": "\uBA54\uB274 \uC5F4\uAE30", children: [jsxRuntimeExports.jsx("span", { className: "designbase-navbar__mobile-toggle-line" }), jsxRuntimeExports.jsx("span", { className: "designbase-navbar__mobile-toggle-line" }), jsxRuntimeExports.jsx("span", { className: "designbase-navbar__mobile-toggle-line" })] })] }), isMobileMenuOpen && (jsxRuntimeExports.jsxs("div", { className: "designbase-navbar__mobile-menu", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-navbar__mobile-menu-header", children: [jsxRuntimeExports.jsx("h3", { children: "\uBA54\uB274" }), jsxRuntimeExports.jsx("button", { className: "designbase-navbar__mobile-menu-close", onClick: handleMobileMenuToggle, "aria-label": "\uBA54\uB274 \uB2EB\uAE30", children: jsxRuntimeExports.jsx("i", { className: "designbase-icon-x" }) })] }), showSearch && (jsxRuntimeExports.jsx("div", { className: "designbase-navbar__mobile-search", children: jsxRuntimeExports.jsx(SearchBar, { placeholder: searchPlaceholder, value: searchQuery, onChange: setSearchQuery, onSearch: onSearch, size: "s", variant: "outlined", fullWidth: true }) })), jsxRuntimeExports.jsx("ul", { className: "designbase-navbar__mobile-nav-list", children: items.map((item) => (jsxRuntimeExports.jsxs("li", { className: "designbase-navbar__mobile-nav-item", children: [jsxRuntimeExports.jsxs("a", { href: item.href, className: clsx('designbase-navbar__mobile-nav-link', {
|
|
8837
|
-
'designbase-navbar__mobile-nav-link--active': item.active,
|
|
8838
|
-
'designbase-navbar__mobile-nav-link--disabled': item.disabled,
|
|
8839
|
-
}), onClick: (e) => {
|
|
8840
|
-
if (item.disabled) {
|
|
8841
|
-
e.preventDefault();
|
|
8842
|
-
return;
|
|
8843
|
-
}
|
|
8844
|
-
handleItemClick(item);
|
|
8845
|
-
}, children: [item.icon && React.createElement(item.icon, { size: iconSize, color: 'currentColor' }), item.label] }), item.children && item.children.length > 0 && (jsxRuntimeExports.jsx("ul", { className: "designbase-navbar__mobile-submenu", children: item.children.map((child) => (jsxRuntimeExports.jsx("li", { children: jsxRuntimeExports.jsxs("a", { href: child.href, className: clsx('designbase-navbar__mobile-submenu-link', {
|
|
8846
|
-
'designbase-navbar__mobile-submenu-link--active': child.active,
|
|
8847
|
-
'designbase-navbar__mobile-submenu-link--disabled': child.disabled,
|
|
9390
|
+
const isExpanded = expandedItems[item.id];
|
|
9391
|
+
if (isMobile) {
|
|
9392
|
+
// 모바일에서는 MenuItem 컴포넌트 사용
|
|
9393
|
+
return (jsxRuntimeExports.jsx(MenuItem, { id: item.id, label: item.label, href: item.href, icon: item.icon, active: item.active, disabled: item.disabled, badge: item.badge, badgeColor: item.badgeColor, variant: item.variant, type: "block", size: size, style: "accordion", subItems: item.children?.map(child => ({
|
|
9394
|
+
id: child.id,
|
|
9395
|
+
label: child.label,
|
|
9396
|
+
href: child.href,
|
|
9397
|
+
icon: child.icon,
|
|
9398
|
+
active: child.active,
|
|
9399
|
+
disabled: child.disabled,
|
|
9400
|
+
badge: child.badge,
|
|
9401
|
+
badgeColor: child.badgeColor,
|
|
9402
|
+
variant: child.variant,
|
|
9403
|
+
onClick: () => handleSubItemClick(child),
|
|
9404
|
+
})), expandable: hasChildren, expanded: isExpanded, onClick: () => handleItemClick(item, true) }, item.id));
|
|
9405
|
+
}
|
|
9406
|
+
// 데스크톱에서는 기존 방식 유지
|
|
9407
|
+
return (jsxRuntimeExports.jsx("li", { className: "designbase-navbar__nav-item", children: hasChildren ? (jsxRuntimeExports.jsxs("div", { className: "designbase-navbar__dropdown", children: [jsxRuntimeExports.jsxs("button", { className: clsx('designbase-navbar__nav-link', 'designbase-navbar__nav-link--has-children', {
|
|
9408
|
+
'designbase-navbar__nav-link--active': item.active,
|
|
9409
|
+
'designbase-navbar__nav-link--disabled': item.disabled,
|
|
9410
|
+
}), onClick: () => handleItemClick(item, false), disabled: item.disabled, children: [item.icon && React.createElement(item.icon, { size: iconSize, color: 'currentColor' }), item.label, jsxRuntimeExports.jsx(icons.ChevronDownIcon, { size: 12, className: clsx('designbase-navbar__chevron', {
|
|
9411
|
+
'designbase-navbar__chevron--expanded': isExpanded,
|
|
9412
|
+
}) })] }), jsxRuntimeExports.jsx("ul", { className: "designbase-navbar__dropdown-menu", children: item.children.map((child) => (jsxRuntimeExports.jsx("li", { children: jsxRuntimeExports.jsxs("a", { href: child.href, className: clsx('designbase-navbar__dropdown-item', {
|
|
9413
|
+
'designbase-navbar__dropdown-item--active': child.active,
|
|
9414
|
+
'designbase-navbar__dropdown-item--disabled': child.disabled,
|
|
9415
|
+
}), onClick: (e) => {
|
|
9416
|
+
if (child.disabled) {
|
|
9417
|
+
e.preventDefault();
|
|
9418
|
+
return;
|
|
9419
|
+
}
|
|
9420
|
+
handleItemClick(child, false);
|
|
9421
|
+
}, children: [child.icon && React.createElement(child.icon, { size: iconSize, color: 'currentColor' }), child.label] }) }, child.id))) })] })) : (jsxRuntimeExports.jsxs("a", { href: item.href, className: clsx('designbase-navbar__nav-link', {
|
|
9422
|
+
'designbase-navbar__nav-link--active': item.active,
|
|
9423
|
+
'designbase-navbar__nav-link--disabled': item.disabled,
|
|
9424
|
+
}), onClick: (e) => {
|
|
9425
|
+
if (item.disabled) {
|
|
9426
|
+
e.preventDefault();
|
|
9427
|
+
return;
|
|
9428
|
+
}
|
|
9429
|
+
handleItemClick(item, false);
|
|
9430
|
+
}, children: [item.icon && React.createElement(item.icon, { size: iconSize, color: 'currentColor' }), item.label] })) }, item.id));
|
|
9431
|
+
};
|
|
9432
|
+
return (jsxRuntimeExports.jsxs("nav", { ref: navbarRef, className: clsx(classes, {
|
|
9433
|
+
'designbase-navbar--mobile-open': isMobileMenuOpen,
|
|
9434
|
+
}), role: "navigation", "aria-label": "\uBA54\uC778 \uB124\uBE44\uAC8C\uC774\uC158", ...props, children: [jsxRuntimeExports.jsxs("div", { className: containerClasses, children: [jsxRuntimeExports.jsx("div", { className: "designbase-navbar__left", children: jsxRuntimeExports.jsx("div", { className: "designbase-navbar__brand", children: jsxRuntimeExports.jsx("div", { className: "designbase-navbar__logo", onClick: onLogoClick, role: onLogoClick ? 'button' : undefined, tabIndex: onLogoClick ? 0 : undefined, children: logo || jsxRuntimeExports.jsx(Logo, { size: "s" }) }) }) }), jsxRuntimeExports.jsxs("div", { className: "designbase-navbar__right", children: [jsxRuntimeExports.jsx("div", { className: "designbase-navbar__nav designbase-navbar__nav--desktop", children: jsxRuntimeExports.jsx("ul", { className: "designbase-navbar__nav-list", children: items.map(item => renderMenuItem(item, false)) }) }), showSearch && (jsxRuntimeExports.jsx("div", { className: "designbase-navbar__search", children: jsxRuntimeExports.jsx(SearchBar, { placeholder: searchPlaceholder, value: searchQuery, onChange: setSearchQuery, onSearch: onSearch, size: size === 's' ? 's' : size === 'l' ? 'l' : 'm', variant: "outlined" }) })), jsxRuntimeExports.jsx("div", { className: "designbase-navbar__user-menu", children: isAuthenticated && userProfile ? (jsxRuntimeExports.jsxs("div", { className: "designbase-navbar__user-dropdown", children: [jsxRuntimeExports.jsxs("button", { className: "designbase-navbar__user-toggle", onClick: handleUserMenuToggle, "aria-expanded": isUserMenuOpen, children: [jsxRuntimeExports.jsx(Avatar, { src: userProfile.avatar, alt: userProfile.name, initials: userProfile.name, size: size === 's' ? 's' : size === 'l' ? 'l' : 'm', badge: userProfile.badge, badgeVariant: userProfile.badgeVariant, badgeStyle: userProfile.badgeStyle, badgeText: userProfile.badgeText }), jsxRuntimeExports.jsx("span", { className: "designbase-navbar__user-name", children: userProfile.name }), jsxRuntimeExports.jsx(icons.ChevronDownIcon, { size: 12 })] }), isUserMenuOpen && (jsxRuntimeExports.jsxs("ul", { className: "designbase-navbar__user-dropdown-menu", children: [userProfile.email && (jsxRuntimeExports.jsx("li", { className: "designbase-navbar__user-info", children: jsxRuntimeExports.jsx("span", { className: "designbase-navbar__user-email", children: userProfile.email }) })), userMenuItems.map((item) => (jsxRuntimeExports.jsx("li", { children: jsxRuntimeExports.jsxs("a", { href: item.href, className: clsx('designbase-navbar__user-dropdown-item', {
|
|
9435
|
+
'designbase-navbar__user-dropdown-item--disabled': item.disabled,
|
|
9436
|
+
}), onClick: (e) => {
|
|
9437
|
+
if (item.disabled) {
|
|
9438
|
+
e.preventDefault();
|
|
9439
|
+
return;
|
|
9440
|
+
}
|
|
9441
|
+
handleUserMenuItemClick(item);
|
|
9442
|
+
}, children: [item.icon && React.createElement(item.icon, { size: iconSize, color: 'currentColor' }), item.label] }) }, item.id)))] }))] })) : (jsxRuntimeExports.jsx(Button, { variant: "primary", size: size === 's' ? 's' : size === 'l' ? 'l' : 'm', onClick: onLoginClick, children: "\uB85C\uADF8\uC778" })) })] }), jsxRuntimeExports.jsxs("button", { className: "designbase-navbar__mobile-toggle", onClick: handleMobileMenuToggle, "aria-expanded": isMobileMenuOpen, "aria-label": "\uBA54\uB274 \uC5F4\uAE30", children: [jsxRuntimeExports.jsx("span", { className: "designbase-navbar__mobile-toggle-line" }), jsxRuntimeExports.jsx("span", { className: "designbase-navbar__mobile-toggle-line" }), jsxRuntimeExports.jsx("span", { className: "designbase-navbar__mobile-toggle-line" })] })] }), isMobileMenuOpen && (jsxRuntimeExports.jsxs("div", { className: "designbase-navbar__mobile-menu", children: [showSearch && (jsxRuntimeExports.jsx("div", { className: "designbase-navbar__mobile-search", children: jsxRuntimeExports.jsx(SearchBar, { placeholder: searchPlaceholder, value: searchQuery, onChange: setSearchQuery, onSearch: onSearch, size: "s", variant: "outlined", fullWidth: true }) })), jsxRuntimeExports.jsx("ul", { className: "designbase-navbar__mobile-nav-list", children: items.map((item) => renderMenuItem(item, true)) }), jsxRuntimeExports.jsx("div", { className: "designbase-navbar__mobile-user", children: isAuthenticated && userProfile ? (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsxs("div", { className: "designbase-navbar__mobile-user-info", children: [userProfile.avatar ? (jsxRuntimeExports.jsx("img", { src: userProfile.avatar, alt: userProfile.name, className: "designbase-navbar__mobile-user-avatar" })) : (jsxRuntimeExports.jsx("div", { className: "designbase-navbar__mobile-user-avatar-placeholder", children: userProfile.name.charAt(0).toUpperCase() })), jsxRuntimeExports.jsxs("div", { children: [jsxRuntimeExports.jsx("div", { className: "designbase-navbar__mobile-user-name", children: userProfile.name }), userProfile.email && (jsxRuntimeExports.jsx("div", { className: "designbase-navbar__mobile-user-email", children: userProfile.email }))] })] }), jsxRuntimeExports.jsx("ul", { className: "designbase-navbar__mobile-user-menu", children: userMenuItems.map((item) => (jsxRuntimeExports.jsx("li", { children: jsxRuntimeExports.jsxs("a", { href: item.href, className: clsx('designbase-navbar__mobile-user-menu-link', {
|
|
9443
|
+
'designbase-navbar__mobile-user-menu-link--disabled': item.disabled,
|
|
8848
9444
|
}), onClick: (e) => {
|
|
8849
|
-
if (
|
|
9445
|
+
if (item.disabled) {
|
|
8850
9446
|
e.preventDefault();
|
|
8851
9447
|
return;
|
|
8852
9448
|
}
|
|
8853
|
-
|
|
8854
|
-
}, children: [
|
|
8855
|
-
'designbase-navbar__mobile-user-menu-link--disabled': item.disabled,
|
|
8856
|
-
}), onClick: (e) => {
|
|
8857
|
-
if (item.disabled) {
|
|
8858
|
-
e.preventDefault();
|
|
8859
|
-
return;
|
|
8860
|
-
}
|
|
8861
|
-
handleUserMenuItemClick(item);
|
|
8862
|
-
}, children: [item.icon && React.createElement(item.icon, { size: iconSize, color: 'currentColor' }), item.label] }) }, item.id))) })] }))] }))] }));
|
|
9449
|
+
handleUserMenuItemClick(item);
|
|
9450
|
+
}, children: [item.icon && React.createElement(item.icon, { size: iconSize, color: 'currentColor' }), item.label] }) }, item.id))) })] })) : (jsxRuntimeExports.jsx(Button, { variant: "primary", size: "m", onClick: onLoginClick, fullWidth: true, children: "\uB85C\uADF8\uC778" })) })] }))] }));
|
|
8863
9451
|
};
|
|
8864
9452
|
Navbar.displayName = 'Navbar';
|
|
8865
9453
|
|
|
@@ -8941,302 +9529,269 @@ const Pagination = ({ currentPage, totalPages, totalItems, pageSize = 10, pageSi
|
|
|
8941
9529
|
};
|
|
8942
9530
|
Pagination.displayName = 'Pagination';
|
|
8943
9531
|
|
|
8944
|
-
const
|
|
9532
|
+
const GAP$1 = 8; // trigger와 popover 사이 간격
|
|
9533
|
+
const Popover = ({ content, children, title, position = 'top', size = 'm', variant = 'default', trigger = 'click', delay = 0, // 클릭/포커스는 즉시, 호버는 아래에서만 적용
|
|
9534
|
+
hideDelay = 80, alwaysShow = false, disabled = false, maxWidth = 300, showArrow = true, closeOnOutsideClick = true, closeOnEscape = true, open: controlledOpen, onOpenChange, className, ...props }) => {
|
|
8945
9535
|
const [internalOpen, setInternalOpen] = React.useState(false);
|
|
8946
|
-
const
|
|
8947
|
-
const
|
|
9536
|
+
const isControlled = controlledOpen !== undefined;
|
|
9537
|
+
const isOpen = isControlled ? !!controlledOpen : internalOpen;
|
|
8948
9538
|
const triggerRef = React.useRef(null);
|
|
8949
9539
|
const popoverRef = React.useRef(null);
|
|
8950
|
-
const
|
|
8951
|
-
|
|
8952
|
-
const
|
|
8953
|
-
const
|
|
8954
|
-
|
|
9540
|
+
const [style, setStyle] = React.useState({});
|
|
9541
|
+
const [arrowStyle, setArrowStyle] = React.useState({});
|
|
9542
|
+
const [placementGroup, setPlacementGroup] = React.useState('top'); // 애니메이션 방향용
|
|
9543
|
+
const rafId = React.useRef(null);
|
|
9544
|
+
const timers = React.useRef({});
|
|
9545
|
+
// ----- 위치 계산 ----------------------------------------------------------
|
|
8955
9546
|
const calculatePosition = React.useCallback(() => {
|
|
8956
9547
|
if (!triggerRef.current || !popoverRef.current)
|
|
8957
9548
|
return;
|
|
8958
|
-
const
|
|
8959
|
-
const
|
|
8960
|
-
|
|
8961
|
-
|
|
8962
|
-
let
|
|
8963
|
-
|
|
8964
|
-
|
|
8965
|
-
|
|
8966
|
-
|
|
8967
|
-
|
|
9549
|
+
const tRect = triggerRef.current.getBoundingClientRect();
|
|
9550
|
+
const pRect = popoverRef.current.getBoundingClientRect();
|
|
9551
|
+
// fixed 기준 → 스크롤값 불필요
|
|
9552
|
+
let top = 0, left = 0;
|
|
9553
|
+
let aTop = 0, aLeft = 0;
|
|
9554
|
+
const setGroup = (p) => {
|
|
9555
|
+
if (p.startsWith('top'))
|
|
9556
|
+
return 'top';
|
|
9557
|
+
if (p.startsWith('bottom'))
|
|
9558
|
+
return 'bottom';
|
|
9559
|
+
if (p.startsWith('left'))
|
|
9560
|
+
return 'left';
|
|
9561
|
+
return 'right';
|
|
9562
|
+
};
|
|
8968
9563
|
switch (position) {
|
|
8969
9564
|
case 'top':
|
|
8970
|
-
top =
|
|
8971
|
-
left =
|
|
8972
|
-
|
|
8973
|
-
|
|
9565
|
+
top = tRect.top - pRect.height - GAP$1;
|
|
9566
|
+
left = tRect.left + tRect.width / 2 - pRect.width / 2;
|
|
9567
|
+
aTop = pRect.height;
|
|
9568
|
+
aLeft = pRect.width / 2 - 4;
|
|
8974
9569
|
break;
|
|
8975
9570
|
case 'top-start':
|
|
8976
|
-
top =
|
|
8977
|
-
left =
|
|
8978
|
-
|
|
8979
|
-
|
|
9571
|
+
top = tRect.top - pRect.height - GAP$1;
|
|
9572
|
+
left = tRect.left;
|
|
9573
|
+
aTop = pRect.height;
|
|
9574
|
+
aLeft = 12;
|
|
8980
9575
|
break;
|
|
8981
9576
|
case 'top-end':
|
|
8982
|
-
top =
|
|
8983
|
-
left =
|
|
8984
|
-
|
|
8985
|
-
|
|
9577
|
+
top = tRect.top - pRect.height - GAP$1;
|
|
9578
|
+
left = tRect.right - pRect.width;
|
|
9579
|
+
aTop = pRect.height;
|
|
9580
|
+
aLeft = pRect.width - 12;
|
|
8986
9581
|
break;
|
|
8987
9582
|
case 'bottom':
|
|
8988
|
-
top =
|
|
8989
|
-
left =
|
|
8990
|
-
|
|
8991
|
-
|
|
9583
|
+
top = tRect.bottom + GAP$1;
|
|
9584
|
+
left = tRect.left + tRect.width / 2 - pRect.width / 2;
|
|
9585
|
+
aTop = -4;
|
|
9586
|
+
aLeft = pRect.width / 2 - 4;
|
|
8992
9587
|
break;
|
|
8993
9588
|
case 'bottom-start':
|
|
8994
|
-
top =
|
|
8995
|
-
left =
|
|
8996
|
-
|
|
8997
|
-
|
|
9589
|
+
top = tRect.bottom + GAP$1;
|
|
9590
|
+
left = tRect.left;
|
|
9591
|
+
aTop = -4;
|
|
9592
|
+
aLeft = 12;
|
|
8998
9593
|
break;
|
|
8999
9594
|
case 'bottom-end':
|
|
9000
|
-
top =
|
|
9001
|
-
left =
|
|
9002
|
-
|
|
9003
|
-
|
|
9595
|
+
top = tRect.bottom + GAP$1;
|
|
9596
|
+
left = tRect.right - pRect.width;
|
|
9597
|
+
aTop = -4;
|
|
9598
|
+
aLeft = pRect.width - 12;
|
|
9004
9599
|
break;
|
|
9005
9600
|
case 'left':
|
|
9006
|
-
top =
|
|
9007
|
-
left =
|
|
9008
|
-
|
|
9009
|
-
|
|
9601
|
+
top = tRect.top + tRect.height / 2 - pRect.height / 2;
|
|
9602
|
+
left = tRect.left - pRect.width - GAP$1;
|
|
9603
|
+
aTop = pRect.height / 2 - 4;
|
|
9604
|
+
aLeft = pRect.width;
|
|
9010
9605
|
break;
|
|
9011
9606
|
case 'left-start':
|
|
9012
|
-
top =
|
|
9013
|
-
left =
|
|
9014
|
-
|
|
9015
|
-
|
|
9607
|
+
top = tRect.top;
|
|
9608
|
+
left = tRect.left - pRect.width - GAP$1;
|
|
9609
|
+
aTop = 12;
|
|
9610
|
+
aLeft = pRect.width;
|
|
9016
9611
|
break;
|
|
9017
9612
|
case 'left-end':
|
|
9018
|
-
top =
|
|
9019
|
-
left =
|
|
9020
|
-
|
|
9021
|
-
|
|
9613
|
+
top = tRect.bottom - pRect.height;
|
|
9614
|
+
left = tRect.left - pRect.width - GAP$1;
|
|
9615
|
+
aTop = pRect.height - 12;
|
|
9616
|
+
aLeft = pRect.width;
|
|
9022
9617
|
break;
|
|
9023
9618
|
case 'right':
|
|
9024
|
-
top =
|
|
9025
|
-
left =
|
|
9026
|
-
|
|
9027
|
-
|
|
9619
|
+
top = tRect.top + tRect.height / 2 - pRect.height / 2;
|
|
9620
|
+
left = tRect.right + GAP$1;
|
|
9621
|
+
aTop = pRect.height / 2 - 4;
|
|
9622
|
+
aLeft = -4;
|
|
9028
9623
|
break;
|
|
9029
9624
|
case 'right-start':
|
|
9030
|
-
top =
|
|
9031
|
-
left =
|
|
9032
|
-
|
|
9033
|
-
|
|
9625
|
+
top = tRect.top;
|
|
9626
|
+
left = tRect.right + GAP$1;
|
|
9627
|
+
aTop = 12;
|
|
9628
|
+
aLeft = -4;
|
|
9034
9629
|
break;
|
|
9035
9630
|
case 'right-end':
|
|
9036
|
-
top =
|
|
9037
|
-
left =
|
|
9038
|
-
|
|
9039
|
-
|
|
9631
|
+
top = tRect.bottom - pRect.height;
|
|
9632
|
+
left = tRect.right + GAP$1;
|
|
9633
|
+
aTop = pRect.height - 12;
|
|
9634
|
+
aLeft = -4;
|
|
9040
9635
|
break;
|
|
9041
9636
|
}
|
|
9042
|
-
//
|
|
9043
|
-
const
|
|
9044
|
-
const
|
|
9045
|
-
|
|
9046
|
-
if (left < 8) {
|
|
9637
|
+
// viewport 충돌 보정(살짝 shift)
|
|
9638
|
+
const vw = window.innerWidth;
|
|
9639
|
+
const vh = window.innerHeight;
|
|
9640
|
+
if (left < 8)
|
|
9047
9641
|
left = 8;
|
|
9048
|
-
|
|
9049
|
-
|
|
9050
|
-
|
|
9051
|
-
}
|
|
9052
|
-
else if (left + popoverRect.width > viewportWidth - 8) {
|
|
9053
|
-
left = viewportWidth - popoverRect.width - 8;
|
|
9054
|
-
if (isHorizontal) {
|
|
9055
|
-
arrowLeft = Math.min(arrowLeft, popoverRect.width - 4);
|
|
9056
|
-
}
|
|
9057
|
-
}
|
|
9058
|
-
// 수직 경계 체크
|
|
9059
|
-
if (top < 8) {
|
|
9642
|
+
if (left + pRect.width > vw - 8)
|
|
9643
|
+
left = vw - pRect.width - 8;
|
|
9644
|
+
if (top < 8)
|
|
9060
9645
|
top = 8;
|
|
9061
|
-
|
|
9062
|
-
|
|
9063
|
-
|
|
9064
|
-
}
|
|
9065
|
-
else if (top + popoverRect.height > viewportHeight - 8) {
|
|
9066
|
-
top = viewportHeight - popoverRect.height - 8;
|
|
9067
|
-
if (isVertical) {
|
|
9068
|
-
arrowTop = Math.min(arrowTop, popoverRect.height - 4);
|
|
9069
|
-
}
|
|
9070
|
-
}
|
|
9071
|
-
setPopoverStyle({
|
|
9646
|
+
if (top + pRect.height > vh - 8)
|
|
9647
|
+
top = vh - pRect.height - 8;
|
|
9648
|
+
setStyle({
|
|
9072
9649
|
position: 'fixed',
|
|
9073
|
-
top
|
|
9074
|
-
left
|
|
9075
|
-
maxWidth
|
|
9650
|
+
top,
|
|
9651
|
+
left,
|
|
9652
|
+
maxWidth,
|
|
9076
9653
|
zIndex: 9999,
|
|
9077
9654
|
});
|
|
9078
|
-
setArrowStyle({
|
|
9079
|
-
|
|
9080
|
-
top: `${arrowTop}px`,
|
|
9081
|
-
left: `${arrowLeft}px`,
|
|
9082
|
-
});
|
|
9655
|
+
setArrowStyle({ position: 'absolute', top: aTop, left: aLeft });
|
|
9656
|
+
setPlacementGroup(setGroup(position));
|
|
9083
9657
|
}, [position, maxWidth]);
|
|
9084
|
-
//
|
|
9085
|
-
|
|
9086
|
-
if (
|
|
9087
|
-
|
|
9088
|
-
if (timeoutRef.current) {
|
|
9089
|
-
clearTimeout(timeoutRef.current);
|
|
9658
|
+
// 첫 페인트 전에 위치를 확정 (깜빡임/점프 제거)
|
|
9659
|
+
React.useLayoutEffect(() => {
|
|
9660
|
+
if (isOpen) {
|
|
9661
|
+
calculatePosition();
|
|
9090
9662
|
}
|
|
9091
|
-
|
|
9092
|
-
|
|
9093
|
-
|
|
9094
|
-
|
|
9095
|
-
|
|
9096
|
-
|
|
9097
|
-
|
|
9098
|
-
|
|
9663
|
+
}, [isOpen, calculatePosition]);
|
|
9664
|
+
// 스크롤/리사이즈는 rAF에 태워 스로틀
|
|
9665
|
+
React.useEffect(() => {
|
|
9666
|
+
if (!isOpen)
|
|
9667
|
+
return;
|
|
9668
|
+
const onMove = () => {
|
|
9669
|
+
if (rafId.current != null)
|
|
9670
|
+
cancelAnimationFrame(rafId.current);
|
|
9671
|
+
rafId.current = requestAnimationFrame(() => {
|
|
9099
9672
|
calculatePosition();
|
|
9673
|
+
rafId.current = null;
|
|
9100
9674
|
});
|
|
9101
|
-
}
|
|
9102
|
-
|
|
9103
|
-
|
|
9675
|
+
};
|
|
9676
|
+
window.addEventListener('scroll', onMove, { passive: true, capture: true });
|
|
9677
|
+
window.addEventListener('resize', onMove, { passive: true });
|
|
9678
|
+
// 컨텐츠/트리거 크기 변화 추적
|
|
9679
|
+
const ResizeObserver = window.ResizeObserver;
|
|
9680
|
+
const ro = ResizeObserver ? new ResizeObserver(() => onMove()) : null;
|
|
9681
|
+
if (ro) {
|
|
9682
|
+
if (triggerRef.current)
|
|
9683
|
+
ro.observe(triggerRef.current);
|
|
9684
|
+
if (popoverRef.current)
|
|
9685
|
+
ro.observe(popoverRef.current);
|
|
9686
|
+
}
|
|
9687
|
+
return () => {
|
|
9688
|
+
window.removeEventListener('scroll', onMove, true);
|
|
9689
|
+
window.removeEventListener('resize', onMove);
|
|
9690
|
+
if (rafId.current != null)
|
|
9691
|
+
cancelAnimationFrame(rafId.current);
|
|
9692
|
+
ro?.disconnect?.();
|
|
9693
|
+
};
|
|
9694
|
+
}, [isOpen, calculatePosition]);
|
|
9695
|
+
// open/close helpers
|
|
9696
|
+
const setOpen = React.useCallback((next) => {
|
|
9697
|
+
if (!isControlled)
|
|
9698
|
+
setInternalOpen(next);
|
|
9699
|
+
onOpenChange?.(next);
|
|
9700
|
+
}, [isControlled, onOpenChange]);
|
|
9701
|
+
const openPopover = React.useCallback(() => {
|
|
9702
|
+
if (disabled || alwaysShow)
|
|
9703
|
+
return;
|
|
9704
|
+
if (timers.current.t)
|
|
9705
|
+
clearTimeout(timers.current.t);
|
|
9706
|
+
const d = trigger === 'hover' ? Math.max(0, delay) : 0;
|
|
9707
|
+
timers.current.t = setTimeout(() => {
|
|
9708
|
+
setOpen(true);
|
|
9709
|
+
}, d);
|
|
9710
|
+
}, [trigger, delay, disabled, alwaysShow, setOpen]);
|
|
9104
9711
|
const closePopover = React.useCallback(() => {
|
|
9105
9712
|
if (disabled || alwaysShow)
|
|
9106
9713
|
return;
|
|
9107
|
-
if (
|
|
9108
|
-
clearTimeout(
|
|
9109
|
-
|
|
9110
|
-
|
|
9111
|
-
|
|
9112
|
-
|
|
9113
|
-
|
|
9114
|
-
|
|
9115
|
-
onOpenChange?.(newOpen);
|
|
9116
|
-
}, trigger === 'hover' ? hideDelay : 0);
|
|
9117
|
-
}, [disabled, alwaysShow, isControlled, onOpenChange, trigger, hideDelay]);
|
|
9118
|
-
// 이벤트 핸들러
|
|
9714
|
+
if (timers.current.t)
|
|
9715
|
+
clearTimeout(timers.current.t);
|
|
9716
|
+
const d = trigger === 'hover' ? Math.max(0, hideDelay) : 0;
|
|
9717
|
+
timers.current.t = setTimeout(() => {
|
|
9718
|
+
setOpen(false);
|
|
9719
|
+
}, d);
|
|
9720
|
+
}, [trigger, hideDelay, disabled, alwaysShow, setOpen]);
|
|
9721
|
+
// 트리거 이벤트
|
|
9119
9722
|
const handleClick = React.useCallback((e) => {
|
|
9120
|
-
if (trigger
|
|
9121
|
-
|
|
9122
|
-
|
|
9123
|
-
|
|
9124
|
-
|
|
9125
|
-
}
|
|
9126
|
-
else {
|
|
9127
|
-
openPopover();
|
|
9128
|
-
}
|
|
9129
|
-
}
|
|
9723
|
+
if (trigger !== 'click')
|
|
9724
|
+
return;
|
|
9725
|
+
e.preventDefault();
|
|
9726
|
+
e.stopPropagation();
|
|
9727
|
+
isOpen ? closePopover() : openPopover();
|
|
9130
9728
|
}, [trigger, isOpen, openPopover, closePopover]);
|
|
9131
9729
|
const handleMouseEnter = React.useCallback(() => {
|
|
9132
|
-
if (trigger === 'hover')
|
|
9730
|
+
if (trigger === 'hover')
|
|
9133
9731
|
openPopover();
|
|
9134
|
-
}
|
|
9135
9732
|
}, [trigger, openPopover]);
|
|
9136
9733
|
const handleMouseLeave = React.useCallback(() => {
|
|
9137
|
-
if (trigger === 'hover')
|
|
9734
|
+
if (trigger === 'hover')
|
|
9138
9735
|
closePopover();
|
|
9139
|
-
}
|
|
9140
9736
|
}, [trigger, closePopover]);
|
|
9141
9737
|
const handleFocus = React.useCallback(() => {
|
|
9142
|
-
if (trigger === 'focus')
|
|
9738
|
+
if (trigger === 'focus')
|
|
9143
9739
|
openPopover();
|
|
9144
|
-
}
|
|
9145
9740
|
}, [trigger, openPopover]);
|
|
9146
9741
|
const handleBlur = React.useCallback(() => {
|
|
9147
|
-
if (trigger === 'focus')
|
|
9742
|
+
if (trigger === 'focus')
|
|
9148
9743
|
closePopover();
|
|
9149
|
-
}
|
|
9150
9744
|
}, [trigger, closePopover]);
|
|
9151
|
-
// 키보드 이벤트
|
|
9152
9745
|
const handleKeyDown = React.useCallback((e) => {
|
|
9153
|
-
if (e.key === 'Escape' && closeOnEscape)
|
|
9746
|
+
if (e.key === 'Escape' && closeOnEscape)
|
|
9154
9747
|
closePopover();
|
|
9155
|
-
}
|
|
9156
9748
|
}, [closeOnEscape, closePopover]);
|
|
9157
|
-
|
|
9158
|
-
const handleCloseClick = React.useCallback((e) => {
|
|
9159
|
-
e.preventDefault();
|
|
9160
|
-
e.stopPropagation();
|
|
9749
|
+
const handleCloseClick = React.useCallback(() => {
|
|
9161
9750
|
closePopover();
|
|
9162
9751
|
}, [closePopover]);
|
|
9163
|
-
// 외부 클릭
|
|
9164
|
-
React.useEffect(() => {
|
|
9165
|
-
if (isOpen && closeOnOutsideClick) {
|
|
9166
|
-
const handleOutsideClick = (e) => {
|
|
9167
|
-
if (triggerRef.current &&
|
|
9168
|
-
popoverRef.current &&
|
|
9169
|
-
!triggerRef.current.contains(e.target) &&
|
|
9170
|
-
!popoverRef.current.contains(e.target)) {
|
|
9171
|
-
closePopover();
|
|
9172
|
-
}
|
|
9173
|
-
};
|
|
9174
|
-
document.addEventListener('mousedown', handleOutsideClick);
|
|
9175
|
-
return () => {
|
|
9176
|
-
document.removeEventListener('mousedown', handleOutsideClick);
|
|
9177
|
-
};
|
|
9178
|
-
}
|
|
9179
|
-
}, [isOpen, closeOnOutsideClick, closePopover]);
|
|
9180
|
-
// 윈도우 리사이즈 및 스크롤 이벤트
|
|
9752
|
+
// 외부 클릭 닫기
|
|
9181
9753
|
React.useEffect(() => {
|
|
9182
|
-
if (isOpen)
|
|
9183
|
-
|
|
9184
|
-
|
|
9185
|
-
|
|
9186
|
-
|
|
9187
|
-
|
|
9188
|
-
|
|
9189
|
-
|
|
9190
|
-
};
|
|
9191
|
-
}
|
|
9192
|
-
}, [isOpen, calculatePosition]);
|
|
9193
|
-
// 컴포넌트 언마운트 시 타이머 정리
|
|
9194
|
-
React.useEffect(() => {
|
|
9195
|
-
return () => {
|
|
9196
|
-
if (timeoutRef.current) {
|
|
9197
|
-
clearTimeout(timeoutRef.current);
|
|
9754
|
+
if (!isOpen || !closeOnOutsideClick)
|
|
9755
|
+
return;
|
|
9756
|
+
const onDown = (e) => {
|
|
9757
|
+
if (!triggerRef.current || !popoverRef.current)
|
|
9758
|
+
return;
|
|
9759
|
+
const t = e.target;
|
|
9760
|
+
if (!triggerRef.current.contains(t) && !popoverRef.current.contains(t)) {
|
|
9761
|
+
closePopover();
|
|
9198
9762
|
}
|
|
9199
9763
|
};
|
|
9200
|
-
|
|
9201
|
-
|
|
9764
|
+
document.addEventListener('mousedown', onDown);
|
|
9765
|
+
return () => document.removeEventListener('mousedown', onDown);
|
|
9766
|
+
}, [isOpen, closeOnOutsideClick, closePopover]);
|
|
9767
|
+
// alwaysShow 모드 동기화
|
|
9202
9768
|
React.useEffect(() => {
|
|
9203
|
-
if (
|
|
9204
|
-
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
}
|
|
9213
|
-
|
|
9214
|
-
const newOpen = false;
|
|
9215
|
-
if (!isControlled) {
|
|
9216
|
-
setInternalOpen(newOpen);
|
|
9217
|
-
}
|
|
9218
|
-
onOpenChange?.(newOpen);
|
|
9219
|
-
}
|
|
9220
|
-
}, [alwaysShow, disabled, isControlled, onOpenChange, calculatePosition]);
|
|
9221
|
-
const classes = clsx('designbase-popover', `designbase-popover--${size}`, `designbase-popover--${variant}`, `designbase-popover--${position}`, {
|
|
9222
|
-
'designbase-popover--visible': isOpen,
|
|
9223
|
-
'designbase-popover--disabled': disabled,
|
|
9224
|
-
}, className);
|
|
9769
|
+
if (disabled)
|
|
9770
|
+
return;
|
|
9771
|
+
if (alwaysShow)
|
|
9772
|
+
setOpen(true);
|
|
9773
|
+
else if (!isControlled)
|
|
9774
|
+
setOpen(false);
|
|
9775
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
9776
|
+
}, [alwaysShow, disabled]);
|
|
9777
|
+
React.useEffect(() => () => { if (timers.current.t)
|
|
9778
|
+
clearTimeout(timers.current.t); }, []);
|
|
9779
|
+
const classes = clsx('designbase-popover', `designbase-popover--${size}`, `designbase-popover--${variant}`, `designbase-popover--${position}`, { 'designbase-popover--visible': isOpen, 'designbase-popover--disabled': disabled }, className);
|
|
9225
9780
|
const arrowClasses = clsx('designbase-popover__arrow', `designbase-popover__arrow--${position}`);
|
|
9226
|
-
//
|
|
9781
|
+
// 자식에 트리거 이벤트 주입
|
|
9227
9782
|
const enhancedChildren = React.cloneElement(children, {
|
|
9228
9783
|
ref: triggerRef,
|
|
9229
|
-
onClick: handleClick,
|
|
9230
|
-
onMouseEnter: handleMouseEnter,
|
|
9231
|
-
onMouseLeave: handleMouseLeave,
|
|
9232
|
-
onFocus: handleFocus,
|
|
9233
|
-
onBlur: handleBlur,
|
|
9234
|
-
onKeyDown: handleKeyDown,
|
|
9784
|
+
onClick: (e) => { children.props.onClick?.(e); handleClick(e); },
|
|
9785
|
+
onMouseEnter: (e) => { children.props.onMouseEnter?.(e); handleMouseEnter(); },
|
|
9786
|
+
onMouseLeave: (e) => { children.props.onMouseLeave?.(e); handleMouseLeave(); },
|
|
9787
|
+
onFocus: (e) => { children.props.onFocus?.(e); handleFocus(); },
|
|
9788
|
+
onBlur: (e) => { children.props.onBlur?.(e); handleBlur(); },
|
|
9789
|
+
onKeyDown: (e) => { children.props.onKeyDown?.(e); handleKeyDown(e); },
|
|
9235
9790
|
tabIndex: children.props.tabIndex ?? 0,
|
|
9236
9791
|
'aria-expanded': isOpen,
|
|
9237
9792
|
'aria-haspopup': 'dialog',
|
|
9238
9793
|
});
|
|
9239
|
-
return (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [enhancedChildren, isOpen && (jsxRuntimeExports.jsxs("div", { ref: popoverRef, className: classes, style:
|
|
9794
|
+
return (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [enhancedChildren, isOpen && (jsxRuntimeExports.jsxs("div", { ref: popoverRef, className: classes, style: style, role: "dialog", "aria-modal": "false", "data-placement-group": placementGroup, ...props, children: [jsxRuntimeExports.jsxs("div", { className: "designbase-popover__content", children: [(title || true) && (jsxRuntimeExports.jsxs("div", { className: "designbase-popover__header", children: [title && (jsxRuntimeExports.jsx("h3", { className: "designbase-popover__title", children: title })), jsxRuntimeExports.jsx(Button, { size: "s", variant: "tertiary", iconOnly: true, onPress: handleCloseClick, "aria-label": "\uD31D\uC624\uBC84 \uB2EB\uAE30", className: "designbase-popover__close-button", children: jsxRuntimeExports.jsx(icons.CloseIcon, {}) })] })), jsxRuntimeExports.jsx("div", { className: "designbase-popover__body", children: content })] }), showArrow && jsxRuntimeExports.jsx("div", { className: arrowClasses, style: arrowStyle })] }))] }));
|
|
9240
9795
|
};
|
|
9241
9796
|
Popover.displayName = 'Popover';
|
|
9242
9797
|
|
|
@@ -9258,7 +9813,7 @@ const Progress = ({ value, max = 100, size = 'm', variant = 'default', type = 'l
|
|
|
9258
9813
|
return (jsxRuntimeExports.jsx("div", { className: classes, children: type === 'linear' ? renderLinearProgress() : renderCircularProgress() }));
|
|
9259
9814
|
};
|
|
9260
9815
|
|
|
9261
|
-
const ProgressStep = ({ items,
|
|
9816
|
+
const ProgressStep = ({ items, size = 'm', layout = 'vertical', currentStep = 0, clickable = false, fullWidth = false, disabled = false, className, style, onStepClick, }) => {
|
|
9262
9817
|
const handleStepClick = (item, index) => {
|
|
9263
9818
|
if (disabled || item.disabled || !clickable)
|
|
9264
9819
|
return;
|
|
@@ -9274,7 +9829,7 @@ const ProgressStep = ({ items, variant = 'default', size = 'm', layout = 'vertic
|
|
|
9274
9829
|
return 'active';
|
|
9275
9830
|
return 'pending';
|
|
9276
9831
|
};
|
|
9277
|
-
const classes = clsx('designbase-progress-step', `designbase-progress-step--${
|
|
9832
|
+
const classes = clsx('designbase-progress-step', `designbase-progress-step--${size}`, `designbase-progress-step--${layout}`, {
|
|
9278
9833
|
'designbase-progress-step--clickable': clickable,
|
|
9279
9834
|
'designbase-progress-step--full-width': fullWidth,
|
|
9280
9835
|
'designbase-progress-step--disabled': disabled,
|
|
@@ -10153,7 +10708,7 @@ const Section = ({ title, subtitle, description, size = 'm', variant = 'default'
|
|
|
10153
10708
|
return (jsxRuntimeExports.jsxs("section", { className: classes, children: [hasHeader && (jsxRuntimeExports.jsx("div", { className: "designbase-section__header", children: header || (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [(title || subtitle) && (jsxRuntimeExports.jsxs("div", { className: "designbase-section__title-area", children: [title && (jsxRuntimeExports.jsx("h2", { className: "designbase-section__title", children: title })), subtitle && (jsxRuntimeExports.jsx("h3", { className: "designbase-section__subtitle", children: subtitle })), description && (jsxRuntimeExports.jsx("p", { className: "designbase-section__description", children: description }))] })), actions && (jsxRuntimeExports.jsx("div", { className: "designbase-section__actions", children: actions }))] })) })), jsxRuntimeExports.jsx("div", { className: "designbase-section__content", children: children }), footer && (jsxRuntimeExports.jsx("div", { className: "designbase-section__footer", children: footer }))] }));
|
|
10154
10709
|
};
|
|
10155
10710
|
|
|
10156
|
-
const Share = ({ url, title, description = '', imageUrl, hashtags = [], variant = 'button', size = 'm', position = 'bottom', platforms = ['facebook', '
|
|
10711
|
+
const Share = ({ url, title, description = '', imageUrl, hashtags = [], variant = 'button', size = 'm', position = 'bottom', platforms = ['facebook', 'x', 'linkedin', 'whatsapp', 'email', 'link'], customPlatforms = {}, buttonText = '공유', buttonIcon = icons.ShareAltIcon, modalTitle = '공유하기', copySuccessMessage = '링크가 클립보드에 복사되었습니다!', showQrCode = true, qrCodeSize = 200, className, style, onShare, onShareError, onCopySuccess, onCopyError, }) => {
|
|
10157
10712
|
const [isOpen, setIsOpen] = React.useState(false);
|
|
10158
10713
|
const [isCopied, setIsCopied] = React.useState(false);
|
|
10159
10714
|
const [qrCodeData, setQrCodeData] = React.useState('');
|
|
@@ -10161,19 +10716,19 @@ const Share = ({ url, title, description = '', imageUrl, hashtags = [], variant
|
|
|
10161
10716
|
const defaultPlatforms = {
|
|
10162
10717
|
facebook: {
|
|
10163
10718
|
name: 'Facebook',
|
|
10164
|
-
icon: icons.
|
|
10719
|
+
icon: icons.FacebookIcon,
|
|
10165
10720
|
color: '#1877f2',
|
|
10166
10721
|
shareUrl: 'https://www.facebook.com/sharer/sharer.php?u={url}',
|
|
10167
10722
|
},
|
|
10168
|
-
|
|
10169
|
-
name: '
|
|
10170
|
-
icon: icons.
|
|
10171
|
-
color: '#
|
|
10723
|
+
x: {
|
|
10724
|
+
name: 'X',
|
|
10725
|
+
icon: icons.XIcon,
|
|
10726
|
+
color: '#000000',
|
|
10172
10727
|
shareUrl: 'https://twitter.com/intent/tweet?url={url}&text={title}&hashtags={hashtags}',
|
|
10173
10728
|
},
|
|
10174
10729
|
instagram: {
|
|
10175
10730
|
name: 'Instagram',
|
|
10176
|
-
icon: icons.
|
|
10731
|
+
icon: icons.InstagramIcon,
|
|
10177
10732
|
color: '#e4405f',
|
|
10178
10733
|
shareUrl: 'https://www.instagram.com/',
|
|
10179
10734
|
customShare: () => {
|
|
@@ -10183,25 +10738,25 @@ const Share = ({ url, title, description = '', imageUrl, hashtags = [], variant
|
|
|
10183
10738
|
},
|
|
10184
10739
|
linkedin: {
|
|
10185
10740
|
name: 'LinkedIn',
|
|
10186
|
-
icon: icons.
|
|
10741
|
+
icon: icons.LinkedinIcon,
|
|
10187
10742
|
color: '#0077b5',
|
|
10188
10743
|
shareUrl: 'https://www.linkedin.com/sharing/share-offsite/?url={url}',
|
|
10189
10744
|
},
|
|
10190
10745
|
pinterest: {
|
|
10191
10746
|
name: 'Pinterest',
|
|
10192
|
-
icon: icons.
|
|
10747
|
+
icon: icons.PinterestIcon,
|
|
10193
10748
|
color: '#bd081c',
|
|
10194
10749
|
shareUrl: 'https://pinterest.com/pin/create/button/?url={url}&description={title}&media={imageUrl}',
|
|
10195
10750
|
},
|
|
10196
10751
|
whatsapp: {
|
|
10197
10752
|
name: 'WhatsApp',
|
|
10198
|
-
icon: icons.
|
|
10753
|
+
icon: icons.WhatsappIcon,
|
|
10199
10754
|
color: '#25d366',
|
|
10200
10755
|
shareUrl: 'https://wa.me/?text={title}%20{url}',
|
|
10201
10756
|
},
|
|
10202
10757
|
telegram: {
|
|
10203
10758
|
name: 'Telegram',
|
|
10204
|
-
icon: icons.
|
|
10759
|
+
icon: icons.TelegramIcon,
|
|
10205
10760
|
color: '#0088cc',
|
|
10206
10761
|
shareUrl: 'https://t.me/share/url?url={url}&text={title}',
|
|
10207
10762
|
},
|
|
@@ -10220,7 +10775,7 @@ const Share = ({ url, title, description = '', imageUrl, hashtags = [], variant
|
|
|
10220
10775
|
},
|
|
10221
10776
|
qr: {
|
|
10222
10777
|
name: 'QR 코드',
|
|
10223
|
-
icon: icons.
|
|
10778
|
+
icon: icons.ScanQrcodeIcon,
|
|
10224
10779
|
color: '#6c757d',
|
|
10225
10780
|
shareUrl: '',
|
|
10226
10781
|
customShare: () => handleShowQrCode(),
|
|
@@ -10296,7 +10851,7 @@ const Share = ({ url, title, description = '', imageUrl, hashtags = [], variant
|
|
|
10296
10851
|
// 공유 버튼 렌더링
|
|
10297
10852
|
const renderShareButton = () => {
|
|
10298
10853
|
const ButtonIcon = buttonIcon;
|
|
10299
|
-
return (jsxRuntimeExports.jsx(Button, { variant: "secondary", size: size,
|
|
10854
|
+
return (jsxRuntimeExports.jsx(Button, { variant: "secondary", size: size, onPress: () => setIsOpen(!isOpen), className: "designbase-share__trigger", startIcon: ButtonIcon, children: buttonText }));
|
|
10300
10855
|
};
|
|
10301
10856
|
// 플랫폼 버튼 렌더링
|
|
10302
10857
|
const renderPlatformButton = (platform) => {
|
|
@@ -10310,7 +10865,7 @@ const Share = ({ url, title, description = '', imageUrl, hashtags = [], variant
|
|
|
10310
10865
|
}), style: { '--platform-color': config.color }, onClick: () => handlePlatformShare(platform), title: config.name, "aria-label": `${config.name}에 공유하기`, children: [jsxRuntimeExports.jsx(PlatformIcon, { className: "designbase-share__platform-icon" }), jsxRuntimeExports.jsx("span", { className: "designbase-share__platform-name", children: config.name }), isLinkCopy && isCopied && (jsxRuntimeExports.jsxs("span", { className: "designbase-share__copy-success", children: [jsxRuntimeExports.jsx(icons.CopyIcon, {}), "\uBCF5\uC0AC\uB428!"] }))] }, platform));
|
|
10311
10866
|
};
|
|
10312
10867
|
// 공유 영역 렌더링
|
|
10313
|
-
const renderShareContent = () => (jsxRuntimeExports.jsxs("div", { className: "designbase-share__content", children: [jsxRuntimeExports.jsx("div", { className: "designbase-share__platforms", children: activePlatforms.map(renderPlatformButton) }), showQrCode && qrCodeData && (jsxRuntimeExports.jsxs("div", { className: "designbase-share__qr-section", children: [jsxRuntimeExports.jsx("h4", { className: "designbase-share__qr-title", children: "QR \uCF54\uB4DC" }), jsxRuntimeExports.jsx("div", { className: "designbase-share__qr-code", children: jsxRuntimeExports.jsx("img", { src: generateQrCode(), alt: "QR Code", width: qrCodeSize, height: qrCodeSize }) }), jsxRuntimeExports.jsx("p", { className: "designbase-share__qr-description", children: "QR \uCF54\uB4DC\uB97C \uC2A4\uCE94\uD558\uC5EC \uB9C1\uD06C\uC5D0 \uC811\uC18D\uD558\uC138\uC694" })] })), jsxRuntimeExports.jsxs("div", { className: "designbase-share__link-preview", children: [jsxRuntimeExports.jsx("h4", { className: "designbase-share__link-title", children: "\uACF5\uC720 \uB9C1\uD06C" }), jsxRuntimeExports.jsxs("div", { className: "designbase-share__link-container", children: [jsxRuntimeExports.jsx(
|
|
10868
|
+
const renderShareContent = () => (jsxRuntimeExports.jsxs("div", { className: "designbase-share__content", children: [jsxRuntimeExports.jsx("div", { className: "designbase-share__platforms", children: activePlatforms.map(renderPlatformButton) }), showQrCode && qrCodeData && (jsxRuntimeExports.jsxs("div", { className: "designbase-share__qr-section", children: [jsxRuntimeExports.jsx("h4", { className: "designbase-share__qr-title", children: "QR \uCF54\uB4DC" }), jsxRuntimeExports.jsx("div", { className: "designbase-share__qr-code", children: jsxRuntimeExports.jsx("img", { src: generateQrCode(), alt: "QR Code", width: qrCodeSize, height: qrCodeSize }) }), jsxRuntimeExports.jsx("p", { className: "designbase-share__qr-description", children: "QR \uCF54\uB4DC\uB97C \uC2A4\uCE94\uD558\uC5EC \uB9C1\uD06C\uC5D0 \uC811\uC18D\uD558\uC138\uC694" })] })), jsxRuntimeExports.jsxs("div", { className: "designbase-share__link-preview", children: [jsxRuntimeExports.jsx("h4", { className: "designbase-share__link-title", children: "\uACF5\uC720 \uB9C1\uD06C" }), jsxRuntimeExports.jsxs("div", { className: "designbase-share__link-container", children: [jsxRuntimeExports.jsx(Input, { type: "text", value: url, readOnly: true, size: "s", fullWidth: true, className: "designbase-share__link-input" }), jsxRuntimeExports.jsx(Button, { size: "s", variant: "primary", onPress: handleCopyLink, className: "designbase-share__copy-button", startIcon: isCopied ? icons.CopyIcon : icons.LinkIcon, children: isCopied ? '복사됨!' : '복사' })] })] })] }));
|
|
10314
10869
|
// 변형별 렌더링
|
|
10315
10870
|
switch (variant) {
|
|
10316
10871
|
case 'dropdown':
|
|
@@ -10381,22 +10936,14 @@ const Sidebar = ({ size = 'm', variant = 'default', position = 'left', logo, onL
|
|
|
10381
10936
|
e.preventDefault();
|
|
10382
10937
|
onLogoClick();
|
|
10383
10938
|
}
|
|
10384
|
-
}, children: logo || jsxRuntimeExports.jsx(Logo, { size:
|
|
10939
|
+
}, children: logo || jsxRuntimeExports.jsx(Logo, { size: "s" }) }), collapsible && (jsxRuntimeExports.jsx(Button, { variant: "ghost", size: "s", iconOnly: true, className: "designbase-sidebar__toggle", onPress: handleToggle, "aria-label": collapsed ? '사이드바 펼치기' : '사이드바 접기', children: jsxRuntimeExports.jsx(icons.ChevronLeftIcon, { className: clsx('designbase-sidebar__toggle-icon', {
|
|
10385
10940
|
'designbase-sidebar__toggle-icon--collapsed': collapsed,
|
|
10386
10941
|
}) }) }))] }), jsxRuntimeExports.jsx("nav", { className: "designbase-sidebar__nav", children: jsxRuntimeExports.jsx("ul", { className: "designbase-sidebar__nav-list", children: items.map((item) => renderSidebarItem(item)) }) }), userProfile && !collapsed && (jsxRuntimeExports.jsxs("div", { className: "designbase-sidebar__user", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-sidebar__user-info", onClick: () => onUserMenuItemClick?.({ id: 'profile', label: '프로필', href: '#' }), role: "button", tabIndex: 0, onKeyDown: (e) => {
|
|
10387
10942
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
10388
10943
|
e.preventDefault();
|
|
10389
10944
|
onUserMenuItemClick?.({ id: 'profile', label: '프로필', href: '#' });
|
|
10390
10945
|
}
|
|
10391
|
-
}, style: { cursor: 'pointer' }, children: [userProfile.avatar ? (jsxRuntimeExports.jsx("img", { src: userProfile.avatar, alt: userProfile.name, className: "designbase-sidebar__user-avatar" })) : (jsxRuntimeExports.jsx("div", { className: "designbase-sidebar__user-avatar-placeholder", children: userProfile.name.charAt(0).toUpperCase() })), jsxRuntimeExports.jsxs("div", { className: "designbase-sidebar__user-details", children: [jsxRuntimeExports.jsx("div", { className: "designbase-sidebar__user-name", children: userProfile.name }), userProfile.email && (jsxRuntimeExports.jsx("div", { className: "designbase-sidebar__user-email", children: userProfile.email })), userProfile.role && (jsxRuntimeExports.jsx("div", { className: "designbase-sidebar__user-role", children: userProfile.role }))] })] }), userMenuItems.length > 0 && (jsxRuntimeExports.jsx("ul", { className: "designbase-sidebar__user-menu", children: userMenuItems.map((item) => (jsxRuntimeExports.jsx("li", { children: jsxRuntimeExports.
|
|
10392
|
-
'designbase-sidebar__user-menu-link--disabled': item.disabled,
|
|
10393
|
-
}), onClick: (e) => {
|
|
10394
|
-
if (item.disabled) {
|
|
10395
|
-
e.preventDefault();
|
|
10396
|
-
return;
|
|
10397
|
-
}
|
|
10398
|
-
handleUserMenuItemClick(item);
|
|
10399
|
-
}, children: [item.icon && React.createElement(item.icon, { size: 16 }), item.label] }) }, item.id))) }))] })), userProfile && collapsed && (jsxRuntimeExports.jsx("div", { className: "designbase-sidebar__user-collapsed", onClick: () => onUserMenuItemClick?.({ id: 'profile', label: '프로필', href: '#' }), role: "button", tabIndex: 0, onKeyDown: (e) => {
|
|
10946
|
+
}, style: { cursor: 'pointer' }, children: [userProfile.avatar ? (jsxRuntimeExports.jsx("img", { src: userProfile.avatar, alt: userProfile.name, className: "designbase-sidebar__user-avatar" })) : (jsxRuntimeExports.jsx("div", { className: "designbase-sidebar__user-avatar-placeholder", children: userProfile.name.charAt(0).toUpperCase() })), jsxRuntimeExports.jsxs("div", { className: "designbase-sidebar__user-details", children: [jsxRuntimeExports.jsx("div", { className: "designbase-sidebar__user-name", children: userProfile.name }), userProfile.email && (jsxRuntimeExports.jsx("div", { className: "designbase-sidebar__user-email", children: userProfile.email })), userProfile.role && (jsxRuntimeExports.jsx("div", { className: "designbase-sidebar__user-role", children: userProfile.role }))] })] }), userMenuItems.length > 0 && (jsxRuntimeExports.jsx("ul", { className: "designbase-sidebar__user-menu", children: userMenuItems.map((item) => (jsxRuntimeExports.jsx("li", { children: jsxRuntimeExports.jsx(MenuItem, { id: item.id, label: item.label, href: item.href, icon: item.icon, disabled: item.disabled, type: "block", style: "accordion", onClick: () => handleUserMenuItemClick(item) }) }, item.id))) }))] })), userProfile && collapsed && (jsxRuntimeExports.jsx("div", { className: "designbase-sidebar__user-collapsed", onClick: () => onUserMenuItemClick?.({ id: 'profile', label: '프로필', href: '#' }), role: "button", tabIndex: 0, onKeyDown: (e) => {
|
|
10400
10947
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
10401
10948
|
e.preventDefault();
|
|
10402
10949
|
onUserMenuItemClick?.({ id: 'profile', label: '프로필', href: '#' });
|
|
@@ -10632,8 +11179,12 @@ const Stepper = ({ value = 0, min = 0, max = 100, step = 1, size = 'm', variant
|
|
|
10632
11179
|
return (jsxRuntimeExports.jsxs("div", { className: classes, children: [jsxRuntimeExports.jsx("button", { type: "button", className: decrementClasses, onClick: handleDecrement, disabled: disabled || readonly || isMinReached, "aria-label": "\uAC10\uC18C", "aria-describedby": "stepper-description", children: jsxRuntimeExports.jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsxRuntimeExports.jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" }) }) }), jsxRuntimeExports.jsx("div", { className: "designbase-stepper__value", children: jsxRuntimeExports.jsx("span", { className: "designbase-stepper__value-text", children: currentValue }) }), jsxRuntimeExports.jsx("button", { type: "button", className: incrementClasses, onClick: handleIncrement, disabled: disabled || readonly || isMaxReached, "aria-label": "\uC99D\uAC00", "aria-describedby": "stepper-description", children: jsxRuntimeExports.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [jsxRuntimeExports.jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }), jsxRuntimeExports.jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })] }) }), jsxRuntimeExports.jsxs("div", { id: "stepper-description", className: "sr-only", children: ["\uC22B\uC790 \uC870\uC815\uAE30. \uD654\uC0B4\uD45C \uD0A4\uB098 +/- \uD0A4\uB97C \uC0AC\uC6A9\uD558\uC5EC \uAC12\uC744 \uC870\uC815\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uD604\uC7AC \uAC12: ", currentValue, ", \uCD5C\uC18C\uAC12: ", min, ", \uCD5C\uB300\uAC12: ", max, ", \uB2E8\uACC4: ", step] })] }));
|
|
10633
11180
|
};
|
|
10634
11181
|
|
|
10635
|
-
const Table = ({ data, columns, title, showCountBadge = false, showFilter = false, filterOptions = [], onFilterChange, size = 'm', variant = 'default', selectable = false, multiSelect = false, selectedRows = [], onSelectionChange, sortable = false, sortColumn, sortDirection, onSortChange, loading = false, emptyMessage = '데이터가 없습니다.', onRowClick, rowKey = 'id', height, scrollable = false, className, ...props }) => {
|
|
11182
|
+
const Table = ({ data, columns, title, showCountBadge = false, showFilter = false, filterOptions = [], onFilterChange, size = 'm', variant = 'default', selectable = false, multiSelect = false, selectedRows = [], onSelectionChange, sortable = false, sortColumn, sortDirection, onSortChange, loading = false, emptyMessage = '데이터가 없습니다.', onRowClick, rowKey = 'id', height, scrollable = false, pagination = false, page, pageSize, totalItems, onPageChange, onPageSizeChange, pageSizeOptions, className, ...props }) => {
|
|
10636
11183
|
const [internalSelectedRows, setInternalSelectedRows] = React.useState(selectedRows);
|
|
11184
|
+
const [internalPage, setInternalPage] = React.useState(page || 1);
|
|
11185
|
+
const [internalPageSize, setInternalPageSize] = React.useState(pageSize || 10);
|
|
11186
|
+
const [internalSortColumn, setInternalSortColumn] = React.useState(sortColumn);
|
|
11187
|
+
const [internalSortDirection, setInternalSortDirection] = React.useState(sortDirection ?? null);
|
|
10637
11188
|
const getRowKey = (item, index) => {
|
|
10638
11189
|
if (typeof rowKey === 'string') {
|
|
10639
11190
|
return item[rowKey] || index.toString();
|
|
@@ -10643,8 +11194,27 @@ const Table = ({ data, columns, title, showCountBadge = false, showFilter = fals
|
|
|
10643
11194
|
const handleSort = (column) => {
|
|
10644
11195
|
if (!sortable || !column.sortable)
|
|
10645
11196
|
return;
|
|
10646
|
-
const
|
|
10647
|
-
|
|
11197
|
+
const currentCol = effectiveSortColumn;
|
|
11198
|
+
const currentDir = effectiveSortDirection;
|
|
11199
|
+
let nextDir = 'asc';
|
|
11200
|
+
if (currentCol === column.key) {
|
|
11201
|
+
if (currentDir === 'asc')
|
|
11202
|
+
nextDir = 'desc';
|
|
11203
|
+
else if (currentDir === 'desc')
|
|
11204
|
+
nextDir = null;
|
|
11205
|
+
else
|
|
11206
|
+
nextDir = 'asc';
|
|
11207
|
+
}
|
|
11208
|
+
else {
|
|
11209
|
+
nextDir = 'asc';
|
|
11210
|
+
}
|
|
11211
|
+
if (onSortChange) {
|
|
11212
|
+
onSortChange(column.key, nextDir);
|
|
11213
|
+
}
|
|
11214
|
+
else {
|
|
11215
|
+
setInternalSortColumn(nextDir ? column.key : undefined);
|
|
11216
|
+
setInternalSortDirection(nextDir);
|
|
11217
|
+
}
|
|
10648
11218
|
};
|
|
10649
11219
|
const handleRowSelect = (itemRowKey, checked) => {
|
|
10650
11220
|
let newSelectedRows;
|
|
@@ -10668,24 +11238,34 @@ const Table = ({ data, columns, title, showCountBadge = false, showFilter = fals
|
|
|
10668
11238
|
setInternalSelectedRows(newSelectedRows);
|
|
10669
11239
|
onSelectionChange?.(newSelectedRows);
|
|
10670
11240
|
};
|
|
11241
|
+
const effectiveSortColumn = sortColumn !== undefined ? sortColumn : internalSortColumn;
|
|
11242
|
+
const effectiveSortDirection = sortDirection !== undefined ? sortDirection : internalSortDirection;
|
|
10671
11243
|
const sortedData = React.useMemo(() => {
|
|
10672
|
-
if (!
|
|
11244
|
+
if (!effectiveSortColumn || !effectiveSortDirection)
|
|
10673
11245
|
return data;
|
|
10674
|
-
const column = columns.find(col => col.key ===
|
|
11246
|
+
const column = columns.find(col => col.key === effectiveSortColumn);
|
|
10675
11247
|
if (!column || !column.sortable)
|
|
10676
11248
|
return data;
|
|
10677
11249
|
return [...data].sort((a, b) => {
|
|
10678
11250
|
const aValue = column.accessor ? column.accessor(a) : a[column.key];
|
|
10679
11251
|
const bValue = column.accessor ? column.accessor(b) : b[column.key];
|
|
10680
11252
|
if (aValue < bValue)
|
|
10681
|
-
return
|
|
11253
|
+
return effectiveSortDirection === 'asc' ? -1 : 1;
|
|
10682
11254
|
if (aValue > bValue)
|
|
10683
|
-
return
|
|
11255
|
+
return effectiveSortDirection === 'asc' ? 1 : -1;
|
|
10684
11256
|
return 0;
|
|
10685
11257
|
});
|
|
10686
|
-
}, [data,
|
|
11258
|
+
}, [data, effectiveSortColumn, effectiveSortDirection, columns, internalSortColumn, internalSortDirection]);
|
|
10687
11259
|
const isAllSelected = data.length > 0 && internalSelectedRows.length === data.length;
|
|
10688
11260
|
const isIndeterminate = internalSelectedRows.length > 0 && internalSelectedRows.length < data.length;
|
|
11261
|
+
// 페이지네이션 계산
|
|
11262
|
+
const effectivePageSize = pageSize || internalPageSize;
|
|
11263
|
+
const total = totalItems ?? sortedData.length;
|
|
11264
|
+
const totalPages = Math.max(1, Math.ceil(total / effectivePageSize));
|
|
11265
|
+
const currentPage = Math.min(page || internalPage, totalPages);
|
|
11266
|
+
const startIndex = (currentPage - 1) * effectivePageSize;
|
|
11267
|
+
const endIndex = startIndex + effectivePageSize;
|
|
11268
|
+
const paginatedData = pagination ? sortedData.slice(startIndex, endIndex) : sortedData;
|
|
10689
11269
|
const classes = clsx('designbase-table', `designbase-table--${size}`, `designbase-table--${variant}`, {
|
|
10690
11270
|
'designbase-table--selectable': selectable,
|
|
10691
11271
|
'designbase-table--sortable': sortable,
|
|
@@ -10698,13 +11278,13 @@ const Table = ({ data, columns, title, showCountBadge = false, showFilter = fals
|
|
|
10698
11278
|
const renderSortIcon = (column) => {
|
|
10699
11279
|
if (!sortable || !column.sortable)
|
|
10700
11280
|
return null;
|
|
10701
|
-
const isSorted =
|
|
10702
|
-
const isAsc = isSorted &&
|
|
10703
|
-
const isDesc = isSorted &&
|
|
10704
|
-
return (jsxRuntimeExports.jsx("span", { className: "designbase-table__sort-icon", children: jsxRuntimeExports.jsx(icons.
|
|
11281
|
+
const isSorted = effectiveSortColumn === column.key;
|
|
11282
|
+
const isAsc = isSorted && effectiveSortDirection === 'asc';
|
|
11283
|
+
const isDesc = isSorted && effectiveSortDirection === 'desc';
|
|
11284
|
+
return (jsxRuntimeExports.jsx("span", { className: "designbase-table__sort-icon", children: isSorted ? (jsxRuntimeExports.jsx(icons.VerticalArrowsIcon, { size: 14, className: clsx({
|
|
10705
11285
|
'designbase-table__sort-icon--asc': isAsc,
|
|
10706
11286
|
'designbase-table__sort-icon--desc': isDesc,
|
|
10707
|
-
}) }) }));
|
|
11287
|
+
}) })) : (jsxRuntimeExports.jsx(icons.CaretUpdownFilledIcon, { size: 14, className: "designbase-table__sort-icon--idle" })) }));
|
|
10708
11288
|
};
|
|
10709
11289
|
const renderCell = (column, item, index) => {
|
|
10710
11290
|
const value = column.accessor ? column.accessor(item) : item[column.key];
|
|
@@ -10722,7 +11302,7 @@ const Table = ({ data, columns, title, showCountBadge = false, showFilter = fals
|
|
|
10722
11302
|
[`designbase-table__th--${column.align || 'left'}`]: column.align,
|
|
10723
11303
|
'designbase-table__th--fixed-left': column.fixed === 'left',
|
|
10724
11304
|
'designbase-table__th--fixed-right': column.fixed === 'right',
|
|
10725
|
-
}), style: { width: column.width }, onClick: () => handleSort(column), children: jsxRuntimeExports.jsxs("div", { className: "designbase-table__th-content", children: [jsxRuntimeExports.jsx("span", { className: "designbase-table__th-text", children: column.header }), renderSortIcon(column)] }) }, column.key)))] }) }), jsxRuntimeExports.jsx("tbody", { className: "designbase-table__tbody", children:
|
|
11305
|
+
}), style: { width: column.width }, onClick: () => handleSort(column), children: jsxRuntimeExports.jsxs("div", { className: "designbase-table__th-content", children: [jsxRuntimeExports.jsx("span", { className: "designbase-table__th-text", children: column.header }), renderSortIcon(column)] }) }, column.key)))] }) }), jsxRuntimeExports.jsx("tbody", { className: "designbase-table__tbody", children: paginatedData.length === 0 ? (jsxRuntimeExports.jsx("tr", { className: "designbase-table__tr", children: jsxRuntimeExports.jsx("td", { colSpan: columns.length + (selectable ? 1 : 0), className: "designbase-table__td designbase-table__td--empty", children: jsxRuntimeExports.jsx(EmptyState, { title: "\uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4", description: emptyMessage, variant: "no-data", size: "m" }) }) })) : (paginatedData.map((item, index) => {
|
|
10726
11306
|
const key = getRowKey(item, index);
|
|
10727
11307
|
const isSelected = internalSelectedRows.includes(key);
|
|
10728
11308
|
return (jsxRuntimeExports.jsxs("tr", { className: clsx('designbase-table__tr', {
|
|
@@ -10733,7 +11313,7 @@ const Table = ({ data, columns, title, showCountBadge = false, showFilter = fals
|
|
|
10733
11313
|
'designbase-table__td--fixed-left': column.fixed === 'left',
|
|
10734
11314
|
'designbase-table__td--fixed-right': column.fixed === 'right',
|
|
10735
11315
|
}), children: renderCell(column, item, index) }, column.key)))] }, key));
|
|
10736
|
-
})) })] }) })] }));
|
|
11316
|
+
})) })] }) }), pagination && (jsxRuntimeExports.jsx("div", { className: "designbase-table__footer", children: jsxRuntimeExports.jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, totalItems: total, pageSize: effectivePageSize, pageSizeOptions: pageSizeOptions || [10, 20, 50], onPageChange: (p) => { setInternalPage(p); onPageChange?.(p); }, onPageSizeChange: (s) => { setInternalPage(1); setInternalPageSize(s); onPageSizeChange?.(s); onPageChange?.(1); }, showPageSizeSelector: true, showTotal: true, alignment: "right", size: size }) }))] }));
|
|
10737
11317
|
};
|
|
10738
11318
|
Table.displayName = 'Table';
|
|
10739
11319
|
|
|
@@ -10854,198 +11434,7 @@ const Tabs = ({ items, defaultSelectedId, selectedId, orientation = 'horizontal'
|
|
|
10854
11434
|
};
|
|
10855
11435
|
Tabs.displayName = 'Tabs';
|
|
10856
11436
|
|
|
10857
|
-
|
|
10858
|
-
const [isOpen, setIsOpen] = React.useState(false);
|
|
10859
|
-
const [selectedHour, setSelectedHour] = React.useState(() => {
|
|
10860
|
-
if (value) {
|
|
10861
|
-
return value.getHours();
|
|
10862
|
-
}
|
|
10863
|
-
return 0;
|
|
10864
|
-
});
|
|
10865
|
-
const [selectedMinute, setSelectedMinute] = React.useState(() => {
|
|
10866
|
-
if (value) {
|
|
10867
|
-
return value.getMinutes();
|
|
10868
|
-
}
|
|
10869
|
-
return 0;
|
|
10870
|
-
});
|
|
10871
|
-
const [selectedPeriod, setSelectedPeriod] = React.useState(() => {
|
|
10872
|
-
if (value && format === '12h') {
|
|
10873
|
-
return value.getHours() >= 12 ? 'PM' : 'AM';
|
|
10874
|
-
}
|
|
10875
|
-
return 'AM';
|
|
10876
|
-
});
|
|
10877
|
-
const containerRef = React.useRef(null);
|
|
10878
|
-
// 시간 유효성 검사
|
|
10879
|
-
const isTimeDisabled = (hour, minute) => {
|
|
10880
|
-
if (minTime) {
|
|
10881
|
-
const minHour = minTime.getHours();
|
|
10882
|
-
const minMinute = minTime.getMinutes();
|
|
10883
|
-
if (hour < minHour || (hour === minHour && minute < minMinute)) {
|
|
10884
|
-
return true;
|
|
10885
|
-
}
|
|
10886
|
-
}
|
|
10887
|
-
if (maxTime) {
|
|
10888
|
-
const maxHour = maxTime.getHours();
|
|
10889
|
-
const maxMinute = maxTime.getMinutes();
|
|
10890
|
-
if (hour > maxHour || (hour === maxHour && minute > maxMinute)) {
|
|
10891
|
-
return true;
|
|
10892
|
-
}
|
|
10893
|
-
}
|
|
10894
|
-
return false;
|
|
10895
|
-
};
|
|
10896
|
-
// 24시간을 12시간으로 변환
|
|
10897
|
-
const to12Hour = (hour) => {
|
|
10898
|
-
if (hour === 0)
|
|
10899
|
-
return 12;
|
|
10900
|
-
if (hour > 12)
|
|
10901
|
-
return hour - 12;
|
|
10902
|
-
return hour;
|
|
10903
|
-
};
|
|
10904
|
-
// 12시간을 24시간으로 변환
|
|
10905
|
-
const to24Hour = (hour, period) => {
|
|
10906
|
-
if (period === 'AM') {
|
|
10907
|
-
return hour === 12 ? 0 : hour;
|
|
10908
|
-
}
|
|
10909
|
-
else {
|
|
10910
|
-
return hour === 12 ? 12 : hour + 12;
|
|
10911
|
-
}
|
|
10912
|
-
};
|
|
10913
|
-
// 시간 변경 핸들러
|
|
10914
|
-
const handleTimeChange = (hour, minute, period) => {
|
|
10915
|
-
if (isTimeDisabled(hour, minute))
|
|
10916
|
-
return;
|
|
10917
|
-
let finalHour = hour;
|
|
10918
|
-
if (format === '12h' && period) {
|
|
10919
|
-
finalHour = to24Hour(hour, period);
|
|
10920
|
-
}
|
|
10921
|
-
const newTime = new Date();
|
|
10922
|
-
newTime.setHours(finalHour, minute, 0, 0);
|
|
10923
|
-
setSelectedHour(hour);
|
|
10924
|
-
setSelectedMinute(minute);
|
|
10925
|
-
if (period) {
|
|
10926
|
-
setSelectedPeriod(period);
|
|
10927
|
-
}
|
|
10928
|
-
onChange?.(newTime);
|
|
10929
|
-
if (mode === 'dropdown') {
|
|
10930
|
-
setIsOpen(false);
|
|
10931
|
-
}
|
|
10932
|
-
};
|
|
10933
|
-
// 시간 표시 형식
|
|
10934
|
-
const formatDisplayTime = () => {
|
|
10935
|
-
if (!value)
|
|
10936
|
-
return '';
|
|
10937
|
-
if (displayFormat) {
|
|
10938
|
-
return value.toLocaleTimeString('ko-KR', {
|
|
10939
|
-
hour: '2-digit',
|
|
10940
|
-
minute: '2-digit',
|
|
10941
|
-
hour12: format === '12h',
|
|
10942
|
-
});
|
|
10943
|
-
}
|
|
10944
|
-
const hour = format === '12h' ? to12Hour(value.getHours()) : value.getHours();
|
|
10945
|
-
const minute = value.getMinutes();
|
|
10946
|
-
const period = format === '12h' ? (value.getHours() >= 12 ? 'PM' : 'AM') : '';
|
|
10947
|
-
return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}${period ? ` ${period}` : ''}`;
|
|
10948
|
-
};
|
|
10949
|
-
// 시간 옵션 생성
|
|
10950
|
-
const generateHourOptions = () => {
|
|
10951
|
-
const maxHour = format === '12h' ? 12 : 24;
|
|
10952
|
-
const hours = [];
|
|
10953
|
-
for (let i = 0; i < maxHour; i += hourStep) {
|
|
10954
|
-
if (format === '12h' && i === 0)
|
|
10955
|
-
continue; // 12시간 형식에서는 0시 제외
|
|
10956
|
-
hours.push(i);
|
|
10957
|
-
}
|
|
10958
|
-
return hours;
|
|
10959
|
-
};
|
|
10960
|
-
// 분 옵션 생성
|
|
10961
|
-
const generateMinuteOptions = () => {
|
|
10962
|
-
const minutes = [];
|
|
10963
|
-
for (let i = 0; i < 60; i += minuteStep) {
|
|
10964
|
-
minutes.push(i);
|
|
10965
|
-
}
|
|
10966
|
-
return minutes;
|
|
10967
|
-
};
|
|
10968
|
-
// 외부 클릭 감지
|
|
10969
|
-
React.useEffect(() => {
|
|
10970
|
-
const handleClickOutside = (event) => {
|
|
10971
|
-
if (containerRef.current && !containerRef.current.contains(event.target)) {
|
|
10972
|
-
setIsOpen(false);
|
|
10973
|
-
}
|
|
10974
|
-
};
|
|
10975
|
-
if (isOpen) {
|
|
10976
|
-
document.addEventListener('mousedown', handleClickOutside);
|
|
10977
|
-
}
|
|
10978
|
-
return () => {
|
|
10979
|
-
document.removeEventListener('mousedown', handleClickOutside);
|
|
10980
|
-
};
|
|
10981
|
-
}, [isOpen]);
|
|
10982
|
-
// 키보드 네비게이션
|
|
10983
|
-
const handleKeyDown = (event) => {
|
|
10984
|
-
if (disabled || readonly)
|
|
10985
|
-
return;
|
|
10986
|
-
switch (event.key) {
|
|
10987
|
-
case 'Enter':
|
|
10988
|
-
case ' ':
|
|
10989
|
-
event.preventDefault();
|
|
10990
|
-
setIsOpen(!isOpen);
|
|
10991
|
-
break;
|
|
10992
|
-
case 'Escape':
|
|
10993
|
-
setIsOpen(false);
|
|
10994
|
-
break;
|
|
10995
|
-
}
|
|
10996
|
-
};
|
|
10997
|
-
const hourOptions = generateHourOptions();
|
|
10998
|
-
const minuteOptions = generateMinuteOptions();
|
|
10999
|
-
const classes = clsx('designbase-time-picker', `designbase-time-picker--size-${size}`, `designbase-time-picker--variant-${variant}`, `designbase-time-picker--mode-${mode}`, {
|
|
11000
|
-
'designbase-time-picker--disabled': disabled,
|
|
11001
|
-
'designbase-time-picker--readonly': readonly,
|
|
11002
|
-
'designbase-time-picker--open': isOpen,
|
|
11003
|
-
}, className);
|
|
11004
|
-
if (mode === 'inline') {
|
|
11005
|
-
return (jsxRuntimeExports.jsx("div", { className: classes, children: jsxRuntimeExports.jsxs("div", { className: "designbase-time-picker__inline", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-time-picker__column", children: [jsxRuntimeExports.jsx("div", { className: "designbase-time-picker__column-header", children: "\uC2DC\uAC04" }), jsxRuntimeExports.jsx("div", { className: "designbase-time-picker__options", children: hourOptions.map((hour) => {
|
|
11006
|
-
const displayHour = format === '12h' ? to12Hour(hour) : hour;
|
|
11007
|
-
const isDisabled = isTimeDisabled(hour, selectedMinute);
|
|
11008
|
-
const isSelected = selectedHour === displayHour;
|
|
11009
|
-
return (jsxRuntimeExports.jsx("button", { className: clsx('designbase-time-picker__option', {
|
|
11010
|
-
'designbase-time-picker__option--selected': isSelected,
|
|
11011
|
-
'designbase-time-picker__option--disabled': isDisabled,
|
|
11012
|
-
}), onClick: () => handleTimeChange(hour, selectedMinute, selectedPeriod), disabled: isDisabled, children: displayHour.toString().padStart(2, '0') }, hour));
|
|
11013
|
-
}) })] }), jsxRuntimeExports.jsxs("div", { className: "designbase-time-picker__column", children: [jsxRuntimeExports.jsx("div", { className: "designbase-time-picker__column-header", children: "\uBD84" }), jsxRuntimeExports.jsx("div", { className: "designbase-time-picker__options", children: minuteOptions.map((minute) => {
|
|
11014
|
-
const isDisabled = isTimeDisabled(selectedHour, minute);
|
|
11015
|
-
const isSelected = selectedMinute === minute;
|
|
11016
|
-
return (jsxRuntimeExports.jsx("button", { className: clsx('designbase-time-picker__option', {
|
|
11017
|
-
'designbase-time-picker__option--selected': isSelected,
|
|
11018
|
-
'designbase-time-picker__option--disabled': isDisabled,
|
|
11019
|
-
}), onClick: () => handleTimeChange(selectedHour, minute, selectedPeriod), disabled: isDisabled, children: minute.toString().padStart(2, '0') }, minute));
|
|
11020
|
-
}) })] }), format === '12h' && (jsxRuntimeExports.jsxs("div", { className: "designbase-time-picker__column", children: [jsxRuntimeExports.jsx("div", { className: "designbase-time-picker__column-header", children: "\uAD6C\uBD84" }), jsxRuntimeExports.jsx("div", { className: "designbase-time-picker__options", children: ['AM', 'PM'].map((period) => {
|
|
11021
|
-
const isSelected = selectedPeriod === period;
|
|
11022
|
-
return (jsxRuntimeExports.jsx("button", { className: clsx('designbase-time-picker__option', {
|
|
11023
|
-
'designbase-time-picker__option--selected': isSelected,
|
|
11024
|
-
}), onClick: () => handleTimeChange(selectedHour, selectedMinute, period), children: period }, period));
|
|
11025
|
-
}) })] }))] }) }));
|
|
11026
|
-
}
|
|
11027
|
-
return (jsxRuntimeExports.jsxs("div", { className: classes, ref: containerRef, children: [jsxRuntimeExports.jsxs("button", { className: "designbase-time-picker__trigger", onClick: () => !disabled && !readonly && setIsOpen(!isOpen), onKeyDown: handleKeyDown, disabled: disabled, type: "button", "aria-haspopup": "listbox", "aria-expanded": isOpen, children: [jsxRuntimeExports.jsx("span", { className: "designbase-time-picker__display", children: value ? formatDisplayTime() : placeholder }), jsxRuntimeExports.jsx("span", { className: "designbase-time-picker__arrow", children: jsxRuntimeExports.jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", children: jsxRuntimeExports.jsx("path", { d: "M3 4.5L6 7.5L9 4.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) })] }), isOpen && (jsxRuntimeExports.jsx("div", { className: "designbase-time-picker__dropdown", children: jsxRuntimeExports.jsxs("div", { className: "designbase-time-picker__dropdown-content", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-time-picker__column", children: [jsxRuntimeExports.jsx("div", { className: "designbase-time-picker__column-header", children: "\uC2DC\uAC04" }), jsxRuntimeExports.jsx("div", { className: "designbase-time-picker__options", children: hourOptions.map((hour) => {
|
|
11028
|
-
const displayHour = format === '12h' ? to12Hour(hour) : hour;
|
|
11029
|
-
const isDisabled = isTimeDisabled(hour, selectedMinute);
|
|
11030
|
-
const isSelected = selectedHour === displayHour;
|
|
11031
|
-
return (jsxRuntimeExports.jsx("button", { className: clsx('designbase-time-picker__option', {
|
|
11032
|
-
'designbase-time-picker__option--selected': isSelected,
|
|
11033
|
-
'designbase-time-picker__option--disabled': isDisabled,
|
|
11034
|
-
}), onClick: () => handleTimeChange(hour, selectedMinute, selectedPeriod), disabled: isDisabled, children: displayHour.toString().padStart(2, '0') }, hour));
|
|
11035
|
-
}) })] }), jsxRuntimeExports.jsxs("div", { className: "designbase-time-picker__column", children: [jsxRuntimeExports.jsx("div", { className: "designbase-time-picker__column-header", children: "\uBD84" }), jsxRuntimeExports.jsx("div", { className: "designbase-time-picker__options", children: minuteOptions.map((minute) => {
|
|
11036
|
-
const isDisabled = isTimeDisabled(selectedHour, minute);
|
|
11037
|
-
const isSelected = selectedMinute === minute;
|
|
11038
|
-
return (jsxRuntimeExports.jsx("button", { className: clsx('designbase-time-picker__option', {
|
|
11039
|
-
'designbase-time-picker__option--selected': isSelected,
|
|
11040
|
-
'designbase-time-picker__option--disabled': isDisabled,
|
|
11041
|
-
}), onClick: () => handleTimeChange(selectedHour, minute, selectedPeriod), disabled: isDisabled, children: minute.toString().padStart(2, '0') }, minute));
|
|
11042
|
-
}) })] }), format === '12h' && (jsxRuntimeExports.jsxs("div", { className: "designbase-time-picker__column", children: [jsxRuntimeExports.jsx("div", { className: "designbase-time-picker__column-header", children: "\uAD6C\uBD84" }), jsxRuntimeExports.jsx("div", { className: "designbase-time-picker__options", children: ['AM', 'PM'].map((period) => {
|
|
11043
|
-
const isSelected = selectedPeriod === period;
|
|
11044
|
-
return (jsxRuntimeExports.jsx("button", { className: clsx('designbase-time-picker__option', {
|
|
11045
|
-
'designbase-time-picker__option--selected': isSelected,
|
|
11046
|
-
}), onClick: () => handleTimeChange(selectedHour, selectedMinute, period), children: period }, period));
|
|
11047
|
-
}) })] }))] }) }))] }));
|
|
11048
|
-
};
|
|
11437
|
+
TimePicker;
|
|
11049
11438
|
|
|
11050
11439
|
const Timeline = ({ items, position = 'alternate', variant = 'default', size = 'm', color = 'primary', clickable = false, fullWidth = false, disabled = false, className, style, onItemClick, }) => {
|
|
11051
11440
|
const handleItemClick = (item, index) => {
|
|
@@ -11208,6 +11597,22 @@ Toggle.displayName = 'Toggle';
|
|
|
11208
11597
|
|
|
11209
11598
|
const Toolbar = ({ items, size = 'm', variant = 'default', position = 'top', fullWidth = false, fixed = false, shadow = true, rounded = true, className, children, }) => {
|
|
11210
11599
|
const [openDropdown, setOpenDropdown] = React.useState(null);
|
|
11600
|
+
const mapSizeToButtonSize = (size) => {
|
|
11601
|
+
switch (size) {
|
|
11602
|
+
case 's': return 's';
|
|
11603
|
+
case 'm': return 'm';
|
|
11604
|
+
case 'l': return 'l';
|
|
11605
|
+
default: return 'm';
|
|
11606
|
+
}
|
|
11607
|
+
};
|
|
11608
|
+
const getIconComponentFromNode = (node) => {
|
|
11609
|
+
if (!node)
|
|
11610
|
+
return undefined;
|
|
11611
|
+
if (React.isValidElement(node) && typeof node.type === 'function') {
|
|
11612
|
+
return node.type;
|
|
11613
|
+
}
|
|
11614
|
+
return undefined;
|
|
11615
|
+
};
|
|
11211
11616
|
const handleItemClick = (item) => {
|
|
11212
11617
|
if (item.disabled)
|
|
11213
11618
|
return;
|
|
@@ -11227,10 +11632,14 @@ const Toolbar = ({ items, size = 'm', variant = 'default', position = 'top', ful
|
|
|
11227
11632
|
const renderItem = (item) => {
|
|
11228
11633
|
switch (item.type) {
|
|
11229
11634
|
case 'button':
|
|
11230
|
-
|
|
11231
|
-
|
|
11232
|
-
|
|
11233
|
-
|
|
11635
|
+
{
|
|
11636
|
+
const isIconOnly = !item.label;
|
|
11637
|
+
const StartIconComp = getIconComponentFromNode(item.icon);
|
|
11638
|
+
return (jsxRuntimeExports.jsx(Button, { variant: "tertiary", size: mapSizeToButtonSize(size), className: clsx('designbase-toolbar__item', 'designbase-toolbar__item--button', {
|
|
11639
|
+
'designbase-toolbar__item--active': item.active,
|
|
11640
|
+
'designbase-toolbar__item--disabled': item.disabled,
|
|
11641
|
+
}), iconOnly: isIconOnly, startIcon: !isIconOnly ? StartIconComp : undefined, onPress: () => handleItemClick(item), disabled: item.disabled, "aria-label": isIconOnly ? (item.tooltip || item.label || item.id) : undefined, title: item.tooltip, children: isIconOnly && item.icon ? item.icon : item.label }, item.id));
|
|
11642
|
+
}
|
|
11234
11643
|
case 'dropdown':
|
|
11235
11644
|
return (jsxRuntimeExports.jsxs("div", { className: clsx('designbase-toolbar__item', 'designbase-toolbar__item--dropdown', {
|
|
11236
11645
|
'designbase-toolbar__item--open': openDropdown === item.id,
|
|
@@ -11265,219 +11674,211 @@ const Toolbar = ({ items, size = 'm', variant = 'default', position = 'top', ful
|
|
|
11265
11674
|
return (jsxRuntimeExports.jsx("div", { className: classes, children: jsxRuntimeExports.jsxs("div", { className: "designbase-toolbar__content", children: [Object.entries(groupedItems).map(([groupName, groupItems], groupIndex) => (jsxRuntimeExports.jsxs("div", { className: "designbase-toolbar__group", children: [groupItems.map(renderItem), groupIndex < Object.keys(groupedItems).length - 1 && (jsxRuntimeExports.jsx("div", { className: "designbase-toolbar__group-separator" }))] }, groupName))), children && (jsxRuntimeExports.jsx("div", { className: "designbase-toolbar__children", children: children }))] }) }));
|
|
11266
11675
|
};
|
|
11267
11676
|
|
|
11268
|
-
const
|
|
11269
|
-
|
|
11270
|
-
|
|
11677
|
+
const GAP = 8; // 트리거와의 간격
|
|
11678
|
+
const ARW = 6; // 화살표 반쪽 길이(삼각형 변 길이)
|
|
11679
|
+
const PAD = 8; // 화살표가 박스 안에서 안전하게 보일 최소 여백
|
|
11680
|
+
const Tooltip = ({ content, children, position = 'top', size = 'm', variant = 'default', delay = 200, hideDelay = 80, alwaysShow = false, disabled = false, maxWidth = 240, showArrow = true, className, ...props }) => {
|
|
11681
|
+
const [visible, setVisible] = React.useState(false);
|
|
11682
|
+
const [style, setStyle] = React.useState({});
|
|
11271
11683
|
const [arrowStyle, setArrowStyle] = React.useState({});
|
|
11272
|
-
|
|
11273
|
-
const
|
|
11684
|
+
const [placementGroup, setPlacementGroup] = React.useState('top');
|
|
11685
|
+
const triggerRef = React.useRef(null);
|
|
11274
11686
|
const tooltipRef = React.useRef(null);
|
|
11275
|
-
|
|
11276
|
-
const
|
|
11687
|
+
const timers = React.useRef({});
|
|
11688
|
+
const rafId = React.useRef(null);
|
|
11277
11689
|
const clearTimer = () => {
|
|
11278
|
-
if (
|
|
11279
|
-
|
|
11280
|
-
|
|
11690
|
+
if (timers.current.t) {
|
|
11691
|
+
clearTimeout(timers.current.t);
|
|
11692
|
+
timers.current.t = undefined;
|
|
11281
11693
|
}
|
|
11282
11694
|
};
|
|
11283
|
-
|
|
11695
|
+
const groupOf = (p) => p.startsWith('top') ? 'top' :
|
|
11696
|
+
p.startsWith('bottom') ? 'bottom' :
|
|
11697
|
+
p.startsWith('left') ? 'left' : 'right';
|
|
11698
|
+
// 위치 계산 (⚠️ 뷰포트 클램핑 후 화살표도 보정)
|
|
11284
11699
|
const calculatePosition = React.useCallback(() => {
|
|
11285
|
-
if (!
|
|
11700
|
+
if (!triggerRef.current || !tooltipRef.current)
|
|
11286
11701
|
return;
|
|
11287
|
-
const
|
|
11288
|
-
const
|
|
11289
|
-
|
|
11290
|
-
|
|
11291
|
-
let
|
|
11292
|
-
let left = 0;
|
|
11293
|
-
let arrowTop = 0;
|
|
11294
|
-
let arrowLeft = 0;
|
|
11295
|
-
const isVertical = position.startsWith('top') || position.startsWith('bottom');
|
|
11296
|
-
const isHorizontal = position.startsWith('left') || position.startsWith('right');
|
|
11702
|
+
const tRect = triggerRef.current.getBoundingClientRect();
|
|
11703
|
+
const pRect = tooltipRef.current.getBoundingClientRect();
|
|
11704
|
+
let top = 0, left = 0;
|
|
11705
|
+
// 1차: 이상적(미보정) 화살표 좌표
|
|
11706
|
+
let aTop = 0, aLeft = 0;
|
|
11297
11707
|
switch (position) {
|
|
11298
11708
|
case 'top':
|
|
11299
|
-
top =
|
|
11300
|
-
left =
|
|
11301
|
-
|
|
11302
|
-
|
|
11303
|
-
arrowLeft = 0;
|
|
11709
|
+
top = tRect.top - pRect.height - GAP;
|
|
11710
|
+
left = tRect.left + tRect.width / 2 - pRect.width / 2;
|
|
11711
|
+
aTop = pRect.height;
|
|
11712
|
+
aLeft = pRect.width / 2 - ARW;
|
|
11304
11713
|
break;
|
|
11305
11714
|
case 'top-start':
|
|
11306
|
-
top =
|
|
11307
|
-
left =
|
|
11308
|
-
|
|
11309
|
-
|
|
11310
|
-
arrowLeft = 0;
|
|
11715
|
+
top = tRect.top - pRect.height - GAP;
|
|
11716
|
+
left = tRect.left;
|
|
11717
|
+
aTop = pRect.height;
|
|
11718
|
+
aLeft = PAD;
|
|
11311
11719
|
break;
|
|
11312
11720
|
case 'top-end':
|
|
11313
|
-
top =
|
|
11314
|
-
left =
|
|
11315
|
-
|
|
11316
|
-
|
|
11317
|
-
arrowLeft = 0;
|
|
11721
|
+
top = tRect.top - pRect.height - GAP;
|
|
11722
|
+
left = tRect.right - pRect.width;
|
|
11723
|
+
aTop = pRect.height;
|
|
11724
|
+
aLeft = pRect.width - PAD;
|
|
11318
11725
|
break;
|
|
11319
11726
|
case 'bottom':
|
|
11320
|
-
top =
|
|
11321
|
-
left =
|
|
11322
|
-
|
|
11323
|
-
|
|
11324
|
-
arrowLeft = 0;
|
|
11727
|
+
top = tRect.bottom + GAP;
|
|
11728
|
+
left = tRect.left + tRect.width / 2 - pRect.width / 2;
|
|
11729
|
+
aTop = -ARW;
|
|
11730
|
+
aLeft = pRect.width / 2 - ARW;
|
|
11325
11731
|
break;
|
|
11326
11732
|
case 'bottom-start':
|
|
11327
|
-
top =
|
|
11328
|
-
left =
|
|
11329
|
-
|
|
11330
|
-
|
|
11331
|
-
arrowLeft = 0;
|
|
11733
|
+
top = tRect.bottom + GAP;
|
|
11734
|
+
left = tRect.left;
|
|
11735
|
+
aTop = -ARW;
|
|
11736
|
+
aLeft = PAD;
|
|
11332
11737
|
break;
|
|
11333
11738
|
case 'bottom-end':
|
|
11334
|
-
top =
|
|
11335
|
-
left =
|
|
11336
|
-
|
|
11337
|
-
|
|
11338
|
-
arrowLeft = 0;
|
|
11739
|
+
top = tRect.bottom + GAP;
|
|
11740
|
+
left = tRect.right - pRect.width;
|
|
11741
|
+
aTop = -ARW;
|
|
11742
|
+
aLeft = pRect.width - PAD;
|
|
11339
11743
|
break;
|
|
11340
11744
|
case 'left':
|
|
11341
|
-
top =
|
|
11342
|
-
left =
|
|
11343
|
-
|
|
11344
|
-
|
|
11345
|
-
arrowLeft = 0;
|
|
11745
|
+
top = tRect.top + tRect.height / 2 - pRect.height / 2;
|
|
11746
|
+
left = tRect.left - pRect.width - GAP;
|
|
11747
|
+
aTop = pRect.height / 2 - ARW;
|
|
11748
|
+
aLeft = pRect.width;
|
|
11346
11749
|
break;
|
|
11347
11750
|
case 'left-start':
|
|
11348
|
-
top =
|
|
11349
|
-
left =
|
|
11350
|
-
|
|
11351
|
-
|
|
11352
|
-
arrowLeft = 0;
|
|
11751
|
+
top = tRect.top;
|
|
11752
|
+
left = tRect.left - pRect.width - GAP;
|
|
11753
|
+
aTop = PAD;
|
|
11754
|
+
aLeft = pRect.width;
|
|
11353
11755
|
break;
|
|
11354
11756
|
case 'left-end':
|
|
11355
|
-
top =
|
|
11356
|
-
left =
|
|
11357
|
-
|
|
11358
|
-
|
|
11359
|
-
arrowLeft = 0;
|
|
11757
|
+
top = tRect.bottom - pRect.height;
|
|
11758
|
+
left = tRect.left - pRect.width - GAP;
|
|
11759
|
+
aTop = pRect.height - PAD;
|
|
11760
|
+
aLeft = pRect.width;
|
|
11360
11761
|
break;
|
|
11361
11762
|
case 'right':
|
|
11362
|
-
top =
|
|
11363
|
-
left =
|
|
11364
|
-
|
|
11365
|
-
|
|
11366
|
-
arrowLeft = 0;
|
|
11763
|
+
top = tRect.top + tRect.height / 2 - pRect.height / 2;
|
|
11764
|
+
left = tRect.right + GAP;
|
|
11765
|
+
aTop = pRect.height / 2 - ARW;
|
|
11766
|
+
aLeft = -ARW;
|
|
11367
11767
|
break;
|
|
11368
11768
|
case 'right-start':
|
|
11369
|
-
top =
|
|
11370
|
-
left =
|
|
11371
|
-
|
|
11372
|
-
|
|
11373
|
-
arrowLeft = 0;
|
|
11769
|
+
top = tRect.top;
|
|
11770
|
+
left = tRect.right + GAP;
|
|
11771
|
+
aTop = PAD;
|
|
11772
|
+
aLeft = -ARW;
|
|
11374
11773
|
break;
|
|
11375
11774
|
case 'right-end':
|
|
11376
|
-
top =
|
|
11377
|
-
left =
|
|
11378
|
-
|
|
11379
|
-
|
|
11380
|
-
arrowLeft = 0;
|
|
11775
|
+
top = tRect.bottom - pRect.height;
|
|
11776
|
+
left = tRect.right + GAP;
|
|
11777
|
+
aTop = pRect.height - PAD;
|
|
11778
|
+
aLeft = -ARW;
|
|
11381
11779
|
break;
|
|
11382
11780
|
}
|
|
11383
|
-
//
|
|
11384
|
-
const
|
|
11385
|
-
const
|
|
11386
|
-
|
|
11387
|
-
if (left < 8) {
|
|
11781
|
+
// 뷰포트 클램핑
|
|
11782
|
+
const vw = window.innerWidth;
|
|
11783
|
+
const vh = window.innerHeight;
|
|
11784
|
+
if (left < 8)
|
|
11388
11785
|
left = 8;
|
|
11389
|
-
|
|
11390
|
-
|
|
11391
|
-
|
|
11392
|
-
else if (left + tooltipRect.width > viewportWidth - 8) {
|
|
11393
|
-
left = viewportWidth - tooltipRect.width - 8;
|
|
11394
|
-
if (isHorizontal)
|
|
11395
|
-
arrowLeft = Math.min(arrowLeft, tooltipRect.width - 4);
|
|
11396
|
-
}
|
|
11397
|
-
// 수직 경계
|
|
11398
|
-
if (top < 8) {
|
|
11786
|
+
if (left + pRect.width > vw - 8)
|
|
11787
|
+
left = vw - pRect.width - 8;
|
|
11788
|
+
if (top < 8)
|
|
11399
11789
|
top = 8;
|
|
11400
|
-
|
|
11401
|
-
|
|
11790
|
+
if (top + pRect.height > vh - 8)
|
|
11791
|
+
top = vh - pRect.height - 8;
|
|
11792
|
+
// 🔁 클램핑으로 박스 위치가 바뀌었을 수 있으니, 화살표를 박스 내부에서 다시 보정
|
|
11793
|
+
const g = groupOf(position);
|
|
11794
|
+
if (g === 'top' || g === 'bottom') {
|
|
11795
|
+
// 트리거 중앙 X 를 툴팁 좌표계로 변환
|
|
11796
|
+
const triggerCenterX = tRect.left + tRect.width / 2;
|
|
11797
|
+
const localX = triggerCenterX - left - ARW; // 화살표 기준점을 고려
|
|
11798
|
+
// 8px ~ (width-8px) 범위로 제한
|
|
11799
|
+
aLeft = Math.min(pRect.width - PAD, Math.max(PAD, localX));
|
|
11800
|
+
// aTop은 이미 위/아래에 고정(-ARW 또는 높이)
|
|
11402
11801
|
}
|
|
11403
|
-
else
|
|
11404
|
-
|
|
11405
|
-
|
|
11406
|
-
|
|
11802
|
+
else {
|
|
11803
|
+
const triggerCenterY = tRect.top + tRect.height / 2;
|
|
11804
|
+
const localY = triggerCenterY - top - ARW;
|
|
11805
|
+
aTop = Math.min(pRect.height - PAD, Math.max(PAD, localY));
|
|
11806
|
+
// aLeft는 이미 좌/우에 고정(-ARW 또는 너비)
|
|
11407
11807
|
}
|
|
11408
|
-
|
|
11808
|
+
setStyle({
|
|
11409
11809
|
position: 'fixed',
|
|
11410
|
-
top
|
|
11411
|
-
left
|
|
11412
|
-
maxWidth
|
|
11413
|
-
zIndex: 9999
|
|
11810
|
+
top,
|
|
11811
|
+
left,
|
|
11812
|
+
maxWidth,
|
|
11813
|
+
zIndex: 9999,
|
|
11814
|
+
pointerEvents: 'none', // hover 유지
|
|
11414
11815
|
});
|
|
11415
|
-
|
|
11416
|
-
|
|
11816
|
+
setArrowStyle({ position: 'absolute', top: aTop, left: aLeft });
|
|
11817
|
+
setPlacementGroup(g);
|
|
11417
11818
|
}, [position, maxWidth]);
|
|
11418
|
-
//
|
|
11419
|
-
|
|
11420
|
-
if (
|
|
11819
|
+
// 초기 페인트 전에 위치 확정
|
|
11820
|
+
React.useLayoutEffect(() => {
|
|
11821
|
+
if (visible || alwaysShow)
|
|
11822
|
+
calculatePosition();
|
|
11823
|
+
}, [visible, alwaysShow, calculatePosition]);
|
|
11824
|
+
// 스크롤/리사이즈/크기변화 추적 (rAF 스로틀)
|
|
11825
|
+
React.useEffect(() => {
|
|
11826
|
+
if (!(visible || alwaysShow))
|
|
11421
11827
|
return;
|
|
11422
|
-
|
|
11423
|
-
|
|
11424
|
-
|
|
11425
|
-
requestAnimationFrame(() => {
|
|
11828
|
+
const onMove = () => {
|
|
11829
|
+
if (rafId.current != null)
|
|
11830
|
+
cancelAnimationFrame(rafId.current);
|
|
11831
|
+
rafId.current = requestAnimationFrame(() => {
|
|
11426
11832
|
calculatePosition();
|
|
11833
|
+
rafId.current = null;
|
|
11427
11834
|
});
|
|
11428
|
-
}
|
|
11429
|
-
|
|
11430
|
-
|
|
11431
|
-
|
|
11432
|
-
|
|
11835
|
+
};
|
|
11836
|
+
window.addEventListener('scroll', onMove, { capture: true, passive: true });
|
|
11837
|
+
window.addEventListener('resize', onMove, { passive: true });
|
|
11838
|
+
let ro = null;
|
|
11839
|
+
const ResizeObs = window.ResizeObserver;
|
|
11840
|
+
if (ResizeObs) {
|
|
11841
|
+
ro = new ResizeObs(() => onMove());
|
|
11842
|
+
if (tooltipRef.current)
|
|
11843
|
+
ro.observe(tooltipRef.current);
|
|
11844
|
+
if (triggerRef.current)
|
|
11845
|
+
ro.observe(triggerRef.current);
|
|
11846
|
+
}
|
|
11847
|
+
return () => {
|
|
11848
|
+
window.removeEventListener('scroll', onMove, { capture: true });
|
|
11849
|
+
window.removeEventListener('resize', onMove);
|
|
11850
|
+
ro?.disconnect?.();
|
|
11851
|
+
if (rafId.current != null)
|
|
11852
|
+
cancelAnimationFrame(rafId.current);
|
|
11853
|
+
};
|
|
11854
|
+
}, [visible, alwaysShow, calculatePosition]);
|
|
11855
|
+
// show/hide
|
|
11856
|
+
const show = React.useCallback(() => {
|
|
11857
|
+
if (disabled)
|
|
11433
11858
|
return;
|
|
11434
11859
|
clearTimer();
|
|
11435
|
-
|
|
11436
|
-
|
|
11437
|
-
|
|
11438
|
-
|
|
11439
|
-
// 키보드 이스케이프
|
|
11440
|
-
const handleKeyDown = React.useCallback((e) => {
|
|
11441
|
-
if (e.key === 'Escape')
|
|
11442
|
-
hideTooltip();
|
|
11443
|
-
}, [hideTooltip]);
|
|
11444
|
-
// 리사이즈/스크롤 중 위치 업데이트
|
|
11445
|
-
React.useEffect(() => {
|
|
11446
|
-
if (!isVisible)
|
|
11860
|
+
timers.current.t = setTimeout(() => setVisible(true), Math.max(0, delay));
|
|
11861
|
+
}, [disabled, delay]);
|
|
11862
|
+
const hide = React.useCallback(() => {
|
|
11863
|
+
if (disabled)
|
|
11447
11864
|
return;
|
|
11448
|
-
|
|
11449
|
-
|
|
11450
|
-
|
|
11451
|
-
window.addEventListener('scroll', onScroll, true);
|
|
11452
|
-
return () => {
|
|
11453
|
-
window.removeEventListener('resize', onResize);
|
|
11454
|
-
window.removeEventListener('scroll', onScroll, true);
|
|
11455
|
-
};
|
|
11456
|
-
}, [isVisible, calculatePosition]);
|
|
11457
|
-
// 언마운트 시 타이머 정리
|
|
11865
|
+
clearTimer();
|
|
11866
|
+
timers.current.t = setTimeout(() => setVisible(false), Math.max(0, hideDelay));
|
|
11867
|
+
}, [disabled, hideDelay]);
|
|
11458
11868
|
React.useEffect(() => () => clearTimer(), []);
|
|
11459
|
-
|
|
11460
|
-
|
|
11461
|
-
|
|
11462
|
-
|
|
11463
|
-
|
|
11464
|
-
|
|
11465
|
-
|
|
11466
|
-
|
|
11869
|
+
React.useEffect(() => { if (!disabled && alwaysShow)
|
|
11870
|
+
setVisible(true);
|
|
11871
|
+
else if (!alwaysShow)
|
|
11872
|
+
setVisible(false); }, [alwaysShow, disabled]);
|
|
11873
|
+
const onKeyDown = React.useCallback((e) => {
|
|
11874
|
+
if (e.key === 'Escape') {
|
|
11875
|
+
clearTimer();
|
|
11876
|
+
setVisible(false);
|
|
11467
11877
|
}
|
|
11468
|
-
}, [
|
|
11469
|
-
|
|
11470
|
-
React.useEffect(() => {
|
|
11471
|
-
if (!isVisible)
|
|
11472
|
-
return;
|
|
11473
|
-
const ro = new ResizeObserver(() => calculatePosition());
|
|
11474
|
-
if (tooltipRef.current)
|
|
11475
|
-
ro.observe(tooltipRef.current);
|
|
11476
|
-
return () => ro.disconnect();
|
|
11477
|
-
}, [isVisible, calculatePosition, content]);
|
|
11478
|
-
const classes = clsx('designbase-tooltip', `designbase-tooltip--${size}`, `designbase-tooltip--${variant}`, `designbase-tooltip--${position}`, { 'designbase-tooltip--visible': isVisible, 'designbase-tooltip--disabled': disabled }, className);
|
|
11878
|
+
}, []);
|
|
11879
|
+
const classes = clsx('designbase-tooltip', `designbase-tooltip--${size}`, `designbase-tooltip--${variant}`, `designbase-tooltip--${position}`, { 'designbase-tooltip--visible': visible || alwaysShow, 'designbase-tooltip--disabled': disabled }, className);
|
|
11479
11880
|
const arrowClasses = clsx('designbase-tooltip__arrow', `designbase-tooltip__arrow--${position}`);
|
|
11480
|
-
return (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("span", { ref:
|
|
11881
|
+
return (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("span", { ref: triggerRef, className: "designbase-tooltip__trigger", onMouseEnter: show, onMouseLeave: hide, onFocus: show, onBlur: hide, onKeyDown: onKeyDown, tabIndex: 0, "aria-describedby": visible || alwaysShow ? 'db-tooltip' : undefined, children: children }), (visible || alwaysShow) && (jsxRuntimeExports.jsxs("div", { ref: tooltipRef, className: classes, style: style, role: "tooltip", id: "db-tooltip", "aria-hidden": !(visible || alwaysShow), "data-placement-group": placementGroup, ...props, children: [jsxRuntimeExports.jsx("div", { className: "designbase-tooltip__content", children: content }), showArrow && jsxRuntimeExports.jsx("div", { className: arrowClasses, style: arrowStyle })] }))] }));
|
|
11481
11882
|
};
|
|
11482
11883
|
Tooltip.displayName = 'Tooltip';
|
|
11483
11884
|
|
|
@@ -11969,6 +12370,7 @@ exports.Card = Card;
|
|
|
11969
12370
|
exports.Carousel = Carousel;
|
|
11970
12371
|
exports.Checkbox = Checkbox;
|
|
11971
12372
|
exports.Chip = Chip;
|
|
12373
|
+
exports.ColorPicker = ColorPicker$1;
|
|
11972
12374
|
exports.Confirm = Confirm;
|
|
11973
12375
|
exports.Container = Container;
|
|
11974
12376
|
exports.ContextMenu = ContextMenu;
|
|
@@ -11995,6 +12397,9 @@ exports.MarkdownEditor = MarkdownEditor;
|
|
|
11995
12397
|
exports.Masonry = Masonry;
|
|
11996
12398
|
exports.MenuItem = MenuItem;
|
|
11997
12399
|
exports.Modal = Modal;
|
|
12400
|
+
exports.ModalBody = ModalBody;
|
|
12401
|
+
exports.ModalFooter = ModalFooter;
|
|
12402
|
+
exports.ModalHeader = ModalHeader;
|
|
11998
12403
|
exports.Navbar = Navbar;
|
|
11999
12404
|
exports.Pagination = Pagination;
|
|
12000
12405
|
exports.Popover = Popover;
|
|
@@ -12022,7 +12427,7 @@ exports.Stepper = Stepper;
|
|
|
12022
12427
|
exports.Table = Table;
|
|
12023
12428
|
exports.Tabs = Tabs;
|
|
12024
12429
|
exports.Textarea = Textarea;
|
|
12025
|
-
exports.TimePicker = TimePicker;
|
|
12430
|
+
exports.TimePicker = TimePicker$1;
|
|
12026
12431
|
exports.Timeline = Timeline;
|
|
12027
12432
|
exports.Toast = Toast;
|
|
12028
12433
|
exports.Toggle = Toggle;
|