@designbasekorea/ui 0.1.41 → 0.1.43

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 { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import React, { useState, useCallback, useEffect, useRef, useMemo, useContext, forwardRef, useId, useLayoutEffect } from 'react';
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, BulbIcon, CloudCloseIcon, BellActiveIcon, AwardIcon, CalendarIcon, PlusIcon, ErrorIcon, 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, VerticalArrowsIcon, CaretUpdownFilledIcon } from '@designbasekorea/icons';
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, BulbIcon, CloudCloseIcon, BellActiveIcon, AwardIcon, CalendarIcon, PlusIcon, ErrorIcon, ClockIcon, MinusIcon as MinusIcon$1, VideoIcon, CodeIcon, WriteIcon, UploadIcon, XIcon, ArrowBarLeftIcon, ArrowBarRightIcon, StarHalfIcon, MoveIcon, MoreVerticalIcon, ArrowDownIcon, ArrowUpLeftIcon, ArrowUpRightIcon, ArrowDownLeftIcon, ArrowDownRightIcon, FacebookIcon, InstagramIcon, LinkedinIcon, PinterestIcon, WhatsappIcon, TelegramIcon, MailIcon, LinkIcon, ScanQrcodeIcon, VerticalArrowsIcon, CaretUpdownFilledIcon } from '@designbasekorea/icons';
4
4
  import { flushSync, createPortal } from 'react-dom';
5
5
 
6
6
  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}
@@ -2265,49 +2265,49 @@ Button.displayName = 'Button';
2265
2265
  // 톤별 색상 팔레트
