@pk-design/react-tailwind 1.8.1 → 1.9.0
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/cjs/components/Banner/Banner.js +1 -1
- package/dist/cjs/components/Banner/Banner.js.map +1 -1
- package/dist/cjs/components/Button/Button.js +88 -60
- package/dist/cjs/components/Button/Button.js.map +1 -1
- package/dist/cjs/components/SelectBox/SelectBox.js +25 -4
- package/dist/cjs/components/SelectBox/SelectBox.js.map +1 -1
- package/dist/cjs/components/Toast/Toast.js +1 -1
- package/dist/cjs/components/Toast/Toast.js.map +1 -1
- package/dist/es/components/Banner/Banner.js +1 -1
- package/dist/es/components/Banner/Banner.js.map +1 -1
- package/dist/es/components/Button/Button.js +89 -61
- package/dist/es/components/Button/Button.js.map +1 -1
- package/dist/es/components/SelectBox/SelectBox.js +26 -5
- package/dist/es/components/SelectBox/SelectBox.js.map +1 -1
- package/dist/es/components/Toast/Toast.js +1 -1
- package/dist/es/components/Toast/Toast.js.map +1 -1
- package/dist/react-tailwind.cjs.js +115 -66
- package/dist/react-tailwind.cjs.js.map +1 -1
- package/dist/react-tailwind.es.js +116 -67
- package/dist/react-tailwind.es.js.map +1 -1
- package/dist/types/components/Button/Button.types.d.ts +12 -6
- package/dist/types/components/SelectBox/SelectBox.types.d.ts +18 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import React__default, { useId, useMemo, useState, forwardRef, Children, isValidElement, cloneElement, useRef, useImperativeHandle,
|
|
3
|
+
import React__default, { useId, useMemo, useState, forwardRef, useEffect, Children, isValidElement, cloneElement, useRef, useImperativeHandle, useCallback, Fragment as Fragment$1, createContext, useContext, useLayoutEffect } from 'react';
|
|
4
4
|
import { twMerge } from 'tailwind-merge';
|
|
5
5
|
import { Dialog, DialogBackdrop, DialogPanel, DialogTitle, Menu, MenuButton, MenuItems, MenuItem, Combobox, ComboboxInput, ComboboxButton, ComboboxOptions, ComboboxOption, TabGroup, TabList, Tab, TabPanels, TabPanel, Transition } from '@headlessui/react';
|
|
6
6
|
import { FixedSizeList } from 'react-window';
|
|
@@ -131,59 +131,35 @@ TextContent.displayName = 'TextContent';
|
|
|
131
131
|
|
|
132
132
|
/**
|
|
133
133
|
* Tailwind CSS classes for theme + variant combinations.
|
|
134
|
+
*
|
|
135
|
+
* Only colours live here. Focus outline width/offset and disabled styling are
|
|
136
|
+
* applied uniformly in the component so they can be toggled in one place.
|
|
134
137
|
*/
|
|
135
138
|
const THEMED_BTN_CLASS = {
|
|
136
139
|
primary: {
|
|
137
|
-
default: 'text-white bg-[var(--ui-primary)] hover:bg-[var(--ui-primary-hover)] focus-visible:
|
|
138
|
-
outlined: 'text-[var(--ui-primary-text)] hover:text-white border border-[var(--ui-primary)] hover:bg-[var(--ui-primary)] focus-visible:
|
|
139
|
-
plain: 'text-[var(--ui-primary-text)] hover:bg-
|
|
140
|
+
default: 'text-white bg-[var(--ui-primary)] hover:bg-[var(--ui-primary-hover)] focus-visible:outline-[var(--ui-primary-ring)]',
|
|
141
|
+
outlined: 'text-[var(--ui-primary-text)] hover:text-white border border-[var(--ui-primary)] hover:bg-[var(--ui-primary)] focus-visible:outline-[var(--ui-primary-ring)]',
|
|
142
|
+
plain: 'text-[var(--ui-primary-text)] hover:bg-current/10 active:bg-current/15 focus-visible:outline-[var(--ui-primary-ring)]',
|
|
140
143
|
},
|
|
141
144
|
danger: {
|
|
142
|
-
default: 'text-white bg-[var(--ui-danger)] hover:bg-[var(--ui-danger-hover)] focus-visible:
|
|
143
|
-
outlined: 'text-[var(--ui-danger-text)] hover:text-white border border-[var(--ui-danger)] hover:bg-[var(--ui-danger)] focus-visible:
|
|
144
|
-
plain: 'text-[var(--ui-danger-text)] hover:bg-
|
|
145
|
+
default: 'text-white bg-[var(--ui-danger)] hover:bg-[var(--ui-danger-hover)] focus-visible:outline-[var(--ui-danger-ring)]',
|
|
146
|
+
outlined: 'text-[var(--ui-danger-text)] hover:text-white border border-[var(--ui-danger)] hover:bg-[var(--ui-danger)] focus-visible:outline-[var(--ui-danger-ring)]',
|
|
147
|
+
plain: 'text-[var(--ui-danger-text)] hover:bg-current/10 active:bg-current/15 focus-visible:outline-[var(--ui-danger-ring)]',
|
|
145
148
|
},
|
|
146
149
|
secondary: {
|
|
147
|
-
default: 'text-gray-700 bg-white border border-[var(--ui-border)] hover:bg-gray-100 dark:bg-gray-800 dark:text-white dark:hover:bg-gray-700 focus-visible:
|
|
148
|
-
outlined: 'text-gray-700 hover:text-white border border-[var(--ui-border)] hover:bg-gray-700 dark:text-gray-200 dark:hover:text-white dark:hover:bg-gray-600 focus-visible:
|
|
149
|
-
plain: 'text-gray-700 hover:bg-
|
|
150
|
+
default: 'text-gray-700 bg-white border border-[var(--ui-border)] hover:bg-gray-100 dark:bg-gray-800 dark:text-white dark:hover:bg-gray-700 focus-visible:outline-[var(--ui-focus-ring)]',
|
|
151
|
+
outlined: 'text-gray-700 hover:text-white border border-[var(--ui-border)] hover:bg-gray-700 dark:text-gray-200 dark:hover:text-white dark:hover:bg-gray-600 focus-visible:outline-[var(--ui-focus-ring)]',
|
|
152
|
+
plain: 'text-gray-700 hover:bg-current/10 active:bg-current/15 dark:text-gray-200 focus-visible:outline-[var(--ui-focus-ring)]',
|
|
150
153
|
},
|
|
151
154
|
success: {
|
|
152
|
-
default: 'text-white bg-emerald-600 hover:bg-emerald-700 focus-visible:
|
|
153
|
-
outlined: 'text-emerald-
|
|
154
|
-
plain: 'text-emerald-
|
|
155
|
+
default: 'text-white bg-emerald-600 hover:bg-emerald-700 focus-visible:outline-emerald-500',
|
|
156
|
+
outlined: 'text-emerald-700 hover:text-white border border-emerald-600 hover:bg-emerald-600 dark:text-emerald-400 dark:border-emerald-500 focus-visible:outline-emerald-500',
|
|
157
|
+
plain: 'text-emerald-700 hover:bg-current/10 active:bg-current/15 dark:text-emerald-400 focus-visible:outline-emerald-500',
|
|
155
158
|
},
|
|
156
159
|
warning: {
|
|
157
|
-
default: 'text-white bg-amber-500 hover:bg-amber-600 focus-visible:
|
|
158
|
-
outlined: 'text-amber-
|
|
159
|
-
plain: 'text-amber-
|
|
160
|
-
},
|
|
161
|
-
};
|
|
162
|
-
const LOADER_TEXT_CLASS = {
|
|
163
|
-
primary: {
|
|
164
|
-
default: 'text-white',
|
|
165
|
-
outlined: 'text-[var(--ui-primary-text)]',
|
|
166
|
-
plain: 'text-[var(--ui-primary-text)]',
|
|
167
|
-
},
|
|
168
|
-
danger: {
|
|
169
|
-
default: 'text-white',
|
|
170
|
-
outlined: 'text-[var(--ui-danger-text)]',
|
|
171
|
-
plain: 'text-[var(--ui-danger-text)]',
|
|
172
|
-
},
|
|
173
|
-
secondary: {
|
|
174
|
-
default: 'text-gray-700 dark:text-white',
|
|
175
|
-
outlined: 'text-gray-700 dark:text-gray-200',
|
|
176
|
-
plain: 'text-gray-700 dark:text-gray-200',
|
|
177
|
-
},
|
|
178
|
-
success: {
|
|
179
|
-
default: 'text-white',
|
|
180
|
-
outlined: 'text-emerald-600 dark:text-emerald-400',
|
|
181
|
-
plain: 'text-emerald-600 dark:text-emerald-400',
|
|
182
|
-
},
|
|
183
|
-
warning: {
|
|
184
|
-
default: 'text-white',
|
|
185
|
-
outlined: 'text-amber-600 dark:text-amber-400',
|
|
186
|
-
plain: 'text-amber-600 dark:text-amber-400',
|
|
160
|
+
default: 'text-white bg-amber-500 hover:bg-amber-600 focus-visible:outline-amber-500',
|
|
161
|
+
outlined: 'text-amber-700 hover:text-white border border-amber-500 hover:bg-amber-500 dark:text-amber-400 dark:border-amber-500 focus-visible:outline-amber-500',
|
|
162
|
+
plain: 'text-amber-700 hover:bg-current/10 active:bg-current/15 dark:text-amber-400 focus-visible:outline-amber-500',
|
|
187
163
|
},
|
|
188
164
|
};
|
|
189
165
|
const SIZE_CLASSES = {
|
|
@@ -203,22 +179,32 @@ const LOADER_SIZE = {
|
|
|
203
179
|
};
|
|
204
180
|
/**
|
|
205
181
|
* Base shared tailwind classes.
|
|
182
|
+
*
|
|
183
|
+
* Disabled styling is keyed off both the native `disabled` attribute and
|
|
184
|
+
* `aria-disabled`, so non-button elements rendered via `as` look disabled too.
|
|
206
185
|
*/
|
|
207
186
|
const BASE_BTN_CLASS = 'font-medium inline-flex items-center justify-center shrink-0 cursor-pointer select-none ' +
|
|
208
|
-
'
|
|
209
|
-
'active:scale-[0.98]
|
|
187
|
+
'transition-[color,background-color,border-color,box-shadow,opacity,transform] duration-150 ease-out ' +
|
|
188
|
+
'active:scale-[0.98] focus:outline-none ' +
|
|
189
|
+
'disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none ' +
|
|
190
|
+
'aria-disabled:pointer-events-none aria-disabled:opacity-50 aria-disabled:shadow-none';
|
|
210
191
|
const Button = forwardRef(({ as, type, ...props }, ref) => {
|
|
211
|
-
var _a, _b, _c, _d;
|
|
212
192
|
const { className = '', children, theme = 'primary', variant = 'default', size = 'md', leftIcon, rightIcon, fullWidth = false, loading = false, loadingText = '', rounded = false, disabled, iconOnly = false, noOutlineOnFocus = false, active, onClick, ...restProps } = props;
|
|
213
193
|
const Component = as || 'button';
|
|
214
194
|
const isNativeButton = Component === 'button';
|
|
195
|
+
// Clean invalid props when not rendering a real button
|
|
196
|
+
const { type: _ignoreType, disabled: _ignoreDisabled, role, tabIndex, onKeyDown, onKeyUp, ...domProps } = restProps;
|
|
197
|
+
const ariaLabel = domProps['aria-label'];
|
|
198
|
+
const ariaLabelledBy = domProps['aria-labelledby'];
|
|
215
199
|
// Accessibility guard for icon-only buttons
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
200
|
+
useEffect(() => {
|
|
201
|
+
if (iconOnly && !ariaLabel && !ariaLabelledBy) {
|
|
202
|
+
console.warn('[ui] <Button iconOnly> requires aria-label or aria-labelledby for accessibility.');
|
|
203
|
+
}
|
|
204
|
+
}, [iconOnly, ariaLabel, ariaLabelledBy]);
|
|
219
205
|
const resolvedDisabled = Boolean(disabled || loading);
|
|
220
|
-
//
|
|
221
|
-
const
|
|
206
|
+
// Links (`href`, or `to` for router Link components) keep their native semantics.
|
|
207
|
+
const isLink = !isNativeButton && (domProps.href != null || domProps.to != null);
|
|
222
208
|
const handleClick = (e) => {
|
|
223
209
|
if (resolvedDisabled) {
|
|
224
210
|
e.preventDefault();
|
|
@@ -227,31 +213,73 @@ const Button = forwardRef(({ as, type, ...props }, ref) => {
|
|
|
227
213
|
}
|
|
228
214
|
onClick === null || onClick === void 0 ? void 0 : onClick(e);
|
|
229
215
|
};
|
|
216
|
+
// Mirror native <button> keyboard activation for non-button elements:
|
|
217
|
+
// Enter activates on keydown, Space on keyup (and must not scroll the page).
|
|
218
|
+
const handleKeyDown = (e) => {
|
|
219
|
+
onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(e);
|
|
220
|
+
if (e.defaultPrevented || e.target !== e.currentTarget)
|
|
221
|
+
return;
|
|
222
|
+
if (e.key === 'Enter') {
|
|
223
|
+
e.preventDefault();
|
|
224
|
+
e.currentTarget.click();
|
|
225
|
+
}
|
|
226
|
+
else if (e.key === ' ') {
|
|
227
|
+
e.preventDefault();
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
const handleKeyUp = (e) => {
|
|
231
|
+
onKeyUp === null || onKeyUp === void 0 ? void 0 : onKeyUp(e);
|
|
232
|
+
if (e.defaultPrevented || e.target !== e.currentTarget)
|
|
233
|
+
return;
|
|
234
|
+
if (e.key === ' ') {
|
|
235
|
+
e.preventDefault();
|
|
236
|
+
e.currentTarget.click();
|
|
237
|
+
}
|
|
238
|
+
};
|
|
230
239
|
const elementProps = isNativeButton
|
|
231
240
|
? {
|
|
232
241
|
type: type !== null && type !== void 0 ? type : 'button',
|
|
233
242
|
disabled: resolvedDisabled,
|
|
243
|
+
role,
|
|
244
|
+
tabIndex,
|
|
234
245
|
onClick: handleClick,
|
|
246
|
+
onKeyDown,
|
|
247
|
+
onKeyUp,
|
|
235
248
|
}
|
|
236
|
-
:
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
249
|
+
: isLink
|
|
250
|
+
? {
|
|
251
|
+
role,
|
|
252
|
+
tabIndex: resolvedDisabled ? -1 : tabIndex,
|
|
253
|
+
'aria-disabled': resolvedDisabled ? true : undefined,
|
|
254
|
+
onClick: handleClick,
|
|
255
|
+
onKeyDown,
|
|
256
|
+
onKeyUp,
|
|
257
|
+
}
|
|
258
|
+
: {
|
|
259
|
+
role: role !== null && role !== void 0 ? role : 'button',
|
|
260
|
+
tabIndex: resolvedDisabled ? -1 : (tabIndex !== null && tabIndex !== void 0 ? tabIndex : 0),
|
|
261
|
+
'aria-disabled': resolvedDisabled ? true : undefined,
|
|
262
|
+
onClick: handleClick,
|
|
263
|
+
onKeyDown: handleKeyDown,
|
|
264
|
+
onKeyUp: handleKeyUp,
|
|
265
|
+
};
|
|
242
266
|
const sizeObj = SIZE_CLASSES[size] || SIZE_CLASSES.md;
|
|
243
267
|
const classes = useMemo(() => {
|
|
244
268
|
var _a;
|
|
245
|
-
return buildClassName(BASE_BTN_CLASS, ((_a = THEMED_BTN_CLASS[theme]) === null || _a === void 0 ? void 0 : _a[variant]) || THEMED_BTN_CLASS.primary.default, iconOnly ? sizeObj.iconOnly : sizeObj.normal,
|
|
269
|
+
return buildClassName(BASE_BTN_CLASS, ((_a = THEMED_BTN_CLASS[theme]) === null || _a === void 0 ? void 0 : _a[variant]) || THEMED_BTN_CLASS.primary.default, iconOnly ? sizeObj.iconOnly : sizeObj.normal,
|
|
270
|
+
// An outline (not a ring) so the offset gap stays transparent on any surface/mode.
|
|
271
|
+
!noOutlineOnFocus &&
|
|
272
|
+
'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-solid', rounded && 'rounded-full', fullWidth && 'w-full', variant === 'default' && 'shadow-xs hover:shadow-none', active && 'ring-2 ring-current', className);
|
|
246
273
|
}, [theme, variant, sizeObj, iconOnly, noOutlineOnFocus, rounded, fullWidth, active, className]);
|
|
247
|
-
|
|
274
|
+
// The spinner and loading text inherit the button's text colour.
|
|
275
|
+
const loaderIcon = (jsx(Loader, { "data-testid": "btn-loader-icon", size: LOADER_SIZE[size] || 'xs', className: "text-current" }));
|
|
248
276
|
/**
|
|
249
277
|
* SPECIAL CASE: iconOnly + loading → show spinner only
|
|
250
278
|
*/
|
|
251
279
|
if (loading && iconOnly) {
|
|
252
|
-
return (jsx(Component, { ref: ref, className: classes, "aria-busy": "true", "aria-pressed": active !== undefined ? active : undefined, ...
|
|
280
|
+
return (jsx(Component, { ref: ref, className: classes, "aria-busy": "true", "aria-pressed": active !== undefined ? active : undefined, ...domProps, ...elementProps, children: loaderIcon }));
|
|
253
281
|
}
|
|
254
|
-
return (jsx(Component, { ref: ref, className: classes, "aria-busy": loading ? 'true' : undefined, "aria-pressed": active !== undefined ? active : undefined, ...
|
|
282
|
+
return (jsx(Component, { ref: ref, className: classes, "aria-busy": loading ? 'true' : undefined, "aria-pressed": active !== undefined ? active : undefined, ...domProps, ...elementProps, children: loading ? (jsxs("span", { className: "inline-flex items-center gap-2", "aria-live": "polite", children: [loaderIcon, loadingText ? (jsx(TextContent, { className: "text-current", children: loadingText })) : (children)] })) : (jsxs(Fragment, { children: [leftIcon && jsx("span", { className: "shrink-0 inline-flex items-center", children: leftIcon }), children, rightIcon && jsx("span", { className: "shrink-0 inline-flex items-center", children: rightIcon })] })) }));
|
|
255
283
|
});
|
|
256
284
|
Button.displayName = 'Button';
|
|
257
285
|
|
|
@@ -575,7 +603,7 @@ const Banner = forwardRef((props, ref) => {
|
|
|
575
603
|
const { type = 'info', iconSize = 'sm', children, icon, removable = false, onRemove, className, ...restProps } = props;
|
|
576
604
|
const DefaultIcon = IconMap[type];
|
|
577
605
|
const iconNode = icon !== undefined ? (icon) : (jsx(DefaultIcon, { "data-testid": `banner-${type}-icon`, className: buildClassName('shrink-0', sizeMap[iconSize]) }));
|
|
578
|
-
return (jsxs("div", { ref: ref, className: buildClassName('text-sm rounded-md flex w-full p-3 items-center gap-3 ring-1 ring-inset', TypeClassMap[type], className), "data-testid": `banner-${type}`, ...restProps, children: [iconNode, jsx("div", { className: "flex items-center w-full justify-between", children: children }), removable && (jsx(Button, { iconOnly: true, "aria-label": "Dismiss banner", size: "xs", variant: "plain",
|
|
606
|
+
return (jsxs("div", { ref: ref, className: buildClassName('text-sm rounded-md flex w-full p-3 items-center gap-3 ring-1 ring-inset', TypeClassMap[type], className), "data-testid": `banner-${type}`, ...restProps, children: [iconNode, jsx("div", { className: "flex items-center w-full justify-between", children: children }), removable && (jsx(Button, { iconOnly: true, "aria-label": "Dismiss banner", size: "xs", variant: "plain", className: "text-current focus-visible:outline-current shrink-0", onClick: () => onRemove === null || onRemove === void 0 ? void 0 : onRemove(), "data-testid": "banner-remove-btn", children: jsx(SvgX, { className: "size-4.5", "data-testid": "banner-remove-icon" }) }))] }));
|
|
579
607
|
});
|
|
580
608
|
Banner.displayName = 'Banner';
|
|
581
609
|
|
|
@@ -3828,9 +3856,18 @@ const sizeClasses$1 = {
|
|
|
3828
3856
|
},
|
|
3829
3857
|
};
|
|
3830
3858
|
const SelectBox = forwardRef((props, forwardedRef) => {
|
|
3831
|
-
const { labelKey = 'label', valueKey = 'value', name, id = name, options = EMPTY_ARRAY, groups, selected, size = 'md', label = null, labelWrapperClass = '', labelClass = '', labelHint = null, disabled = false, containerClass = '', onChange = NOOP, placeholder, dropdownContainerClass = 'z-20', multiple = false, maxSelection, showSelectAll = false, immediate = true, showErrorMessage = true, error = null, helperText, leftGroup, renderOption, async = false, searchable = true, required = false, allowAdd = false, addNewText = 'Create', onAdd = NOOP, allowClear = false, allowFreeText = false, modalDropdown = false, noOptionsText = 'No options available', } = props;
|
|
3859
|
+
const { labelKey = 'label', valueKey = 'value', name, id = name, options = EMPTY_ARRAY, groups, selected: selectedProp, defaultSelected, size = 'md', label = null, labelWrapperClass = '', labelClass = '', labelHint = null, disabled = false, containerClass = '', onChange: onChangeProp = NOOP, placeholder, dropdownContainerClass = 'z-20', dropdownMinWidth, multiple = false, maxSelection, showSelectAll = false, immediate = true, showErrorMessage = true, error = null, helperText, leftGroup, renderOption, async = false, searchable = true, required = false, allowAdd = false, addNewText = 'Create', onAdd = NOOP, allowClear = false, allowFreeText = false, modalDropdown = false, noOptionsText = 'No options available', } = props;
|
|
3860
|
+
// Uncontrolled when `selected` is not passed: keep the selection internally.
|
|
3861
|
+
const isControlled = selectedProp !== undefined;
|
|
3862
|
+
const [internalSelected, setInternalSelected] = useState(defaultSelected !== null && defaultSelected !== void 0 ? defaultSelected : null);
|
|
3863
|
+
const selected = isControlled ? selectedProp : internalSelected;
|
|
3864
|
+
const onChange = useCallback((value) => {
|
|
3865
|
+
if (!isControlled)
|
|
3866
|
+
setInternalSelected(value);
|
|
3867
|
+
onChangeProp(value);
|
|
3868
|
+
}, [isControlled, onChangeProp]);
|
|
3832
3869
|
if (props.asBottomSheet || props.mobileMode === 'sheet') {
|
|
3833
|
-
return jsx(MobilePicker, { ...props });
|
|
3870
|
+
return jsx(MobilePicker, { ...props, selected: selected, onChange: onChange });
|
|
3834
3871
|
}
|
|
3835
3872
|
const hasError = Boolean(error);
|
|
3836
3873
|
const [query, setQuery] = useState('');
|
|
@@ -4028,6 +4065,16 @@ const SelectBox = forwardRef((props, forwardedRef) => {
|
|
|
4028
4065
|
}
|
|
4029
4066
|
}, [isAllSelected, onChange, allOptions]);
|
|
4030
4067
|
const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
|
|
4068
|
+
// Reserve room for the chevron (and the clear button when shown) so long values
|
|
4069
|
+
// don't run underneath them.
|
|
4070
|
+
const showClearButton = allowClear && !multiple && !isEmpty(normalizedSelected);
|
|
4071
|
+
const rightPaddingClass = showClearButton
|
|
4072
|
+
? 'pe-16'
|
|
4073
|
+
: size === 'lg'
|
|
4074
|
+
? 'pe-11'
|
|
4075
|
+
: size === 'sm'
|
|
4076
|
+
? 'pe-9'
|
|
4077
|
+
: 'pe-10';
|
|
4031
4078
|
const hasFreeTextOption = Boolean(allowFreeText &&
|
|
4032
4079
|
query.trim() &&
|
|
4033
4080
|
!allOptions.some((opt) => { var _a; return ((_a = getDisplayValue(opt)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === query.trim().toLowerCase(); }));
|
|
@@ -4040,7 +4087,7 @@ const SelectBox = forwardRef((props, forwardedRef) => {
|
|
|
4040
4087
|
const isOptSelected = selectedValueSet.has(optVal);
|
|
4041
4088
|
return (jsx(ComboboxOption, { disabled: option.disabled, value: option, className: "group rounded-lg py-1.5 px-3 select-none data-[focus]:bg-gray-100 dark:data-[focus]:bg-white/10 data-disabled:opacity-50 cursor-pointer data-disabled:pointer-events-none", children: renderOption ? (renderOption(option, Boolean(isOptSelected))) : (jsxs("div", { className: "flex items-center justify-between gap-2 text-gray-900 dark:text-white group-data-[selected]:text-blue-600", children: [jsx("span", { className: "text-sm/6", children: getDisplayValue(option) }), jsx(SvgCheck, { className: "invisible size-5 dark:stroke-gray-300 group-data-[selected]:visible" })] })) }, optVal));
|
|
4042
4089
|
};
|
|
4043
|
-
return (jsxs("div", { className: buildClassName('group space-y-2', hasError && 'has-error', containerClass), ref: containerRef, children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsx(Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), labelHint && jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxs(Combobox, { multiple: multiple, immediate: immediate, name: name, value: normalizedSelected, onChange: comboboxOnChangeWrapper, onClose: () => setQuery(''), disabled: disabled, by: (a, b) => (a && b ? getValue(a) === getValue(b) : a === b), children: [multiple ? (jsxs("div", { className: buildClassName(baseInputClass, 'flex flex-wrap gap-2 peer relative', hasLeftGroup ? (size === 'lg' ? 'ps-12' : size === 'sm' ? 'ps-8' : 'ps-10') : ''), ref: forwardedRef, children: [hasLeftGroup && (jsx("span", { className: buildClassName('absolute left-0 top-0 h-full flex items-center justify-center text-gray-400 dark:text-gray-500', currentSize.group), children: leftGroup })), isArray(normalizedSelected) &&
|
|
4090
|
+
return (jsxs("div", { className: buildClassName('group space-y-2', hasError && 'has-error', containerClass), ref: containerRef, children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsx(Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), labelHint && jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxs(Combobox, { multiple: multiple, immediate: immediate, name: name, value: normalizedSelected, onChange: comboboxOnChangeWrapper, onClose: () => setQuery(''), disabled: disabled, by: (a, b) => (a && b ? getValue(a) === getValue(b) : a === b), children: [multiple ? (jsxs("div", { className: buildClassName(baseInputClass, 'flex flex-wrap gap-2 peer relative', rightPaddingClass, hasLeftGroup ? (size === 'lg' ? 'ps-12' : size === 'sm' ? 'ps-8' : 'ps-10') : ''), ref: forwardedRef, children: [hasLeftGroup && (jsx("span", { className: buildClassName('absolute left-0 top-0 h-full flex items-center justify-center text-gray-400 dark:text-gray-500', currentSize.group), children: leftGroup })), isArray(normalizedSelected) &&
|
|
4044
4091
|
(normalizedSelected === null || normalizedSelected === void 0 ? void 0 : normalizedSelected.length) > 0 &&
|
|
4045
4092
|
normalizedSelected.map((option, idx) => (jsx(Badge, { theme: "secondary", size: currentSize.badgeSize, removable: !disabled, onRemove: () => removeSelection(idx), children: getDisplayValue(option) }, `${getValue(option)}-${idx}`))), jsx(ComboboxInput, { className: "outline-none flex-1 dark:placeholder-gray-400 placeholder-gray-400 bg-transparent", "aria-label": name, value: query, autoComplete: "off", placeholder: (normalizedSelected === null || normalizedSelected === void 0 ? void 0 : normalizedSelected.length) ? '' : placeholder, onChange: (e) => {
|
|
4046
4093
|
const val = e.target.value;
|
|
@@ -4080,7 +4127,9 @@ const SelectBox = forwardRef((props, forwardedRef) => {
|
|
|
4080
4127
|
}
|
|
4081
4128
|
}, onFocus: () => {
|
|
4082
4129
|
setQuery('');
|
|
4083
|
-
}, className: buildClassName(baseInputClass, hasLeftGroup ? (size === 'lg' ? 'ps-12' : size === 'sm' ? 'ps-8' : 'ps-10') : ''), disabled: disabled, id: id, readOnly: !searchable, ref: inputRef, "data-testid": "combobox-input" }),
|
|
4130
|
+
}, className: buildClassName(baseInputClass, 'text-ellipsis', rightPaddingClass, hasLeftGroup ? (size === 'lg' ? 'ps-12' : size === 'sm' ? 'ps-8' : 'ps-10') : ''), disabled: disabled, id: id, readOnly: !searchable, ref: inputRef, "data-testid": "combobox-input" }), showClearButton && (jsx(Button, { size: "xs", iconOnly: true, "aria-label": "Clear selection", theme: "secondary", variant: "plain", className: "group absolute inset-y-0 right-8 h-6 top-2", onClick: () => handleOnChangeInternal(multiple ? [] : null), disabled: disabled, "data-testid": "combobox-clear-button", children: jsx(SvgX, { className: "size-4 stroke-gray-500 dark:stroke-gray-300" }) })), jsx(ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5 flex items-center justify-center", "data-testid": "combobox-button", children: jsx(SvgChevronDown, { className: chevronIconClass }) })] })), jsxs(ComboboxOptions, { anchor: { to: 'bottom start', gap: 5, padding: 8 }, as: "div", modal: modalDropdown,
|
|
4131
|
+
// Matches the input width by default; min-width lets the panel grow wider than a narrow input.
|
|
4132
|
+
style: dropdownMinWidth != null ? { minWidth: dropdownMinWidth } : undefined, className: buildClassName('w-[var(--input-width)] rounded-lg border border-[var(--ui-border)] bg-white dark:bg-gray-800 p-1 shadow-lg max-h-80 overflow-y-auto z-50 [--anchor-gap:var(--spacing-1)]', '[--anchor-max-height:20rem]', 'origin-top border transition duration-200 ease-[cubic-bezier(0.4, 0.0, 0.2, 1)] data-[closed]:translate-y-[-15%] data-[closed]:opacity-0', dropdownContainerClass), transition: true, "data-testid": "combobox-options", children: [multiple && showSelectAll && allOptions.length > 0 && (jsxs("div", { className: "pb-1 mb-1 border-b border-[var(--ui-border-muted)] px-3 pt-1 flex items-center justify-between", children: [jsx("button", { type: "button", onClick: handleToggleSelectAll, className: "text-xs font-semibold text-blue-600 dark:text-blue-400 hover:underline cursor-pointer", children: isAllSelected ? 'Deselect All' : 'Select All' }), jsxs("span", { className: "text-[11px] font-medium text-gray-400", children: [Array.isArray(normalizedSelected) ? normalizedSelected.length : 0, " of", ' ', allOptions.length] })] })), optionsLoading ? (jsx(BodyText, { "data-testid": "options-loading", className: "flex items-center justify-center py-2 px-3 select-none", children: jsx(Loader, { size: "sm" }) })) : shouldShowTypeToSearch ? (jsx(BodyText, { className: "flex items-center justify-between py-2 px-3 select-none", children: "Type to search..." })) : (jsxs("div", { children: [allowFreeText &&
|
|
4084
4133
|
query.trim() &&
|
|
4085
4134
|
!allOptions.some((opt) => { var _a; return ((_a = getDisplayValue(opt)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === query.trim().toLowerCase(); }) && (jsx(ComboboxOption, { value: createFreeTextOption(query.trim()), className: "group flex items-center gap-2 rounded-lg py-1.5 px-3 select-none text-sm data-[focus]:bg-blue-50 dark:data-[focus]:bg-blue-950/40 text-blue-600 dark:text-blue-400 font-medium cursor-pointer w-full", "data-testid": "freetext-option", children: jsxs("span", { children: ["Use \"", query.trim(), "\""] }) }, `freetext-${query.trim()}`)), filteredGroups
|
|
4086
4135
|
? filteredGroups.map((grp, gIdx) => (jsxs("div", { children: [jsx("div", { className: "text-[11px] font-bold uppercase tracking-wider text-gray-400 dark:text-gray-500 px-3 py-1 select-none", children: grp.group }), grp.options.map(renderOptionItem)] }, gIdx)))
|
|
@@ -4550,7 +4599,7 @@ const Toast = ({ id, title, message, type, variant = 'accent', action, icon, sho
|
|
|
4550
4599
|
const renderedIcon = icon !== null && icon !== void 0 ? icon : (variant === 'filled' ? filledIconMap[type] : iconMap[type]);
|
|
4551
4600
|
return (jsx(Transition, { appear: true, show: true, enter: "transform transition duration-300 ease-out", enterFrom: enterFrom, enterTo: "opacity-100 translate-y-0 scale-100", leave: "transition duration-200 ease-in", leaveFrom: "opacity-100 translate-y-0 scale-100", leaveTo: leaveTo, children: jsxs("div", { role: "status", id: id.toString(), className: buildClassName('relative overflow-hidden flex items-start gap-3 rounded-xl px-4 py-3.5 min-w-[280px] max-w-md transition-all', variantStyleMap[variant][type], className), children: [jsx("div", { className: "shrink-0 mt-0.5", children: variant === 'filled' ? (renderedIcon) : (jsx(TextContent, { className: buildClassName(textColorMap[type]), children: renderedIcon })) }), jsxs("div", { className: "flex-1 flex flex-col gap-0.5", children: [title && (jsx("div", { className: buildClassName('font-semibold text-sm leading-snug', variant === 'filled' ? 'text-white' : 'text-gray-900 dark:text-gray-100'), children: title })), jsx("div", { className: buildClassName('text-sm font-medium leading-relaxed', variant === 'filled' ? 'text-white/95' : 'text-gray-700 dark:text-gray-300'), children: message }), action && (jsx("div", { className: "mt-1.5", children: jsx("button", { type: "button", onClick: action.onClick, className: buildClassName('text-xs font-semibold underline underline-offset-2 transition-opacity focus:outline-none', variant === 'filled'
|
|
4552
4601
|
? 'text-white hover:opacity-85'
|
|
4553
|
-
: 'text-[var(--ui-primary)] hover:opacity-80'), children: action.label }) }))] }), jsx(Button, { onClick: onClose, "aria-label": "Close toast", className: buildClassName('shrink-0 mt-0.5 opacity-60 hover:opacity-100 transition', variant === 'filled' && 'text-white hover:text-white'), iconOnly: true, size: "xs", variant: "plain", theme: variant === 'filled' ? 'primary' : 'secondary',
|
|
4602
|
+
: 'text-[var(--ui-primary)] hover:opacity-80'), children: action.label }) }))] }), jsx(Button, { onClick: onClose, "aria-label": "Close toast", className: buildClassName('shrink-0 mt-0.5 opacity-60 hover:opacity-100 focus-visible:opacity-100 transition', variant === 'filled' && 'text-white hover:text-white focus-visible:outline-current'), iconOnly: true, size: "xs", variant: "plain", theme: variant === 'filled' ? 'primary' : 'secondary', children: jsx(SvgX, { className: "size-4" }) }), showProgressBar && duration > 0 && (jsx("div", { className: "absolute bottom-0 left-0 right-0 h-1 bg-black/10 dark:bg-white/10 overflow-hidden", children: jsx("div", { className: buildClassName('h-full transition-all duration-75 ease-linear', variant === 'filled' ? 'bg-white/70' : progressBarColorMap[type]), style: { width: `${Math.max(0, progress)}%` } }) }))] }) }));
|
|
4554
4603
|
};
|
|
4555
4604
|
Toast.displayName = 'Toast';
|
|
4556
4605
|
|