@designbasekorea/ui 0.1.21 → 0.1.22

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.umd.js CHANGED
@@ -1455,7 +1455,7 @@
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, color: "var(--db-icon-primary)" }) })) : null;
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 @@
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, color: "var(--db-icon-primary)" }) })] }), 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));
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 @@
3638
3638
  });
3639
3639
  Button.displayName = 'Button';
3640
3640
 
3641
- const Alert = ({ title, children, variant = 'info', size = 'm', showIcon = true, closable = false, actions, actionButtons, onClose, className, ...props }) => {
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 @@
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 || 'secondary', onClick: action.onClick, disabled: action.disabled, children: action.label }, index))) })) : (actions) }))] }));
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 @@
4492
4492
  });
4493
4493
  Backdrop.displayName = 'Backdrop';
4494
4494
 
4495
- const Banner = ({ title, description, icon, image, imageAlt, actions = [], dismissible = false, autoDismiss, size = 'm', variant = 'default', style = 'solid', position = 'top', alignment = 'left', animated = false, fullWidth = false, shadow = false, rounded = true, backgroundImage, overlayColor, overlayOpacity = 0.1, onDismiss, className, }) => {
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
- backgroundImage: `url(${backgroundImage})`,
4522
- backgroundSize: 'cover',
4523
- backgroundPosition: 'center',
4524
- backgroundRepeat: 'no-repeat'
4525
- } }));
4526
- };
4527
- const renderOverlay = () => {
4528
- if (!overlayColor && !backgroundImage)
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(), dismissible && (jsxRuntimeExports.jsx("button", { className: "designbase-banner__dismiss", onClick: handleDismiss, "aria-label": "\uB2EB\uAE30", children: jsxRuntimeExports.jsx(icons.CloseIcon, { size: iconSize }) }))] })] }));
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, backdropBlur = true, backdropOpacity = 0.5, stickyHeader = false, stickyFooter = false, className, style, header, footer, children, ...props }, forwardedRef) => {
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 @@
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 @@
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': StartIcon,
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 @@
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: [StartIcon && (jsxRuntimeExports.jsx("div", { className: "designbase-input__start-icon", children: jsxRuntimeExports.jsx(StartIcon, { size: iconSize }) })), jsxRuntimeExports.jsx("input", { ...props, ref: forwardedRef, id: inputId, type: type, 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 }), EndIcon && (jsxRuntimeExports.jsx("div", { className: "designbase-input__end-icon", children: jsxRuntimeExports.jsx(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 }))] }));
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 @@
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: "l", 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) => {
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 @@
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
- // 아이콘 크기 계산 (m이 기본값) - 카드에 맞게 조정
5687
- const iconSize = size === 's' ? 16 : size === 'l' ? 20 : size === 'xl' ? 24 : 18;
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 @@
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.length === 0)
5703
+ if (!tags?.length)
5731
5704
  return null;
5732
- return (jsxRuntimeExports.jsx("div", { className: "designbase-card__tags", children: tags.map((tag, index) => (jsxRuntimeExports.jsx("span", { className: "designbase-card__tag", children: tag }, index))) }));
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
- return (jsxRuntimeExports.jsxs("div", { className: "designbase-card__meta", children: [meta.author && (jsxRuntimeExports.jsxs("span", { className: "designbase-card__meta-item", children: [jsxRuntimeExports.jsx("span", { className: "designbase-card__meta-label", children: "\uC791\uC131\uC790:" }), jsxRuntimeExports.jsx("span", { className: "designbase-card__meta-value", children: meta.author })] })), meta.date && (jsxRuntimeExports.jsxs("span", { className: "designbase-card__meta-item", children: [jsxRuntimeExports.jsx("span", { className: "designbase-card__meta-label", children: "\uB0A0\uC9DC:" }), jsxRuntimeExports.jsx("span", { className: "designbase-card__meta-value", children: meta.date })] })), meta.views !== undefined && (jsxRuntimeExports.jsxs("span", { className: "designbase-card__meta-item", children: [jsxRuntimeExports.jsx("span", { className: "designbase-card__meta-label", children: "\uC870\uD68C\uC218:" }), jsxRuntimeExports.jsx("span", { className: "designbase-card__meta-value", children: meta.views })] })), meta.likes !== undefined && (jsxRuntimeExports.jsxs("span", { className: "designbase-card__meta-item", children: [jsxRuntimeExports.jsx("span", { className: "designbase-card__meta-label", children: "\uC88B\uC544\uC694:" }), jsxRuntimeExports.jsx("span", { className: "designbase-card__meta-value", children: meta.likes })] })), meta.comments !== undefined && (jsxRuntimeExports.jsxs("span", { className: "designbase-card__meta-item", children: [jsxRuntimeExports.jsx("span", { className: "designbase-card__meta-label", children: "\uB313\uAE00:" }), jsxRuntimeExports.jsx("span", { className: "designbase-card__meta-value", children: meta.comments })] })), meta.custom && Object.entries(meta.custom).map(([key, value]) => (jsxRuntimeExports.jsxs("span", { className: "designbase-card__meta-item", children: [jsxRuntimeExports.jsxs("span", { className: "designbase-card__meta-label", children: [key, ":"] }), jsxRuntimeExports.jsx("span", { className: "designbase-card__meta-value", children: value })] }, key)))] }));
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.length === 0)
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' && renderImage(), imagePosition === 'left' && renderImage(), renderContent(), imagePosition === 'bottom' && renderImage(), imagePosition === 'right' && renderImage()] }))] }));
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 @@
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 (!isDragging || !enableTouch || disabled || readonly || !touchStartRef.current)
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 @@
5945
5931
  goToNext();
5946
5932
  }
5947
5933
  }
5934
+ // 상태 초기화
5935
+ setIsDragging(false);
5948
5936
  setDragOffset(0);
5949
- }, [isDragging, enableTouch, disabled, readonly, dragOffset, swipeSensitivity, goToPrevious, goToNext]);
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 @@
6015
6004
  const currentItem = items[currentIndex];
6016
6005
  // 슬라이드 스타일 계산
