@fluxfiles/react 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +202 -0
- package/dist/index.d.ts +202 -0
- package/dist/index.js +305 -0
- package/dist/index.mjs +276 -0
- package/package.json +58 -0
- package/src/FluxFiles.tsx +91 -0
- package/src/FluxFilesModal.tsx +134 -0
- package/src/index.ts +17 -0
- package/src/types.ts +124 -0
- package/src/useFluxFiles.ts +138 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import React$1 from 'react';
|
|
2
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
/** A file or directory entry returned by FluxFiles. */
|
|
5
|
+
interface FluxFile {
|
|
6
|
+
path: string;
|
|
7
|
+
basename: string;
|
|
8
|
+
type: 'file' | 'dir';
|
|
9
|
+
size?: number;
|
|
10
|
+
mime?: string;
|
|
11
|
+
modified?: number;
|
|
12
|
+
url?: string;
|
|
13
|
+
title?: string;
|
|
14
|
+
alt_text?: string;
|
|
15
|
+
caption?: string;
|
|
16
|
+
hash?: string;
|
|
17
|
+
variants?: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
/** Event payload dispatched by the file manager iframe. */
|
|
20
|
+
interface FluxEvent {
|
|
21
|
+
action: string;
|
|
22
|
+
disk?: string;
|
|
23
|
+
path?: string;
|
|
24
|
+
file?: FluxFile;
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
}
|
|
27
|
+
/** Configuration for the FluxFiles component. */
|
|
28
|
+
interface FluxFilesConfig {
|
|
29
|
+
/** Base URL of the FluxFiles API. */
|
|
30
|
+
endpoint: string;
|
|
31
|
+
/** JWT token for authentication. */
|
|
32
|
+
token: string;
|
|
33
|
+
/** Storage disk to use. */
|
|
34
|
+
disk?: string;
|
|
35
|
+
/** Display mode: "picker" selects a file, "browser" is free-browse. */
|
|
36
|
+
mode?: 'picker' | 'browser';
|
|
37
|
+
/** Filter displayed file types (e.g. ["image/*", ".pdf"]). */
|
|
38
|
+
allowedTypes?: string[] | null;
|
|
39
|
+
/** Max file size filter in bytes. */
|
|
40
|
+
maxSize?: number | null;
|
|
41
|
+
/** Locale code (e.g. "en", "vi", "ar"). */
|
|
42
|
+
locale?: string | null;
|
|
43
|
+
}
|
|
44
|
+
/** Props for the <FluxFiles /> embedded component. */
|
|
45
|
+
interface FluxFilesProps extends FluxFilesConfig {
|
|
46
|
+
/** Container width. */
|
|
47
|
+
width?: string | number;
|
|
48
|
+
/** Container height. */
|
|
49
|
+
height?: string | number;
|
|
50
|
+
/** CSS class for the wrapper div. */
|
|
51
|
+
className?: string;
|
|
52
|
+
/** Inline styles for the wrapper div. */
|
|
53
|
+
style?: React.CSSProperties;
|
|
54
|
+
/** Fired when a file is selected (picker mode). */
|
|
55
|
+
onSelect?: (file: FluxFile) => void;
|
|
56
|
+
/** Fired when the file manager signals a close. */
|
|
57
|
+
onClose?: () => void;
|
|
58
|
+
/** Fired when the iframe is ready. */
|
|
59
|
+
onReady?: () => void;
|
|
60
|
+
/** Fired on file operations (upload, delete, move, etc.). */
|
|
61
|
+
onEvent?: (event: FluxEvent) => void;
|
|
62
|
+
}
|
|
63
|
+
/** Props for the <FluxFilesModal /> component. */
|
|
64
|
+
interface FluxFilesModalProps extends FluxFilesConfig {
|
|
65
|
+
/** Whether the modal is open. */
|
|
66
|
+
open: boolean;
|
|
67
|
+
/** Fired when a file is selected. */
|
|
68
|
+
onSelect?: (file: FluxFile) => void;
|
|
69
|
+
/** Fired when the modal should close (overlay click, escape, or FM_CLOSE). */
|
|
70
|
+
onClose?: () => void;
|
|
71
|
+
/** Fired when the iframe is ready. */
|
|
72
|
+
onReady?: () => void;
|
|
73
|
+
/** Fired on file operations. */
|
|
74
|
+
onEvent?: (event: FluxEvent) => void;
|
|
75
|
+
/** CSS class for the overlay. */
|
|
76
|
+
overlayClassName?: string;
|
|
77
|
+
/** CSS class for the modal content. */
|
|
78
|
+
modalClassName?: string;
|
|
79
|
+
}
|
|
80
|
+
/** Commands that can be sent to the file manager. */
|
|
81
|
+
type FluxCommand = {
|
|
82
|
+
action: 'navigate';
|
|
83
|
+
path: string;
|
|
84
|
+
} | {
|
|
85
|
+
action: 'setDisk';
|
|
86
|
+
disk: string;
|
|
87
|
+
} | {
|
|
88
|
+
action: 'refresh';
|
|
89
|
+
} | {
|
|
90
|
+
action: 'search';
|
|
91
|
+
q: string;
|
|
92
|
+
} | {
|
|
93
|
+
action: 'crossCopy';
|
|
94
|
+
dst_disk: string;
|
|
95
|
+
dst_path?: string;
|
|
96
|
+
} | {
|
|
97
|
+
action: 'crossMove';
|
|
98
|
+
dst_disk: string;
|
|
99
|
+
dst_path?: string;
|
|
100
|
+
} | {
|
|
101
|
+
action: 'crop';
|
|
102
|
+
x: number;
|
|
103
|
+
y: number;
|
|
104
|
+
width: number;
|
|
105
|
+
height: number;
|
|
106
|
+
save_path?: string;
|
|
107
|
+
} | {
|
|
108
|
+
action: 'aiTag';
|
|
109
|
+
};
|
|
110
|
+
/** Return type of useFluxFiles hook. */
|
|
111
|
+
interface FluxFilesHandle {
|
|
112
|
+
/** Send a command to the iframe. */
|
|
113
|
+
command: (action: string, data?: Record<string, unknown>) => void;
|
|
114
|
+
/** Navigate to a path. */
|
|
115
|
+
navigate: (path: string) => void;
|
|
116
|
+
/** Switch disk. */
|
|
117
|
+
setDisk: (disk: string) => void;
|
|
118
|
+
/** Refresh the file list. */
|
|
119
|
+
refresh: () => void;
|
|
120
|
+
/** Search files. */
|
|
121
|
+
search: (q: string) => void;
|
|
122
|
+
/** Copy selected files to another disk. */
|
|
123
|
+
crossCopy: (dstDisk: string, dstPath?: string) => void;
|
|
124
|
+
/** Move selected files to another disk. */
|
|
125
|
+
crossMove: (dstDisk: string, dstPath?: string) => void;
|
|
126
|
+
/** Crop the currently selected image. */
|
|
127
|
+
crop: (x: number, y: number, width: number, height: number, savePath?: string) => void;
|
|
128
|
+
/** Trigger AI tagging on the currently selected image. */
|
|
129
|
+
aiTag: () => void;
|
|
130
|
+
/** Whether the iframe has reported ready. */
|
|
131
|
+
ready: boolean;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Embedded FluxFiles file manager component.
|
|
136
|
+
*
|
|
137
|
+
* Renders an iframe inside a container div. Use `ref` to access command methods.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```tsx
|
|
141
|
+
* const ref = useRef<FluxFilesHandle>(null);
|
|
142
|
+
*
|
|
143
|
+
* <FluxFiles
|
|
144
|
+
* ref={ref}
|
|
145
|
+
* endpoint="https://files.example.com"
|
|
146
|
+
* token={jwt}
|
|
147
|
+
* disk="local"
|
|
148
|
+
* onSelect={(file) => console.log(file)}
|
|
149
|
+
* height="600px"
|
|
150
|
+
* />
|
|
151
|
+
*
|
|
152
|
+
* // Programmatic control:
|
|
153
|
+
* ref.current?.navigate('/uploads');
|
|
154
|
+
* ref.current?.refresh();
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
declare const FluxFiles: React$1.ForwardRefExoticComponent<FluxFilesProps & React$1.RefAttributes<FluxFilesHandle>>;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Modal wrapper for FluxFiles.
|
|
161
|
+
*
|
|
162
|
+
* Renders a fullscreen overlay with the file manager when `open` is true.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```tsx
|
|
166
|
+
* const [open, setOpen] = useState(false);
|
|
167
|
+
*
|
|
168
|
+
* <button onClick={() => setOpen(true)}>Pick file</button>
|
|
169
|
+
*
|
|
170
|
+
* <FluxFilesModal
|
|
171
|
+
* open={open}
|
|
172
|
+
* endpoint="https://files.example.com"
|
|
173
|
+
* token={jwt}
|
|
174
|
+
* onSelect={(file) => {
|
|
175
|
+
* console.log(file);
|
|
176
|
+
* setOpen(false);
|
|
177
|
+
* }}
|
|
178
|
+
* onClose={() => setOpen(false)}
|
|
179
|
+
* />
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
declare function FluxFilesModal({ open, endpoint, token, disk, mode, allowedTypes, maxSize, onSelect, onClose, onReady, onEvent, overlayClassName, modalClassName, }: FluxFilesModalProps): react_jsx_runtime.JSX.Element | null;
|
|
183
|
+
|
|
184
|
+
interface UseFluxFilesOptions extends FluxFilesConfig {
|
|
185
|
+
onSelect?: (file: FluxFile) => void;
|
|
186
|
+
onClose?: () => void;
|
|
187
|
+
onReady?: () => void;
|
|
188
|
+
onEvent?: (event: FluxEvent) => void;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Low-level hook that manages the postMessage bridge to a FluxFiles iframe.
|
|
192
|
+
*
|
|
193
|
+
* Returns a ref callback to attach to the iframe element, plus command helpers.
|
|
194
|
+
*/
|
|
195
|
+
declare function useFluxFiles(options: UseFluxFilesOptions): FluxFilesHandle & {
|
|
196
|
+
/** Ref callback — attach to the <iframe> element. */
|
|
197
|
+
iframeRef: (el: HTMLIFrameElement | null) => void;
|
|
198
|
+
/** The iframe src URL. */
|
|
199
|
+
iframeSrc: string;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
export { type FluxCommand, type FluxEvent, type FluxFile, FluxFiles, type FluxFilesConfig, type FluxFilesHandle, FluxFilesModal, type FluxFilesModalProps, type FluxFilesProps, useFluxFiles };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import React$1 from 'react';
|
|
2
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
/** A file or directory entry returned by FluxFiles. */
|
|
5
|
+
interface FluxFile {
|
|
6
|
+
path: string;
|
|
7
|
+
basename: string;
|
|
8
|
+
type: 'file' | 'dir';
|
|
9
|
+
size?: number;
|
|
10
|
+
mime?: string;
|
|
11
|
+
modified?: number;
|
|
12
|
+
url?: string;
|
|
13
|
+
title?: string;
|
|
14
|
+
alt_text?: string;
|
|
15
|
+
caption?: string;
|
|
16
|
+
hash?: string;
|
|
17
|
+
variants?: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
/** Event payload dispatched by the file manager iframe. */
|
|
20
|
+
interface FluxEvent {
|
|
21
|
+
action: string;
|
|
22
|
+
disk?: string;
|
|
23
|
+
path?: string;
|
|
24
|
+
file?: FluxFile;
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
}
|
|
27
|
+
/** Configuration for the FluxFiles component. */
|
|
28
|
+
interface FluxFilesConfig {
|
|
29
|
+
/** Base URL of the FluxFiles API. */
|
|
30
|
+
endpoint: string;
|
|
31
|
+
/** JWT token for authentication. */
|
|
32
|
+
token: string;
|
|
33
|
+
/** Storage disk to use. */
|
|
34
|
+
disk?: string;
|
|
35
|
+
/** Display mode: "picker" selects a file, "browser" is free-browse. */
|
|
36
|
+
mode?: 'picker' | 'browser';
|
|
37
|
+
/** Filter displayed file types (e.g. ["image/*", ".pdf"]). */
|
|
38
|
+
allowedTypes?: string[] | null;
|
|
39
|
+
/** Max file size filter in bytes. */
|
|
40
|
+
maxSize?: number | null;
|
|
41
|
+
/** Locale code (e.g. "en", "vi", "ar"). */
|
|
42
|
+
locale?: string | null;
|
|
43
|
+
}
|
|
44
|
+
/** Props for the <FluxFiles /> embedded component. */
|
|
45
|
+
interface FluxFilesProps extends FluxFilesConfig {
|
|
46
|
+
/** Container width. */
|
|
47
|
+
width?: string | number;
|
|
48
|
+
/** Container height. */
|
|
49
|
+
height?: string | number;
|
|
50
|
+
/** CSS class for the wrapper div. */
|
|
51
|
+
className?: string;
|
|
52
|
+
/** Inline styles for the wrapper div. */
|
|
53
|
+
style?: React.CSSProperties;
|
|
54
|
+
/** Fired when a file is selected (picker mode). */
|
|
55
|
+
onSelect?: (file: FluxFile) => void;
|
|
56
|
+
/** Fired when the file manager signals a close. */
|
|
57
|
+
onClose?: () => void;
|
|
58
|
+
/** Fired when the iframe is ready. */
|
|
59
|
+
onReady?: () => void;
|
|
60
|
+
/** Fired on file operations (upload, delete, move, etc.). */
|
|
61
|
+
onEvent?: (event: FluxEvent) => void;
|
|
62
|
+
}
|
|
63
|
+
/** Props for the <FluxFilesModal /> component. */
|
|
64
|
+
interface FluxFilesModalProps extends FluxFilesConfig {
|
|
65
|
+
/** Whether the modal is open. */
|
|
66
|
+
open: boolean;
|
|
67
|
+
/** Fired when a file is selected. */
|
|
68
|
+
onSelect?: (file: FluxFile) => void;
|
|
69
|
+
/** Fired when the modal should close (overlay click, escape, or FM_CLOSE). */
|
|
70
|
+
onClose?: () => void;
|
|
71
|
+
/** Fired when the iframe is ready. */
|
|
72
|
+
onReady?: () => void;
|
|
73
|
+
/** Fired on file operations. */
|
|
74
|
+
onEvent?: (event: FluxEvent) => void;
|
|
75
|
+
/** CSS class for the overlay. */
|
|
76
|
+
overlayClassName?: string;
|
|
77
|
+
/** CSS class for the modal content. */
|
|
78
|
+
modalClassName?: string;
|
|
79
|
+
}
|
|
80
|
+
/** Commands that can be sent to the file manager. */
|
|
81
|
+
type FluxCommand = {
|
|
82
|
+
action: 'navigate';
|
|
83
|
+
path: string;
|
|
84
|
+
} | {
|
|
85
|
+
action: 'setDisk';
|
|
86
|
+
disk: string;
|
|
87
|
+
} | {
|
|
88
|
+
action: 'refresh';
|
|
89
|
+
} | {
|
|
90
|
+
action: 'search';
|
|
91
|
+
q: string;
|
|
92
|
+
} | {
|
|
93
|
+
action: 'crossCopy';
|
|
94
|
+
dst_disk: string;
|
|
95
|
+
dst_path?: string;
|
|
96
|
+
} | {
|
|
97
|
+
action: 'crossMove';
|
|
98
|
+
dst_disk: string;
|
|
99
|
+
dst_path?: string;
|
|
100
|
+
} | {
|
|
101
|
+
action: 'crop';
|
|
102
|
+
x: number;
|
|
103
|
+
y: number;
|
|
104
|
+
width: number;
|
|
105
|
+
height: number;
|
|
106
|
+
save_path?: string;
|
|
107
|
+
} | {
|
|
108
|
+
action: 'aiTag';
|
|
109
|
+
};
|
|
110
|
+
/** Return type of useFluxFiles hook. */
|
|
111
|
+
interface FluxFilesHandle {
|
|
112
|
+
/** Send a command to the iframe. */
|
|
113
|
+
command: (action: string, data?: Record<string, unknown>) => void;
|
|
114
|
+
/** Navigate to a path. */
|
|
115
|
+
navigate: (path: string) => void;
|
|
116
|
+
/** Switch disk. */
|
|
117
|
+
setDisk: (disk: string) => void;
|
|
118
|
+
/** Refresh the file list. */
|
|
119
|
+
refresh: () => void;
|
|
120
|
+
/** Search files. */
|
|
121
|
+
search: (q: string) => void;
|
|
122
|
+
/** Copy selected files to another disk. */
|
|
123
|
+
crossCopy: (dstDisk: string, dstPath?: string) => void;
|
|
124
|
+
/** Move selected files to another disk. */
|
|
125
|
+
crossMove: (dstDisk: string, dstPath?: string) => void;
|
|
126
|
+
/** Crop the currently selected image. */
|
|
127
|
+
crop: (x: number, y: number, width: number, height: number, savePath?: string) => void;
|
|
128
|
+
/** Trigger AI tagging on the currently selected image. */
|
|
129
|
+
aiTag: () => void;
|
|
130
|
+
/** Whether the iframe has reported ready. */
|
|
131
|
+
ready: boolean;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Embedded FluxFiles file manager component.
|
|
136
|
+
*
|
|
137
|
+
* Renders an iframe inside a container div. Use `ref` to access command methods.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```tsx
|
|
141
|
+
* const ref = useRef<FluxFilesHandle>(null);
|
|
142
|
+
*
|
|
143
|
+
* <FluxFiles
|
|
144
|
+
* ref={ref}
|
|
145
|
+
* endpoint="https://files.example.com"
|
|
146
|
+
* token={jwt}
|
|
147
|
+
* disk="local"
|
|
148
|
+
* onSelect={(file) => console.log(file)}
|
|
149
|
+
* height="600px"
|
|
150
|
+
* />
|
|
151
|
+
*
|
|
152
|
+
* // Programmatic control:
|
|
153
|
+
* ref.current?.navigate('/uploads');
|
|
154
|
+
* ref.current?.refresh();
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
declare const FluxFiles: React$1.ForwardRefExoticComponent<FluxFilesProps & React$1.RefAttributes<FluxFilesHandle>>;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Modal wrapper for FluxFiles.
|
|
161
|
+
*
|
|
162
|
+
* Renders a fullscreen overlay with the file manager when `open` is true.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```tsx
|
|
166
|
+
* const [open, setOpen] = useState(false);
|
|
167
|
+
*
|
|
168
|
+
* <button onClick={() => setOpen(true)}>Pick file</button>
|
|
169
|
+
*
|
|
170
|
+
* <FluxFilesModal
|
|
171
|
+
* open={open}
|
|
172
|
+
* endpoint="https://files.example.com"
|
|
173
|
+
* token={jwt}
|
|
174
|
+
* onSelect={(file) => {
|
|
175
|
+
* console.log(file);
|
|
176
|
+
* setOpen(false);
|
|
177
|
+
* }}
|
|
178
|
+
* onClose={() => setOpen(false)}
|
|
179
|
+
* />
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
declare function FluxFilesModal({ open, endpoint, token, disk, mode, allowedTypes, maxSize, onSelect, onClose, onReady, onEvent, overlayClassName, modalClassName, }: FluxFilesModalProps): react_jsx_runtime.JSX.Element | null;
|
|
183
|
+
|
|
184
|
+
interface UseFluxFilesOptions extends FluxFilesConfig {
|
|
185
|
+
onSelect?: (file: FluxFile) => void;
|
|
186
|
+
onClose?: () => void;
|
|
187
|
+
onReady?: () => void;
|
|
188
|
+
onEvent?: (event: FluxEvent) => void;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Low-level hook that manages the postMessage bridge to a FluxFiles iframe.
|
|
192
|
+
*
|
|
193
|
+
* Returns a ref callback to attach to the iframe element, plus command helpers.
|
|
194
|
+
*/
|
|
195
|
+
declare function useFluxFiles(options: UseFluxFilesOptions): FluxFilesHandle & {
|
|
196
|
+
/** Ref callback — attach to the <iframe> element. */
|
|
197
|
+
iframeRef: (el: HTMLIFrameElement | null) => void;
|
|
198
|
+
/** The iframe src URL. */
|
|
199
|
+
iframeSrc: string;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
export { type FluxCommand, type FluxEvent, type FluxFile, FluxFiles, type FluxFilesConfig, type FluxFilesHandle, FluxFilesModal, type FluxFilesModalProps, type FluxFilesProps, useFluxFiles };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
FluxFiles: () => FluxFiles,
|
|
24
|
+
FluxFilesModal: () => FluxFilesModal,
|
|
25
|
+
useFluxFiles: () => useFluxFiles
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
|
|
29
|
+
// src/FluxFiles.tsx
|
|
30
|
+
var import_react2 = require("react");
|
|
31
|
+
|
|
32
|
+
// src/useFluxFiles.ts
|
|
33
|
+
var import_react = require("react");
|
|
34
|
+
var SOURCE = "fluxfiles";
|
|
35
|
+
var VERSION = 1;
|
|
36
|
+
function uid() {
|
|
37
|
+
return "ff-" + Math.random().toString(36).slice(2, 11) + Date.now().toString(36);
|
|
38
|
+
}
|
|
39
|
+
function useFluxFiles(options) {
|
|
40
|
+
const iframeElRef = (0, import_react.useRef)(null);
|
|
41
|
+
const [ready, setReady] = (0, import_react.useState)(false);
|
|
42
|
+
const optionsRef = (0, import_react.useRef)(options);
|
|
43
|
+
optionsRef.current = options;
|
|
44
|
+
const endpoint = (options.endpoint || "").replace(/\/+$/, "");
|
|
45
|
+
const iframeSrc = endpoint + "/public/index.html";
|
|
46
|
+
const post = (0, import_react.useCallback)((type, payload = {}) => {
|
|
47
|
+
const el = iframeElRef.current;
|
|
48
|
+
if (!el?.contentWindow) return;
|
|
49
|
+
el.contentWindow.postMessage(
|
|
50
|
+
{ source: SOURCE, type, v: VERSION, id: uid(), payload },
|
|
51
|
+
"*"
|
|
52
|
+
);
|
|
53
|
+
}, []);
|
|
54
|
+
const sendConfig = (0, import_react.useCallback)(() => {
|
|
55
|
+
const opts = optionsRef.current;
|
|
56
|
+
post("FM_CONFIG", {
|
|
57
|
+
disk: opts.disk || "local",
|
|
58
|
+
token: opts.token || "",
|
|
59
|
+
mode: opts.mode || "picker",
|
|
60
|
+
allowedTypes: opts.allowedTypes || null,
|
|
61
|
+
maxSize: opts.maxSize || null,
|
|
62
|
+
endpoint: opts.endpoint || "",
|
|
63
|
+
locale: opts.locale || null
|
|
64
|
+
});
|
|
65
|
+
}, [post]);
|
|
66
|
+
(0, import_react.useEffect)(() => {
|
|
67
|
+
function onMessage(e) {
|
|
68
|
+
const msg = e.data;
|
|
69
|
+
if (!msg || msg.source !== SOURCE) return;
|
|
70
|
+
const opts = optionsRef.current;
|
|
71
|
+
switch (msg.type) {
|
|
72
|
+
case "FM_READY":
|
|
73
|
+
setReady(true);
|
|
74
|
+
sendConfig();
|
|
75
|
+
opts.onReady?.();
|
|
76
|
+
break;
|
|
77
|
+
case "FM_SELECT":
|
|
78
|
+
opts.onSelect?.(msg.payload);
|
|
79
|
+
break;
|
|
80
|
+
case "FM_EVENT":
|
|
81
|
+
opts.onEvent?.(msg.payload);
|
|
82
|
+
break;
|
|
83
|
+
case "FM_CLOSE":
|
|
84
|
+
opts.onClose?.();
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
window.addEventListener("message", onMessage);
|
|
89
|
+
return () => {
|
|
90
|
+
window.removeEventListener("message", onMessage);
|
|
91
|
+
};
|
|
92
|
+
}, [sendConfig]);
|
|
93
|
+
(0, import_react.useEffect)(() => {
|
|
94
|
+
if (ready) {
|
|
95
|
+
sendConfig();
|
|
96
|
+
}
|
|
97
|
+
}, [options.token, options.disk, options.mode, options.locale, ready, sendConfig]);
|
|
98
|
+
const command = (0, import_react.useCallback)(
|
|
99
|
+
(action, data = {}) => {
|
|
100
|
+
post("FM_COMMAND", { action, ...data });
|
|
101
|
+
},
|
|
102
|
+
[post]
|
|
103
|
+
);
|
|
104
|
+
const navigate = (0, import_react.useCallback)((path) => command("navigate", { path }), [command]);
|
|
105
|
+
const setDisk = (0, import_react.useCallback)((disk) => command("setDisk", { disk }), [command]);
|
|
106
|
+
const refresh = (0, import_react.useCallback)(() => command("refresh"), [command]);
|
|
107
|
+
const search = (0, import_react.useCallback)((q) => command("search", { q }), [command]);
|
|
108
|
+
const crossCopy = (0, import_react.useCallback)((dstDisk, dstPath) => command("crossCopy", { dst_disk: dstDisk, dst_path: dstPath || "" }), [command]);
|
|
109
|
+
const crossMove = (0, import_react.useCallback)((dstDisk, dstPath) => command("crossMove", { dst_disk: dstDisk, dst_path: dstPath || "" }), [command]);
|
|
110
|
+
const crop = (0, import_react.useCallback)((x, y, width, height, savePath) => command("crop", { x, y, width, height, save_path: savePath || "" }), [command]);
|
|
111
|
+
const aiTag = (0, import_react.useCallback)(() => command("aiTag"), [command]);
|
|
112
|
+
const iframeRef = (0, import_react.useCallback)((el) => {
|
|
113
|
+
iframeElRef.current = el;
|
|
114
|
+
if (!el) {
|
|
115
|
+
setReady(false);
|
|
116
|
+
}
|
|
117
|
+
}, []);
|
|
118
|
+
return {
|
|
119
|
+
iframeRef,
|
|
120
|
+
iframeSrc,
|
|
121
|
+
ready,
|
|
122
|
+
command,
|
|
123
|
+
navigate,
|
|
124
|
+
setDisk,
|
|
125
|
+
refresh,
|
|
126
|
+
search,
|
|
127
|
+
crossCopy,
|
|
128
|
+
crossMove,
|
|
129
|
+
crop,
|
|
130
|
+
aiTag
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/FluxFiles.tsx
|
|
135
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
136
|
+
var FluxFiles = (0, import_react2.forwardRef)(
|
|
137
|
+
function FluxFiles2(props, ref) {
|
|
138
|
+
const {
|
|
139
|
+
endpoint,
|
|
140
|
+
token,
|
|
141
|
+
disk,
|
|
142
|
+
mode,
|
|
143
|
+
allowedTypes,
|
|
144
|
+
maxSize,
|
|
145
|
+
width = "100%",
|
|
146
|
+
height = "600px",
|
|
147
|
+
className,
|
|
148
|
+
style,
|
|
149
|
+
onSelect,
|
|
150
|
+
onClose,
|
|
151
|
+
onReady,
|
|
152
|
+
onEvent
|
|
153
|
+
} = props;
|
|
154
|
+
const handle = useFluxFiles({
|
|
155
|
+
endpoint,
|
|
156
|
+
token,
|
|
157
|
+
disk,
|
|
158
|
+
mode,
|
|
159
|
+
allowedTypes,
|
|
160
|
+
maxSize,
|
|
161
|
+
onSelect,
|
|
162
|
+
onClose,
|
|
163
|
+
onReady,
|
|
164
|
+
onEvent
|
|
165
|
+
});
|
|
166
|
+
(0, import_react2.useImperativeHandle)(ref, () => ({
|
|
167
|
+
command: handle.command,
|
|
168
|
+
navigate: handle.navigate,
|
|
169
|
+
setDisk: handle.setDisk,
|
|
170
|
+
refresh: handle.refresh,
|
|
171
|
+
search: handle.search,
|
|
172
|
+
crossCopy: handle.crossCopy,
|
|
173
|
+
crossMove: handle.crossMove,
|
|
174
|
+
crop: handle.crop,
|
|
175
|
+
aiTag: handle.aiTag,
|
|
176
|
+
ready: handle.ready
|
|
177
|
+
}), [handle]);
|
|
178
|
+
const containerStyle = {
|
|
179
|
+
width: typeof width === "number" ? `${width}px` : width,
|
|
180
|
+
height: typeof height === "number" ? `${height}px` : height,
|
|
181
|
+
...style
|
|
182
|
+
};
|
|
183
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, style: containerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
184
|
+
"iframe",
|
|
185
|
+
{
|
|
186
|
+
ref: handle.iframeRef,
|
|
187
|
+
src: handle.iframeSrc,
|
|
188
|
+
style: { width: "100%", height: "100%", border: "none" },
|
|
189
|
+
allow: "clipboard-write",
|
|
190
|
+
title: "FluxFiles File Manager"
|
|
191
|
+
}
|
|
192
|
+
) });
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
// src/FluxFilesModal.tsx
|
|
197
|
+
var import_react3 = require("react");
|
|
198
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
199
|
+
var defaultOverlayStyle = {
|
|
200
|
+
position: "fixed",
|
|
201
|
+
inset: 0,
|
|
202
|
+
background: "rgba(0, 0, 0, 0.5)",
|
|
203
|
+
zIndex: 99999,
|
|
204
|
+
display: "flex",
|
|
205
|
+
alignItems: "center",
|
|
206
|
+
justifyContent: "center"
|
|
207
|
+
};
|
|
208
|
+
var defaultModalStyle = {
|
|
209
|
+
width: "90vw",
|
|
210
|
+
maxWidth: "1200px",
|
|
211
|
+
height: "85vh",
|
|
212
|
+
background: "#fff",
|
|
213
|
+
borderRadius: "8px",
|
|
214
|
+
overflow: "hidden",
|
|
215
|
+
boxShadow: "0 25px 50px rgba(0, 0, 0, 0.25)"
|
|
216
|
+
};
|
|
217
|
+
function FluxFilesModal({
|
|
218
|
+
open,
|
|
219
|
+
endpoint,
|
|
220
|
+
token,
|
|
221
|
+
disk,
|
|
222
|
+
mode = "picker",
|
|
223
|
+
allowedTypes,
|
|
224
|
+
maxSize,
|
|
225
|
+
onSelect,
|
|
226
|
+
onClose,
|
|
227
|
+
onReady,
|
|
228
|
+
onEvent,
|
|
229
|
+
overlayClassName,
|
|
230
|
+
modalClassName
|
|
231
|
+
}) {
|
|
232
|
+
const handle = useFluxFiles({
|
|
233
|
+
endpoint,
|
|
234
|
+
token,
|
|
235
|
+
disk,
|
|
236
|
+
mode,
|
|
237
|
+
allowedTypes,
|
|
238
|
+
maxSize,
|
|
239
|
+
onSelect,
|
|
240
|
+
onClose,
|
|
241
|
+
onReady,
|
|
242
|
+
onEvent
|
|
243
|
+
});
|
|
244
|
+
(0, import_react3.useEffect)(() => {
|
|
245
|
+
if (!open) return;
|
|
246
|
+
function onKeyDown(e) {
|
|
247
|
+
if (e.key === "Escape") {
|
|
248
|
+
onClose?.();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
document.addEventListener("keydown", onKeyDown);
|
|
252
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
253
|
+
}, [open, onClose]);
|
|
254
|
+
(0, import_react3.useEffect)(() => {
|
|
255
|
+
if (!open) return;
|
|
256
|
+
const prev = document.body.style.overflow;
|
|
257
|
+
document.body.style.overflow = "hidden";
|
|
258
|
+
return () => {
|
|
259
|
+
document.body.style.overflow = prev;
|
|
260
|
+
};
|
|
261
|
+
}, [open]);
|
|
262
|
+
const handleOverlayClick = (0, import_react3.useCallback)(
|
|
263
|
+
(e) => {
|
|
264
|
+
if (e.target === e.currentTarget) {
|
|
265
|
+
onClose?.();
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
[onClose]
|
|
269
|
+
);
|
|
270
|
+
if (!open) return null;
|
|
271
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
272
|
+
"div",
|
|
273
|
+
{
|
|
274
|
+
className: overlayClassName,
|
|
275
|
+
style: overlayClassName ? void 0 : defaultOverlayStyle,
|
|
276
|
+
onClick: handleOverlayClick,
|
|
277
|
+
role: "dialog",
|
|
278
|
+
"aria-modal": "true",
|
|
279
|
+
"aria-label": "FluxFiles File Manager",
|
|
280
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
281
|
+
"div",
|
|
282
|
+
{
|
|
283
|
+
className: modalClassName,
|
|
284
|
+
style: modalClassName ? void 0 : defaultModalStyle,
|
|
285
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
286
|
+
"iframe",
|
|
287
|
+
{
|
|
288
|
+
ref: handle.iframeRef,
|
|
289
|
+
src: handle.iframeSrc,
|
|
290
|
+
style: { width: "100%", height: "100%", border: "none" },
|
|
291
|
+
allow: "clipboard-write",
|
|
292
|
+
title: "FluxFiles File Manager"
|
|
293
|
+
}
|
|
294
|
+
)
|
|
295
|
+
}
|
|
296
|
+
)
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
301
|
+
0 && (module.exports = {
|
|
302
|
+
FluxFiles,
|
|
303
|
+
FluxFilesModal,
|
|
304
|
+
useFluxFiles
|
|
305
|
+
});
|