@designbasekorea/ui 0.2.41 → 0.3.1

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
@@ -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,7 @@ 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
+ 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 }), !iconOnly && jsxRuntime.jsx("span", { children: "\uB85C\uB529 \uC911..." })] }));
2458
2453
  }
2459
2454
  // iconOnly 버튼일 때는 children을 아이콘으로 처리
2460
2455
  if (iconOnly && children && React.isValidElement(children)) {
@@ -4013,7 +4008,7 @@ ModalHeader.displayName = 'ModalHeader';
4013
4008
  ModalBody.displayName = 'ModalBody';
4014
4009
  ModalFooter.displayName = 'ModalFooter';
4015
4010
 
4016
- const Label = ({ children, htmlFor, required = false, size = 's', disabled = false, error = false, className, onClick, }) => {
4011
+ const Label = ({ children, htmlFor, id, required = false, size = 's', disabled = false, error = false, className, onClick, }) => {
4017
4012
  const classes = [
4018
4013
  'designbase-label',
4019
4014
  `designbase-label--${size}`,
@@ -4023,7 +4018,7 @@ const Label = ({ children, htmlFor, required = false, size = 's', disabled = fal
4023
4018
  ]
4024
4019
  .filter(Boolean)
4025
4020
  .join(' ');
4026
- return (jsxRuntime.jsxs("label", { htmlFor: htmlFor, className: classes, onClick: onClick, children: [children, required && jsxRuntime.jsx("span", { className: "designbase-label__required", children: "*" })] }));
4021
+ return (jsxRuntime.jsxs("label", { htmlFor: htmlFor, id: id, className: classes, onClick: onClick, children: [children, required && jsxRuntime.jsx("span", { className: "designbase-label__required", children: "*" })] }));
4027
4022
  };
4028
4023
  Label.displayName = 'Label';
4029
4024
 
@@ -4054,12 +4049,17 @@ const Input = React.forwardRef(({ type = 'text', label, placeholder, defaultValu
4054
4049
  'designbase-input--with-end-icon': EndIcon || isPassword,
4055
4050
  }, className);
4056
4051
  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];
4052
+ const iconSize = React.useMemo(() => {
4053
+ switch (size) {
4054
+ case 's':
4055
+ return 16;
4056
+ case 'l':
4057
+ return 24;
4058
+ case 'm':
4059
+ default:
4060
+ return 20;
4061
+ }
4062
+ }, [size]);
4063
4063
  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
4064
  });
4065
4065
  Input.displayName = 'Input';
@@ -4100,6 +4100,157 @@ const Checkbox = React.forwardRef(({ isSelected, defaultSelected, isIndeterminat
4100
4100
  });
4101
4101
  Checkbox.displayName = 'Checkbox';
4102
4102
 
4103
+ 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 }) => {
4104
+ const [internalValue, setInternalValue] = React.useState(defaultValue);
4105
+ const [recentSearches, setRecentSearches] = React.useState([]);
4106
+ const [showRecentSearches, setShowRecentSearches] = React.useState(false);
4107
+ const [currentSuggestion, setCurrentSuggestion] = React.useState(0);
4108
+ const [isFocused, setIsFocused] = React.useState(false);
4109
+ const inputRef = React.useRef(null);
4110
+ const suggestionIntervalRef = React.useRef(null);
4111
+ const currentValue = value !== undefined ? value : internalValue;
4112
+ // 최근 검색어 로드
4113
+ React.useEffect(() => {
4114
+ if (enableRecentSearches) {
4115
+ try {
4116
+ const stored = localStorage.getItem(recentSearchesKey);
4117
+ if (stored) {
4118
+ setRecentSearches(JSON.parse(stored));
4119
+ }
4120
+ }
4121
+ catch (error) {
4122
+ console.warn('최근 검색어를 불러올 수 없습니다:', error);
4123
+ }
4124
+ }
4125
+ }, [enableRecentSearches, recentSearchesKey]);
4126
+ // 추천 검색어 롤링 (value가 없을 때만)
4127
+ React.useEffect(() => {
4128
+ if (suggestedSearches.length > 0 && !currentValue && currentValue === '') {
4129
+ suggestionIntervalRef.current = setInterval(() => {
4130
+ setCurrentSuggestion(prev => (prev + 1) % suggestedSearches.length);
4131
+ }, suggestionRollingInterval);
4132
+ }
4133
+ else {
4134
+ if (suggestionIntervalRef.current) {
4135
+ clearInterval(suggestionIntervalRef.current);
4136
+ suggestionIntervalRef.current = null;
4137
+ }
4138
+ }
4139
+ return () => {
4140
+ if (suggestionIntervalRef.current) {
4141
+ clearInterval(suggestionIntervalRef.current);
4142
+ }
4143
+ };
4144
+ }, [suggestedSearches.length, currentValue, suggestionRollingInterval]);
4145
+ React.useEffect(() => {
4146
+ if (value !== undefined) {
4147
+ setInternalValue(value);
4148
+ }
4149
+ }, [value]);
4150
+ const handleChange = (e) => {
4151
+ const newValue = e.target.value;
4152
+ setInternalValue(newValue);
4153
+ onChange?.(newValue);
4154
+ };
4155
+ const handleClear = () => {
4156
+ setInternalValue('');
4157
+ onChange?.('');
4158
+ inputRef.current?.focus();
4159
+ };
4160
+ const handleSearch = (searchValue) => {
4161
+ const searchTerm = searchValue || currentValue.trim();
4162
+ if (searchTerm) {
4163
+ onSearch?.(searchTerm);
4164
+ // 최근 검색어 저장
4165
+ if (enableRecentSearches) {
4166
+ const newRecentSearches = [
4167
+ searchTerm,
4168
+ ...recentSearches.filter(item => item !== searchTerm)
4169
+ ].slice(0, 10); // 최대 10개
4170
+ setRecentSearches(newRecentSearches);
4171
+ try {
4172
+ localStorage.setItem(recentSearchesKey, JSON.stringify(newRecentSearches));
4173
+ }
4174
+ catch (error) {
4175
+ console.warn('최근 검색어를 저장할 수 없습니다:', error);
4176
+ }
4177
+ }
4178
+ }
4179
+ };
4180
+ const handleKeyDown = (e) => {
4181
+ if (e.key === 'Enter') {
4182
+ handleSearch();
4183
+ }
4184
+ onKeyDown?.(e);
4185
+ };
4186
+ const handleFocus = (e) => {
4187
+ setIsFocused(true);
4188
+ setShowRecentSearches(enableRecentSearches && recentSearches.length > 0);
4189
+ onFocus?.(e);
4190
+ };
4191
+ const handleBlur = (e) => {
4192
+ setIsFocused(false);
4193
+ // 약간의 지연을 두어 클릭 이벤트가 처리되도록 함
4194
+ setTimeout(() => {
4195
+ setShowRecentSearches(false);
4196
+ }, 200);
4197
+ onBlur?.(e);
4198
+ };
4199
+ const handleRecentSearchClick = (searchTerm) => {
4200
+ setInternalValue(searchTerm);
4201
+ onChange?.(searchTerm);
4202
+ setShowRecentSearches(false);
4203
+ handleSearch(searchTerm);
4204
+ };
4205
+ const handleRemoveRecentSearch = (searchTerm) => {
4206
+ const newRecentSearches = recentSearches.filter(item => item !== searchTerm);
4207
+ setRecentSearches(newRecentSearches);
4208
+ try {
4209
+ localStorage.setItem(recentSearchesKey, JSON.stringify(newRecentSearches));
4210
+ }
4211
+ catch (error) {
4212
+ console.warn('최근 검색어를 삭제할 수 없습니다:', error);
4213
+ }
4214
+ };
4215
+ const handleClearAllRecentSearches = () => {
4216
+ setRecentSearches([]);
4217
+ try {
4218
+ localStorage.removeItem(recentSearchesKey);
4219
+ }
4220
+ catch (error) {
4221
+ console.warn('최근 검색어를 모두 삭제할 수 없습니다:', error);
4222
+ }
4223
+ };
4224
+ const handleSuggestionClick = (suggestion) => {
4225
+ setInternalValue(suggestion);
4226
+ onChange?.(suggestion);
4227
+ // 롤링 중단
4228
+ if (suggestionIntervalRef.current) {
4229
+ clearInterval(suggestionIntervalRef.current);
4230
+ suggestionIntervalRef.current = null;
4231
+ }
4232
+ handleSearch(suggestion);
4233
+ };
4234
+ const classes = clsx('designbase-search-bar', `designbase-search-bar--${size}`, `designbase-search-bar--${variant}`, {
4235
+ 'designbase-search-bar--disabled': disabled,
4236
+ 'designbase-search-bar--readonly': readOnly,
4237
+ 'designbase-search-bar--full-width': fullWidth,
4238
+ 'designbase-search-bar--has-value': currentValue && currentValue.length > 0,
4239
+ }, className);
4240
+ const inputClasses = clsx('designbase-search-bar__input', {
4241
+ 'designbase-search-bar__input--disabled': disabled,
4242
+ 'designbase-search-bar__input--readonly': readOnly,
4243
+ });
4244
+ // 현재 플레이스홀더 (value가 없을 때만 추천 검색어 롤링)
4245
+ const currentPlaceholder = suggestedSearches.length > 0 && !currentValue && currentValue === ''
4246
+ ? suggestedSearches[currentSuggestion]
4247
+ : placeholder;
4248
+ 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', {
4249
+ 'designbase-search-bar__suggestion-item--active': index === currentSuggestion
4250
+ }), onClick: () => handleSuggestionClick(suggestion), children: suggestion }, index))) })] }))] }));
4251
+ };
4252
+ SearchBar.displayName = 'SearchBar';
4253
+
4103
4254
  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
