@pk-design/react-tailwind 1.7.1 → 1.8.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/Button/Button.js +46 -28
- package/dist/cjs/components/Button/Button.js.map +1 -1
- package/dist/cjs/components/Checkbox/Checkbox.js +10 -4
- package/dist/cjs/components/Checkbox/Checkbox.js.map +1 -1
- package/dist/cjs/components/CheckboxGroup/CheckboxGroup.js +19 -13
- package/dist/cjs/components/CheckboxGroup/CheckboxGroup.js.map +1 -1
- package/dist/cjs/components/DataList/DataList.js +2 -2
- package/dist/cjs/components/DataList/DataList.js.map +1 -1
- package/dist/cjs/components/DataTable/DataTable.js +72 -40
- package/dist/cjs/components/DataTable/DataTable.js.map +1 -1
- package/dist/cjs/components/DataTable/DataTable.utils.js +2 -1
- package/dist/cjs/components/DataTable/DataTable.utils.js.map +1 -1
- package/dist/cjs/components/Input/Input.js +11 -8
- package/dist/cjs/components/Input/Input.js.map +1 -1
- package/dist/cjs/components/Pagination/Pagination.js +27 -15
- package/dist/cjs/components/Pagination/Pagination.js.map +1 -1
- package/dist/cjs/components/Radio/Radio.js +9 -5
- package/dist/cjs/components/Radio/Radio.js.map +1 -1
- package/dist/cjs/components/RadioGroup/RadioGroup.js +8 -3
- package/dist/cjs/components/RadioGroup/RadioGroup.js.map +1 -1
- package/dist/cjs/components/SelectBox/SelectBox.js +31 -28
- package/dist/cjs/components/SelectBox/SelectBox.js.map +1 -1
- package/dist/cjs/components/Textarea/Textarea.js +8 -5
- package/dist/cjs/components/Textarea/Textarea.js.map +1 -1
- package/dist/es/components/Button/Button.js +46 -28
- package/dist/es/components/Button/Button.js.map +1 -1
- package/dist/es/components/Checkbox/Checkbox.js +10 -4
- package/dist/es/components/Checkbox/Checkbox.js.map +1 -1
- package/dist/es/components/CheckboxGroup/CheckboxGroup.js +20 -14
- package/dist/es/components/CheckboxGroup/CheckboxGroup.js.map +1 -1
- package/dist/es/components/DataList/DataList.js +2 -2
- package/dist/es/components/DataList/DataList.js.map +1 -1
- package/dist/es/components/DataTable/DataTable.js +74 -42
- package/dist/es/components/DataTable/DataTable.js.map +1 -1
- package/dist/es/components/DataTable/DataTable.utils.js +2 -1
- package/dist/es/components/DataTable/DataTable.utils.js.map +1 -1
- package/dist/es/components/Input/Input.js +12 -9
- package/dist/es/components/Input/Input.js.map +1 -1
- package/dist/es/components/Pagination/Pagination.js +27 -15
- package/dist/es/components/Pagination/Pagination.js.map +1 -1
- package/dist/es/components/Radio/Radio.js +9 -5
- package/dist/es/components/Radio/Radio.js.map +1 -1
- package/dist/es/components/RadioGroup/RadioGroup.js +9 -4
- package/dist/es/components/RadioGroup/RadioGroup.js.map +1 -1
- package/dist/es/components/SelectBox/SelectBox.js +31 -28
- package/dist/es/components/SelectBox/SelectBox.js.map +1 -1
- package/dist/es/components/Textarea/Textarea.js +9 -6
- package/dist/es/components/Textarea/Textarea.js.map +1 -1
- package/dist/react-tailwind.cjs.js +271 -180
- package/dist/react-tailwind.cjs.js.map +1 -1
- package/dist/react-tailwind.es.js +271 -180
- package/dist/react-tailwind.es.js.map +1 -1
- package/dist/types/components/Button/Button.types.d.ts +5 -0
- package/dist/types/components/CheckboxGroup/CheckboxGroup.types.d.ts +9 -1
- package/dist/types/components/DataList/DataList.d.ts +1 -1
- package/dist/types/components/DataList/DataList.types.d.ts +8 -0
- package/dist/types/components/DataTable/DataTable.d.ts +1 -1
- package/dist/types/components/DataTable/DataTable.types.d.ts +46 -0
- package/dist/types/components/Pagination/Pagination.types.d.ts +21 -24
- package/dist/types/components/RadioGroup/RadioGroup.types.d.ts +8 -0
- package/package.json +1 -1
|
@@ -156,17 +156,17 @@ const THEMED_BTN_CLASS = {
|
|
|
156
156
|
primary: {
|
|
157
157
|
default: 'text-white bg-[var(--ui-primary)] hover:bg-[var(--ui-primary-hover)] focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-primary-ring)] disabled:bg-[var(--ui-primary-disabled)]',
|
|
158
158
|
outlined: 'text-[var(--ui-primary-text)] hover:text-white border border-[var(--ui-primary)] hover:bg-[var(--ui-primary)] focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-primary-ring)] disabled:text-[var(--ui-primary-disabled)] disabled:border-[var(--ui-primary-disabled)]',
|
|
159
|
-
plain: 'text-[var(--ui-primary-text)] hover:bg-
|
|
159
|
+
plain: 'text-[var(--ui-primary-text)] hover:bg-blue-50/80 dark:hover:bg-blue-950/40 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-primary-ring)] disabled:text-[var(--ui-primary-disabled)]',
|
|
160
160
|
},
|
|
161
161
|
danger: {
|
|
162
162
|
default: 'text-white bg-[var(--ui-danger)] hover:bg-[var(--ui-danger-hover)] focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-danger-ring)] disabled:bg-[var(--ui-danger-disabled)]',
|
|
163
163
|
outlined: 'text-[var(--ui-danger-text)] hover:text-white border border-[var(--ui-danger)] hover:bg-[var(--ui-danger)] focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-danger-ring)] disabled:text-[var(--ui-danger-disabled)] disabled:border-[var(--ui-danger-disabled)]',
|
|
164
|
-
plain: 'text-[var(--ui-danger-text)] hover:bg-
|
|
164
|
+
plain: 'text-[var(--ui-danger-text)] hover:bg-red-50/80 dark:hover:bg-red-950/40 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-danger-ring)] disabled:text-[var(--ui-danger-disabled)]',
|
|
165
165
|
},
|
|
166
166
|
secondary: {
|
|
167
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:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-focus-ring)] disabled:bg-gray-200 dark:disabled:bg-gray-700',
|
|
168
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:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-focus-ring)] disabled:text-gray-400 disabled:border-[var(--ui-border-muted)]',
|
|
169
|
-
plain: 'text-gray-700 hover:bg-
|
|
169
|
+
plain: 'text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-800 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-focus-ring)] disabled:text-gray-400',
|
|
170
170
|
},
|
|
171
171
|
success: {
|
|
172
172
|
default: 'text-white bg-emerald-600 hover:bg-emerald-700 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-emerald-500 disabled:bg-emerald-300 dark:disabled:bg-emerald-900',
|
|
@@ -206,54 +206,72 @@ const LOADER_TEXT_CLASS = {
|
|
|
206
206
|
plain: 'text-amber-600 dark:text-amber-400',
|
|
207
207
|
},
|
|
208
208
|
};
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
: 'h-10 px-5 rounded-lg gap-2 text-base';
|
|
224
|
-
}
|
|
209
|
+
const SIZE_CLASSES = {
|
|
210
|
+
xs: { normal: 'h-7 px-2.5 rounded-sm gap-1 text-xs', iconOnly: 'h-7 w-7 p-1 rounded-sm text-xs' },
|
|
211
|
+
sm: { normal: 'h-8 px-3 rounded gap-1.5 text-sm', iconOnly: 'h-8 w-8 p-1.5 rounded text-sm' },
|
|
212
|
+
md: { normal: 'h-9 px-4 rounded-md gap-2 text-sm', iconOnly: 'h-9 w-9 p-2 rounded-md text-sm' },
|
|
213
|
+
lg: {
|
|
214
|
+
normal: 'h-10 px-5 rounded-lg gap-2 text-base',
|
|
215
|
+
iconOnly: 'h-10 w-10 p-2.5 rounded-lg text-base',
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
const LOADER_SIZE = {
|
|
219
|
+
xs: 'xs',
|
|
220
|
+
sm: 'xs',
|
|
221
|
+
md: 'sm',
|
|
222
|
+
lg: 'sm',
|
|
225
223
|
};
|
|
226
224
|
/**
|
|
227
225
|
* Base shared tailwind classes.
|
|
228
226
|
*/
|
|
229
|
-
const BASE_BTN_CLASS = 'font-medium flex items-center justify-center shrink-0 cursor-pointer ' +
|
|
227
|
+
const BASE_BTN_CLASS = 'font-medium inline-flex items-center justify-center shrink-0 cursor-pointer select-none ' +
|
|
230
228
|
'disabled:cursor-not-allowed disabled:pointer-events-none disabled:opacity-75 ' +
|
|
231
229
|
'active:scale-[0.98] transition-all duration-150 ease-out focus:outline-none';
|
|
232
230
|
const Button = React.forwardRef(({ as, type, ...props }, ref) => {
|
|
233
|
-
|
|
231
|
+
var _a, _b, _c, _d;
|
|
232
|
+
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;
|
|
234
233
|
const Component = as || 'button';
|
|
234
|
+
const isNativeButton = Component === 'button';
|
|
235
235
|
// Accessibility guard for icon-only buttons
|
|
236
236
|
if (iconOnly && !restProps['aria-label']) {
|
|
237
237
|
console.warn('[ui] <Button iconOnly> requires aria-label for accessibility.');
|
|
238
238
|
}
|
|
239
|
-
const resolvedDisabled = disabled || loading;
|
|
239
|
+
const resolvedDisabled = Boolean(disabled || loading);
|
|
240
240
|
// Clean invalid props when not rendering a real button
|
|
241
241
|
const { type: _ignoreType, disabled: _ignoreDisabled, ...cleanRest } = restProps;
|
|
242
|
-
const
|
|
242
|
+
const handleClick = (e) => {
|
|
243
|
+
if (resolvedDisabled) {
|
|
244
|
+
e.preventDefault();
|
|
245
|
+
e.stopPropagation();
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
onClick === null || onClick === void 0 ? void 0 : onClick(e);
|
|
249
|
+
};
|
|
250
|
+
const elementProps = isNativeButton
|
|
243
251
|
? {
|
|
244
252
|
type: type !== null && type !== void 0 ? type : 'button',
|
|
245
253
|
disabled: resolvedDisabled,
|
|
254
|
+
onClick: handleClick,
|
|
246
255
|
}
|
|
247
|
-
: {
|
|
248
|
-
|
|
256
|
+
: {
|
|
257
|
+
role: (_a = cleanRest.role) !== null && _a !== void 0 ? _a : 'button',
|
|
258
|
+
tabIndex: resolvedDisabled ? -1 : ((_b = cleanRest.tabIndex) !== null && _b !== void 0 ? _b : 0),
|
|
259
|
+
'aria-disabled': resolvedDisabled ? true : undefined,
|
|
260
|
+
onClick: handleClick,
|
|
261
|
+
};
|
|
262
|
+
const sizeObj = SIZE_CLASSES[size] || SIZE_CLASSES.md;
|
|
263
|
+
const classes = React.useMemo(() => {
|
|
264
|
+
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, !noOutlineOnFocus && 'focus-visible:ring-2 focus-visible:ring-offset-2', rounded && 'rounded-full', fullWidth && 'w-full', variant === 'default' && 'shadow-xs hover:shadow-none disabled:shadow-none', active && 'ring-2 ring-offset-2 ring-current', className);
|
|
266
|
+
}, [theme, variant, sizeObj, iconOnly, noOutlineOnFocus, rounded, fullWidth, active, className]);
|
|
267
|
+
const loaderIcon = (jsxRuntime.jsx(Loader, { "data-testid": "btn-loader-icon", size: LOADER_SIZE[size] || 'xs', className: buildClassName((_c = LOADER_TEXT_CLASS[theme]) === null || _c === void 0 ? void 0 : _c[variant]) }));
|
|
249
268
|
/**
|
|
250
269
|
* SPECIAL CASE: iconOnly + loading → show spinner only
|
|
251
270
|
*/
|
|
252
271
|
if (loading && iconOnly) {
|
|
253
|
-
return (jsxRuntime.jsx(Component, { ref: ref, className: classes,
|
|
272
|
+
return (jsxRuntime.jsx(Component, { ref: ref, className: classes, "aria-busy": "true", "aria-pressed": active !== undefined ? active : undefined, ...elementProps, ...cleanRest, children: loaderIcon }));
|
|
254
273
|
}
|
|
255
|
-
|
|
256
|
-
return (jsxRuntime.jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: loading && !loadingText ? (jsxRuntime.jsxs("span", { className: "flex items-center gap-2", "aria-live": "polite", children: [loaderIcon, children] })) : loading && loadingText ? (jsxRuntime.jsxs("span", { className: "flex items-center gap-2", "aria-live": "polite", children: [loaderIcon, jsxRuntime.jsx(TextContent, { className: buildClassName(LOADER_TEXT_CLASS[theme][variant]), children: loadingText })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [leftIcon && jsxRuntime.jsx("span", { className: "shrink-0 flex items-center", children: leftIcon }), children, rightIcon && jsxRuntime.jsx("span", { className: "shrink-0 flex items-center", children: rightIcon })] })) }));
|
|
274
|
+
return (jsxRuntime.jsx(Component, { ref: ref, className: classes, "aria-busy": loading ? 'true' : undefined, "aria-pressed": active !== undefined ? active : undefined, ...elementProps, ...cleanRest, children: loading ? (jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2", "aria-live": "polite", children: [loaderIcon, loadingText ? (jsxRuntime.jsx(TextContent, { className: buildClassName((_d = LOADER_TEXT_CLASS[theme]) === null || _d === void 0 ? void 0 : _d[variant]), 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 })] })) }));
|
|
257
275
|
});
|
|
258
276
|
Button.displayName = 'Button';
|
|
259
277
|
|
|
@@ -900,6 +918,34 @@ const Label = React.forwardRef((props, ref) => {
|
|
|
900
918
|
});
|
|
901
919
|
Label.displayName = 'Label';
|
|
902
920
|
|
|
921
|
+
/**
|
|
922
|
+
* Checks if a value is an array.
|
|
923
|
+
* Equivalent to lodash's _.isArray.
|
|
924
|
+
*/
|
|
925
|
+
function isArray(value) {
|
|
926
|
+
return Array.isArray(value);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
/**
|
|
930
|
+
* Checks if a value is empty.
|
|
931
|
+
* Works for arrays, objects, strings, Maps, and Sets.
|
|
932
|
+
* Equivalent to lodash's _.isEmpty.
|
|
933
|
+
*/
|
|
934
|
+
function isEmpty(value) {
|
|
935
|
+
if (value == null)
|
|
936
|
+
return true; // null or undefined
|
|
937
|
+
if (typeof value === 'string' || isArray(value)) {
|
|
938
|
+
return value.length === 0;
|
|
939
|
+
}
|
|
940
|
+
if (value instanceof Map || value instanceof Set) {
|
|
941
|
+
return value.size === 0;
|
|
942
|
+
}
|
|
943
|
+
if (typeof value === 'object') {
|
|
944
|
+
return Object.keys(value).length === 0;
|
|
945
|
+
}
|
|
946
|
+
return false; // for numbers, booleans, functions, etc.
|
|
947
|
+
}
|
|
948
|
+
|
|
903
949
|
const sizeStyles = {
|
|
904
950
|
sm: {
|
|
905
951
|
box: 'size-3.5',
|
|
@@ -922,7 +968,7 @@ const sizeStyles = {
|
|
|
922
968
|
};
|
|
923
969
|
const Checkbox = React.forwardRef((props, ref) => {
|
|
924
970
|
var _a;
|
|
925
|
-
const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, helperText, disabled = false, indeterminate = false, size = 'md', variant = 'default', description, className = '', checked, onChange, ...rest } = props;
|
|
971
|
+
const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, helperText, disabled = false, indeterminate = false, size = 'md', variant = 'default', description, className = '', checked, onChange, required, 'aria-describedby': ariaDescribedBy, ...rest } = props;
|
|
926
972
|
const generatedId = React.useId();
|
|
927
973
|
const checkboxId = (_a = id !== null && id !== void 0 ? id : name) !== null && _a !== void 0 ? _a : generatedId;
|
|
928
974
|
const innerRef = React.useRef(null);
|
|
@@ -934,6 +980,11 @@ const Checkbox = React.forwardRef((props, ref) => {
|
|
|
934
980
|
}, [indeterminate]);
|
|
935
981
|
const currentSize = sizeStyles[size] || sizeStyles.md;
|
|
936
982
|
const isCard = variant === 'card';
|
|
983
|
+
const hasError = !isEmpty(error);
|
|
984
|
+
const errorId = checkboxId && hasError ? `${checkboxId}-error` : undefined;
|
|
985
|
+
const helperId = checkboxId && helperText && !hasError ? `${checkboxId}-helper` : undefined;
|
|
986
|
+
const descId = checkboxId && description ? `${checkboxId}-desc` : undefined;
|
|
987
|
+
const describedBy = [ariaDescribedBy, errorId, helperId, descId].filter(Boolean).join(' ') || undefined;
|
|
937
988
|
return (jsxRuntime.jsxs("div", { className: buildClassName(wrapperClass), children: [jsxRuntime.jsx("div", { className: buildClassName(isCard
|
|
938
989
|
? 'rounded-xl border p-3.5 transition-all duration-150 relative'
|
|
939
990
|
: 'flex items-center', isCard &&
|
|
@@ -941,16 +992,16 @@ const Checkbox = React.forwardRef((props, ref) => {
|
|
|
941
992
|
? 'border-[var(--ui-border-muted)] bg-gray-50/50 dark:bg-gray-900/40 opacity-70'
|
|
942
993
|
: checked || indeterminate
|
|
943
994
|
? 'border-blue-500/80 bg-blue-50/30 dark:bg-blue-950/20 dark:border-blue-500/60 shadow-xs'
|
|
944
|
-
: 'border-[var(--ui-border)] bg-white dark:bg-gray-900 hover:border-gray-300 dark:hover:border-gray-700 shadow-2xs'), containerClass), children: jsxRuntime.jsxs("label", { htmlFor: checkboxId, className: buildClassName('inline-flex relative select-none w-full', description ? 'items-start' : 'items-center', disabled ? 'cursor-not-allowed opacity-70' : 'cursor-pointer'), children: [jsxRuntime.jsxs("div", { className: "relative inline-flex items-center justify-center shrink-0", children: [jsxRuntime.jsx("input", { ref: innerRef, ...rest, id: checkboxId, name: name, type: "checkbox", disabled: disabled, checked: checked, onChange: onChange, "aria-checked": indeterminate ? 'mixed' : checked, className: buildClassName('peer cursor-pointer transition-all appearance-none rounded', currentSize.box, 'border focus:ring-2 focus:ring-offset-2', 'checked:bg-blue-600 checked:border-blue-600 dark:checked:bg-blue-600 dark:checked:border-blue-600', 'focus:ring-[var(--ui-focus-ring)] dark:focus:ring-offset-gray-900', disabled
|
|
995
|
+
: 'border-[var(--ui-border)] bg-white dark:bg-gray-900 hover:border-gray-300 dark:hover:border-gray-700 shadow-2xs'), containerClass), children: jsxRuntime.jsxs("label", { htmlFor: checkboxId, className: buildClassName('inline-flex relative select-none w-full', description ? 'items-start' : 'items-center', disabled ? 'cursor-not-allowed opacity-70' : 'cursor-pointer'), children: [jsxRuntime.jsxs("div", { className: "relative inline-flex items-center justify-center shrink-0", children: [jsxRuntime.jsx("input", { ref: innerRef, ...rest, id: checkboxId, name: name, type: "checkbox", disabled: disabled, checked: checked, onChange: onChange, required: required, "aria-required": required ? true : undefined, "aria-invalid": hasError ? true : undefined, "aria-describedby": describedBy, "aria-checked": indeterminate ? 'mixed' : checked, className: buildClassName('peer cursor-pointer transition-all appearance-none rounded', currentSize.box, 'border focus:ring-2 focus:ring-offset-2', 'checked:bg-blue-600 checked:border-blue-600 dark:checked:bg-blue-600 dark:checked:border-blue-600', 'focus:ring-[var(--ui-focus-ring)] dark:focus:ring-offset-gray-900', disabled
|
|
945
996
|
? 'border-[var(--ui-border-muted)] bg-gray-100 dark:bg-gray-800 pointer-events-none'
|
|
946
997
|
: 'bg-white dark:bg-gray-700 border-[var(--ui-border)] hover:border-gray-400 dark:hover:border-gray-500', indeterminate &&
|
|
947
998
|
'!bg-blue-600 !border-blue-600 dark:!bg-blue-600 dark:!border-blue-600', className) }), !indeterminate && (jsxRuntime.jsx("span", { className: buildClassName('absolute inset-0 flex items-center justify-center text-white pointer-events-none transition-opacity', checked !== undefined
|
|
948
999
|
? checked
|
|
949
1000
|
? 'opacity-100'
|
|
950
1001
|
: 'opacity-0'
|
|
951
|
-
: 'opacity-0 peer-checked:opacity-100'), children: jsxRuntime.jsx("svg", { className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", children: jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }) })), indeterminate && (jsxRuntime.jsx("span", { className: "absolute inset-0 flex items-center justify-center text-white pointer-events-none opacity-100 z-10", children: jsxRuntime.jsx("svg", { className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", children: jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M4 10a1 1 0 011-1h10a1 1 0 110 2H5a1 1 0 01-1-1z", clipRule: "evenodd" }) }) }))] }), (label || description) && (jsxRuntime.jsxs("div", { className: "ml-2.5 flex flex-col justify-center min-w-0 flex-1", children: [label && (jsxRuntime.jsx(Label, { htmlFor: checkboxId, className: buildClassName(disabled
|
|
1002
|
+
: 'opacity-0 peer-checked:opacity-100'), children: jsxRuntime.jsx("svg", { className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", children: jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }) })), indeterminate && (jsxRuntime.jsx("span", { className: "absolute inset-0 flex items-center justify-center text-white pointer-events-none opacity-100 z-10", children: jsxRuntime.jsx("svg", { className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", children: jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M4 10a1 1 0 011-1h10a1 1 0 110 2H5a1 1 0 01-1-1z", clipRule: "evenodd" }) }) }))] }), (label || description) && (jsxRuntime.jsxs("div", { className: "ml-2.5 flex flex-col justify-center min-w-0 flex-1", children: [label && (jsxRuntime.jsx(Label, { htmlFor: checkboxId, "aria-required": required, className: buildClassName(disabled
|
|
952
1003
|
? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
|
|
953
|
-
: 'cursor-pointer text-gray-800 dark:text-gray-200 font-medium', currentSize.text, labelClass), children: label })), description && (jsxRuntime.jsx("span", { className: buildClassName('mt-0.5 text-gray-500 dark:text-gray-400 font-normal leading-normal', currentSize.subtext), children: description }))] }))] }) }), (error || helperText) && (jsxRuntime.jsx("div", { className: "mt-1.5 ml-0.5", children: error ? (jsxRuntime.jsx(TextContent, { error: true, small: true, children: error })) : (jsxRuntime.jsx(TextContent, { muted: true, small: true, children: helperText })) }))] }));
|
|
1004
|
+
: 'cursor-pointer text-gray-800 dark:text-gray-200 font-medium', currentSize.text, labelClass), children: label })), description && (jsxRuntime.jsx("span", { id: descId, className: buildClassName('mt-0.5 text-gray-500 dark:text-gray-400 font-normal leading-normal', currentSize.subtext), children: description }))] }))] }) }), (error || helperText) && (jsxRuntime.jsx("div", { className: "mt-1.5 ml-0.5", children: error ? (jsxRuntime.jsx(TextContent, { error: true, small: true, id: errorId, children: error })) : (jsxRuntime.jsx(TextContent, { muted: true, small: true, id: helperId, children: helperText })) }))] }));
|
|
954
1005
|
});
|
|
955
1006
|
Checkbox.displayName = 'Checkbox';
|
|
956
1007
|
|
|
@@ -961,25 +1012,26 @@ const columnClasses = {
|
|
|
961
1012
|
4: 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3',
|
|
962
1013
|
};
|
|
963
1014
|
const CheckboxGroup = (props) => {
|
|
964
|
-
const { name, options, label, labelClass = '', labelWrapperClass = '', labelHint, containerClass = '', value: controlledValue, defaultValue, onChange, inline = false, columns, size = 'md', variant = 'default', showSelectAll = false, selectAllLabel = 'Select All', error, helperText, disabled = false, } = props;
|
|
1015
|
+
const { name, options, label, labelClass = '', labelWrapperClass = '', labelHint, containerClass = '', value: controlledValue, defaultValue, onChange, inline = false, columns, size = 'md', variant = 'default', showSelectAll = false, selectAllLabel = 'Select All', error, helperText, disabled = false, required, 'aria-describedby': ariaDescribedBy, } = props;
|
|
965
1016
|
const groupId = React.useId();
|
|
966
1017
|
const [internalValue, setInternalValue] = React.useState(defaultValue || []);
|
|
967
1018
|
const isControlled = controlledValue !== undefined;
|
|
968
|
-
const currentValue = isControlled ? controlledValue : internalValue;
|
|
1019
|
+
const currentValue = isControlled ? controlledValue || [] : internalValue;
|
|
969
1020
|
const items = React.useMemo(() => options.map((opt) => typeof opt === 'string' ? { label: opt, value: opt } : opt), [options]);
|
|
970
1021
|
const enabledItems = React.useMemo(() => items.filter((i) => !i.disabled && !disabled), [items, disabled]);
|
|
1022
|
+
const currentValueKeySet = React.useMemo(() => new Set(currentValue), [currentValue]);
|
|
971
1023
|
// Select All State calculation
|
|
972
1024
|
const allSelected = React.useMemo(() => {
|
|
973
1025
|
if (!enabledItems.length)
|
|
974
1026
|
return false;
|
|
975
|
-
return enabledItems.every((item) =>
|
|
976
|
-
}, [enabledItems,
|
|
1027
|
+
return enabledItems.every((item) => currentValueKeySet.has(item.value));
|
|
1028
|
+
}, [enabledItems, currentValueKeySet]);
|
|
977
1029
|
const isIndeterminate = React.useMemo(() => {
|
|
978
1030
|
if (allSelected)
|
|
979
1031
|
return false;
|
|
980
|
-
return enabledItems.some((item) =>
|
|
981
|
-
}, [allSelected, enabledItems,
|
|
982
|
-
const handleSelectAllChange = (e) => {
|
|
1032
|
+
return enabledItems.some((item) => currentValueKeySet.has(item.value));
|
|
1033
|
+
}, [allSelected, enabledItems, currentValueKeySet]);
|
|
1034
|
+
const handleSelectAllChange = React.useCallback((e) => {
|
|
983
1035
|
let updated;
|
|
984
1036
|
if (allSelected || isIndeterminate) {
|
|
985
1037
|
const enabledValues = new Set(enabledItems.map((i) => i.value));
|
|
@@ -994,8 +1046,8 @@ const CheckboxGroup = (props) => {
|
|
|
994
1046
|
setInternalValue(updated);
|
|
995
1047
|
}
|
|
996
1048
|
onChange === null || onChange === void 0 ? void 0 : onChange(updated, e);
|
|
997
|
-
};
|
|
998
|
-
const handleGroupChange = (e) => {
|
|
1049
|
+
}, [allSelected, isIndeterminate, enabledItems, currentValue, isControlled, onChange]);
|
|
1050
|
+
const handleGroupChange = React.useCallback((e) => {
|
|
999
1051
|
const raw = e.target.value;
|
|
1000
1052
|
const checked = e.target.checked;
|
|
1001
1053
|
const matched = items.find((i) => String(i.value) === raw);
|
|
@@ -1011,16 +1063,21 @@ const CheckboxGroup = (props) => {
|
|
|
1011
1063
|
setInternalValue(updated);
|
|
1012
1064
|
}
|
|
1013
1065
|
onChange === null || onChange === void 0 ? void 0 : onChange(updated, e);
|
|
1014
|
-
};
|
|
1066
|
+
}, [items, currentValue, isControlled, onChange]);
|
|
1067
|
+
const hasError = Boolean(error);
|
|
1068
|
+
const labelId = label ? `${groupId}-label` : undefined;
|
|
1069
|
+
const errorId = groupId && hasError ? `${groupId}-error` : undefined;
|
|
1070
|
+
const helperId = groupId && helperText && !hasError ? `${groupId}-helper` : undefined;
|
|
1071
|
+
const describedBy = [ariaDescribedBy, errorId, helperId].filter(Boolean).join(' ') || undefined;
|
|
1015
1072
|
const layoutClass = columns
|
|
1016
1073
|
? columnClasses[columns]
|
|
1017
1074
|
: inline
|
|
1018
1075
|
? 'flex flex-wrap gap-3'
|
|
1019
1076
|
: 'flex flex-col gap-3';
|
|
1020
|
-
return (jsxRuntime.jsxs("div", { className: buildClassName('mb-3 space-y-3', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, children: label }), labelHint && (jsxRuntime.jsx(BodyText, { muted: true, small: true, children: labelHint }))] })), showSelectAll && items.length > 0 && (jsxRuntime.jsx("div", { className: "pb-1 border-b border-[var(--ui-border)]", children: jsxRuntime.jsx(Checkbox, { id: `${groupId}-select-all`, label: selectAllLabel, size: size, variant: variant, checked: allSelected, indeterminate: isIndeterminate, disabled: disabled || enabledItems.length === 0, onChange: handleSelectAllChange }) })), jsxRuntime.jsx("div", { className: layoutClass, children: items.map((item, index) => {
|
|
1077
|
+
return (jsxRuntime.jsxs("div", { role: "group", "aria-labelledby": labelId, "aria-describedby": describedBy, className: buildClassName('mb-3 space-y-3', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsxRuntime.jsx(Label, { id: labelId, className: labelClass, "aria-required": required, children: label }), labelHint && (jsxRuntime.jsx(BodyText, { muted: true, small: true, children: labelHint }))] })), showSelectAll && items.length > 0 && (jsxRuntime.jsx("div", { className: "pb-1 border-b border-[var(--ui-border)]", children: jsxRuntime.jsx(Checkbox, { id: `${groupId}-select-all`, label: selectAllLabel, size: size, variant: variant, checked: allSelected, indeterminate: isIndeterminate, disabled: disabled || enabledItems.length === 0, onChange: handleSelectAllChange }) })), jsxRuntime.jsx("div", { className: layoutClass, children: items.map((item, index) => {
|
|
1021
1078
|
const key = typeof item.value === 'string' || typeof item.value === 'number' ? item.value : index;
|
|
1022
|
-
return (jsxRuntime.jsx(Checkbox, { name: name, id: `${groupId}-${String(item.value)}`, label: item.label, description: item.description, value: String(item.value), size: size, variant: variant, disabled: disabled || item.disabled, checked:
|
|
1023
|
-
}) }), (error || helperText) && (jsxRuntime.jsx("div", { className: "mt-1.5 ml-0.5", children: error ? (jsxRuntime.jsx(TextContent, { error: true, small: true, children: error })) : (jsxRuntime.jsx(TextContent, { muted: true, small: true, children: helperText })) }))] }));
|
|
1079
|
+
return (jsxRuntime.jsx(Checkbox, { name: name, id: `${groupId}-${String(item.value)}`, label: item.label, description: item.description, value: String(item.value), size: size, variant: variant, disabled: disabled || item.disabled, checked: currentValueKeySet.has(item.value), onChange: handleGroupChange }, key));
|
|
1080
|
+
}) }), (error || helperText) && (jsxRuntime.jsx("div", { className: "mt-1.5 ml-0.5", children: error ? (jsxRuntime.jsx(TextContent, { error: true, small: true, id: errorId, children: error })) : (jsxRuntime.jsx(TextContent, { muted: true, small: true, id: helperId, children: helperText })) }))] }));
|
|
1024
1081
|
};
|
|
1025
1082
|
CheckboxGroup.displayName = 'CheckboxGroup';
|
|
1026
1083
|
|
|
@@ -1114,27 +1171,39 @@ var SvgChevronRight = function SvgChevronRight(props) {
|
|
|
1114
1171
|
})));
|
|
1115
1172
|
};
|
|
1116
1173
|
|
|
1117
|
-
const Pagination = (
|
|
1118
|
-
|
|
1119
|
-
const
|
|
1120
|
-
const
|
|
1121
|
-
const
|
|
1122
|
-
const
|
|
1174
|
+
const Pagination = (props) => {
|
|
1175
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1176
|
+
const { className, ellipsis = true, onChange, isMobile, showSummary = true, showPageNumbers = true, } = props;
|
|
1177
|
+
const currentPage = (_c = (_b = (_a = props.page) !== null && _a !== void 0 ? _a : props.current) !== null && _b !== void 0 ? _b : props.currentPage) !== null && _c !== void 0 ? _c : 1;
|
|
1178
|
+
const limit = (_e = (_d = props.limit) !== null && _d !== void 0 ? _d : props.pageSize) !== null && _e !== void 0 ? _e : 10;
|
|
1179
|
+
const total = (_f = props.total) !== null && _f !== void 0 ? _f : 0;
|
|
1180
|
+
const totalPages = Math.max(1, Math.ceil(total / limit));
|
|
1181
|
+
const isFirstPage = currentPage <= 1;
|
|
1182
|
+
const isLastPage = total === 0 || currentPage >= totalPages;
|
|
1183
|
+
const range = usePagination({ totalPages, currentPage });
|
|
1123
1184
|
const handlePageChange = (nextVal) => {
|
|
1124
|
-
if (nextVal === currentPage)
|
|
1185
|
+
if (nextVal === currentPage || nextVal < 1 || nextVal > totalPages)
|
|
1125
1186
|
return;
|
|
1126
1187
|
onChange({ page: nextVal });
|
|
1127
1188
|
};
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
return (jsxRuntime.jsx("li", { children: jsxRuntime.jsx(Button, { size: "xs", theme: page === currentPage ? 'primary' : 'secondary', variant: page === currentPage ? 'default' : 'plain', "aria-current": page === currentPage ? 'page' : undefined, onClick: () => handlePageChange(page), children: page }) }, `${page}-${idx}`));
|
|
1134
|
-
}), jsxRuntime.jsx("li", { children: jsxRuntime.jsx(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": "ellipsis-next-btn", children: "Next" }) })] })] })] }));
|
|
1189
|
+
const startResult = total === 0 ? 0 : Math.max((currentPage - 1) * limit + 1, 1);
|
|
1190
|
+
const endResult = Math.min(currentPage * limit, total);
|
|
1191
|
+
// Explicit Mobile UX (when isMobile=true)
|
|
1192
|
+
if (isMobile) {
|
|
1193
|
+
return (jsxRuntime.jsxs("div", { "data-testid": "pagination-container", className: buildClassName('flex items-center justify-between bg-white dark:bg-gray-800 px-4 py-3 sm:px-6 rounded-xl border border-[var(--ui-border-muted)] w-full', className), children: [jsxRuntime.jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": ellipsis ? 'ellipsis-mobile-prev-btn' : 'mobile-prev-btn', className: "flex items-center gap-1 font-medium", children: [jsxRuntime.jsx(SvgChevronLeft, { className: "size-4" }), jsxRuntime.jsx("span", { children: "Previous" })] }), jsxRuntime.jsxs("span", { className: "text-xs font-semibold text-gray-700 dark:text-gray-300", children: ["Page ", currentPage, " of ", totalPages] }), jsxRuntime.jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": ellipsis ? 'ellipsis-mobile-next-btn' : 'mobile-next-btn', className: "flex items-center gap-1 font-medium", children: [jsxRuntime.jsx("span", { children: "Next" }), jsxRuntime.jsx(SvgChevronRight, { className: "size-4" })] })] }));
|
|
1135
1194
|
}
|
|
1136
|
-
|
|
1195
|
+
// Standard Responsive View
|
|
1196
|
+
return (jsxRuntime.jsxs("div", { "data-testid": "pagination-container", className: buildClassName('flex items-center justify-between bg-white dark:bg-gray-800 px-4 py-3 sm:px-6 rounded-xl border border-[var(--ui-border-muted)] w-full', className), children: [jsxRuntime.jsxs("div", { className: "flex flex-1 items-center justify-between sm:hidden", children: [jsxRuntime.jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": ellipsis ? 'ellipsis-mobile-prev-btn' : 'mobile-prev-btn', className: "flex items-center gap-1", children: [jsxRuntime.jsx(SvgChevronLeft, { className: "size-4" }), jsxRuntime.jsx("span", { children: "Previous" })] }), jsxRuntime.jsxs("span", { className: "text-xs font-semibold text-gray-700 dark:text-gray-300", children: ["Page ", currentPage, " of ", totalPages] }), jsxRuntime.jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": ellipsis ? 'ellipsis-mobile-next-btn' : 'mobile-next-btn', className: "flex items-center gap-1", children: [jsxRuntime.jsx("span", { children: "Next" }), jsxRuntime.jsx(SvgChevronRight, { className: "size-4" })] })] }), jsxRuntime.jsxs("div", { className: "hidden sm:flex sm:flex-1 sm:items-center sm:justify-between", children: [showSummary && (jsxRuntime.jsxs("p", { "data-testid": "pagination-summary", className: "text-sm text-gray-600 dark:text-gray-400 flex items-center gap-1", children: ["Showing", jsxRuntime.jsx("span", { className: "font-medium", children: startResult }), "to", jsxRuntime.jsx("span", { className: "font-medium", children: endResult }), "of", jsxRuntime.jsx("span", { className: "font-medium", children: total }), "results"] })), showPageNumbers && (jsxRuntime.jsxs("ul", { className: "flex items-center space-x-2 text-sm", children: [jsxRuntime.jsx("li", { children: jsxRuntime.jsx(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": ellipsis ? 'ellipsis-prev-btn' : 'prev-btn', children: ellipsis ? 'Prev' : 'Previous' }) }), ellipsis
|
|
1197
|
+
? range.map((pageItem, idx) => {
|
|
1198
|
+
if (pageItem === DOTS) {
|
|
1199
|
+
return (jsxRuntime.jsx("li", { "data-testid": `page-${pageItem}`, className: "px-2 py-1 text-gray-400", children: "\u2026" }, `${pageItem}-${idx}`));
|
|
1200
|
+
}
|
|
1201
|
+
const pageNum = Number(pageItem);
|
|
1202
|
+
return (jsxRuntime.jsx("li", { children: jsxRuntime.jsx(Button, { size: "xs", theme: pageNum === currentPage ? 'primary' : 'secondary', variant: pageNum === currentPage ? 'default' : 'plain', "aria-current": pageNum === currentPage ? 'page' : undefined, onClick: () => handlePageChange(pageNum), children: pageNum }) }, `${pageItem}-${idx}`));
|
|
1203
|
+
})
|
|
1204
|
+
: Array.from({ length: totalPages }, (_, i) => i + 1).map((pageNum) => (jsxRuntime.jsx("li", { children: jsxRuntime.jsx(Button, { size: "xs", theme: pageNum === currentPage ? 'primary' : 'secondary', variant: pageNum === currentPage ? 'default' : 'plain', "aria-current": pageNum === currentPage ? 'page' : undefined, onClick: () => handlePageChange(pageNum), children: jsxRuntime.jsx("span", { className: "size-4 block", children: pageNum }) }) }, pageNum))), jsxRuntime.jsx("li", { children: jsxRuntime.jsx(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": ellipsis ? 'ellipsis-next-btn' : 'next-btn', children: ellipsis ? 'Next' : 'Next' }) })] }))] })] }));
|
|
1137
1205
|
};
|
|
1206
|
+
Pagination.displayName = 'Pagination';
|
|
1138
1207
|
|
|
1139
1208
|
function get(obj, path, defaultValue) {
|
|
1140
1209
|
var _a;
|
|
@@ -1221,9 +1290,10 @@ const useRowSelection = ({ items, getKey, selectedRowKeys, defaultSelectedRowKey
|
|
|
1221
1290
|
emit(Array.from(new Set([...keys, ...selectableKeySet])));
|
|
1222
1291
|
}
|
|
1223
1292
|
}, [allSelected, selectableEntries, keys, emit]);
|
|
1293
|
+
const isSelected = React.useCallback((key) => keySet.has(key), [keySet]);
|
|
1224
1294
|
return {
|
|
1225
1295
|
selectedKeys: keys,
|
|
1226
|
-
isSelected
|
|
1296
|
+
isSelected,
|
|
1227
1297
|
toggleRow,
|
|
1228
1298
|
toggleAll,
|
|
1229
1299
|
allSelected,
|
|
@@ -1334,6 +1404,8 @@ const SortIconMap = {
|
|
|
1334
1404
|
};
|
|
1335
1405
|
const getSortIcon = (sort) => sort ? (SortIconMap[sort]) : (jsxRuntime.jsx(SvgArrowUpDown, { className: "size-4 text-gray-400 shrink-0 group-hover:text-gray-800 dark:group-hover:text-white" }));
|
|
1336
1406
|
|
|
1407
|
+
const OVERFLOW_REGEX = /\boverflow-(x-|y-)?(auto|hidden|scroll|visible|clip)\b/;
|
|
1408
|
+
const NOOP$1 = () => { };
|
|
1337
1409
|
const DENSITY_CELL_CLASS$2 = {
|
|
1338
1410
|
compact: 'px-2 py-1.5',
|
|
1339
1411
|
default: '',
|
|
@@ -1363,41 +1435,33 @@ const autoSortItems = (items, column, sort) => {
|
|
|
1363
1435
|
return 0;
|
|
1364
1436
|
});
|
|
1365
1437
|
};
|
|
1366
|
-
function DataTable({ items, columns, sorting = {}, onSort, loading, pagination, setPagination =
|
|
1367
|
-
const
|
|
1368
|
-
const
|
|
1369
|
-
React.useEffect(() => {
|
|
1370
|
-
setSortedItems(items);
|
|
1371
|
-
}, [items]);
|
|
1372
|
-
const doAutoSort = (column) => {
|
|
1373
|
-
internalSorting.current = updateSortQuery(internalSorting.current, column.name, column.type);
|
|
1374
|
-
setSortedItems(autoSortItems(items, column, internalSorting.current[column.name]));
|
|
1375
|
-
};
|
|
1376
|
-
const handleSort = (column) => {
|
|
1438
|
+
function DataTable({ items, columns, sorting = {}, onSort, loading, pagination, setPagination = NOOP$1, emptyMessage = 'No data found', containerClass = '', wrapperClass = '', tableClass = '', headClass = '', footerRow, rowKey = 'id', onRowClick, rowClass, density = 'default', striped = false, layout = 'auto', selectable = false, selectedRowKeys, defaultSelectedRowKeys, onSelectionChange, isRowSelectable, stickyHeader = true, stickyPagination = true, rowHoverActions, renderRowHoverActions, rowHoverActionHeader, rowHoverActionMode = 'overlay', rowHoverActionClass, paginationClass, paginationContainerClass, paginationProps, renderPagination, }) {
|
|
1439
|
+
const [internalSortingState, setInternalSortingState] = React.useState({});
|
|
1440
|
+
const handleSort = React.useCallback((column) => {
|
|
1377
1441
|
if (!column.sortable)
|
|
1378
1442
|
return;
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
};
|
|
1387
|
-
const
|
|
1388
|
-
if (
|
|
1389
|
-
return
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1443
|
+
if (onSort) {
|
|
1444
|
+
onSort(column);
|
|
1445
|
+
}
|
|
1446
|
+
else {
|
|
1447
|
+
setInternalSortingState((prev) => updateSortQuery(prev, column.name, column.type));
|
|
1448
|
+
}
|
|
1449
|
+
}, [onSort]);
|
|
1450
|
+
const activeSorting = React.useMemo(() => ({ ...sorting, ...internalSortingState }), [sorting, internalSortingState]);
|
|
1451
|
+
const sortedItems = React.useMemo(() => {
|
|
1452
|
+
if (onSort)
|
|
1453
|
+
return items;
|
|
1454
|
+
const activeSortKey = Object.keys(activeSorting)[0];
|
|
1455
|
+
if (!activeSortKey)
|
|
1456
|
+
return items;
|
|
1457
|
+
const column = columns.find((c) => c.name === activeSortKey);
|
|
1458
|
+
if (!column)
|
|
1459
|
+
return items;
|
|
1460
|
+
return autoSortItems(items, column, activeSorting[activeSortKey]);
|
|
1461
|
+
}, [items, columns, onSort, activeSorting]);
|
|
1462
|
+
const getKey = React.useCallback((item, idx) => { var _a; return typeof rowKey === 'function' ? rowKey(item) : ((_a = item[rowKey]) !== null && _a !== void 0 ? _a : idx); }, [rowKey]);
|
|
1463
|
+
const getRowClass = React.useCallback((item, idx) => typeof rowClass === 'function' ? rowClass(item, idx) : rowClass || '', [rowClass]);
|
|
1464
|
+
const getCellClass = React.useCallback((column, item) => typeof column.cellClass === 'function' ? column.cellClass(item) : column.cellClass || '', []);
|
|
1401
1465
|
const { isSelected, toggleRow, toggleAll, allSelected, someSelected } = useRowSelection({
|
|
1402
1466
|
items: sortedItems,
|
|
1403
1467
|
getKey,
|
|
@@ -1406,15 +1470,50 @@ function DataTable({ items, columns, sorting = {}, onSort, loading, pagination,
|
|
|
1406
1470
|
onSelectionChange,
|
|
1407
1471
|
isRowSelectable,
|
|
1408
1472
|
});
|
|
1473
|
+
const hasRowHoverActions = Boolean(rowHoverActions || renderRowHoverActions);
|
|
1474
|
+
const isInlineMode = hasRowHoverActions && rowHoverActionMode === 'inline';
|
|
1475
|
+
const isOverlayMode = hasRowHoverActions && rowHoverActionMode !== 'inline';
|
|
1409
1476
|
const densityCellClass = DENSITY_CELL_CLASS$2[density];
|
|
1410
|
-
const colSize = columns.length + (selectable ? 1 : 0);
|
|
1411
|
-
const wrapperHasOverflow =
|
|
1477
|
+
const colSize = columns.length + (selectable ? 1 : 0) + (isInlineMode ? 1 : 0);
|
|
1478
|
+
const wrapperHasOverflow = OVERFLOW_REGEX.test(wrapperClass);
|
|
1479
|
+
const renderActionsContent = React.useCallback((item, idx) => {
|
|
1480
|
+
if (renderRowHoverActions)
|
|
1481
|
+
return renderRowHoverActions(item, idx);
|
|
1482
|
+
if (!rowHoverActions)
|
|
1483
|
+
return null;
|
|
1484
|
+
return rowHoverActions(item, idx).map((act) => {
|
|
1485
|
+
const isDisabled = typeof act.disabled === 'function' ? act.disabled(item) : act.disabled;
|
|
1486
|
+
const isIconOnly = Boolean(act.icon && !act.label);
|
|
1487
|
+
return (jsxRuntime.jsxs(Button, { size: "xs", theme: act.theme || 'secondary', variant: act.variant || 'plain', disabled: isDisabled, title: act.title || (typeof act.label === 'string' ? act.label : undefined), className: buildClassName(isIconOnly
|
|
1488
|
+
? 'p-1.5 rounded-full hover:bg-black/5 dark:hover:bg-white/10'
|
|
1489
|
+
: 'px-2 py-1', act.className), onClick: (e) => {
|
|
1490
|
+
e.stopPropagation();
|
|
1491
|
+
act.onClick(item, idx, e);
|
|
1492
|
+
}, children: [act.icon && jsxRuntime.jsx("span", { className: "shrink-0", children: act.icon }), act.label && jsxRuntime.jsx("span", { children: act.label })] }, act.id));
|
|
1493
|
+
});
|
|
1494
|
+
}, [renderRowHoverActions, rowHoverActions]);
|
|
1412
1495
|
return (jsxRuntime.jsxs("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: [jsxRuntime.jsx("div", { className: buildClassName(!wrapperHasOverflow && 'overflow-x-auto', 'rounded-lg border border-[var(--ui-border)]', wrapperClass, pagination && 'rounded-b-none border-b-0'), children: jsxRuntime.jsxs(Table, { className: buildClassName('relative w-full max-w-full h-fit', tableClass), layout: layout, children: [jsxRuntime.jsxs(TableHead, { className: headClass, children: [selectable && (jsxRuntime.jsx(TableHeaderCell, { className: buildClassName('bg-gray-50 dark:bg-gray-800 w-px', densityCellClass, stickyHeader && 'sticky top-0 z-20 shadow-[0_1px_2px_rgba(0,0,0,0.06)]'), children: jsxRuntime.jsx(Checkbox, { "aria-label": "Select all rows", checked: allSelected, ref: (el) => {
|
|
1413
1496
|
if (el)
|
|
1414
1497
|
el.indeterminate = someSelected;
|
|
1415
|
-
}, onChange: toggleAll }) })), columns.map((column) =>
|
|
1416
|
-
|
|
1417
|
-
|
|
1498
|
+
}, onChange: toggleAll }) })), columns.map((column) => {
|
|
1499
|
+
const sortState = activeSorting[column.name];
|
|
1500
|
+
const ariaSortValue = column.sortable
|
|
1501
|
+
? sortState === 'asc' || sortState === '1'
|
|
1502
|
+
? 'ascending'
|
|
1503
|
+
: sortState === 'desc' || sortState === '-1'
|
|
1504
|
+
? 'descending'
|
|
1505
|
+
: 'none'
|
|
1506
|
+
: undefined;
|
|
1507
|
+
return (jsxRuntime.jsx(TableHeaderCell, { align: column.headerAlign, style: { width: column.width || 'auto' }, "aria-sort": ariaSortValue, className: buildClassName('bg-gray-50 dark:bg-gray-800', densityCellClass, stickyHeader && 'sticky top-0 z-20 shadow-[0_1px_2px_rgba(0,0,0,0.06)]', column.sticky === 'left' &&
|
|
1508
|
+
'sticky left-0 z-30 shadow-[2px_0_6px_rgba(0,0,0,0.06)]', column.sticky === 'right' &&
|
|
1509
|
+
'sticky right-0 z-30 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]', column.headerClass), children: jsxRuntime.jsxs(TextContent, { role: column.sortable ? 'button' : undefined, tabIndex: column.sortable ? 0 : undefined, "aria-label": column.sortable ? `Sort by ${column.label}` : undefined, className: buildClassName('group', column.sortable &&
|
|
1510
|
+
'inline-flex items-center gap-2 cursor-pointer select-none focus:outline-none focus-visible:ring-1 focus-visible:ring-blue-500 rounded'), onClick: () => handleSort(column), onKeyDown: (e) => {
|
|
1511
|
+
if (column.sortable && (e.key === 'Enter' || e.key === ' ')) {
|
|
1512
|
+
e.preventDefault();
|
|
1513
|
+
handleSort(column);
|
|
1514
|
+
}
|
|
1515
|
+
}, children: [jsxRuntime.jsx(TextContent, { className: "shrink-0", children: column.label }), column.sortable ? getSortIcon(activeSorting[column.name]) : null] }) }, column.name));
|
|
1516
|
+
}), isInlineMode && (jsxRuntime.jsx(TableHeaderCell, { align: "right", className: buildClassName('bg-gray-50 dark:bg-gray-800 text-right w-px', densityCellClass, stickyHeader && 'sticky top-0 z-20 shadow-[0_1px_2px_rgba(0,0,0,0.06)]'), children: rowHoverActionHeader !== null && rowHoverActionHeader !== void 0 ? rowHoverActionHeader : '' })), isOverlayMode && (jsxRuntime.jsx(TableHeaderCell, { align: "right", style: { padding: 0, width: 0, minWidth: 0, maxWidth: 0 }, className: buildClassName('sticky right-0 z-20 !p-0 !pr-0 !pl-0 !py-0 w-0 max-w-0 min-w-0 border-0 bg-gray-50 dark:bg-gray-800', stickyHeader && 'top-0'), children: null }))] }), jsxRuntime.jsx(TableBody, { loading: loading, colSize: colSize, rowSize: (pagination === null || pagination === void 0 ? void 0 : pagination.limit) || 5, children: !loading && sortedItems.length === 0 ? (jsxRuntime.jsx(EmptyTableRow, { colSpan: colSize, children: emptyMessage })) : (sortedItems.map((item, idx) => {
|
|
1418
1517
|
const key = getKey(item, idx);
|
|
1419
1518
|
const rowSelectable = !isRowSelectable || isRowSelectable(item);
|
|
1420
1519
|
return (jsxRuntime.jsxs(TableRow, { className: buildClassName('group', onRowClick && 'cursor-pointer', striped && idx % 2 === 1 && 'bg-gray-50 dark:bg-gray-800/40', getRowClass(item, idx)), onClick: onRowClick ? () => onRowClick(item, idx) : undefined, children: [selectable && (jsxRuntime.jsx(TableCell, { className: buildClassName(densityCellClass, striped && idx % 2 === 1 && 'bg-gray-50 dark:bg-gray-800/40'), children: jsxRuntime.jsx("span", { onClick: (e) => e.stopPropagation(), children: jsxRuntime.jsx(Checkbox, { "aria-label": "Select row", checked: isSelected(key), disabled: !rowSelectable, onChange: () => toggleRow(key) }) }) })), columns.map((column) => {
|
|
@@ -1424,8 +1523,10 @@ function DataTable({ items, columns, sorting = {}, onSort, loading, pagination,
|
|
|
1424
1523
|
'sticky right-0 z-10 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]', getCellClass(column, item)), children: column.render
|
|
1425
1524
|
? column.render(item)
|
|
1426
1525
|
: ((_a = get(item, column.name)) !== null && _a !== void 0 ? _a : 'Not set') }, column.name));
|
|
1427
|
-
})
|
|
1428
|
-
|
|
1526
|
+
}), isInlineMode && (jsxRuntime.jsx(TableCell, { align: "right", className: buildClassName(densityCellClass, striped && idx % 2 === 1 && 'bg-gray-50 dark:bg-gray-800/40'), onClick: (e) => e.stopPropagation(), children: jsxRuntime.jsx("div", { className: buildClassName('opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 transition-opacity duration-150 flex items-center justify-end gap-1.5', rowHoverActionClass), "data-testid": `row-hover-actions-${key}`, children: renderActionsContent(item, idx) }) })), isOverlayMode && (jsxRuntime.jsx(TableCell, { align: "right", style: { padding: 0, width: 0, minWidth: 0, maxWidth: 0 }, className: "sticky right-0 z-20 !p-0 !pr-0 !pl-0 !py-0 w-0 max-w-0 min-w-0 border-0 overflow-visible text-right", onClick: (e) => e.stopPropagation(), children: jsxRuntime.jsx("div", { className: buildClassName('absolute right-0 top-0 bottom-0 h-full opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 transition-opacity duration-150 flex items-center justify-end gap-1 px-3 z-30 pointer-events-none group-hover:pointer-events-auto whitespace-nowrap', striped && idx % 2 === 1
|
|
1527
|
+
? 'bg-gray-100 dark:bg-gray-800'
|
|
1528
|
+
: 'bg-white group-hover:bg-gray-50 dark:bg-gray-900 dark:group-hover:bg-gray-800', rowHoverActionClass), "data-testid": `row-hover-actions-${key}`, children: renderActionsContent(item, idx) }) }))] }, key));
|
|
1529
|
+
})) }), footerRow && jsxRuntime.jsx(TableFoot, { children: footerRow })] }) }), pagination && (jsxRuntime.jsx("div", { className: buildClassName(stickyPagination && 'sticky bottom-0 z-20 bg-white dark:bg-gray-800', paginationClass, paginationContainerClass), children: renderPagination ? (renderPagination(pagination, setPagination)) : (jsxRuntime.jsx(Pagination, { ...pagination, ...paginationProps, className: buildClassName('rounded-t-none rounded-b-lg border border-[var(--ui-border)]', stickyPagination && 'sticky bottom-0 z-20 bg-white dark:bg-gray-800', paginationProps === null || paginationProps === void 0 ? void 0 : paginationProps.className), onChange: setPagination })) }))] }));
|
|
1429
1530
|
}
|
|
1430
1531
|
DataTable.displayName = 'DataTable';
|
|
1431
1532
|
|
|
@@ -2054,20 +2155,20 @@ function buildInputClass(className, { isFocusWithin = false, disabled = false, h
|
|
|
2054
2155
|
: 'focus:outline-[var(--ui-focus-ring)] focus:outline-2 focus:-outline-offset-2', className);
|
|
2055
2156
|
}
|
|
2056
2157
|
const Input = React.forwardRef((props, ref) => {
|
|
2057
|
-
const { name, id = name, type, label, labelHint, className, labelClass, labelWrapperClass, containerClass, error, showErrorMessage = true, rightGroup, leftGroup, prefix, suffix, clearable = false, onClear, showCount = false, helperText, size = 'md', value, defaultValue, onChange, maxLength, ...inputProps } = props;
|
|
2158
|
+
const { name, id = name, type, label, labelHint, className, labelClass, labelWrapperClass, containerClass, error, showErrorMessage = true, rightGroup, leftGroup, prefix, suffix, clearable = false, onClear, showCount = false, helperText, size = 'md', value, defaultValue, onChange, maxLength, required, 'aria-describedby': ariaDescribedBy, ...inputProps } = props;
|
|
2058
2159
|
const innerRef = React.useRef(null);
|
|
2059
2160
|
React.useImperativeHandle(ref, () => innerRef.current);
|
|
2060
2161
|
const [uncontrolledValue, setUncontrolledValue] = React.useState(String(defaultValue !== null && defaultValue !== void 0 ? defaultValue : ''));
|
|
2061
2162
|
const isControlled = value !== undefined;
|
|
2062
2163
|
const currentVal = isControlled ? String(value !== null && value !== void 0 ? value : '') : uncontrolledValue;
|
|
2063
2164
|
const charLength = currentVal.length;
|
|
2064
|
-
const handleInputChange = (e) => {
|
|
2165
|
+
const handleInputChange = React.useCallback((e) => {
|
|
2065
2166
|
if (!isControlled) {
|
|
2066
2167
|
setUncontrolledValue(e.target.value);
|
|
2067
2168
|
}
|
|
2068
2169
|
onChange === null || onChange === void 0 ? void 0 : onChange(e);
|
|
2069
|
-
};
|
|
2070
|
-
const handleClear = () => {
|
|
2170
|
+
}, [isControlled, onChange]);
|
|
2171
|
+
const handleClear = React.useCallback(() => {
|
|
2071
2172
|
if (innerRef.current) {
|
|
2072
2173
|
innerRef.current.value = '';
|
|
2073
2174
|
const event = new Event('input', {
|
|
@@ -2080,18 +2181,21 @@ const Input = React.forwardRef((props, ref) => {
|
|
|
2080
2181
|
onChange === null || onChange === void 0 ? void 0 : onChange(event);
|
|
2081
2182
|
}
|
|
2082
2183
|
onClear === null || onClear === void 0 ? void 0 : onClear();
|
|
2083
|
-
};
|
|
2184
|
+
}, [isControlled, onChange, onClear]);
|
|
2084
2185
|
const hasError = Boolean(error);
|
|
2085
2186
|
const currentSize = sizeClasses$4[size] || sizeClasses$4.md;
|
|
2187
|
+
const errorId = id && hasError && showErrorMessage ? `${id}-error` : undefined;
|
|
2188
|
+
const helperId = id && helperText && !hasError ? `${id}-helper` : undefined;
|
|
2189
|
+
const describedBy = [ariaDescribedBy, errorId, helperId].filter(Boolean).join(' ') || undefined;
|
|
2086
2190
|
const showClearButton = clearable && charLength > 0 && !inputProps.disabled;
|
|
2087
|
-
const actualRightGroup = rightGroup !== null && rightGroup !== void 0 ? rightGroup : (showClearButton ? (jsxRuntime.jsx("button", { type: "button", onClick: handleClear, className: "p-1 rounded-full text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors cursor-pointer", title: "Clear text", tabIndex: -1, children: jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: currentSize.icon, children: jsxRuntime.jsx("path", { d: "M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" }) }) })) : null);
|
|
2191
|
+
const actualRightGroup = rightGroup !== null && rightGroup !== void 0 ? rightGroup : (showClearButton ? (jsxRuntime.jsx("button", { type: "button", onClick: handleClear, className: "p-1 rounded-full text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors cursor-pointer", "aria-label": "Clear input text", title: "Clear text", tabIndex: -1, children: jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: currentSize.icon, children: jsxRuntime.jsx("path", { d: "M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" }) }) })) : null);
|
|
2088
2192
|
const hasRightGroup = actualRightGroup !== null && actualRightGroup !== undefined;
|
|
2089
2193
|
const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
|
|
2090
|
-
return (jsxRuntime.jsxs("div", { className: buildClassName('group', hasError && 'has-error', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between mb-1.5', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, htmlFor: id, children: label }), labelHint && jsxRuntime.jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxRuntime.jsxs("div", { className: "flex rounded-lg shadow-2xs", children: [prefix && (jsxRuntime.jsx("span", { className: "inline-flex items-center px-3 rounded-l-lg border border-r-0 border-[var(--ui-border)] bg-gray-50 dark:bg-gray-800 text-gray-500 dark:text-gray-400 text-xs font-medium shrink-0", children: prefix })), jsxRuntime.jsxs("div", { className: "relative flex-1 min-w-0", children: [hasLeftGroup && (jsxRuntime.jsx("span", { className: buildClassName('absolute top-0 h-full flex items-center justify-center text-gray-400 dark:text-gray-500', currentSize.group), children: leftGroup })), jsxRuntime.jsx("input", { ref: innerRef, autoComplete: "off", ...inputProps, value: value, defaultValue: defaultValue, onChange: handleInputChange, maxLength: maxLength, name: name, id: id, type: type, className: buildClassName(buildInputClass(className, {
|
|
2194
|
+
return (jsxRuntime.jsxs("div", { className: buildClassName('group', hasError && 'has-error', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between mb-1.5', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), labelHint && jsxRuntime.jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxRuntime.jsxs("div", { className: "flex rounded-lg shadow-2xs", children: [prefix && (jsxRuntime.jsx("span", { className: "inline-flex items-center px-3 rounded-l-lg border border-r-0 border-[var(--ui-border)] bg-gray-50 dark:bg-gray-800 text-gray-500 dark:text-gray-400 text-xs font-medium shrink-0", children: prefix })), jsxRuntime.jsxs("div", { className: "relative flex-1 min-w-0", children: [hasLeftGroup && (jsxRuntime.jsx("span", { className: buildClassName('absolute top-0 h-full flex items-center justify-center text-gray-400 dark:text-gray-500', currentSize.group), children: leftGroup })), jsxRuntime.jsx("input", { ref: innerRef, autoComplete: "off", ...inputProps, value: value, defaultValue: defaultValue, onChange: handleInputChange, maxLength: maxLength, name: name, id: id, type: type, required: required, "aria-invalid": hasError ? true : undefined, "aria-describedby": describedBy, className: buildClassName(buildInputClass(className, {
|
|
2091
2195
|
disabled: inputProps.disabled,
|
|
2092
2196
|
hasError,
|
|
2093
2197
|
size,
|
|
2094
|
-
}), prefix ? '!rounded-l-none' : '', suffix ? '!rounded-r-none' : '', hasRightGroup ? (size === 'lg' ? 'pe-12' : size === 'sm' ? 'pe-8' : 'pe-10') : '', hasLeftGroup ? (size === 'lg' ? 'ps-12' : size === 'sm' ? 'ps-8' : 'ps-10') : '') }), hasRightGroup && (jsxRuntime.jsx("span", { className: buildClassName('absolute end-0 top-0 h-full flex items-center justify-center text-gray-400 dark:text-gray-500', currentSize.group), children: actualRightGroup }))] }), suffix && (jsxRuntime.jsx("span", { className: "inline-flex items-center px-3 rounded-r-lg border border-l-0 border-[var(--ui-border)] bg-gray-50 dark:bg-gray-800 text-gray-500 dark:text-gray-400 text-xs font-medium shrink-0", children: suffix }))] }), (showErrorMessage && error) || helperText || showCount ? (jsxRuntime.jsxs("div", { className: "mt-1.5 flex items-center justify-between gap-2 text-xs", children: [jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: showErrorMessage && error ? (jsxRuntime.jsx(TextContent, { error: true, small: true, children: error })) : helperText ? (jsxRuntime.jsx(TextContent, { muted: true, small: true, children: helperText })) : null }), showCount && (jsxRuntime.jsxs("span", { className: buildClassName('text-[11px] font-medium shrink-0 ml-auto', maxLength && charLength >= maxLength
|
|
2198
|
+
}), prefix ? '!rounded-l-none' : '', suffix ? '!rounded-r-none' : '', hasRightGroup ? (size === 'lg' ? 'pe-12' : size === 'sm' ? 'pe-8' : 'pe-10') : '', hasLeftGroup ? (size === 'lg' ? 'ps-12' : size === 'sm' ? 'ps-8' : 'ps-10') : '') }), hasRightGroup && (jsxRuntime.jsx("span", { className: buildClassName('absolute end-0 top-0 h-full flex items-center justify-center text-gray-400 dark:text-gray-500', currentSize.group), children: actualRightGroup }))] }), suffix && (jsxRuntime.jsx("span", { className: "inline-flex items-center px-3 rounded-r-lg border border-l-0 border-[var(--ui-border)] bg-gray-50 dark:bg-gray-800 text-gray-500 dark:text-gray-400 text-xs font-medium shrink-0", children: suffix }))] }), (showErrorMessage && error) || helperText || showCount ? (jsxRuntime.jsxs("div", { className: "mt-1.5 flex items-center justify-between gap-2 text-xs", children: [jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: showErrorMessage && error ? (jsxRuntime.jsx(TextContent, { error: true, small: true, id: errorId, children: error })) : helperText ? (jsxRuntime.jsx(TextContent, { muted: true, small: true, id: helperId, children: helperText })) : null }), showCount && (jsxRuntime.jsxs("span", { className: buildClassName('text-[11px] font-medium shrink-0 ml-auto', maxLength && charLength >= maxLength
|
|
2095
2199
|
? 'text-red-500 font-semibold'
|
|
2096
2200
|
: 'text-gray-400 dark:text-gray-500'), children: [charLength, maxLength ? ` / ${maxLength}` : ''] }))] })) : null] }));
|
|
2097
2201
|
});
|
|
@@ -2316,16 +2420,20 @@ const THEME_MAP = {
|
|
|
2316
2420
|
danger: 'checked:border-[var(--ui-danger)] checked:hover:border-[var(--ui-danger)] focus:ring-[var(--ui-danger-ring)]',
|
|
2317
2421
|
};
|
|
2318
2422
|
const Radio = React.forwardRef((props, ref) => {
|
|
2319
|
-
const { id, name, label, description, size = 'md', theme = 'primary', labelClass = '', descriptionClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '', ...rest } = props;
|
|
2423
|
+
const { id, name, label, description, size = 'md', theme = 'primary', labelClass = '', descriptionClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '', required, 'aria-describedby': ariaDescribedBy, ...rest } = props;
|
|
2320
2424
|
const generatedId = React.useId();
|
|
2321
2425
|
const radioId = id !== null && id !== void 0 ? id : (name && rest.value ? `${name}-${rest.value}` : generatedId);
|
|
2322
|
-
|
|
2426
|
+
const hasError = Boolean(error);
|
|
2427
|
+
const errorId = radioId && hasError ? `${radioId}-error` : undefined;
|
|
2428
|
+
const descId = radioId && description ? `${radioId}-desc` : undefined;
|
|
2429
|
+
const describedBy = [ariaDescribedBy, errorId, descId].filter(Boolean).join(' ') || undefined;
|
|
2430
|
+
return (jsxRuntime.jsxs("div", { className: buildClassName(wrapperClass), children: [jsxRuntime.jsxs("div", { className: buildClassName('flex items-start gap-2.5', containerClass), children: [jsxRuntime.jsx("div", { className: "grid place-items-center shrink-0 pt-0.5", children: jsxRuntime.jsx("input", { ref: ref, ...rest, id: radioId, name: name, type: "radio", disabled: disabled, required: required, "aria-required": required ? true : undefined, "aria-invalid": hasError ? true : undefined, "aria-describedby": describedBy, "aria-checked": rest.checked, className: buildClassName('peer transition-all col-start-1 row-start-1 appearance-none shrink-0 rounded-full', 'border focus:ring-2 focus:ring-offset-2', 'checked:border-5 checked:bg-white', 'dark:focus:ring-offset-gray-800', SIZE_MAP[size], THEME_MAP[theme], disabled
|
|
2323
2431
|
? 'border-[var(--ui-border-muted)] bg-gray-100 dark:bg-gray-800 pointer-events-none'
|
|
2324
|
-
: 'bg-white dark:bg-gray-700 border-[var(--ui-border)] hover:border-gray-400 dark:hover:border-gray-500 cursor-pointer', className) }) }), (label || description) && (jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5 min-w-0", children: [label && (jsxRuntime.jsx(Label, { htmlFor: radioId, className: buildClassName(disabled
|
|
2432
|
+
: 'bg-white dark:bg-gray-700 border-[var(--ui-border)] hover:border-gray-400 dark:hover:border-gray-500 cursor-pointer', className) }) }), (label || description) && (jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5 min-w-0", children: [label && (jsxRuntime.jsx(Label, { htmlFor: radioId, "aria-required": required, className: buildClassName(disabled
|
|
2325
2433
|
? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
|
|
2326
|
-
: 'cursor-pointer text-gray-700 dark:text-gray-200 font-medium', labelClass), children: label })), description && (jsxRuntime.jsx("p", { className: buildClassName('text-xs leading-normal', disabled
|
|
2434
|
+
: 'cursor-pointer text-gray-700 dark:text-gray-200 font-medium', labelClass), children: label })), description && (jsxRuntime.jsx("p", { id: descId, className: buildClassName('text-xs leading-normal', disabled
|
|
2327
2435
|
? 'text-gray-300 dark:text-gray-600'
|
|
2328
|
-
: 'text-gray-500 dark:text-gray-400', descriptionClass), children: description }))] }))] }), error && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) }))] }));
|
|
2436
|
+
: 'text-gray-500 dark:text-gray-400', descriptionClass), children: description }))] }))] }), error && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { error: true, small: true, id: errorId, children: error }) }))] }));
|
|
2329
2437
|
});
|
|
2330
2438
|
Radio.displayName = 'Radio';
|
|
2331
2439
|
|
|
@@ -2336,7 +2444,8 @@ const COLUMN_GRID_MAP = {
|
|
|
2336
2444
|
4: 'grid-cols-1 md:grid-cols-4',
|
|
2337
2445
|
};
|
|
2338
2446
|
const RadioGroup = (props) => {
|
|
2339
|
-
const { name, variant = 'default', size = 'md', theme = 'primary', label, labelClass, labelWrapperClass, labelHint, containerClass, options, value, columns, disabled = false, row, error, showErrorMessage = true, onChange, } = props;
|
|
2447
|
+
const { name, variant = 'default', size = 'md', theme = 'primary', label, labelClass, labelWrapperClass, labelHint, containerClass, options, value, columns, disabled = false, row, error, showErrorMessage = true, onChange, required, 'aria-describedby': ariaDescribedBy, } = props;
|
|
2448
|
+
const groupId = React.useId();
|
|
2340
2449
|
const items = React.useMemo(() => {
|
|
2341
2450
|
var _a;
|
|
2342
2451
|
return (_a = options === null || options === void 0 ? void 0 : options.map((option) => typeof option === 'string'
|
|
@@ -2350,7 +2459,11 @@ const RadioGroup = (props) => {
|
|
|
2350
2459
|
: option)) !== null && _a !== void 0 ? _a : [];
|
|
2351
2460
|
}, [options]);
|
|
2352
2461
|
const isCards = variant === 'cards';
|
|
2353
|
-
|
|
2462
|
+
const hasError = Boolean(error);
|
|
2463
|
+
const labelId = label ? `${groupId}-label` : undefined;
|
|
2464
|
+
const errorId = groupId && hasError && showErrorMessage ? `${groupId}-error` : undefined;
|
|
2465
|
+
const describedBy = [ariaDescribedBy, errorId].filter(Boolean).join(' ') || undefined;
|
|
2466
|
+
return (jsxRuntime.jsxs("div", { role: "radiogroup", "aria-labelledby": labelId, "aria-describedby": describedBy, className: buildClassName(containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between mb-3', labelWrapperClass), children: [jsxRuntime.jsx(Label, { id: labelId, className: labelClass, "aria-required": required, children: label }), labelHint] })), jsxRuntime.jsx("div", { className: buildClassName(columns
|
|
2354
2467
|
? buildClassName('grid gap-3', COLUMN_GRID_MAP[columns])
|
|
2355
2468
|
: row || isCards
|
|
2356
2469
|
? 'flex flex-wrap gap-3'
|
|
@@ -2364,7 +2477,7 @@ const RadioGroup = (props) => {
|
|
|
2364
2477
|
'opacity-50 cursor-not-allowed pointer-events-none bg-gray-50 dark:bg-gray-800/50'), children: [jsxRuntime.jsx(Radio, { name: name, id: `${name}-${item.value}`, value: item.value, "aria-label": item.label, size: size, theme: theme, checked: isSelected, disabled: isItemDisabled, onChange: onChange, containerClass: "pt-0.5" }), jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [item.icon && (jsxRuntime.jsx("span", { className: "shrink-0 text-gray-500 dark:text-gray-400", children: item.icon })), jsxRuntime.jsx("span", { className: "text-sm font-semibold text-gray-900 dark:text-white truncate", children: item.label })] }), item.description && (jsxRuntime.jsx("p", { className: "mt-1 text-xs text-gray-500 dark:text-gray-400 font-normal leading-relaxed", children: item.description }))] })] }, item.value));
|
|
2365
2478
|
}
|
|
2366
2479
|
return (jsxRuntime.jsx(Radio, { name: name, id: `${name}-${item.value}`, label: item.label, description: item.description, value: item.value, size: size, theme: theme, checked: isSelected, disabled: isItemDisabled, onChange: onChange }, item.value));
|
|
2367
|
-
}) }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1.5", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) }))] }));
|
|
2480
|
+
}) }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1.5", children: jsxRuntime.jsx(TextContent, { error: true, small: true, id: errorId, children: error }) }))] }));
|
|
2368
2481
|
};
|
|
2369
2482
|
RadioGroup.displayName = 'RadioGroup';
|
|
2370
2483
|
|
|
@@ -2476,34 +2589,6 @@ const RadioSwitch = ({ label, items, selected, size = 'md', theme = 'primary', d
|
|
|
2476
2589
|
};
|
|
2477
2590
|
RadioSwitch.displayName = 'RadioSwitch';
|
|
2478
2591
|
|
|
2479
|
-
/**
|
|
2480
|
-
* Checks if a value is an array.
|
|
2481
|
-
* Equivalent to lodash's _.isArray.
|
|
2482
|
-
*/
|
|
2483
|
-
function isArray(value) {
|
|
2484
|
-
return Array.isArray(value);
|
|
2485
|
-
}
|
|
2486
|
-
|
|
2487
|
-
/**
|
|
2488
|
-
* Checks if a value is empty.
|
|
2489
|
-
* Works for arrays, objects, strings, Maps, and Sets.
|
|
2490
|
-
* Equivalent to lodash's _.isEmpty.
|
|
2491
|
-
*/
|
|
2492
|
-
function isEmpty(value) {
|
|
2493
|
-
if (value == null)
|
|
2494
|
-
return true; // null or undefined
|
|
2495
|
-
if (typeof value === 'string' || isArray(value)) {
|
|
2496
|
-
return value.length === 0;
|
|
2497
|
-
}
|
|
2498
|
-
if (value instanceof Map || value instanceof Set) {
|
|
2499
|
-
return value.size === 0;
|
|
2500
|
-
}
|
|
2501
|
-
if (typeof value === 'object') {
|
|
2502
|
-
return Object.keys(value).length === 0;
|
|
2503
|
-
}
|
|
2504
|
-
return false; // for numbers, booleans, functions, etc.
|
|
2505
|
-
}
|
|
2506
|
-
|
|
2507
2592
|
const sizeClasses$3 = {
|
|
2508
2593
|
sm: {
|
|
2509
2594
|
track: 'h-1.5',
|
|
@@ -3486,7 +3571,7 @@ const parseNumericValue = (val) => {
|
|
|
3486
3571
|
const cleaned = String(val).replace(/[^0-9.-]/g, '');
|
|
3487
3572
|
return cleaned !== '' ? Number(cleaned) : NaN;
|
|
3488
3573
|
};
|
|
3489
|
-
function DataList({ items, columns, renderItem, searchable = false, searchQuery: externalSearchQuery, onSearchChange, searchFields, filterFields, selectedFilters: externalFilters, onFilterChange, sorting: externalSorting, onSortChange, swipeableActions, rowKey = 'id', selectable = false, selectedKeys = [], onSelectionChange, onItemClick, loading = false, emptyMessage = 'No items found', pagination, setPagination, containerClass = '', itemClass = '', }) {
|
|
3574
|
+
function DataList({ items, columns, renderItem, searchable = false, searchQuery: externalSearchQuery, onSearchChange, searchFields, filterFields, selectedFilters: externalFilters, onFilterChange, sorting: externalSorting, onSortChange, swipeableActions, rowKey = 'id', selectable = false, selectedKeys = [], onSelectionChange, onItemClick, loading = false, emptyMessage = 'No items found', pagination, setPagination, paginationClass, paginationContainerClass, paginationProps, renderPagination, containerClass = '', itemClass = '', }) {
|
|
3490
3575
|
// Internal Search State
|
|
3491
3576
|
const [internalSearchQuery, setInternalSearchQuery] = React.useState('');
|
|
3492
3577
|
const searchQuery = externalSearchQuery !== undefined ? externalSearchQuery : internalSearchQuery;
|
|
@@ -3734,10 +3819,12 @@ function DataList({ items, columns, renderItem, searchable = false, searchQuery:
|
|
|
3734
3819
|
return (jsxRuntime.jsx(SwipeableRow, { leftActions: actions.leftActions, rightActions: actions.rightActions, children: contentNode }, key));
|
|
3735
3820
|
}
|
|
3736
3821
|
return jsxRuntime.jsx("div", { children: contentNode }, key);
|
|
3737
|
-
})) }), pagination && pagination.total > 0 && (jsxRuntime.jsx("div", { className:
|
|
3822
|
+
})) }), pagination && pagination.total > 0 && (jsxRuntime.jsx("div", { className: buildClassName('pt-2 flex justify-center w-full', paginationClass, paginationContainerClass), children: renderPagination ? (renderPagination(pagination, setPagination)) : (jsxRuntime.jsx(Pagination, { ...pagination, ...paginationProps, onChange: (newPag) => setPagination === null || setPagination === void 0 ? void 0 : setPagination(newPag) })) }))] }));
|
|
3738
3823
|
}
|
|
3739
3824
|
DataList.displayName = 'DataList';
|
|
3740
3825
|
|
|
3826
|
+
const NOOP = () => { };
|
|
3827
|
+
const EMPTY_ARRAY = [];
|
|
3741
3828
|
const sizeClasses$1 = {
|
|
3742
3829
|
sm: {
|
|
3743
3830
|
input: 'px-2.5 py-1.5 text-xs leading-5 min-h-[32px]',
|
|
@@ -3759,7 +3846,7 @@ const sizeClasses$1 = {
|
|
|
3759
3846
|
},
|
|
3760
3847
|
};
|
|
3761
3848
|
const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
3762
|
-
const { labelKey = 'label', valueKey = 'value', name, id = name, options =
|
|
3849
|
+
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;
|
|
3763
3850
|
if (props.asBottomSheet || props.mobileMode === 'sheet') {
|
|
3764
3851
|
return jsxRuntime.jsx(MobilePicker, { ...props });
|
|
3765
3852
|
}
|
|
@@ -3777,7 +3864,7 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
3777
3864
|
const shouldShowTypeToSearch = async && !query && !optionsLoading && allOptions.length === 0;
|
|
3778
3865
|
const currentSize = sizeClasses$1[size] || sizeClasses$1.md;
|
|
3779
3866
|
const baseInputClass = React.useMemo(() => buildInputClass('', { disabled, hasError, size }), [disabled, hasError, size]);
|
|
3780
|
-
const chevronIconClass = buildClassName(currentSize.icon, 'stroke-gray-500 group-data-[hover]:stroke-gray-800', 'dark:stroke-gray-300 dark:group-data-[hover]:stroke-gray-100', disabled && 'dark:stroke-gray-600 stroke-gray-400');
|
|
3867
|
+
const chevronIconClass = React.useMemo(() => buildClassName(currentSize.icon, 'stroke-gray-500 group-data-[hover]:stroke-gray-800', 'dark:stroke-gray-300 dark:group-data-[hover]:stroke-gray-100', disabled && 'dark:stroke-gray-600 stroke-gray-400'), [currentSize.icon, disabled]);
|
|
3781
3868
|
const inputRef = React.useRef(null);
|
|
3782
3869
|
const containerRef = React.useRef(null);
|
|
3783
3870
|
React.useImperativeHandle(forwardedRef, () => inputRef.current, [inputRef]);
|
|
@@ -3853,7 +3940,7 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
3853
3940
|
maxSelection,
|
|
3854
3941
|
onChange,
|
|
3855
3942
|
]);
|
|
3856
|
-
const removeSelection = (idx) => {
|
|
3943
|
+
const removeSelection = React.useCallback((idx) => {
|
|
3857
3944
|
var _a;
|
|
3858
3945
|
if (!isArray(selected))
|
|
3859
3946
|
return;
|
|
@@ -3861,7 +3948,7 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
3861
3948
|
next.splice(idx, 1);
|
|
3862
3949
|
onChange(next);
|
|
3863
3950
|
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
3864
|
-
};
|
|
3951
|
+
}, [selected, onChange]);
|
|
3865
3952
|
const handleOnChangeInternal = React.useCallback((value) => {
|
|
3866
3953
|
if (value === null) {
|
|
3867
3954
|
onChange(multiple ? [] : null);
|
|
@@ -3905,9 +3992,7 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
3905
3992
|
}
|
|
3906
3993
|
}, [onAdd, handleOnChangeInternal]);
|
|
3907
3994
|
const onSearchRef = React.useRef(props.onSearch);
|
|
3908
|
-
|
|
3909
|
-
onSearchRef.current = props.onSearch;
|
|
3910
|
-
}, [props.onSearch]);
|
|
3995
|
+
onSearchRef.current = props.onSearch;
|
|
3911
3996
|
const handleSearch = React.useCallback(async (searchString) => {
|
|
3912
3997
|
var _a;
|
|
3913
3998
|
try {
|
|
@@ -3939,20 +4024,40 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
3939
4024
|
return selected !== null && selected !== void 0 ? selected : null;
|
|
3940
4025
|
}
|
|
3941
4026
|
}, [multiple, selected]);
|
|
4027
|
+
const selectedValueSet = React.useMemo(() => {
|
|
4028
|
+
if (!normalizedSelected)
|
|
4029
|
+
return new Set();
|
|
4030
|
+
if (Array.isArray(normalizedSelected)) {
|
|
4031
|
+
return new Set(normalizedSelected.map((s) => getValue(s)));
|
|
4032
|
+
}
|
|
4033
|
+
return new Set([getValue(normalizedSelected)]);
|
|
4034
|
+
}, [normalizedSelected, getValue]);
|
|
3942
4035
|
const isAllSelected = React.useMemo(() => {
|
|
3943
4036
|
if (!multiple || !Array.isArray(normalizedSelected) || !allOptions.length)
|
|
3944
4037
|
return false;
|
|
3945
4038
|
return allOptions.length === normalizedSelected.length;
|
|
3946
4039
|
}, [multiple, normalizedSelected, allOptions]);
|
|
3947
|
-
const handleToggleSelectAll = () => {
|
|
4040
|
+
const handleToggleSelectAll = React.useCallback(() => {
|
|
3948
4041
|
if (isAllSelected) {
|
|
3949
4042
|
onChange([]);
|
|
3950
4043
|
}
|
|
3951
4044
|
else {
|
|
3952
4045
|
onChange(allOptions);
|
|
3953
4046
|
}
|
|
3954
|
-
};
|
|
4047
|
+
}, [isAllSelected, onChange, allOptions]);
|
|
3955
4048
|
const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
|
|
4049
|
+
const hasFreeTextOption = Boolean(allowFreeText &&
|
|
4050
|
+
query.trim() &&
|
|
4051
|
+
!allOptions.some((opt) => { var _a; return ((_a = getDisplayValue(opt)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === query.trim().toLowerCase(); }));
|
|
4052
|
+
const hasAddOption = Boolean(!allowFreeText && allowAdd && query.trim());
|
|
4053
|
+
const hasGroupOptions = Boolean(filteredGroups && filteredGroups.length > 0);
|
|
4054
|
+
const hasFlatOptions = filteredOptions.length > 0;
|
|
4055
|
+
const hasAnyOptionsToDisplay = hasFlatOptions || hasGroupOptions || hasFreeTextOption || hasAddOption;
|
|
4056
|
+
const renderOptionItem = (option) => {
|
|
4057
|
+
const optVal = getValue(option);
|
|
4058
|
+
const isOptSelected = selectedValueSet.has(optVal);
|
|
4059
|
+
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));
|
|
4060
|
+
};
|
|
3956
4061
|
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) &&
|
|
3957
4062
|
(normalizedSelected === null || normalizedSelected === void 0 ? void 0 : normalizedSelected.length) > 0 &&
|
|
3958
4063
|
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) => {
|
|
@@ -3996,26 +4101,9 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
3996
4101
|
}, 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" }), allowClear && !isEmpty(normalizedSelected) && (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", noOutlineOnFocus: true, 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', gap: 5 }, as: "div", modal: modalDropdown, 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 &&
|
|
3997
4102
|
query.trim() &&
|
|
3998
4103
|
!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
|
|
3999
|
-
? 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(
|
|
4000
|
-
const optVal = getValue(option);
|
|
4001
|
-
const isOptSelected = multiple
|
|
4002
|
-
? isArray(normalizedSelected) &&
|
|
4003
|
-
normalizedSelected.some((s) => getValue(s) === optVal)
|
|
4004
|
-
: normalizedSelected && getValue(normalizedSelected) === optVal;
|
|
4005
|
-
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));
|
|
4006
|
-
})] }, gIdx)))
|
|
4104
|
+
? 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)))
|
|
4007
4105
|
: /* Render Standard Flat Options */
|
|
4008
|
-
filteredOptions.map((
|
|
4009
|
-
const optVal = getValue(option);
|
|
4010
|
-
const isOptSelected = multiple
|
|
4011
|
-
? isArray(normalizedSelected) &&
|
|
4012
|
-
normalizedSelected.some((s) => getValue(s) === optVal)
|
|
4013
|
-
: normalizedSelected && getValue(normalizedSelected) === optVal;
|
|
4014
|
-
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));
|
|
4015
|
-
}), filteredOptions.length === 0 &&
|
|
4016
|
-
!(filteredGroups === null || filteredGroups === void 0 ? void 0 : filteredGroups.length) &&
|
|
4017
|
-
!allowFreeText &&
|
|
4018
|
-
!(allowAdd && query.trim()) && (jsxRuntime.jsxs("div", { "data-testid": "no-result-found", className: "flex items-center gap-2 py-1.5 px-3 select-none", children: [jsxRuntime.jsx(TextContent, { muted: true, small: true, children: noOptionsText }), jsxRuntime.jsx(TextContent, { strong: true, small: true, children: query })] })), !allowFreeText && allowAdd && Boolean(query.trim()) && (jsxRuntime.jsx(react.ComboboxOption, { value: query, className: "group rounded-lg py-1.5 px-3 cursor-pointer text-gray-700 dark:text-gray-300 data-[focus]:bg-gray-100 dark:data-[focus]:bg-gray-700", onClick: () => void handleAddAndSelect(query), children: jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsx(SvgCirclePlus, { className: "size-4.5" }), jsxRuntime.jsxs(TextContent, { children: [addNewText, " \"", query, "\""] })] }) }, `add-${query.trim()}`))] }))] })] }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) })), !error && helperText && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { muted: true, small: true, children: helperText }) }))] }));
|
|
4106
|
+
filteredOptions.map(renderOptionItem), !hasAnyOptionsToDisplay && Boolean(noOptionsText) && (jsxRuntime.jsxs("div", { "data-testid": "no-result-found", className: "flex items-center gap-2 py-1.5 px-3 select-none", children: [jsxRuntime.jsx(TextContent, { muted: true, small: true, children: noOptionsText }), Boolean(query.trim()) && (jsxRuntime.jsxs(TextContent, { strong: true, small: true, children: ["\"", query.trim(), "\""] }))] })), !allowFreeText && allowAdd && Boolean(query.trim()) && (jsxRuntime.jsx(react.ComboboxOption, { value: query, className: "group rounded-lg py-1.5 px-3 cursor-pointer text-gray-700 dark:text-gray-300 data-[focus]:bg-gray-100 dark:data-[focus]:bg-gray-700", onClick: () => void handleAddAndSelect(query), children: jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsx(SvgCirclePlus, { className: "size-4.5" }), jsxRuntime.jsxs(TextContent, { children: [addNewText, " \"", query, "\""] })] }) }, `add-${query.trim()}`))] }))] })] }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) })), !error && helperText && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { muted: true, small: true, children: helperText }) }))] }));
|
|
4019
4107
|
});
|
|
4020
4108
|
SelectBox.displayName = 'SelectBox';
|
|
4021
4109
|
|
|
@@ -4369,7 +4457,7 @@ const resizeClasses = {
|
|
|
4369
4457
|
both: 'resize',
|
|
4370
4458
|
};
|
|
4371
4459
|
const Textarea = React.forwardRef((props, ref) => {
|
|
4372
|
-
const { name, id = name, label = null, labelHint = null, className = '', labelClass = '', labelWrapperClass = '', containerClass = '', error, showErrorMessage = true, rightGroup = null, leftGroup = null, size = 'md', autoSize = false, showCount = false, helperText, resize = 'vertical', value, defaultValue, onChange, maxLength, ...inputProps } = props;
|
|
4460
|
+
const { name, id = name, label = null, labelHint = null, className = '', labelClass = '', labelWrapperClass = '', containerClass = '', error, showErrorMessage = true, rightGroup = null, leftGroup = null, size = 'md', autoSize = false, showCount = false, helperText, resize = 'vertical', value, defaultValue, onChange, maxLength, required, 'aria-describedby': ariaDescribedBy, ...inputProps } = props;
|
|
4373
4461
|
const innerRef = React.useRef(null);
|
|
4374
4462
|
React.useImperativeHandle(ref, () => innerRef.current);
|
|
4375
4463
|
const [uncontrolledValue, setUncontrolledValue] = React.useState(String(defaultValue !== null && defaultValue !== void 0 ? defaultValue : ''));
|
|
@@ -4383,19 +4471,22 @@ const Textarea = React.forwardRef((props, ref) => {
|
|
|
4383
4471
|
innerRef.current.style.height = `${innerRef.current.scrollHeight}px`;
|
|
4384
4472
|
}
|
|
4385
4473
|
}, [autoSize, currentVal]);
|
|
4386
|
-
const handleTextareaChange = (e) => {
|
|
4474
|
+
const handleTextareaChange = React.useCallback((e) => {
|
|
4387
4475
|
if (!isControlled) {
|
|
4388
4476
|
setUncontrolledValue(e.target.value);
|
|
4389
4477
|
}
|
|
4390
4478
|
onChange === null || onChange === void 0 ? void 0 : onChange(e);
|
|
4391
|
-
};
|
|
4479
|
+
}, [isControlled, onChange]);
|
|
4392
4480
|
const hasError = !isEmpty(error);
|
|
4393
4481
|
const hasRightGroup = rightGroup != null && rightGroup !== undefined;
|
|
4394
4482
|
const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
|
|
4395
4483
|
const currentSize = sizeClasses[size] || sizeClasses.md;
|
|
4396
|
-
|
|
4484
|
+
const errorId = id && hasError && showErrorMessage ? `${id}-error` : undefined;
|
|
4485
|
+
const helperId = id && helperText && !hasError ? `${id}-helper` : undefined;
|
|
4486
|
+
const describedBy = [ariaDescribedBy, errorId, helperId].filter(Boolean).join(' ') || undefined;
|
|
4487
|
+
return (jsxRuntime.jsxs("div", { className: buildClassName('group', hasError && 'has-error', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between mb-1.5', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), labelHint && jsxRuntime.jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxRuntime.jsxs("div", { className: "relative", children: [hasLeftGroup && (jsxRuntime.jsx("span", { className: "absolute top-2.5 left-3 flex items-center justify-center text-gray-400 dark:text-gray-500", children: leftGroup })), jsxRuntime.jsx("textarea", { ref: innerRef, autoComplete: "off", ...inputProps, value: value, defaultValue: defaultValue, onChange: handleTextareaChange, maxLength: maxLength, name: name, id: id, required: required, "aria-invalid": hasError ? true : undefined, "aria-describedby": describedBy, className: buildClassName('transition-all duration-150', 'block w-full rounded-lg bg-white', currentSize.textarea, autoSize ? 'resize-none overflow-hidden' : resizeClasses[resize], 'text-gray-900 outline outline-1 -outline-offset-1', hasError
|
|
4397
4488
|
? '!outline-red-500 !ring-1 !ring-red-500/50 dark:!outline-red-500'
|
|
4398
|
-
: 'outline-[var(--ui-border)]', 'focus:outline-[var(--ui-focus-ring)] focus:outline-2 focus:-outline-offset-2', 'dark:bg-gray-700 dark:placeholder-gray-400 placeholder:text-gray-400 dark:text-white', 'disabled:pointer-events-none disabled:bg-gray-100 disabled:outline-[var(--ui-border-muted)]', 'dark:disabled:bg-gray-800 dark:disabled:text-gray-600 dark:disabled:placeholder-gray-600 dark:disabled:outline-[var(--ui-border-muted)]', hasRightGroup ? 'pe-10' : '', hasLeftGroup ? 'ps-10' : '', className) }), hasRightGroup && (jsxRuntime.jsx("span", { className: "absolute end-3 top-2.5 flex items-center justify-center text-gray-400 dark:text-gray-500", children: rightGroup }))] }), (showErrorMessage && error) || helperText || showCount ? (jsxRuntime.jsxs("div", { className: "mt-1.5 flex items-center justify-between gap-2 text-xs", children: [jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: showErrorMessage && error ? (jsxRuntime.jsx(TextContent, { error: true, small: true, children: error })) : helperText ? (jsxRuntime.jsx(TextContent, { muted: true, small: true, children: helperText })) : null }), showCount && (jsxRuntime.jsxs("span", { className: buildClassName('text-[11px] font-medium shrink-0 ml-auto', maxLength && charLength >= maxLength
|
|
4489
|
+
: 'outline-[var(--ui-border)]', 'focus:outline-[var(--ui-focus-ring)] focus:outline-2 focus:-outline-offset-2', 'dark:bg-gray-700 dark:placeholder-gray-400 placeholder:text-gray-400 dark:text-white', 'disabled:pointer-events-none disabled:bg-gray-100 disabled:outline-[var(--ui-border-muted)]', 'dark:disabled:bg-gray-800 dark:disabled:text-gray-600 dark:disabled:placeholder-gray-600 dark:disabled:outline-[var(--ui-border-muted)]', hasRightGroup ? 'pe-10' : '', hasLeftGroup ? 'ps-10' : '', className) }), hasRightGroup && (jsxRuntime.jsx("span", { className: "absolute end-3 top-2.5 flex items-center justify-center text-gray-400 dark:text-gray-500", children: rightGroup }))] }), (showErrorMessage && error) || helperText || showCount ? (jsxRuntime.jsxs("div", { className: "mt-1.5 flex items-center justify-between gap-2 text-xs", children: [jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: showErrorMessage && error ? (jsxRuntime.jsx(TextContent, { error: true, small: true, id: errorId, children: error })) : helperText ? (jsxRuntime.jsx(TextContent, { muted: true, small: true, id: helperId, children: helperText })) : null }), showCount && (jsxRuntime.jsxs("span", { className: buildClassName('text-[11px] font-medium shrink-0 ml-auto', maxLength && charLength >= maxLength
|
|
4399
4490
|
? 'text-red-500 font-semibold'
|
|
4400
4491
|
: 'text-gray-400 dark:text-gray-500'), children: [charLength, maxLength ? ` / ${maxLength}` : ''] }))] })) : null] }));
|
|
4401
4492
|
});
|