@designbasekorea/ui 0.1.4 → 0.1.7

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.esm.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import React, { useRef, useCallback, useEffect, useMemo, useState, useContext, forwardRef, useId, createContext } from 'react';
2
2
  import { flushSync, createPortal } from 'react-dom';
3
- import { CloseIcon, ChevronUpIcon, ChevronDownIcon, SearchIcon, UserIcon, ArrowBarLeftIcon, ChevronLeftIcon, ChevronRightIcon, ArrowBarRightIcon, ChevronsUpIcon, InfoFilledIcon, ErrorFilledIcon, WarningFilledIcon, CircleCheckFilledIcon, BulbIcon, CloudCloseIcon, BellActiveIcon, AwardIcon, GalleryIcon, ShareAltIcon, MailIcon, LinkIcon, CopyIcon, MoveIcon, MoreVerticalIcon, StarFilledIcon, StarHalfIcon, StarIcon, SettingsIcon, PauseIcon, PlayIcon, MuteFilledIcon, VolumeUpIcon, ShrinkIcon, ExpandIcon, RefreshIcon, RepeatIcon, VideoIcon, CodeIcon, WriteIcon, ShowIcon, HideIcon, UploadIcon, MinusIcon, PlusIcon, DownloadIcon, HeartIcon, BookmarkIcon, ArrowLeftIcon, ArrowRightIcon } from '@designbasekorea/icons';
3
+ import { CloseIcon, ChevronUpIcon, ChevronDownIcon, SearchIcon, UserIcon, ChevronRightIcon, ChevronLeftIcon, ArrowBarLeftIcon, ArrowBarRightIcon, ChevronsUpIcon, InfoFilledIcon, ErrorFilledIcon, WarningFilledIcon, CircleCheckFilledIcon, BulbIcon, CloudCloseIcon, BellActiveIcon, AwardIcon, GalleryIcon, ShareAltIcon, MailIcon, LinkIcon, CopyIcon, MoveIcon, MoreVerticalIcon, StarFilledIcon, StarHalfIcon, StarIcon, SettingsIcon, PauseIcon, PlayIcon, MuteFilledIcon, VolumeUpIcon, ShrinkIcon, ExpandIcon, RefreshIcon, RepeatIcon, VideoIcon, CodeIcon, WriteIcon, ShowIcon, HideIcon, UploadIcon, MinusIcon, PlusIcon, DownloadIcon, HeartIcon, BookmarkIcon, ArrowLeftIcon, ArrowRightIcon, ArrowDownIcon, ArrowUpLeftIcon, ArrowUpRightIcon, ArrowDownLeftIcon, ArrowDownRightIcon } from '@designbasekorea/icons';
4
4
 