4255
  const [isOpen, setIsOpen] = React.useState(false);
4105
4256
  const [selectedValue, setSelectedValue] = React.useState(value ?? defaultValue ?? (multiple ? [] : ''));
@@ -4244,9 +4395,6 @@ const Select = ({ value, defaultValue, options, label, placeholder = '선택하
4244
4395
  setSelectedValue(newValue);
4245
4396
  onChange?.(newValue);
4246
4397
  };
4247
- const handleSearchChange = (e) => {
4248
- setSearchTerm(e.target.value);
4249
- };
4250
4398
  const classes = clsx('designbase-select', `designbase-select--${size}`, {
4251
4399
  'designbase-select--open': isOpen,
4252
4400
  'designbase-select--error': error,
@@ -4254,9 +4402,11 @@ const Select = ({ value, defaultValue, options, label, placeholder = '선택하
4254
4402
  'designbase-select--readonly': readOnly,
4255
4403
  'designbase-select--full-width': fullWidth,
4256
4404
  'designbase-select--multiple': multiple,
4405
+ 'designbase-select--searchable': searchable,
4257
4406
  }, className);
4258
4407
  const triggerClasses = clsx('designbase-select__trigger', {
4259
4408
  'designbase-select__trigger--focused': isOpen,
4409
+ 'designbase-select__trigger--searchable': searchable,
4260
4410
  });
4261
4411
  const dropdownClasses = clsx('designbase-select__dropdown', `designbase-select__dropdown--${dropdownWidth}`, `designbase-select__dropdown--${position}`, {
4262
4412
  'designbase-select__dropdown--open': isOpen,
@@ -4264,23 +4414,23 @@ const Select = ({ value, defaultValue, options, label, placeholder = '선택하
4264
4414
  const filteredOptions = getFilteredOptions();
4265
4415
  const selectedLabels = getSelectedLabels();
4266
4416
  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 }))] }));