6017
6006
  const getSlideStyle = () => {
6018
- const translateX = -(currentIndex * (100 / itemsPerView)) + (dragOffset / (containerRef.current?.offsetWidth || 1)) * (100 / itemsPerView);
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 ease`,
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 @@
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 @@
6115
6106
  };
6116
6107
  Chip.displayName = 'Chip';
6117
6108
 
6118
- const Confirm = ({ open, title, children, confirmText = '확인', cancelText = '취소', confirmVariant = 'primary', variant = 'info', size = 'm', showIcon = true, confirmDisabled = false, cancelDisabled = false, onConfirm, onCancel, onClose, closeOnEscape = true, closeOnOverlayClick = true, className, ...props }) => {
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 @@
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 = 'inline', size = 'm', style = 'dropdown', subItems, expanded = false, expandable = false, depth = 0, onClick, onChildClick, className, }) => {
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 @@
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 @@
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 [internalIsOpen, setInternalIsOpen] = React.useState(false);
6748
+ const [isOpen, setIsOpen] = React.useState(false);
6489
6749
  const [rangeStart, setRangeStart] = React.useState(null);
6490
- const containerRef = React.useRef(null);
6491
- // 드롭다운 상태 관리
6492
- const isOpen = controlledIsOpen !== undefined ? controlledIsOpen : internalIsOpen;
6493
- const setIsOpen = (open) => {
6494
- if (controlledIsOpen === undefined) {
6495
- setInternalIsOpen(open);
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
- onOpenChange?.(open);
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 (containerRef.current && !containerRef.current.contains(event.target)) {
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
- // ESC 감지
6512
- React.useEffect(() => {
6513
- if (!isOpen || inline)
6514
- return;
6515
- const handleEscape = (event) => {
6516
- if (event.key === 'Escape') {
6517
- setIsOpen(false);
6518
- }
6519
- };
6520
- document.addEventListener('keydown', handleEscape);
6521
- return () => document.removeEventListener('keydown', handleEscape);
6522
- }, [isOpen, inline]);
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 @@
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
- onChange?.(date);
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 (value && 'start' in value && !rangeStart) {
6876
+ if (currentValue && 'start' in currentValue && !rangeStart) {
6544
6877
  // 기존 범위를 해제하고 새로운 범위 시작
6545
- onChange?.(undefined);
6878
+ handleChange(undefined);
6546
6879
  setRangeStart(date);
6547
6880
  return;
6548
6881
  }
@@ -6556,29 +6889,29 @@
6556
6889
  const end = date;
6557
6890
  if (start > end) {
6558
6891
  // 시작일이 끝일보다 늦으면 순서 바꿈
6559
- onChange?.({ start: end, end: start });
6892
+ handleChange({ start: end, end: start });
6560
6893
  }
6561
6894
  else {
6562
- onChange?.({ start, end });
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(value) ? value : [];
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
- onChange?.(currentDates.filter(d => d.toISOString().split('T')[0] !== dateStr));
6907
+ handleChange(currentDates.filter(d => d.toISOString().split('T')[0] !== dateStr));
6575
6908
  }
6576
6909
  else {
6577
- onChange?.([...currentDates, date]);
6910
+ handleChange([...currentDates, date]);
6578
6911
  }
6579
6912
  break;
6580
6913
  }
6581
- }, [mode, value, onChange, disabled, readonly, isDateValid, rangeStart, inline]);
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 @@
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 @@
6604
6938
  start = rangeStart;
6605
6939
  end = hoveredDate;
6606
6940
  }
6607
- else if (value && 'start' in value) {
6941
+ else if (currentValue && 'start' in currentValue) {
6608
6942
  // 완성된 범위
6609
- start = value.start;
6610
- end = value.end;
6943
+ start = currentValue.start;
6944
+ end = currentValue.end;
6611
6945
  }
6612
6946
  if (!start)
6613
6947
  return false;
@@ -6615,28 +6949,29 @@
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, value, rangeStart, hoveredDate]);
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 value && value instanceof Date && value.toISOString().split('T')[0] === dateStr;
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 (!value || !('start' in value))
6964
+ if (!currentValue || !('start' in currentValue))
6630
6965
  return false;
6631
- const range = value;
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(value) && value.some(d => d.toISOString().split('T')[0] === dateStr);
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, value, rangeStart]);
6974
+ }, [mode, rangeStart, type, tempValue, selectedValue]);
6640
6975
  // 월 이동
6641
6976
  const goToPreviousMonth = React.useCallback(() => {
6642
6977
  setCurrentDate(prev => {
@@ -6684,22 +7019,23 @@
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
- (value && 'start' in value && value.start.toDateString() === date.toDateString())),
6701
- 'designbase-date-picker__date--range-end': mode === 'range' && value && 'end' in value &&
6702
- value.end.toDateString() === date.toDateString(),
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 @@
6711
7047
  isDateSelected,
6712
7048
  isInRange,
6713
7049
  mode,
6714
- value,
6715
7050
  rangeStart,
6716
7051
  highlightWeekends,
6717
7052
  getEventsForDate,
@@ -6721,21 +7056,29 @@
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
- if (!inline && trigger) {
6729
- return (jsxRuntimeExports.jsxs("div", { ref: containerRef, className: "designbase-date-picker-container", children: [jsxRuntimeExports.jsx("div", { onClick: () => !disabled && !readonly && setIsOpen(!isOpen), children: trigger }), isOpen && (jsxRuntimeExports.jsx("div", { className: clsx('designbase-date-picker-dropdown', `designbase-date-picker-dropdown--placement-${placement}`), children: jsxRuntimeExports.jsxs("div", { className: clsx('designbase-date-picker', `designbase-date-picker--size-${size}`, `designbase-date-picker--variant-${variant}`, {
6730
- 'designbase-date-picker--disabled': disabled,
6731
- 'designbase-date-picker--readonly': readonly,
6732
- }, className), 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)) }), 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" }) })] }) }))] }));
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
- return (jsxRuntimeExports.jsxs("div", { className: clsx('designbase-date-picker', `designbase-date-picker--size-${size}`, `designbase-date-picker--variant-${variant}`, {
6736
- 'designbase-date-picker--disabled': disabled,
6737
- 'designbase-date-picker--readonly': readonly,
6738
- }, className), 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)) }), 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" }) })] }));
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 @@
7091
7434
  const getDefaultIcon = () => {
7092
7435
  const defaultIconSize = iconSize || (() => {
7093
7436
  switch (size) {
7094
- case 'xs': return 32;
7095
- case 's': return 48;
7096
- case 'm': return 64;
7097
- case 'l': return 96;
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 @@
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 @@
7244
7586
  const getStatusIcon = React.useCallback((status) => {
7245
7587
  switch (status) {
7246
7588
  case 'pending':
7247
- return (jsxRuntimeExports.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: jsxRuntimeExports.jsx("circle", { cx: "8", cy: "8", r: "7", stroke: "currentColor", strokeWidth: "2", fill: "none" }) }));
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 (jsxRuntimeExports.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: jsxRuntimeExports.jsx("path", { d: "M13 5L6 12L3 9", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }));
7593
+ return jsxRuntimeExports.jsx(icons.DoneIcon, { size: 20 });
7252
7594
  case 'error':
7253
- return (jsxRuntimeExports.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [jsxRuntimeExports.jsx("path", { d: "M8 1L15 8L8 15L1 8L8 1Z", stroke: "currentColor", strokeWidth: "2" }), jsxRuntimeExports.jsx("path", { d: "M8 5V9", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }), jsxRuntimeExports.jsx("circle", { cx: "8", cy: "12", r: "1", fill: "currentColor" })] }));
7595
+ return jsxRuntimeExports.jsx(icons.ErrorIcon, { size: 20 });
7254
7596
  default:
7255
7597
  return null;
7256
7598
  }
@@ -7261,11 +7603,12 @@
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 @@
7337
7680
  });
7338
7681
  Textarea.displayName = 'Textarea';
7339
7682
 
7340
- const Form = ({ fields, layout = 'vertical', size = 'm', variant = 'default', onSubmit, onChange, initialValues = {}, submitText = '제출', submitLoading = false, submitDisabled = false, showReset = false, resetText = '리셋', title, description, className, style, }) => {
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 @@
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("input", { type: "file", id: field.name, name: field.name, accept: field.fileUpload?.accept, multiple: field.fileUpload?.multiple, disabled: field.disabled, required: field.required, onChange: (e) => {
7501
- const files = e.target.files;
7502
- if (files) {
7503
- const fileArray = Array.from(files);
7504
- handleFieldChange(field.name, field.fileUpload?.multiple ? fileArray : fileArray[0]);
7505
- }
7506
- }, onBlur: () => handleFieldBlur(field.name), className: clsx('designbase-form__file-input', {
7507
- 'designbase-form__input--error': touched[field.name] && errors[field.name],
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--${layout}`, `designbase-form--${size}`, `designbase-form--${variant}`, className);
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 @@
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 !== undefined && (jsxRuntimeExports.jsx("div", { className: "designbase-stat__progress", children: jsxRuntimeExports.jsx("div", { className: "designbase-stat__progress-bar", style: { width: `${Math.min(Math.max(progress, 0), 100)}%` } }) }))] }));
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 @@
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', variant = 'default', theme = 'light', 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, readonly = false, disabled = false, className, }) => {
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 @@
7789
8376
  }, [onOpenChange]);