5
5
  function getDefaultExportFromCjs (x) {
6
6
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
@@ -3456,20 +3456,20 @@ const Spinner = ({ type = 'circular', size = 'm', color, speed = 1, label = '로
3456
3456
  const renderSpinner = () => {
3457
3457
  switch (type) {
3458
3458
  case 'circular':
3459
- return (jsxRuntimeExports.jsxs("svg", { className: "designbase-spinner__circular", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsxRuntimeExports.jsx("circle", { className: "designbase-spinner__circular-track", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "2", fill: "none" }), jsxRuntimeExports.jsx("circle", { className: "designbase-spinner__circular-indicator", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "2", fill: "none", strokeLinecap: "round" })] }));
3459
+ return (jsxRuntimeExports.jsxs("svg", { className: "designbase-spinner__circular", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [jsxRuntimeExports.jsx("circle", { className: "designbase-spinner__circular-track", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "2", fill: "none" }), jsxRuntimeExports.jsx("circle", { className: "designbase-spinner__circular-indicator", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "2", fill: "none", strokeLinecap: "round" })] }));
3460
3460
  case 'dots':
3461
- return (jsxRuntimeExports.jsxs("div", { className: "designbase-spinner__dots", children: [jsxRuntimeExports.jsx("div", { className: "designbase-spinner__dot" }), jsxRuntimeExports.jsx("div", { className: "designbase-spinner__dot" }), jsxRuntimeExports.jsx("div", { className: "designbase-spinner__dot" })] }));
3461
+ return (jsxRuntimeExports.jsxs("div", { className: "designbase-spinner__dots", "aria-hidden": "true", children: [jsxRuntimeExports.jsx("div", { className: "designbase-spinner__dot" }), jsxRuntimeExports.jsx("div", { className: "designbase-spinner__dot" }), jsxRuntimeExports.jsx("div", { className: "designbase-spinner__dot" })] }));
3462
3462
  case 'bars':
3463
- return (jsxRuntimeExports.jsxs("div", { className: "designbase-spinner__bars", children: [jsxRuntimeExports.jsx("div", { className: "designbase-spinner__bar" }), jsxRuntimeExports.jsx("div", { className: "designbase-spinner__bar" }), jsxRuntimeExports.jsx("div", { className: "designbase-spinner__bar" }), jsxRuntimeExports.jsx("div", { className: "designbase-spinner__bar" }), jsxRuntimeExports.jsx("div", { className: "designbase-spinner__bar" })] }));
3463
+ return (jsxRuntimeExports.jsxs("div", { className: "designbase-spinner__bars", "aria-hidden": "true", children: [jsxRuntimeExports.jsx("div", { className: "designbase-spinner__bar" }), jsxRuntimeExports.jsx("div", { className: "designbase-spinner__bar" }), jsxRuntimeExports.jsx("div", { className: "designbase-spinner__bar" }), jsxRuntimeExports.jsx("div", { className: "designbase-spinner__bar" }), jsxRuntimeExports.jsx("div", { className: "designbase-spinner__bar" })] }));
3464
3464
  case 'pulse':
3465
- return (jsxRuntimeExports.jsx("div", { className: "designbase-spinner__pulse" }));
3465
+ return jsxRuntimeExports.jsx("div", { className: "designbase-spinner__pulse", "aria-hidden": "true" });
3466
3466
  case 'ripple':
3467
- return (jsxRuntimeExports.jsxs("div", { className: "designbase-spinner__ripple", children: [jsxRuntimeExports.jsx("div", { className: "designbase-spinner__ripple-circle" }), jsxRuntimeExports.jsx("div", { className: "designbase-spinner__ripple-circle" })] }));
3467
+ return (jsxRuntimeExports.jsxs("div", { className: "designbase-spinner__ripple", "aria-hidden": "true", children: [jsxRuntimeExports.jsx("div", { className: "designbase-spinner__ripple-circle" }), jsxRuntimeExports.jsx("div", { className: "designbase-spinner__ripple-circle" })] }));
3468
3468
  default:
3469
3469
  return null;
3470
3470
  }
3471
3471
  };
3472
- return (jsxRuntimeExports.jsxs("div", { className: classes, style: style, role: "status", "aria-label": showLabel ? label : undefined, ...props, children: [renderSpinner(), showLabel && label && (jsxRuntimeExports.jsx("span", { className: "designbase-spinner__label", "aria-hidden": "true", children: label }))] }));
3472
+ return (jsxRuntimeExports.jsxs("div", { className: classes, style: style, role: "status", "aria-live": "polite", "aria-busy": "true", "aria-label": showLabel ? label : undefined, ...props, children: [renderSpinner(), showLabel && label && (jsxRuntimeExports.jsx("span", { className: "designbase-spinner__label", "aria-hidden": "true", children: label }))] }));
3473
3473
  };
3474
3474
  Spinner.displayName = 'Spinner';
3475
3475
 
@@ -3655,7 +3655,7 @@ const Checkbox = forwardRef(({ isSelected, defaultSelected, isIndeterminate = fa
3655
3655
  });
3656
3656
  Checkbox.displayName = 'Checkbox';
3657
3657
 
3658
- const Toggle = forwardRef(({ isSelected, defaultSelected, isDisabled = false, isReadOnly = false, isRequired = false, hasLabel = true, size = 'm', children, className, name, value, onChange, ...props }, forwardedRef) => {
3658
+ const Toggle = forwardRef(({ isSelected, defaultSelected, isDisabled = false, isReadOnly = false, size = 'm', children, className, onChange, ...props }, forwardedRef) => {
3659
3659
  const [selected, setSelected] = React.useState(isSelected ?? defaultSelected ?? false);
3660
3660
  React.useEffect(() => {
3661
3661
  if (isSelected !== undefined) {
@@ -3673,9 +3673,9 @@ const Toggle = forwardRef(({ isSelected, defaultSelected, isDisabled = false, is
3673
3673
  'designbase-toggle--selected': selected,
3674
3674
  'designbase-toggle--disabled': isDisabled,
3675
3675
  'designbase-toggle--readonly': isReadOnly,
3676
- 'designbase-toggle--no-label': !hasLabel,
3676
+ 'designbase-toggle--no-label': !children,
3677
3677
  }, className);
3678
- return (jsxRuntimeExports.jsxs("button", { ...props, ref: forwardedRef, name: name, value: value, className: classes, onClick: handleClick, disabled: isDisabled, type: "button", children: [jsxRuntimeExports.jsx("div", { className: "designbase-toggle__track", children: jsxRuntimeExports.jsx("div", { className: "designbase-toggle__thumb" }) }), children && jsxRuntimeExports.jsx("span", { className: "designbase-toggle__label", children: children })] }));
3678
+ return (jsxRuntimeExports.jsxs("button", { ...props, ref: forwardedRef, className: classes, onClick: handleClick, disabled: isDisabled, type: "button", children: [jsxRuntimeExports.jsx("div", { className: "designbase-toggle__track", children: jsxRuntimeExports.jsx("div", { className: "designbase-toggle__thumb" }) }), children && jsxRuntimeExports.jsx("span", { className: "designbase-toggle__label", children: children })] }));
3679
3679
  });
3680
3680
  Toggle.displayName = 'Toggle';
3681
3681
 
@@ -3783,7 +3783,7 @@ const Toast = ({ id, status = 'info', title, description, icon: Icon, duration =
3783
3783
  const getIconColor = () => {
3784
3784
  const colorMap = {
3785
3785
  success: 'success',
3786
- error: 'danger',
3786
+ error: 'error',
3787
3787
  warning: 'warning',
3788
3788
  info: 'info',
3789
3789
  };
@@ -4458,77 +4458,6 @@ const Progressbar = ({ value, max = 100, min = 0, size = 'm', variant = 'default
4458
4458
  };
4459
4459
  Progressbar.displayName = 'Progressbar';
4460
4460
 
4461
- const Badge = ({ children, size = 'm', variant = 'primary', style = 'text', count, maxCount = 99, disabled = false, className, ...props }) => {
4462
- // 숫자 스타일일 때 count 값을 사용
4463
- const displayContent = style === 'number' && count !== undefined
4464
- ? (count > maxCount ? `${maxCount}+` : count.toString())
4465
- : children;
4466
- const classes = clsx('designbase-badge', `designbase-badge--${size}`, `designbase-badge--${variant}`, `designbase-badge--${style}`, {
4467
- 'designbase-badge--disabled': disabled,
4468
- }, className);
4469
- return (jsxRuntimeExports.jsx("span", { className: classes, ...props, children: displayContent }));
4470
- };
4471
- Badge.displayName = 'Badge';
4472
-
4473
- 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, }) => {
4474
- const [internalExpanded, setInternalExpanded] = useState(expanded);
4475
- // expanded prop이 변경될 때 internalExpanded 업데이트
4476
- useEffect(() => {
4477
- setInternalExpanded(expanded);
4478
- }, [expanded]);
4479
- const handleClick = (e) => {
4480
- e.preventDefault();
4481
- if (disabled)
4482
- return;
4483
- if (expandable && subItems && subItems.length > 0) {
4484
- setInternalExpanded(!internalExpanded);
4485
- }
4486
- onClick?.({
4487
- id,
4488
- label,
4489
- href,
4490
- icon: Icon,
4491
- active,
4492
- disabled,
4493
- badge,
4494
- badgeColor,
4495
- variant,
4496
- type,
4497
- size,
4498
- subItems,
4499
- expanded: internalExpanded,
4500
- expandable,
4501
- onClick,
4502
- onChildClick,
4503
- className,
4504
- });
4505
- };
4506
- const handleChildClick = (child) => {
4507
- if (child.disabled)
4508
- return;
4509
- onChildClick?.(child);
4510
- };
4511
- const isExpanded = expandable ? internalExpanded : expanded;
4512
- const hasChildren = subItems && subItems.length > 0;
4513
- const classes = clsx('designbase-menu-item', `designbase-menu-item--${type}`, `designbase-menu-item--${size}`, `designbase-menu-item--${variant}`, `designbase-menu-item--${style}`, `designbase-menu-item--depth-${depth}`, {
4514
- 'designbase-menu-item--active': active,
4515
- 'designbase-menu-item--disabled': disabled,
4516
- 'designbase-menu-item--expandable': expandable,
4517
- 'designbase-menu-item--expanded': isExpanded,
4518
- 'designbase-menu-item--with-children': hasChildren,
4519
- 'designbase-menu-item--with-badge': badge,
4520
- 'designbase-menu-item--with-icon': Icon,
4521
- }, className);
4522
- const contentClasses = clsx('designbase-menu-item__content', {
4523
- 'designbase-menu-item__content--clickable': onClick || href,
4524
- });
4525
- 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}`, {
4526
- 'designbase-menu-item__icon--active': active,
4527
- 'designbase-menu-item__icon--disabled': disabled,
4528
- }), 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(ChevronUpIcon, { size: 16 })) : (jsxRuntimeExports.jsx(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))) }))] }));
4529
- };
4530
- MenuItem.displayName = 'MenuItem';
4531
-
4532
4461
  const SearchBar = ({ value, defaultValue = '', placeholder = '검색...', size = 'm', variant = 'default', disabled = false, readOnly = false, fullWidth = false, searchIcon: SearchIconComponent = SearchIcon, clearIcon: ClearIconComponent = CloseIcon, onChange, onSearch, onFocus, onBlur, onKeyDown, className, ...props }) => {
4533
4462
  const [internalValue, setInternalValue] = useState(defaultValue);
4534
4463
  const inputRef = useRef(null);
@@ -4573,6 +4502,18 @@ const SearchBar = ({ value, defaultValue = '', placeholder = '검색...', size =
4573
4502
  };
4574
4503
  SearchBar.displayName = 'SearchBar';
4575
4504
 
4505
+ const Badge = ({ children, size = 'm', variant = 'primary', style = 'text', count, maxCount = 99, disabled = false, className, ...props }) => {
4506
+ // 숫자 스타일일 때 count 값을 사용
4507
+ const displayContent = style === 'number' && count !== undefined
4508
+ ? (count > maxCount ? `${maxCount}+` : count.toString())
4509
+ : children;
4510
+ const classes = clsx('designbase-badge', `designbase-badge--${size}`, `designbase-badge--${variant}`, `designbase-badge--${style}`, {
4511
+ 'designbase-badge--disabled': disabled,
4512
+ }, className);
4513
+ return (jsxRuntimeExports.jsx("span", { className: classes, ...props, children: displayContent }));
4514
+ };
4515
+ Badge.displayName = 'Badge';
4516
+
4576
4517
  const Avatar = ({ src, alt, initials, icon, size = 'm', variant = 'circle', status, badge, badgeMaxCount = 99, badgeVariant = 'danger', badgeStyle = 'number', badgeText, onClick, disabled = false, className, ...props }) => {
4577
4518
  const [imageError, setImageError] = useState(false);
4578
4519
  const [imageLoading, setImageLoading] = useState(true);
@@ -4705,8 +4646,65 @@ const Navbar = ({ size = 'm', variant = 'default', position = 'static', logo, on
4705
4646
  const containerClasses = clsx('designbase-navbar__container', {
4706
4647
  'designbase-navbar__container--full-width': fullWidth,
4707
4648
  });
4708
- const renderNavItem = (item) => (jsxRuntimeExports.jsx("li", { className: "designbase-navbar__nav-item", children: jsxRuntimeExports.jsx(MenuItem, { ...item, type: "inline", style: "dropdown", onClick: () => handleItemClick(item), onChildClick: (child) => handleItemClick(child) }) }, item.id));
4709
- 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("i", { className: "designbase-icon-chevron-down" })] }), 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', {
4649
+ // 재귀적으로 메뉴 아이템을 렌더링하는 함수
4650
+ const renderMenuItem = (item, depth = 0) => {
4651
+ const hasChildren = item.children && item.children.length > 0;
4652
+ const isFirstLevel = depth === 0;
4653
+ const isSecondLevel = depth === 1;
4654
+ if (isFirstLevel) {
4655
+ // 1depth - 메인 네비게이션
4656
+ 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', {
4657
+ 'designbase-navbar__nav-link--active': item.active,
4658
+ 'designbase-navbar__nav-link--disabled': item.disabled,
4659
+ }), onClick: () => handleItemClick(item), disabled: item.disabled, children: [item.icon && React.createElement(item.icon, { size: iconSize, color: 'currentColor' }), item.label, jsxRuntimeExports.jsx(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', {
4660
+ 'designbase-navbar__nav-link--active': item.active,
4661
+ 'designbase-navbar__nav-link--disabled': item.disabled,
4662
+ }), onClick: (e) => {
4663
+ if (item.disabled) {
4664
+ e.preventDefault();
4665
+ return;
4666
+ }
4667
+ handleItemClick(item);
4668
+ }, children: [item.icon && React.createElement(item.icon, { size: iconSize, color: 'currentColor' }), item.label] })) }, item.id));
4669
+ }
4670
+ else if (isSecondLevel) {
4671
+ // 2depth - 드롭다운 메뉴
4672
+ 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', {
4673
+ 'designbase-navbar__dropdown-item--active': item.active,
4674
+ 'designbase-navbar__dropdown-item--disabled': item.disabled,
4675
+ }), onClick: (e) => {
4676
+ if (item.disabled) {
4677
+ e.preventDefault();
4678
+ return;
4679
+ }
4680
+ handleItemClick(item);
4681
+ }, children: [item.icon && React.createElement(item.icon, { size: iconSize, color: 'currentColor' }), item.label, jsxRuntimeExports.jsx(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', {
4682
+ 'designbase-navbar__dropdown-item--active': item.active,
4683
+ 'designbase-navbar__dropdown-item--disabled': item.disabled,
4684
+ }), onClick: (e) => {
4685
+ if (item.disabled) {
4686
+ e.preventDefault();
4687
+ return;
4688
+ }
4689
+ handleItemClick(item);
4690
+ }, children: [item.icon && React.createElement(item.icon, { size: iconSize, color: 'currentColor' }), item.label] })) }, item.id));
4691
+ }
4692
+ else {
4693
+ // 3depth 이상 - 서브메뉴
4694
+ return (jsxRuntimeExports.jsx("li", { children: jsxRuntimeExports.jsxs("a", { href: item.href, className: clsx('designbase-navbar__dropdown-item', {
4695
+ 'designbase-navbar__dropdown-item--active': item.active,
4696
+ 'designbase-navbar__dropdown-item--disabled': item.disabled,
4697
+ }), onClick: (e) => {
4698
+ if (item.disabled) {
4699
+ e.preventDefault();
4700
+ return;
4701
+ }
4702
+ handleItemClick(item);
4703
+ }, children: [item.icon && React.createElement(item.icon, { size: iconSize, color: 'currentColor' }), item.label] }) }, item.id));
4704
+ }
4705
+ };
4706
+ const renderNavItem = (item) => renderMenuItem(item, 0);
4707
+ 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(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', {
4710
4708
  'designbase-navbar__user-dropdown-item--disabled': item.disabled,
4711
4709
  }), onClick: (e) => {
4712
4710
  if (item.disabled) {
@@ -4744,6 +4742,65 @@ const Navbar = ({ size = 'm', variant = 'default', position = 'static', logo, on
4744
4742
  };
4745
4743
  Navbar.displayName = 'Navbar';
4746
4744
 
4745
+ 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, }) => {
4746
+ const [internalExpanded, setInternalExpanded] = useState(expanded);
4747
+ // expanded prop이 변경될 때 internalExpanded 업데이트
4748
+ useEffect(() => {
4749
+ setInternalExpanded(expanded);
4750
+ }, [expanded]);
4751
+ const handleClick = (e) => {
4752
+ e.preventDefault();
4753
+ if (disabled)
4754
+ return;
4755
+ if (expandable && subItems && subItems.length > 0) {
4756
+ setInternalExpanded(!internalExpanded);
4757
+ }
4758
+ onClick?.({
4759
+ id,
4760
+ label,
4761
+ href,
4762
+ icon: Icon,
4763
+ active,
4764
+ disabled,
4765
+ badge,
4766
+ badgeColor,
4767
+ variant,
4768
+ type,
4769
+ size,
4770
+ subItems,
4771
+ expanded: internalExpanded,
4772
+ expandable,
4773
+ onClick,
4774
+ onChildClick,
4775
+ className,
4776
+ });
4777
+ };
4778
+ const handleChildClick = (child) => {
4779
+ if (child.disabled)
4780
+ return;
4781
+ onChildClick?.(child);
4782
+ };
4783
+ const isExpanded = expandable ? internalExpanded : expanded;
4784
+ const hasChildren = subItems && subItems.length > 0;
4785
+ const classes = clsx('designbase-menu-item', `designbase-menu-item--${type}`, `designbase-menu-item--${size}`, `designbase-menu-item--${variant}`, `designbase-menu-item--${style}`, `designbase-menu-item--depth-${depth}`, {
4786
+ 'designbase-menu-item--active': active,
4787
+ 'designbase-menu-item--disabled': disabled,
4788
+ 'designbase-menu-item--expandable': expandable,
4789
+ 'designbase-menu-item--expanded': isExpanded,
4790
+ 'designbase-menu-item--with-children': hasChildren,
4791
+ 'designbase-menu-item--with-badge': badge,
4792
+ 'designbase-menu-item--with-icon': Icon,
4793
+ }, className);
4794
+ const contentClasses = clsx('designbase-menu-item__content', {
4795
+ 'designbase-menu-item__content--clickable': onClick || href,
4796
+ });
4797
+ 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}`, {
4798
+ 'designbase-menu-item__icon--active': active,
4799
+ 'designbase-menu-item__icon--disabled': disabled,
4800
+ }), 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(ChevronUpIcon, { size: 16 })) : (jsxRuntimeExports.jsx(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))) }))] }));
4801
+ };
4802
+ MenuItem.displayName = 'MenuItem';
4803
+
4747
4804
  const Sidebar = ({ size = 'm', variant = 'default', position = 'left', logo, onLogoClick, items = [], onItemClick, userProfile, userMenuItems = [], onUserMenuItemClick, collapsed = false, onToggle, collapsible = true, fixed = false, fullHeight = false, shadow = false, className, ...props }) => {
4748
4805
  const [expandedItems, setExpandedItems] = useState([]);
4749
4806
  const handleToggle = () => {
@@ -4785,9 +4842,19 @@ const Sidebar = ({ size = 'm', variant = 'default', position = 'left', logo, onL
4785
4842
  const hasChildren = item.children && item.children.length > 0;
4786
4843
  return (jsxRuntimeExports.jsx("li", { className: "designbase-sidebar__item", children: jsxRuntimeExports.jsx(MenuItem, { ...item, type: "block", style: "accordion", depth: level, expanded: isExpanded, expandable: hasChildren, onClick: () => handleItemClick(item), onChildClick: (child) => handleItemClick(child) }) }, item.id));
4787
4844
  };
4788
- return (jsxRuntimeExports.jsx("aside", { className: classes, role: "complementary", "aria-label": "\uC0AC\uC774\uB4DC\uBC14 \uB124\uBE44\uAC8C\uC774\uC158", ...props, children: jsxRuntimeExports.jsxs("div", { className: "designbase-sidebar__container", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-sidebar__header", children: [jsxRuntimeExports.jsx("div", { className: "designbase-sidebar__logo", onClick: onLogoClick, role: onLogoClick ? 'button' : undefined, tabIndex: onLogoClick ? 0 : undefined, children: logo || jsxRuntimeExports.jsx(Logo, { size: size === 's' ? 's' : size === 'l' ? 'l' : 'm' }) }), collapsible && (jsxRuntimeExports.jsx("button", { className: "designbase-sidebar__toggle", onClick: handleToggle, "aria-label": collapsed ? '사이드바 펼치기' : '사이드바 접기', children: jsxRuntimeExports.jsx("i", { className: clsx('designbase-sidebar__toggle-icon', 'designbase-icon-chevron-left', {
4845
+ return (jsxRuntimeExports.jsx("aside", { className: classes, role: "complementary", "aria-label": "\uC0AC\uC774\uB4DC\uBC14 \uB124\uBE44\uAC8C\uC774\uC158", ...props, children: jsxRuntimeExports.jsxs("div", { className: "designbase-sidebar__container", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-sidebar__header", children: [jsxRuntimeExports.jsx("div", { className: "designbase-sidebar__logo", onClick: onLogoClick, role: onLogoClick ? 'button' : undefined, tabIndex: onLogoClick ? 0 : undefined, onKeyDown: (e) => {
4846
+ if (onLogoClick && (e.key === 'Enter' || e.key === ' ')) {
4847
+ e.preventDefault();
4848
+ onLogoClick();
4849
+ }
4850
+ }, 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(ChevronLeftIcon, { className: clsx('designbase-sidebar__toggle-icon', {
4789
4851
  'designbase-sidebar__toggle-icon--collapsed': collapsed,
4790
- }) }) }))] }), 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", 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', {
4852
+ }) }) }))] }), 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) => {
4853
+ if (e.key === 'Enter' || e.key === ' ') {
4854
+ e.preventDefault();
4855
+ onUserMenuItemClick?.({ id: 'profile', label: '프로필', href: '#' });
4856
+ }
4857
+ }, 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', {
4791
4858
  'designbase-sidebar__user-menu-link--disabled': item.disabled,
