@designbasekorea/ui 0.5.1 → 0.5.4

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
@@ -2,6 +2,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import React, { useState, useCallback, useEffect, useRef, useMemo, useContext, useLayoutEffect, forwardRef, useId, createContext } from 'react';
3
3
  import { ChevronDownIcon, StarIcon, TrendingUpIcon, StarFilledIcon, CartIcon, CloseIcon, ArrowRightIcon, InfoFilledIcon, ErrorFilledIcon, WarningFilledIcon, CircleCheckFilledIcon, RefreshIcon, ChevronLeftIcon, PauseIcon, PlayIcon, ChevronRightIcon, RepeatIcon, MuteFilledIcon, VolumeUpIcon, SettingsIcon, UserIcon, HideIcon, ShowIcon, SearchIcon, ChevronUpIcon, GalleryIcon, HeartIcon, BookmarkIcon, ShareAltIcon, DownloadIcon, ArrowLeftIcon, ShrinkIcon, ExpandIcon, DoneIcon as DoneIcon$1, CopyIcon, EyedropperIcon, BulbIcon, CloudCloseIcon, BellActiveIcon, AwardIcon, CalendarIcon, PlusIcon, ErrorIcon, FileBlankIcon, ClockIcon, MinusIcon as MinusIcon$1, VideoIcon, CodeIcon, WriteIcon, UploadIcon, ArrowBarLeftIcon, ArrowBarRightIcon, StarHalfIcon, MoveIcon, MoreVerticalIcon, ArrowDownIcon, ArrowUpLeftIcon, ArrowUpRightIcon, ArrowDownLeftIcon, ArrowDownRightIcon, FacebookIcon, XIcon, InstagramIcon, LinkedinIcon, PinterestIcon, WhatsappIcon, TelegramIcon, MailIcon, LinkIcon, ScanQrcodeIcon, CaretUpdownFilledIcon, WarningIcon, InfoIcon } from '@designbasekorea/icons';
4
4
  import { flushSync, createPortal } from 'react-dom';
5
+ export { getTheme, setTheme, toggleTheme } from '@designbasekorea/theme';
5
6
 
6
7
  function r(e){var t,f,n="";if("string"==typeof e||"number"==typeof e)n+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=" "),n+=f);}else for(f in e)e[f]&&(n&&(n+=" "),n+=f);return n}function clsx(){for(var e,t,f=0,n="",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=" "),n+=t);return n}
7
8
 
@@ -2993,44 +2994,71 @@ const drawStars = (ctx, width, height, starsRef, options) => {
2993
2994
  };
2994
2995
  const drawWaves = (ctx, width, height, options) => {
2995
2996
  const { colors, offset, speedFactor } = options;
2996
- offset.current += 0.02 * speedFactor;
2997
+ offset.current += 0.005 * speedFactor; // Slower, smoother movement
2997
2998
  colors.forEach((color, i) => {
2999
+ const rgb = hexToRgb$1(color);
3000
+ const gradient = ctx.createLinearGradient(0, 0, 0, height);
3001
+ // Gradient from color to transparent for depth
3002
+ gradient.addColorStop(0, `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0)`);
3003
+ gradient.addColorStop(0.5, `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.5)`);
3004
+ gradient.addColorStop(1, `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.8)`);
3005
+ ctx.fillStyle = gradient;
2998
3006
  ctx.beginPath();
2999
3007
  ctx.moveTo(0, height);
3000
- const freq = 0.003 + i * 0.001;
3001
- const amp = 30 + i * 20;
3002
- const phase = offset.current + i * 2;
3003
- for (let x = 0; x <= width; x += 10) {
3004
- const y = height / 2 +
3005
- Math.sin(x * freq + phase) * amp +
3006
- Math.sin(x * 0.001 + phase * 0.5) * 50;
3008
+ // Organic wave generation
3009
+ for (let x = 0; x <= width; x += 5) {
3010
+ const freq1 = 0.002 + i * 0.0005;
3011
+ const freq2 = 0.005 + i * 0.001;
3012
+ const phase = offset.current + i * 1.5;
3013
+ // Combine two sine waves for more natural look
3014
+ const y1 = Math.sin(x * freq1 + phase) * (40 + i * 15);
3015
+ const y2 = Math.sin(x * freq2 + phase * 1.2) * (20 + i * 5);
3016
+ const y = height / 1.5 + y1 + y2 + (i * 30); // Base height + waves + layering offset
3007
3017
  ctx.lineTo(x, y);
3008
3018
  }
3009
3019
  ctx.lineTo(width, height);
3010
3020
  ctx.lineTo(0, height);
3011
3021
  ctx.closePath();
3012
- ctx.fillStyle = color;
3022
+ // Add subtle blur for "dreamy" effect
3023
+ // Note: filter can be expensive, use sparingly.
3024
+ // If performance is an issue, remove this toggle or reduce canvas size.
3025
+ // ctx.filter = 'blur(4px)';
3026
+ ctx.globalAlpha = 0.6; // Base transparency
3013
3027
  ctx.fill();
3028
+ ctx.globalAlpha = 1.0;
3029
+ // ctx.filter = 'none';
3014
3030
  });
3015
3031
  };
3016
3032
  const drawPulse = (ctx, width, height, options) => {
3017
3033
  const { colors, time, intensity } = options;
3018
3034
  const centerX = width / 2;
3019
3035
  const centerY = height / 2;
3020
- const maxRadius = Math.max(width, height) * 0.8;
3021
- const intensityMap = { subtle: 0.5, medium: 1, vivid: 1.5 };
3036
+ const maxRadius = Math.max(width, height) * 0.6; // Slightly reduced max radius to keep it contained
3037
+ const intensityMap = { subtle: 0.8, medium: 1.2, vivid: 1.8 }; // Increased base intensity
3022
3038
  const factor = intensityMap[intensity];
3023
3039
  colors.forEach((color, i) => {
3024
- const t = time * 0.02 * factor + (i * (Math.PI * 2)) / colors.length;
3025
- const radius = (Math.sin(t) * 0.2 + 0.5) * maxRadius;
3026
- const alpha = (Math.sin(t + Math.PI) * 0.2 + 0.3) * factor;
3040
+ // Offset time for each color layer
3041
+ const t = time * 0.002 * factor + (i * (Math.PI / 1.5));
3042
+ // Breathing effect for radius
3043
+ const radius = (Math.sin(t) * 0.15 + 0.6) * maxRadius;
3044
+ // Breathing effect for opacity
3045
+ const alpha = (Math.sin(t + Math.PI / 2) * 0.3 + 0.5) * factor; // Higher base alpha (0.2->0.8 range)
3046
+ // Clamp alpha max to 1
3047
+ const safeAlpha = Math.min(Math.max(alpha, 0), 1);
3048
+ if (safeAlpha <= 0.01)
3049
+ return;
3027
3050
  const gradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, radius);
3028
3051
  const rgb = hexToRgb$1(color);
3052
+ // Harder center, softer edge
3029
3053
  gradient.addColorStop(0, `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0)`);
3030
- gradient.addColorStop(0.5, `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha * 0.5})`);
3054
+ gradient.addColorStop(0.2, `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${safeAlpha * 0.2})`);
3055
+ gradient.addColorStop(0.6, `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${safeAlpha * 0.6})`);
3031
3056
  gradient.addColorStop(1, `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0)`);
3032
3057
  ctx.fillStyle = gradient;
3058
+ // Use globalCompositeOperation to blend nicely
3059
+ ctx.globalCompositeOperation = 'screen';
3033
3060
  ctx.fillRect(0, 0, width, height);
3061
+ ctx.globalCompositeOperation = 'source-over';
3034
3062
  });
3035
3063
  };
