@nomos-ui/form 0.0.18 → 0.0.19
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/file-input.d.ts +111 -0
- package/dist/components/file-input.d.ts.map +1 -0
- package/dist/components/file-input.js +124 -0
- package/dist/components/file-input.js.map +1 -0
- package/dist/components/form-file-input.d.ts +44 -0
- package/dist/components/form-file-input.d.ts.map +1 -0
- package/dist/components/form-file-input.js +49 -0
- package/dist/components/form-file-input.js.map +1 -0
- package/dist/exports/index.d.ts +6 -2
- package/dist/exports/index.d.ts.map +1 -1
- package/dist/exports/index.js +7 -1
- package/dist/exports/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a single file entry managed by `FileInput`.
|
|
3
|
+
*
|
|
4
|
+
* The `id` is a stable random key generated on ingestion so React
|
|
5
|
+
* reconciliation stays correct even when the same filename is added twice.
|
|
6
|
+
*/
|
|
7
|
+
export type FileInputFile = {
|
|
8
|
+
/** The native browser `File` object. */
|
|
9
|
+
file: File;
|
|
10
|
+
/**
|
|
11
|
+
* Stable random identifier assigned when the file is added.
|
|
12
|
+
* Use this as the React `key` and to target a specific file for removal.
|
|
13
|
+
*/
|
|
14
|
+
id: string;
|
|
15
|
+
};
|
|
16
|
+
export type FileInputProps = {
|
|
17
|
+
/** Additional class name for the root container. */
|
|
18
|
+
className?: string;
|
|
19
|
+
/** Additional class name applied to the dashed dropzone area. */
|
|
20
|
+
dropzoneClassName?: string;
|
|
21
|
+
/** Label text rendered above the dropzone. */
|
|
22
|
+
label?: string;
|
|
23
|
+
/** Additional class name for the label row wrapper. */
|
|
24
|
+
labelClassName?: string;
|
|
25
|
+
/** Marks the field as required for native form validation. */
|
|
26
|
+
required?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* When `true`, renders a red asterisk next to the label.
|
|
29
|
+
* Only has an effect when `required` is also `true`.
|
|
30
|
+
*/
|
|
31
|
+
showRequiredSign?: boolean;
|
|
32
|
+
/** Helper text rendered below the file list (hidden when `error` is set). */
|
|
33
|
+
tip?: string;
|
|
34
|
+
/** Validation error message rendered below the file list in destructive color. */
|
|
35
|
+
error?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Accepted file types forwarded directly to the native `<input accept>`.
|
|
38
|
+
* Supports MIME types (e.g. `"image/*"`) and extensions (e.g. `".pdf,.docx"`).
|
|
39
|
+
*/
|
|
40
|
+
accept?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Human-readable format list shown inside the dropzone subtitle.
|
|
43
|
+
* Keep in sync with `accept` but written for end-users (e.g. `"JPEG, PNG, PDF"`).
|
|
44
|
+
*/
|
|
45
|
+
acceptLabel?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Maximum allowed file size **in bytes**.
|
|
48
|
+
* Files that exceed this limit are rejected — `onError` is called and the
|
|
49
|
+
* file is NOT added to the list.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* // 50 MB limit
|
|
53
|
+
* maxSize={50 * 1024 * 1024}
|
|
54
|
+
*/
|
|
55
|
+
maxSize?: number;
|
|
56
|
+
/**
|
|
57
|
+
* Allow the user to pick or drop more than one file at a time.
|
|
58
|
+
* When `false` (default) a new selection replaces the existing file.
|
|
59
|
+
*/
|
|
60
|
+
multiple?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Controlled list of currently accepted files.
|
|
63
|
+
* Pass an empty array (`[]`) to represent an empty / reset state.
|
|
64
|
+
*/
|
|
65
|
+
value?: FileInputFile[];
|
|
66
|
+
/**
|
|
67
|
+
* Fired whenever the file list changes — either a file was added or removed.
|
|
68
|
+
*
|
|
69
|
+
* @param files - The complete updated file list.
|
|
70
|
+
*/
|
|
71
|
+
onChange?: (files: FileInputFile[]) => void;
|
|
72
|
+
/**
|
|
73
|
+
* Fired when a file is rejected before being added to the list.
|
|
74
|
+
* Common reasons: file exceeds `maxSize`.
|
|
75
|
+
*
|
|
76
|
+
* @param message - A human-readable description of why the file was rejected.
|
|
77
|
+
*/
|
|
78
|
+
onError?: (message: string) => void;
|
|
79
|
+
/** Disables the dropzone click / drag and all per-file remove buttons. */
|
|
80
|
+
disabled?: boolean;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* A file-upload input that follows the shadcn component ideology.
|
|
84
|
+
*
|
|
85
|
+
* Features:
|
|
86
|
+
* - Semantic design tokens (`foreground`, `muted-foreground`, `destructive`, …)
|
|
87
|
+
* - Label + required sign + tip + error pattern identical to `Input` / `Textarea`
|
|
88
|
+
* - Drag-and-drop with a clear visual drag state
|
|
89
|
+
* - Per-file rows listing name, size, and a remove button
|
|
90
|
+
* - Single or multiple file support
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```tsx
|
|
94
|
+
* const [files, setFiles] = React.useState<FileInputFile[]>([]);
|
|
95
|
+
*
|
|
96
|
+
* <FileInput
|
|
97
|
+
* label="Resume"
|
|
98
|
+
* required
|
|
99
|
+
* showRequiredSign
|
|
100
|
+
* accept=".pdf,.doc,.docx"
|
|
101
|
+
* acceptLabel="PDF, DOC, DOCX"
|
|
102
|
+
* maxSize={50 * 1024 * 1024}
|
|
103
|
+
* tip="Max 50 MB per file."
|
|
104
|
+
* value={files}
|
|
105
|
+
* onChange={setFiles}
|
|
106
|
+
* onError={(msg) => toast.error(msg)}
|
|
107
|
+
* />
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export default function FileInput({ className, dropzoneClassName, label, labelClassName, required, showRequiredSign, tip, error, accept, acceptLabel, maxSize, multiple, value, onChange, onError, disabled, }: FileInputProps): import("react/jsx-runtime").JSX.Element;
|
|
111
|
+
//# sourceMappingURL=file-input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-input.d.ts","sourceRoot":"","sources":["../../src/components/file-input.tsx"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,wCAAwC;IACxC,IAAI,EAAE,IAAI,CAAC;IACX;;;OAGG;IACH,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,6EAA6E;IAC7E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,aAAa,EAAE,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,IAAI,CAAC;IAC5C;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAcF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAChC,SAAS,EACT,iBAAiB,EACjB,KAAK,EACL,cAAc,EACd,QAAgB,EAChB,gBAAwB,EACxB,GAAG,EACH,KAAK,EACL,MAAM,EACN,WAAW,EACX,OAAO,EACP,QAAgB,EAChB,KAAU,EACV,QAAQ,EACR,OAAO,EACP,QAAgB,GACjB,EAAE,cAAc,2CAyJhB"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.default = FileInput;
|
|
37
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
38
|
+
const React = __importStar(require("react"));
|
|
39
|
+
const lucide_react_1 = require("lucide-react");
|
|
40
|
+
const utils_1 = require("../utils/shadcn-ui/utils");
|
|
41
|
+
/** Converts a raw byte count into a human-readable string (B / KB / MB). */
|
|
42
|
+
function formatBytes(bytes) {
|
|
43
|
+
if (bytes < 1024)
|
|
44
|
+
return `${bytes} B`;
|
|
45
|
+
if (bytes < 1024 * 1024)
|
|
46
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
47
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
48
|
+
}
|
|
49
|
+
/** Generates a short random alphanumeric ID used as a stable React key. */
|
|
50
|
+
function generateId() {
|
|
51
|
+
return Math.random().toString(36).slice(2, 9);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A file-upload input that follows the shadcn component ideology.
|
|
55
|
+
*
|
|
56
|
+
* Features:
|
|
57
|
+
* - Semantic design tokens (`foreground`, `muted-foreground`, `destructive`, …)
|
|
58
|
+
* - Label + required sign + tip + error pattern identical to `Input` / `Textarea`
|
|
59
|
+
* - Drag-and-drop with a clear visual drag state
|
|
60
|
+
* - Per-file rows listing name, size, and a remove button
|
|
61
|
+
* - Single or multiple file support
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```tsx
|
|
65
|
+
* const [files, setFiles] = React.useState<FileInputFile[]>([]);
|
|
66
|
+
*
|
|
67
|
+
* <FileInput
|
|
68
|
+
* label="Resume"
|
|
69
|
+
* required
|
|
70
|
+
* showRequiredSign
|
|
71
|
+
* accept=".pdf,.doc,.docx"
|
|
72
|
+
* acceptLabel="PDF, DOC, DOCX"
|
|
73
|
+
* maxSize={50 * 1024 * 1024}
|
|
74
|
+
* tip="Max 50 MB per file."
|
|
75
|
+
* value={files}
|
|
76
|
+
* onChange={setFiles}
|
|
77
|
+
* onError={(msg) => toast.error(msg)}
|
|
78
|
+
* />
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
function FileInput({ className, dropzoneClassName, label, labelClassName, required = false, showRequiredSign = false, tip, error, accept, acceptLabel, maxSize, multiple = false, value = [], onChange, onError, disabled = false, }) {
|
|
82
|
+
const inputRef = React.useRef(null);
|
|
83
|
+
const [isDragging, setIsDragging] = React.useState(false);
|
|
84
|
+
function processFiles(rawFiles) {
|
|
85
|
+
if (!rawFiles || disabled)
|
|
86
|
+
return;
|
|
87
|
+
const incoming = [];
|
|
88
|
+
Array.from(rawFiles).forEach((file) => {
|
|
89
|
+
if (maxSize && file.size > maxSize) {
|
|
90
|
+
onError?.(`"${file.name}" exceeds the maximum size of ${formatBytes(maxSize)}.`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
incoming.push({ file, id: generateId() });
|
|
94
|
+
});
|
|
95
|
+
if (!incoming.length)
|
|
96
|
+
return;
|
|
97
|
+
const next = multiple ? [...value, ...incoming] : [incoming[0]];
|
|
98
|
+
onChange?.(next);
|
|
99
|
+
}
|
|
100
|
+
function removeFile(id) {
|
|
101
|
+
onChange?.(value.filter((f) => f.id !== id));
|
|
102
|
+
if (inputRef.current)
|
|
103
|
+
inputRef.current.value = "";
|
|
104
|
+
}
|
|
105
|
+
function onDragOver(e) {
|
|
106
|
+
e.preventDefault();
|
|
107
|
+
if (!disabled)
|
|
108
|
+
setIsDragging(true);
|
|
109
|
+
}
|
|
110
|
+
function onDragLeave(e) {
|
|
111
|
+
e.preventDefault();
|
|
112
|
+
setIsDragging(false);
|
|
113
|
+
}
|
|
114
|
+
function onDrop(e) {
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
setIsDragging(false);
|
|
117
|
+
processFiles(e.dataTransfer.files);
|
|
118
|
+
}
|
|
119
|
+
const hasFiles = value.length > 0;
|
|
120
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, utils_1.cn)("w-full space-y-1.5", className), children: [label && ((0, jsx_runtime_1.jsxs)("div", { className: (0, utils_1.cn)("flex items-center gap-1", labelClassName), children: [(0, jsx_runtime_1.jsx)("label", { className: "text-sm font-bold text-foreground", children: label }), required && showRequiredSign && ((0, jsx_runtime_1.jsx)(lucide_react_1.AsteriskIcon, { className: "h-3 w-3 text-destructive" }))] })), (0, jsx_runtime_1.jsxs)("div", { onDragOver: onDragOver, onDragLeave: onDragLeave, onDrop: onDrop, className: (0, utils_1.cn)("relative flex flex-col items-center justify-center gap-3 rounded-lg border-2 border-dashed px-6 py-8 text-center transition-colors", "border-input bg-transparent", !disabled && "hover:border-ring/50 hover:bg-accent/40", isDragging && "border-ring bg-accent/60", error && "border-destructive", disabled && "cursor-not-allowed opacity-50", dropzoneClassName), children: [(0, jsx_runtime_1.jsx)("input", { ref: inputRef, type: "file", accept: accept, multiple: multiple, disabled: disabled, className: "absolute inset-0 h-full w-full cursor-pointer opacity-0 disabled:cursor-not-allowed", onChange: (e) => processFiles(e.target.files) }), (0, jsx_runtime_1.jsx)(lucide_react_1.CloudUploadIcon, { className: (0, utils_1.cn)("h-8 w-8 transition-colors", isDragging ? "text-ring" : "text-muted-foreground"), strokeWidth: 1.5 }), (0, jsx_runtime_1.jsxs)("div", { className: "pointer-events-none select-none space-y-1", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm font-medium text-foreground", children: "Choose a file or drag & drop it here" }), (acceptLabel || maxSize) && ((0, jsx_runtime_1.jsx)("p", { className: "text-xs text-muted-foreground", children: [acceptLabel, maxSize ? `up to ${formatBytes(maxSize)}` : null]
|
|
121
|
+
.filter(Boolean)
|
|
122
|
+
.join(", ") }))] }), (0, jsx_runtime_1.jsx)("span", { className: "pointer-events-none select-none inline-flex items-center rounded-md border border-input bg-background px-3 py-1.5 text-sm font-medium shadow-xs", children: "Browse File" })] }), hasFiles && ((0, jsx_runtime_1.jsx)("ul", { className: "space-y-2", children: value.map(({ file, id }) => ((0, jsx_runtime_1.jsxs)("li", { className: "flex items-center gap-3 rounded-lg border border-input bg-background px-3 py-2.5 shadow-xs", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-md border border-input bg-muted", children: (0, jsx_runtime_1.jsx)(lucide_react_1.FileIcon, { className: "h-4 w-4 text-muted-foreground" }) }), (0, jsx_runtime_1.jsxs)("div", { className: "min-w-0 flex-1", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate text-sm font-medium text-foreground", children: file.name }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-muted-foreground", children: formatBytes(file.size) })] }), !disabled && ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => removeFile(id), className: "shrink-0 rounded-full p-1 text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", "aria-label": `Remove ${file.name}`, children: (0, jsx_runtime_1.jsx)(lucide_react_1.XIcon, { className: "h-4 w-4" }) }))] }, id))) })), tip && !error && ((0, jsx_runtime_1.jsx)("p", { className: "text-xs text-muted-foreground tip-message", children: tip })), error && ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-destructive error-message", children: ["*", error] }))] }));
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=file-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-input.js","sourceRoot":"","sources":["../../src/components/file-input.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Control, FieldValues, Path } from "react-hook-form";
|
|
2
|
+
import { FileInputProps } from "./file-input";
|
|
3
|
+
/**
|
|
4
|
+
* Props for the `FormFileInput` component with type-safe form integration.
|
|
5
|
+
*
|
|
6
|
+
* Omits the props that are managed internally by React Hook Form
|
|
7
|
+
* (`value`, `onChange`, `onError`, `error`) and adds the RHF-specific ones.
|
|
8
|
+
*/
|
|
9
|
+
export type FormFileInputProps<TFieldValues extends FieldValues> = Omit<FileInputProps, "value" | "onChange" | "onError" | "error"> & {
|
|
10
|
+
/** React Hook Form control instance. */
|
|
11
|
+
control: Control<TFieldValues>;
|
|
12
|
+
/** The name of the field (type-safe, must match the form schema). */
|
|
13
|
+
name: Path<TFieldValues>;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* A file-upload field that integrates `FileInput` with React Hook Form.
|
|
17
|
+
*
|
|
18
|
+
* - Wires `value`, `onChange`, and `error` automatically via `Controller`
|
|
19
|
+
* - File rejection errors (e.g. size exceeded) are forwarded to RHF via `setError`
|
|
20
|
+
* - Supports the `required` rule out of the box
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* type FormData = {
|
|
25
|
+
* resume: FileInputFile[];
|
|
26
|
+
* };
|
|
27
|
+
*
|
|
28
|
+
* const { control } = useForm<FormData>({ defaultValues: { resume: [] } });
|
|
29
|
+
*
|
|
30
|
+
* <FormFileInput
|
|
31
|
+
* control={control}
|
|
32
|
+
* name="resume"
|
|
33
|
+
* label="Resume"
|
|
34
|
+
* required
|
|
35
|
+
* showRequiredSign
|
|
36
|
+
* accept=".pdf,.doc,.docx"
|
|
37
|
+
* acceptLabel="PDF, DOC, DOCX"
|
|
38
|
+
* maxSize={50 * 1024 * 1024}
|
|
39
|
+
* tip="Max 50 MB per file."
|
|
40
|
+
* />
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export default function FormFileInput<TFieldValues extends FieldValues>({ control, name, ...props }: FormFileInputProps<TFieldValues>): import("react/jsx-runtime").JSX.Element;
|
|
44
|
+
//# sourceMappingURL=form-file-input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-file-input.d.ts","sourceRoot":"","sources":["../../src/components/form-file-input.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAc,WAAW,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAkB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAGzD;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,CAAC,YAAY,SAAS,WAAW,IAAI,IAAI,CACrE,cAAc,EACd,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAC3C,GAAG;IACF,wCAAwC;IACxC,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IAC/B,qEAAqE;IACrE,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;CAC1B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,YAAY,SAAS,WAAW,EAAE,EACtE,OAAO,EACP,IAAI,EACJ,GAAG,KAAK,EACT,EAAE,kBAAkB,CAAC,YAAY,CAAC,2CAwBlC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.default = FormFileInput;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const react_hook_form_1 = require("react-hook-form");
|
|
9
|
+
const file_input_1 = __importDefault(require("./file-input"));
|
|
10
|
+
const form_helpers_1 = require("../utils/helpers/form.helpers");
|
|
11
|
+
/**
|
|
12
|
+
* A file-upload field that integrates `FileInput` with React Hook Form.
|
|
13
|
+
*
|
|
14
|
+
* - Wires `value`, `onChange`, and `error` automatically via `Controller`
|
|
15
|
+
* - File rejection errors (e.g. size exceeded) are forwarded to RHF via `setError`
|
|
16
|
+
* - Supports the `required` rule out of the box
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* type FormData = {
|
|
21
|
+
* resume: FileInputFile[];
|
|
22
|
+
* };
|
|
23
|
+
*
|
|
24
|
+
* const { control } = useForm<FormData>({ defaultValues: { resume: [] } });
|
|
25
|
+
*
|
|
26
|
+
* <FormFileInput
|
|
27
|
+
* control={control}
|
|
28
|
+
* name="resume"
|
|
29
|
+
* label="Resume"
|
|
30
|
+
* required
|
|
31
|
+
* showRequiredSign
|
|
32
|
+
* accept=".pdf,.doc,.docx"
|
|
33
|
+
* acceptLabel="PDF, DOC, DOCX"
|
|
34
|
+
* maxSize={50 * 1024 * 1024}
|
|
35
|
+
* tip="Max 50 MB per file."
|
|
36
|
+
* />
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
function FormFileInput({ control, name, ...props }) {
|
|
40
|
+
return ((0, jsx_runtime_1.jsx)(react_hook_form_1.Controller, { control: control, name: name, rules: {
|
|
41
|
+
required: props.required ? "Required" : false,
|
|
42
|
+
}, render: ({ field: { onChange, value }, formState: { errors } }) => ((0, jsx_runtime_1.jsx)(file_input_1.default, { ...props, value: value ?? [], onChange: onChange, onError: (errorMessage) => {
|
|
43
|
+
control.setError(name, {
|
|
44
|
+
type: "manual",
|
|
45
|
+
message: errorMessage,
|
|
46
|
+
});
|
|
47
|
+
}, error: (0, form_helpers_1.getNestedErrorMessage)(errors, name) })) }));
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=form-file-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-file-input.js","sourceRoot":"","sources":["../../src/components/form-file-input.tsx"],"names":[],"mappings":";;;;;AAgDA,gCA4BC;;AA5ED,qDAAyE;AACzE,8DAAyD;AACzD,gEAAsE;AAkBtE;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,SAAwB,aAAa,CAAmC,EACtE,OAAO,EACP,IAAI,EACJ,GAAG,KAAK,EACyB;IACjC,OAAO,CACL,uBAAC,4BAAU,IACT,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,IAAI,EACV,KAAK,EAAE;YACL,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK;SAC9C,EACD,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CACjE,uBAAC,oBAAS,OACJ,KAAK,EACT,KAAK,EAAE,KAAK,IAAI,EAAE,EAClB,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,CAAC,YAAoB,EAAE,EAAE;gBAChC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE;oBACrB,IAAI,EAAE,QAAQ;oBACd,OAAO,EAAE,YAAY;iBACtB,CAAC,CAAC;YACL,CAAC,EACD,KAAK,EAAE,IAAA,oCAAqB,EAAC,MAAM,EAAE,IAAI,CAAC,GAC1C,CACH,GACD,CACH,CAAC;AACJ,CAAC"}
|
package/dist/exports/index.d.ts
CHANGED
|
@@ -7,9 +7,13 @@ import Textarea from "../components/textarea";
|
|
|
7
7
|
import FormTextarea from "../components/form-textarea";
|
|
8
8
|
import FormImageInput from "../components/form-image-input";
|
|
9
9
|
import ImageInput from "../components/image-input";
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
import FileInput, { FileInputProps, FileInputFile } from "../components/file-input";
|
|
11
|
+
import FormFileInput, { FormFileInputProps } from "../components/form-file-input";
|
|
12
|
+
export { Button, FormInput, Input, Select, FormSelect, Textarea, FormTextarea, ImageInput, FormImageInput, FileInput, FormFileInput, };
|
|
13
|
+
export type { SelectOption, SelectProps, FormSelectProps, FormFileInputProps, FileInputProps, FileInputFile, };
|
|
12
14
|
export * from "../components/input";
|
|
13
15
|
export * from "../components/form-input";
|
|
14
16
|
export * from "../components/button";
|
|
17
|
+
export * from "../components/file-input";
|
|
18
|
+
export * from "../components/form-file-input";
|
|
15
19
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/exports/index.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,sBAAsB,CAAC;AAC1C,OAAO,SAAS,MAAM,0BAA0B,CAAC;AACjD,OAAO,KAAK,MAAM,qBAAqB,CAAC;AACxC,OAAO,MAAM,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,UAAU,EAAE,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,QAAQ,MAAM,wBAAwB,CAAC;AAC9C,OAAO,YAAY,MAAM,6BAA6B,CAAC;AACvD,OAAO,cAAc,MAAM,gCAAgC,CAAC;AAC5D,OAAO,UAAU,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/exports/index.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,sBAAsB,CAAC;AAC1C,OAAO,SAAS,MAAM,0BAA0B,CAAC;AACjD,OAAO,KAAK,MAAM,qBAAqB,CAAC;AACxC,OAAO,MAAM,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,UAAU,EAAE,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,QAAQ,MAAM,wBAAwB,CAAC;AAC9C,OAAO,YAAY,MAAM,6BAA6B,CAAC;AACvD,OAAO,cAAc,MAAM,gCAAgC,CAAC;AAC5D,OAAO,UAAU,MAAM,2BAA2B,CAAC;AACnD,OAAO,SAAS,EAAE,EAChB,cAAc,EACd,aAAa,EACd,MAAM,0BAA0B,CAAC;AAClC,OAAO,aAAa,EAAE,EACpB,kBAAkB,EACnB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EACL,MAAM,EACN,SAAS,EACT,KAAK,EACL,MAAM,EACN,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,cAAc,EACd,SAAS,EACT,aAAa,GACd,CAAC;AACF,YAAY,EACV,YAAY,EACZ,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,aAAa,GACd,CAAC;AACF,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,+BAA+B,CAAC"}
|
package/dist/exports/index.js
CHANGED
|
@@ -17,7 +17,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
17
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.FormImageInput = exports.ImageInput = exports.FormTextarea = exports.Textarea = exports.FormSelect = exports.Select = exports.Input = exports.FormInput = exports.Button = void 0;
|
|
20
|
+
exports.FormFileInput = exports.FileInput = exports.FormImageInput = exports.ImageInput = exports.FormTextarea = exports.Textarea = exports.FormSelect = exports.Select = exports.Input = exports.FormInput = exports.Button = void 0;
|
|
21
21
|
const button_1 = __importDefault(require("../components/button"));
|
|
22
22
|
exports.Button = button_1.default;
|
|
23
23
|
const form_input_1 = __importDefault(require("../components/form-input"));
|
|
@@ -36,7 +36,13 @@ const form_image_input_1 = __importDefault(require("../components/form-image-inp
|
|
|
36
36
|
exports.FormImageInput = form_image_input_1.default;
|
|
37
37
|
const image_input_1 = __importDefault(require("../components/image-input"));
|
|
38
38
|
exports.ImageInput = image_input_1.default;
|
|
39
|
+
const file_input_1 = __importDefault(require("../components/file-input"));
|
|
40
|
+
exports.FileInput = file_input_1.default;
|
|
41
|
+
const form_file_input_1 = __importDefault(require("../components/form-file-input"));
|
|
42
|
+
exports.FormFileInput = form_file_input_1.default;
|
|
39
43
|
__exportStar(require("../components/input"), exports);
|
|
40
44
|
__exportStar(require("../components/form-input"), exports);
|
|
41
45
|
__exportStar(require("../components/button"), exports);
|
|
46
|
+
__exportStar(require("../components/file-input"), exports);
|
|
47
|
+
__exportStar(require("../components/form-file-input"), exports);
|
|
42
48
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/exports/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,kEAA0C;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/exports/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,kEAA0C;AAkBxC,iBAlBK,gBAAM,CAkBL;AAjBR,0EAAiD;AAkB/C,oBAlBK,oBAAS,CAkBL;AAjBX,gEAAwC;AAkBtC,gBAlBK,eAAK,CAkBL;AAjBP,kEAAyE;AAkBvE,iBAlBK,gBAAM,CAkBL;AAjBR,4EAAwE;AAkBtE,qBAlBK,qBAAU,CAkBL;AAjBZ,sEAA8C;AAkB5C,mBAlBK,kBAAQ,CAkBL;AAjBV,gFAAuD;AAkBrD,uBAlBK,uBAAY,CAkBL;AAjBd,sFAA4D;AAmB1D,yBAnBK,0BAAc,CAmBL;AAlBhB,4EAAmD;AAiBjD,qBAjBK,qBAAU,CAiBL;AAhBZ,0EAGkC;AAehC,oBAlBK,oBAAS,CAkBL;AAdX,oFAEuC;AAarC,wBAfK,yBAAa,CAeL;AAUf,sDAAoC;AACpC,2DAAyC;AACzC,uDAAqC;AACrC,2DAAyC;AACzC,gEAA8C"}
|