4792
4859
  }), onClick: (e) => {
4793
4860
  if (item.disabled) {
@@ -4795,7 +4862,12 @@ const Sidebar = ({ size = 'm', variant = 'default', position = 'left', logo, onL
4795
4862
  return;
4796
4863
  }
4797
4864
  handleUserMenuItemClick(item);
4798
- }, children: [item.icon && React.createElement(item.icon, { size: 16 }), item.label] }) }, item.id))) }))] })), userProfile && collapsed && (jsxRuntimeExports.jsx("div", { className: "designbase-sidebar__user-collapsed", children: userProfile.avatar ? (jsxRuntimeExports.jsx("img", { src: userProfile.avatar, alt: userProfile.name, className: "designbase-sidebar__user-avatar-collapsed" })) : (jsxRuntimeExports.jsx("div", { className: "designbase-sidebar__user-avatar-placeholder-collapsed", children: userProfile.name.charAt(0).toUpperCase() })) }))] }) }));
4865
+ }, 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) => {
4866
+ if (e.key === 'Enter' || e.key === ' ') {
4867
+ e.preventDefault();
4868
+ onUserMenuItemClick?.({ id: 'profile', label: '프로필', href: '#' });
4869
+ }
4870
+ }, style: { cursor: 'pointer' }, children: userProfile.avatar ? (jsxRuntimeExports.jsx("img", { src: userProfile.avatar, alt: userProfile.name, className: "designbase-sidebar__user-avatar-collapsed" })) : (jsxRuntimeExports.jsx("div", { className: "designbase-sidebar__user-avatar-placeholder-collapsed", children: userProfile.name.charAt(0).toUpperCase() })) }))] }) }));
4799
4871
  };
