@thetechfossil/upfiles 1.0.0 → 1.0.3
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 +449 -443
- package/dist/index.d.mts +28 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +49 -23
- package/dist/index.mjs +49 -23
- package/package.json +45 -43
package/dist/index.d.mts
CHANGED
|
@@ -34,6 +34,19 @@ type FileListItem = {
|
|
|
34
34
|
uploadedAt: string;
|
|
35
35
|
url: string;
|
|
36
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* Upfiles Client - JavaScript client for Upfiles Plugin API
|
|
39
|
+
*
|
|
40
|
+
* 📚 Full documentation: https://ttf-upfiles-docs.netlify.app/
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* const client = new UpfilesClient({
|
|
45
|
+
* baseUrl: 'https://your-upfiles.example.com',
|
|
46
|
+
* apiKey: 'upk_...',
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
37
50
|
type UpfilesClientOptions = {
|
|
38
51
|
baseUrl?: string;
|
|
39
52
|
presignPath?: string;
|
|
@@ -44,6 +57,21 @@ type UpfilesClientOptions = {
|
|
|
44
57
|
apiKeyHeader?: 'authorization' | 'x-api-key' | 'x-up-api-key';
|
|
45
58
|
thumbnailsPath?: string;
|
|
46
59
|
};
|
|
60
|
+
/**
|
|
61
|
+
* UpfilesClient - Main client class for interacting with Upfiles Plugin API
|
|
62
|
+
*
|
|
63
|
+
* 📚 Documentation: https://ttf-upfiles-docs.netlify.app/
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* const client = new UpfilesClient({
|
|
68
|
+
* baseUrl: 'https://your-upfiles.example.com',
|
|
69
|
+
* apiKey: 'upk_...',
|
|
70
|
+
* });
|
|
71
|
+
*
|
|
72
|
+
* const result = await client.upload(file, { folderPath: 'uploads/' });
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
47
75
|
declare class UpfilesClient {
|
|
48
76
|
private baseUrl?;
|
|
49
77
|
private presignPath;
|
package/dist/index.d.ts
CHANGED
|
@@ -34,6 +34,19 @@ type FileListItem = {
|
|
|
34
34
|
uploadedAt: string;
|
|
35
35
|
url: string;
|
|
36
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* Upfiles Client - JavaScript client for Upfiles Plugin API
|
|
39
|
+
*
|
|
40
|
+
* 📚 Full documentation: https://ttf-upfiles-docs.netlify.app/
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* const client = new UpfilesClient({
|
|
45
|
+
* baseUrl: 'https://your-upfiles.example.com',
|
|
46
|
+
* apiKey: 'upk_...',
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
37
50
|
type UpfilesClientOptions = {
|
|
38
51
|
baseUrl?: string;
|
|
39
52
|
presignPath?: string;
|
|
@@ -44,6 +57,21 @@ type UpfilesClientOptions = {
|
|
|
44
57
|
apiKeyHeader?: 'authorization' | 'x-api-key' | 'x-up-api-key';
|
|
45
58
|
thumbnailsPath?: string;
|
|
46
59
|
};
|
|
60
|
+
/**
|
|
61
|
+
* UpfilesClient - Main client class for interacting with Upfiles Plugin API
|
|
62
|
+
*
|
|
63
|
+
* 📚 Documentation: https://ttf-upfiles-docs.netlify.app/
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* const client = new UpfilesClient({
|
|
68
|
+
* baseUrl: 'https://your-upfiles.example.com',
|
|
69
|
+
* apiKey: 'upk_...',
|
|
70
|
+
* });
|
|
71
|
+
*
|
|
72
|
+
* const result = await client.upload(file, { folderPath: 'uploads/' });
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
47
75
|
declare class UpfilesClient {
|
|
48
76
|
private baseUrl?;
|
|
49
77
|
private presignPath;
|
package/dist/index.js
CHANGED
|
@@ -952,9 +952,12 @@ async function fetchProjectImagesWithThumbs(clientOptions, opts) {
|
|
|
952
952
|
const limit = Math.max(1, Math.min(opts?.limitConcurrent ?? 6, 12));
|
|
953
953
|
const queue = [...files];
|
|
954
954
|
const results = [];
|
|
955
|
+
const processed = /* @__PURE__ */ new Set();
|
|
955
956
|
async function worker() {
|
|
956
957
|
while (queue.length) {
|
|
957
958
|
const f2 = queue.shift();
|
|
959
|
+
if (!f2 || processed.has(f2.key)) continue;
|
|
960
|
+
processed.add(f2.key);
|
|
958
961
|
try {
|
|
959
962
|
const thumbs = await fetchFileThumbnails(clientOptions, f2.key).catch(() => void 0);
|
|
960
963
|
results.push({ ...f2, thumbnails: thumbs });
|
|
@@ -963,7 +966,10 @@ async function fetchProjectImagesWithThumbs(clientOptions, opts) {
|
|
|
963
966
|
}
|
|
964
967
|
}
|
|
965
968
|
}
|
|
966
|
-
|
|
969
|
+
const workers = Math.min(limit, files.length || 1);
|
|
970
|
+
await Promise.all(Array.from({ length: workers }, () => worker()));
|
|
971
|
+
const keyToIndex = new Map(files.map((f2, i) => [f2.key, i]));
|
|
972
|
+
results.sort((a, b) => (keyToIndex.get(a.key) ?? 0) - (keyToIndex.get(b.key) ?? 0));
|
|
967
973
|
return results;
|
|
968
974
|
}
|
|
969
975
|
|
|
@@ -1011,7 +1017,12 @@ function ImageManager(props) {
|
|
|
1011
1017
|
}
|
|
1012
1018
|
}, [clientOptions, folderPath]);
|
|
1013
1019
|
React4.useEffect(() => {
|
|
1014
|
-
if (!open)
|
|
1020
|
+
if (!open) {
|
|
1021
|
+
setItems([]);
|
|
1022
|
+
setQuery("");
|
|
1023
|
+
setError(null);
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1015
1026
|
if (mode !== "upload" && tab === "browse") {
|
|
1016
1027
|
load();
|
|
1017
1028
|
}
|
|
@@ -1060,21 +1071,21 @@ function ImageManager(props) {
|
|
|
1060
1071
|
[clientOptions, deleteUrl, onDelete]
|
|
1061
1072
|
);
|
|
1062
1073
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Dialog2.Root, { open, onOpenChange, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Dialog2.Portal, { children: [
|
|
1063
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Dialog2.Overlay, { className: "fixed inset-0 bg-black/40" }),
|
|
1074
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Dialog2.Overlay, { className: "fixed inset-0 bg-black/40 dark:bg-black/60" }),
|
|
1064
1075
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1065
1076
|
Dialog2.Content,
|
|
1066
1077
|
{
|
|
1067
|
-
className: `fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[95vw] max-w-4xl rounded-lg border bg-white p-4 shadow-lg max-h-[90vh] overflow-auto ${className ?? ""}`,
|
|
1078
|
+
className: `fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[95vw] max-w-4xl rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 p-4 shadow-lg max-h-[90vh] overflow-auto ${className ?? ""}`,
|
|
1068
1079
|
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "space-y-3", children: [
|
|
1069
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Dialog2.Title, { className: "text-lg font-semibold", children: title ?? "Image Manager" }),
|
|
1070
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Dialog2.Description, { className: "text-sm text-
|
|
1071
|
-
mode === "full" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex gap-2 border-b", children: [
|
|
1080
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Dialog2.Title, { className: "text-lg font-semibold text-gray-900 dark:text-white", children: title ?? "Image Manager" }),
|
|
1081
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Dialog2.Description, { className: "text-sm text-gray-600 dark:text-gray-400", children: description ?? "Upload new images or select from existing ones." }),
|
|
1082
|
+
mode === "full" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex gap-2 border-b border-gray-200 dark:border-gray-700", children: [
|
|
1072
1083
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1073
1084
|
"button",
|
|
1074
1085
|
{
|
|
1075
1086
|
type: "button",
|
|
1076
1087
|
onClick: () => setTab("browse"),
|
|
1077
|
-
className: `px-4 py-2 text-sm font-medium border-b-2 transition-colors ${tab === "browse" ? "border-blue-600 text-blue-600" : "border-transparent text-gray-600 hover:text-gray-900"}`,
|
|
1088
|
+
className: `px-4 py-2 text-sm font-medium border-b-2 transition-colors ${tab === "browse" ? "border-blue-600 text-blue-600 dark:text-blue-400" : "border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"}`,
|
|
1078
1089
|
children: "Browse"
|
|
1079
1090
|
}
|
|
1080
1091
|
),
|
|
@@ -1083,7 +1094,7 @@ function ImageManager(props) {
|
|
|
1083
1094
|
{
|
|
1084
1095
|
type: "button",
|
|
1085
1096
|
onClick: () => setTab("upload"),
|
|
1086
|
-
className: `px-4 py-2 text-sm font-medium border-b-2 transition-colors ${tab === "upload" ? "border-blue-600 text-blue-600" : "border-transparent text-gray-600 hover:text-gray-900"}`,
|
|
1097
|
+
className: `px-4 py-2 text-sm font-medium border-b-2 transition-colors ${tab === "upload" ? "border-blue-600 text-blue-600 dark:text-blue-400" : "border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"}`,
|
|
1087
1098
|
children: "Upload"
|
|
1088
1099
|
}
|
|
1089
1100
|
)
|
|
@@ -1093,19 +1104,19 @@ function ImageManager(props) {
|
|
|
1093
1104
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1094
1105
|
"input",
|
|
1095
1106
|
{
|
|
1096
|
-
className: "w-full rounded border px-3 py-2 text-sm",
|
|
1107
|
+
className: "w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white px-3 py-2 text-sm placeholder:text-gray-500 dark:placeholder:text-gray-400",
|
|
1097
1108
|
placeholder: "Search by filename...",
|
|
1098
1109
|
value: query,
|
|
1099
1110
|
onChange: (e) => setQuery(e.target.value)
|
|
1100
1111
|
}
|
|
1101
1112
|
),
|
|
1102
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "px-3 py-2 text-sm rounded border", onClick: load, disabled: loading, children: loading ? "Loading\u2026" : "Refresh" })
|
|
1113
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "px-3 py-2 text-sm rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white hover:bg-gray-50 dark:hover:bg-gray-600 disabled:opacity-50", onClick: load, disabled: loading, children: loading ? "Loading\u2026" : "Refresh" })
|
|
1103
1114
|
] }),
|
|
1104
|
-
error && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "rounded border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700", children: error }),
|
|
1115
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "rounded border border-red-200 dark:border-red-800 bg-red-50 dark:bg-red-900/20 px-3 py-2 text-sm text-red-700 dark:text-red-400", children: error }),
|
|
1105
1116
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: gridClassName ?? "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-3 max-h-[50vh] overflow-auto", children: [
|
|
1106
1117
|
!loading && visible.map((f2) => {
|
|
1107
1118
|
const thumbUrl = f2.thumbnails?.[0]?.url || f2.url;
|
|
1108
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "group relative aspect-square overflow-hidden rounded border", children: [
|
|
1119
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "group relative aspect-square overflow-hidden rounded border border-gray-200 dark:border-gray-700", children: [
|
|
1109
1120
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { src: thumbUrl, alt: f2.originalName, className: "h-full w-full object-cover" }),
|
|
1110
1121
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "absolute inset-0 bg-black/0 group-hover:bg-black/40 transition-colors flex items-center justify-center gap-2", children: [
|
|
1111
1122
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
@@ -1123,7 +1134,7 @@ function ImageManager(props) {
|
|
|
1123
1134
|
});
|
|
1124
1135
|
onOpenChange(false);
|
|
1125
1136
|
},
|
|
1126
|
-
className: "opacity-0 group-hover:opacity-100 px-3 py-1 text-xs bg-blue-600 text-white rounded",
|
|
1137
|
+
className: "opacity-0 group-hover:opacity-100 px-3 py-1 text-xs bg-blue-600 hover:bg-blue-700 text-white rounded",
|
|
1127
1138
|
children: "Select"
|
|
1128
1139
|
}
|
|
1129
1140
|
),
|
|
@@ -1133,7 +1144,7 @@ function ImageManager(props) {
|
|
|
1133
1144
|
type: "button",
|
|
1134
1145
|
onClick: () => handleDelete(f2.key),
|
|
1135
1146
|
disabled: deleting === f2.key,
|
|
1136
|
-
className: "opacity-0 group-hover:opacity-100 px-3 py-1 text-xs bg-red-600 text-white rounded disabled:opacity-50",
|
|
1147
|
+
className: "opacity-0 group-hover:opacity-100 px-3 py-1 text-xs bg-red-600 hover:bg-red-700 text-white rounded disabled:opacity-50",
|
|
1137
1148
|
children: deleting === f2.key ? "..." : "Delete"
|
|
1138
1149
|
}
|
|
1139
1150
|
)
|
|
@@ -1141,8 +1152,8 @@ function ImageManager(props) {
|
|
|
1141
1152
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 bg-black/50 p-1 text-[10px] text-white truncate", children: f2.originalName })
|
|
1142
1153
|
] }, f2.key);
|
|
1143
1154
|
}),
|
|
1144
|
-
loading && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "col-span-full text-sm text-
|
|
1145
|
-
!loading && visible.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "col-span-full text-sm text-
|
|
1155
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "col-span-full text-sm text-gray-600 dark:text-gray-400", children: "Loading\u2026" }),
|
|
1156
|
+
!loading && visible.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "col-span-full text-sm text-gray-600 dark:text-gray-400", children: "No images found" })
|
|
1146
1157
|
] })
|
|
1147
1158
|
] }),
|
|
1148
1159
|
(mode === "upload" || mode === "full" && tab === "upload") && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
@@ -1156,18 +1167,33 @@ function ImageManager(props) {
|
|
|
1156
1167
|
maxFiles,
|
|
1157
1168
|
autoRecordToDb,
|
|
1158
1169
|
fetchThumbnails,
|
|
1170
|
+
onClientUploadComplete: (file) => {
|
|
1171
|
+
if (file.status === "success" && file.publicUrl && file.fileKey) {
|
|
1172
|
+
const newItem = {
|
|
1173
|
+
key: file.fileKey,
|
|
1174
|
+
originalName: file.name,
|
|
1175
|
+
size: file.size,
|
|
1176
|
+
contentType: file.type,
|
|
1177
|
+
uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1178
|
+
url: file.publicUrl,
|
|
1179
|
+
thumbnails: file.thumbnails
|
|
1180
|
+
};
|
|
1181
|
+
setItems((prev) => [newItem, ...prev]);
|
|
1182
|
+
}
|
|
1183
|
+
},
|
|
1159
1184
|
onComplete: (files) => {
|
|
1160
1185
|
if (mode === "full") {
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1186
|
+
setTimeout(() => {
|
|
1187
|
+
load();
|
|
1188
|
+
setTab("browse");
|
|
1189
|
+
}, 500);
|
|
1164
1190
|
}
|
|
1165
1191
|
},
|
|
1166
|
-
dropzoneClassName: "border-2 border-dashed border-gray-300 rounded-lg p-8 text-center cursor-pointer hover:border-blue-500 transition-colors",
|
|
1167
|
-
buttonClassName: "px-4 py-2 text-sm rounded border bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50"
|
|
1192
|
+
dropzoneClassName: "border-2 border-dashed border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-700/50 rounded-lg p-8 text-center cursor-pointer hover:border-blue-500 dark:hover:border-blue-400 transition-colors text-gray-700 dark:text-gray-300",
|
|
1193
|
+
buttonClassName: "px-4 py-2 text-sm rounded border border-gray-300 dark:border-gray-600 bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50"
|
|
1168
1194
|
}
|
|
1169
1195
|
),
|
|
1170
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex justify-end gap-2 pt-2 border-t", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Dialog2.Close, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "px-4 py-2 text-sm rounded border", children: "Close" }) }) })
|
|
1196
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex justify-end gap-2 pt-2 border-t border-gray-200 dark:border-gray-700", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Dialog2.Close, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "px-4 py-2 text-sm rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white hover:bg-gray-50 dark:hover:bg-gray-600", children: "Close" }) }) })
|
|
1171
1197
|
] })
|
|
1172
1198
|
}
|
|
1173
1199
|
)
|
package/dist/index.mjs
CHANGED
|
@@ -903,9 +903,12 @@ async function fetchProjectImagesWithThumbs(clientOptions, opts) {
|
|
|
903
903
|
const limit = Math.max(1, Math.min(opts?.limitConcurrent ?? 6, 12));
|
|
904
904
|
const queue = [...files];
|
|
905
905
|
const results = [];
|
|
906
|
+
const processed = /* @__PURE__ */ new Set();
|
|
906
907
|
async function worker() {
|
|
907
908
|
while (queue.length) {
|
|
908
909
|
const f2 = queue.shift();
|
|
910
|
+
if (!f2 || processed.has(f2.key)) continue;
|
|
911
|
+
processed.add(f2.key);
|
|
909
912
|
try {
|
|
910
913
|
const thumbs = await fetchFileThumbnails(clientOptions, f2.key).catch(() => void 0);
|
|
911
914
|
results.push({ ...f2, thumbnails: thumbs });
|
|
@@ -914,7 +917,10 @@ async function fetchProjectImagesWithThumbs(clientOptions, opts) {
|
|
|
914
917
|
}
|
|
915
918
|
}
|
|
916
919
|
}
|
|
917
|
-
|
|
920
|
+
const workers = Math.min(limit, files.length || 1);
|
|
921
|
+
await Promise.all(Array.from({ length: workers }, () => worker()));
|
|
922
|
+
const keyToIndex = new Map(files.map((f2, i) => [f2.key, i]));
|
|
923
|
+
results.sort((a, b) => (keyToIndex.get(a.key) ?? 0) - (keyToIndex.get(b.key) ?? 0));
|
|
918
924
|
return results;
|
|
919
925
|
}
|
|
920
926
|
|
|
@@ -962,7 +968,12 @@ function ImageManager(props) {
|
|
|
962
968
|
}
|
|
963
969
|
}, [clientOptions, folderPath]);
|
|
964
970
|
React4.useEffect(() => {
|
|
965
|
-
if (!open)
|
|
971
|
+
if (!open) {
|
|
972
|
+
setItems([]);
|
|
973
|
+
setQuery("");
|
|
974
|
+
setError(null);
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
966
977
|
if (mode !== "upload" && tab === "browse") {
|
|
967
978
|
load();
|
|
968
979
|
}
|
|
@@ -1011,21 +1022,21 @@ function ImageManager(props) {
|
|
|
1011
1022
|
[clientOptions, deleteUrl, onDelete]
|
|
1012
1023
|
);
|
|
1013
1024
|
return /* @__PURE__ */ jsx4(Dialog2.Root, { open, onOpenChange, children: /* @__PURE__ */ jsxs4(Dialog2.Portal, { children: [
|
|
1014
|
-
/* @__PURE__ */ jsx4(Dialog2.Overlay, { className: "fixed inset-0 bg-black/40" }),
|
|
1025
|
+
/* @__PURE__ */ jsx4(Dialog2.Overlay, { className: "fixed inset-0 bg-black/40 dark:bg-black/60" }),
|
|
1015
1026
|
/* @__PURE__ */ jsx4(
|
|
1016
1027
|
Dialog2.Content,
|
|
1017
1028
|
{
|
|
1018
|
-
className: `fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[95vw] max-w-4xl rounded-lg border bg-white p-4 shadow-lg max-h-[90vh] overflow-auto ${className ?? ""}`,
|
|
1029
|
+
className: `fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[95vw] max-w-4xl rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 p-4 shadow-lg max-h-[90vh] overflow-auto ${className ?? ""}`,
|
|
1019
1030
|
children: /* @__PURE__ */ jsxs4("div", { className: "space-y-3", children: [
|
|
1020
|
-
/* @__PURE__ */ jsx4(Dialog2.Title, { className: "text-lg font-semibold", children: title ?? "Image Manager" }),
|
|
1021
|
-
/* @__PURE__ */ jsx4(Dialog2.Description, { className: "text-sm text-
|
|
1022
|
-
mode === "full" && /* @__PURE__ */ jsxs4("div", { className: "flex gap-2 border-b", children: [
|
|
1031
|
+
/* @__PURE__ */ jsx4(Dialog2.Title, { className: "text-lg font-semibold text-gray-900 dark:text-white", children: title ?? "Image Manager" }),
|
|
1032
|
+
/* @__PURE__ */ jsx4(Dialog2.Description, { className: "text-sm text-gray-600 dark:text-gray-400", children: description ?? "Upload new images or select from existing ones." }),
|
|
1033
|
+
mode === "full" && /* @__PURE__ */ jsxs4("div", { className: "flex gap-2 border-b border-gray-200 dark:border-gray-700", children: [
|
|
1023
1034
|
/* @__PURE__ */ jsx4(
|
|
1024
1035
|
"button",
|
|
1025
1036
|
{
|
|
1026
1037
|
type: "button",
|
|
1027
1038
|
onClick: () => setTab("browse"),
|
|
1028
|
-
className: `px-4 py-2 text-sm font-medium border-b-2 transition-colors ${tab === "browse" ? "border-blue-600 text-blue-600" : "border-transparent text-gray-600 hover:text-gray-900"}`,
|
|
1039
|
+
className: `px-4 py-2 text-sm font-medium border-b-2 transition-colors ${tab === "browse" ? "border-blue-600 text-blue-600 dark:text-blue-400" : "border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"}`,
|
|
1029
1040
|
children: "Browse"
|
|
1030
1041
|
}
|
|
1031
1042
|
),
|
|
@@ -1034,7 +1045,7 @@ function ImageManager(props) {
|
|
|
1034
1045
|
{
|
|
1035
1046
|
type: "button",
|
|
1036
1047
|
onClick: () => setTab("upload"),
|
|
1037
|
-
className: `px-4 py-2 text-sm font-medium border-b-2 transition-colors ${tab === "upload" ? "border-blue-600 text-blue-600" : "border-transparent text-gray-600 hover:text-gray-900"}`,
|
|
1048
|
+
className: `px-4 py-2 text-sm font-medium border-b-2 transition-colors ${tab === "upload" ? "border-blue-600 text-blue-600 dark:text-blue-400" : "border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"}`,
|
|
1038
1049
|
children: "Upload"
|
|
1039
1050
|
}
|
|
1040
1051
|
)
|
|
@@ -1044,19 +1055,19 @@ function ImageManager(props) {
|
|
|
1044
1055
|
/* @__PURE__ */ jsx4(
|
|
1045
1056
|
"input",
|
|
1046
1057
|
{
|
|
1047
|
-
className: "w-full rounded border px-3 py-2 text-sm",
|
|
1058
|
+
className: "w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white px-3 py-2 text-sm placeholder:text-gray-500 dark:placeholder:text-gray-400",
|
|
1048
1059
|
placeholder: "Search by filename...",
|
|
1049
1060
|
value: query,
|
|
1050
1061
|
onChange: (e) => setQuery(e.target.value)
|
|
1051
1062
|
}
|
|
1052
1063
|
),
|
|
1053
|
-
/* @__PURE__ */ jsx4("button", { type: "button", className: "px-3 py-2 text-sm rounded border", onClick: load, disabled: loading, children: loading ? "Loading\u2026" : "Refresh" })
|
|
1064
|
+
/* @__PURE__ */ jsx4("button", { type: "button", className: "px-3 py-2 text-sm rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white hover:bg-gray-50 dark:hover:bg-gray-600 disabled:opacity-50", onClick: load, disabled: loading, children: loading ? "Loading\u2026" : "Refresh" })
|
|
1054
1065
|
] }),
|
|
1055
|
-
error && /* @__PURE__ */ jsx4("div", { className: "rounded border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700", children: error }),
|
|
1066
|
+
error && /* @__PURE__ */ jsx4("div", { className: "rounded border border-red-200 dark:border-red-800 bg-red-50 dark:bg-red-900/20 px-3 py-2 text-sm text-red-700 dark:text-red-400", children: error }),
|
|
1056
1067
|
/* @__PURE__ */ jsxs4("div", { className: gridClassName ?? "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-3 max-h-[50vh] overflow-auto", children: [
|
|
1057
1068
|
!loading && visible.map((f2) => {
|
|
1058
1069
|
const thumbUrl = f2.thumbnails?.[0]?.url || f2.url;
|
|
1059
|
-
return /* @__PURE__ */ jsxs4("div", { className: "group relative aspect-square overflow-hidden rounded border", children: [
|
|
1070
|
+
return /* @__PURE__ */ jsxs4("div", { className: "group relative aspect-square overflow-hidden rounded border border-gray-200 dark:border-gray-700", children: [
|
|
1060
1071
|
/* @__PURE__ */ jsx4("img", { src: thumbUrl, alt: f2.originalName, className: "h-full w-full object-cover" }),
|
|
1061
1072
|
/* @__PURE__ */ jsxs4("div", { className: "absolute inset-0 bg-black/0 group-hover:bg-black/40 transition-colors flex items-center justify-center gap-2", children: [
|
|
1062
1073
|
/* @__PURE__ */ jsx4(
|
|
@@ -1074,7 +1085,7 @@ function ImageManager(props) {
|
|
|
1074
1085
|
});
|
|
1075
1086
|
onOpenChange(false);
|
|
1076
1087
|
},
|
|
1077
|
-
className: "opacity-0 group-hover:opacity-100 px-3 py-1 text-xs bg-blue-600 text-white rounded",
|
|
1088
|
+
className: "opacity-0 group-hover:opacity-100 px-3 py-1 text-xs bg-blue-600 hover:bg-blue-700 text-white rounded",
|
|
1078
1089
|
children: "Select"
|
|
1079
1090
|
}
|
|
1080
1091
|
),
|
|
@@ -1084,7 +1095,7 @@ function ImageManager(props) {
|
|
|
1084
1095
|
type: "button",
|
|
1085
1096
|
onClick: () => handleDelete(f2.key),
|
|
1086
1097
|
disabled: deleting === f2.key,
|
|
1087
|
-
className: "opacity-0 group-hover:opacity-100 px-3 py-1 text-xs bg-red-600 text-white rounded disabled:opacity-50",
|
|
1098
|
+
className: "opacity-0 group-hover:opacity-100 px-3 py-1 text-xs bg-red-600 hover:bg-red-700 text-white rounded disabled:opacity-50",
|
|
1088
1099
|
children: deleting === f2.key ? "..." : "Delete"
|
|
1089
1100
|
}
|
|
1090
1101
|
)
|
|
@@ -1092,8 +1103,8 @@ function ImageManager(props) {
|
|
|
1092
1103
|
/* @__PURE__ */ jsx4("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 bg-black/50 p-1 text-[10px] text-white truncate", children: f2.originalName })
|
|
1093
1104
|
] }, f2.key);
|
|
1094
1105
|
}),
|
|
1095
|
-
loading && /* @__PURE__ */ jsx4("div", { className: "col-span-full text-sm text-
|
|
1096
|
-
!loading && visible.length === 0 && /* @__PURE__ */ jsx4("div", { className: "col-span-full text-sm text-
|
|
1106
|
+
loading && /* @__PURE__ */ jsx4("div", { className: "col-span-full text-sm text-gray-600 dark:text-gray-400", children: "Loading\u2026" }),
|
|
1107
|
+
!loading && visible.length === 0 && /* @__PURE__ */ jsx4("div", { className: "col-span-full text-sm text-gray-600 dark:text-gray-400", children: "No images found" })
|
|
1097
1108
|
] })
|
|
1098
1109
|
] }),
|
|
1099
1110
|
(mode === "upload" || mode === "full" && tab === "upload") && /* @__PURE__ */ jsx4(
|
|
@@ -1107,18 +1118,33 @@ function ImageManager(props) {
|
|
|
1107
1118
|
maxFiles,
|
|
1108
1119
|
autoRecordToDb,
|
|
1109
1120
|
fetchThumbnails,
|
|
1121
|
+
onClientUploadComplete: (file) => {
|
|
1122
|
+
if (file.status === "success" && file.publicUrl && file.fileKey) {
|
|
1123
|
+
const newItem = {
|
|
1124
|
+
key: file.fileKey,
|
|
1125
|
+
originalName: file.name,
|
|
1126
|
+
size: file.size,
|
|
1127
|
+
contentType: file.type,
|
|
1128
|
+
uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1129
|
+
url: file.publicUrl,
|
|
1130
|
+
thumbnails: file.thumbnails
|
|
1131
|
+
};
|
|
1132
|
+
setItems((prev) => [newItem, ...prev]);
|
|
1133
|
+
}
|
|
1134
|
+
},
|
|
1110
1135
|
onComplete: (files) => {
|
|
1111
1136
|
if (mode === "full") {
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1137
|
+
setTimeout(() => {
|
|
1138
|
+
load();
|
|
1139
|
+
setTab("browse");
|
|
1140
|
+
}, 500);
|
|
1115
1141
|
}
|
|
1116
1142
|
},
|
|
1117
|
-
dropzoneClassName: "border-2 border-dashed border-gray-300 rounded-lg p-8 text-center cursor-pointer hover:border-blue-500 transition-colors",
|
|
1118
|
-
buttonClassName: "px-4 py-2 text-sm rounded border bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50"
|
|
1143
|
+
dropzoneClassName: "border-2 border-dashed border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-700/50 rounded-lg p-8 text-center cursor-pointer hover:border-blue-500 dark:hover:border-blue-400 transition-colors text-gray-700 dark:text-gray-300",
|
|
1144
|
+
buttonClassName: "px-4 py-2 text-sm rounded border border-gray-300 dark:border-gray-600 bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50"
|
|
1119
1145
|
}
|
|
1120
1146
|
),
|
|
1121
|
-
/* @__PURE__ */ jsx4("div", { className: "flex justify-end gap-2 pt-2 border-t", children: /* @__PURE__ */ jsx4(Dialog2.Close, { asChild: true, children: /* @__PURE__ */ jsx4("button", { type: "button", className: "px-4 py-2 text-sm rounded border", children: "Close" }) }) })
|
|
1147
|
+
/* @__PURE__ */ jsx4("div", { className: "flex justify-end gap-2 pt-2 border-t border-gray-200 dark:border-gray-700", children: /* @__PURE__ */ jsx4(Dialog2.Close, { asChild: true, children: /* @__PURE__ */ jsx4("button", { type: "button", className: "px-4 py-2 text-sm rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white hover:bg-gray-50 dark:hover:bg-gray-600", children: "Close" }) }) })
|
|
1122
1148
|
] })
|
|
1123
1149
|
}
|
|
1124
1150
|
)
|
package/package.json
CHANGED
|
@@ -1,43 +1,45 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@thetechfossil/upfiles",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Lightweight client and React components for Upfiles Plugin API (presigned S3)",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"author": "UpFiles",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/your-org/upfiles.git"
|
|
10
|
-
},
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@thetechfossil/upfiles",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Lightweight client and React components for Upfiles Plugin API (presigned S3)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "UpFiles",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/your-org/upfiles.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://ttf-upfiles-docs.netlify.app/",
|
|
12
|
+
"documentation": "https://ttf-upfiles-docs.netlify.app/",
|
|
13
|
+
"main": "dist/index.cjs",
|
|
14
|
+
"module": "dist/index.mjs",
|
|
15
|
+
"types": "dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"import": "./dist/index.mjs",
|
|
19
|
+
"require": "./dist/index.cjs"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
31
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
32
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
33
|
+
"prepublishOnly": "bun run build"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"react": ">=18",
|
|
37
|
+
"@radix-ui/react-dialog": ">=1.0.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@radix-ui/react-dialog": "^1.1.15",
|
|
41
|
+
"@types/react": "^19",
|
|
42
|
+
"tsup": "^8.3.0",
|
|
43
|
+
"typescript": "^5.6.2"
|
|
44
|
+
}
|
|
45
|
+
}
|