infinity-ui-elements 1.9.28 → 1.13.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/components/FileInput/FileInput.d.ts +38 -0
- package/dist/components/FileInput/FileInput.d.ts.map +1 -0
- package/dist/components/FileInput/FileInput.stories.d.ts +27 -0
- package/dist/components/FileInput/FileInput.stories.d.ts.map +1 -0
- package/dist/components/FileInput/index.d.ts +3 -0
- package/dist/components/FileInput/index.d.ts.map +1 -0
- package/dist/components/SearchableDropdown/SearchableDropdown.stories.d.ts +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +183 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +184 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3334,6 +3334,188 @@ const Dropdown = React__namespace.forwardRef(({ className, trigger, items = [],
|
|
|
3334
3334
|
});
|
|
3335
3335
|
Dropdown.displayName = "Dropdown";
|
|
3336
3336
|
|
|
3337
|
+
const fileInputVariants = classVarianceAuthority.cva("relative flex items-center gap-3 border rounded-large transition-all font-functional font-size-100 leading-100", {
|
|
3338
|
+
variants: {
|
|
3339
|
+
size: {
|
|
3340
|
+
small: "h-[28px] px-3 text-xs gap-2",
|
|
3341
|
+
medium: "h-[36px] px-4 text-sm gap-2",
|
|
3342
|
+
large: "h-[44px] px-5 text-base gap-3",
|
|
3343
|
+
},
|
|
3344
|
+
validationState: {
|
|
3345
|
+
none: `
|
|
3346
|
+
border-action-outline-neutral-faded
|
|
3347
|
+
focus-within:border-action-outline-primary-default
|
|
3348
|
+
focus-within:bg-white!
|
|
3349
|
+
focus-within:ring-2
|
|
3350
|
+
ring-surface-outline-primary-muted`,
|
|
3351
|
+
positive: `
|
|
3352
|
+
border-action-outline-positive-default
|
|
3353
|
+
focus-within:border-action-outline-positive-hover
|
|
3354
|
+
focus-within:ring-2
|
|
3355
|
+
ring-action-outline-positive-faded-hover`,
|
|
3356
|
+
negative: `border-action-outline-negative-default
|
|
3357
|
+
focus-within:border-action-outline-negative-hover
|
|
3358
|
+
focus-within:ring-2
|
|
3359
|
+
ring-action-outline-negative-faded-hover`,
|
|
3360
|
+
},
|
|
3361
|
+
isDisabled: {
|
|
3362
|
+
true: `
|
|
3363
|
+
border
|
|
3364
|
+
border-action-outline-neutral-disabled
|
|
3365
|
+
hover:border-action-outline-neutral-disabled
|
|
3366
|
+
bg-surface-fill-neutral-intense
|
|
3367
|
+
hover:bg-surface-fill-neutral-intense
|
|
3368
|
+
cursor-not-allowed`,
|
|
3369
|
+
false: "",
|
|
3370
|
+
},
|
|
3371
|
+
},
|
|
3372
|
+
defaultVariants: {
|
|
3373
|
+
size: "medium",
|
|
3374
|
+
validationState: "none",
|
|
3375
|
+
isDisabled: false,
|
|
3376
|
+
},
|
|
3377
|
+
});
|
|
3378
|
+
const formatFileSize$1 = (bytes) => {
|
|
3379
|
+
if (bytes === 0)
|
|
3380
|
+
return "0 Bytes";
|
|
3381
|
+
const k = 1024;
|
|
3382
|
+
const sizes = ["Bytes", "KB", "MB", "GB"];
|
|
3383
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
3384
|
+
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + " " + sizes[i];
|
|
3385
|
+
};
|
|
3386
|
+
const FileInput = React__namespace.forwardRef(({ label, helperText, errorText, successText, size = "medium", validationState = "none", isDisabled = false, isRequired = false, isOptional = false, accept, multiple = false, maxSize, value, onChange, containerClassName, labelClassName, inputClassName, infoHeading, infoDescription, LinkComponent, linkText, linkHref, onLinkClick, buttonText = "Choose File", placeholderText = "No file chosen", showClearButton = true, onClear, className, ...props }, ref) => {
|
|
3387
|
+
const fileInputRef = React__namespace.useRef(null);
|
|
3388
|
+
const [selectedFiles, setSelectedFiles] = React__namespace.useState([]);
|
|
3389
|
+
const [error, setError] = React__namespace.useState(null);
|
|
3390
|
+
// Initialize from value prop
|
|
3391
|
+
React__namespace.useEffect(() => {
|
|
3392
|
+
if (value) {
|
|
3393
|
+
const filesArray = Array.isArray(value) ? value : [value];
|
|
3394
|
+
setSelectedFiles(filesArray);
|
|
3395
|
+
}
|
|
3396
|
+
else {
|
|
3397
|
+
setSelectedFiles([]);
|
|
3398
|
+
}
|
|
3399
|
+
}, [value]);
|
|
3400
|
+
const processFiles = (files) => {
|
|
3401
|
+
const fileArray = Array.from(files);
|
|
3402
|
+
const validFiles = [];
|
|
3403
|
+
let currentError = null;
|
|
3404
|
+
fileArray.forEach((file) => {
|
|
3405
|
+
// Check file size
|
|
3406
|
+
if (maxSize && file.size > maxSize) {
|
|
3407
|
+
currentError = `File "${file.name}" exceeds maximum size of ${formatFileSize$1(maxSize)}`;
|
|
3408
|
+
setError(currentError);
|
|
3409
|
+
return;
|
|
3410
|
+
}
|
|
3411
|
+
// Check if file type is accepted
|
|
3412
|
+
if (accept) {
|
|
3413
|
+
const acceptedTypes = accept.split(",").map((type) => type.trim());
|
|
3414
|
+
const isAccepted = acceptedTypes.some((type) => {
|
|
3415
|
+
if (type.startsWith(".")) {
|
|
3416
|
+
return file.name.toLowerCase().endsWith(type.toLowerCase());
|
|
3417
|
+
}
|
|
3418
|
+
if (type.includes("/*")) {
|
|
3419
|
+
const baseType = type.split("/")[0];
|
|
3420
|
+
return file.type.startsWith(baseType + "/");
|
|
3421
|
+
}
|
|
3422
|
+
return file.type === type;
|
|
3423
|
+
});
|
|
3424
|
+
if (!isAccepted) {
|
|
3425
|
+
currentError = `File type "${file.type}" is not accepted`;
|
|
3426
|
+
setError(currentError);
|
|
3427
|
+
return;
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
validFiles.push(file);
|
|
3431
|
+
});
|
|
3432
|
+
if (currentError) {
|
|
3433
|
+
setError(currentError);
|
|
3434
|
+
}
|
|
3435
|
+
else {
|
|
3436
|
+
setError(null);
|
|
3437
|
+
}
|
|
3438
|
+
return validFiles;
|
|
3439
|
+
};
|
|
3440
|
+
const handleFileInputChange = (e) => {
|
|
3441
|
+
if (e.target.files && e.target.files.length > 0) {
|
|
3442
|
+
const validFiles = processFiles(e.target.files);
|
|
3443
|
+
if (validFiles.length > 0) {
|
|
3444
|
+
const newFiles = multiple
|
|
3445
|
+
? [...selectedFiles, ...validFiles]
|
|
3446
|
+
: validFiles;
|
|
3447
|
+
setSelectedFiles(newFiles);
|
|
3448
|
+
if (onChange) {
|
|
3449
|
+
onChange(multiple ? newFiles : newFiles[0] || null);
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
// Reset input value to allow selecting the same file again
|
|
3453
|
+
e.target.value = "";
|
|
3454
|
+
}
|
|
3455
|
+
};
|
|
3456
|
+
const handleButtonClick = () => {
|
|
3457
|
+
if (!isDisabled && fileInputRef.current) {
|
|
3458
|
+
fileInputRef.current.click();
|
|
3459
|
+
}
|
|
3460
|
+
};
|
|
3461
|
+
const handleClear = () => {
|
|
3462
|
+
if (isDisabled)
|
|
3463
|
+
return;
|
|
3464
|
+
setSelectedFiles([]);
|
|
3465
|
+
setError(null);
|
|
3466
|
+
// Clear the file input
|
|
3467
|
+
if (fileInputRef.current) {
|
|
3468
|
+
fileInputRef.current.value = "";
|
|
3469
|
+
}
|
|
3470
|
+
if (onClear) {
|
|
3471
|
+
onClear();
|
|
3472
|
+
}
|
|
3473
|
+
if (onChange) {
|
|
3474
|
+
onChange(null);
|
|
3475
|
+
}
|
|
3476
|
+
};
|
|
3477
|
+
// Determine which helper text to show
|
|
3478
|
+
const displayHelperText = errorText || successText || helperText || error;
|
|
3479
|
+
const currentValidationState = errorText
|
|
3480
|
+
? "negative"
|
|
3481
|
+
: successText
|
|
3482
|
+
? "positive"
|
|
3483
|
+
: validationState;
|
|
3484
|
+
const sizeConfig = {
|
|
3485
|
+
small: {
|
|
3486
|
+
gap: "gap-2",
|
|
3487
|
+
buttonSize: "xsmall",
|
|
3488
|
+
},
|
|
3489
|
+
medium: {
|
|
3490
|
+
gap: "gap-2",
|
|
3491
|
+
buttonSize: "small",
|
|
3492
|
+
},
|
|
3493
|
+
large: {
|
|
3494
|
+
gap: "gap-3",
|
|
3495
|
+
buttonSize: "small",
|
|
3496
|
+
},
|
|
3497
|
+
};
|
|
3498
|
+
const config = sizeConfig[size];
|
|
3499
|
+
const getDisplayText = () => {
|
|
3500
|
+
if (selectedFiles.length === 0) {
|
|
3501
|
+
return placeholderText;
|
|
3502
|
+
}
|
|
3503
|
+
if (selectedFiles.length === 1) {
|
|
3504
|
+
return selectedFiles[0].name;
|
|
3505
|
+
}
|
|
3506
|
+
return `${selectedFiles.length} file(s) selected`;
|
|
3507
|
+
};
|
|
3508
|
+
const hasFiles = selectedFiles.length > 0;
|
|
3509
|
+
return (jsxRuntime.jsxs("div", { ref: ref, className: cn("w-full flex flex-col", config.gap, containerClassName), ...props, children: [label && (jsxRuntime.jsx(FormHeader, { label: label, size: size, isRequired: isRequired, isOptional: isOptional, infoHeading: infoHeading, infoDescription: infoDescription, LinkComponent: LinkComponent, linkText: linkText, linkHref: linkHref, onLinkClick: onLinkClick, className: "mb-2", labelClassName: labelClassName })), jsxRuntime.jsxs("div", { className: cn(fileInputVariants({
|
|
3510
|
+
size,
|
|
3511
|
+
validationState: currentValidationState,
|
|
3512
|
+
isDisabled,
|
|
3513
|
+
}), className), children: [jsxRuntime.jsx("input", { ref: fileInputRef, type: "file", accept: accept, multiple: multiple, disabled: isDisabled, onChange: handleFileInputChange, className: "hidden", "aria-label": label || "File upload" }), jsxRuntime.jsx(Link, { type: "action", color: "primary", size: config.buttonSize, onClick: handleButtonClick, isDisabled: isDisabled, className: "shrink-0", children: buttonText }), jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: jsxRuntime.jsx(Text, { variant: "body", size: "small", color: isDisabled ? "disabled" : "muted", className: "truncate", title: getDisplayText(), children: getDisplayText() }) }), showClearButton && hasFiles && !isDisabled && (jsxRuntime.jsx(IconButton, { icon: "close", size: "xsmall", onClick: handleClear, "aria-label": "Clear selected files", className: "shrink-0" }))] }), jsxRuntime.jsx(FormFooter, { helperText: displayHelperText || undefined, validationState: currentValidationState === "none"
|
|
3514
|
+
? "default"
|
|
3515
|
+
: currentValidationState, size: size, isDisabled: isDisabled, className: "mt-1" })] }));
|
|
3516
|
+
});
|
|
3517
|
+
FileInput.displayName = "FileInput";
|
|
3518
|
+
|
|
3337
3519
|
const Modal = React__namespace.forwardRef(({ isOpen, onClose, title, description, footer, children, variant = "default", size = "medium", showCloseButton = true, closeOnOverlayClick = true, closeOnEscape = true, className, contentClassName, headerClassName, bodyClassName, footerClassName, overlayClassName, ariaLabel, ariaDescribedBy, }, ref) => {
|
|
3338
3520
|
const modalRef = React__namespace.useRef(null);
|
|
3339
3521
|
const contentRef = ref || modalRef;
|
|
@@ -6190,6 +6372,7 @@ exports.DateRangePicker = DateRangePicker;
|
|
|
6190
6372
|
exports.Divider = Divider;
|
|
6191
6373
|
exports.Dropdown = Dropdown;
|
|
6192
6374
|
exports.DropdownMenu = DropdownMenu;
|
|
6375
|
+
exports.FileInput = FileInput;
|
|
6193
6376
|
exports.FormFooter = FormFooter;
|
|
6194
6377
|
exports.FormHeader = FormHeader;
|
|
6195
6378
|
exports.Icon = Icon;
|
|
@@ -6232,6 +6415,7 @@ exports.counterVariants = counterVariants;
|
|
|
6232
6415
|
exports.datePickerVariants = datePickerVariants;
|
|
6233
6416
|
exports.dateRangePickerVariants = dateRangePickerVariants;
|
|
6234
6417
|
exports.dropdownVariants = dropdownVariants;
|
|
6418
|
+
exports.fileInputVariants = fileInputVariants;
|
|
6235
6419
|
exports.getAvailableIcons = getAvailableIcons;
|
|
6236
6420
|
exports.hasIcon = hasIcon;
|
|
6237
6421
|
exports.iconButtonVariants = iconButtonVariants;
|