ikoncomponents 1.6.9 → 1.7.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/README.md +36 -36
- package/dist/ikoncomponents/fileUpload/index.d.ts +11 -12
- package/dist/ikoncomponents/fileUpload/index.js +117 -59
- package/dist/ikoncomponents/fileUploadApi/index.d.ts +31 -0
- package/dist/ikoncomponents/fileUploadApi/index.js +159 -0
- package/dist/ikoncomponents/main-layout/nav-main.js +3 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.js +8 -2
- package/dist/styles.css +28 -0
- package/dist/utils/api/file-upload copy/index.d.ts +13 -0
- package/dist/utils/api/file-upload copy/index.js +120 -0
- package/package.json +2 -1
- package/dist/ikoncomponents/assistant-ui/Assistant.d.ts +0 -28
- package/dist/ikoncomponents/assistant-ui/Assistant.js +0 -306
- package/dist/ikoncomponents/assistant-ui/agent-dropdown.d.ts +0 -24
- package/dist/ikoncomponents/assistant-ui/agent-dropdown.js +0 -16
- package/dist/ikoncomponents/assistant-ui/agentTextChatTransport.d.ts +0 -30
- package/dist/ikoncomponents/assistant-ui/agentTextChatTransport.js +0 -208
- package/dist/ikoncomponents/assistant-ui/attachment.d.ts +0 -4
- package/dist/ikoncomponents/assistant-ui/attachment.js +0 -93
- package/dist/ikoncomponents/assistant-ui/markdown-text.d.ts +0 -2
- package/dist/ikoncomponents/assistant-ui/markdown-text.js +0 -126
- package/dist/ikoncomponents/assistant-ui/thread.d.ts +0 -10
- package/dist/ikoncomponents/assistant-ui/thread.js +0 -115
- package/dist/ikoncomponents/assistant-ui/tool-fallback.d.ts +0 -2
- package/dist/ikoncomponents/assistant-ui/tool-fallback.js +0 -18
- package/dist/ikoncomponents/assistant-ui/tooltip-icon-button.d.ts +0 -7
- package/dist/ikoncomponents/assistant-ui/tooltip-icon-button.js +0 -23
- package/dist/ikoncomponents/table/component/DataTable.d.ts +0 -14
- package/dist/ikoncomponents/table/component/DataTable.js +0 -10
- package/dist/ikoncomponents/table/component/DataTablePageSize.d.ts +0 -1
- package/dist/ikoncomponents/table/component/DataTablePageSize.js +0 -16
- package/dist/ikoncomponents/table/component/DataTablePagination.d.ts +0 -6
- package/dist/ikoncomponents/table/component/DataTablePagination.js +0 -14
- package/dist/ikoncomponents/table/component/DataTableSearch.d.ts +0 -1
- package/dist/ikoncomponents/table/component/DataTableSearch.js +0 -27
- package/dist/utils/userType.d.ts +0 -13
- package/dist/utils/userType.js +0 -1
package/README.md
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
|
2
|
-
|
|
3
|
-
## Getting Started
|
|
4
|
-
|
|
5
|
-
First, run the development server:
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm run dev
|
|
9
|
-
# or
|
|
10
|
-
yarn dev
|
|
11
|
-
# or
|
|
12
|
-
pnpm dev
|
|
13
|
-
# or
|
|
14
|
-
bun dev
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
|
18
|
-
|
|
19
|
-
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
|
20
|
-
|
|
21
|
-
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
|
22
|
-
|
|
23
|
-
## Learn More
|
|
24
|
-
|
|
25
|
-
To learn more about Next.js, take a look at the following resources:
|
|
26
|
-
|
|
27
|
-
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
|
28
|
-
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
|
29
|
-
|
|
30
|
-
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
|
31
|
-
|
|
32
|
-
## Deploy on Vercel
|
|
33
|
-
|
|
34
|
-
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
|
35
|
-
|
|
36
|
-
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
|
1
|
+
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
|
2
|
+
|
|
3
|
+
## Getting Started
|
|
4
|
+
|
|
5
|
+
First, run the development server:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm run dev
|
|
9
|
+
# or
|
|
10
|
+
yarn dev
|
|
11
|
+
# or
|
|
12
|
+
pnpm dev
|
|
13
|
+
# or
|
|
14
|
+
bun dev
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
|
18
|
+
|
|
19
|
+
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
|
20
|
+
|
|
21
|
+
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
|
22
|
+
|
|
23
|
+
## Learn More
|
|
24
|
+
|
|
25
|
+
To learn more about Next.js, take a look at the following resources:
|
|
26
|
+
|
|
27
|
+
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
|
28
|
+
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
|
29
|
+
|
|
30
|
+
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
|
31
|
+
|
|
32
|
+
## Deploy on Vercel
|
|
33
|
+
|
|
34
|
+
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
|
35
|
+
|
|
36
|
+
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
export
|
|
1
|
+
export declare const convertFileToObject: (file: File) => Promise<any>;
|
|
2
|
+
interface FileUploaderProps {
|
|
2
3
|
label?: string;
|
|
3
4
|
isDrag?: boolean;
|
|
4
|
-
|
|
5
|
+
fileInput?: boolean;
|
|
6
|
+
isMultiple?: boolean;
|
|
7
|
+
tooltipContent?: string;
|
|
8
|
+
fileNamePlaceholder?: string;
|
|
9
|
+
showPreview?: boolean;
|
|
10
|
+
onFileSelect: (files: any[]) => Promise<any> | void;
|
|
5
11
|
}
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
size: number;
|
|
10
|
-
type: string;
|
|
11
|
-
lastModified: number;
|
|
12
|
-
base64: string;
|
|
13
|
-
}>;
|
|
14
|
-
export declare function FileUploader({ label, isDrag, onFileSelect, }: FileUploaderProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
-
export declare function getImageFromObject(obj: any): string;
|
|
12
|
+
export default function FileUploader({ label, isDrag, fileInput, isMultiple, tooltipContent, fileNamePlaceholder, showPreview, onFileSelect, }: FileUploaderProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export declare function getFileUrlFromObject(obj: any): string;
|
|
14
|
+
export {};
|
|
@@ -1,69 +1,127 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState } from "react";
|
|
4
|
-
import { UploadCloud, FileUp } from "lucide-react";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
lastModified: file.lastModified,
|
|
15
|
-
base64: base64,
|
|
16
|
-
};
|
|
17
|
-
};
|
|
18
|
-
export function FileUploader({ label = "Upload File", isDrag = false, onFileSelect, }) {
|
|
19
|
-
const [isDragging, setIsDragging] = useState(false);
|
|
20
|
-
const handleFile = async (file) => {
|
|
21
|
-
if (!file)
|
|
22
|
-
return;
|
|
23
|
-
const fileObj = await convertFileToObject(file); // convert to object
|
|
24
|
-
await onFileSelect(fileObj); // pass object to parent
|
|
25
|
-
};
|
|
26
|
-
// ---- DRAG HANDLERS ----
|
|
27
|
-
const handleDrop = (e) => {
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useRef } from "react";
|
|
4
|
+
import { UploadCloud, FileUp, Upload, X } from "lucide-react";
|
|
5
|
+
import { v4 as uuidv4 } from "uuid";
|
|
6
|
+
import { Input } from "@/shadcn/input";
|
|
7
|
+
import { IconButtonWithTooltip } from "../buttons";
|
|
8
|
+
/* ----------------------------------------------------
|
|
9
|
+
SAFE Base64 Converter
|
|
10
|
+
---------------------------------------------------- */
|
|
11
|
+
export const convertFileToObject = (file) => new Promise((resolve, reject) => {
|
|
12
|
+
const reader = new FileReader();
|
|
13
|
+
reader.onload = () => {
|
|
28
14
|
var _a;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
15
|
+
const result = reader.result;
|
|
16
|
+
const base64 = result.split(",")[1];
|
|
17
|
+
const nameParts = file.name.split(".");
|
|
18
|
+
const ext = nameParts.length >= 2 ? (_a = nameParts.pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase() : "";
|
|
19
|
+
const fileType = file.type || (ext ? `${ext}` : "application/octet-stream");
|
|
20
|
+
resolve({
|
|
21
|
+
resourceId: uuidv4(),
|
|
22
|
+
message: "File processed successfully",
|
|
23
|
+
fileName: file.name,
|
|
24
|
+
fileSize: file.size,
|
|
25
|
+
fileType, // ✅ FIXED
|
|
26
|
+
base64,
|
|
27
|
+
});
|
|
36
28
|
};
|
|
37
|
-
|
|
38
|
-
|
|
29
|
+
reader.onerror = reject;
|
|
30
|
+
reader.readAsDataURL(file);
|
|
31
|
+
});
|
|
32
|
+
/* ----------------------------------------------------
|
|
33
|
+
FileInput UI (Text + Button Input)
|
|
34
|
+
---------------------------------------------------- */
|
|
35
|
+
function FileInputUI({ tooltipContent, fileNames, fileNamePlaceholder, onFileNamesChange, onFileSelect, isMultiple, }) {
|
|
36
|
+
const inputRef = useRef(null);
|
|
37
|
+
const handleChange = async (e) => {
|
|
38
|
+
const files = e.target.files;
|
|
39
|
+
if (!(files === null || files === void 0 ? void 0 : files.length))
|
|
39
40
|
return;
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
const fileObjects = await Promise.all(Array.from(files).map((f) => convertFileToObject(f)));
|
|
42
|
+
const names = fileObjects.map((f) => f.fileName).join(", ");
|
|
43
|
+
onFileNamesChange === null || onFileNamesChange === void 0 ? void 0 : onFileNamesChange(names);
|
|
44
|
+
onFileSelect === null || onFileSelect === void 0 ? void 0 : onFileSelect(fileObjects);
|
|
42
45
|
};
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
const filesCount = fileNames ? fileNames.split(", ").length : 0;
|
|
47
|
+
return (_jsxs("div", { className: "flex w-full", children: [_jsx(Input, { ref: inputRef, type: "file", multiple: isMultiple, className: "hidden", onChange: handleChange }), _jsx(Input, { className: "rounded-e-none", placeholder: fileNamePlaceholder, value: filesCount === 0
|
|
48
|
+
? ""
|
|
49
|
+
: filesCount === 1
|
|
50
|
+
? fileNames
|
|
51
|
+
: `${filesCount} files selected`, readOnly: true }), _jsx(IconButtonWithTooltip, { tooltipContent: tooltipContent || "Browse File", onClick: () => { var _a; return (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, className: "border-s-0 rounded-s-none", children: _jsx(Upload, {}) })] }));
|
|
52
|
+
}
|
|
53
|
+
/* ----------------------------------------------------
|
|
54
|
+
Image Preview Component
|
|
55
|
+
---------------------------------------------------- */
|
|
56
|
+
function ImagePreview({ previews, onRemove, }) {
|
|
57
|
+
if (!previews.length)
|
|
58
|
+
return null;
|
|
59
|
+
return (_jsx("div", { className: "flex flex-wrap gap-2 mt-2 justify-center", children: previews.map((preview, i) => (_jsxs("div", { className: "relative group", children: [_jsx("img", { src: preview.url, alt: preview.name, className: "h-16 w-16 object-cover rounded-md border p-1" }), onRemove && (_jsx("button", { onClick: (e) => {
|
|
60
|
+
e.stopPropagation();
|
|
61
|
+
onRemove(i);
|
|
62
|
+
}, className: "absolute -top-1.5 -right-1.5 bg-red-500 text-white rounded-full w-4 h-4 \r\n flex items-center justify-center opacity-0 group-hover:opacity-100 \r\n transition-opacity shadow-sm", children: _jsx(X, { className: "w-2.5 h-2.5" }) })), _jsx("p", { className: "text-xs text-gray-500 text-center mt-0.5 max-w-[64px] truncate", children: preview.name })] }, i))) }));
|
|
63
|
+
}
|
|
64
|
+
export default function FileUploader({ label = "Upload File", isDrag = false, fileInput = false, isMultiple = false, tooltipContent, fileNamePlaceholder = "Choose files...", showPreview = true, onFileSelect, }) {
|
|
65
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
66
|
+
const [fileNames, setFileNames] = useState("");
|
|
67
|
+
const [previews, setPreviews] = useState([]);
|
|
68
|
+
const filesCount = fileNames ? fileNames.split(", ").length : 0;
|
|
69
|
+
/* ------------ GENERATE PREVIEWS ------------ */
|
|
70
|
+
const generatePreviews = (files) => {
|
|
71
|
+
previews.forEach((p) => URL.revokeObjectURL(p.url));
|
|
72
|
+
const newPreviews = files
|
|
73
|
+
.filter((f) => f.type.startsWith("image/"))
|
|
74
|
+
.map((f) => ({ url: URL.createObjectURL(f), name: f.name }));
|
|
75
|
+
setPreviews(newPreviews);
|
|
47
76
|
};
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
77
|
+
/* ------------ REMOVE A SINGLE PREVIEW ------------ */
|
|
78
|
+
const handleRemovePreview = (index) => {
|
|
79
|
+
URL.revokeObjectURL(previews[index].url);
|
|
80
|
+
setPreviews((prev) => prev.filter((_, i) => i !== index));
|
|
81
|
+
};
|
|
82
|
+
/* ------------ COMMON FILE HANDLER ------------ */
|
|
83
|
+
const handleFiles = async (files) => {
|
|
84
|
+
const fileArray = Array.from(files);
|
|
85
|
+
const fileList = isMultiple ? fileArray : [fileArray[0]];
|
|
86
|
+
const fileObjects = await Promise.all(fileList.map((file) => convertFileToObject(file)));
|
|
87
|
+
setFileNames(fileObjects.map((f) => f.fileName).join(", "));
|
|
88
|
+
if (showPreview)
|
|
89
|
+
generatePreviews(fileList);
|
|
90
|
+
onFileSelect(fileObjects);
|
|
53
91
|
};
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
92
|
+
/* ------------ DRAG HANDLERS ------------ */
|
|
93
|
+
const dragHandlers = isDrag
|
|
94
|
+
? {
|
|
95
|
+
onDragOver: (e) => {
|
|
96
|
+
e.preventDefault();
|
|
97
|
+
setIsDragging(true);
|
|
98
|
+
},
|
|
99
|
+
onDragLeave: () => setIsDragging(false),
|
|
100
|
+
onDrop: (e) => {
|
|
101
|
+
e.preventDefault();
|
|
102
|
+
setIsDragging(false);
|
|
103
|
+
if (e.dataTransfer.files.length) {
|
|
104
|
+
handleFiles(e.dataTransfer.files);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
: {};
|
|
109
|
+
return (_jsxs("div", Object.assign({}, dragHandlers, { className: `flex flex-col ${fileInput ? "items-start text-left" : "items-center text-center"} justify-center gap-2 cursor-pointer px-4 pb-4 rounded-lg transition ${fileInput
|
|
110
|
+
? ""
|
|
111
|
+
: `border-2 border-dashed ${isDragging ? "border-blue-600 bg-blue-50" : "border-gray-300"}`}`, children: [_jsx("label", { className: "text-md mt-4 font-bold", children: label }), fileInput && (_jsxs(_Fragment, { children: [_jsx(FileInputUI, { fileNames: fileNames, fileNamePlaceholder: fileNamePlaceholder, tooltipContent: tooltipContent, onFileNamesChange: setFileNames, onFileSelect: onFileSelect, isMultiple: isMultiple }), isDrag && (_jsx("p", { className: "text-sm text-gray-500", children: isDragging ? "Drop files here..." : "or drag & drop files here" })), filesCount > 1 && (_jsxs("p", { className: "text-sm font-medium", children: [filesCount, " files selected"] }))] })), !fileInput && (_jsxs(_Fragment, { children: [_jsx("input", { type: "file", id: "fileInput", className: "hidden", multiple: isMultiple, onChange: (e) => e.target.files && handleFiles(e.target.files) }), isDrag ? (_jsxs("div", { className: "mt-3 p-6 w-full text-center cursor-pointer", onClick: () => { var _a; return (_a = document.getElementById("fileInput")) === null || _a === void 0 ? void 0 : _a.click(); }, children: [filesCount === 0 && (_jsxs("div", { className: "flex flex-col items-center gap-3", children: [_jsx(UploadCloud, { className: "w-10 h-10 text-blue-600" }), _jsxs("p", { className: "text-gray-600", children: ["Drag & drop ", isMultiple ? "files" : "a file", " or", " ", _jsx("span", { className: "text-blue-600 underline", children: "browse" })] })] })), filesCount > 0 && (_jsx("p", { className: "mt-3 text-sm font-medium", children: filesCount === 1
|
|
112
|
+
? fileNames
|
|
113
|
+
: `${filesCount} files selected` }))] })) : (_jsxs("div", { className: "border rounded-lg p-4 flex flex-col items-center gap-2 cursor-pointer text-center", onClick: () => { var _a; return (_a = document.getElementById("fileInput")) === null || _a === void 0 ? void 0 : _a.click(); }, children: [filesCount === 0 && (_jsxs(_Fragment, { children: [_jsx(FileUp, { className: "w-8 h-8 text-blue-600" }), _jsx("span", { className: "text-blue-600 underline", children: isMultiple ? "Browse Files" : "Browse File" })] })), filesCount > 0 && (_jsx("p", { className: "text-sm font-medium", children: filesCount === 1
|
|
114
|
+
? fileNames
|
|
115
|
+
: `${filesCount} files selected` }))] })), showPreview && (_jsx(ImagePreview, { previews: previews, onRemove: handleRemovePreview }))] }))] })));
|
|
59
116
|
}
|
|
60
|
-
|
|
61
|
-
|
|
117
|
+
/* ----------------------------------------------------
|
|
118
|
+
Convert Base64 back to Blob URL
|
|
119
|
+
---------------------------------------------------- */
|
|
120
|
+
export function getFileUrlFromObject(obj) {
|
|
62
121
|
const byteCharacters = atob(obj.base64);
|
|
63
|
-
const byteNumbers =
|
|
64
|
-
|
|
65
|
-
.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return URL.createObjectURL(blob); // usable in <img src="..." />
|
|
122
|
+
const byteNumbers = Array.from(byteCharacters).map((c) => c.charCodeAt(0));
|
|
123
|
+
const blob = new Blob([new Uint8Array(byteNumbers)], {
|
|
124
|
+
type: obj.resourceType,
|
|
125
|
+
});
|
|
126
|
+
return URL.createObjectURL(blob);
|
|
69
127
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export declare const convertFileToObject: (file: File) => Promise<any>;
|
|
3
|
+
export interface FileUploader2Ref {
|
|
4
|
+
/** Triggers the upload for already-selected files. Resolves when done. */
|
|
5
|
+
upload: () => Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
export interface FileUploader2Props {
|
|
8
|
+
label?: string;
|
|
9
|
+
isDrag?: boolean;
|
|
10
|
+
onUploadComplete?: (response: any | any[]) => void;
|
|
11
|
+
imageUrl?: string;
|
|
12
|
+
fileInput?: boolean;
|
|
13
|
+
isMultiple?: boolean;
|
|
14
|
+
tooltipContent?: string;
|
|
15
|
+
fileNamePlaceholder?: string;
|
|
16
|
+
/** Called with the raw File objects before upload */
|
|
17
|
+
onFileSelect?: (files: File[]) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Custom upload endpoint. When provided, files are POSTed as multipart/form-data
|
|
20
|
+
* and the full JSON response is passed to onUploadComplete.
|
|
21
|
+
* Falls back to uploadFilePublic() when omitted.
|
|
22
|
+
*/
|
|
23
|
+
apiUrl?: string;
|
|
24
|
+
/**
|
|
25
|
+
* When true, the upload does NOT start automatically on file selection.
|
|
26
|
+
* Instead, call ref.upload() to trigger it manually.
|
|
27
|
+
*/
|
|
28
|
+
manual?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare const FileUploaderApi: React.ForwardRefExoticComponent<FileUploader2Props & React.RefAttributes<FileUploader2Ref>>;
|
|
31
|
+
export declare function getFileUrlFromObject(obj: any): string;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useRef, useImperativeHandle, forwardRef } from "react";
|
|
4
|
+
import { UploadCloud, FileUp, Upload } from "lucide-react";
|
|
5
|
+
import { v4 as uuidv4 } from "uuid";
|
|
6
|
+
import { Input } from "@/shadcn/input";
|
|
7
|
+
import { IconButtonWithTooltip } from "../buttons";
|
|
8
|
+
import { uploadFilePublic } from "../../utils/api/file-upload copy";
|
|
9
|
+
/* ----------------------------------------------------
|
|
10
|
+
SAFE Base64 Converter (kept for local preview use)
|
|
11
|
+
---------------------------------------------------- */
|
|
12
|
+
export const convertFileToObject = (file) => new Promise((resolve, reject) => {
|
|
13
|
+
const reader = new FileReader();
|
|
14
|
+
reader.onload = () => {
|
|
15
|
+
const result = reader.result;
|
|
16
|
+
const base64 = result.split(",")[1];
|
|
17
|
+
resolve({
|
|
18
|
+
resourceId: uuidv4(),
|
|
19
|
+
message: "File processed successfully",
|
|
20
|
+
resourceName: file.name,
|
|
21
|
+
resourceSize: file.size,
|
|
22
|
+
resourceType: file.type,
|
|
23
|
+
base64,
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
reader.onerror = reject;
|
|
27
|
+
reader.readAsDataURL(file);
|
|
28
|
+
});
|
|
29
|
+
/* ----------------------------------------------------
|
|
30
|
+
FileInput UI (Text + Button Input)
|
|
31
|
+
---------------------------------------------------- */
|
|
32
|
+
function FileInputUI({ tooltipContent, fileNames, fileNamePlaceholder, onFileNamesChange, onFileSelect, isMultiple, inputId, }) {
|
|
33
|
+
const inputRef = useRef(null);
|
|
34
|
+
const handleChange = (e) => {
|
|
35
|
+
const files = e.target.files;
|
|
36
|
+
if (!(files === null || files === void 0 ? void 0 : files.length))
|
|
37
|
+
return;
|
|
38
|
+
const fileList = isMultiple ? Array.from(files) : [files[0]];
|
|
39
|
+
const names = fileList.map((f) => f.name).join(", ");
|
|
40
|
+
onFileNamesChange === null || onFileNamesChange === void 0 ? void 0 : onFileNamesChange(names);
|
|
41
|
+
onFileSelect === null || onFileSelect === void 0 ? void 0 : onFileSelect(fileList);
|
|
42
|
+
};
|
|
43
|
+
const filesCount = fileNames ? fileNames.split(", ").length : 0;
|
|
44
|
+
return (_jsxs("div", { className: "flex w-full", children: [_jsx(Input, { ref: inputRef, type: "file", multiple: isMultiple, className: "hidden", onChange: handleChange, id: inputId }), _jsx(Input, { className: "rounded-e-none", placeholder: fileNamePlaceholder, value: filesCount === 0
|
|
45
|
+
? ""
|
|
46
|
+
: filesCount === 1
|
|
47
|
+
? fileNames
|
|
48
|
+
: `${filesCount} files selected`, readOnly: true }), _jsx(IconButtonWithTooltip, { tooltipContent: tooltipContent || "Browse File", onClick: () => { var _a; return (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, className: "border-s-0 rounded-s-none", children: _jsx(Upload, {}) })] }));
|
|
49
|
+
}
|
|
50
|
+
/* ----------------------------------------------------
|
|
51
|
+
Main FileUploader2 Component
|
|
52
|
+
---------------------------------------------------- */
|
|
53
|
+
export const FileUploaderApi = forwardRef(function FileUploaderApi({ label = "Upload File", isDrag = false, onUploadComplete, imageUrl, fileInput = false, isMultiple = false, tooltipContent, fileNamePlaceholder = "Choose files...", onFileSelect, apiUrl, manual = false, }, ref) {
|
|
54
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
55
|
+
const [previews, setPreviews] = useState([]);
|
|
56
|
+
const [fileNames, setFileNames] = useState("");
|
|
57
|
+
const [loading, setLoading] = useState(false);
|
|
58
|
+
// Holds files pending upload when manual=true
|
|
59
|
+
const pendingFilesRef = useRef([]);
|
|
60
|
+
const inputId = `fileInput-${label.replace(/\s+/g, "-").toLowerCase()}`;
|
|
61
|
+
const filesCount = fileNames ? fileNames.split(", ").length : 0;
|
|
62
|
+
/* ------------ UPLOAD EXECUTOR ------------ */
|
|
63
|
+
const executeUpload = async (fileList) => {
|
|
64
|
+
try {
|
|
65
|
+
setLoading(true);
|
|
66
|
+
const responses = await Promise.all(fileList.map((f) => {
|
|
67
|
+
if (apiUrl) {
|
|
68
|
+
const formData = new FormData();
|
|
69
|
+
formData.append("file", f);
|
|
70
|
+
return fetch(apiUrl, { method: "POST", body: formData })
|
|
71
|
+
.then((res) => {
|
|
72
|
+
if (!res.ok)
|
|
73
|
+
throw new Error(`Upload failed: ${res.statusText}`);
|
|
74
|
+
return res.json();
|
|
75
|
+
})
|
|
76
|
+
.then((data) => data);
|
|
77
|
+
}
|
|
78
|
+
return uploadFilePublic(f);
|
|
79
|
+
}));
|
|
80
|
+
onUploadComplete === null || onUploadComplete === void 0 ? void 0 : onUploadComplete(isMultiple ? responses : responses[0]);
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
console.error("Upload failed:", err);
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
setLoading(false);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
/* ------------ EXPOSE upload() TO PARENT ------------ */
|
|
90
|
+
useImperativeHandle(ref, () => ({
|
|
91
|
+
upload: async () => {
|
|
92
|
+
if (!pendingFilesRef.current.length) {
|
|
93
|
+
console.warn("FileUploader2: no files selected to upload.");
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
await executeUpload(pendingFilesRef.current);
|
|
97
|
+
},
|
|
98
|
+
}));
|
|
99
|
+
/* ------------ CORE FILE HANDLER ------------ */
|
|
100
|
+
const handleFiles = async (files) => {
|
|
101
|
+
const fileList = isMultiple ? files : [files[0]];
|
|
102
|
+
// Local previews for images
|
|
103
|
+
const newPreviews = fileList.map((f) => f.type.startsWith("image/") ? URL.createObjectURL(f) : "");
|
|
104
|
+
setPreviews(newPreviews.filter(Boolean));
|
|
105
|
+
setFileNames(fileList.map((f) => f.name).join(", "));
|
|
106
|
+
// Notify parent of raw files
|
|
107
|
+
onFileSelect === null || onFileSelect === void 0 ? void 0 : onFileSelect(fileList);
|
|
108
|
+
if (manual) {
|
|
109
|
+
// Store files for later — upload() will consume them
|
|
110
|
+
pendingFilesRef.current = fileList;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
await executeUpload(fileList);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
/* ------------ INPUT CHANGE ------------ */
|
|
117
|
+
const handleInputChange = (e) => {
|
|
118
|
+
const files = e.target.files;
|
|
119
|
+
if (files === null || files === void 0 ? void 0 : files.length)
|
|
120
|
+
handleFiles(Array.from(files));
|
|
121
|
+
};
|
|
122
|
+
/* ------------ DRAG HANDLERS ------------ */
|
|
123
|
+
const dragHandlers = isDrag
|
|
124
|
+
? {
|
|
125
|
+
onDragOver: (e) => {
|
|
126
|
+
e.preventDefault();
|
|
127
|
+
setIsDragging(true);
|
|
128
|
+
},
|
|
129
|
+
onDragLeave: () => setIsDragging(false),
|
|
130
|
+
onDrop: (e) => {
|
|
131
|
+
e.preventDefault();
|
|
132
|
+
setIsDragging(false);
|
|
133
|
+
if (e.dataTransfer.files.length) {
|
|
134
|
+
handleFiles(Array.from(e.dataTransfer.files));
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
}
|
|
138
|
+
: {};
|
|
139
|
+
/* ------------ DISPLAY PREVIEWS ------------ */
|
|
140
|
+
const displayPreviews = previews.length > 0 ? previews : imageUrl ? [imageUrl] : [];
|
|
141
|
+
return (_jsxs("div", Object.assign({}, dragHandlers, { className: `flex flex-col ${fileInput ? "items-start text-left" : "items-center text-center"} justify-center gap-2 cursor-pointer px-4 pb-4 rounded-lg transition ${fileInput
|
|
142
|
+
? ""
|
|
143
|
+
: `border-2 border-dashed ${isDragging ? "border-blue-600 bg-blue-50" : "border-gray-300"}`}`, children: [_jsx("label", { className: "text-md mt-4 font-bold", children: label }), _jsx("input", { type: "file", id: inputId, className: "hidden", multiple: isMultiple, onChange: handleInputChange }), fileInput && (_jsxs(_Fragment, { children: [_jsx(FileInputUI, { fileNames: fileNames, fileNamePlaceholder: fileNamePlaceholder, tooltipContent: tooltipContent, onFileNamesChange: setFileNames, onFileSelect: (files) => handleFiles(files), isMultiple: isMultiple, inputId: `${inputId}-fileinput` }), isDrag && (_jsx("p", { className: "text-sm text-gray-500", children: isDragging ? "Drop files here..." : "or drag & drop files here" })), loading && (_jsx("p", { className: "text-sm text-blue-500 animate-pulse", children: "Uploading..." })), filesCount > 1 && !loading && (_jsxs("p", { className: "text-sm font-medium", children: [filesCount, " files selected"] }))] })), !fileInput && (_jsx(_Fragment, { children: isDrag ? (_jsxs("div", { className: "mt-3 p-6 w-full text-center cursor-pointer", onClick: () => { var _a; return (_a = document.getElementById(inputId)) === null || _a === void 0 ? void 0 : _a.click(); }, children: [filesCount === 0 && !loading && (_jsxs("div", { className: "flex flex-col items-center gap-3", children: [_jsx(UploadCloud, { className: "w-10 h-10 text-blue-600" }), _jsxs("p", { className: "text-gray-600", children: ["Drag & drop ", isMultiple ? "files" : "a file", " or", " ", _jsx("span", { className: "text-blue-600 underline", children: "browse" })] })] })), loading && (_jsx("p", { className: "text-sm text-blue-500 animate-pulse", children: "Uploading..." })), filesCount > 0 && !loading && (_jsx("p", { className: "mt-3 text-sm font-medium", children: filesCount === 1
|
|
144
|
+
? fileNames
|
|
145
|
+
: `${filesCount} files selected` }))] })) : (_jsxs("div", { className: "border rounded-lg p-4 flex flex-col items-center gap-2 cursor-pointer text-center", onClick: () => { var _a; return (_a = document.getElementById(inputId)) === null || _a === void 0 ? void 0 : _a.click(); }, children: [filesCount === 0 && !loading && (_jsxs(_Fragment, { children: [_jsx(FileUp, { className: "w-8 h-8 text-blue-600" }), _jsx("span", { className: "text-blue-600 underline", children: isMultiple ? "Browse Files" : "Browse File" })] })), loading && (_jsx("p", { className: "text-sm text-blue-500 animate-pulse", children: "Uploading..." })), filesCount > 0 && !loading && (_jsx("p", { className: "text-sm font-medium", children: filesCount === 1
|
|
146
|
+
? fileNames
|
|
147
|
+
: `${filesCount} files selected` }))] })) })), !fileInput && displayPreviews.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-2 mt-1 justify-center", children: displayPreviews.map((src, i) => (_jsx("img", { src: src, alt: `preview-${i}`, className: "h-10 object-cover rounded-md border p-1" }, i))) }))] })));
|
|
148
|
+
});
|
|
149
|
+
/* ----------------------------------------------------
|
|
150
|
+
Convert Base64 back to Blob URL (utility, kept for compat)
|
|
151
|
+
---------------------------------------------------- */
|
|
152
|
+
export function getFileUrlFromObject(obj) {
|
|
153
|
+
const byteCharacters = atob(obj.base64);
|
|
154
|
+
const byteNumbers = Array.from(byteCharacters).map((c) => c.charCodeAt(0));
|
|
155
|
+
const blob = new Blob([new Uint8Array(byteNumbers)], {
|
|
156
|
+
type: obj.resourceType,
|
|
157
|
+
});
|
|
158
|
+
return URL.createObjectURL(blob);
|
|
159
|
+
}
|
|
@@ -5,13 +5,15 @@ import { ChevronRight } from "lucide-react";
|
|
|
5
5
|
import { SidebarGroup, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem } from "../../shadcn/sidebar";
|
|
6
6
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../../shadcn/collapsible";
|
|
7
7
|
import Link from "next/link";
|
|
8
|
+
import { usePathname } from "next/navigation";
|
|
8
9
|
import { useSidebarNav } from "./SidebarNavContext";
|
|
9
10
|
export function NavMain() {
|
|
10
11
|
const { navItems } = useSidebarNav();
|
|
12
|
+
const pathname = usePathname();
|
|
11
13
|
// if (!navItems || navItems.length === 0) {
|
|
12
14
|
// return null;
|
|
13
15
|
// }
|
|
14
|
-
return (_jsx(SidebarGroup, { children: _jsx(SidebarMenu, { children: navItems.map((item) => item.items && item.items.length > 0 ? (_jsx(Collapsible, { asChild: true, defaultOpen: item.isActive, className: "group/collapsible", children: _jsxs(SidebarMenuItem, { children: [_jsx(CollapsibleTrigger, { asChild: true, children: _jsxs(SidebarMenuButton, { tooltip: item.title, children: [item.icon && _jsx(item.icon, {}), _jsx("span", { children: item.title }), _jsx(ChevronRight, { className: "ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" })] }) }), _jsx(CollapsibleContent, { children: _jsx(SidebarMenuSub, { children: item.items.map((subItem) => (_jsx(SidebarMenuSubItem, { children: _jsx(SidebarMenuSubButton, { asChild: true, children: _jsx(Link, { href: subItem.url, children: _jsx("span", { children: subItem.title }) }) }) }, subItem.title))) }) })] }) }, item.title)) : (_jsx(SidebarMenuItem, { children: _jsx(SidebarMenuButton, { asChild: true, tooltip: item.title, children: _jsxs(Link, { href: item.url, className: "flex items-center gap-2 w-full", children: [item.icon && _jsx(item.icon, {}), _jsx("span", { children: item.title })] }) }) }, item.title))) }) }));
|
|
16
|
+
return (_jsx(SidebarGroup, { children: _jsx(SidebarMenu, { children: navItems.map((item) => item.items && item.items.length > 0 ? (_jsx(Collapsible, { asChild: true, defaultOpen: item.isActive || item.items.some(sub => pathname.startsWith(sub.url)), className: "group/collapsible", children: _jsxs(SidebarMenuItem, { children: [_jsx(CollapsibleTrigger, { asChild: true, children: _jsxs(SidebarMenuButton, { tooltip: item.title, isActive: pathname.startsWith(item.url) || item.items.some(sub => pathname.startsWith(sub.url)), children: [item.icon && _jsx(item.icon, {}), _jsx("span", { children: item.title }), _jsx(ChevronRight, { className: "ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" })] }) }), _jsx(CollapsibleContent, { children: _jsx(SidebarMenuSub, { children: item.items.map((subItem) => (_jsx(SidebarMenuSubItem, { children: _jsx(SidebarMenuSubButton, { asChild: true, isActive: pathname === subItem.url || pathname.startsWith(`${subItem.url}/`), children: _jsx(Link, { href: subItem.url, children: _jsx("span", { children: subItem.title }) }) }) }, subItem.title))) }) })] }) }, item.title)) : (_jsx(SidebarMenuItem, { children: _jsx(SidebarMenuButton, { asChild: true, tooltip: item.title, isActive: pathname === item.url || pathname.startsWith(`${item.url}/`), children: _jsxs(Link, { href: item.url, className: "flex items-center gap-2 w-full", children: [item.icon && _jsx(item.icon, {}), _jsx("span", { children: item.title })] }) }) }, item.title))) }) }));
|
|
15
17
|
}
|
|
16
18
|
// Helper component to set nav items from pages
|
|
17
19
|
export function RenderSidebarNav({ items, sidebarHeader, sidebarFooter }) {
|
package/dist/index.d.ts
CHANGED
|
@@ -53,8 +53,6 @@ export type { ComboBoxInputProps, ComboboxItemProps, } from "./ikoncomponents/co
|
|
|
53
53
|
export { DataTableColumnFilter } from "./ikoncomponents/data-table/datatable-column-filter";
|
|
54
54
|
export { DataTableFacetedFilter } from "./ikoncomponents/data-table/datatable-faceted-filter";
|
|
55
55
|
export { DataTableFilterMenu } from "./ikoncomponents/data-table/datatable-filter-menu";
|
|
56
|
-
export { convertFileToObject, FileUploader, getImageFromObject, } from "./ikoncomponents/fileUpload";
|
|
57
|
-
export type { FileUploaderProps } from "./ikoncomponents/fileUpload";
|
|
58
56
|
export { DataTablePagination } from "./ikoncomponents/data-table/datatable-pagination";
|
|
59
57
|
export { DataTableToolbar } from "./ikoncomponents/data-table/datatable-toolbar";
|
|
60
58
|
export { getDataTableColumnTitle } from "./ikoncomponents/data-table/function";
|
|
@@ -129,7 +127,6 @@ export { NewImageForm } from "./ikoncomponents/image-cropper-upload/components/n
|
|
|
129
127
|
export type { ImageFormProps } from "./ikoncomponents/image-cropper-upload/components/newImageUploadForm";
|
|
130
128
|
export { WorkInProgress } from "./ikoncomponents/work-in-progress";
|
|
131
129
|
export { CustomComboboxInput } from "./ikoncomponents/custom-combo-dropdown";
|
|
132
|
-
export { AssistantComponent } from "./ikoncomponents/assistant-ui/Assistant";
|
|
133
130
|
export { ThemeProvider } from "./utils/theme-provider";
|
|
134
131
|
export { RadiusProvider, useRadius } from "./utils/border-radius-provider";
|
|
135
132
|
export { FontProvider, useFont } from "./utils/font-provider";
|
|
@@ -140,3 +137,11 @@ export { getValidAccessToken, refreshAccessToken, decodeAccessToken, logOut } fr
|
|
|
140
137
|
export type { TokenResponse } from "./utils/token-management/types";
|
|
141
138
|
export { useIsMobile } from "./hooks/use-mobile";
|
|
142
139
|
export { useRefresh } from "./ikoncomponents/main-layout/RefreshContext";
|
|
140
|
+
export { FileUploaderApi } from "./ikoncomponents/fileUploadApi";
|
|
141
|
+
export type { FileUploader2Ref, FileUploader2Props } from "./ikoncomponents/fileUploadApi";
|
|
142
|
+
export { fetchFileAsBase64 } from "./utils/api/file-upload copy/index";
|
|
143
|
+
export { downloadFileByResourceId } from "./utils/api/file-upload copy/index";
|
|
144
|
+
export { fetchImagePreview } from "./utils/api/file-upload copy/index";
|
|
145
|
+
export { convertFileToObject } from "./ikoncomponents/fileUpload";
|
|
146
|
+
export { getFileUrlFromObject } from "./ikoncomponents/fileUpload";
|
|
147
|
+
export { default as FileUploader } from "./ikoncomponents/fileUpload";
|
package/dist/index.js
CHANGED
|
@@ -50,7 +50,6 @@ export { ComboboxInput } from "./ikoncomponents/combobox-input";
|
|
|
50
50
|
export { DataTableColumnFilter } from "./ikoncomponents/data-table/datatable-column-filter";
|
|
51
51
|
export { DataTableFacetedFilter } from "./ikoncomponents/data-table/datatable-faceted-filter";
|
|
52
52
|
export { DataTableFilterMenu } from "./ikoncomponents/data-table/datatable-filter-menu";
|
|
53
|
-
export { convertFileToObject, FileUploader, getImageFromObject, } from "./ikoncomponents/fileUpload";
|
|
54
53
|
export { DataTablePagination } from "./ikoncomponents/data-table/datatable-pagination";
|
|
55
54
|
export { DataTableToolbar } from "./ikoncomponents/data-table/datatable-toolbar";
|
|
56
55
|
export { getDataTableColumnTitle } from "./ikoncomponents/data-table/function";
|
|
@@ -108,7 +107,7 @@ export { NewCropperImg } from "./ikoncomponents/image-cropper-upload/components/
|
|
|
108
107
|
export { NewImageForm } from "./ikoncomponents/image-cropper-upload/components/newImageUploadForm";
|
|
109
108
|
export { WorkInProgress } from "./ikoncomponents/work-in-progress";
|
|
110
109
|
export { CustomComboboxInput } from "./ikoncomponents/custom-combo-dropdown";
|
|
111
|
-
export { AssistantComponent } from "./ikoncomponents/assistant-ui/Assistant";
|
|
110
|
+
// export { AssistantComponent } from "./ikoncomponents/assistant-ui/Assistant";
|
|
112
111
|
export { ThemeProvider } from "./utils/theme-provider";
|
|
113
112
|
export { RadiusProvider, useRadius } from "./utils/border-radius-provider";
|
|
114
113
|
export { FontProvider, useFont } from "./utils/font-provider";
|
|
@@ -117,3 +116,10 @@ export { setCookieSession, getCookieSession, clearCookieSession, clearAllCookieS
|
|
|
117
116
|
export { getValidAccessToken, refreshAccessToken, decodeAccessToken, logOut } from "./utils/token-management";
|
|
118
117
|
export { useIsMobile } from "./hooks/use-mobile";
|
|
119
118
|
export { useRefresh } from "./ikoncomponents/main-layout/RefreshContext";
|
|
119
|
+
export { FileUploaderApi } from "./ikoncomponents/fileUploadApi";
|
|
120
|
+
export { fetchFileAsBase64 } from "./utils/api/file-upload copy/index";
|
|
121
|
+
export { downloadFileByResourceId } from "./utils/api/file-upload copy/index";
|
|
122
|
+
export { fetchImagePreview } from "./utils/api/file-upload copy/index";
|
|
123
|
+
export { convertFileToObject } from "./ikoncomponents/fileUpload";
|
|
124
|
+
export { getFileUrlFromObject } from "./ikoncomponents/fileUpload";
|
|
125
|
+
export { default as FileUploader } from "./ikoncomponents/fileUpload";
|