4417
+ 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) => {
4418
+ const option = options.find(opt => opt.value === value);
4419
+ 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) => {
4420
+ e.stopPropagation();
4421
+ handleRemoveValue(value);
4422
+ }, children: jsxRuntime.jsx(icons.CloseIcon, { size: 12 }) })] }, value));
4423
+ }) })) : (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) => {
4424
+ const isSelected = multiple
4425
+ ? selectedValue.includes(option.value)
4426
+ : selectedValue === option.value;
4427
+ const isFocused = index === focusedIndex;
4428
+ return (jsxRuntime.jsxs("div", { className: clsx('designbase-select__option', {
4429
+ 'designbase-select__option--selected': isSelected,
4430
+ 'designbase-select__option--focused': isFocused,
4431
+ 'designbase-select__option--disabled': option.disabled,
4432
+ }), 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));
4433
+ })) })] }), 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
4434
  };
4285
4435
  Select.displayName = 'Select';
4286
4436
 
@@ -5221,8 +5371,6 @@ const Carousel = ({ items, size = 'm', variant = 'default', theme = 'light', tra
5221
5371
  const handleTouchEndEvent = React.useCallback(() => {
5222
5372
  handleTouchEnd();
5223
5373
  }, [handleTouchEnd]);
5224
- // 현재 아이템
5225
- const currentItem = items[currentIndex];
5226
5374
  // 슬라이드 스타일 계산
5227
5375
  const getSlideStyle = () => {
5228
5376
  const containerWidth = containerRef.current?.offsetWidth || 1;
@@ -5237,9 +5385,6 @@ const Carousel = ({ items, size = 'm', variant = 'default', theme = 'light', tra
5237
5385
  // 네비게이션 버튼 비활성화 상태
5238
5386
  const isPrevDisabled = !infinite && currentIndex === 0;
5239
5387
  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
5388
  if (items.length === 0)
5244
5389
  return null;
5245
5390
  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 +5405,17 @@ const Carousel = ({ items, size = 'm', variant = 'default', theme = 'light', tra
5260
5405
  color: item.textColor || (item.image ? '#ffffff' : undefined),
5261
5406
  }, children: item.title })), showDescription && item.description && (jsxRuntime.jsx("p", { className: "designbase-carousel__slide-description", style: {
5262
5407
  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) => {
5408
+ }, 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', {
5409
+ 'designbase-carousel__action-button--active': likedItems.has(item.id),
5410
+ }), onClick: (e) => {
5264
5411
  e.stopPropagation();
5265
5412
  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) => {
5413
+ }, 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', {
5414
+ 'designbase-carousel__action-button--active': bookmarkedItems.has(item.id),
5415
+ }), onClick: (e) => {
5267
5416
  e.stopPropagation();
5268
5417
  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) => {
5418
+ }, 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
5419
  e.stopPropagation();
5271
5420
  handleShare(item, index);
5272
5421
  }, 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 +9078,6 @@ const Tutorial = ({ steps, currentStep = 0, isActive, onStart, onEnd, onStepChan
8929
9078
  };
8930
9079
  Tutorial.displayName = 'Tutorial';
8931
9080
 
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
9081
  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
9082
  const [isMobileMenuOpen, setIsMobileMenuOpen] = React.useState(false);
9085
9083
  const [isUserMenuOpen, setIsUserMenuOpen] = React.useState(false);
@@ -11592,6 +11590,47 @@ const Toggle = React.forwardRef(({ isSelected, defaultSelected, isDisabled = fal
11592
11590
  });
11593
11591
  Toggle.displayName = 'Toggle';
11594
11592
 
11593
+ const clampRating = (rating, maxRating) => {
11594
+ if (typeof rating !== 'number' || Number.isNaN(rating))
11595
+ return undefined;
11596
+ if (rating < 0)
11597
+ return 0;
11598
+ if (rating > maxRating)
11599
+ return maxRating;
11600
+ return Math.round(rating * 10) / 10;
11601
+ };
11602
+ const Testimonial = ({ quote, author, role, company, avatar, rating, maxRating = 5, size = 'm', align = 'left', badge, children, className, badgeProps, avatarProps, ratingProps, }) => {
11603
+ const clampedRating = clampRating(rating, maxRating);
11604
+ const avatarSizeMap = {
11605
+ s: 's',
11606
+ m: 'm',
11607
+ l: 'l',
11608
+ };
11609
+ const ratingSizeMap = {
11610
+ s: 's',
11611
+ m: 'm',
11612
+ l: 'l',
11613
+ };
11614
+ const { className: badgeCustomClassName, size: badgeSize, variant: badgeVariant, style: badgeStyle, ...restBadgeProps } = badgeProps ?? {};
11615
+ const { className: avatarCustomClassName, size: avatarSize, alt: avatarAlt, src: avatarSrc, ...restAvatarProps } = avatarProps ?? {};
11616
+ const { className: ratingCustomClassName, maxValue: ratingMaxValueOverride, size: ratingSize, display: ratingDisplay, allowHalf: ratingAllowHalf, readonly: ratingReadonly, color: ratingColor, customColor: ratingCustomColor, ...restRatingProps } = ratingProps ?? {};
11617
+ const computedBadgeSize = badgeSize ?? 's';
11618
+ const computedBadgeVariant = badgeVariant ?? 'primary';
11619
+ const computedBadgeStyle = badgeStyle ?? 'text';
11620
+ const computedAvatarSrc = avatarSrc ?? avatar;
11621
+ const computedAvatarAlt = avatarAlt ?? `${author} 프로필 이미지`;
11622
+ const computedAvatarSize = avatarSize ?? avatarSizeMap[size];
11623
+ const computedRatingMaxValue = ratingMaxValueOverride ?? maxRating;
11624
+ const computedRatingSize = ratingSize ?? ratingSizeMap[size];
11625
+ const computedRatingDisplay = ratingDisplay ?? 'both';
11626
+ const computedRatingAllowHalf = ratingAllowHalf ?? true;
11627
+ const computedRatingReadonly = ratingReadonly ?? true;
11628
+ const computedRatingColor = ratingColor ?? 'primary';
11629
+ const computedRatingCustomColor = computedRatingColor === 'custom' ? ratingCustomColor : undefined;
11630
+ const shouldRenderAvatar = Boolean(computedAvatarSrc || restAvatarProps.initials || restAvatarProps.icon);
11631
+ 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 })] }));
11632
+ };
11633
+
11595
11634
  const Toolbar = ({ items, size = 'm', variant = 'default', position = 'top', fullWidth = false, fixed = false, shadow = true, rounded = true, className, children, }) => {
11596
11635
  const [openDropdown, setOpenDropdown] = React.useState(null);
11597
11636
  const mapSizeToButtonSize = (size) => {
@@ -12221,6 +12260,7 @@ exports.Stat = Stat;
12221
12260
  exports.Stepper = Stepper;
12222
12261
  exports.Table = Table;
12223
12262
  exports.Tabs = Tabs;
12263
+ exports.Testimonial = Testimonial;
12224
12264
  exports.Textarea = Textarea;
12225
12265
  exports.TimePicker = TimePicker;
12226
12266
  exports.Timeline = Timeline;