@muhgholy/next-drive 1.0.2 → 1.1.1
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 +98 -1
- package/dist/client/index.css +50 -45
- package/dist/client/index.css.map +1 -1
- package/dist/client/index.d.ts +11 -3
- package/dist/client/index.js +78 -43
- package/dist/client/index.js.map +1 -1
- package/dist/client/styles.css +71 -0
- package/dist/{index-CIbbTroj.d.ts → index-C4pw1JjP.d.ts} +3 -1
- package/dist/server/index.d.ts +12 -3
- package/dist/server/index.js +33 -0
- package/dist/server/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@ npm install @muhgholy/next-drive
|
|
|
23
23
|
- Next.js >= 14
|
|
24
24
|
- React >= 18
|
|
25
25
|
- Mongoose >= 7
|
|
26
|
+
- Tailwind CSS >= 3
|
|
26
27
|
|
|
27
28
|
**System Requirements:**
|
|
28
29
|
|
|
@@ -31,6 +32,29 @@ npm install @muhgholy/next-drive
|
|
|
31
32
|
- Ubuntu: `sudo apt install ffmpeg`
|
|
32
33
|
- Windows: Download from official site and add to PATH.
|
|
33
34
|
|
|
35
|
+
### Tailwind CSS Configuration
|
|
36
|
+
|
|
37
|
+
Since this package uses Tailwind CSS for styling, you **must** configure Tailwind to scan the package's files:
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
// tailwind.config.js
|
|
41
|
+
export default {
|
|
42
|
+
content: [
|
|
43
|
+
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
|
44
|
+
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
|
45
|
+
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
|
46
|
+
// Add the next-drive package
|
|
47
|
+
"./node_modules/@muhgholy/next-drive/dist/**/*.{js,mjs}",
|
|
48
|
+
],
|
|
49
|
+
theme: {
|
|
50
|
+
extend: {},
|
|
51
|
+
},
|
|
52
|
+
plugins: [],
|
|
53
|
+
};
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
> **Note**: The CSS is automatically injected when you import from `@muhgholy/next-drive/client` - no need to manually import stylesheets.
|
|
57
|
+
|
|
34
58
|
## Quick Start
|
|
35
59
|
|
|
36
60
|
### 1. Configure Server
|
|
@@ -74,7 +98,10 @@ export const drive = driveConfiguration({
|
|
|
74
98
|
|
|
75
99
|
Set up the API route handler that `next-drive` will use to communicate with the client.
|
|
76
100
|
|
|
77
|
-
**Important:**
|
|
101
|
+
**Important:**
|
|
102
|
+
|
|
103
|
+
- The API route must be in the `pages` folder (Pages Router)
|
|
104
|
+
- **You MUST disable Next.js body parser** for uploads to work properly
|
|
78
105
|
|
|
79
106
|
```typescript
|
|
80
107
|
// pages/api/drive.ts
|
|
@@ -83,8 +110,35 @@ import { driveAPIHandler } from "@muhgholy/next-drive/server";
|
|
|
83
110
|
import type { NextApiRequest, NextApiResponse } from "next";
|
|
84
111
|
|
|
85
112
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
113
|
+
// Manually parse JSON body for non-upload requests
|
|
114
|
+
if (!req.body) req.body = {};
|
|
115
|
+
|
|
116
|
+
if (req.headers["content-type"]?.includes("application/json")) {
|
|
117
|
+
try {
|
|
118
|
+
const buffer = await new Promise<Buffer>((resolve, reject) => {
|
|
119
|
+
const chunks: Buffer[] = [];
|
|
120
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
121
|
+
req.on("end", () => resolve(Buffer.concat(chunks)));
|
|
122
|
+
req.on("error", reject);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
if (buffer.length > 0) {
|
|
126
|
+
req.body = JSON.parse(buffer.toString());
|
|
127
|
+
}
|
|
128
|
+
} catch (e) {
|
|
129
|
+
console.error("Failed to parse JSON body", e);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
86
133
|
return driveAPIHandler(req, res);
|
|
87
134
|
}
|
|
135
|
+
|
|
136
|
+
// ⚠️ CRITICAL: Disable body parser for file uploads
|
|
137
|
+
export const config = {
|
|
138
|
+
api: {
|
|
139
|
+
bodyParser: false,
|
|
140
|
+
},
|
|
141
|
+
};
|
|
88
142
|
```
|
|
89
143
|
|
|
90
144
|
### 3. Add Provider
|
|
@@ -147,6 +201,49 @@ type MyFormData = z.infer<typeof myFormSchema>;
|
|
|
147
201
|
|
|
148
202
|
## Key Capabilities
|
|
149
203
|
|
|
204
|
+
### Client-Side File URLs
|
|
205
|
+
|
|
206
|
+
**Generate File URL:**
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import { useDrive } from "@muhgholy/next-drive/client";
|
|
210
|
+
import type { TDriveFile } from "@muhgholy/next-drive/client";
|
|
211
|
+
|
|
212
|
+
function MyComponent() {
|
|
213
|
+
const { createUrl } = useDrive();
|
|
214
|
+
|
|
215
|
+
// Basic URL generation
|
|
216
|
+
const url = createUrl(driveFile);
|
|
217
|
+
// Returns: /api/drive?action=serve&id={fileId}
|
|
218
|
+
|
|
219
|
+
// With image quality and format
|
|
220
|
+
const url = createUrl(driveFile, {
|
|
221
|
+
quality: "medium",
|
|
222
|
+
format: "webp",
|
|
223
|
+
});
|
|
224
|
+
// Returns: /api/drive?action=serve&id={fileId}&q=medium&format=webp
|
|
225
|
+
|
|
226
|
+
// Use in Next.js Image component
|
|
227
|
+
return <Image src={createUrl(driveFile)} alt={driveFile.file.name} />;
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Responsive Image SrcSet:**
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
import { useDrive } from "@muhgholy/next-drive/client";
|
|
235
|
+
|
|
236
|
+
function ResponsiveImage({ driveFile }: { driveFile: TDriveFile }) {
|
|
237
|
+
const { createUrl, createSrcSet } = useDrive();
|
|
238
|
+
|
|
239
|
+
// Generate responsive srcSet for optimal image loading
|
|
240
|
+
const { srcSet, sizes } = createSrcSet(driveFile, "webp");
|
|
241
|
+
|
|
242
|
+
// Use in img tag
|
|
243
|
+
return <img src={createUrl(driveFile, { quality: "medium" })} srcSet={srcSet} sizes={sizes} alt={driveFile.file.name} />;
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
150
247
|
### Server-Side File Access
|
|
151
248
|
|
|
152
249
|
**Get File URL:**
|
package/dist/client/index.css
CHANGED
|
@@ -1,48 +1,53 @@
|
|
|
1
1
|
/* src/client/styles.css */
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
2
|
+
@tailwind base;
|
|
3
|
+
@tailwind components;
|
|
4
|
+
@tailwind utilities;
|
|
5
|
+
@layer base {
|
|
6
|
+
:root {
|
|
7
|
+
--background: 0 0% 100%;
|
|
8
|
+
--foreground: 222.2 84% 4.9%;
|
|
9
|
+
--card: 0 0% 100%;
|
|
10
|
+
--card-foreground: 222.2 84% 4.9%;
|
|
11
|
+
--popover: 0 0% 100%;
|
|
12
|
+
--popover-foreground: 222.2 84% 4.9%;
|
|
13
|
+
--primary: 222.2 47.4% 11.2%;
|
|
14
|
+
--primary-foreground: 210 40% 98%;
|
|
15
|
+
--secondary: 210 40% 96.1%;
|
|
16
|
+
--secondary-foreground: 222.2 47.4% 11.2%;
|
|
17
|
+
--muted: 210 40% 96.1%;
|
|
18
|
+
--muted-foreground: 215.4 16.3% 46.9%;
|
|
19
|
+
--accent: 210 40% 96.1%;
|
|
20
|
+
--accent-foreground: 222.2 47.4% 11.2%;
|
|
21
|
+
--destructive: 0 84.2% 60.2%;
|
|
22
|
+
--destructive-foreground: 210 40% 98%;
|
|
23
|
+
--border: 214.3 31.8% 91.4%;
|
|
24
|
+
--input: 214.3 31.8% 91.4%;
|
|
25
|
+
--ring: 222.2 84% 4.9%;
|
|
26
|
+
--radius: 0.5rem;
|
|
27
|
+
}
|
|
28
|
+
.dark {
|
|
29
|
+
--background: 222.2 84% 4.9%;
|
|
30
|
+
--foreground: 210 40% 98%;
|
|
31
|
+
--card: 222.2 84% 4.9%;
|
|
32
|
+
--card-foreground: 210 40% 98%;
|
|
33
|
+
--popover: 222.2 84% 4.9%;
|
|
34
|
+
--popover-foreground: 210 40% 98%;
|
|
35
|
+
--primary: 210 40% 98%;
|
|
36
|
+
--primary-foreground: 222.2 47.4% 11.2%;
|
|
37
|
+
--secondary: 217.2 32.6% 17.5%;
|
|
38
|
+
--secondary-foreground: 210 40% 98%;
|
|
39
|
+
--muted: 217.2 32.6% 17.5%;
|
|
40
|
+
--muted-foreground: 215 20.2% 65.1%;
|
|
41
|
+
--accent: 217.2 32.6% 17.5%;
|
|
42
|
+
--accent-foreground: 210 40% 98%;
|
|
43
|
+
--destructive: 0 62.8% 30.6%;
|
|
44
|
+
--destructive-foreground: 210 40% 98%;
|
|
45
|
+
--border: 217.2 32.6% 17.5%;
|
|
46
|
+
--input: 217.2 32.6% 17.5%;
|
|
47
|
+
--ring: 212.7 26.8% 83.9%;
|
|
48
|
+
}
|
|
49
|
+
* {
|
|
50
|
+
border-color: hsl(var(--border));
|
|
51
|
+
}
|
|
47
52
|
}
|
|
48
53
|
/*# sourceMappingURL=index.css.map */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/styles.css"],"sourcesContent":[":root {\n
|
|
1
|
+
{"version":3,"sources":["../../src/client/styles.css"],"sourcesContent":["@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@layer base {\n :root {\n --background: 0 0% 100%;\n --foreground: 222.2 84% 4.9%;\n\n --card: 0 0% 100%;\n --card-foreground: 222.2 84% 4.9%;\n\n --popover: 0 0% 100%;\n --popover-foreground: 222.2 84% 4.9%;\n\n --primary: 222.2 47.4% 11.2%;\n --primary-foreground: 210 40% 98%;\n\n --secondary: 210 40% 96.1%;\n --secondary-foreground: 222.2 47.4% 11.2%;\n\n --muted: 210 40% 96.1%;\n --muted-foreground: 215.4 16.3% 46.9%;\n\n --accent: 210 40% 96.1%;\n --accent-foreground: 222.2 47.4% 11.2%;\n\n --destructive: 0 84.2% 60.2%;\n --destructive-foreground: 210 40% 98%;\n\n --border: 214.3 31.8% 91.4%;\n --input: 214.3 31.8% 91.4%;\n --ring: 222.2 84% 4.9%;\n\n --radius: 0.5rem;\n }\n\n .dark {\n --background: 222.2 84% 4.9%;\n --foreground: 210 40% 98%;\n\n --card: 222.2 84% 4.9%;\n --card-foreground: 210 40% 98%;\n\n --popover: 222.2 84% 4.9%;\n --popover-foreground: 210 40% 98%;\n\n --primary: 210 40% 98%;\n --primary-foreground: 222.2 47.4% 11.2%;\n\n --secondary: 217.2 32.6% 17.5%;\n --secondary-foreground: 210 40% 98%;\n\n --muted: 217.2 32.6% 17.5%;\n --muted-foreground: 215 20.2% 65.1%;\n\n --accent: 217.2 32.6% 17.5%;\n --accent-foreground: 210 40% 98%;\n\n --destructive: 0 62.8% 30.6%;\n --destructive-foreground: 210 40% 98%;\n\n --border: 217.2 32.6% 17.5%;\n --input: 217.2 32.6% 17.5%;\n --ring: 212.7 26.8% 83.9%;\n }\n\n * {\n border-color: hsl(var(--border));\n }\n}"],"mappings":";AAAA,UAAU;AACV,UAAU;AACV,UAAU;AAEV;AACI;AACI,kBAAc,EAAE,GAAG;AACnB,kBAAc,MAAM,IAAI;AAExB,YAAQ,EAAE,GAAG;AACb,uBAAmB,MAAM,IAAI;AAE7B,eAAW,EAAE,GAAG;AAChB,0BAAsB,MAAM,IAAI;AAEhC,eAAW,MAAM,MAAM;AACvB,0BAAsB,IAAI,IAAI;AAE9B,iBAAa,IAAI,IAAI;AACrB,4BAAwB,MAAM,MAAM;AAEpC,aAAS,IAAI,IAAI;AACjB,wBAAoB,MAAM,MAAM;AAEhC,cAAU,IAAI,IAAI;AAClB,yBAAqB,MAAM,MAAM;AAEjC,mBAAe,EAAE,MAAM;AACvB,8BAA0B,IAAI,IAAI;AAElC,cAAU,MAAM,MAAM;AACtB,aAAS,MAAM,MAAM;AACrB,YAAQ,MAAM,IAAI;AAElB,cAAU;AACd;AAEA,GAAC;AACG,kBAAc,MAAM,IAAI;AACxB,kBAAc,IAAI,IAAI;AAEtB,YAAQ,MAAM,IAAI;AAClB,uBAAmB,IAAI,IAAI;AAE3B,eAAW,MAAM,IAAI;AACrB,0BAAsB,IAAI,IAAI;AAE9B,eAAW,IAAI,IAAI;AACnB,0BAAsB,MAAM,MAAM;AAElC,iBAAa,MAAM,MAAM;AACzB,4BAAwB,IAAI,IAAI;AAEhC,aAAS,MAAM,MAAM;AACrB,wBAAoB,IAAI,MAAM;AAE9B,cAAU,MAAM,MAAM;AACtB,yBAAqB,IAAI,IAAI;AAE7B,mBAAe,EAAE,MAAM;AACvB,8BAA0B,IAAI,IAAI;AAElC,cAAU,MAAM,MAAM;AACtB,aAAS,MAAM,MAAM;AACrB,YAAQ,MAAM,MAAM;AACxB;AAEA;AACI,kBAAc,IAAI,IAAI;AAC1B;AACJ;","names":[]}
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { ReactNode } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
export {
|
|
2
|
+
import { n as TDrivePathItem, d as TDatabaseDrive, o as TDriveQuota, e as TDriveFile, f as TImageQuality, g as TImageFormat, k as TDriveAPIResponse, p as TDriveUploadState } from '../index-C4pw1JjP.js';
|
|
3
|
+
export { q as driveFileSchemaZod } from '../index-C4pw1JjP.js';
|
|
4
4
|
import 'zod';
|
|
5
5
|
|
|
6
6
|
type TDriveContext = {
|
|
@@ -47,6 +47,14 @@ type TDriveContext = {
|
|
|
47
47
|
};
|
|
48
48
|
selectedFileIds: string[];
|
|
49
49
|
setSelectedFileIds: React.Dispatch<React.SetStateAction<string[]>>;
|
|
50
|
+
createUrl: (driveFile: TDriveFile, options?: {
|
|
51
|
+
quality?: TImageQuality;
|
|
52
|
+
format?: TImageFormat;
|
|
53
|
+
}) => string;
|
|
54
|
+
createSrcSet: (driveFile: TDriveFile, format?: TImageFormat) => {
|
|
55
|
+
srcSet: string;
|
|
56
|
+
sizes: string;
|
|
57
|
+
};
|
|
50
58
|
navigateToFolder: (item: {
|
|
51
59
|
id: string | null;
|
|
52
60
|
name: string;
|
|
@@ -122,4 +130,4 @@ declare const DriveHeader: () => React.JSX.Element;
|
|
|
122
130
|
|
|
123
131
|
declare const DriveSidebar: () => React.JSX.Element;
|
|
124
132
|
|
|
125
|
-
export { DriveExplorer, DriveFileChooser, DriveHeader, DrivePathBar, DriveProvider, DriveSidebar, DriveStorageIndicator, DriveUpload, type TDriveContext, TDriveFile, useDrive, useUpload };
|
|
133
|
+
export { DriveExplorer, DriveFileChooser, DriveHeader, DrivePathBar, DriveProvider, DriveSidebar, DriveStorageIndicator, DriveUpload, type TDriveContext, TDriveFile, TImageFormat, TImageQuality, useDrive, useUpload };
|
package/dist/client/index.js
CHANGED
|
@@ -1,6 +1,75 @@
|
|
|
1
1
|
// src/client/context.tsx
|
|
2
2
|
import React, { createContext, useContext, useState, useCallback, useMemo } from "react";
|
|
3
|
+
|
|
4
|
+
// src/client/utils.tsx
|
|
5
|
+
import { clsx } from "clsx";
|
|
6
|
+
import { twMerge } from "tailwind-merge";
|
|
7
|
+
import { File, Folder, Image, Video, Music, FileText, FileCode, FileArchive } from "lucide-react";
|
|
3
8
|
import { jsx } from "react/jsx-runtime";
|
|
9
|
+
function cn(...inputs) {
|
|
10
|
+
return twMerge(clsx(inputs));
|
|
11
|
+
}
|
|
12
|
+
var formatBytes = (bytes, decimals = 2) => {
|
|
13
|
+
if (bytes === 0) return "0 Bytes";
|
|
14
|
+
const k = 1024;
|
|
15
|
+
const dm = decimals < 0 ? 0 : decimals;
|
|
16
|
+
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
|
17
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
18
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
|
|
19
|
+
};
|
|
20
|
+
var getFileIcon = (mime, isFolder, className = "w-6 h-6") => {
|
|
21
|
+
if (isFolder) return /* @__PURE__ */ jsx(Folder, { className: cn("text-blue-500 fill-blue-500/20", className) });
|
|
22
|
+
if (mime.startsWith("image/")) return /* @__PURE__ */ jsx(Image, { className: cn("text-purple-500", className) });
|
|
23
|
+
if (mime.startsWith("video/")) return /* @__PURE__ */ jsx(Video, { className: cn("text-red-500", className) });
|
|
24
|
+
if (mime.startsWith("audio/")) return /* @__PURE__ */ jsx(Music, { className: cn("text-yellow-500", className) });
|
|
25
|
+
if (mime === "application/pdf") return /* @__PURE__ */ jsx(FileText, { className: cn("text-orange-500", className) });
|
|
26
|
+
if (mime.includes("text") || mime.includes("document")) return /* @__PURE__ */ jsx(FileText, { className: cn("text-slate-500", className) });
|
|
27
|
+
if (mime.includes("zip") || mime.includes("compressed")) return /* @__PURE__ */ jsx(FileArchive, { className: cn("text-amber-500", className) });
|
|
28
|
+
if (mime.includes("javascript") || mime.includes("typescript") || mime.includes("json") || mime.includes("html") || mime.includes("css")) return /* @__PURE__ */ jsx(FileCode, { className: cn("text-green-500", className) });
|
|
29
|
+
return /* @__PURE__ */ jsx(File, { className: cn("text-gray-400", className) });
|
|
30
|
+
};
|
|
31
|
+
var matchesMimeFilter = (mime, isFolder, filter) => {
|
|
32
|
+
if (!filter) return true;
|
|
33
|
+
if (isFolder) return true;
|
|
34
|
+
if (filter === "*/*") return true;
|
|
35
|
+
const types = filter.split(",").map((t) => t.trim());
|
|
36
|
+
return types.some((type) => {
|
|
37
|
+
if (type === mime) return true;
|
|
38
|
+
if (type.endsWith("/*")) {
|
|
39
|
+
const prefix = type.slice(0, -2);
|
|
40
|
+
return mime.startsWith(`${prefix}/`);
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
var driveCreateUrl = (driveFile, apiEndpoint, options) => {
|
|
46
|
+
const params = new URLSearchParams({
|
|
47
|
+
action: "serve",
|
|
48
|
+
id: driveFile.id
|
|
49
|
+
});
|
|
50
|
+
if (options?.quality) params.set("q", options.quality);
|
|
51
|
+
if (options?.format) params.set("format", options.format);
|
|
52
|
+
return `${apiEndpoint}?${params.toString()}`;
|
|
53
|
+
};
|
|
54
|
+
var driveCreateSrcSet = (driveFile, apiEndpoint, format = "webp") => {
|
|
55
|
+
const qualities = ["ultralow", "low", "medium", "high"];
|
|
56
|
+
const qualityWidthMap = {
|
|
57
|
+
ultralow: 200,
|
|
58
|
+
low: 400,
|
|
59
|
+
medium: 800,
|
|
60
|
+
high: 1200,
|
|
61
|
+
normal: 1600
|
|
62
|
+
};
|
|
63
|
+
const srcSet = qualities.map((quality) => {
|
|
64
|
+
const url = driveCreateUrl(driveFile, apiEndpoint, { quality, format });
|
|
65
|
+
return `${url} ${qualityWidthMap[quality]}w`;
|
|
66
|
+
}).join(", ");
|
|
67
|
+
const sizes = "(max-width: 320px) 200px, (max-width: 480px) 400px, (max-width: 768px) 800px, 1200px";
|
|
68
|
+
return { srcSet, sizes };
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// src/client/context.tsx
|
|
72
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
4
73
|
var DriveContext = createContext(null);
|
|
5
74
|
var DriveProvider = (props) => {
|
|
6
75
|
const { children, apiEndpoint, initialActiveAccountId = null, initialSelectionMode = { type: "SINGLE" }, defaultSelectedFileIds = [] } = props;
|
|
@@ -216,7 +285,13 @@ var DriveProvider = (props) => {
|
|
|
216
285
|
React.useEffect(() => {
|
|
217
286
|
refreshAccounts();
|
|
218
287
|
}, [refreshAccounts]);
|
|
219
|
-
|
|
288
|
+
const createUrl = useCallback((driveFile, options) => {
|
|
289
|
+
return driveCreateUrl(driveFile, apiEndpoint, options);
|
|
290
|
+
}, [apiEndpoint]);
|
|
291
|
+
const createSrcSet = useCallback((driveFile, format) => {
|
|
292
|
+
return driveCreateSrcSet(driveFile, apiEndpoint, format);
|
|
293
|
+
}, [apiEndpoint]);
|
|
294
|
+
return /* @__PURE__ */ jsx2(DriveContext.Provider, { value: {
|
|
220
295
|
apiEndpoint,
|
|
221
296
|
currentFolderId,
|
|
222
297
|
path,
|
|
@@ -247,6 +322,8 @@ var DriveProvider = (props) => {
|
|
|
247
322
|
selectionMode,
|
|
248
323
|
selectedFileIds,
|
|
249
324
|
setSelectedFileIds,
|
|
325
|
+
createUrl,
|
|
326
|
+
createSrcSet,
|
|
250
327
|
navigateToFolder,
|
|
251
328
|
navigateUp,
|
|
252
329
|
refreshItems,
|
|
@@ -415,48 +492,6 @@ var useUpload = (apiEndpoint, activeAccountId, onUploadComplete) => {
|
|
|
415
492
|
// src/client/file-chooser.tsx
|
|
416
493
|
import { useState as useState6, useCallback as useCallback4, useMemo as useMemo3, useEffect as useEffect5 } from "react";
|
|
417
494
|
|
|
418
|
-
// src/client/utils.tsx
|
|
419
|
-
import { clsx } from "clsx";
|
|
420
|
-
import { twMerge } from "tailwind-merge";
|
|
421
|
-
import { File, Folder, Image, Video, Music, FileText, FileCode, FileArchive } from "lucide-react";
|
|
422
|
-
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
423
|
-
function cn(...inputs) {
|
|
424
|
-
return twMerge(clsx(inputs));
|
|
425
|
-
}
|
|
426
|
-
var formatBytes = (bytes, decimals = 2) => {
|
|
427
|
-
if (bytes === 0) return "0 Bytes";
|
|
428
|
-
const k = 1024;
|
|
429
|
-
const dm = decimals < 0 ? 0 : decimals;
|
|
430
|
-
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
|
431
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
432
|
-
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
|
|
433
|
-
};
|
|
434
|
-
var getFileIcon = (mime, isFolder, className = "w-6 h-6") => {
|
|
435
|
-
if (isFolder) return /* @__PURE__ */ jsx2(Folder, { className: cn("text-blue-500 fill-blue-500/20", className) });
|
|
436
|
-
if (mime.startsWith("image/")) return /* @__PURE__ */ jsx2(Image, { className: cn("text-purple-500", className) });
|
|
437
|
-
if (mime.startsWith("video/")) return /* @__PURE__ */ jsx2(Video, { className: cn("text-red-500", className) });
|
|
438
|
-
if (mime.startsWith("audio/")) return /* @__PURE__ */ jsx2(Music, { className: cn("text-yellow-500", className) });
|
|
439
|
-
if (mime === "application/pdf") return /* @__PURE__ */ jsx2(FileText, { className: cn("text-orange-500", className) });
|
|
440
|
-
if (mime.includes("text") || mime.includes("document")) return /* @__PURE__ */ jsx2(FileText, { className: cn("text-slate-500", className) });
|
|
441
|
-
if (mime.includes("zip") || mime.includes("compressed")) return /* @__PURE__ */ jsx2(FileArchive, { className: cn("text-amber-500", className) });
|
|
442
|
-
if (mime.includes("javascript") || mime.includes("typescript") || mime.includes("json") || mime.includes("html") || mime.includes("css")) return /* @__PURE__ */ jsx2(FileCode, { className: cn("text-green-500", className) });
|
|
443
|
-
return /* @__PURE__ */ jsx2(File, { className: cn("text-gray-400", className) });
|
|
444
|
-
};
|
|
445
|
-
var matchesMimeFilter = (mime, isFolder, filter) => {
|
|
446
|
-
if (!filter) return true;
|
|
447
|
-
if (isFolder) return true;
|
|
448
|
-
if (filter === "*/*") return true;
|
|
449
|
-
const types = filter.split(",").map((t) => t.trim());
|
|
450
|
-
return types.some((type) => {
|
|
451
|
-
if (type === mime) return true;
|
|
452
|
-
if (type.endsWith("/*")) {
|
|
453
|
-
const prefix = type.slice(0, -2);
|
|
454
|
-
return mime.startsWith(`${prefix}/`);
|
|
455
|
-
}
|
|
456
|
-
return false;
|
|
457
|
-
});
|
|
458
|
-
};
|
|
459
|
-
|
|
460
495
|
// src/client/components/drive/explorer.tsx
|
|
461
496
|
import React7, { useMemo as useMemo2, useEffect as useEffect4, useRef as useRef3 } from "react";
|
|
462
497
|
import { Folder as Folder2, Loader2 as Loader22, RotateCcw, ChevronRight as ChevronRight3 } from "lucide-react";
|