7790
8377
  // 줌 인
7791
8378
  const handleZoomIn = React.useCallback(() => {
7792
- if (!enableZoom || disabled || readonly)
8379
+ if (!enableZoom)
7793
8380
  return;
7794
8381
  setZoomLevel(prev => Math.min(prev * 1.5, 5));
7795
- }, [enableZoom, disabled, readonly]);
8382
+ }, [enableZoom]);
7796
8383
  // 줌 아웃
7797
8384
  const handleZoomOut = React.useCallback(() => {
7798
- if (!enableZoom || disabled || readonly)
8385
+ if (!enableZoom)
7799
8386
  return;
7800
8387
  setZoomLevel(prev => Math.max(prev / 1.5, 0.1));
7801
- }, [enableZoom, disabled, readonly]);
8388
+ }, [enableZoom]);
7802
8389
  // 줌 리셋
7803
8390
  const handleZoomReset = React.useCallback(() => {
7804
- if (!enableZoom || disabled || readonly)
8391
+ if (!enableZoom)
7805
8392
  return;
7806
8393
  setZoomLevel(1);
7807
8394
  setImagePosition({ x: 0, y: 0 });
7808
- }, [enableZoom, disabled, readonly]);
8395
+ }, [enableZoom]);
7809
8396
  // 회전
7810
8397
  const handleRotate = React.useCallback(() => {
7811
- if (!enableRotate || disabled || readonly)
8398
+ if (!enableRotate)
7812
8399
  return;
7813
8400
  setRotation(prev => (prev + 90) % 360);
7814
- }, [enableRotate, disabled, readonly]);
8401
+ }, [enableRotate]);
7815
8402
  // 다운로드
7816
8403
  const handleDownload = React.useCallback(() => {
7817
- if (!enableDownload || disabled || readonly)
8404
+ if (!enableDownload)
7818
8405
  return;
7819
8406
  const currentImage = images[internalCurrentIndex];
7820
8407
  if (currentImage) {
@@ -7851,13 +8438,13 @@
7851
8438
  document.body.removeChild(link);
7852
8439
  });
7853
8440
  }
7854
- }, [enableDownload, disabled, readonly, images, internalCurrentIndex]);
8441
+ }, [enableDownload, images, internalCurrentIndex]);
7855
8442
  // 전체화면 토글
7856
8443
  const handleFullscreenToggle = React.useCallback(() => {
7857
- if (!enableFullscreen || disabled || readonly)
8444
+ if (!enableFullscreen)
7858
8445
  return;
7859
8446
  setIsFullscreen(prev => !prev);
7860
- }, [enableFullscreen, disabled, readonly]);
8447
+ }, [enableFullscreen]);
7861
8448
  // 이미지 로드 핸들러
7862
8449
  const handleImageLoad = React.useCallback(() => {
7863
8450
  setIsImageLoaded(true);
@@ -7881,7 +8468,7 @@
7881
8468
  }, [isOpen, internalCurrentIndex, images]);
