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