2266
2266
  const colorPalettes = {
2267
2267
  primary: {
2268
- light: ['#dbeafe', '#bfdbfe', '#93c5fd', '#60a5fa', '#3b82f6'], // blue-100 ~ blue-500
2268
+ light: ['#eff6ff', '#dbeafe', '#bfdbfe', '#93c5fd', '#60a5fa'], // blue-50 ~ blue-400 (더 밝게)
2269
2269
  vivid: ['#3b82f6', '#2563eb', '#1d4ed8', '#1e40af', '#1e3a8a'], // blue-500 ~ blue-900
2270
- dark: ['#1e3a8a', '#1e40af', '#1d4ed8', '#2563eb', '#3b82f6'], // blue-900 ~ blue-500
2270
+ dark: ['#0f172a', '#1e293b', '#334155', '#475569', '#64748b'], // slate-900 ~ slate-600 (더 어둡게)
2271
2271
  },
2272
2272
  secondary: {
2273
- light: ['#f3f4f6', '#e5e7eb', '#d1d5db', '#9ca3af', '#6b7280'], // gray-100 ~ gray-500
2273
+ light: ['#f8fafc', '#f1f5f9', '#e2e8f0', '#cbd5e1', '#94a3b8'], // slate-50 ~ slate-400 (더 밝게)
2274
2274
  vivid: ['#6b7280', '#4b5563', '#374151', '#1f2937', '#111827'], // gray-500 ~ gray-900
2275
- dark: ['#111827', '#1f2937', '#374151', '#4b5563', '#6b7280'], // gray-900 ~ gray-500
2275
+ dark: ['#0f0f0f', '#1a1a1a', '#262626', '#404040', '#525252'], // neutral-950 ~ neutral-600 (더 어둡게)
2276
2276
  },
2277
2277
  success: {
2278
- light: ['#dcfce7', '#bbf7d0', '#86efac', '#4ade80', '#22c55e'], // green-100 ~ green-500
2278
+ light: ['#f0fdf4', '#dcfce7', '#bbf7d0', '#86efac', '#4ade80'], // green-50 ~ green-400 (더 밝게)
2279
2279
  vivid: ['#22c55e', '#16a34a', '#15803d', '#166534', '#14532d'], // green-500 ~ green-900
2280
- dark: ['#14532d', '#166534', '#15803d', '#16a34a', '#22c55e'], // green-900 ~ green-500
2280
+ dark: ['#052e16', '#14532d', '#166534', '#15803d', '#16a34a'], // green-950 ~ green-600 (더 어둡게)
2281
2281
  },
2282
2282
  warning: {
2283
- light: ['#fef3c7', '#fde68a', '#fcd34d', '#f59e0b', '#d97706'], // yellow-100 ~ yellow-600
2284
- vivid: ['#f59e0b', '#d97706', '#b45309', '#92400e', '#78350f'], // yellow-600 ~ yellow-900
2285
- dark: ['#78350f', '#92400e', '#b45309', '#d97706', '#f59e0b'], // yellow-900 ~ yellow-600
2283
+ light: ['#fffbeb', '#fef3c7', '#fde68a', '#fcd34d', '#fbbf24'], // amber-50 ~ amber-400 (더 밝게)
2284
+ vivid: ['#f59e0b', '#d97706', '#b45309', '#92400e', '#78350f'], // amber-600 ~ amber-900
2285
+ dark: ['#451a03', '#78350f', '#92400e', '#b45309', '#d97706'], // amber-950 ~ amber-600 (더 어둡게)
2286
2286
  },
2287
2287
  error: {
2288
- light: ['#fecaca', '#fca5a5', '#f87171', '#ef4444', '#dc2626'], // red-100 ~ red-600
2288
+ light: ['#fef2f2', '#fecaca', '#fca5a5', '#f87171', '#f56565'], // red-50 ~ red-400 (더 밝게)
2289
2289
  vivid: ['#ef4444', '#dc2626', '#b91c1c', '#991b1b', '#7f1d1d'], // red-500 ~ red-900
2290
- dark: ['#7f1d1d', '#991b1b', '#b91c1c', '#dc2626', '#ef4444'], // red-900 ~ red-500
2290
+ dark: ['#450a0a', '#7f1d1d', '#991b1b', '#b91c1c', '#dc2626'], // red-950 ~ red-600 (더 어둡게)
2291
2291
  },
2292
2292
  info: {
2293
- light: ['#dbeafe', '#bfdbfe', '#93c5fd', '#60a5fa', '#3b82f6'], // blue-100 ~ blue-500
2293
+ light: ['#eff6ff', '#dbeafe', '#bfdbfe', '#93c5fd', '#60a5fa'], // blue-50 ~ blue-400 (더 밝게)
2294
2294
  vivid: ['#3b82f6', '#2563eb', '#1d4ed8', '#1e40af', '#1e3a8a'], // blue-500 ~ blue-900
2295
- dark: ['#1e3a8a', '#1e40af', '#1d4ed8', '#2563eb', '#3b82f6'], // blue-900 ~ blue-500
2295
+ dark: ['#0f172a', '#1e293b', '#334155', '#475569', '#64748b'], // slate-900 ~ slate-600 (더 어둡게)
2296
2296
  },
2297
2297
  purple: {
2298
- light: ['#e9d5ff', '#d8b4fe', '#c084fc', '#a855f7', '#9333ea'], // purple-100 ~ purple-600
2298
+ light: ['#faf5ff', '#f3e8ff', '#e9d5ff', '#d8b4fe', '#c084fc'], // purple-50 ~ purple-400 (더 밝게)
2299
2299
  vivid: ['#a855f7', '#9333ea', '#7c3aed', '#6d28d9', '#5b21b6'], // purple-600 ~ purple-900
2300
- dark: ['#5b21b6', '#6d28d9', '#7c3aed', '#9333ea', '#a855f7'], // purple-900 ~ purple-600
2300
+ dark: ['#3b0764', '#581c87', '#6b21a8', '#7c3aed', '#9333ea'], // purple-950 ~ purple-600 (더 어둡게)
2301
2301
  },
2302
2302
  ocean: {
2303
- light: ['#ccfbf1', '#99f6e4', '#5eead4', '#2dd4bf', '#14b8a6'], // teal-100 ~ teal-500
2303
+ light: ['#f0fdfa', '#ccfbf1', '#99f6e4', '#5eead4', '#2dd4bf'], // teal-50 ~ teal-400 (더 밝게)
2304
2304
  vivid: ['#14b8a6', '#0d9488', '#0f766e', '#115e59', '#134e4a'], // teal-500 ~ teal-900
2305
- dark: ['#134e4a', '#115e59', '#0f766e', '#0d9488', '#14b8a6'], // teal-900 ~ teal-500
2305
+ dark: ['#042f2e', '#134e4a', '#115e59', '#0f766e', '#0d9488'], // teal-950 ~ teal-600 (더 어둡게)
2306
2306
  },
2307
2307
  sunset: {
2308
- light: ['#fed7aa', '#fdba74', '#fb923c', '#f97316', '#ea580c'], // orange-100 ~ orange-600
2308
+ light: ['#fff7ed', '#fed7aa', '#fdba74', '#fb923c', '#f97316'], // orange-50 ~ orange-500 (더 밝게)
2309
2309
  vivid: ['#f97316', '#ea580c', '#c2410c', '#9a3412', '#7c2d12'], // orange-600 ~ orange-900
2310
- dark: ['#7c2d12', '#9a3412', '#c2410c', '#ea580c', '#f97316'], // orange-900 ~ orange-600
2310
+ dark: ['#431407', '#7c2d12', '#9a3412', '#c2410c', '#ea580c'], // orange-950 ~ orange-600 (더 어둡게)
2311
2311
  },
2312
2312
  };