7882
8469
  // 마우스 휠 줌 핸들러
7883
8470
  const handleWheel = React.useCallback((event) => {
7884
- if (!enableWheelZoom || disabled || readonly)
8471
+ if (!enableWheelZoom)
7885
8472
  return;
7886
8473
  event.preventDefault();
7887
8474
  if (event.deltaY < 0) {
@@ -7890,44 +8477,40 @@
7890
8477
  else {
7891
8478
  handleZoomOut();
7892
8479
  }
7893
- }, [enableWheelZoom, disabled, readonly, handleZoomIn, handleZoomOut]);
8480
+ }, [enableWheelZoom, handleZoomIn, handleZoomOut]);
7894
8481
  // 드래그 시작 핸들러
7895
8482
  const handleMouseDown = React.useCallback((event) => {
7896
- if (zoomLevel <= 1 || disabled || readonly)
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, disabled, readonly, imagePosition]);
8488
+ }, [zoomLevel, imagePosition]);
7902
8489
  // 드래그 중 핸들러
7903
8490
  const handleMouseMove = React.useCallback((event) => {
7904
- if (!isDragging || zoomLevel <= 1 || disabled || readonly)
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, disabled, readonly, dragStart]);
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
- }, [disabled, readonly]);
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
- }, [disabled, readonly]);
8513
+ }, []);
7931
8514
  // 스와이프 처리
7932
8515
  const handleSwipe = React.useCallback(() => {
7933
8516
  const deltaX = touchStart.x - touchEnd.x;
@@ -7947,7 +8530,7 @@
7947
8530
  }, [touchStart, touchEnd, goToNext, goToPrevious]);
7948
8531
  // 키보드 이벤트 핸들러