4800
4872
  Sidebar.displayName = 'Sidebar';
4801
4873
 
@@ -5300,14 +5372,22 @@ const Tooltip = ({ content, children, position = 'top', size = 'm', variant = 'd
5300
5372
  const [isVisible, setIsVisible] = useState(false);
5301
5373
  const [tooltipStyle, setTooltipStyle] = useState({});
5302
5374
  const [arrowStyle, setArrowStyle] = useState({});
5303
- const triggerRef = useRef(null);
5375
+ // 🔑 DOM 래퍼(ref/이벤트가 항상 붙도록)
5376
+ const triggerWrapperRef = useRef(null);
5304
5377
  const tooltipRef = useRef(null);
5305
- const timeoutRef = useRef();
5378
+ // 브라우저 setTimeout은 number 반환
5379
+ const timeoutRef = useRef(null);
5380
+ const clearTimer = () => {
5381
+ if (timeoutRef.current !== null) {
5382
+ window.clearTimeout(timeoutRef.current);
5383
+ timeoutRef.current = null;
5384
+ }
5385
+ };
5306
5386
  // 툴팁 위치 계산
5307
5387
  const calculatePosition = useCallback(() => {
5308
- if (!triggerRef.current || !tooltipRef.current)
5388
+ if (!triggerWrapperRef.current || !tooltipRef.current)
5309
5389
  return;
5310
- const triggerRect = triggerRef.current.getBoundingClientRect();
5390
+ const triggerRect = triggerWrapperRef.current.getBoundingClientRect();
5311
5391
  const tooltipRect = tooltipRef.current.getBoundingClientRect();
5312
5392
  const scrollX = window.pageXOffset || document.documentElement.scrollLeft;
5313
5393
  const scrollY = window.pageYOffset || document.documentElement.scrollTop;
@@ -5320,129 +5400,131 @@ const Tooltip = ({ content, children, position = 'top', size = 'm', variant = 'd
5320
5400
  switch (position) {
5321
5401
  case 'top':
5322
5402
  top = triggerRect.top + scrollY - tooltipRect.height - 8;
5323
- left = triggerRect.left + scrollX + (triggerRect.width / 2) - (tooltipRect.width / 2);
5324
- arrowTop = tooltipRect.height;
5325
- arrowLeft = tooltipRect.width / 2 - 4;
5403
+ left = triggerRect.left + scrollX + triggerRect.width / 2 - tooltipRect.width / 2;
5404
+ // CSS에서 화살표는 자동으로 중앙에 위치하므로 스타일 제거
5405
+ arrowTop = 0;
5406
+ arrowLeft = 0;
5326
5407
  break;
5327
5408
  case 'top-start':
5328
5409
  top = triggerRect.top + scrollY - tooltipRect.height - 8;
5329
5410
  left = triggerRect.left + scrollX;
5330
- arrowTop = tooltipRect.height;
5331
- arrowLeft = 12;
5411
+ // CSS에서 화살표는 자동으로 위치하므로 스타일 제거
5412
+ arrowTop = 0;
5413
+ arrowLeft = 0;
5332
5414
  break;
5333
5415
  case 'top-end':
5334
5416
  top = triggerRect.top + scrollY - tooltipRect.height - 8;
5335
5417
  left = triggerRect.right + scrollX - tooltipRect.width;
5336
- arrowTop = tooltipRect.height;
5337
- arrowLeft = tooltipRect.width - 12;
5418
+ // CSS에서 화살표는 자동으로 위치하므로 스타일 제거
5419
+ arrowTop = 0;
5420
+ arrowLeft = 0;
5338
5421
  break;
5339
5422
  case 'bottom':
5340
5423
  top = triggerRect.bottom + scrollY + 8;
5341
- left = triggerRect.left + scrollX + (triggerRect.width / 2) - (tooltipRect.width / 2);
5342
- arrowTop = -4;
5343
- arrowLeft = tooltipRect.width / 2 - 4;
5424
+ left = triggerRect.left + scrollX + triggerRect.width / 2 - tooltipRect.width / 2;
5425
+ // CSS에서 화살표는 자동으로 중앙에 위치하므로 스타일 제거
5426
+ arrowTop = 0;
5427
+ arrowLeft = 0;
5344
5428
  break;
5345
5429
  case 'bottom-start':
5346
5430
  top = triggerRect.bottom + scrollY + 8;
5347
5431
  left = triggerRect.left + scrollX;
5348
- arrowTop = -4;
5349
- arrowLeft = 12;
5432
+ // CSS에서 화살표는 자동으로 위치하므로 스타일 제거
5433
+ arrowTop = 0;
5434
+ arrowLeft = 0;
5350
5435
  break;
5351
5436
  case 'bottom-end':
5352
5437
  top = triggerRect.bottom + scrollY + 8;
5353
5438
  left = triggerRect.right + scrollX - tooltipRect.width;
5354
- arrowTop = -4;
5355
- arrowLeft = tooltipRect.width - 12;
5439
+ // CSS에서 화살표는 자동으로 위치하므로 스타일 제거
5440
+ arrowTop = 0;
5441
+ arrowLeft = 0;
5356
5442
  break;
5357
5443
  case 'left':
5358
- top = triggerRect.top + scrollY + (triggerRect.height / 2) - (tooltipRect.height / 2);
5444
+ top = triggerRect.top + scrollY + triggerRect.height / 2 - tooltipRect.height / 2;
5359
5445
  left = triggerRect.left + scrollX - tooltipRect.width - 8;
5360
- arrowTop = tooltipRect.height / 2 - 4;
5361
- arrowLeft = tooltipRect.width;
5446
+ // CSS에서 화살표는 자동으로 중앙에 위치하므로 스타일 제거
5447
+ arrowTop = 0;
5448
+ arrowLeft = 0;
5362
5449
  break;
5363
5450
  case 'left-start':
5364
5451
  top = triggerRect.top + scrollY;
5365
5452
  left = triggerRect.left + scrollX - tooltipRect.width - 8;
5366
- arrowTop = 12;
5367
- arrowLeft = tooltipRect.width;
5453
+ // CSS에서 화살표는 자동으로 위치하므로 스타일 제거
5454
+ arrowTop = 0;
5455
+ arrowLeft = 0;
5368
5456
  break;
5369
5457
  case 'left-end':
5370
5458
  top = triggerRect.bottom + scrollY - tooltipRect.height;
5371
5459
  left = triggerRect.left + scrollX - tooltipRect.width - 8;
5372
- arrowTop = tooltipRect.height - 12;
5373
- arrowLeft = tooltipRect.width;
5460
+ // CSS에서 화살표는 자동으로 위치하므로 스타일 제거
5461
+ arrowTop = 0;
5462
+ arrowLeft = 0;
5374
5463
  break;
5375
5464
  case 'right':
5376
- top = triggerRect.top + scrollY + (triggerRect.height / 2) - (tooltipRect.height / 2);
5465
+ top = triggerRect.top + scrollY + triggerRect.height / 2 - tooltipRect.height / 2;
5377
5466
  left = triggerRect.right + scrollX + 8;
5378
- arrowTop = tooltipRect.height / 2 - 4;
5379
- arrowLeft = -4;
5467
+ // CSS에서 화살표는 자동으로 중앙에 위치하므로 스타일 제거
5468
+ arrowTop = 0;
5469
+ arrowLeft = 0;
5380
5470
  break;
5381
5471
  case 'right-start':
5382
5472
  top = triggerRect.top + scrollY;
5383
5473
  left = triggerRect.right + scrollX + 8;
5384
- arrowTop = 12;
5385
- arrowLeft = -4;
5474
+ // CSS에서 화살표는 자동으로 위치하므로 스타일 제거
5475
+ arrowTop = 0;
5476
+ arrowLeft = 0;
5386
5477
  break;
5387
5478
  case 'right-end':
5388
5479
  top = triggerRect.bottom + scrollY - tooltipRect.height;
5389
5480
  left = triggerRect.right + scrollX + 8;
5390
- arrowTop = tooltipRect.height - 12;
5391
- arrowLeft = -4;
5481
+ // CSS에서 화살표는 자동으로 위치하므로 스타일 제거
5482
+ arrowTop = 0;
5483
+ arrowLeft = 0;
5392
5484
  break;
5393
5485
  }
5394
5486
  // 화면 경계 체크 및 조정
5395
5487
  const viewportWidth = window.innerWidth;
5396
5488
  const viewportHeight = window.innerHeight;
5397
- // 수평 경계 체크
5489
+ // 수평 경계
5398
5490
  if (left < 8) {
5399
5491
  left = 8;
5400
- if (isHorizontal) {
5492
+ if (isHorizontal)
5401
5493
  arrowLeft = Math.max(arrowLeft, 4);
5402
- }
5403
5494
  }
5404
5495
  else if (left + tooltipRect.width > viewportWidth - 8) {
5405
5496
  left = viewportWidth - tooltipRect.width - 8;
5406
- if (isHorizontal) {
5497
+ if (isHorizontal)
5407
5498
  arrowLeft = Math.min(arrowLeft, tooltipRect.width - 4);
5408
- }
5409
5499
  }
5410
- // 수직 경계 체크
5500
+ // 수직 경계
5411
5501
  if (top < 8) {
5412
5502
  top = 8;
5413
- if (isVertical) {
5503
+ if (isVertical)
5414
5504
  arrowTop = Math.max(arrowTop, 4);
5415
- }
5416
5505
  }
5417
5506
  else if (top + tooltipRect.height > viewportHeight - 8) {
5418
5507
  top = viewportHeight - tooltipRect.height - 8;
5419
- if (isVertical) {
5508
+ if (isVertical)
5420
5509
  arrowTop = Math.min(arrowTop, tooltipRect.height - 4);
5421
- }
5422
5510
  }
5423
5511
  setTooltipStyle({
5424
5512
  position: 'fixed',
5425
5513
  top: `${top}px`,
5426
5514
  left: `${left}px`,
5427
5515
  maxWidth: `${maxWidth}px`,
5428
- zIndex: 9999,
5429
- });
5430
- setArrowStyle({
5431
- position: 'absolute',
5432
- top: `${arrowTop}px`,
5433
- left: `${arrowLeft}px`,
5516
+ zIndex: 9999
5434
5517
  });
5518
+ // CSS에서 화살표 위치를 자동으로 처리하므로 인라인 스타일 제거
5519
+ setArrowStyle({});
5435
5520
  }, [position, maxWidth]);
5436
5521
  // 툴팁 표시
5437
5522
  const showTooltip = useCallback(() => {
5438
5523
  if (disabled || alwaysShow)
5439
5524
  return;
5440
- if (timeoutRef.current) {
5441
- clearTimeout(timeoutRef.current);
5442
- }
5443
- timeoutRef.current = setTimeout(() => {
5525
+ clearTimer();
5526
+ timeoutRef.current = window.setTimeout(() => {
5444
5527
  setIsVisible(true);
5445
- // 위치 계산을 위해 다음 프레임에서 실행
5446
5528
  requestAnimationFrame(() => {
5447
5529
  calculatePosition();
5448
5530
  });
@@ -5452,89 +5534,57 @@ const Tooltip = ({ content, children, position = 'top', size = 'm', variant = 'd
5452
5534
  const hideTooltip = useCallback(() => {
5453
5535
  if (disabled || alwaysShow)
5454
5536
  return;
5455
- if (timeoutRef.current) {
5456
- clearTimeout(timeoutRef.current);
5457
- }
5458
- timeoutRef.current = setTimeout(() => {
5537
+ clearTimer();
5538
+ timeoutRef.current = window.setTimeout(() => {
5459
5539
  setIsVisible(false);
5460
5540
  }, hideDelay);
5461
5541
  }, [disabled, alwaysShow, hideDelay]);
5462
- // 이벤트 핸들러 - Tooltip은 항상 hover 기반
5463
- const handleMouseEnter = useCallback(() => {
5464
- showTooltip();
5465
- }, [showTooltip]);
5466
- const handleMouseLeave = useCallback(() => {
5467
- hideTooltip();
5468
- }, [hideTooltip]);
5469
- const handleFocus = useCallback(() => {
5470
- showTooltip();
5471
- }, [showTooltip]);
5472
- const handleBlur = useCallback(() => {
5473
- hideTooltip();
5474
- }, [hideTooltip]);
5475
- // 키보드 이벤트
5542
+ // 키보드 이스케이프
5476
5543
  const handleKeyDown = useCallback((e) => {
5477
- if (e.key === 'Escape') {
5544
+ if (e.key === 'Escape')
5478
5545
  hideTooltip();
5479
- }
5480
5546
  }, [hideTooltip]);
5481
- // 윈도우 리사이즈 스크롤 이벤트
5482
- useEffect(() => {
5483
- if (isVisible) {
5484
- const handleResize = () => calculatePosition();
5485
- const handleScroll = () => calculatePosition();
5486
- window.addEventListener('resize', handleResize);
5487
- window.addEventListener('scroll', handleScroll, true);
5488
- return () => {
5489
- window.removeEventListener('resize', handleResize);
5490
- window.removeEventListener('scroll', handleScroll, true);
5491
- };
5492
- }
5493
- }, [isVisible, calculatePosition]);
5494
- // 컴포넌트 언마운트 시 타이머 정리
5547
+ // 리사이즈/스크롤 위치 업데이트
5495
5548
  useEffect(() => {
5549
+ if (!isVisible)
5550
+ return;
5551
+ const onResize = () => calculatePosition();
5552
+ const onScroll = () => calculatePosition();
5553
+ window.addEventListener('resize', onResize);
5554
+ window.addEventListener('scroll', onScroll, true);
5496
5555
  return () => {
5497
- if (timeoutRef.current) {
5498
- clearTimeout(timeoutRef.current);
5499
- }
5556
+ window.removeEventListener('resize', onResize);
5557
+ window.removeEventListener('scroll', onScroll, true);
5500
5558
  };
5501
- }, []);
5502
- // 항상 표시 모드
5559
+ }, [isVisible, calculatePosition]);
5560
+ // 언마운트 타이머 정리
5561
+ useEffect(() => () => clearTimer(), []);
5562
+ // 항상표시 모드
5503
5563
  useEffect(() => {
5504
5564
  if (alwaysShow && !disabled) {
5505
5565
  setIsVisible(true);
5506
- requestAnimationFrame(() => {
5507
- calculatePosition();
5508
- });
5566
+ requestAnimationFrame(() => calculatePosition());
5509
5567
  }
5510
5568
  else if (!alwaysShow) {
5511
5569
  setIsVisible(false);
5512
5570
  }
5513
5571
  }, [alwaysShow, disabled, calculatePosition]);
5514
- const classes = clsx('designbase-tooltip', `designbase-tooltip--${size}`, `designbase-tooltip--${variant}`, `designbase-tooltip--${position}`, {
5515
- 'designbase-tooltip--visible': isVisible,
5516
- 'designbase-tooltip--disabled': disabled,
5517
- }, className);
5572
+ // content가 동적으로 바뀌어도 위치 갱신
5573
+ useEffect(() => {
5574
+ if (!isVisible)
5575
+ return;
5576
+ const ro = new ResizeObserver(() => calculatePosition());
5577
+ if (tooltipRef.current)
5578
+ ro.observe(tooltipRef.current);
5579
+ return () => ro.disconnect();
5580
+ }, [isVisible, calculatePosition, content]);
5581
+ const classes = clsx('designbase-tooltip', `designbase-tooltip--${size}`, `designbase-tooltip--${variant}`, `designbase-tooltip--${position}`, { 'designbase-tooltip--visible': isVisible, 'designbase-tooltip--disabled': disabled }, className);
5518
5582
  const arrowClasses = clsx('designbase-tooltip__arrow', `designbase-tooltip__arrow--${position}`);
5519
- // 자식 요소에 이벤트 핸들러 추가
5520
- const enhancedChildren = React.cloneElement(children, {
5521
- ref: triggerRef,
5522
- onMouseEnter: handleMouseEnter,
5523
- onMouseLeave: handleMouseLeave,
5524
- onFocus: handleFocus,
5525
- onBlur: handleBlur,
5526
- onKeyDown: handleKeyDown,
5527
- onMouseDown: (e) => {
5528
- // 클릭으로 인한 focus 방지 (hover 전용 보장)
5529
- e.preventDefault();
5530
- },
5531
- tabIndex: children.props.tabIndex ?? 0,
5532
- });
5533
- return (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [enhancedChildren, isVisible && (jsxRuntimeExports.jsxs("div", { ref: tooltipRef, className: classes, style: tooltipStyle, role: "tooltip", "aria-hidden": !isVisible, ...props, children: [jsxRuntimeExports.jsx("div", { className: "designbase-tooltip__content", children: content }), showArrow && (jsxRuntimeExports.jsx("div", { className: arrowClasses, style: arrowStyle }))] }))] }));
5583
+ 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 })] }))] }));
5534
5584
  };