2313
2313
  const RandomGradient = ({ scheme = 'primary', tone = 'vivid', width = '100%', height = '600px', blur = 60, colorCount = 4, animated = false, animationDuration = 10, overlay = true, overlayOpacity = 0.2, children, className, }) => {
@@ -8144,10 +8144,48 @@ const Masonry = ({ images, columns = 3, spacing = 'm', ratio = 'auto', fit = 'co
8144
8144
  })), currentIndex: selectedImageIndex, isOpen: true, onOpenChange: handleLightboxClose, onImageChange: handleLightboxNavigate }))] }));
8145
8145
  };
8146
8146
 
8147
- 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 }) => {
8147
+ const SearchBar = ({ value, defaultValue = '', placeholder = '검색...', size = 'm', variant = 'default', disabled = false, readOnly = false, fullWidth = false, searchIcon: SearchIconComponent = SearchIcon, clearIcon: ClearIconComponent = CloseIcon, enableRecentSearches = false, recentSearchesKey = 'searchbar-recent-searches', suggestedSearches = [], suggestionRollingInterval = 5000, onChange, onSearch, onFocus, onBlur, onKeyDown, className, ...props }) => {
8148
8148
  const [internalValue, setInternalValue] = useState(defaultValue);
8149
+ const [recentSearches, setRecentSearches] = useState([]);
8150
+ const [showRecentSearches, setShowRecentSearches] = useState(false);
8151
+ const [currentSuggestion, setCurrentSuggestion] = useState(0);
8152
+ const [isFocused, setIsFocused] = useState(false);
8149
8153
  const inputRef = useRef(null);
8154
+ const suggestionIntervalRef = useRef(null);
8150
8155
  const currentValue = value !== undefined ? value : internalValue;
8156
+ // 최근 검색어 로드
8157
+ useEffect(() => {
8158
+ if (enableRecentSearches) {
8159
+ try {
8160
+ const stored = localStorage.getItem(recentSearchesKey);
8161
+ if (stored) {
8162
+ setRecentSearches(JSON.parse(stored));
8163
+ }
8164
+ }
8165
+ catch (error) {
8166
+ console.warn('최근 검색어를 불러올 수 없습니다:', error);
8167
+ }
8168
+ }
8169
+ }, [enableRecentSearches, recentSearchesKey]);
8170
+ // 추천 검색어 롤링
8171
+ useEffect(() => {
8172
+ if (suggestedSearches.length > 0 && isFocused && !currentValue) {
8173
+ suggestionIntervalRef.current = setInterval(() => {
8174
+ setCurrentSuggestion(prev => (prev + 1) % suggestedSearches.length);
8175
+ }, suggestionRollingInterval);
8176
+ }
8177
+ else {
8178
+ if (suggestionIntervalRef.current) {
8179
+ clearInterval(suggestionIntervalRef.current);
8180
+ suggestionIntervalRef.current = null;
8181
+ }
8182
+ }
8183
+ return () => {
8184
+ if (suggestionIntervalRef.current) {
8185
+ clearInterval(suggestionIntervalRef.current);
8186
+ }
8187
+ };
8188
+ }, [suggestedSearches.length, isFocused, currentValue, suggestionRollingInterval]);
8151
8189
  useEffect(() => {
8152
8190
  if (value !== undefined) {
8153
8191
  setInternalValue(value);
@@ -8163,9 +8201,24 @@ const SearchBar = ({ value, defaultValue = '', placeholder = '검색...', size =
8163
8201
  onChange?.('');
8164
8202
  inputRef.current?.focus();
8165
8203
  };
8166
- const handleSearch = () => {
8167
- if (currentValue.trim()) {
8168
- onSearch?.(currentValue.trim());
8204
+ const handleSearch = (searchValue) => {
8205
+ const searchTerm = searchValue || currentValue.trim();
8206
+ if (searchTerm) {
8207
+ onSearch?.(searchTerm);
8208
+ // 최근 검색어 저장
8209
+ if (enableRecentSearches) {
8210
+ const newRecentSearches = [
8211
+ searchTerm,
8212
+ ...recentSearches.filter(item => item !== searchTerm)
8213
+ ].slice(0, 10); // 최대 10개
8214
+ setRecentSearches(newRecentSearches);
8215
+ try {
8216
+ localStorage.setItem(recentSearchesKey, JSON.stringify(newRecentSearches));
8217
+ }
8218
+ catch (error) {
8219
+ console.warn('최근 검색어를 저장할 수 없습니다:', error);
8220
+ }
8221
+ }
8169
8222
  }
8170
8223
  };
8171
8224
  const handleKeyDown = (e) => {
@@ -8174,6 +8227,49 @@ const SearchBar = ({ value, defaultValue = '', placeholder = '검색...', size =
8174
8227
  }
8175
8228
  onKeyDown?.(e);
8176
8229
  };
8230
+ const handleFocus = (e) => {
8231
+ setIsFocused(true);
8232
+ setShowRecentSearches(enableRecentSearches && recentSearches.length > 0);
8233
+ onFocus?.(e);
8234
+ };
8235
+ const handleBlur = (e) => {
8236
+ setIsFocused(false);
8237
+ // 약간의 지연을 두어 클릭 이벤트가 처리되도록 함
8238
+ setTimeout(() => {
8239
+ setShowRecentSearches(false);
8240
+ }, 200);
8241
+ onBlur?.(e);
8242
+ };
8243
+ const handleRecentSearchClick = (searchTerm) => {
8244
+ setInternalValue(searchTerm);
8245
+ onChange?.(searchTerm);
8246
+ setShowRecentSearches(false);
8247
+ handleSearch(searchTerm);
8248
+ };
8249
+ const handleRemoveRecentSearch = (searchTerm) => {
8250
+ const newRecentSearches = recentSearches.filter(item => item !== searchTerm);
8251
+ setRecentSearches(newRecentSearches);
8252
+ try {
8253
+ localStorage.setItem(recentSearchesKey, JSON.stringify(newRecentSearches));
8254
+ }
8255
+ catch (error) {
8256
+ console.warn('최근 검색어를 삭제할 수 없습니다:', error);
8257
+ }
8258
+ };
8259
+ const handleClearAllRecentSearches = () => {
8260
+ setRecentSearches([]);
8261
+ try {
8262
+ localStorage.removeItem(recentSearchesKey);
8263
+ }
8264
+ catch (error) {
8265
+ console.warn('최근 검색어를 모두 삭제할 수 없습니다:', error);
8266
+ }
8267
+ };
8268
+ const handleSuggestionClick = (suggestion) => {
8269
+ setInternalValue(suggestion);
8270
+ onChange?.(suggestion);
8271
+ handleSearch(suggestion);
8272
+ };
8177
8273
  const classes = clsx('designbase-search-bar', `designbase-search-bar--${size}`, `designbase-search-bar--${variant}`, {
8178
8274
  'designbase-search-bar--disabled': disabled,
8179
8275
  'designbase-search-bar--readonly': readOnly,
@@ -8184,7 +8280,13 @@ const SearchBar = ({ value, defaultValue = '', placeholder = '검색...', size =
8184
8280
  'designbase-search-bar__input--disabled': disabled,
8185
8281
  'designbase-search-bar__input--readonly': readOnly,
8186
8282
  });
8187
- return (jsx("div", { className: classes, role: "search", children: jsxs("div", { className: "designbase-search-bar__container", children: [jsx("div", { className: "designbase-search-bar__search-icon", children: jsx(SearchIconComponent, { size: size === 's' ? 16 : size === 'l' ? 24 : 20 }) }), jsx("input", { ref: inputRef, type: "text", className: inputClasses, value: currentValue, placeholder: placeholder, disabled: disabled, readOnly: readOnly, onChange: handleChange, onFocus: onFocus, onBlur: onBlur, onKeyDown: handleKeyDown, "aria-label": "\uAC80\uC0C9\uC5B4 \uC785\uB825", ...props }), currentValue && currentValue.length > 0 && !disabled && !readOnly && (jsx("button", { type: "button", className: "designbase-search-bar__clear-button", onClick: handleClear, "aria-label": "\uAC80\uC0C9\uC5B4 \uC9C0\uC6B0\uAE30", children: jsx(ClearIconComponent, { size: size === 's' ? 16 : size === 'l' ? 24 : 20 }) }))] }) }));
8283
+ // 현재 플레이스홀더 (추천 검색어가 있으면 롤링)
8284
+ const currentPlaceholder = suggestedSearches.length > 0 && isFocused && !currentValue
8285
+ ? suggestedSearches[currentSuggestion]
8286
+ : placeholder;
8287
+ return (jsxs("div", { className: classes, role: "search", children: [jsxs("div", { className: "designbase-search-bar__container", children: [jsx("div", { className: "designbase-search-bar__search-icon", children: jsx(SearchIconComponent, { size: size === 's' ? 16 : size === 'l' ? 24 : 20 }) }), 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", ...props }), currentValue && currentValue.length > 0 && !disabled && !readOnly && (jsx("button", { type: "button", className: "designbase-search-bar__clear-button", onClick: handleClear, "aria-label": "\uAC80\uC0C9\uC5B4 \uC9C0\uC6B0\uAE30", children: jsx(ClearIconComponent, { size: size === 's' ? 16 : size === 'l' ? 24 : 20 }) }))] }), showRecentSearches && recentSearches.length > 0 && (jsxs("div", { className: "designbase-search-bar__recent-searches", children: [jsxs("div", { className: "designbase-search-bar__recent-header", children: [jsx("span", { className: "designbase-search-bar__recent-title", children: "\uCD5C\uADFC \uAC80\uC0C9\uC5B4" }), 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" })] }), jsx("div", { className: "designbase-search-bar__recent-list", children: recentSearches.map((searchTerm, index) => (jsxs("div", { className: "designbase-search-bar__recent-item", children: [jsx("button", { type: "button", className: "designbase-search-bar__recent-search-button", onClick: () => handleRecentSearchClick(searchTerm), children: searchTerm }), jsx("button", { type: "button", className: "designbase-search-bar__recent-remove-button", onClick: () => handleRemoveRecentSearch(searchTerm), "aria-label": `${searchTerm} 삭제`, children: jsx(XIcon, { size: 16 }) })] }, index))) })] })), suggestedSearches.length > 0 && isFocused && !currentValue && (jsxs("div", { className: "designbase-search-bar__suggestions", children: [jsx("div", { className: "designbase-search-bar__suggestions-header", children: jsx("span", { className: "designbase-search-bar__suggestions-title", children: "\uCD94\uCC9C \uAC80\uC0C9\uC5B4" }) }), jsx("div", { className: "designbase-search-bar__suggestions-list", children: suggestedSearches.map((suggestion, index) => (jsx("button", { type: "button", className: clsx('designbase-search-bar__suggestion-item', {
8288
+ 'designbase-search-bar__suggestion-item--active': index === currentSuggestion
8289
+ }), onClick: () => handleSuggestionClick(suggestion), children: suggestion }, index))) })] }))] }));
8188
8290
  };
8189
8291
  SearchBar.displayName = 'SearchBar';
8190
8292