7949
8532
  React.useEffect(() => {
7950
- if (!enableKeyboard || !isOpen || disabled || readonly)
8533
+ if (!enableKeyboard || !isOpen)
7951
8534
  return;
7952
8535
  const handleKeyDown = (event) => {
7953
8536
  switch (event.key) {
@@ -7988,8 +8571,6 @@
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 @@
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}`, `designbase-lightbox--variant-${variant}`, `designbase-lightbox--theme-${theme}`, {
8607
+ return (jsxRuntimeExports.jsx("div", { className: clsx('designbase-lightbox', `designbase-lightbox--size-${size}`, {
8027
8608
  'designbase-lightbox--fullscreen': isFullscreen,
8028
- 'designbase-lightbox--disabled': disabled,
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 @@
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), disabled: disabled || readonly, 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))) }) }))] }) }) }));
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 = 'md', ratio = '16:9', fit = 'cover', rounded = false, shadow = false, hover = true, clickable = true, lightbox = true, onImageClick, onImageLoad, onImageError, loading = 'lazy', responsive = true, className, style, }) => {
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 @@
8136
8715
  const renderBadge = (badge) => {
8137
8716
  if (!badge)
8138
8717
  return null;
8139
- const badgeClasses = clsx('designbase-list__item-badge', `designbase-list__item-badge--color-${badge.color || 'primary'}`, `designbase-list__item-badge--variant-${badge.variant || 'solid'}`);
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 @@
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 = 'md', 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, }) => {
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 @@
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 === 'sm' ? 8 : spacing === 'md' ? 16 : spacing === 'lg' ? 24 : 32);
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 @@
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, onAuthClick, isAuthenticated = false, showSearch = false, onSearch, searchPlaceholder = '검색...', fullWidth = false, shadow = false, className, ...props }) => {
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 @@
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 @@
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, depth = 0) => {
9387
+ // 메뉴 아이템을 렌더링하는 함수
9388
+ const renderMenuItem = (item, isMobile = false) => {
8772
9389
  const hasChildren = item.children && item.children.length > 0;
8773
- const isFirstLevel = depth === 0;
8774
- const isSecondLevel = depth === 1;
8775
- if (isFirstLevel) {
8776
- // 1depth - 메인 네비게이션
8777
- 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', {
8778
- 'designbase-navbar__nav-link--active': item.active,
8779
- 'designbase-navbar__nav-link--disabled': item.disabled,
8780
- }), onClick: () => handleItemClick(item), disabled: item.disabled, children: [item.icon && React.createElement(item.icon, { size: iconSize, color: 'currentColor' }), item.label, jsxRuntimeExports.jsx(icons.ChevronDownIcon, { size: 12, className: "designbase-navbar__chevron" })] }), jsxRuntimeExports.jsx("ul", { className: "designbase-navbar__dropdown-menu", children: item.children.map((child) => renderMenuItem(child, 1)) })] })) : (jsxRuntimeExports.jsxs("a", { href: item.href, className: clsx('designbase-navbar__nav-link', {
8781
- 'designbase-navbar__nav-link--active': item.active,
8782
- 'designbase-navbar__nav-link--disabled': item.disabled,
8783
- }), onClick: (e) => {
8784
- if (item.disabled) {
8785
- e.preventDefault();
8786
- return;
8787
- }
8788
- handleItemClick(item);
8789
- }, children: [item.icon && React.createElement(item.icon, { size: iconSize, color: 'currentColor' }), item.label] })) }, item.id));
8790
- }
8791
- else if (isSecondLevel) {
8792
- // 2depth - 드롭다운 메뉴
8793
- return (jsxRuntimeExports.jsx("li", { children: hasChildren ? (jsxRuntimeExports.jsxs("div", { className: "designbase-navbar__dropdown-item designbase-navbar__dropdown-item--has-children", children: [jsxRuntimeExports.jsxs("a", { href: item.href, className: clsx('designbase-navbar__dropdown-item', {
8794
- 'designbase-navbar__dropdown-item--active': item.active,
8795
- 'designbase-navbar__dropdown-item--disabled': item.disabled,
8796
- }), onClick: (e) => {
8797
- if (item.disabled) {
8798
- e.preventDefault();
8799
- return;
8800
- }
8801
- handleItemClick(item);
8802
- }, children: [item.icon && React.createElement(item.icon, { size: iconSize, color: 'currentColor' }), item.label, jsxRuntimeExports.jsx(icons.ChevronRightIcon, { size: 12, className: "designbase-navbar__chevron" })] }), jsxRuntimeExports.jsx("ul", { className: "designbase-navbar__submenu", children: item.children.map((child) => renderMenuItem(child, 2)) })] })) : (jsxRuntimeExports.jsxs("a", { href: item.href, className: clsx('designbase-navbar__dropdown-item', {
8803
- 'designbase-navbar__dropdown-item--active': item.active,
8804
- 'designbase-navbar__dropdown-item--disabled': item.disabled,
8805
- }), onClick: (e) => {
8806
- if (item.disabled) {
8807
- e.preventDefault();
8808
- return;
8809
- }
8810
- handleItemClick(item);
8811
- }, children: [item.icon && React.createElement(item.icon, { size: iconSize, color: 'currentColor' }), item.label] })) }, item.id));
8812
- }
8813
- else {
8814
- // 3depth 이상 - 서브메뉴
8815
- return (jsxRuntimeExports.jsx("li", { children: jsxRuntimeExports.jsxs("a", { href: item.href, className: clsx('designbase-navbar__dropdown-item', {
8816
- 'designbase-navbar__dropdown-item--active': item.active,
8817
- 'designbase-navbar__dropdown-item--disabled': item.disabled,
8818
- }), onClick: (e) => {
8819
- if (item.disabled) {
8820
- e.preventDefault();
8821
- return;
8822
- }
8823
- handleItemClick(item);
8824
- }, children: [item.icon && React.createElement(item.icon, { size: iconSize, color: 'currentColor' }), item.label] }) }, item.id));
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 (child.disabled) {
9445
+ if (item.disabled) {
8850
9446
  e.preventDefault();
8851
9447
  return;
8852
9448
  }
8853
- handleItemClick(child);
8854
- }, children: [child.icon && React.createElement(child.icon, { size: iconSize, color: 'currentColor' }), child.label] }) }, child.id))) }))] }, item.id))) }), isAuthenticated && userProfile && (jsxRuntimeExports.jsxs("div", { className: "designbase-navbar__mobile-user", 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', {
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 @@
8941
9529
  };
8942
9530
  Pagination.displayName = 'Pagination';
8943
9531
 
8944
- const Popover = ({ content, children, position = 'top', size = 'm', variant = 'default', trigger = 'click', delay = 200, hideDelay = 0, alwaysShow = false, disabled = false, maxWidth = 300, showArrow = true, closeOnOutsideClick = true, closeOnEscape = true, open: controlledOpen, onOpenChange, showCloseButton = true, className, ...props }) => {
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 [popoverStyle, setPopoverStyle] = React.useState({});
8947
- const [arrowStyle, setArrowStyle] = React.useState({});
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 timeoutRef = React.useRef();
8951
- // 제어/비제어 컴포넌트 처리
8952
- const isControlled = controlledOpen !== undefined;
8953
- const isOpen = isControlled ? controlledOpen : internalOpen;
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 triggerRect = triggerRef.current.getBoundingClientRect();
8959
- const popoverRect = popoverRef.current.getBoundingClientRect();
8960
- const scrollX = window.pageXOffset || document.documentElement.scrollLeft;
8961
- const scrollY = window.pageYOffset || document.documentElement.scrollTop;
8962
- let top = 0;
8963
- let left = 0;
8964
- let arrowTop = 0;
8965
- let arrowLeft = 0;
8966
- const isVertical = position.startsWith('top') || position.startsWith('bottom');
8967
- const isHorizontal = position.startsWith('left') || position.startsWith('right');
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 = triggerRect.top + scrollY - popoverRect.height - 8;
8971
- left = triggerRect.left + scrollX + (triggerRect.width / 2) - (popoverRect.width / 2);
8972
- arrowTop = popoverRect.height;
8973
- arrowLeft = popoverRect.width / 2 - 4;
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 = triggerRect.top + scrollY - popoverRect.height - 8;
8977
- left = triggerRect.left + scrollX;
8978
- arrowTop = popoverRect.height;
8979
- arrowLeft = 12;
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 = triggerRect.top + scrollY - popoverRect.height - 8;
8983
- left = triggerRect.right + scrollX - popoverRect.width;
8984
- arrowTop = popoverRect.height;
8985
- arrowLeft = popoverRect.width - 12;
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 = triggerRect.bottom + scrollY + 8;
8989
- left = triggerRect.left + scrollX + (triggerRect.width / 2) - (popoverRect.width / 2);
8990
- arrowTop = -4;
8991
- arrowLeft = popoverRect.width / 2 - 4;
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 = triggerRect.bottom + scrollY + 8;
8995
- left = triggerRect.left + scrollX;
8996
- arrowTop = -4;
8997
- arrowLeft = 12;
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 = triggerRect.bottom + scrollY + 8;
9001
- left = triggerRect.right + scrollX - popoverRect.width;
9002
- arrowTop = -4;
9003
- arrowLeft = popoverRect.width - 12;
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 = triggerRect.top + scrollY + (triggerRect.height / 2) - (popoverRect.height / 2);
9007
- left = triggerRect.left + scrollX - popoverRect.width - 8;
9008
- arrowTop = popoverRect.height / 2 - 4;
9009
- arrowLeft = popoverRect.width;
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 = triggerRect.top + scrollY;
9013
- left = triggerRect.left + scrollX - popoverRect.width - 8;
9014
- arrowTop = 12;
9015
- arrowLeft = popoverRect.width;
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 = triggerRect.bottom + scrollY - popoverRect.height;
9019
- left = triggerRect.left + scrollX - popoverRect.width - 8;
9020
- arrowTop = popoverRect.height - 12;
9021
- arrowLeft = popoverRect.width;
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 = triggerRect.top + scrollY + (triggerRect.height / 2) - (popoverRect.height / 2);
9025
- left = triggerRect.right + scrollX + 8;
9026
- arrowTop = popoverRect.height / 2 - 4;
9027
- arrowLeft = -4;
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 = triggerRect.top + scrollY;
9031
- left = triggerRect.right + scrollX + 8;
9032
- arrowTop = 12;
9033
- arrowLeft = -4;
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 = triggerRect.bottom + scrollY - popoverRect.height;
9037
- left = triggerRect.right + scrollX + 8;
9038
- arrowTop = popoverRect.height - 12;
9039
- arrowLeft = -4;
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 viewportWidth = window.innerWidth;
9044
- const viewportHeight = window.innerHeight;
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
- if (isHorizontal) {
9049
- arrowLeft = Math.max(arrowLeft, 4);
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
- if (isVertical) {
9062
- arrowTop = Math.max(arrowTop, 4);
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: `${top}px`,
9074
- left: `${left}px`,
9075
- maxWidth: `${maxWidth}px`,
9650
+ top,
9651
+ left,
9652
+ maxWidth,
9076
9653
  zIndex: 9999,
9077
9654
  });
9078
- setArrowStyle({
9079
- position: 'absolute',
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
- const openPopover = React.useCallback(() => {
9086
- if (disabled || alwaysShow)
9087
- return;
9088
- if (timeoutRef.current) {
9089
- clearTimeout(timeoutRef.current);
9658
+ // 페인트 전에 위치를 확정 (깜빡임/점프 제거)
9659
+ React.useLayoutEffect(() => {
9660
+ if (isOpen) {
9661
+ calculatePosition();
9090
9662
  }
9091
- timeoutRef.current = setTimeout(() => {
9092
- const newOpen = true;
9093
- if (!isControlled) {
9094
- setInternalOpen(newOpen);
9095
- }
9096
- onOpenChange?.(newOpen);
9097
- // 위치 계산을 위해 다음 프레임에서 실행
9098
- requestAnimationFrame(() => {
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
- }, trigger === 'hover' ? delay : 0);
9102
- }, [disabled, alwaysShow, isControlled, onOpenChange, calculatePosition, trigger, delay]);
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 (timeoutRef.current) {
9108
- clearTimeout(timeoutRef.current);
9109
- }
9110
- timeoutRef.current = setTimeout(() => {
9111
- const newOpen = false;
9112
- if (!isControlled) {
9113
- setInternalOpen(newOpen);
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 === 'click') {
9121
- e.preventDefault();
9122
- e.stopPropagation();
9123
- if (isOpen) {
9124
- closePopover();
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
- const handleResize = () => calculatePosition();
9184
- const handleScroll = () => calculatePosition();
9185
- window.addEventListener('resize', handleResize);
9186
- window.addEventListener('scroll', handleScroll, true);
9187
- return () => {
9188
- window.removeEventListener('resize', handleResize);
9189
- window.removeEventListener('scroll', handleScroll, true);
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 (alwaysShow && !disabled) {
9204
- const newOpen = true;
9205
- if (!isControlled) {
9206
- setInternalOpen(newOpen);
9207
- }
9208
- onOpenChange?.(newOpen);
9209
- requestAnimationFrame(() => {
9210
- calculatePosition();
9211
- });
9212
- }
9213
- else if (!alwaysShow) {
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: popoverStyle, role: "dialog", "aria-modal": "false", ...props, children: [jsxRuntimeExports.jsxs("div", { className: "designbase-popover__content", children: [showCloseButton && (jsxRuntimeExports.jsx("button", { className: "designbase-popover__close-button", onClick: handleCloseClick, "aria-label": "\uD31D\uC624\uBC84 \uB2EB\uAE30", type: "button" })), content] }), showArrow && (jsxRuntimeExports.jsx("div", { className: arrowClasses, style: arrowStyle }))] }))] }));
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 @@
9258
9813
  return (jsxRuntimeExports.jsx("div", { className: classes, children: type === 'linear' ? renderLinearProgress() : renderCircularProgress() }));
9259
9814
  };
9260
9815
 
9261
- const ProgressStep = ({ items, variant = 'default', size = 'm', layout = 'vertical', currentStep = 0, clickable = false, fullWidth = false, disabled = false, className, style, onStepClick, }) => {
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 @@
9274
9829
  return 'active';
9275
9830
  return 'pending';
9276
9831
  };
9277
- const classes = clsx('designbase-progress-step', `designbase-progress-step--${variant}`, `designbase-progress-step--${size}`, `designbase-progress-step--${layout}`, {
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 @@
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', 'twitter', 'linkedin', 'whatsapp', 'email', 'link'], customPlatforms = {}, buttonText = '공유', buttonIcon = icons.ShareAltIcon, modalTitle = '공유하기', copySuccessMessage = '링크가 클립보드에 복사되었습니다!', showQrCode = true, qrCodeSize = 200, className, style, onShare, onShareError, onCopySuccess, onCopyError, }) => {
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 @@
10161
10716
  const defaultPlatforms = {
10162
10717
  facebook: {
10163
10718
  name: 'Facebook',
10164
- icon: icons.ShareAltIcon,
10719
+ icon: icons.FacebookIcon,
10165
10720
  color: '#1877f2',
10166
10721
  shareUrl: 'https://www.facebook.com/sharer/sharer.php?u={url}',
10167
10722
  },
10168
- twitter: {
10169
- name: 'Twitter',
10170
- icon: icons.ShareAltIcon,
10171
- color: '#1da1f2',
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.ShareAltIcon,
10731
+ icon: icons.InstagramIcon,
10177
10732
  color: '#e4405f',
10178
10733
  shareUrl: 'https://www.instagram.com/',
10179
10734
  customShare: () => {
@@ -10183,25 +10738,25 @@
10183
10738
  },
10184
10739
  linkedin: {
10185
10740
  name: 'LinkedIn',
10186
- icon: icons.ShareAltIcon,
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.ShareAltIcon,
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.ShareAltIcon,
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.ShareAltIcon,
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 @@
10220
10775
  },
10221
10776
  qr: {
10222
10777
  name: 'QR 코드',
10223
- icon: icons.ShareAltIcon,
10778
+ icon: icons.ScanQrcodeIcon,
10224
10779
  color: '#6c757d',
10225
10780
  shareUrl: '',
10226
10781
  customShare: () => handleShowQrCode(),
@@ -10296,7 +10851,7 @@
10296
10851
  // 공유 버튼 렌더링
10297
10852
  const renderShareButton = () => {
10298
10853
  const ButtonIcon = buttonIcon;
10299
- return (jsxRuntimeExports.jsx(Button, { variant: "secondary", size: size, onClick: () => setIsOpen(!isOpen), className: "designbase-share__trigger", icon: ButtonIcon, children: buttonText }));
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 @@
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("input", { type: "text", value: url, readOnly: true, className: "designbase-share__link-input" }), jsxRuntimeExports.jsx(Button, { size: "s", variant: "primary", onClick: handleCopyLink, className: "designbase-share__copy-button", icon: isCopied ? icons.CopyIcon : icons.LinkIcon, children: isCopied ? '복사됨!' : '복사' })] })] })] }));
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 @@
10381
10936
  e.preventDefault();
10382
10937
  onLogoClick();
10383
10938
  }
10384
- }, children: logo || jsxRuntimeExports.jsx(Logo, { size: size === 's' ? 's' : size === 'l' ? 'l' : 'm' }) }), 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', {
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.jsxs("a", { href: item.href, className: clsx('designbase-sidebar__user-menu-link', {
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 @@
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 @@
10643
11194
  const handleSort = (column) => {
10644
11195
  if (!sortable || !column.sortable)
10645
11196
  return;
10646
- const newDirection = sortColumn === column.key && sortDirection === 'asc' ? 'desc' : 'asc';
10647
- onSortChange?.(column.key, newDirection);
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 @@
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 (!sortColumn || !sortDirection)
11244
+ if (!effectiveSortColumn || !effectiveSortDirection)
10673
11245
  return data;
10674
- const column = columns.find(col => col.key === sortColumn);
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 sortDirection === 'asc' ? -1 : 1;
11253
+ return effectiveSortDirection === 'asc' ? -1 : 1;
10682
11254
  if (aValue > bValue)
10683
- return sortDirection === 'asc' ? 1 : -1;
11255
+ return effectiveSortDirection === 'asc' ? 1 : -1;
10684
11256
  return 0;
10685
11257
  });
10686
- }, [data, sortColumn, sortDirection, columns]);
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 @@
10698
11278
  const renderSortIcon = (column) => {
10699
11279
  if (!sortable || !column.sortable)
10700
11280
  return null;
10701
- const isSorted = sortColumn === column.key;
10702
- const isAsc = isSorted && sortDirection === 'asc';
10703
- const isDesc = isSorted && sortDirection === 'desc';
10704
- return (jsxRuntimeExports.jsx("span", { className: "designbase-table__sort-icon", children: jsxRuntimeExports.jsx(icons.ChevronsUpIcon, { size: 16, className: clsx({
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 @@
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: sortedData.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("div", { className: "designbase-table__empty", children: jsxRuntimeExports.jsx("span", { children: emptyMessage }) }) }) })) : (sortedData.map((item, index) => {
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 @@
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 @@
10854
11434
  };
10855
11435
  Tabs.displayName = 'Tabs';
10856
11436
 
10857
- const TimePicker = ({ value, onChange, format = '24h', mode = 'dropdown', hourStep = 1, minuteStep = 15, minTime, maxTime, size = 'm', variant = 'default', disabled = false, readonly = false, placeholder = '시간을 선택하세요', displayFormat, className, }) => {
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 @@
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 @@
11227
11632
  const renderItem = (item) => {
11228
11633
  switch (item.type) {
11229
11634
  case 'button':
11230
- return (jsxRuntimeExports.jsxs("button", { className: clsx('designbase-toolbar__item', 'designbase-toolbar__item--button', {
11231
- 'designbase-toolbar__item--active': item.active,
11232
- 'designbase-toolbar__item--disabled': item.disabled,
11233
- }), onClick: () => handleItemClick(item), disabled: item.disabled, title: item.tooltip, children: [item.icon && (jsxRuntimeExports.jsx("span", { className: "designbase-toolbar__item-icon", children: item.icon })), item.label && (jsxRuntimeExports.jsx("span", { className: "designbase-toolbar__item-label", children: item.label }))] }, item.id));
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 @@
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 Tooltip = ({ content, children, position = 'top', size = 'm', variant = 'default', delay = 200, hideDelay = 0, alwaysShow = false, disabled = false, maxWidth = 200, showArrow = true, className, ...props }) => {
11269
- const [isVisible, setIsVisible] = React.useState(false);
11270
- const [tooltipStyle, setTooltipStyle] = React.useState({});
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
- // 🔑 DOM 래퍼(ref/이벤트가 항상 붙도록)
11273
- const triggerWrapperRef = React.useRef(null);
11684
+ const [placementGroup, setPlacementGroup] = React.useState('top');
11685
+ const triggerRef = React.useRef(null);
11274
11686
  const tooltipRef = React.useRef(null);
11275
- // 브라우저 setTimeout은 number 반환
11276
- const timeoutRef = React.useRef(null);
11687
+ const timers = React.useRef({});
11688
+ const rafId = React.useRef(null);
11277
11689
  const clearTimer = () => {
11278
- if (timeoutRef.current !== null) {
11279
- window.clearTimeout(timeoutRef.current);
11280
- timeoutRef.current = null;
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 (!triggerWrapperRef.current || !tooltipRef.current)
11700
+ if (!triggerRef.current || !tooltipRef.current)
11286
11701
  return;
11287
- const triggerRect = triggerWrapperRef.current.getBoundingClientRect();
11288
- const tooltipRect = tooltipRef.current.getBoundingClientRect();
11289
- const scrollX = window.pageXOffset || document.documentElement.scrollLeft;
11290
- const scrollY = window.pageYOffset || document.documentElement.scrollTop;
11291
- let top = 0;
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 = triggerRect.top + scrollY - tooltipRect.height - 8;
11300
- left = triggerRect.left + scrollX + triggerRect.width / 2 - tooltipRect.width / 2;
11301
- // CSS에서 화살표는 자동으로 중앙에 위치하므로 스타일 제거
11302
- arrowTop = 0;
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 = triggerRect.top + scrollY - tooltipRect.height - 8;
11307
- left = triggerRect.left + scrollX;
11308
- // CSS에서 화살표는 자동으로 위치하므로 스타일 제거
11309
- arrowTop = 0;
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 = triggerRect.top + scrollY - tooltipRect.height - 8;
11314
- left = triggerRect.right + scrollX - tooltipRect.width;
11315
- // CSS에서 화살표는 자동으로 위치하므로 스타일 제거
11316
- arrowTop = 0;
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 = triggerRect.bottom + scrollY + 8;
11321
- left = triggerRect.left + scrollX + triggerRect.width / 2 - tooltipRect.width / 2;
11322
- // CSS에서 화살표는 자동으로 중앙에 위치하므로 스타일 제거
11323
- arrowTop = 0;
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 = triggerRect.bottom + scrollY + 8;
11328
- left = triggerRect.left + scrollX;
11329
- // CSS에서 화살표는 자동으로 위치하므로 스타일 제거
11330
- arrowTop = 0;
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 = triggerRect.bottom + scrollY + 8;
11335
- left = triggerRect.right + scrollX - tooltipRect.width;
11336
- // CSS에서 화살표는 자동으로 위치하므로 스타일 제거
11337
- arrowTop = 0;
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 = triggerRect.top + scrollY + triggerRect.height / 2 - tooltipRect.height / 2;
11342
- left = triggerRect.left + scrollX - tooltipRect.width - 8;
11343
- // CSS에서 화살표는 자동으로 중앙에 위치하므로 스타일 제거
11344
- arrowTop = 0;
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 = triggerRect.top + scrollY;
11349
- left = triggerRect.left + scrollX - tooltipRect.width - 8;
11350
- // CSS에서 화살표는 자동으로 위치하므로 스타일 제거
11351
- arrowTop = 0;
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 = triggerRect.bottom + scrollY - tooltipRect.height;
11356
- left = triggerRect.left + scrollX - tooltipRect.width - 8;
11357
- // CSS에서 화살표는 자동으로 위치하므로 스타일 제거
11358
- arrowTop = 0;
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 = triggerRect.top + scrollY + triggerRect.height / 2 - tooltipRect.height / 2;
11363
- left = triggerRect.right + scrollX + 8;
11364
- // CSS에서 화살표는 자동으로 중앙에 위치하므로 스타일 제거
11365
- arrowTop = 0;
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 = triggerRect.top + scrollY;
11370
- left = triggerRect.right + scrollX + 8;
11371
- // CSS에서 화살표는 자동으로 위치하므로 스타일 제거
11372
- arrowTop = 0;
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 = triggerRect.bottom + scrollY - tooltipRect.height;
11377
- left = triggerRect.right + scrollX + 8;
11378
- // CSS에서 화살표는 자동으로 위치하므로 스타일 제거
11379
- arrowTop = 0;
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 viewportWidth = window.innerWidth;
11385
- const viewportHeight = window.innerHeight;
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
- if (isHorizontal)
11390
- arrowLeft = Math.max(arrowLeft, 4);
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
- if (isVertical)
11401
- arrowTop = Math.max(arrowTop, 4);
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 if (top + tooltipRect.height > viewportHeight - 8) {
11404
- top = viewportHeight - tooltipRect.height - 8;
11405
- if (isVertical)
11406
- arrowTop = Math.min(arrowTop, tooltipRect.height - 4);
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
- setTooltipStyle({
11808
+ setStyle({
11409
11809
  position: 'fixed',
11410
- top: `${top}px`,
11411
- left: `${left}px`,
11412
- maxWidth: `${maxWidth}px`,
11413
- zIndex: 9999
11810
+ top,
11811
+ left,
11812
+ maxWidth,
11813
+ zIndex: 9999,
11814
+ pointerEvents: 'none', // hover 유지
11414
11815
  });
11415
- // CSS에서 화살표 위치를 자동으로 처리하므로 인라인 스타일 제거
11416
- setArrowStyle({});
11816
+ setArrowStyle({ position: 'absolute', top: aTop, left: aLeft });
11817
+ setPlacementGroup(g);
11417
11818
  }, [position, maxWidth]);
11418
- // 툴팁 표시
11419
- const showTooltip = React.useCallback(() => {
11420
- if (disabled || alwaysShow)
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
- clearTimer();
11423
- timeoutRef.current = window.setTimeout(() => {
11424
- setIsVisible(true);
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
- }, delay);
11429
- }, [disabled, alwaysShow, delay, calculatePosition]);
11430
- // 툴팁 숨김
11431
- const hideTooltip = React.useCallback(() => {
11432
- if (disabled || alwaysShow)
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
- timeoutRef.current = window.setTimeout(() => {
11436
- setIsVisible(false);
11437
- }, hideDelay);
11438
- }, [disabled, alwaysShow, hideDelay]);
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
- const onResize = () => calculatePosition();
11449
- const onScroll = () => calculatePosition();
11450
- window.addEventListener('resize', onResize);
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
- React.useEffect(() => {
11461
- if (alwaysShow && !disabled) {
11462
- setIsVisible(true);
11463
- requestAnimationFrame(() => calculatePosition());
11464
- }
11465
- else if (!alwaysShow) {
11466
- setIsVisible(false);
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
- }, [alwaysShow, disabled, calculatePosition]);
11469
- // content가 동적으로 바뀌어도 위치 갱신
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: triggerWrapperRef, className: "designbase-tooltip__trigger", onMouseEnter: showTooltip, onMouseLeave: hideTooltip, onFocus: showTooltip, onBlur: hideTooltip, onKeyDown: handleKeyDown, tabIndex: 0, "aria-describedby": isVisible ? 'designbase-tooltip' : undefined, children: children }), isVisible && (jsxRuntimeExports.jsxs("div", { ref: tooltipRef, className: classes, style: tooltipStyle, role: "tooltip", id: "designbase-tooltip", "aria-hidden": !isVisible, ...props, children: [jsxRuntimeExports.jsx("div", { className: "designbase-tooltip__content", children: content }), showArrow && jsxRuntimeExports.jsx("div", { className: arrowClasses, style: arrowStyle })] }))] }));
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 @@
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 @@
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 @@
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;