5535
5585
  Tooltip.displayName = 'Tooltip';
5536
5586
 
5537
- 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, className, ...props }) => {
5587
+ 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 }) => {
5538
5588
  const [internalOpen, setInternalOpen] = useState(false);
5539
5589
  const [popoverStyle, setPopoverStyle] = useState({});
5540
5590
  const [arrowStyle, setArrowStyle] = useState({});
@@ -5747,6 +5797,12 @@ const Popover = ({ content, children, position = 'top', size = 'm', variant = 'd
5747
5797
  closePopover();
5748
5798
  }
5749
5799
  }, [closeOnEscape, closePopover]);
5800
+ // 닫기 버튼 핸들러
5801
+ const handleCloseClick = useCallback((e) => {
5802
+ e.preventDefault();
5803
+ e.stopPropagation();
5804
+ closePopover();
5805
+ }, [closePopover]);
5750
5806
  // 외부 클릭 처리
5751
5807
  useEffect(() => {
5752
5808
  if (isOpen && closeOnOutsideClick) {
@@ -5823,7 +5879,7 @@ const Popover = ({ content, children, position = 'top', size = 'm', variant = 'd
5823
5879
  'aria-expanded': isOpen,
5824
5880
  'aria-haspopup': 'dialog',
5825
5881
  });
5826
- 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.jsx("div", { className: "designbase-popover__content", children: content }), showArrow && (jsxRuntimeExports.jsx("div", { className: arrowClasses, style: arrowStyle }))] }))] }));
5882
+ 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 }))] }))] }));
5827
5883
  };