3036
3064
 
@@ -3322,63 +3350,132 @@ const AnimationBackground = ({ type = 'gradient', theme = 'dark', intensity = 's
3322
3350
  }, children: content }), showGrid && (jsx(GridOverlay, { size: gridSize, color: effectiveGridColor, opacity: gridOpacity })), children && (jsx("div", { className: "designbase-animation-background__content", style: { position: 'relative', zIndex: 10, width: '100%', height: '100%' }, children: children }))] }));
3323
3351
  };
3324
3352
 
3325
- const AnimationText = ({ children, type = 'fade', speed = 1000, repeat = 0, delay = 0, direction = 'left', size = 'm', color = 'primary', customColor, weight = 'normal', align = 'left', gradientColors = ['#667eea', '#764ba2'], waveColors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4', '#feca57'], glowColor = '#667eea', clickable = false, disabled = false, className, onClick, }) => {
3353
+ const AnimationText = ({ children, trigger = 'mount', type = 'fade', speed = 1000, repeat = 1, delay = 0, direction = 'left', size = 'm', color = 'primary', customColor, weight = 'normal', align = 'left', gradientColors = ['#667eea', '#764ba2'], waveColors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4', '#feca57'], glowColor = '#667eea', clickable = false, disabled = false, className, onClick, }) => {
3326
3354
  const [isVisible, setIsVisible] = useState(false);
3327
- const [currentIndex, setCurrentIndex] = useState(0);
3328
3355
  const [isAnimating, setIsAnimating] = useState(false);
3356
+ // Initialize with full text unless it's a mount trigger for typing/decode
3357
+ const [displayText, setDisplayText] = useState((type === 'decode' || type === 'typing') && trigger === 'mount' ? '' : children);
3358
+ const [currentRepeat, setCurrentRepeat] = useState(0);
3329
3359
  const textRef = useRef(null);
3330
- useRef(null);
3331
- // 타이핑 애니메이션을 위한 상태
3332
- const [displayText, setDisplayText] = useState('');
3360
+ const observerRef = useRef(null);
3361
+ // Initial trigger logic
3333
3362
  useEffect(() => {
3334
- if (delay > 0) {
3363
+ if (trigger === 'mount') {
3335
3364
  const timer = setTimeout(() => {
3336
3365
  setIsVisible(true);
3366
+ startAnimation();
3337
3367
  }, delay);
3338
3368
  return () => clearTimeout(timer);
3339
3369
  }
3340
- else {
3341
- setIsVisible(true);
3370
+ else if (trigger === 'in-view' && textRef.current) {
3371
+ observerRef.current = new IntersectionObserver((entries) => {
3372
+ if (entries[0].isIntersecting) {
3373
+ setTimeout(() => {
3374
+ setIsVisible(true);
3375
+ startAnimation();
3376
+ }, delay);
3377
+ observerRef.current?.disconnect();
3378
+ }
3379
+ });
3380
+ observerRef.current.observe(textRef.current);
3381
+ return () => observerRef.current?.disconnect();
3342
3382
  }
3343
- }, [delay]);
3344
- // 타이핑 애니메이션
3383
+ }, [trigger, delay]);
3384
+ // Update initial text visibility when props change
3345
3385
  useEffect(() => {
3346
- if (type === 'typing' && isVisible) {
3347
- setDisplayText('');
3348
- setCurrentIndex(0);
3349
- const typeInterval = setInterval(() => {
3350
- setCurrentIndex(prev => {
3351
- if (prev >= children.length) {
3352
- clearInterval(typeInterval);
3353
- return prev;
3354
- }
3355
- setDisplayText(children.slice(0, prev + 1));
3356
- return prev + 1;
3357
- });
3358
- }, speed / children.length);
3359
- return () => clearInterval(typeInterval);
3386
+ if ((type === 'typing' || type === 'decode') && trigger === 'mount') ;
3387
+ else {
3388
+ // For hover/click, ensure text is visible when not animating
3389
+ if (!isAnimating) {
3390
+ setDisplayText(children);
3391
+ }
3360
3392
  }
3361
- }, [type, isVisible, children, speed]);
3362
- // 반복 애니메이션
3363
- useEffect(() => {
3364
- if (repeat > 0 && type !== 'typing') {
3365
- let repeatCount = 0;
3366
- const repeatAnimation = () => {
3367
- setIsAnimating(true);
3368
- setTimeout(() => {
3393
+ }, [children, type, trigger]);
3394
+ const startAnimation = () => {
3395
+ setIsAnimating(true);
3396
+ setCurrentRepeat(0);
3397
+ if (type === 'typing') {
3398
+ startTyping(0);
3399
+ }
3400
+ else if (type === 'decode') {
3401
+ startDecoding(0);
3402
+ }
3403
+ };
3404
+ const startTyping = (iteration) => {
3405
+ setDisplayText('');
3406
+ let currentIndex = 0;
3407
+ const interval = setInterval(() => {
3408
+ if (currentIndex >= children.length) {
3409
+ clearInterval(interval);
3410
+ // Repetition logic
3411
+ if (repeat === 0 || iteration < repeat - 1) {
3412
+ setTimeout(() => {
3413
+ startTyping(iteration + 1);
3414
+ }, 500);
3415
+ }
3416
+ else {
3369
3417
  setIsAnimating(false);
3370
- repeatCount++;
3371
- if (repeatCount < repeat) {
3372
- setTimeout(repeatAnimation, speed);
3373
- }
3374
- }, speed);
3375
- };
3376
- if (isVisible) {
3377
- setTimeout(repeatAnimation, speed);
3418
+ }
3419
+ return;
3420
+ }
3421
+ setDisplayText(children.slice(0, currentIndex + 1));
3422
+ currentIndex++;
3423
+ }, speed / children.length);
3424
+ };
3425
+ const startDecoding = (iteration) => {
3426
+ const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+';
3427
+ let iterations = 0;
3428
+ const interval = setInterval(() => {
3429
+ setDisplayText(children.split('').map((char, index) => {
3430
+ if (index < iterations) {
3431
+ return children[index];
3432
+ }
3433
+ return characters[Math.floor(Math.random() * characters.length)];
3434
+ }).join(''));
3435
+ if (iterations >= children.length) {
3436
+ clearInterval(interval);
3437
+ setDisplayText(children);
3438
+ // Repetition logic
3439
+ if (repeat === 0 || iteration < repeat - 1) {
3440
+ setTimeout(() => {
3441
+ startDecoding(iteration + 1);
3442
+ }, 500);
3443
+ }
3444
+ else {
3445
+ setIsAnimating(false);
3446
+ }
3378
3447
  }
3448
+ iterations += 1 / 3;
3449
+ }, 30);
3450
+ };
3451
+ const handleMouseEnter = () => {
3452
+ if (trigger === 'hover') {
3453
+ setIsVisible(true);
3454
+ startAnimation();
3379
3455
  }
3380
- }, [repeat, type, speed, isVisible]);
3456
+ };
3457
+ const handleMouseLeave = () => {
3458
+ if (trigger === 'hover') {
3459
+ setIsVisible(false);
3460
+ setIsAnimating(false);
3461
+ if (type === 'typing' || type === 'decode') {
3462
+ setDisplayText(children);
3463
+ }
3464
+ }
3465
+ };
3381
3466
  const handleClick = () => {
3467
+ if (trigger === 'click') {
3468
+ setIsVisible(!isVisible);
3469
+ if (isVisible) {
3470
+ setIsAnimating(false);
3471
+ if (type === 'typing' || type === 'decode') {
3472
+ setDisplayText(children);
3473
+ }
3474
+ }
3475
+ else {
3476
+ startAnimation();
3477
+ }
3478
+ }
3382
3479
  if (clickable && !disabled && onClick) {
3383
3480
  onClick();
3384
3481
  }
@@ -3395,14 +3492,6 @@ const AnimationText = ({ children, type = 'fade', speed = 1000, repeat = 0, dela
3395
3492
  }
3396
3493
  return {};
3397
3494
  };
3398
- const getWaveStyle = () => {
3399
- if (type === 'wave') {
3400
- return {
3401
- '--wave-colors': waveColors.join(', '),
3402
- };
3403
- }
3404
- return {};
3405
- };
3406
3495
  const getGlowStyle = () => {
3407
3496
  if (type === 'glow') {
3408
3497
  return {
@@ -3411,26 +3500,52 @@ const AnimationText = ({ children, type = 'fade', speed = 1000, repeat = 0, dela
3411
3500
  }
3412
3501
  return {};
3413
3502
  };
3414
- const classes = classNames('designbase-animation-text', `designbase-animation-text--${type}`, `designbase-animation-text--${size}`, `designbase-animation-text--${color}`, `designbase-animation-text--${weight}`, `designbase-animation-text--${align}`, `designbase-animation-text--${direction}`, {
3503
+ // For typing, the class should be applied to the inner element to handle caret correctly with ghost element
3504
+ const containerClasses = classNames('designbase-animation-text',
3505
+ // Exclude typing logic from outer container to prevent caret on full-width ghost container
3506
+ type !== 'typing' && `designbase-animation-text--${type}`, `designbase-animation-text--${size}`, `designbase-animation-text--${color}`, `designbase-animation-text--${weight}`, `designbase-animation-text--${align}`, `designbase-animation-text--${direction}`, {
3415
3507
  'designbase-animation-text--visible': isVisible,
3416
3508
  'designbase-animation-text--animating': isAnimating,
3417
- 'designbase-animation-text--clickable': clickable,
3509
+ 'designbase-animation-text--clickable': clickable || trigger === 'click',
3418
3510
  'designbase-animation-text--disabled': disabled,
3419
3511
  }, className);
3420
3512
  const style = {
3421
3513
  ...getGradientStyle(),
3422
- ...getWaveStyle(),
3423
3514
  ...getGlowStyle(),
3424
3515
  '--db-animation-speed': `${speed}ms`,
3425
3516
  '--db-text-custom': customColor,
3517
+ '--db-wave-colors': waveColors.join(', '),
3518
+ '--db-animation-iteration-count': repeat === 0 ? 'infinite' : repeat,
3426
3519
  };
3427
- const renderText = () => {
3428
- if (type === 'typing') {
3429
- return displayText;
3430
- }
3431
- return children;
3432
- };
3433
- return (jsx("div", { ref: textRef, className: classes, style: style, onClick: handleClick, children: renderText() }));
3520
+ const renderContent = () => {
3521
+ if (type === 'wave' || type === 'shake') {
3522
+ if (!isVisible && !isAnimating)
3523
+ return children;
3524
+ return children.split('').map((char, index) => (jsx("span", { style: {
3525
+ animationDelay: `${index * 0.05}s`,
3526
+ display: 'inline-block'
3527
+ }, children: char === ' ' ? '\u00A0' : char }, index)));
3528
+ }
3529
+ return displayText;
3530
+ };
3531
+ return (jsxs("div", { ref: textRef, className: containerClasses, style: style, onClick: handleClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: [jsx("span", { "aria-hidden": "true", style: {
3532
+ visibility: 'hidden',
3533
+ pointerEvents: 'none',
3534
+ userSelect: 'none',
3535
+ whiteSpace: type === 'typing' ? 'pre-wrap' : undefined
3536
+ }, children: children }), jsx("span", { className: classNames({
3537
+ 'designbase-animation-text--typing': type === 'typing',
3538
+ 'designbase-animation-text--animating': isAnimating && type === 'typing' // Ensure animating class is passed for typing caret
3539
+ }), style: {
3540
+ position: 'absolute',
3541
+ top: 0,
3542
+ left: 0,
3543
+ // For typing, we want width to be auto so caret follows text
3544
+ // For others, width 100% matches ghost element
3545
+ width: type === 'typing' ? 'auto' : '100%',
3546
+ height: '100%',
3547
+ whiteSpace: type === 'typing' ? 'pre-wrap' : undefined // Ensure pre-wrap behavior matches
3548
+ }, children: renderContent() })] }));
3434
3549
  };
3435
3550
 
3436
3551
  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, }) => {
@@ -4192,6 +4307,7 @@ SegmentControl.displayName = 'SegmentControl';
4192
4307
 
4193
4308
  const Modal = ({ isOpen, onClose, title, size = 'm', closeOnOutsideClick = true, closeOnEscape = true, children, className, overlayClassName, }) => {
4194
4309
  const modalRef = useRef(null);
4310
+ const titleId = useId();
4195
4311
  // 아이콘 크기 계산 (m이 기본값)
4196
4312
  const iconSize = size === 's' ? 16 : size === 'l' ? 20 : size === 'xl' ? 24 : 18;
4197
4313
  // Prevent scrolling while modal is open
@@ -4223,11 +4339,12 @@ const Modal = ({ isOpen, onClose, title, size = 'm', closeOnOutsideClick = true,
4223
4339
  return null;
4224
4340
  const modalClasses = clsx('designbase-modal', `designbase-modal--${size}`, className);
4225
4341
  const overlayClasses = clsx('designbase-modal__overlay', overlayClassName);
4226
- return (jsx("div", { className: overlayClasses, onClick: closeOnOutsideClick ? onClose : undefined, children: jsxs("div", { ref: modalRef, className: modalClasses, onClick: (e) => e.stopPropagation(), role: "dialog", "aria-modal": "true", "aria-labelledby": title ? 'modal-title' : undefined, children: [title && (jsx(ModalHeader, { title: title, showCloseButton: true, onClose: onClose, iconSize: iconSize })), jsx("div", { className: "designbase-modal__content", children: children })] }) }));
4342
+ return (jsx("div", { className: overlayClasses, onClick: closeOnOutsideClick ? onClose : undefined, children: jsxs("div", { ref: modalRef, className: modalClasses, onClick: (e) => e.stopPropagation(), role: "dialog", "aria-modal": "true", "aria-labelledby": title ? titleId : undefined, children: [title && (jsx(ModalHeader, { title: title, titleId: titleId, showCloseButton: true, onClose: onClose, iconSize: iconSize })), jsx("div", { className: "designbase-modal__content", children: children })] }) }));
4227
4343
  };
4228
- const ModalHeader = ({ title, showCloseButton = true, onClose, iconSize = 20, className, children, }) => {
4344
+ const ModalHeader = ({ title, titleId, showCloseButton = true, onClose, iconSize = 20, className, children, }) => {
4345
+ const fallbackTitleId = useId();
4229
4346
  const classes = clsx('designbase-modal__header', className);
4230
- return (jsxs("div", { className: classes, children: [jsxs("div", { className: "designbase-modal__header-content", children: [title && (jsx("h2", { id: "modal-title", className: "designbase-modal__title", children: title })), children] }), showCloseButton && onClose && (jsx("button", { type: "button", onClick: onClose, "aria-label": "\uBAA8\uB2EC \uB2EB\uAE30", className: "designbase-modal__close-button", children: jsx(CloseIcon, { size: iconSize, color: "currentColor" }) }))] }));
4347
+ return (jsxs("div", { className: classes, children: [jsxs("div", { className: "designbase-modal__header-content", children: [title && (jsx("h2", { id: titleId || fallbackTitleId, className: "designbase-modal__title", children: title })), children] }), showCloseButton && onClose && (jsx("button", { type: "button", onClick: onClose, "aria-label": "\uBAA8\uB2EC \uB2EB\uAE30", className: "designbase-modal__close-button", children: jsx(CloseIcon, { size: iconSize, color: "currentColor" }) }))] }));
4231
4348
  };
4232
4349
  const ModalBody = ({ className, children, }) => {
4233
4350
  const classes = clsx('designbase-modal__body', className);
@@ -4490,6 +4607,11 @@ const Select = ({ value, defaultValue, options, label, placeholder = '선택하
4490
4607
  const [selectedValue, setSelectedValue] = useState(value ?? defaultValue ?? (multiple ? [] : ''));
4491
4608
  const [searchTerm, setSearchTerm] = useState('');
4492
4609
  const [focusedIndex, setFocusedIndex] = useState(-1);
4610
+ const selectId = useId();
4611
+ const labelId = `${selectId}-label`;
4612
+ const listboxId = `${selectId}-listbox`;
4613
+ const helperTextId = `${selectId}-helper-text`;
4614
+ const errorTextId = `${selectId}-error-text`;
4493
4615
  const containerRef = useRef(null);
4494
4616
  const inputRef = useRef(null);
4495
4617
  const dropdownRef = useRef(null);
@@ -4649,23 +4771,29 @@ const Select = ({ value, defaultValue, options, label, placeholder = '선택하
4649
4771
  const filteredOptions = getFilteredOptions();
4650
4772
  const selectedLabels = getSelectedLabels();
4651
4773
  const hasValue = multiple ? selectedValue.length > 0 : selectedValue !== '';
4652
- return (jsxs("div", { className: classes, ref: containerRef, children: [label && (jsxs("label", { className: "designbase-select__label", children: [label, required && jsx("span", { className: "designbase-select__required", children: "*" })] })), jsxs("div", { className: triggerClasses, onClick: handleToggle, onFocus: onFocus, onBlur: onBlur, tabIndex: disabled || readOnly ? -1 : 0, role: "combobox", "aria-expanded": isOpen, "aria-haspopup": "listbox", "aria-labelledby": label ? 'select-label' : undefined, ...props, children: [jsx("div", { className: "designbase-select__value", children: multiple ? (jsx("div", { className: "designbase-select__tags", ref: tagsContainerRef, children: selectedValue.map((value) => {
4774
+ const activeDescendantId = focusedIndex >= 0 ? `${selectId}-option-${focusedIndex}` : undefined;
4775
+ const describedBy = error && errorMessage
4776
+ ? errorTextId
4777
+ : helperText
4778
+ ? helperTextId
4779
+ : undefined;
4780
+ return (jsxs("div", { className: classes, ref: containerRef, children: [label && (jsxs("label", { id: labelId, className: "designbase-select__label", children: [label, required && jsx("span", { className: "designbase-select__required", children: "*" })] })), jsxs("div", { className: triggerClasses, onClick: handleToggle, onFocus: onFocus, onBlur: onBlur, tabIndex: disabled || readOnly ? -1 : 0, role: "combobox", "aria-expanded": isOpen, "aria-haspopup": "listbox", "aria-controls": listboxId, "aria-activedescendant": activeDescendantId, "aria-labelledby": label ? labelId : undefined, "aria-describedby": describedBy, "aria-invalid": error, ...props, children: [jsx("div", { className: "designbase-select__value", children: multiple ? (jsx("div", { className: "designbase-select__tags", ref: tagsContainerRef, children: selectedValue.map((value) => {
4653
4781
  const option = options.find(opt => opt.value === value);
4654
4782
  return (jsxs("span", { className: "designbase-select__tag", children: [jsx("span", { className: "designbase-select__tag-label", children: option?.label || value }), jsx("button", { type: "button", className: "designbase-select__tag-remove", onClick: (e) => {
4655
4783
  e.stopPropagation();
4656
4784
  handleRemoveValue(value);
4657
4785
  }, children: jsx(CloseIcon, { size: 12 }) })] }, value));
4658
- }) })) : (jsx("span", { className: "designbase-select__single-value", children: selectedLabels || placeholder })) }), jsxs("div", { className: "designbase-select__indicators", children: [showClearButton && hasValue && !disabled && !readOnly && (jsx("button", { type: "button", className: "designbase-select__clear-button", onClick: handleClearAll, "aria-label": "\uBAA8\uB4E0 \uAC12 \uC9C0\uC6B0\uAE30", children: jsx(CloseIcon, { size: 16 }) })), jsx("div", { className: "designbase-select__chevron", children: isOpen ? jsx(ChevronUpIcon, { size: 16 }) : jsx(ChevronDownIcon, { size: 16 }) })] })] }), jsxs("div", { className: dropdownClasses, ref: dropdownRef, children: [searchable && (jsx("div", { className: "designbase-select__search", onMouseDown: (e) => e.stopPropagation(), onClick: (e) => e.stopPropagation(), children: jsx(SearchBar, { value: searchTerm, onChange: (value) => setSearchTerm(value), onSearch: (val) => setSearchTerm(val), placeholder: "\uC635\uC158 \uAC80\uC0C9", size: size, variant: "outlined", fullWidth: true, onFocus: (e) => e.stopPropagation(), onBlur: (e) => e.stopPropagation() }) })), jsx("div", { className: "designbase-select__options", style: { maxHeight: `${maxHeight}px` }, role: "listbox", children: filteredOptions.length === 0 ? (jsx("div", { className: "designbase-select__no-options", children: searchTerm ? '검색 결과가 없습니다.' : '옵션이 없습니다.' })) : (filteredOptions.map((option, index) => {
4786
+ }) })) : (jsx("span", { className: "designbase-select__single-value", children: selectedLabels || placeholder })) }), jsxs("div", { className: "designbase-select__indicators", children: [showClearButton && hasValue && !disabled && !readOnly && (jsx("button", { type: "button", className: "designbase-select__clear-button", onClick: handleClearAll, "aria-label": "\uBAA8\uB4E0 \uAC12 \uC9C0\uC6B0\uAE30", children: jsx(CloseIcon, { size: 16 }) })), jsx("div", { className: "designbase-select__chevron", children: isOpen ? jsx(ChevronUpIcon, { size: 16 }) : jsx(ChevronDownIcon, { size: 16 }) })] })] }), jsxs("div", { className: dropdownClasses, ref: dropdownRef, children: [searchable && (jsx("div", { className: "designbase-select__search", onMouseDown: (e) => e.stopPropagation(), onClick: (e) => e.stopPropagation(), children: jsx(SearchBar, { value: searchTerm, onChange: (value) => setSearchTerm(value), onSearch: (val) => setSearchTerm(val), placeholder: "\uC635\uC158 \uAC80\uC0C9", size: size, variant: "outlined", fullWidth: true, onFocus: (e) => e.stopPropagation(), onBlur: (e) => e.stopPropagation() }) })), jsx("div", { className: "designbase-select__options", style: { maxHeight: `${maxHeight}px` }, id: listboxId, role: "listbox", children: filteredOptions.length === 0 ? (jsx("div", { className: "designbase-select__no-options", children: searchTerm ? '검색 결과가 없습니다.' : '옵션이 없습니다.' })) : (filteredOptions.map((option, index) => {
4659
4787
  const isSelected = multiple
4660
4788
  ? selectedValue.includes(option.value)
4661
4789
  : selectedValue === option.value;
4662
4790
  const isFocused = index === focusedIndex;
4663
- return (jsxs("div", { className: clsx('designbase-select__option', {
4791
+ return (jsxs("div", { id: `${selectId}-option-${index}`, className: clsx('designbase-select__option', {
4664
4792
  'designbase-select__option--selected': isSelected,
4665
4793
  'designbase-select__option--focused': isFocused,
4666
4794
  'designbase-select__option--disabled': option.disabled,
4667
4795
  }), onClick: () => handleOptionSelect(option), role: "option", "aria-selected": isSelected, children: [multiple && (jsx("div", { className: "designbase-select__checkbox", children: jsx(Checkbox, { isSelected: isSelected, isDisabled: option.disabled, size: "s", hasLabel: false, onChange: () => handleOptionSelect(option) }) })), jsx("span", { className: "designbase-select__option-label", children: option.label })] }, option.value));
4668
- })) })] }), helperText && !error && (jsx("p", { className: "designbase-select__helper-text", children: helperText })), error && errorMessage && (jsx("p", { className: "designbase-select__error-message", children: errorMessage }))] }));
4796
+ })) })] }), helperText && !error && (jsx("p", { id: helperTextId, className: "designbase-select__helper-text", children: helperText })), error && errorMessage && (jsx("p", { id: errorTextId, className: "designbase-select__error-message", children: errorMessage }))] }));
4669
4797
  };
4670
4798
  Select.displayName = 'Select';
4671
4799
 
@@ -5269,7 +5397,7 @@ const Card = forwardRef(function Card({ title, subtitle, description, children,
5269
5397
  return null;
5270
5398
  return (jsx("div", { className: "designbase-card__actions", children: actions.map((action, index) => {
5271
5399
  const ActionIcon = action.icon;
5272
- return (jsx(Button, { variant: action.variant || 'primary', size: action.size || 's', onClick: () => handleActionClick(action, index), disabled: action.disabled || disabled, loading: !!action.loading, icon: ActionIcon, children: action.label }, index));
5400
+ return (jsx(Button, { variant: action.variant || 'primary', size: action.size || 's', onClick: () => handleActionClick(action, index), disabled: action.disabled || disabled, loading: !!action.loading, startIcon: ActionIcon, children: action.label }, index));
5273
5401
  }) }));
5274
5402
  };
5275
5403
  const renderContent = () => (jsxs("div", { className: "designbase-card__content", children: [renderIcon(), (title || subtitle) && (jsxs("div", { className: "designbase-card__header", children: [title && jsx("h3", { className: "designbase-card__title", children: title }), subtitle && jsx("p", { className: "designbase-card__subtitle", children: subtitle })] })), description && jsx("p", { className: "designbase-card__description", children: description }), children && jsx("div", { className: "designbase-card__body", children: children }), renderTags(), renderMeta(), renderActions()] }));
@@ -10897,7 +11025,7 @@ const Share = ({ url, title, description = '', imageUrl, hashtags = [], variant
10897
11025
  }
10898
11026
  };
10899
11027
 
10900
- 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 }) => {
11028
+ const Sidebar = ({ size = 'm', variant = 'default', position = 'left', showLogo = false, logo, onLogoClick, items = [], onItemClick, userProfile, userMenuItems = [], onUserMenuItemClick, collapsed = false, onToggle, collapsible = true, fixed = false, fullHeight = false, shadow = false, className, ...props }) => {
10901
11029
  const [expandedItems, setExpandedItems] = useState([]);
10902
11030
  const handleToggle = () => {
10903
11031
  if (onToggle) {
@@ -10938,24 +11066,24 @@ const Sidebar = ({ size = 'm', variant = 'default', position = 'left', logo, onL
10938
11066
  const hasChildren = item.children && item.children.length > 0;
10939
11067
  return (jsx("li", { className: "designbase-sidebar__item", children: jsx(MenuItem, { ...item, type: "block", style: "accordion", depth: level, expanded: isExpanded, expandable: hasChildren, onClick: () => handleItemClick(item), onChildClick: (child) => handleItemClick(child) }) }, item.id));
10940
11068
  };
10941
- return (jsx("aside", { className: classes, role: "complementary", "aria-label": "\uC0AC\uC774\uB4DC\uBC14 \uB124\uBE44\uAC8C\uC774\uC158", ...props, children: jsxs("div", { className: "designbase-sidebar__container", children: [jsxs("div", { className: "designbase-sidebar__header", children: [jsx("div", { className: "designbase-sidebar__logo", onClick: onLogoClick, role: onLogoClick ? 'button' : undefined, tabIndex: onLogoClick ? 0 : undefined, onKeyDown: (e) => {
11069
+ return (jsx("aside", { className: classes, role: "complementary", "aria-label": "\uC0AC\uC774\uB4DC\uBC14 \uB124\uBE44\uAC8C\uC774\uC158", ...props, children: jsxs("div", { className: "designbase-sidebar__container", children: [showLogo && (jsxs("div", { className: "designbase-sidebar__header", children: [jsx("div", { className: "designbase-sidebar__logo", onClick: onLogoClick, role: onLogoClick ? 'button' : undefined, tabIndex: onLogoClick ? 0 : undefined, onKeyDown: (e) => {
10942
11070
  if (onLogoClick && (e.key === 'Enter' || e.key === ' ')) {
10943
11071
  e.preventDefault();
10944
11072
  onLogoClick();
10945
11073
  }
10946
11074
  }, children: logo || jsx(Logo, { size: "s" }) }), collapsible && (jsx(Button, { variant: "ghost", size: "s", iconOnly: true, className: "designbase-sidebar__toggle", onPress: handleToggle, "aria-label": collapsed ? '사이드바 펼치기' : '사이드바 접기', children: jsx(ChevronLeftIcon, { className: clsx('designbase-sidebar__toggle-icon', {
10947
11075
  'designbase-sidebar__toggle-icon--collapsed': collapsed,
10948
- }) }) }))] }), jsx("nav", { className: "designbase-sidebar__nav", children: jsx("ul", { className: "designbase-sidebar__nav-list", children: items.map((item) => renderSidebarItem(item)) }) }), userProfile && !collapsed && (jsxs("div", { className: "designbase-sidebar__user", children: [jsxs("div", { className: "designbase-sidebar__user-info", onClick: () => onUserMenuItemClick?.({ id: 'profile', label: '프로필', href: '#' }), role: "button", tabIndex: 0, onKeyDown: (e) => {
11076
+ }) }) }))] })), jsx("nav", { className: "designbase-sidebar__nav", children: jsx("ul", { className: "designbase-sidebar__nav-list", children: items.map((item) => renderSidebarItem(item)) }) }), userProfile && !collapsed && (jsxs("div", { className: "designbase-sidebar__user", children: [jsxs("div", { className: "designbase-sidebar__user-info", onClick: () => onUserMenuItemClick?.({ id: 'profile', label: '프로필', href: '#' }), role: "button", tabIndex: 0, onKeyDown: (e) => {
10949
11077
  if (e.key === 'Enter' || e.key === ' ') {
10950
11078
  e.preventDefault();
10951
11079
  onUserMenuItemClick?.({ id: 'profile', label: '프로필', href: '#' });
10952
11080
  }
10953
- }, style: { cursor: 'pointer' }, children: [userProfile.avatar ? (jsx("img", { src: userProfile.avatar, alt: userProfile.name, className: "designbase-sidebar__user-avatar" })) : (jsx("div", { className: "designbase-sidebar__user-avatar-placeholder", children: userProfile.name.charAt(0).toUpperCase() })), jsxs("div", { className: "designbase-sidebar__user-details", children: [jsx("div", { className: "designbase-sidebar__user-name", children: userProfile.name }), userProfile.email && (jsx("div", { className: "designbase-sidebar__user-email", children: userProfile.email })), userProfile.role && (jsx("div", { className: "designbase-sidebar__user-role", children: userProfile.role }))] })] }), userMenuItems.length > 0 && (jsx("ul", { className: "designbase-sidebar__user-menu", children: userMenuItems.map((item) => (jsx("li", { children: jsx(MenuItem, { id: item.id, label: item.label, href: item.href, icon: item.icon, disabled: item.disabled, type: "block", style: "accordion", onClick: () => handleUserMenuItemClick(item) }) }, item.id))) }))] })), userProfile && collapsed && (jsx("div", { className: "designbase-sidebar__user-collapsed", onClick: () => onUserMenuItemClick?.({ id: 'profile', label: '프로필', href: '#' }), role: "button", tabIndex: 0, onKeyDown: (e) => {
11081
+ }, style: { cursor: 'pointer' }, children: [jsx(Avatar, { src: userProfile.avatar, initials: userProfile.name, alt: userProfile.name, size: "s", className: "designbase-sidebar__user-avatar" }), jsxs("div", { className: "designbase-sidebar__user-details", children: [jsx("div", { className: "designbase-sidebar__user-name", children: userProfile.name }), userProfile.email && (jsx("div", { className: "designbase-sidebar__user-email", children: userProfile.email })), userProfile.role && (jsx("div", { className: "designbase-sidebar__user-role", children: userProfile.role }))] })] }), userMenuItems.length > 0 && (jsx("ul", { className: "designbase-sidebar__user-menu", children: userMenuItems.map((item) => (jsx("li", { children: jsx(MenuItem, { id: item.id, label: item.label, href: item.href, icon: item.icon, disabled: item.disabled, type: "block", style: "accordion", onClick: () => handleUserMenuItemClick(item) }) }, item.id))) }))] })), userProfile && collapsed && (jsx("div", { className: "designbase-sidebar__user-collapsed", onClick: () => onUserMenuItemClick?.({ id: 'profile', label: '프로필', href: '#' }), role: "button", tabIndex: 0, onKeyDown: (e) => {
10954
11082
  if (e.key === 'Enter' || e.key === ' ') {
10955
11083
  e.preventDefault();
10956
11084
  onUserMenuItemClick?.({ id: 'profile', label: '프로필', href: '#' });
10957
11085
  }
10958
- }, style: { cursor: 'pointer' }, children: userProfile.avatar ? (jsx("img", { src: userProfile.avatar, alt: userProfile.name, className: "designbase-sidebar__user-avatar-collapsed" })) : (jsx("div", { className: "designbase-sidebar__user-avatar-placeholder-collapsed", children: userProfile.name.charAt(0).toUpperCase() })) }))] }) }));
11086
+ }, style: { cursor: 'pointer' }, children: jsx(Avatar, { src: userProfile.avatar, initials: userProfile.name, alt: userProfile.name, size: "s", className: "designbase-sidebar__user-avatar-collapsed" }) }))] }) }));
10959
11087
  };
10960
11088
  Sidebar.displayName = 'Sidebar';
10961
11089
 
@@ -12390,24 +12518,5 @@ const YouTubePlayer = ({ videoId, title, description, size = 'm', variant = 'def
12390
12518
  };
12391
12519
  YouTubePlayer.displayName = 'YouTubePlayer';
12392
12520
 
12393
- /**
12394
- * Designbase UI 컴포넌트 라이브러리 메인 엔트리 포인트
12395
- *
12396
- * 목적: 모든 UI 컴포넌트와 타입을 내보냄
12397
- * 기능: Tree-shaking 가능한 개별 컴포넌트 내보내기
12398
- * 사용법: import { Button, Input } from '@designbasekorea/ui'
12399
- */
12400
- // 테마 CSS 자동 로드 (로컬 복사본 사용)
12401
- // 테마 관련 유틸리티 재내보내기
12402
- const setTheme = (theme) => {
12403
- console.log('setTheme called with:', theme);
12404
- };
12405
- const getTheme = () => {
12406
- return 'light';
12407
- };
12408
- const toggleTheme = () => {
12409
- console.log('toggleTheme called');
12410
- };
12411
-
12412
- export { Accordion, AdBanner, Alert, AnimationBackground, AnimationText, AudioPlayer, Avatar, Backdrop, Badge, Banner, BottomNavigation, BottomSheet, Breadcrumbs, Button, Calendar, Card, Carousel, Checkbox, Chip, ColorPicker, Confirm, Container, ContextMenu, Countdown, DatePicker, Divider, Drawer, Dropdown, Dropzone, EmptyState, FileUploader, FloatingActionButton, Form, Gradient, Grid, HeroFeature, Image$1 as Image, ImageList, Indicator, Input, Label, Lightbox, List, Logo, MarkdownEditor, Masonry, MenuItem, Modal, ModalBody, ModalFooter, ModalHeader, Navbar, OnboardingModal, Pagination, Popover, Progress, ProgressStep, Progressbar, Radio, RandomGradient, RangeSlider, Rating, Reorder, ResizablePanels, ScrollArea, SearchBar, Section, SegmentControl, Select, Share, Sidebar, Skeleton, Spinner, SplitView, Stack, Stat, Stepper, Table, Tabs, Testimonial, Textarea, TimePicker, Timeline, Toast, ToastContainer, ToastProvider, Toggle, Toolbar, Tooltip, Tutorial, VideoPlayer, YouTubePlayer, getTheme, setTheme, toggleTheme, useToast };
12521
+ export { Accordion, AdBanner, Alert, AnimationBackground, AnimationText, AudioPlayer, Avatar, Backdrop, Badge, Banner, BottomNavigation, BottomSheet, Breadcrumbs, Button, Calendar, Card, Carousel, Checkbox, Chip, ColorPicker, Confirm, Container, ContextMenu, Countdown, DatePicker, Divider, Drawer, Dropdown, Dropzone, EmptyState, FileUploader, FloatingActionButton, Form, Gradient, Grid, HeroFeature, Image$1 as Image, ImageList, Indicator, Input, Label, Lightbox, List, Logo, MarkdownEditor, Masonry, MenuItem, Modal, ModalBody, ModalFooter, ModalHeader, Navbar, OnboardingModal, Pagination, Popover, Progress, ProgressStep, Progressbar, Radio, RandomGradient, RangeSlider, Rating, Reorder, ResizablePanels, ScrollArea, SearchBar, Section, SegmentControl, Select, Share, Sidebar, Skeleton, Spinner, SplitView, Stack, Stat, Stepper, Table, Tabs, Testimonial, Textarea, TimePicker, Timeline, Toast, ToastContainer, ToastProvider, Toggle, Toolbar, Tooltip, Tutorial, VideoPlayer, YouTubePlayer, useToast };
12413
12522
  //# sourceMappingURL=index.esm.js.map