@designbasekorea/ui 0.5.4 → 0.5.6

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.js CHANGED
@@ -4453,7 +4453,7 @@ const Checkbox = React.forwardRef(({ isSelected, defaultSelected, isIndeterminat
4453
4453
  });
4454
4454
  Checkbox.displayName = 'Checkbox';
4455
4455
 
4456
- const SearchBar = ({ value, defaultValue = '', placeholder = '검색...', size = 'm', variant = 'default', disabled = false, readOnly = false, fullWidth = false, searchIcon: SearchIconComponent = icons.SearchIcon, clearIcon: ClearIconComponent = icons.CloseIcon, enableRecentSearches = false, recentSearchesKey = 'searchbar-recent-searches', suggestedSearches = [], suggestionRollingInterval = 5000, onChange, onSearch, onFocus, onBlur, onKeyDown, className, ...rest }) => {
4456
+ const SearchBar = ({ value, defaultValue = '', placeholder = '검색...', size = 'm', variant = 'default', disabled = false, readOnly = false, fullWidth = false, searchIcon: SearchIconComponent = icons.SearchIcon, clearIcon: ClearIconComponent = icons.CloseIcon, enableRecentSearches = false, recentSearchesKey = 'searchbar-recent-searches', suggestedSearches = [], suggestionRollingInterval = 5000, onChange, onSearch, onFocus, onBlur, onKeyDown, showShortcut = false, shortcutLabel = '⌘K', className, ...rest }) => {
4457
4457
  const [internalValue, setInternalValue] = React.useState(defaultValue);
4458
4458
  const [recentSearches, setRecentSearches] = React.useState([]);
4459
4459
  const [showRecentSearches, setShowRecentSearches] = React.useState(false);
@@ -4598,7 +4598,8 @@ const SearchBar = ({ value, defaultValue = '', placeholder = '검색...', size =
4598
4598
  const currentPlaceholder = suggestedSearches.length > 0 && !currentValue && currentValue === ''
4599
4599
  ? suggestedSearches[currentSuggestion]
4600
4600
  : placeholder;
4601
- return (jsxRuntime.jsxs("div", { className: classes, role: "search", children: [jsxRuntime.jsxs("div", { className: "designbase-search-bar__container", children: [jsxRuntime.jsx("div", { className: "designbase-search-bar__search-icon", children: jsxRuntime.jsx(SearchIconComponent, { size: size === 's' ? 16 : size === 'l' ? 24 : 20 }) }), jsxRuntime.jsx("input", { ref: inputRef, type: "text", className: inputClasses, value: currentValue, placeholder: currentPlaceholder, disabled: disabled, readOnly: readOnly, onChange: handleChange, onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown, "aria-label": "\uAC80\uC0C9\uC5B4 \uC785\uB825", ...rest }), currentValue && currentValue.length > 0 && !disabled && !readOnly && (jsxRuntime.jsx("button", { type: "button", className: "designbase-search-bar__clear-button", onClick: handleClear, "aria-label": "\uAC80\uC0C9\uC5B4 \uC9C0\uC6B0\uAE30", children: jsxRuntime.jsx(ClearIconComponent, { size: size === 's' ? 16 : size === 'l' ? 24 : 20 }) }))] }), showRecentSearches && recentSearches.length > 0 && (jsxRuntime.jsxs("div", { className: "designbase-search-bar__recent-searches", children: [jsxRuntime.jsxs("div", { className: "designbase-search-bar__recent-header", children: [jsxRuntime.jsx("span", { className: "designbase-search-bar__recent-title", children: "\uCD5C\uADFC \uAC80\uC0C9\uC5B4" }), jsxRuntime.jsx("button", { type: "button", className: "designbase-search-bar__clear-all-button", onClick: handleClearAllRecentSearches, "aria-label": "\uBAA8\uB4E0 \uCD5C\uADFC \uAC80\uC0C9\uC5B4 \uC0AD\uC81C", children: "\uC804\uCCB4 \uC0AD\uC81C" })] }), jsxRuntime.jsx("div", { className: "designbase-search-bar__recent-list", children: recentSearches.map((searchTerm, index) => (jsxRuntime.jsxs("div", { className: "designbase-search-bar__recent-item", children: [jsxRuntime.jsx("button", { type: "button", className: "designbase-search-bar__recent-search-button", onClick: () => handleRecentSearchClick(searchTerm), children: searchTerm }), jsxRuntime.jsx("button", { type: "button", className: "designbase-search-bar__recent-remove-button", onClick: () => handleRemoveRecentSearch(searchTerm), "aria-label": `${searchTerm} 삭제`, children: jsxRuntime.jsx(icons.CloseIcon, { size: 16 }) })] }, index))) })] })), suggestedSearches.length > 0 && isFocused && !currentValue && (jsxRuntime.jsxs("div", { className: "designbase-search-bar__suggestions", children: [jsxRuntime.jsx("div", { className: "designbase-search-bar__suggestions-header", children: jsxRuntime.jsx("span", { className: "designbase-search-bar__suggestions-title", children: "\uCD94\uCC9C \uAC80\uC0C9\uC5B4" }) }), jsxRuntime.jsx("div", { className: "designbase-search-bar__suggestions-list", children: suggestedSearches.map((suggestion, index) => (jsxRuntime.jsx("button", { type: "button", className: clsx('designbase-search-bar__suggestion-item', {
4601
+ const shortcutBadgeSize = size === 'l' ? 'm' : 's';
4602
+ return (jsxRuntime.jsxs("div", { className: classes, role: "search", children: [jsxRuntime.jsxs("div", { className: "designbase-search-bar__container", children: [jsxRuntime.jsx("div", { className: "designbase-search-bar__search-icon", children: jsxRuntime.jsx(SearchIconComponent, { size: size === 's' ? 16 : size === 'l' ? 24 : 20 }) }), jsxRuntime.jsx("input", { ref: inputRef, type: "text", className: inputClasses, value: currentValue, placeholder: currentPlaceholder, disabled: disabled, readOnly: readOnly, onChange: handleChange, onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown, "aria-label": "\uAC80\uC0C9\uC5B4 \uC785\uB825", ...rest }), showShortcut && !currentValue && !disabled && !readOnly && (jsxRuntime.jsx(Badge, { className: "designbase-search-bar__shortcut-badge", size: shortcutBadgeSize, variant: "secondary", style: "text", "aria-hidden": "true", children: shortcutLabel })), currentValue && currentValue.length > 0 && !disabled && !readOnly && (jsxRuntime.jsx("button", { type: "button", className: "designbase-search-bar__clear-button", onClick: handleClear, "aria-label": "\uAC80\uC0C9\uC5B4 \uC9C0\uC6B0\uAE30", children: jsxRuntime.jsx(ClearIconComponent, { size: size === 's' ? 16 : size === 'l' ? 24 : 20 }) }))] }), showRecentSearches && recentSearches.length > 0 && (jsxRuntime.jsxs("div", { className: "designbase-search-bar__recent-searches", children: [jsxRuntime.jsxs("div", { className: "designbase-search-bar__recent-header", children: [jsxRuntime.jsx("span", { className: "designbase-search-bar__recent-title", children: "\uCD5C\uADFC \uAC80\uC0C9\uC5B4" }), jsxRuntime.jsx("button", { type: "button", className: "designbase-search-bar__clear-all-button", onClick: handleClearAllRecentSearches, "aria-label": "\uBAA8\uB4E0 \uCD5C\uADFC \uAC80\uC0C9\uC5B4 \uC0AD\uC81C", children: "\uC804\uCCB4 \uC0AD\uC81C" })] }), jsxRuntime.jsx("div", { className: "designbase-search-bar__recent-list", children: recentSearches.map((searchTerm, index) => (jsxRuntime.jsxs("div", { className: "designbase-search-bar__recent-item", children: [jsxRuntime.jsx("button", { type: "button", className: "designbase-search-bar__recent-search-button", onClick: () => handleRecentSearchClick(searchTerm), children: searchTerm }), jsxRuntime.jsx("button", { type: "button", className: "designbase-search-bar__recent-remove-button", onClick: () => handleRemoveRecentSearch(searchTerm), "aria-label": `${searchTerm} 삭제`, children: jsxRuntime.jsx(icons.CloseIcon, { size: 16 }) })] }, index))) })] })), suggestedSearches.length > 0 && isFocused && !currentValue && (jsxRuntime.jsxs("div", { className: "designbase-search-bar__suggestions", children: [jsxRuntime.jsx("div", { className: "designbase-search-bar__suggestions-header", children: jsxRuntime.jsx("span", { className: "designbase-search-bar__suggestions-title", children: "\uCD94\uCC9C \uAC80\uC0C9\uC5B4" }) }), jsxRuntime.jsx("div", { className: "designbase-search-bar__suggestions-list", children: suggestedSearches.map((suggestion, index) => (jsxRuntime.jsx("button", { type: "button", className: clsx('designbase-search-bar__suggestion-item', {
4602
4603
  'designbase-search-bar__suggestion-item--active': index === currentSuggestion
4603
4604
  }), onClick: () => handleSuggestionClick(suggestion), children: suggestion }, index))) })] }))] }));
4604
4605
  };
@@ -8207,7 +8208,7 @@ const HeroFeature = ({ title, subtitle, description, image, imageAlt, background
8207
8208
  const renderButtons = () => {
8208
8209
  if (buttons.length === 0)
8209
8210
  return null;
8210
- return (jsxRuntime.jsx("div", { className: "designbase-hero-feature__buttons", children: buttons.map((button, index) => (jsxRuntime.jsx(Button, { href: button.href, variant: button.variant || 'primary', size: button.size || 'md', onClick: button.onClick, target: button.external ? '_blank' : undefined, rel: button.external ? 'noopener noreferrer' : undefined, icon: button.icon, children: button.text }, index))) }));
8211
+ return (jsxRuntime.jsx("div", { className: "designbase-hero-feature__buttons", children: buttons.map((button, index) => (jsxRuntime.jsx(Button, { href: button.href, variant: button.variant || 'primary', size: button.size || 'm', onClick: button.onClick, target: button.external ? '_blank' : undefined, rel: button.external ? 'noopener noreferrer' : undefined, icon: button.icon, children: button.text }, index))) }));
8211
8212
  };
8212
8213
  const renderStats = () => {
8213
8214
  if (stats.length === 0)
@@ -8598,12 +8599,11 @@ const ImageList = ({ images, layout = 'grid', columns = 3, spacing = 'm', ratio
8598
8599
  })), currentIndex: selectedImageIndex, isOpen: true, onOpenChange: handleLightboxClose, onImageChange: handleLightboxNavigate }))] }));
8599
8600
  };
8600
8601
 
8601
- const List = ({ items, size = 'm', variant = 'default', itemType = 'default', layout = 'vertical', selectable = false, multiple = false, selectedItems = [], draggable = false, spacing = 'm', alignment = 'start', onItemClick, onItemSelect, onItemDrag, emptyState, loading = false, loadingCount = 3, className, }) => {
8602
+ const List = ({ items, size = 'm', variant = 'navigation', itemType = 'default', layout = 'vertical', selectable = false, multiple = false, selectedItems = [], draggable = false, spacing = 's', alignment = 'start', showLeadingIcon = true, showDescription = false, showMeta = false, showBadge = true, showArrow = true, clickableArea = 'item', onItemClick, onItemSelect, onItemDrag, emptyState, loading = false, loadingCount = 3, className, }) => {
8602
8603
  const [internalSelectedItems, setInternalSelectedItems] = React.useState(selectedItems);
8603
8604
  const [draggedItem, setDraggedItem] = React.useState(null);
8604
- // 아이콘 크기 계산 (m이 기본값)
8605
8605
  const iconSize = size === 's' ? 16 : size === 'l' ? 24 : 20;
8606
- // 외부에서 선택된 아이템이 변경되면 내부 상태 업데이트
8606
+ const shouldShowArrow = showArrow;
8607
8607
  React.useEffect(() => {
8608
8608
  setInternalSelectedItems(selectedItems);
8609
8609
  }, [selectedItems]);
@@ -8614,7 +8614,7 @@ const List = ({ items, size = 'm', variant = 'default', itemType = 'default', la
8614
8614
  let newSelectedItems;
8615
8615
  if (multiple) {
8616
8616
  if (internalSelectedItems.includes(item.id)) {
8617
- newSelectedItems = internalSelectedItems.filter(id => id !== item.id);
8617
+ newSelectedItems = internalSelectedItems.filter((id) => id !== item.id);
8618
8618
  }
8619
8619
  else {
8620
8620
  newSelectedItems = [...internalSelectedItems, item.id];
@@ -8628,6 +8628,12 @@ const List = ({ items, size = 'm', variant = 'default', itemType = 'default', la
8628
8628
  }
8629
8629
  onItemClick?.(item, index);
8630
8630
  };
8631
+ const handleItemKeyDown = (e, item, index) => {
8632
+ if (e.key === 'Enter' || e.key === ' ') {
8633
+ e.preventDefault();
8634
+ handleItemClick(item, index);
8635
+ }
8636
+ };
8631
8637
  const handleItemDragStart = (e, itemId) => {
8632
8638
  if (!draggable)
8633
8639
  return;
@@ -8650,9 +8656,7 @@ const List = ({ items, size = 'm', variant = 'default', itemType = 'default', la
8650
8656
  const handleItemDragEnd = () => {
8651
8657
  setDraggedItem(null);
8652
8658
  };
8653
- const isItemSelected = (itemId) => {
8654
- return internalSelectedItems.includes(itemId);
8655
- };
8659
+ const isItemSelected = (itemId) => internalSelectedItems.includes(itemId);
8656
8660
  const renderBadge = (badge) => {
8657
8661
  if (!badge)
8658
8662
  return null;
@@ -8671,17 +8675,38 @@ const List = ({ items, size = 'm', variant = 'default', itemType = 'default', la
8671
8675
  const renderItem = (item, index) => {
8672
8676
  const isSelected = isItemSelected(item.id);
8673
8677
  const isDragging = draggedItem === item.id;
8678
+ const isClickable = Boolean(onItemClick || selectable);
8679
+ const hasDescription = showDescription && Boolean(item.description);
8680
+ const hasMeta = showMeta && Boolean(item.meta?.length);
8681
+ const hasSecondaryInfo = hasDescription || hasMeta;
8682
+ const hasLeadingVisual = Boolean(item.image || (showLeadingIcon && item.icon));
8683
+ const hasTrailing = Boolean((showBadge && item.badge) ||
8684
+ item.actions?.length ||
8685
+ shouldShowArrow ||
8686
+ draggable);
8674
8687
  const itemClasses = clsx('designbase-list__item', `designbase-list__item--size-${size}`, `designbase-list__item--variant-${variant}`, `designbase-list__item--type-${itemType}`, `designbase-list__item--layout-${layout}`, `designbase-list__item--spacing-${spacing}`, `designbase-list__item--alignment-${alignment}`, {
8675
8688
  'designbase-list__item--selected': isSelected,
8676
8689
  'designbase-list__item--disabled': item.disabled,
8677
8690
  'designbase-list__item--dragging': isDragging,
8678
- 'designbase-list__item--clickable': onItemClick || selectable,
8691
+ 'designbase-list__item--clickable': isClickable,
8679
8692
  'designbase-list__item--draggable': draggable,
8693
+ 'designbase-list__item--with-leading': hasLeadingVisual,
8694
+ 'designbase-list__item--with-secondary': hasSecondaryInfo,
8695
+ 'designbase-list__item--with-trailing': hasTrailing,
8680
8696
  });
8681
- return (jsxRuntime.jsxs("div", { className: itemClasses, onClick: () => handleItemClick(item, index), draggable: draggable, onDragStart: (e) => handleItemDragStart(e, item.id), onDragOver: handleItemDragOver, onDrop: (e) => handleItemDrop(e, item.id), onDragEnd: handleItemDragEnd, role: selectable ? 'button' : undefined, tabIndex: selectable ? 0 : undefined, "aria-selected": selectable ? isSelected : undefined, children: [selectable && (jsxRuntime.jsx("div", { className: "designbase-list__item-checkbox", children: jsxRuntime.jsx("input", { type: multiple ? 'checkbox' : 'radio', checked: isSelected, onChange: () => handleItemClick(item, index), disabled: item.disabled }) })), item.icon && (jsxRuntime.jsx("div", { className: "designbase-list__item-icon", children: React.isValidElement(item.icon) ? (React.cloneElement(item.icon, {
8682
- size: iconSize,
8683
- color: 'currentColor'
8684
- })) : item.icon })), item.image && (jsxRuntime.jsx("div", { className: "designbase-list__item-image", children: jsxRuntime.jsx("img", { src: item.image, alt: item.title }) })), jsxRuntime.jsxs("div", { className: "designbase-list__item-content", children: [jsxRuntime.jsxs("div", { className: "designbase-list__item-header", children: [jsxRuntime.jsx("h4", { className: "designbase-list__item-title", children: item.title }), item.badge && renderBadge(item.badge)] }), item.description && (jsxRuntime.jsx("p", { className: "designbase-list__item-description", children: item.description })), item.meta && renderMeta(item.meta)] }), item.actions && renderActions(item.actions), draggable && (jsxRuntime.jsx("div", { className: "designbase-list__item-drag-handle", children: "\u22EE\u22EE" }))] }, item.id));
8697
+ const itemIsInteractive = clickableArea === 'item' && isClickable && !item.disabled;
8698
+ const contentIsInteractive = clickableArea === 'content' && isClickable && !item.disabled;
8699
+ return (jsxRuntime.jsxs("div", { className: itemClasses, onClick: itemIsInteractive ? () => handleItemClick(item, index) : undefined, onKeyDown: itemIsInteractive ? (e) => handleItemKeyDown(e, item, index) : undefined, draggable: draggable, onDragStart: (e) => handleItemDragStart(e, item.id), onDragOver: handleItemDragOver, onDrop: (e) => handleItemDrop(e, item.id), onDragEnd: handleItemDragEnd, role: itemIsInteractive ? 'button' : undefined, tabIndex: itemIsInteractive ? 0 : undefined, "aria-selected": selectable ? isSelected : undefined, children: [selectable && (jsxRuntime.jsx("div", { className: "designbase-list__item-checkbox", onClick: (e) => e.stopPropagation(), children: jsxRuntime.jsx("input", { type: multiple ? 'checkbox' : 'radio', checked: isSelected, onChange: (e) => {
8700
+ e.stopPropagation();
8701
+ handleItemClick(item, index);
8702
+ }, disabled: item.disabled }) })), showLeadingIcon && item.icon && !item.image && (jsxRuntime.jsx("div", { className: "designbase-list__item-icon", children: React.isValidElement(item.icon)
8703
+ ? React.cloneElement(item.icon, {
8704
+ size: iconSize,
8705
+ color: 'currentColor',
8706
+ })
8707
+ : item.icon })), item.image && (jsxRuntime.jsx("div", { className: "designbase-list__item-image", children: jsxRuntime.jsx("img", { src: item.image, alt: item.title }) })), jsxRuntime.jsxs("div", { className: clsx('designbase-list__item-content', {
8708
+ 'designbase-list__item-content--clickable': contentIsInteractive,
8709
+ }), onClick: contentIsInteractive ? () => handleItemClick(item, index) : undefined, onKeyDown: contentIsInteractive ? (e) => handleItemKeyDown(e, item, index) : undefined, role: contentIsInteractive ? 'button' : undefined, tabIndex: contentIsInteractive ? 0 : undefined, children: [jsxRuntime.jsx("div", { className: "designbase-list__item-header", children: jsxRuntime.jsx("h4", { className: "designbase-list__item-title", children: item.title }) }), hasDescription && jsxRuntime.jsx("p", { className: "designbase-list__item-description", children: item.description }), hasMeta && renderMeta(item.meta)] }), (showBadge && item.badge) || item.actions || shouldShowArrow || draggable ? (jsxRuntime.jsxs("div", { className: "designbase-list__item-trailing", children: [showBadge && item.badge && renderBadge(item.badge), item.actions && renderActions(item.actions), shouldShowArrow && (jsxRuntime.jsx("div", { className: "designbase-list__item-arrow", "aria-hidden": "true", children: jsxRuntime.jsx(icons.ChevronRightIcon, { size: 16 }) })), draggable && jsxRuntime.jsx("div", { className: "designbase-list__item-drag-handle", children: "\u22EE\u22EE" })] })) : null] }, item.id));
8685
8710
  };
8686
8711
  const renderLoadingSkeleton = () => {
8687
8712
  return Array.from({ length: loadingCount }, (_, index) => (jsxRuntime.jsx("div", { className: "designbase-list__item designbase-list__item--loading", children: jsxRuntime.jsxs("div", { className: "designbase-list__item-skeleton", children: [jsxRuntime.jsx("div", { className: "designbase-list__item-skeleton-icon" }), jsxRuntime.jsxs("div", { className: "designbase-list__item-skeleton-content", children: [jsxRuntime.jsx("div", { className: "designbase-list__item-skeleton-title" }), jsxRuntime.jsx("div", { className: "designbase-list__item-skeleton-description" })] })] }) }, index)));
@@ -8693,12 +8718,12 @@ const List = ({ items, size = 'm', variant = 'default', itemType = 'default', la
8693
8718
  'designbase-list--loading': loading,
8694
8719
  }, className);
8695
8720
  if (loading) {
8696
- return (jsxRuntime.jsx("div", { className: classes, children: renderLoadingSkeleton() }));
8721
+ return jsxRuntime.jsx("div", { className: classes, children: renderLoadingSkeleton() });
8697
8722
  }
8698
8723
  if (items.length === 0 && emptyState) {
8699
8724
  return (jsxRuntime.jsx("div", { className: classes, children: jsxRuntime.jsx("div", { className: "designbase-list__empty", children: emptyState }) }));
8700
8725
  }
8701
- return (jsxRuntime.jsx("div", { className: classes, children: items.map((item, index) => renderItem(item, index)) }));
8726
+ return jsxRuntime.jsx("div", { className: classes, children: items.map((item, index) => renderItem(item, index)) });
8702
8727
  };
8703
8728
 
8704
8729
  const DesignBaseLogo = ({ width = 193, height = 40, color, className, }) => {
@@ -8786,6 +8811,77 @@ const Logo = ({ type = 'designbase', text = 'Logo', src, alt, size = 'm', varian
8786
8811
  };
8787
8812
  Logo.displayName = 'Logo';
8788
8813
 
8814
+ const Marquee = ({ children, items, direction = 'left', duration = 20, speed = 1, size = 'm', variant = 'default', bordered, pauseOnHover = true, fadeEdges = false, fadeWidth = 48, edgePadding, gap = 24, alignY = 'center', className, ariaLabel = 'Scrolling content', }) => {
8815
+ const containerRef = React.useRef(null);
8816
+ const baseGroupRef = React.useRef(null);
8817
+ const [containerWidth, setContainerWidth] = React.useState(0);
8818
+ const [baseGroupWidth, setBaseGroupWidth] = React.useState(0);
8819
+ const resolvedItems = React.useMemo(() => ((items && items.length > 0) ? items : (children ? [children] : [])), [items, children]);
8820
+ if (resolvedItems.length === 0) {
8821
+ return null;
8822
+ }
8823
+ const resolvedSpeed = Number.isFinite(speed) && speed > 0 ? speed : 1;
8824
+ const resolvedDuration = Math.max(duration, 1) / resolvedSpeed;
8825
+ const resolvedFadeWidth = Math.max(fadeWidth, 0);
8826
+ const resolvedEdgePadding = edgePadding !== undefined
8827
+ ? Math.max(edgePadding, 0)
8828
+ : (fadeEdges ? resolvedFadeWidth : 0);
8829
+ const resolvedGap = Math.max(gap, 0);
8830
+ React.useEffect(() => {
8831
+ const updateSize = () => {
8832
+ const nextContainerWidth = Math.ceil(containerRef.current?.clientWidth ?? 0);
8833
+ const nextBaseGroupWidth = Math.ceil(baseGroupRef.current?.scrollWidth ?? 0);
8834
+ setContainerWidth((prev) => (prev !== nextContainerWidth ? nextContainerWidth : prev));
8835
+ setBaseGroupWidth((prev) => (prev !== nextBaseGroupWidth ? nextBaseGroupWidth : prev));
8836
+ };
8837
+ updateSize();
8838
+ const observer = new ResizeObserver(updateSize);
8839
+ if (containerRef.current)
8840
+ observer.observe(containerRef.current);
8841
+ if (baseGroupRef.current)
8842
+ observer.observe(baseGroupRef.current);
8843
+ const images = baseGroupRef.current?.querySelectorAll('img') ?? [];
8844
+ images.forEach((img) => {
8845
+ img.addEventListener('load', updateSize);
8846
+ img.addEventListener('error', updateSize);
8847
+ });
8848
+ return () => {
8849
+ observer.disconnect();
8850
+ images.forEach((img) => {
8851
+ img.removeEventListener('load', updateSize);
8852
+ img.removeEventListener('error', updateSize);
8853
+ });
8854
+ };
8855
+ }, [resolvedItems, resolvedGap, size, fadeEdges, resolvedEdgePadding]);
8856
+ const duplicateCount = React.useMemo(() => {
8857
+ if (baseGroupWidth <= 0)
8858
+ return 1;
8859
+ return Math.max(1, Math.ceil(containerWidth / baseGroupWidth));
8860
+ }, [containerWidth, baseGroupWidth]);
8861
+ const isReady = baseGroupWidth > 0;
8862
+ const classes = clsx('designbase-marquee', `designbase-marquee--size-${size}`, `designbase-marquee--variant-${variant}`, `designbase-marquee--align-${alignY}`, {
8863
+ 'designbase-marquee--pause-on-hover': pauseOnHover,
8864
+ 'designbase-marquee--fade-edges': fadeEdges,
8865
+ 'designbase-marquee--right': direction === 'right',
8866
+ 'designbase-marquee--bordered': bordered ?? variant === 'outlined',
8867
+ 'designbase-marquee--ready': isReady,
8868
+ }, className);
8869
+ const style = {
8870
+ '--db-marquee-duration': `${resolvedDuration}s`,
8871
+ // 그룹간 간격까지 포함해 이동 거리를 맞춰야 루프 경계 점프가 사라짐
8872
+ '--db-marquee-shift': `${baseGroupWidth + resolvedGap}px`,
8873
+ '--db-marquee-gap': `${resolvedGap}px`,
8874
+ '--db-marquee-fade-width': `${resolvedFadeWidth}px`,
8875
+ '--db-marquee-edge-padding': `${resolvedEdgePadding}px`,
8876
+ };
8877
+ const screenReaderText = (items && items.length > 0)
8878
+ ? items.filter((item) => typeof item === 'string').join(' • ') || ariaLabel
8879
+ : (typeof children === 'string' ? children : ariaLabel);
8880
+ const renderGroup = (key, ref) => (jsxRuntime.jsx("div", { className: "designbase-marquee__group", "aria-hidden": "true", ref: ref, children: resolvedItems.map((item, index) => (jsxRuntime.jsx("div", { className: "designbase-marquee__item", children: item }, `${key}-${index}`))) }, key));
8881
+ return (jsxRuntime.jsxs("div", { className: classes, style: style, role: "region", "aria-label": ariaLabel, ref: containerRef, children: [jsxRuntime.jsx("span", { className: "designbase-marquee__sr-only", children: screenReaderText }), jsxRuntime.jsxs("div", { className: "designbase-marquee__track", children: [renderGroup('base', baseGroupRef), Array.from({ length: duplicateCount }).map((_, index) => renderGroup(`dup-${index}`))] })] }));
8882
+ };
8883
+ Marquee.displayName = 'Marquee';
8884
+
8789
8885
  const MarkdownEditor = ({ size = 'm', variant = 'default', mode = 'preview', // 기본값을 preview로 변경
8790
8886
  theme = 'light', value = '', placeholder = '마크다운을 입력하세요...', minHeight = 200, maxHeight = 600, autoHeight = false, readonly = false, disabled = false, showToolbar = true, toolbarItems = [], showStatus = true, showWordCount = true, showLineCount = true, autoSave = false, autoSaveInterval = 30000, // 30초
8791
8887
  onFileUpload, onChange, onSave, onFocus, onBlur, className, }) => {
@@ -10835,14 +10931,34 @@ const ScrollArea = ({ direction = 'vertical', scrollbarStyle = 'auto', maxHeight
10835
10931
  return (jsxRuntime.jsx("div", { className: classes, style: style, children: jsxRuntime.jsx("div", { ref: scrollRef, className: "designbase-scroll-area__content", children: children }) }));
10836
10932
  };
10837
10933
 
10838
- const Section = ({ title, subtitle, description, size = 'm', variant = 'default', header, footer, actions, noPadding = false, fullWidth = false, fullHeight = false, className, children, }) => {
10934
+ const Section = ({ title, subtitle, description = '', size = 'm', variant = 'default', header, footer, actions, noPadding = false, fullWidth = false, fullHeight = false, className, children, }) => {
10935
+ const normalizeActionButtonSize = (node) => {
10936
+ if (!React.isValidElement(node))
10937
+ return node;
10938
+ const element = node;
10939
+ const elementType = element.type;
10940
+ const isButtonComponent = elementType?.displayName === 'Button' || elementType?.name === 'Button';
10941
+ // Button이고 size가 지정되지 않았으면 기본값 m 적용
10942
+ if (isButtonComponent && element.props?.size === undefined) {
10943
+ return React.cloneElement(element, { size: 'm' });
10944
+ }
10945
+ if (element.props?.children) {
10946
+ return React.cloneElement(element, {
10947
+ children: React.Children.map(element.props.children, normalizeActionButtonSize),
10948
+ });
10949
+ }
10950
+ return element;
10951
+ };
10952
+ const normalizedActions = actions
10953
+ ? React.Children.map(actions, normalizeActionButtonSize)
10954
+ : actions;
10839
10955
  const classes = clsx('designbase-section', `designbase-section--size-${size}`, `designbase-section--variant-${variant}`, {
10840
10956
  'designbase-section--no-padding': noPadding,
10841
10957
  'designbase-section--full-width': fullWidth,
10842
10958
  'designbase-section--full-height': fullHeight,
10843
10959
  }, className);
10844
- const hasHeader = title || subtitle || description || header || actions;
10845
- return (jsxRuntime.jsxs("section", { className: classes, children: [hasHeader && (jsxRuntime.jsx("div", { className: "designbase-section__header", children: header || (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [(title || subtitle) && (jsxRuntime.jsxs("div", { className: "designbase-section__title-area", children: [title && (jsxRuntime.jsx("h2", { className: "designbase-section__title", children: title })), subtitle && (jsxRuntime.jsx("h3", { className: "designbase-section__subtitle", children: subtitle })), description && (jsxRuntime.jsx("p", { className: "designbase-section__description", children: description }))] })), actions && (jsxRuntime.jsx("div", { className: "designbase-section__actions", children: actions }))] })) })), jsxRuntime.jsx("div", { className: "designbase-section__content", children: children }), footer && (jsxRuntime.jsx("div", { className: "designbase-section__footer", children: footer }))] }));
10960
+ const hasHeader = title || subtitle || description || header || normalizedActions;
10961
+ return (jsxRuntime.jsxs("section", { className: classes, children: [hasHeader && (jsxRuntime.jsx("div", { className: "designbase-section__header", children: header || (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [(title || subtitle) && (jsxRuntime.jsxs("div", { className: "designbase-section__title-area", children: [title && (jsxRuntime.jsx("h2", { className: "designbase-section__title", children: title })), subtitle && (jsxRuntime.jsx("h3", { className: "designbase-section__subtitle", children: subtitle })), description && (jsxRuntime.jsx("p", { className: "designbase-section__description", children: description }))] })), normalizedActions && (jsxRuntime.jsx("div", { className: "designbase-section__actions", children: normalizedActions }))] })) })), jsxRuntime.jsx("div", { className: "designbase-section__content", children: children }), footer && (jsxRuntime.jsx("div", { className: "designbase-section__footer", children: footer }))] }));
10846
10962
  };
10847
10963
 
10848
10964
  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, }) => {
@@ -11095,11 +11211,11 @@ const Skeleton = React.forwardRef(({ variant = 'text', size = 'm', width, height
11095
11211
  switch (size) {
11096
11212
  case 'xs':
11097
11213
  return variant === 'circular' ? '16px' : '12px';
11098
- case 'sm':
11214
+ case 's':
11099
11215
  return variant === 'circular' ? '24px' : '16px';
11100
- case 'md':
11216
+ case 'm':
11101
11217
  return variant === 'circular' ? '32px' : '20px';
11102
- case 'lg':
11218
+ case 'l':
11103
11219
  return variant === 'circular' ? '48px' : '24px';
11104
11220
  case 'xl':
11105
11221
  return variant === 'circular' ? '64px' : '32px';
@@ -11111,9 +11227,9 @@ const Skeleton = React.forwardRef(({ variant = 'text', size = 'm', width, height
11111
11227
  const getTextLineHeight = () => {
11112
11228
  switch (size) {
11113
11229
  case 'xs': return '12px';
11114
- case 'sm': return '16px';
11115
- case 'md': return '20px';
11116
- case 'lg': return '24px';
11230
+ case 's': return '16px';
11231
+ case 'm': return '20px';
11232
+ case 'l': return '24px';
11117
11233
  case 'xl': return '32px';
11118
11234
  default: return '20px';
11119
11235
  }
@@ -12577,6 +12693,7 @@ exports.Lightbox = Lightbox;
12577
12693
  exports.List = List;
12578
12694
  exports.Logo = Logo;
12579
12695
  exports.MarkdownEditor = MarkdownEditor;
12696
+ exports.Marquee = Marquee;
12580
12697
  exports.Masonry = Masonry;
12581
12698
  exports.MenuItem = MenuItem;
12582
12699
  exports.Modal = Modal;