5828
5884
  Popover.displayName = 'Popover';
5829
5885
 
@@ -6909,37 +6965,36 @@ const Reorder = ({ items, variant = 'default', size = 'm', orientation = 'vertic
6909
6965
  setDraggedIndex(index);
6910
6966
  setIsDragging(true);
6911
6967
  setDragOverIndex(-1);
6912
- // 드래그 이미지 설정 - 원본과 동일하게
6968
+ // 드래그 이미지 설정 - 커스텀 드래그 이미지 사용하지 않음
6969
+ // 브라우저 기본 드래그 이미지를 사용하여 자연스러운 드래그 경험 제공
6970
+ e.dataTransfer.effectAllowed = 'move';
6971
+ e.dataTransfer.setData('text/plain', item.id);
6972
+ // 드래그 시작 애니메이션
6913
6973
  const dragElement = itemRefs.current.get(item.id);
6914
6974
  if (dragElement) {
6915
- const rect = dragElement.getBoundingClientRect();
6916
- const dragImage = dragElement.cloneNode(true);
6917
- dragImage.style.position = 'absolute';
6918
- dragImage.style.top = '-1000px';
6919
- dragImage.style.opacity = '0.9';
6920
- dragImage.style.transform = 'none'; // 회전 제거
6921
- dragImage.style.pointerEvents = 'none';
6922
- dragImage.style.zIndex = '9999';
6923
- document.body.appendChild(dragImage);
6924
- e.dataTransfer.setDragImage(dragImage, rect.width / 2, rect.height / 2);
6925
- // 드래그 이미지 정리
6926
- setTimeout(() => {
6927
- if (document.body.contains(dragImage)) {
6928
- document.body.removeChild(dragImage);
6929
- }
6930
- }, 0);
6975
+ dragElement.style.animation = 'dragStart 0.2s ease-out forwards';
6931
6976
  }
6932
- e.dataTransfer.effectAllowed = 'move';
6933
- e.dataTransfer.setData('text/plain', item.id);
6934
6977
  onDragStart?.(item, index);
6935
6978
  }, [disabled, onDragStart]);
