@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
|
@@ -151,59 +151,35 @@ TextContent.displayName = 'TextContent';
|
|
|
151
151
|
|
|
152
152
|
/**
|
|
153
153
|
* Tailwind CSS classes for theme + variant combinations.
|
|
154
|
+
*
|
|
155
|
+
* Only colours live here. Focus outline width/offset and disabled styling are
|
|
156
|
+
* applied uniformly in the component so they can be toggled in one place.
|
|
154
157
|
*/
|
|
155
158
|
const THEMED_BTN_CLASS = {
|
|
156
159
|
primary: {
|
|
157
|
-
default: 'text-white bg-[var(--ui-primary)] hover:bg-[var(--ui-primary-hover)] focus-visible:
|
|
158
|
-
outlined: 'text-[var(--ui-primary-text)] hover:text-white border border-[var(--ui-primary)] hover:bg-[var(--ui-primary)] focus-visible:
|
|
159
|
-
plain: 'text-[var(--ui-primary-text)] hover:bg-
|
|
160
|
+
default: 'text-white bg-[var(--ui-primary)] hover:bg-[var(--ui-primary-hover)] focus-visible:outline-[var(--ui-primary-ring)]',
|
|
161
|
+
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)]',
|
|
162
|
+
plain: 'text-[var(--ui-primary-text)] hover:bg-current/10 active:bg-current/15 focus-visible:outline-[var(--ui-primary-ring)]',
|
|
160
163
|
},
|
|
161
164
|
danger: {
|
|
162
|
-
default: 'text-white bg-[var(--ui-danger)] hover:bg-[var(--ui-danger-hover)] focus-visible:
|
|
163
|
-
outlined: 'text-[var(--ui-danger-text)] hover:text-white border border-[var(--ui-danger)] hover:bg-[var(--ui-danger)] focus-visible:
|
|
164
|
-
plain: 'text-[var(--ui-danger-text)] hover:bg-
|
|
165
|
+
default: 'text-white bg-[var(--ui-danger)] hover:bg-[var(--ui-danger-hover)] focus-visible:outline-[var(--ui-danger-ring)]',
|
|
166
|
+
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)]',
|
|
167
|
+
plain: 'text-[var(--ui-danger-text)] hover:bg-current/10 active:bg-current/15 focus-visible:outline-[var(--ui-danger-ring)]',
|
|
165
168
|
},
|
|
166
169
|
secondary: {
|
|
167
|
-
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:
|
|
168
|
-
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:
|
|
169
|
-
plain: 'text-gray-700 hover:bg-
|
|
170
|
+
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)]',
|
|
171
|
+
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)]',
|
|
172
|
+
plain: 'text-gray-700 hover:bg-current/10 active:bg-current/15 dark:text-gray-200 focus-visible:outline-[var(--ui-focus-ring)]',
|
|
170
173
|
},
|
|
171
174
|
success: {
|
|
172
|
-
default: 'text-white bg-emerald-600 hover:bg-emerald-700 focus-visible:
|
|
173
|
-
outlined: 'text-emerald-
|
|
174
|
-
plain: 'text-emerald-
|
|
175
|
+
default: 'text-white bg-emerald-600 hover:bg-emerald-700 focus-visible:outline-emerald-500',
|
|
176
|
+
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',
|
|
177
|
+
plain: 'text-emerald-700 hover:bg-current/10 active:bg-current/15 dark:text-emerald-400 focus-visible:outline-emerald-500',
|
|
175
178
|
},
|
|
176
179
|
warning: {
|
|
177
|
-
default: 'text-white bg-amber-500 hover:bg-amber-600 focus-visible:
|
|
178
|
-
outlined: 'text-amber-
|
|
179
|
-
plain: 'text-amber-
|
|
180
|
-
},
|
|
181
|
-
};
|
|
182
|
-
const LOADER_TEXT_CLASS = {
|
|
183
|
-
primary: {
|
|
184
|
-
default: 'text-white',
|
|
185
|
-
outlined: 'text-[var(--ui-primary-text)]',
|
|
186
|
-
plain: 'text-[var(--ui-primary-text)]',
|
|
187
|
-
},
|
|
188
|
-
danger: {
|
|
189
|
-
default: 'text-white',
|
|
190
|
-
outlined: 'text-[var(--ui-danger-text)]',
|
|
191
|
-
plain: 'text-[var(--ui-danger-text)]',
|
|
192
|
-
},
|
|
193
|
-
secondary: {
|
|
194
|
-
default: 'text-gray-700 dark:text-white',
|
|
195
|
-
outlined: 'text-gray-700 dark:text-gray-200',
|
|
196
|
-
plain: 'text-gray-700 dark:text-gray-200',
|
|
197
|
-
},
|
|
198
|
-
success: {
|
|
199
|
-
default: 'text-white',
|
|
200
|
-
outlined: 'text-emerald-600 dark:text-emerald-400',
|
|
201
|
-
plain: 'text-emerald-600 dark:text-emerald-400',
|
|
202
|
-
},
|
|
203
|
-
warning: {
|
|
204
|
-
default: 'text-white',
|
|
205
|
-
outlined: 'text-amber-600 dark:text-amber-400',
|
|
206
|
-
plain: 'text-amber-600 dark:text-amber-400',
|
|
180
|
+
default: 'text-white bg-amber-500 hover:bg-amber-600 focus-visible:outline-amber-500',
|
|
181
|
+
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',
|
|
182
|
+
plain: 'text-amber-700 hover:bg-current/10 active:bg-current/15 dark:text-amber-400 focus-visible:outline-amber-500',
|
|
207
183
|
},
|
|
208
184
|
};
|
|
209
185
|
const SIZE_CLASSES = {
|
|
@@ -223,22 +199,32 @@ const LOADER_SIZE = {
|
|
|
223
199
|
};
|
|
224
200
|
/**
|
|
225
201
|
* Base shared tailwind classes.
|
|
202
|
+
*
|
|
203
|
+
* Disabled styling is keyed off both the native `disabled` attribute and
|
|
204
|
+
* `aria-disabled`, so non-button elements rendered via `as` look disabled too.
|
|
226
205
|
*/
|
|
227
206
|
const BASE_BTN_CLASS = 'font-medium inline-flex items-center justify-center shrink-0 cursor-pointer select-none ' +
|
|
228
|
-
'
|
|
229
|
-
'active:scale-[0.98]
|
|
207
|
+
'transition-[color,background-color,border-color,box-shadow,opacity,transform] duration-150 ease-out ' +
|
|
208
|
+
'active:scale-[0.98] focus:outline-none ' +
|
|
209
|
+
'disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none ' +
|
|
210
|
+
'aria-disabled:pointer-events-none aria-disabled:opacity-50 aria-disabled:shadow-none';
|
|
230
211
|
const Button = React.forwardRef(({ as, type, ...props }, ref) => {
|
|
231
|
-
var _a, _b, _c, _d;
|
|
232
212
|
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;
|
|
233
213
|
const Component = as || 'button';
|
|
234
214
|
const isNativeButton = Component === 'button';
|
|
215
|
+
// Clean invalid props when not rendering a real button
|
|
216
|
+
const { type: _ignoreType, disabled: _ignoreDisabled, role, tabIndex, onKeyDown, onKeyUp, ...domProps } = restProps;
|
|
217
|
+
const ariaLabel = domProps['aria-label'];
|
|
218
|
+
const ariaLabelledBy = domProps['aria-labelledby'];
|
|
235
219
|
// Accessibility guard for icon-only buttons
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
220
|
+
React.useEffect(() => {
|
|
221
|
+
if (iconOnly && !ariaLabel && !ariaLabelledBy) {
|
|
222
|
+
console.warn('[ui] <Button iconOnly> requires aria-label or aria-labelledby for accessibility.');
|
|
223
|
+
}
|
|
224
|
+
}, [iconOnly, ariaLabel, ariaLabelledBy]);
|
|
239
225
|
const resolvedDisabled = Boolean(disabled || loading);
|
|
240
|
-
//
|
|
241
|
-
const
|
|
226
|
+
// Links (`href`, or `to` for router Link components) keep their native semantics.
|
|
227
|
+
const isLink = !isNativeButton && (domProps.href != null || domProps.to != null);
|
|
242
228
|
const handleClick = (e) => {
|
|
243
229
|
if (resolvedDisabled) {
|
|
244
230
|
e.preventDefault();
|
|
@@ -247,31 +233,73 @@ const Button = React.forwardRef(({ as, type, ...props }, ref) => {
|
|
|
247
233
|
}
|
|
248
234
|
onClick === null || onClick === void 0 ? void 0 : onClick(e);
|
|
249
235
|
};
|
|
236
|
+
// Mirror native <button> keyboard activation for non-button elements:
|
|
237
|
+
// Enter activates on keydown, Space on keyup (and must not scroll the page).
|
|
238
|
+
const handleKeyDown = (e) => {
|
|
239
|
+
onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(e);
|
|
240
|
+
if (e.defaultPrevented || e.target !== e.currentTarget)
|
|
241
|
+
return;
|
|
242
|
+
if (e.key === 'Enter') {
|
|
243
|
+
e.preventDefault();
|
|
244
|
+
e.currentTarget.click();
|
|
245
|
+
}
|
|
246
|
+
else if (e.key === ' ') {
|
|
247
|
+
e.preventDefault();
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
const handleKeyUp = (e) => {
|
|
251
|
+
onKeyUp === null || onKeyUp === void 0 ? void 0 : onKeyUp(e);
|
|
252
|
+
if (e.defaultPrevented || e.target !== e.currentTarget)
|
|
253
|
+
return;
|
|
254
|
+
if (e.key === ' ') {
|
|
255
|
+
e.preventDefault();
|
|
256
|
+
e.currentTarget.click();
|
|
257
|
+
}
|
|
258
|
+
};
|
|
250
259
|
const elementProps = isNativeButton
|
|
251
260
|
? {
|
|
252
261
|
type: type !== null && type !== void 0 ? type : 'button',
|
|
253
262
|
disabled: resolvedDisabled,
|
|
263
|
+
role,
|
|
264
|
+
tabIndex,
|
|
254
265
|
onClick: handleClick,
|
|
266
|
+
onKeyDown,
|
|
267
|
+
onKeyUp,
|
|
255
268
|
}
|
|
256
|
-
:
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
269
|
+
: isLink
|
|
270
|
+
? {
|
|
271
|
+
role,
|
|
272
|
+
tabIndex: resolvedDisabled ? -1 : tabIndex,
|
|
273
|
+
'aria-disabled': resolvedDisabled ? true : undefined,
|
|
274
|
+
onClick: handleClick,
|
|
275
|
+
onKeyDown,
|
|
276
|
+
onKeyUp,
|
|
277
|
+
}
|
|
278
|
+
: {
|
|
279
|
+
role: role !== null && role !== void 0 ? role : 'button',
|
|
280
|
+
tabIndex: resolvedDisabled ? -1 : (tabIndex !== null && tabIndex !== void 0 ? tabIndex : 0),
|
|
281
|
+
'aria-disabled': resolvedDisabled ? true : undefined,
|
|
282
|
+
onClick: handleClick,
|
|
283
|
+
onKeyDown: handleKeyDown,
|
|
284
|
+
onKeyUp: handleKeyUp,
|
|
285
|
+
};
|
|
262
286
|
const sizeObj = SIZE_CLASSES[size] || SIZE_CLASSES.md;
|
|
263
287
|
const classes = React.useMemo(() => {
|
|
264
288
|
var _a;
|
|
265
|
-
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,
|
|
289
|
+
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,
|
|
290
|
+
// An outline (not a ring) so the offset gap stays transparent on any surface/mode.
|
|
291
|
+
!noOutlineOnFocus &&
|
|
292
|
+
'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);
|
|
266
293
|
}, [theme, variant, sizeObj, iconOnly, noOutlineOnFocus, rounded, fullWidth, active, className]);
|
|
267
|
-
|
|
294
|
+
// The spinner and loading text inherit the button's text colour.
|
|
295
|
+
const loaderIcon = (jsxRuntime.jsx(Loader, { "data-testid": "btn-loader-icon", size: LOADER_SIZE[size] || 'xs', className: "text-current" }));
|
|
268
296
|
/**
|
|
269
297
|
* SPECIAL CASE: iconOnly + loading → show spinner only
|
|
270
298
|
*/
|
|
271
299
|
if (loading && iconOnly) {
|
|
272
|
-
return (jsxRuntime.jsx(Component, { ref: ref, className: classes, "aria-busy": "true", "aria-pressed": active !== undefined ? active : undefined, ...
|
|
300
|
+
return (jsxRuntime.jsx(Component, { ref: ref, className: classes, "aria-busy": "true", "aria-pressed": active !== undefined ? active : undefined, ...domProps, ...elementProps, children: loaderIcon }));
|
|
273
301
|
}
|
|
274
|
-
return (jsxRuntime.jsx(Component, { ref: ref, className: classes, "aria-busy": loading ? 'true' : undefined, "aria-pressed": active !== undefined ? active : undefined, ...
|
|
302
|
+
return (jsxRuntime.jsx(Component, { ref: ref, className: classes, "aria-busy": loading ? 'true' : undefined, "aria-pressed": active !== undefined ? active : undefined, ...domProps, ...elementProps, children: loading ? (jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2", "aria-live": "polite", children: [loaderIcon, loadingText ? (jsxRuntime.jsx(TextContent, { className: "text-current", children: loadingText })) : (children)] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [leftIcon && jsxRuntime.jsx("span", { className: "shrink-0 inline-flex items-center", children: leftIcon }), children, rightIcon && jsxRuntime.jsx("span", { className: "shrink-0 inline-flex items-center", children: rightIcon })] })) }));
|
|
275
303
|
});
|
|
276
304
|
Button.displayName = 'Button';
|
|
277
305
|
|
|
@@ -595,7 +623,7 @@ const Banner = React.forwardRef((props, ref) => {
|
|
|
595
623
|
const { type = 'info', iconSize = 'sm', children, icon, removable = false, onRemove, className, ...restProps } = props;
|
|
596
624
|
const DefaultIcon = IconMap[type];
|
|
597
625
|
const iconNode = icon !== undefined ? (icon) : (jsxRuntime.jsx(DefaultIcon, { "data-testid": `banner-${type}-icon`, className: buildClassName('shrink-0', sizeMap[iconSize]) }));
|
|
598
|
-
return (jsxRuntime.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, jsxRuntime.jsx("div", { className: "flex items-center w-full justify-between", children: children }), removable && (jsxRuntime.jsx(Button, { iconOnly: true, "aria-label": "Dismiss banner", size: "xs", variant: "plain",
|
|
626
|
+
return (jsxRuntime.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, jsxRuntime.jsx("div", { className: "flex items-center w-full justify-between", children: children }), removable && (jsxRuntime.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: jsxRuntime.jsx(SvgX, { className: "size-4.5", "data-testid": "banner-remove-icon" }) }))] }));
|
|
599
627
|
});
|
|
600
628
|
Banner.displayName = 'Banner';
|
|
601
629
|
|
|
@@ -3848,9 +3876,18 @@ const sizeClasses$1 = {
|
|
|
3848
3876
|
},
|
|
3849
3877
|
};
|
|
3850
3878
|
const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
3851
|
-
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;
|
|
3879
|
+
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;
|
|
3880
|
+
// Uncontrolled when `selected` is not passed: keep the selection internally.
|
|
3881
|
+
const isControlled = selectedProp !== undefined;
|
|
3882
|
+
const [internalSelected, setInternalSelected] = React.useState(defaultSelected !== null && defaultSelected !== void 0 ? defaultSelected : null);
|
|
3883
|
+
const selected = isControlled ? selectedProp : internalSelected;
|
|
3884
|
+
const onChange = React.useCallback((value) => {
|
|
3885
|
+
if (!isControlled)
|
|
3886
|
+
setInternalSelected(value);
|
|
3887
|
+
onChangeProp(value);
|
|
3888
|
+
}, [isControlled, onChangeProp]);
|
|
3852
3889
|
if (props.asBottomSheet || props.mobileMode === 'sheet') {
|
|
3853
|
-
return jsxRuntime.jsx(MobilePicker, { ...props });
|
|
3890
|
+
return jsxRuntime.jsx(MobilePicker, { ...props, selected: selected, onChange: onChange });
|
|
3854
3891
|
}
|
|
3855
3892
|
const hasError = Boolean(error);
|
|
3856
3893
|
const [query, setQuery] = React.useState('');
|
|
@@ -4048,6 +4085,16 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
4048
4085
|
}
|
|
4049
4086
|
}, [isAllSelected, onChange, allOptions]);
|
|
4050
4087
|
const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
|
|
4088
|
+
// Reserve room for the chevron (and the clear button when shown) so long values
|
|
4089
|
+
// don't run underneath them.
|
|
4090
|
+
const showClearButton = allowClear && !multiple && !isEmpty(normalizedSelected);
|
|
4091
|
+
const rightPaddingClass = showClearButton
|
|
4092
|
+
? 'pe-16'
|
|
4093
|
+
: size === 'lg'
|
|
4094
|
+
? 'pe-11'
|
|
4095
|
+
: size === 'sm'
|
|
4096
|
+
? 'pe-9'
|
|
4097
|
+
: 'pe-10';
|
|
4051
4098
|
const hasFreeTextOption = Boolean(allowFreeText &&
|
|
4052
4099
|
query.trim() &&
|
|
4053
4100
|
!allOptions.some((opt) => { var _a; return ((_a = getDisplayValue(opt)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === query.trim().toLowerCase(); }));
|
|
@@ -4060,7 +4107,7 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
4060
4107
|
const isOptSelected = selectedValueSet.has(optVal);
|
|
4061
4108
|
return (jsxRuntime.jsx(react.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))) : (jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 text-gray-900 dark:text-white group-data-[selected]:text-blue-600", children: [jsxRuntime.jsx("span", { className: "text-sm/6", children: getDisplayValue(option) }), jsxRuntime.jsx(SvgCheck, { className: "invisible size-5 dark:stroke-gray-300 group-data-[selected]:visible" })] })) }, optVal));
|
|
4062
4109
|
};
|
|
4063
|
-
return (jsxRuntime.jsxs("div", { className: buildClassName('group space-y-2', hasError && 'has-error', containerClass), ref: containerRef, children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), labelHint && jsxRuntime.jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxRuntime.jsxs(react.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 ? (jsxRuntime.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 && (jsxRuntime.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) &&
|
|
4110
|
+
return (jsxRuntime.jsxs("div", { className: buildClassName('group space-y-2', hasError && 'has-error', containerClass), ref: containerRef, children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), labelHint && jsxRuntime.jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxRuntime.jsxs(react.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 ? (jsxRuntime.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 && (jsxRuntime.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) &&
|
|
4064
4111
|
(normalizedSelected === null || normalizedSelected === void 0 ? void 0 : normalizedSelected.length) > 0 &&
|
|
4065
4112
|
normalizedSelected.map((option, idx) => (jsxRuntime.jsx(Badge, { theme: "secondary", size: currentSize.badgeSize, removable: !disabled, onRemove: () => removeSelection(idx), children: getDisplayValue(option) }, `${getValue(option)}-${idx}`))), jsxRuntime.jsx(react.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) => {
|
|
4066
4113
|
const val = e.target.value;
|
|
@@ -4100,7 +4147,9 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
4100
4147
|
}
|
|
4101
4148
|
}, onFocus: () => {
|
|
4102
4149
|
setQuery('');
|
|
4103
|
-
}, 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" }),
|
|
4150
|
+
}, 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 && (jsxRuntime.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: jsxRuntime.jsx(SvgX, { className: "size-4 stroke-gray-500 dark:stroke-gray-300" }) })), jsxRuntime.jsx(react.ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5 flex items-center justify-center", "data-testid": "combobox-button", children: jsxRuntime.jsx(SvgChevronDown, { className: chevronIconClass }) })] })), jsxRuntime.jsxs(react.ComboboxOptions, { anchor: { to: 'bottom start', gap: 5, padding: 8 }, as: "div", modal: modalDropdown,
|
|
4151
|
+
// Matches the input width by default; min-width lets the panel grow wider than a narrow input.
|
|
4152
|
+
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 && (jsxRuntime.jsxs("div", { className: "pb-1 mb-1 border-b border-[var(--ui-border-muted)] px-3 pt-1 flex items-center justify-between", children: [jsxRuntime.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' }), jsxRuntime.jsxs("span", { className: "text-[11px] font-medium text-gray-400", children: [Array.isArray(normalizedSelected) ? normalizedSelected.length : 0, " of", ' ', allOptions.length] })] })), optionsLoading ? (jsxRuntime.jsx(BodyText, { "data-testid": "options-loading", className: "flex items-center justify-center py-2 px-3 select-none", children: jsxRuntime.jsx(Loader, { size: "sm" }) })) : shouldShowTypeToSearch ? (jsxRuntime.jsx(BodyText, { className: "flex items-center justify-between py-2 px-3 select-none", children: "Type to search..." })) : (jsxRuntime.jsxs("div", { children: [allowFreeText &&
|
|
4104
4153
|
query.trim() &&
|
|
4105
4154
|
!allOptions.some((opt) => { var _a; return ((_a = getDisplayValue(opt)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === query.trim().toLowerCase(); }) && (jsxRuntime.jsx(react.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: jsxRuntime.jsxs("span", { children: ["Use \"", query.trim(), "\""] }) }, `freetext-${query.trim()}`)), filteredGroups
|
|
4106
4155
|
? filteredGroups.map((grp, gIdx) => (jsxRuntime.jsxs("div", { children: [jsxRuntime.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)))
|
|
@@ -4570,7 +4619,7 @@ const Toast = ({ id, title, message, type, variant = 'accent', action, icon, sho
|
|
|
4570
4619
|
const renderedIcon = icon !== null && icon !== void 0 ? icon : (variant === 'filled' ? filledIconMap[type] : iconMap[type]);
|
|
4571
4620
|
return (jsxRuntime.jsx(react.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: jsxRuntime.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: [jsxRuntime.jsx("div", { className: "shrink-0 mt-0.5", children: variant === 'filled' ? (renderedIcon) : (jsxRuntime.jsx(TextContent, { className: buildClassName(textColorMap[type]), children: renderedIcon })) }), jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col gap-0.5", children: [title && (jsxRuntime.jsx("div", { className: buildClassName('font-semibold text-sm leading-snug', variant === 'filled' ? 'text-white' : 'text-gray-900 dark:text-gray-100'), children: title })), jsxRuntime.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 && (jsxRuntime.jsx("div", { className: "mt-1.5", children: jsxRuntime.jsx("button", { type: "button", onClick: action.onClick, className: buildClassName('text-xs font-semibold underline underline-offset-2 transition-opacity focus:outline-none', variant === 'filled'
|
|
4572
4621
|
? 'text-white hover:opacity-85'
|
|
4573
|
-
: 'text-[var(--ui-primary)] hover:opacity-80'), children: action.label }) }))] }), jsxRuntime.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',
|
|
4622
|
+
: 'text-[var(--ui-primary)] hover:opacity-80'), children: action.label }) }))] }), jsxRuntime.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: jsxRuntime.jsx(SvgX, { className: "size-4" }) }), showProgressBar && duration > 0 && (jsxRuntime.jsx("div", { className: "absolute bottom-0 left-0 right-0 h-1 bg-black/10 dark:bg-white/10 overflow-hidden", children: jsxRuntime.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)}%` } }) }))] }) }));
|
|
4574
4623
|
};
|
|
4575
4624
|
Toast.displayName = 'Toast';
|
|
4576
4625
|
|