@designbasekorea/ui 0.2.41 → 0.3.2

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
@@ -2404,7 +2404,7 @@ const Tooltip = ({ content, children, position = 'top', size = 'm', variant = 'd
2404
2404
  };
2405
2405
  Tooltip.displayName = 'Tooltip';
2406
2406
 
2407
- const Button = React.forwardRef(({ variant = 'primary', size = 'm', radius, fullWidth = false, disabled = false, loading = false, iconOnly = false, startIcon: StartIcon, endIcon: EndIcon, tooltip, tooltipProps, className, children, onPress, type = 'button', ...props }, forwardedRef) => {
2407
+ const Button = React.forwardRef(({ variant = 'primary', size = 'm', radius, fullWidth = false, disabled = false, loading = false, loadingText, iconOnly = false, startIcon: StartIcon, endIcon: EndIcon, tooltip, tooltipProps, className, children, onPress, type = 'button', ...props }, forwardedRef) => {
2408
2408
  const ref = $df56164dff5785e2$export$4338b53315abf666(forwardedRef);
2409
2409
  const { buttonProps } = $701a24aa0da5b062$export$ea18c227d4417cc3({
2410
2410
  ...props,
@@ -2418,23 +2418,18 @@ const Button = React.forwardRef(({ variant = 'primary', size = 'm', radius, full
2418
2418
  return `designbase-button--radius-${radius}`;
2419
2419
  }
2420
2420
  // 아이콘 전용 버튼도 일반 버튼과 동일한 radius 적용
2421
- return `designbase-button--radius-${size === 'xs' || size === 's' ? 's' : size === 'l' || size === 'xl' ? 'l' : 'm'}`;
2421
+ if (size === 's')
2422
+ return 'designbase-button--radius-s';
2423
+ if (size === 'l')
2424
+ return 'designbase-button--radius-l';
2425
+ return 'designbase-button--radius-m';
2422
2426
  };
2423
2427
  const classes = clsx('designbase-button', `designbase-button--${variant}`, `designbase-button--${size}`, getRadiusClass(), {
2424
2428
  'designbase-button--full-width': fullWidth,
2425
2429
  'designbase-button--loading': loading,
2426
2430
  'designbase-button--icon-only': iconOnly,
2427
2431
  }, className);
2428
- const iconSize = (() => {
2429
- switch (size) {
2430
- case 'xs': return 12;
2431
- case 's': return 14;
2432
- case 'm': return 16;
2433
- case 'l': return 18;
2434
- case 'xl': return 20;
2435
- default: return 16;
2436
- }
2437
- })();
2432
+ const iconSize = size === 's' ? 14 : size === 'l' ? 18 : 16;
2438
2433
  // 아이콘 색상 결정
2439
2434
  const getIconColor = () => {
2440
2435
  switch (variant) {
@@ -2454,7 +2449,13 @@ const Button = React.forwardRef(({ variant = 'primary', size = 'm', radius, full
2454
2449
  };
2455
2450
  const renderContent = () => {
2456
2451
  if (loading) {
2457
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(Spinner, { type: "circular", size: size === 'xs' ? 'xs' : size === 's' ? 's' : 'm', color: getIconColor(), speed: 1, showLabel: false }), !iconOnly && jsxRuntime.jsx("span", { children: "\uB85C\uB529 \uC911..." })] }));
2452
+ // 로딩 표시할 텍스트 결정: loadingText > children > 기본값
2453
+ const displayText = loadingText !== undefined
2454
+ ? loadingText
2455
+ : (!iconOnly && children)
2456
+ ? children
2457
+ : null;
2458
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(Spinner, { type: "circular", size: size === 's' ? 's' : size === 'l' ? 'l' : 'm', color: getIconColor(), speed: 1, showLabel: false }), displayText && jsxRuntime.jsx("span", { children: displayText })] }));
2458
2459
  }
2459
2460
  // iconOnly 버튼일 때는 children을 아이콘으로 처리
2460
2461
  if (iconOnly && children && React.isValidElement(children)) {
@@ -4013,7 +4014,7 @@ ModalHeader.displayName = 'ModalHeader';
4013
4014
  ModalBody.displayName = 'ModalBody';
4014
4015
  ModalFooter.displayName = 'ModalFooter';
4015
4016
 
4016
- const Label = ({ children, htmlFor, required = false, size = 's', disabled = false, error = false, className, onClick, }) => {
4017
+ const Label = ({ children, htmlFor, id, required = false, size = 's', disabled = false, error = false, className, onClick, }) => {
4017
4018
  const classes = [
4018
4019
  'designbase-label',
4019
4020
  `designbase-label--${size}`,
@@ -4023,7 +4024,7 @@ const Label = ({ children, htmlFor, required = false, size = 's', disabled = fal
4023
4024
  ]
4024
4025
  .filter(Boolean)
4025
4026
  .join(' ');
4026
- return (jsxRuntime.jsxs("label", { htmlFor: htmlFor, className: classes, onClick: onClick, children: [children, required && jsxRuntime.jsx("span", { className: "designbase-label__required", children: "*" })] }));
4027
+ return (jsxRuntime.jsxs("label", { htmlFor: htmlFor, id: id, className: classes, onClick: onClick, children: [children, required && jsxRuntime.jsx("span", { className: "designbase-label__required", children: "*" })] }));
4027
4028
  };
4028
4029
  Label.displayName = 'Label';
4029
4030
 
@@ -4054,12 +4055,17 @@ const Input = React.forwardRef(({ type = 'text', label, placeholder, defaultValu
4054
4055
  'designbase-input--with-end-icon': EndIcon || isPassword,
4055
4056
  }, className);
4056
4057
  const inputClasses = clsx('designbase-input__field', inputClassName);
4057
- const iconSizeMap = {
4058
- sm: 14,
4059
- md: 16,
4060
- lg: 18,
4061
- };
4062
- const iconSize = iconSizeMap[size];
4058
+ const iconSize = React.useMemo(() => {
4059
+ switch (size) {
4060
+ case 's':
4061
+ return 16;
4062
+ case 'l':
4063
+ return 24;
4064
+ case 'm':
4065
+ default:
4066
+ return 20;
4067
+ }
4068
+ }, [size]);
4063
4069
  return (jsxRuntime.jsxs("div", { className: classes, children: [label && (jsxRuntime.jsx(Label, { htmlFor: inputId, required: required, error: error, disabled: disabled, size: size === 's' ? 's' : size === 'm' ? 'm' : 'l', children: label })), jsxRuntime.jsxs("div", { className: "designbase-input__wrapper", children: [actualStartIcon && (jsxRuntime.jsx("div", { className: "designbase-input__start-icon", children: React.createElement(actualStartIcon, { size: iconSize }) })), jsxRuntime.jsx("input", { ...props, ref: forwardedRef, id: inputId, type: actualType, value: value, defaultValue: defaultValue, placeholder: placeholder, disabled: disabled, readOnly: readOnly, required: required, className: inputClasses, onChange: handleChange, onFocus: onFocus, onBlur: onBlur, "aria-describedby": clsx(helperText && helperTextId, error && errorMessage && errorMessageId), "aria-invalid": error }), isPassword && (jsxRuntime.jsx("button", { type: "button", className: "designbase-input__password-toggle", onClick: handlePasswordToggle, disabled: disabled, "aria-label": showPassword ? '비밀번호 숨기기' : '비밀번호 보기', children: React.createElement(PasswordIcon, { size: iconSize }) })), EndIcon && !isPassword && (jsxRuntime.jsx("div", { className: "designbase-input__end-icon", children: React.createElement(EndIcon, { size: iconSize }) }))] }), helperText && !error && (jsxRuntime.jsx("p", { id: helperTextId, className: "designbase-input__helper-text", children: helperText })), error && errorMessage && (jsxRuntime.jsx("p", { id: errorMessageId, className: "designbase-input__error-message", children: errorMessage }))] }));
4064
4070
  });
4065
4071
  Input.displayName = 'Input';
@@ -4100,6 +4106,157 @@ const Checkbox = React.forwardRef(({ isSelected, defaultSelected, isIndeterminat
4100
4106
  });
4101
4107
  Checkbox.displayName = 'Checkbox';
4102
4108
 
4109
+ 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 }) => {
4110
+ const [internalValue, setInternalValue] = React.useState(defaultValue);
4111
+ const [recentSearches, setRecentSearches] = React.useState([]);
4112
+ const [showRecentSearches, setShowRecentSearches] = React.useState(false);
4113
+ const [currentSuggestion, setCurrentSuggestion] = React.useState(0);
4114
+ const [isFocused, setIsFocused] = React.useState(false);
4115
+ const inputRef = React.useRef(null);
4116
+ const suggestionIntervalRef = React.useRef(null);
4117
+ const currentValue = value !== undefined ? value : internalValue;
4118
+ // 최근 검색어 로드
4119
+ React.useEffect(() => {
4120
+ if (enableRecentSearches) {
4121
+ try {
4122
+ const stored = localStorage.getItem(recentSearchesKey);
4123
+ if (stored) {
4124
+ setRecentSearches(JSON.parse(stored));
4125
+ }
4126
+ }
4127
+ catch (error) {
4128
+ console.warn('최근 검색어를 불러올 수 없습니다:', error);
4129
+ }
4130
+ }
4131
+ }, [enableRecentSearches, recentSearchesKey]);
4132
+ // 추천 검색어 롤링 (value가 없을 때만)
4133
+ React.useEffect(() => {
4134
+ if (suggestedSearches.length > 0 && !currentValue && currentValue === '') {
4135
+ suggestionIntervalRef.current = setInterval(() => {
4136
+ setCurrentSuggestion(prev => (prev + 1) % suggestedSearches.length);
4137
+ }, suggestionRollingInterval);
4138
+ }
4139
+ else {
4140
+ if (suggestionIntervalRef.current) {
4141
+ clearInterval(suggestionIntervalRef.current);
4142
+ suggestionIntervalRef.current = null;
4143
+ }
4144
+ }
4145
+ return () => {
4146
+ if (suggestionIntervalRef.current) {
4147
+ clearInterval(suggestionIntervalRef.current);
4148
+ }
4149
+ };
4150
+ }, [suggestedSearches.length, currentValue, suggestionRollingInterval]);
4151
+ React.useEffect(() => {
4152
+ if (value !== undefined) {
4153
+ setInternalValue(value);
4154
+ }
4155
+ }, [value]);
4156
+ const handleChange = (e) => {
4157
+ const newValue = e.target.value;
4158
+ setInternalValue(newValue);
4159
+ onChange?.(newValue);
4160
+ };
4161
+ const handleClear = () => {
4162
+ setInternalValue('');
4163
+ onChange?.('');
4164
+ inputRef.current?.focus();
4165
+ };
4166
+ const handleSearch = (searchValue) => {
4167
+ const searchTerm = searchValue || currentValue.trim();
4168
+ if (searchTerm) {
4169
+ onSearch?.(searchTerm);
4170
+ // 최근 검색어 저장
4171
+ if (enableRecentSearches) {
4172
+ const newRecentSearches = [
4173
+ searchTerm,
4174
+ ...recentSearches.filter(item => item !== searchTerm)
4175
+ ].slice(0, 10); // 최대 10개
4176
+ setRecentSearches(newRecentSearches);
4177
+ try {
4178
+ localStorage.setItem(recentSearchesKey, JSON.stringify(newRecentSearches));
4179
+ }
4180
+ catch (error) {
4181
+ console.warn('최근 검색어를 저장할 수 없습니다:', error);
4182
+ }
4183
+ }
4184
+ }
4185
+ };
4186
+ const handleKeyDown = (e) => {
4187
+ if (e.key === 'Enter') {
4188
+ handleSearch();
4189
+ }
4190
+ onKeyDown?.(e);
4191
+ };
4192
+ const handleFocus = (e) => {
4193
+ setIsFocused(true);
4194
+ setShowRecentSearches(enableRecentSearches && recentSearches.length > 0);
4195
+ onFocus?.(e);
4196
+ };
4197
+ const handleBlur = (e) => {
4198
+ setIsFocused(false);
4199
+ // 약간의 지연을 두어 클릭 이벤트가 처리되도록 함
4200
+ setTimeout(() => {
4201
+ setShowRecentSearches(false);
4202
+ }, 200);
4203
+ onBlur?.(e);
4204
+ };
4205
+ const handleRecentSearchClick = (searchTerm) => {
4206
+ setInternalValue(searchTerm);
4207
+ onChange?.(searchTerm);
4208
+ setShowRecentSearches(false);
4209
+ handleSearch(searchTerm);
4210
+ };
4211
+ const handleRemoveRecentSearch = (searchTerm) => {
4212
+ const newRecentSearches = recentSearches.filter(item => item !== searchTerm);
4213
+ setRecentSearches(newRecentSearches);
4214
+ try {
4215
+ localStorage.setItem(recentSearchesKey, JSON.stringify(newRecentSearches));
4216
+ }
4217
+ catch (error) {
4218
+ console.warn('최근 검색어를 삭제할 수 없습니다:', error);
4219
+ }
4220
+ };
4221
+ const handleClearAllRecentSearches = () => {
4222
+ setRecentSearches([]);
4223
+ try {
4224
+ localStorage.removeItem(recentSearchesKey);
4225
+ }
4226
+ catch (error) {
4227
+ console.warn('최근 검색어를 모두 삭제할 수 없습니다:', error);
4228
+ }
4229
+ };
4230
+ const handleSuggestionClick = (suggestion) => {
4231
+ setInternalValue(suggestion);
4232
+ onChange?.(suggestion);
4233
+ // 롤링 중단
4234
+ if (suggestionIntervalRef.current) {
4235
+ clearInterval(suggestionIntervalRef.current);
4236
+ suggestionIntervalRef.current = null;
4237
+ }
4238
+ handleSearch(suggestion);
4239
+ };
4240
+ const classes = clsx('designbase-search-bar', `designbase-search-bar--${size}`, `designbase-search-bar--${variant}`, {
4241
+ 'designbase-search-bar--disabled': disabled,
4242
+ 'designbase-search-bar--readonly': readOnly,
4243
+ 'designbase-search-bar--full-width': fullWidth,
4244
+ 'designbase-search-bar--has-value': currentValue && currentValue.length > 0,
4245
+ }, className);
4246
+ const inputClasses = clsx('designbase-search-bar__input', {
4247
+ 'designbase-search-bar__input--disabled': disabled,
4248
+ 'designbase-search-bar__input--readonly': readOnly,
4249
+ });
4250
+ // 현재 플레이스홀더 (value가 없을 때만 추천 검색어 롤링)
4251
+ const currentPlaceholder = suggestedSearches.length > 0 && !currentValue && currentValue === ''
4252
+ ? suggestedSearches[currentSuggestion]
4253
+ : placeholder;
4254
+ 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', {
4255
+ 'designbase-search-bar__suggestion-item--active': index === currentSuggestion
4256
+ }), onClick: () => handleSuggestionClick(suggestion), children: suggestion }, index))) })] }))] }));
4257
+ };
4258
+ SearchBar.displayName = 'SearchBar';
4259
+
4103
4260
  const Select = ({ value, defaultValue, options, label, placeholder = '선택하세요', multiple = false, searchable = false, disabled = false, readOnly = false, required = false, error = false, errorMessage, helperText, size = 'm', fullWidth = false, dropdownWidth = 'auto', position = 'bottom', maxHeight = 200, showClearButton = true, className, onChange, onFocus, onBlur, ...props }) => {
4104
4261
  const [isOpen, setIsOpen] = React.useState(false);
4105
4262
  const [selectedValue, setSelectedValue] = React.useState(value ?? defaultValue ?? (multiple ? [] : ''));
@@ -4244,9 +4401,6 @@ const Select = ({ value, defaultValue, options, label, placeholder = '선택하
4244
4401
  setSelectedValue(newValue);
4245
4402
  onChange?.(newValue);
4246
4403
  };
4247
- const handleSearchChange = (e) => {
4248
- setSearchTerm(e.target.value);
4249
- };
4250
4404
  const classes = clsx('designbase-select', `designbase-select--${size}`, {
4251
4405
  'designbase-select--open': isOpen,
4252
4406
  'designbase-select--error': error,
@@ -4254,9 +4408,11 @@ const Select = ({ value, defaultValue, options, label, placeholder = '선택하
4254
4408
  'designbase-select--readonly': readOnly,
4255
4409
  'designbase-select--full-width': fullWidth,
4256
4410
  'designbase-select--multiple': multiple,
4411
+ 'designbase-select--searchable': searchable,
4257
4412
  }, className);
4258
4413
  const triggerClasses = clsx('designbase-select__trigger', {
4259
4414
  'designbase-select__trigger--focused': isOpen,
4415
+ 'designbase-select__trigger--searchable': searchable,
4260
4416
  });
4261
4417
  const dropdownClasses = clsx('designbase-select__dropdown', `designbase-select__dropdown--${dropdownWidth}`, `designbase-select__dropdown--${position}`, {
4262
4418
  'designbase-select__dropdown--open': isOpen,
@@ -4264,23 +4420,23 @@ const Select = ({ value, defaultValue, options, label, placeholder = '선택하
4264
4420
  const filteredOptions = getFilteredOptions();
4265
4421
  const selectedLabels = getSelectedLabels();
4266
4422
  const hasValue = multiple ? selectedValue.length > 0 : selectedValue !== '';
4267
- return (jsxRuntime.jsxs("div", { className: classes, ref: containerRef, children: [label && (jsxRuntime.jsxs("label", { className: "designbase-select__label", children: [label, required && jsxRuntime.jsx("span", { className: "designbase-select__required", children: "*" })] })), jsxRuntime.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: [jsxRuntime.jsx("div", { className: "designbase-select__value", children: multiple ? (jsxRuntime.jsxs("div", { className: "designbase-select__tags", ref: tagsContainerRef, children: [selectedValue.map((value) => {
4268
- const option = options.find(opt => opt.value === value);
4269
- return (jsxRuntime.jsxs("span", { className: "designbase-select__tag", children: [jsxRuntime.jsx("span", { className: "designbase-select__tag-label", children: option?.label || value }), jsxRuntime.jsx("button", { type: "button", className: "designbase-select__tag-remove", onClick: (e) => {
4270
- e.stopPropagation();
4271
- handleRemoveValue(value);
4272
- }, children: jsxRuntime.jsx(icons.CloseIcon, { size: 12 }) })] }, value));
4273
- }), searchable && isOpen && (jsxRuntime.jsx("input", { ref: inputRef, type: "text", className: "designbase-select__search-input", value: searchTerm, onChange: handleSearchChange, placeholder: "\uAC80\uC0C9...", onClick: (e) => e.stopPropagation() }))] })) : (jsxRuntime.jsx("span", { className: "designbase-select__single-value", children: selectedLabels || placeholder })) }), jsxRuntime.jsxs("div", { className: "designbase-select__indicators", children: [showClearButton && hasValue && !disabled && !readOnly && (jsxRuntime.jsx("button", { type: "button", className: "designbase-select__clear-button", onClick: handleClearAll, "aria-label": "\uBAA8\uB4E0 \uAC12 \uC9C0\uC6B0\uAE30", children: jsxRuntime.jsx(icons.CloseIcon, { size: 16 }) })), jsxRuntime.jsx("div", { className: "designbase-select__chevron", children: isOpen ? jsxRuntime.jsx(icons.ChevronUpIcon, { size: 16 }) : jsxRuntime.jsx(icons.ChevronDownIcon, { size: 16 }) })] })] }), jsxRuntime.jsx("div", { className: dropdownClasses, ref: dropdownRef, children: jsxRuntime.jsx("div", { className: "designbase-select__options", style: { maxHeight: `${maxHeight}px` }, role: "listbox", children: filteredOptions.length === 0 ? (jsxRuntime.jsx("div", { className: "designbase-select__no-options", children: searchTerm ? '검색 결과가 없습니다.' : '옵션이 없습니다.' })) : (filteredOptions.map((option, index) => {
4274
- const isSelected = multiple
4275
- ? selectedValue.includes(option.value)
4276
- : selectedValue === option.value;
4277
- const isFocused = index === focusedIndex;
4278
- return (jsxRuntime.jsxs("div", { className: clsx('designbase-select__option', {
4279
- 'designbase-select__option--selected': isSelected,
4280
- 'designbase-select__option--focused': isFocused,
4281
- 'designbase-select__option--disabled': option.disabled,
4282
- }), onClick: () => handleOptionSelect(option), role: "option", "aria-selected": isSelected, children: [multiple && (jsxRuntime.jsx("div", { className: "designbase-select__checkbox", children: jsxRuntime.jsx(Checkbox, { isSelected: isSelected, isDisabled: option.disabled, size: "s", hasLabel: false, onChange: () => handleOptionSelect(option) }) })), jsxRuntime.jsx("span", { className: "designbase-select__option-label", children: option.label })] }, option.value));
4283
- })) }) }), helperText && !error && (jsxRuntime.jsx("p", { className: "designbase-select__helper-text", children: helperText })), error && errorMessage && (jsxRuntime.jsx("p", { className: "designbase-select__error-message", children: errorMessage }))] }));
4423
+ return (jsxRuntime.jsxs("div", { className: classes, ref: containerRef, children: [label && (jsxRuntime.jsxs("label", { className: "designbase-select__label", children: [label, required && jsxRuntime.jsx("span", { className: "designbase-select__required", children: "*" })] })), jsxRuntime.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: [jsxRuntime.jsx("div", { className: "designbase-select__value", children: multiple ? (jsxRuntime.jsx("div", { className: "designbase-select__tags", ref: tagsContainerRef, children: selectedValue.map((value) => {
4424
+ const option = options.find(opt => opt.value === value);
4425
+ return (jsxRuntime.jsxs("span", { className: "designbase-select__tag", children: [jsxRuntime.jsx("span", { className: "designbase-select__tag-label", children: option?.label || value }), jsxRuntime.jsx("button", { type: "button", className: "designbase-select__tag-remove", onClick: (e) => {
4426
+ e.stopPropagation();
4427
+ handleRemoveValue(value);
4428
+ }, children: jsxRuntime.jsx(icons.CloseIcon, { size: 12 }) })] }, value));
4429
+ }) })) : (jsxRuntime.jsx("span", { className: "designbase-select__single-value", children: selectedLabels || placeholder })) }), jsxRuntime.jsxs("div", { className: "designbase-select__indicators", children: [showClearButton && hasValue && !disabled && !readOnly && (jsxRuntime.jsx("button", { type: "button", className: "designbase-select__clear-button", onClick: handleClearAll, "aria-label": "\uBAA8\uB4E0 \uAC12 \uC9C0\uC6B0\uAE30", children: jsxRuntime.jsx(icons.CloseIcon, { size: 16 }) })), jsxRuntime.jsx("div", { className: "designbase-select__chevron", children: isOpen ? jsxRuntime.jsx(icons.ChevronUpIcon, { size: 16 }) : jsxRuntime.jsx(icons.ChevronDownIcon, { size: 16 }) })] })] }), jsxRuntime.jsxs("div", { className: dropdownClasses, ref: dropdownRef, children: [searchable && (jsxRuntime.jsx("div", { className: "designbase-select__search", onMouseDown: (e) => e.stopPropagation(), onClick: (e) => e.stopPropagation(), children: jsxRuntime.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() }) })), jsxRuntime.jsx("div", { className: "designbase-select__options", style: { maxHeight: `${maxHeight}px` }, role: "listbox", children: filteredOptions.length === 0 ? (jsxRuntime.jsx("div", { className: "designbase-select__no-options", children: searchTerm ? '검색 결과가 없습니다.' : '옵션이 없습니다.' })) : (filteredOptions.map((option, index) => {
4430
+ const isSelected = multiple
4431
+ ? selectedValue.includes(option.value)
4432
+ : selectedValue === option.value;
4433
+ const isFocused = index === focusedIndex;
4434
+ return (jsxRuntime.jsxs("div", { className: clsx('designbase-select__option', {
4435
+ 'designbase-select__option--selected': isSelected,
4436
+ 'designbase-select__option--focused': isFocused,
4437
+ 'designbase-select__option--disabled': option.disabled,
4438
+ }), onClick: () => handleOptionSelect(option), role: "option", "aria-selected": isSelected, children: [multiple && (jsxRuntime.jsx("div", { className: "designbase-select__checkbox", children: jsxRuntime.jsx(Checkbox, { isSelected: isSelected, isDisabled: option.disabled, size: "s", hasLabel: false, onChange: () => handleOptionSelect(option) }) })), jsxRuntime.jsx("span", { className: "designbase-select__option-label", children: option.label })] }, option.value));
4439
+ })) })] }), helperText && !error && (jsxRuntime.jsx("p", { className: "designbase-select__helper-text", children: helperText })), error && errorMessage && (jsxRuntime.jsx("p", { className: "designbase-select__error-message", children: errorMessage }))] }));
4284
4440
  };
4285
4441
  Select.displayName = 'Select';
4286
4442
 
@@ -5221,8 +5377,6 @@ const Carousel = ({ items, size = 'm', variant = 'default', theme = 'light', tra
5221
5377
  const handleTouchEndEvent = React.useCallback(() => {
5222
5378
  handleTouchEnd();
5223
5379
  }, [handleTouchEnd]);
5224
- // 현재 아이템
5225
- const currentItem = items[currentIndex];
5226
5380
  // 슬라이드 스타일 계산
5227
5381
  const getSlideStyle = () => {
5228
5382
  const containerWidth = containerRef.current?.offsetWidth || 1;
@@ -5237,9 +5391,6 @@ const Carousel = ({ items, size = 'm', variant = 'default', theme = 'light', tra
5237
5391
  // 네비게이션 버튼 비활성화 상태
5238
5392
  const isPrevDisabled = !infinite && currentIndex === 0;
5239
5393
  const isNextDisabled = !infinite && currentIndex === items.length - 1;
5240
- // 좋아요/북마크 상태
5241
- const isLiked = currentItem ? likedItems.has(currentItem.id) : false;
5242
- const isBookmarked = currentItem ? bookmarkedItems.has(currentItem.id) : false;
5243
5394
  if (items.length === 0)
5244
5395
  return null;
5245
5396
  return (jsxRuntime.jsxs("div", { className: clsx('designbase-carousel', `designbase-carousel--size-${size}`, `designbase-carousel--variant-${variant}`, `designbase-carousel--theme-${theme}`, `designbase-carousel--transition-${transition}`, `designbase-carousel--indicator-${indicatorStyle}`, {
@@ -5260,13 +5411,17 @@ const Carousel = ({ items, size = 'm', variant = 'default', theme = 'light', tra
5260
5411
  color: item.textColor || (item.image ? '#ffffff' : undefined),
5261
5412
  }, children: item.title })), showDescription && item.description && (jsxRuntime.jsx("p", { className: "designbase-carousel__slide-description", style: {
5262
5413
  color: item.textColor || (item.image ? '#ffffff' : undefined),
5263
- }, children: item.description })), item.meta && (jsxRuntime.jsxs("div", { className: "designbase-carousel__slide-meta", children: [item.meta.author && (jsxRuntime.jsx("span", { className: "designbase-carousel__slide-author", children: item.meta.author })), item.meta.date && (jsxRuntime.jsx("span", { className: "designbase-carousel__slide-date", children: item.meta.date })), item.meta.rating && (jsxRuntime.jsxs("span", { className: "designbase-carousel__slide-rating", children: ["\u2B50 ", item.meta.rating] }))] }))] })) : null] })), jsxRuntime.jsxs("div", { className: "designbase-carousel__slide-actions", children: [enableLike && (jsxRuntime.jsx("button", { className: clsx('designbase-carousel__action-button', 'designbase-carousel__action-button--like', { 'designbase-carousel__action-button--active': isLiked }), onClick: (e) => {
5414
+ }, children: item.description })), item.meta && (jsxRuntime.jsxs("div", { className: "designbase-carousel__slide-meta", children: [item.meta.author && (jsxRuntime.jsx("span", { className: "designbase-carousel__slide-author", children: item.meta.author })), item.meta.date && (jsxRuntime.jsx("span", { className: "designbase-carousel__slide-date", children: item.meta.date })), item.meta.rating && (jsxRuntime.jsxs("span", { className: "designbase-carousel__slide-rating", children: ["\u2B50 ", item.meta.rating] }))] }))] })) : null] })), jsxRuntime.jsxs("div", { className: "designbase-carousel__slide-actions", children: [enableLike && (jsxRuntime.jsx("button", { className: clsx('designbase-carousel__action-button', 'designbase-carousel__action-button--like', {
5415
+ 'designbase-carousel__action-button--active': likedItems.has(item.id),
5416
+ }), onClick: (e) => {
5264
5417
  e.stopPropagation();
5265
5418
  handleLike(item, index);
5266
- }, title: isLiked ? "좋아요 취소" : "좋아요", children: jsxRuntime.jsx(icons.HeartIcon, { size: iconSize, color: "currentColor" }) })), enableBookmark && (jsxRuntime.jsx("button", { className: clsx('designbase-carousel__action-button', 'designbase-carousel__action-button--bookmark', { 'designbase-carousel__action-button--active': isBookmarked }), onClick: (e) => {
5419
+ }, title: likedItems.has(item.id) ? "좋아요 취소" : "좋아요", children: jsxRuntime.jsx(icons.HeartIcon, { size: iconSize, color: "currentColor" }) })), enableBookmark && (jsxRuntime.jsx("button", { className: clsx('designbase-carousel__action-button', 'designbase-carousel__action-button--bookmark', {
5420
+ 'designbase-carousel__action-button--active': bookmarkedItems.has(item.id),
5421
+ }), onClick: (e) => {
5267
5422
  e.stopPropagation();
5268
5423
  handleBookmark(item, index);
5269
- }, title: isBookmarked ? "북마크 해제" : "북마크", children: jsxRuntime.jsx(icons.BookmarkIcon, { size: iconSize, color: "currentColor" }) })), enableShare && (jsxRuntime.jsx("button", { className: "designbase-carousel__action-button designbase-carousel__action-button--share", onClick: (e) => {
5424
+ }, title: bookmarkedItems.has(item.id) ? "북마크 해제" : "북마크", children: jsxRuntime.jsx(icons.BookmarkIcon, { size: iconSize, color: "currentColor" }) })), enableShare && (jsxRuntime.jsx("button", { className: "designbase-carousel__action-button designbase-carousel__action-button--share", onClick: (e) => {
5270
5425
  e.stopPropagation();
5271
5426
  handleShare(item, index);
5272
5427
  }, title: "\uACF5\uC720", children: jsxRuntime.jsx(icons.ShareAltIcon, { size: iconSize, color: "currentColor" }) })), enableDownload && item.image && (jsxRuntime.jsx("button", { className: "designbase-carousel__action-button designbase-carousel__action-button--download", onClick: (e) => {
@@ -8929,157 +9084,6 @@ const Tutorial = ({ steps, currentStep = 0, isActive, onStart, onEnd, onStepChan
8929
9084
  };
8930
9085
  Tutorial.displayName = 'Tutorial';
8931
9086
 
8932
- 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, ...props }) => {
8933
- const [internalValue, setInternalValue] = React.useState(defaultValue);
8934
- const [recentSearches, setRecentSearches] = React.useState([]);
8935
- const [showRecentSearches, setShowRecentSearches] = React.useState(false);
8936
- const [currentSuggestion, setCurrentSuggestion] = React.useState(0);
8937
- const [isFocused, setIsFocused] = React.useState(false);
8938
- const inputRef = React.useRef(null);
8939
- const suggestionIntervalRef = React.useRef(null);
8940
- const currentValue = value !== undefined ? value : internalValue;
8941
- // 최근 검색어 로드
8942
- React.useEffect(() => {
8943
- if (enableRecentSearches) {
8944
- try {
8945
- const stored = localStorage.getItem(recentSearchesKey);
8946
- if (stored) {
8947
- setRecentSearches(JSON.parse(stored));
8948
- }
8949
- }
8950
- catch (error) {
8951
- console.warn('최근 검색어를 불러올 수 없습니다:', error);
8952
- }
8953
- }
8954
- }, [enableRecentSearches, recentSearchesKey]);
8955
- // 추천 검색어 롤링 (value가 없을 때만)
8956
- React.useEffect(() => {
8957
- if (suggestedSearches.length > 0 && !currentValue && currentValue === '') {
8958
- suggestionIntervalRef.current = setInterval(() => {
8959
- setCurrentSuggestion(prev => (prev + 1) % suggestedSearches.length);
8960
- }, suggestionRollingInterval);
8961
- }
8962
- else {
8963
- if (suggestionIntervalRef.current) {
8964
- clearInterval(suggestionIntervalRef.current);
8965
- suggestionIntervalRef.current = null;
8966
- }
8967
- }
8968
- return () => {
8969
- if (suggestionIntervalRef.current) {
8970
- clearInterval(suggestionIntervalRef.current);
8971
- }
8972
- };
8973
- }, [suggestedSearches.length, currentValue, suggestionRollingInterval]);
8974
- React.useEffect(() => {
8975
- if (value !== undefined) {
8976
- setInternalValue(value);
8977
- }
8978
- }, [value]);
8979
- const handleChange = (e) => {
8980
- const newValue = e.target.value;
8981
- setInternalValue(newValue);
8982
- onChange?.(newValue);
8983
- };
8984
- const handleClear = () => {
8985
- setInternalValue('');
8986
- onChange?.('');
8987
- inputRef.current?.focus();
8988
- };
8989
- const handleSearch = (searchValue) => {
8990
- const searchTerm = searchValue || currentValue.trim();
8991
- if (searchTerm) {
8992
- onSearch?.(searchTerm);
8993
- // 최근 검색어 저장
8994
- if (enableRecentSearches) {
8995
- const newRecentSearches = [
8996
- searchTerm,
8997
- ...recentSearches.filter(item => item !== searchTerm)
8998
- ].slice(0, 10); // 최대 10개
8999
- setRecentSearches(newRecentSearches);
9000
- try {
9001
- localStorage.setItem(recentSearchesKey, JSON.stringify(newRecentSearches));
9002
- }
9003
- catch (error) {
9004
- console.warn('최근 검색어를 저장할 수 없습니다:', error);
9005
- }
9006
- }
9007
- }
9008
- };
9009
- const handleKeyDown = (e) => {
9010
- if (e.key === 'Enter') {
9011
- handleSearch();
9012
- }
9013
- onKeyDown?.(e);
9014
- };
9015
- const handleFocus = (e) => {
9016
- setIsFocused(true);
9017
- setShowRecentSearches(enableRecentSearches && recentSearches.length > 0);
9018
- onFocus?.(e);
9019
- };
9020
- const handleBlur = (e) => {
9021
- setIsFocused(false);
9022
- // 약간의 지연을 두어 클릭 이벤트가 처리되도록 함
9023
- setTimeout(() => {
9024
- setShowRecentSearches(false);
9025
- }, 200);
9026
- onBlur?.(e);
9027
- };
9028
- const handleRecentSearchClick = (searchTerm) => {
9029
- setInternalValue(searchTerm);
9030
- onChange?.(searchTerm);
9031
- setShowRecentSearches(false);
9032
- handleSearch(searchTerm);
9033
- };
9034
- const handleRemoveRecentSearch = (searchTerm) => {
9035
- const newRecentSearches = recentSearches.filter(item => item !== searchTerm);
9036
- setRecentSearches(newRecentSearches);
9037
- try {
9038
- localStorage.setItem(recentSearchesKey, JSON.stringify(newRecentSearches));
9039
- }
9040
- catch (error) {
9041
- console.warn('최근 검색어를 삭제할 수 없습니다:', error);
9042
- }
9043
- };
9044
- const handleClearAllRecentSearches = () => {
9045
- setRecentSearches([]);
9046
- try {
9047
- localStorage.removeItem(recentSearchesKey);
9048
- }
9049
- catch (error) {
9050
- console.warn('최근 검색어를 모두 삭제할 수 없습니다:', error);
9051
- }
9052
- };
9053
- const handleSuggestionClick = (suggestion) => {
9054
- setInternalValue(suggestion);
9055
- onChange?.(suggestion);
9056
- // 롤링 중단
9057
- if (suggestionIntervalRef.current) {
9058
- clearInterval(suggestionIntervalRef.current);
9059
- suggestionIntervalRef.current = null;
9060
- }
9061
- handleSearch(suggestion);
9062
- };
9063
- const classes = clsx('designbase-search-bar', `designbase-search-bar--${size}`, `designbase-search-bar--${variant}`, {
9064
- 'designbase-search-bar--disabled': disabled,
9065
- 'designbase-search-bar--readonly': readOnly,
9066
- 'designbase-search-bar--full-width': fullWidth,
9067
- 'designbase-search-bar--has-value': currentValue && currentValue.length > 0,
9068
- }, className);
9069
- const inputClasses = clsx('designbase-search-bar__input', {
9070
- 'designbase-search-bar__input--disabled': disabled,
9071
- 'designbase-search-bar__input--readonly': readOnly,
9072
- });
9073
- // 현재 플레이스홀더 (value가 없을 때만 추천 검색어 롤링)
9074
- const currentPlaceholder = suggestedSearches.length > 0 && !currentValue && currentValue === ''
9075
- ? suggestedSearches[currentSuggestion]
9076
- : placeholder;
9077
- 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", ...props }), 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', {
9078
- 'designbase-search-bar__suggestion-item--active': index === currentSuggestion
9079
- }), onClick: () => handleSuggestionClick(suggestion), children: suggestion }, index))) })] }))] }));
9080
- };
9081
- SearchBar.displayName = 'SearchBar';
9082
-
9083
9087
  const Navbar = ({ size = 'm', variant = 'default', position = 'static', logo, onLogoClick, items = [], onItemClick, userMenuItems = [], onUserMenuItemClick, userProfile, onLoginClick, onLogoutClick, isAuthenticated = false, showSearch = false, onSearch, searchPlaceholder = '검색...', fullWidth = false, shadow = false, className, ...props }) => {
9084
9088
  const [isMobileMenuOpen, setIsMobileMenuOpen] = React.useState(false);
9085
9089
  const [isUserMenuOpen, setIsUserMenuOpen] = React.useState(false);
@@ -11592,6 +11596,47 @@ const Toggle = React.forwardRef(({ isSelected, defaultSelected, isDisabled = fal
11592
11596
  });
11593
11597
  Toggle.displayName = 'Toggle';
11594
11598
 
11599
+ const clampRating = (rating, maxRating) => {
11600
+ if (typeof rating !== 'number' || Number.isNaN(rating))
11601
+ return undefined;
11602
+ if (rating < 0)
11603
+ return 0;
11604
+ if (rating > maxRating)
11605
+ return maxRating;
11606
+ return Math.round(rating * 10) / 10;
11607
+ };
11608
+ const Testimonial = ({ quote, author, role, company, avatar, rating, maxRating = 5, size = 'm', align = 'left', badge, children, className, badgeProps, avatarProps, ratingProps, }) => {
11609
+ const clampedRating = clampRating(rating, maxRating);
11610
+ const avatarSizeMap = {
11611
+ s: 's',
11612
+ m: 'm',
11613
+ l: 'l',
11614
+ };
11615
+ const ratingSizeMap = {
11616
+ s: 's',
11617
+ m: 'm',
11618
+ l: 'l',
11619
+ };
11620
+ const { className: badgeCustomClassName, size: badgeSize, variant: badgeVariant, style: badgeStyle, ...restBadgeProps } = badgeProps ?? {};
11621
+ const { className: avatarCustomClassName, size: avatarSize, alt: avatarAlt, src: avatarSrc, ...restAvatarProps } = avatarProps ?? {};
11622
+ const { className: ratingCustomClassName, maxValue: ratingMaxValueOverride, size: ratingSize, display: ratingDisplay, allowHalf: ratingAllowHalf, readonly: ratingReadonly, color: ratingColor, customColor: ratingCustomColor, ...restRatingProps } = ratingProps ?? {};
11623
+ const computedBadgeSize = badgeSize ?? 's';
11624
+ const computedBadgeVariant = badgeVariant ?? 'primary';
11625
+ const computedBadgeStyle = badgeStyle ?? 'text';
11626
+ const computedAvatarSrc = avatarSrc ?? avatar;
11627
+ const computedAvatarAlt = avatarAlt ?? `${author} 프로필 이미지`;
11628
+ const computedAvatarSize = avatarSize ?? avatarSizeMap[size];
11629
+ const computedRatingMaxValue = ratingMaxValueOverride ?? maxRating;
11630
+ const computedRatingSize = ratingSize ?? ratingSizeMap[size];
11631
+ const computedRatingDisplay = ratingDisplay ?? 'both';
11632
+ const computedRatingAllowHalf = ratingAllowHalf ?? true;
11633
+ const computedRatingReadonly = ratingReadonly ?? true;
11634
+ const computedRatingColor = ratingColor ?? 'primary';
11635
+ const computedRatingCustomColor = computedRatingColor === 'custom' ? ratingCustomColor : undefined;
11636
+ const shouldRenderAvatar = Boolean(computedAvatarSrc || restAvatarProps.initials || restAvatarProps.icon);
11637
+ return (jsxRuntime.jsxs("figure", { className: clsx('designbase-testimonial', `designbase-testimonial--size-${size}`, `designbase-testimonial--align-${align}`, className), children: [badge && (jsxRuntime.jsx(Badge, { size: computedBadgeSize, variant: computedBadgeVariant, style: computedBadgeStyle, className: clsx('designbase-testimonial__badge', badgeCustomClassName), ...restBadgeProps, children: badge })), jsxRuntime.jsxs("blockquote", { className: "designbase-testimonial__quote", children: ["\u201C", quote, "\u201D"] }), jsxRuntime.jsxs("figcaption", { className: "designbase-testimonial__footer", children: [shouldRenderAvatar && (jsxRuntime.jsx("div", { className: "designbase-testimonial__avatar", children: jsxRuntime.jsx(Avatar, { src: computedAvatarSrc, alt: computedAvatarAlt, size: computedAvatarSize, className: clsx('designbase-testimonial__avatar-image', avatarCustomClassName), ...restAvatarProps }) })), jsxRuntime.jsxs("div", { className: "designbase-testimonial__meta", children: [jsxRuntime.jsx("span", { className: "designbase-testimonial__author", children: author }), (role || company) && (jsxRuntime.jsx("span", { className: "designbase-testimonial__role", children: [role, company].filter(Boolean).join(' · ') }))] }), typeof clampedRating === 'number' && (jsxRuntime.jsx(Rating, { value: clampedRating, maxValue: computedRatingMaxValue, size: computedRatingSize, display: computedRatingDisplay, allowHalf: computedRatingAllowHalf, readonly: computedRatingReadonly, color: computedRatingColor, customColor: computedRatingCustomColor, className: clsx('designbase-testimonial__rating', ratingCustomClassName), ...restRatingProps }))] }), children && jsxRuntime.jsx("div", { className: "designbase-testimonial__extra", children: children })] }));
11638
+ };
11639
+
11595
11640
  const Toolbar = ({ items, size = 'm', variant = 'default', position = 'top', fullWidth = false, fixed = false, shadow = true, rounded = true, className, children, }) => {
11596
11641
  const [openDropdown, setOpenDropdown] = React.useState(null);
11597
11642
  const mapSizeToButtonSize = (size) => {
@@ -12221,6 +12266,7 @@ exports.Stat = Stat;
12221
12266
  exports.Stepper = Stepper;
12222
12267
  exports.Table = Table;
12223
12268
  exports.Tabs = Tabs;
12269
+ exports.Testimonial = Testimonial;
12224
12270
  exports.Textarea = Textarea;
12225
12271
  exports.TimePicker = TimePicker;
12226
12272
  exports.Timeline = Timeline;