6936
- // 드래그 오버 핸들러
6979
+ // 드래그 오버 핸들러 - 드래그 방향에 따른 정확한 디바이더 위치 계산
6937
6980
  const handleDragOver = useCallback((e, index) => {
6938
6981
  e.preventDefault();
6939
6982
  e.dataTransfer.dropEffect = 'move';
6940
- if (draggedIndex !== index) {
6941
- setDragOverIndex(index);
6983
+ if (draggedIndex === -1 || draggedIndex === index) {
6984
+ setDragOverIndex(-1);
6985
+ return;
6986
+ }
6987
+ // 드래그 방향에 따라 디바이더 위치 결정
6988
+ let dropIndex = index;
6989
+ if (draggedIndex < index) {
6990
+ // 아래로 드래그: 현재 아이템 위에 디바이더 표시
6991
+ dropIndex = index;
6992
+ }
6993
+ else if (draggedIndex > index) {
6994
+ // 위로 드래그: 현재 아이템 위에 디바이더 표시 (index 그대로 사용)
6995
+ dropIndex = index;
6942
6996
  }
6997
+ setDragOverIndex(dropIndex);
6943
6998
  }, [draggedIndex]);
6944
6999
  // 드래그 리브 핸들러
6945
7000
  const handleDragLeave = useCallback((e) => {
@@ -6952,25 +7007,55 @@ const Reorder = ({ items, variant = 'default', size = 'm', orientation = 'vertic
6952
7007
  setDragOverIndex(-1);
6953
7008
  }
6954
7009
  }, []);
6955
- // 드롭 핸들러
7010
+ // 드롭 핸들러 - 드래그 방향에 따른 정확한 드롭 위치 사용
6956
7011
  const handleDrop = useCallback((e, dropIndex) => {
6957
7012
  e.preventDefault();
6958
7013
  if (draggedIndex === -1 || draggedIndex === dropIndex) {
6959
7014
  setDragOverIndex(-1);
6960
7015
  return;
6961
7016
  }
7017
+ // 드래그 방향에 따라 실제 드롭 위치 계산
7018
+ let actualDropIndex = dropIndex;
7019
+ if (draggedIndex < dropIndex) {
7020
+ // 아래로 드래그: 현재 위치에 삽입
7021
+ actualDropIndex = dropIndex;
7022
+ }
7023
+ else if (draggedIndex > dropIndex) {
7024
+ // 위로 드래그: 현재 위치에 삽입 (dropIndex 그대로 사용)
7025
+ actualDropIndex = dropIndex;
7026
+ }
6962
7027
  const newItems = [...reorderedItems];
6963
7028
  const [draggedItemData] = newItems.splice(draggedIndex, 1);
6964
- newItems.splice(dropIndex, 0, draggedItemData);
7029
+ newItems.splice(actualDropIndex, 0, draggedItemData);
6965
7030
  setReorderedItems(newItems);
6966
7031
  setDragOverIndex(-1);
6967
7032
  setIsDragging(false);
7033
+ // 드롭 완료 애니메이션
7034
+ const dropElement = itemRefs.current.get(draggedItemData.id);
7035
+ if (dropElement) {
7036
+ dropElement.style.animation = 'dropComplete 0.3s ease-out forwards';
7037
+ setTimeout(() => {
7038
+ dropElement.style.animation = '';
7039
+ }, 300);
7040
+ }
6968
7041
  onReorder?.(newItems);
6969
- onDragEnd?.(draggedItemData, draggedIndex, dropIndex);
7042
+ onDragEnd?.(draggedItemData, draggedIndex, actualDropIndex);
6970
7043
  }, [reorderedItems, draggedIndex, onReorder, onDragEnd]);
6971
7044
  // 드래그 종료 핸들러
6972
7045
  const handleDragEnd = useCallback((e) => {
6973
7046
  e.preventDefault();
7047
+ // 모든 아이템의 스타일 초기화
7048
+ itemRefs.current.forEach((element) => {
7049
+ if (element) {
7050
+ element.style.animation = '';
7051
+ element.style.transform = '';
7052
+ element.style.opacity = '';
7053
+ element.style.zIndex = '';
7054
+ element.style.boxShadow = '';
7055
+ element.style.border = '';
7056
+ element.style.background = '';
7057
+ }
7058
+ });
6974
7059
  setDraggedItem(null);
6975
7060
  setDraggedIndex(-1);
6976
7061
  setDragOverIndex(-1);
@@ -7057,11 +7142,21 @@ const Reorder = ({ items, variant = 'default', size = 'm', orientation = 'vertic
7057
7142
  const isDragged = draggedItem?.id === item.id;
7058
7143
  const isDragOver = dragOverIndex === index;
7059
7144
  const isDisabled = disabled || item.disabled;
7145
+ // 드래그 방향에 따른 디바이더 클래스 (드롭 위치에만 적용)
7146
+ const isDraggingDown = isDragOver && draggedIndex !== -1 && draggedIndex < index;
7147
+ const isDraggingUp = isDragOver && draggedIndex !== -1 && draggedIndex > index;
7148
+ const isDraggingRight = isDragOver && draggedIndex !== -1 && draggedIndex < index;
7149
+ const isDraggingLeft = isDragOver && draggedIndex !== -1 && draggedIndex > index;
7060
7150
  const itemClasses = clsx('designbase-reorder__item', {
7061
7151
  'designbase-reorder__item--selected': isSelected,
7062
7152
  'designbase-reorder__item--dragged': isDragged,
7063
7153
  'designbase-reorder__item--drag-over': isDragOver,
7064
7154
  'designbase-reorder__item--disabled': isDisabled,
7155
+ // 드래그 방향에 따른 클래스
7156
+ 'designbase-reorder__item--drag-down': isDraggingDown,
7157
+ 'designbase-reorder__item--drag-up': isDraggingUp,
7158
+ 'designbase-reorder__item--drag-right': isDraggingRight,
7159
+ 'designbase-reorder__item--drag-left': isDraggingLeft,
7065
7160
  });
7066
7161
  return (jsxRuntimeExports.jsxs("div", { ref: (el) => {
7067
7162
  if (el) {
@@ -7714,7 +7809,7 @@ const VideoPlayer = ({ src, poster, title, description, size = 'm', variant = 'd
7714
7809
  'designbase-video-player--show-controls': showControlsOverlay,
7715
7810
  }, className);
7716
7811
  const currentSrc = playlist.length > 0 ? playlist[playlistIndex] : src;
7717
- return (jsxRuntimeExports.jsxs("div", { ref: containerRef, className: classes, onMouseEnter: () => setShowControlsOverlay(true), onMouseLeave: () => setShowControlsOverlay(false), children: [jsxRuntimeExports.jsxs("video", { ref: videoRef, src: currentSrc, poster: poster, autoPlay: autoPlay, loop: loop, muted: muted, playsInline: true, className: "designbase-video-player__video", children: [subtitles.map((subtitle, index) => (jsxRuntimeExports.jsx("track", { kind: "subtitles", src: subtitle.src, srcLang: subtitle.lang, label: subtitle.label, default: subtitle.label === currentSubtitle }, index))), "\uBE44\uB514\uC624\uB97C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uBE0C\uB77C\uC6B0\uC800\uC785\uB2C8\uB2E4."] }), isLoading && (jsxRuntimeExports.jsx("div", { className: "designbase-video-player__loading", children: jsxRuntimeExports.jsx("div", { className: "designbase-video-player__spinner" }) })), error && (jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__error", children: [jsxRuntimeExports.jsx("p", { children: error }), jsxRuntimeExports.jsx("button", { onClick: () => window.location.reload(), children: "\uB2E4\uC2DC \uC2DC\uB3C4" })] })), showControls && (jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__overlay", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__top-controls", children: [title && (jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__title", children: [jsxRuntimeExports.jsx("h3", { children: title }), description && jsxRuntimeExports.jsx("p", { children: description })] })), showSettings && (jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__settings", children: [jsxRuntimeExports.jsx("button", { className: "designbase-video-player__settings-button", onClick: () => setShowSettingsMenu(!showSettingsMenu), children: jsxRuntimeExports.jsx(SettingsIcon, { size: 20 }) }), showSettingsMenu && (jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__settings-menu", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__setting-group", children: [jsxRuntimeExports.jsx("label", { children: "\uC7AC\uC0DD \uC18D\uB3C4" }), jsxRuntimeExports.jsx("select", { value: playbackRate, onChange: (e) => handlePlaybackRateChange(parseFloat(e.target.value)), children: playbackRates.map(rate => (jsxRuntimeExports.jsxs("option", { value: rate, children: [rate, "x"] }, rate))) })] }), qualities.length > 0 && (jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__setting-group", children: [jsxRuntimeExports.jsx("label", { children: "\uD488\uC9C8" }), jsxRuntimeExports.jsx("select", { value: currentQuality, onChange: (e) => handleQualityChange(e.target.value), children: qualities.map(quality => (jsxRuntimeExports.jsx("option", { value: quality.value, children: quality.label }, quality.value))) })] })), subtitles.length > 0 && (jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__setting-group", children: [jsxRuntimeExports.jsx("label", { children: "\uC790\uB9C9" }), jsxRuntimeExports.jsxs("select", { value: currentSubtitle, onChange: (e) => handleSubtitleChange(e.target.value), children: [jsxRuntimeExports.jsx("option", { value: "", children: "\uC790\uB9C9 \uC5C6\uC74C" }), subtitles.map(subtitle => (jsxRuntimeExports.jsx("option", { value: subtitle.label, children: subtitle.label }, subtitle.label)))] })] }))] }))] }))] }), jsxRuntimeExports.jsx("div", { className: "designbase-video-player__center-controls", children: jsxRuntimeExports.jsx("button", { className: "designbase-video-player__play-button", onClick: togglePlay, children: isPlaying ? jsxRuntimeExports.jsx(PauseIcon, { size: 48 }) : jsxRuntimeExports.jsx(PlayIcon, { size: 48 }) }) }), jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__bottom-controls", children: [showProgress && (jsxRuntimeExports.jsxs("div", { ref: progressRef, className: "designbase-video-player__progress", onClick: handleProgressChange, children: [jsxRuntimeExports.jsx("div", { className: "designbase-video-player__progress-bar", style: { width: `${progressPercentage}%` } }), jsxRuntimeExports.jsx("div", { className: "designbase-video-player__progress-handle", style: { left: `${progressPercentage}%` } })] })), jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__controls", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__left-controls", children: [jsxRuntimeExports.jsx("button", { className: "designbase-video-player__control-button", onClick: togglePlay, children: isPlaying ? jsxRuntimeExports.jsx(PauseIcon, { size: 20 }) : jsxRuntimeExports.jsx(PlayIcon, { size: 20 }) }), showVolume && (jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__volume", children: [jsxRuntimeExports.jsx("button", { className: "designbase-video-player__control-button", onClick: toggleMute, onMouseEnter: () => setShowVolumeSlider(true), onMouseLeave: () => setShowVolumeSlider(false), children: isMuted || volume === 0 ? jsxRuntimeExports.jsx(MuteFilledIcon, { size: 20 }) : jsxRuntimeExports.jsx(VolumeUpIcon, { size: 20 }) }), showVolumeSlider && (jsxRuntimeExports.jsx("div", { ref: volumeRef, className: "designbase-video-player__volume-slider", children: jsxRuntimeExports.jsx("input", { type: "range", min: "0", max: "1", step: "0.1", value: volume, onChange: handleVolumeSliderChange }) }))] })), showTime && (jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__time", children: [jsxRuntimeExports.jsx("span", { children: formatTime(currentTime) }), jsxRuntimeExports.jsx("span", { children: "/" }), jsxRuntimeExports.jsx("span", { children: formatTime(duration) })] }))] }), jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__right-controls", children: [playlist.length > 1 && (jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__playlist-controls", children: [jsxRuntimeExports.jsx("button", { className: "designbase-video-player__control-button", disabled: playlistIndex === 0, onClick: () => handlePlaylistChange(playlistIndex - 1), children: jsxRuntimeExports.jsx(ChevronLeftIcon, { size: 16 }) }), jsxRuntimeExports.jsxs("span", { children: [playlistIndex + 1, " / ", playlist.length] }), jsxRuntimeExports.jsx("button", { className: "designbase-video-player__control-button", disabled: playlistIndex === playlist.length - 1, onClick: () => handlePlaylistChange(playlistIndex + 1), children: jsxRuntimeExports.jsx(ChevronRightIcon, { size: 16 }) })] })), enableFullscreen && (jsxRuntimeExports.jsx("button", { className: "designbase-video-player__control-button", onClick: toggleFullscreen, children: isFullscreen ? jsxRuntimeExports.jsx(ShrinkIcon, { size: 20 }) : jsxRuntimeExports.jsx(ExpandIcon, { size: 20 }) }))] })] })] })] }))] }));
7812
+ return (jsxRuntimeExports.jsxs("div", { ref: containerRef, className: classes, onMouseEnter: () => setShowControlsOverlay(true), onMouseLeave: () => setShowControlsOverlay(false), children: [jsxRuntimeExports.jsxs("video", { ref: videoRef, src: currentSrc, poster: poster, autoPlay: autoPlay, loop: loop, muted: muted, playsInline: true, className: "designbase-video-player__video", children: [subtitles.map((subtitle, index) => (jsxRuntimeExports.jsx("track", { kind: "subtitles", src: subtitle.src, srcLang: subtitle.lang, label: subtitle.label, default: subtitle.label === currentSubtitle }, index))), "\uBE44\uB514\uC624\uB97C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uBE0C\uB77C\uC6B0\uC800\uC785\uB2C8\uB2E4."] }), isLoading && (jsxRuntimeExports.jsx("div", { className: "designbase-video-player__loading", children: jsxRuntimeExports.jsx("div", { className: "designbase-video-player__spinner" }) })), error && (jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__error", children: [jsxRuntimeExports.jsx("p", { children: error }), jsxRuntimeExports.jsx("button", { onClick: () => window.location.reload(), children: "\uB2E4\uC2DC \uC2DC\uB3C4" })] })), showControls && (jsxRuntimeExports.jsx("div", { className: "designbase-video-player__dim-overlay" })), showControls && (jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__overlay", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__top-controls", children: [title && (jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__title", children: [jsxRuntimeExports.jsx("h3", { children: title }), description && jsxRuntimeExports.jsx("p", { children: description })] })), showSettings && (jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__settings", children: [jsxRuntimeExports.jsx("button", { className: "designbase-video-player__settings-button", onClick: () => setShowSettingsMenu(!showSettingsMenu), children: jsxRuntimeExports.jsx(SettingsIcon, { size: 20 }) }), showSettingsMenu && (jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__settings-menu", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__setting-group", children: [jsxRuntimeExports.jsx("label", { children: "\uC7AC\uC0DD \uC18D\uB3C4" }), jsxRuntimeExports.jsx("select", { value: playbackRate, onChange: (e) => handlePlaybackRateChange(parseFloat(e.target.value)), children: playbackRates.map(rate => (jsxRuntimeExports.jsxs("option", { value: rate, children: [rate, "x"] }, rate))) })] }), qualities.length > 0 && (jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__setting-group", children: [jsxRuntimeExports.jsx("label", { children: "\uD488\uC9C8" }), jsxRuntimeExports.jsx("select", { value: currentQuality, onChange: (e) => handleQualityChange(e.target.value), children: qualities.map(quality => (jsxRuntimeExports.jsx("option", { value: quality.value, children: quality.label }, quality.value))) })] })), subtitles.length > 0 && (jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__setting-group", children: [jsxRuntimeExports.jsx("label", { children: "\uC790\uB9C9" }), jsxRuntimeExports.jsxs("select", { value: currentSubtitle, onChange: (e) => handleSubtitleChange(e.target.value), children: [jsxRuntimeExports.jsx("option", { value: "", children: "\uC790\uB9C9 \uC5C6\uC74C" }), subtitles.map(subtitle => (jsxRuntimeExports.jsx("option", { value: subtitle.label, children: subtitle.label }, subtitle.label)))] })] }))] }))] }))] }), jsxRuntimeExports.jsx("div", { className: "designbase-video-player__center-controls", children: jsxRuntimeExports.jsx("button", { className: "designbase-video-player__play-button", onClick: togglePlay, children: isPlaying ? jsxRuntimeExports.jsx(PauseIcon, { size: 48 }) : jsxRuntimeExports.jsx(PlayIcon, { size: 48 }) }) }), jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__bottom-controls", children: [showProgress && (jsxRuntimeExports.jsxs("div", { ref: progressRef, className: "designbase-video-player__progress", onClick: handleProgressChange, children: [jsxRuntimeExports.jsx("div", { className: "designbase-video-player__progress-bar", style: { width: `${progressPercentage}%` } }), jsxRuntimeExports.jsx("div", { className: "designbase-video-player__progress-handle", style: { left: `${progressPercentage}%` } })] })), jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__controls", children: [jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__left-controls", children: [jsxRuntimeExports.jsx("button", { className: "designbase-video-player__control-button", onClick: togglePlay, children: isPlaying ? jsxRuntimeExports.jsx(PauseIcon, { size: 20 }) : jsxRuntimeExports.jsx(PlayIcon, { size: 20 }) }), showVolume && (jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__volume", children: [jsxRuntimeExports.jsx("button", { className: "designbase-video-player__control-button", onClick: toggleMute, onMouseEnter: () => setShowVolumeSlider(true), onMouseLeave: () => setShowVolumeSlider(false), children: isMuted || volume === 0 ? jsxRuntimeExports.jsx(MuteFilledIcon, { size: 20 }) : jsxRuntimeExports.jsx(VolumeUpIcon, { size: 20 }) }), showVolumeSlider && (jsxRuntimeExports.jsx("div", { ref: volumeRef, className: "designbase-video-player__volume-slider", children: jsxRuntimeExports.jsx("input", { type: "range", min: "0", max: "1", step: "0.1", value: volume, onChange: handleVolumeSliderChange }) }))] })), showTime && (jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__time", children: [jsxRuntimeExports.jsx("span", { children: formatTime(currentTime) }), jsxRuntimeExports.jsx("span", { children: "/" }), jsxRuntimeExports.jsx("span", { children: formatTime(duration) })] }))] }), jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__right-controls", children: [playlist.length > 1 && (jsxRuntimeExports.jsxs("div", { className: "designbase-video-player__playlist-controls", children: [jsxRuntimeExports.jsx("button", { className: "designbase-video-player__control-button", disabled: playlistIndex === 0, onClick: () => handlePlaylistChange(playlistIndex - 1), children: jsxRuntimeExports.jsx(ChevronLeftIcon, { size: 16 }) }), jsxRuntimeExports.jsxs("span", { children: [playlistIndex + 1, " / ", playlist.length] }), jsxRuntimeExports.jsx("button", { className: "designbase-video-player__control-button", disabled: playlistIndex === playlist.length - 1, onClick: () => handlePlaylistChange(playlistIndex + 1), children: jsxRuntimeExports.jsx(ChevronRightIcon, { size: 16 }) })] })), enableFullscreen && (jsxRuntimeExports.jsx("button", { className: "designbase-video-player__control-button", onClick: toggleFullscreen, children: isFullscreen ? jsxRuntimeExports.jsx(ShrinkIcon, { size: 20 }) : jsxRuntimeExports.jsx(ExpandIcon, { size: 20 }) }))] })] })] })] }))] }));
7718
7813
  };
7719
7814
 
7720
7815
  const AudioPlayer = ({ src, title, artist, album, albumArt, size = 'm', variant = 'default', theme = 'auto', autoPlay = false, loop = false, muted = false, showControls = true, enableKeyboard = true, showProgress = true, showTime = true, showVolume = true, showSettings = false, playlist = [], currentIndex = 0, autoPause = true, playbackRates = [0.5, 0.75, 1, 1.25, 1.5, 2], defaultPlaybackRate = 1, repeatMode = 'none', shuffle = false, onPlay, onPause, onEnded, onTimeUpdate, onVolumeChange, onPlaylistChange, onPlaybackRateChange, onRepeatModeChange, onShuffleChange, onError, className, }) => {
@@ -8443,7 +8538,7 @@ const Toolbar = ({ items, size = 'm', variant = 'default', position = 'top', ful
8443
8538
  return (jsxRuntimeExports.jsxs("div", { className: clsx('designbase-toolbar__item', 'designbase-toolbar__item--dropdown', {
8444
8539
  'designbase-toolbar__item--open': openDropdown === item.id,
8445
8540
  'designbase-toolbar__item--disabled': item.disabled,
8446
- }), children: [jsxRuntimeExports.jsxs("button", { className: "designbase-toolbar__dropdown-trigger", 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 })), jsxRuntimeExports.jsx("span", { className: "designbase-toolbar__dropdown-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" }) }) })] }), openDropdown === item.id && item.options && (jsxRuntimeExports.jsx("div", { className: "designbase-toolbar__dropdown-menu", children: item.options.map((option) => (jsxRuntimeExports.jsxs("button", { className: clsx('designbase-toolbar__dropdown-option', {
8541
+ }), children: [jsxRuntimeExports.jsxs("button", { className: "designbase-toolbar__dropdown-trigger", 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 })), jsxRuntimeExports.jsx("span", { className: "designbase-toolbar__dropdown-arrow", children: jsxRuntimeExports.jsx(ChevronDownIcon, { size: 12 }) })] }), openDropdown === item.id && item.options && (jsxRuntimeExports.jsx("div", { className: "designbase-toolbar__dropdown-menu", children: item.options.map((option) => (jsxRuntimeExports.jsxs("button", { className: clsx('designbase-toolbar__dropdown-option', {
8447
8542
  'designbase-toolbar__dropdown-option--selected': item.selectedValue === option.value,
8448
8543
  'designbase-toolbar__dropdown-option--disabled': option.disabled,
8449
8544
  }), onClick: () => handleOptionClick(item, option.value), disabled: option.disabled, children: [option.icon && (jsxRuntimeExports.jsx("span", { className: "designbase-toolbar__dropdown-option-icon", children: option.icon })), jsxRuntimeExports.jsx("span", { className: "designbase-toolbar__dropdown-option-label", children: option.label })] }, option.value))) }))] }, item.id));
@@ -11801,8 +11896,26 @@ const ResizablePanels = ({ children, initialWidth = '100%', initialHeight = '100
11801
11896
  return { ...baseStyle, bottom: 0, right: 0 };
11802
11897
  }
11803
11898
  }, [handlePosition, handleSize, handleColor, direction]);
11804
- // 기본 리사이즈 핸들 아이콘
11805
- const defaultHandleIcon = (jsxRuntimeExports.jsxs("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "currentColor", children: [jsxRuntimeExports.jsx("path", { d: "M12 12H8L10 10L12 12Z" }), jsxRuntimeExports.jsx("path", { d: "M8 8H4L6 6L8 8Z" }), jsxRuntimeExports.jsx("path", { d: "M4 4H0L2 2L4 4Z" })] }));
11899
+ // 방향에 따른 기본 리사이즈 핸들 아이콘
11900
+ const getDefaultHandleIcon = () => {
11901
+ const iconSize = Math.max(12, handleSize - 4);
11902
+ switch (handlePosition) {
11903
+ case 'bottom-right':
11904
+ return jsxRuntimeExports.jsx(ArrowDownRightIcon, { size: iconSize });
11905
+ case 'bottom-left':
11906
+ return jsxRuntimeExports.jsx(ArrowDownLeftIcon, { size: iconSize });
11907
+ case 'top-right':
11908
+ return jsxRuntimeExports.jsx(ArrowUpRightIcon, { size: iconSize });
11909
+ case 'top-left':
11910
+ return jsxRuntimeExports.jsx(ArrowUpLeftIcon, { size: iconSize });
11911
+ case 'right':
11912
+ return jsxRuntimeExports.jsx(ArrowRightIcon, { size: iconSize });
11913
+ case 'bottom':
11914
+ return jsxRuntimeExports.jsx(ArrowDownIcon, { size: iconSize });
11915
+ default:
11916
+ return direction === 'both' ? jsxRuntimeExports.jsx(MoveIcon, { size: iconSize }) : jsxRuntimeExports.jsx(ExpandIcon, { size: iconSize });
11917
+ }
11918
+ };
11806
11919
  const classes = classNames('designbase-resizable-panels', {
11807
11920
  'designbase-resizable-panels--resizing': isResizing,
11808
11921
  'designbase-resizable-panels--border': showBorder,
@@ -11820,7 +11933,7 @@ const ResizablePanels = ({ children, initialWidth = '100%', initialHeight = '100
11820
11933
  }, onMouseLeave: (e) => {
11821
11934
  const target = e.currentTarget;
11822
11935
  target.style.backgroundColor = handleColor;
11823
- }, children: handleIcon || defaultHandleIcon })] }));
11936
+ }, children: handleIcon || getDefaultHandleIcon() })] }));
11824
11937
  };
11825
11938
 
11826
11939
  /**