@scaleflex/uploader 0.2.1 → 0.2.2
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/.claude/skills/integrate-uploader/SKILL.md +458 -0
- package/README.md +30 -0
- package/dist/components/drop-zone.d.ts +1 -0
- package/dist/components/drop-zone.d.ts.map +1 -1
- package/dist/components/file-item.d.ts +1 -0
- package/dist/components/file-item.d.ts.map +1 -1
- package/dist/components/file-list.d.ts +13 -0
- package/dist/components/file-list.d.ts.map +1 -1
- package/dist/components/success-card.d.ts.map +1 -1
- package/dist/define.cjs +1 -1
- package/dist/define.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/{provider-browser-ixqCA0XP.js → provider-browser-DJ2LHNd-.js} +1 -1
- package/dist/{provider-browser-yW3pZFSP.cjs → provider-browser-DajdSDoL.cjs} +1 -1
- package/dist/{search-provider-browser-BrKVwGf_.cjs → search-provider-browser-BM-foEWT.cjs} +1 -1
- package/dist/{search-provider-browser-B5ZWGUl8.js → search-provider-browser-CIH8w0aj.js} +1 -1
- package/dist/{sfx-uploader-CU9IwNC7.js → sfx-uploader-CR48BiYQ.js} +3685 -2497
- package/dist/sfx-uploader-CpKFlkgS.cjs +4317 -0
- package/dist/sfx-uploader.d.ts +20 -4
- package/dist/sfx-uploader.d.ts.map +1 -1
- package/dist/store/store.types.d.ts +1 -0
- package/dist/store/store.types.d.ts.map +1 -1
- package/dist/test-utils.d.ts.map +1 -1
- package/dist/utils/file-utils.d.ts +2 -0
- package/dist/utils/file-utils.d.ts.map +1 -1
- package/package.json +2 -1
- package/dist/sfx-uploader-ziRTzw0k.cjs +0 -3298
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: integrate-uploader
|
|
3
|
+
description: Integrate @scaleflex/uploader into any project — install, configure auth,
|
|
4
|
+
wire events, theme, and add restrictions. Works with vanilla JS, React, Vue, Angular, Svelte.
|
|
5
|
+
user_invocable: true
|
|
6
|
+
metadata:
|
|
7
|
+
category: integration
|
|
8
|
+
tags:
|
|
9
|
+
- scaleflex
|
|
10
|
+
- uploader
|
|
11
|
+
- file-upload
|
|
12
|
+
- web-component
|
|
13
|
+
status: ready
|
|
14
|
+
version: 1
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Scaleflex Uploader Integration Skill
|
|
18
|
+
|
|
19
|
+
## When to Use
|
|
20
|
+
|
|
21
|
+
- User says "add uploader", "integrate uploader", "file upload widget", "upload component"
|
|
22
|
+
- User wants to let users upload files to Scaleflex VXP / Filerobot
|
|
23
|
+
- User asks how to use `@scaleflex/uploader` in their project
|
|
24
|
+
|
|
25
|
+
## Step 1 — Detect the Target Framework
|
|
26
|
+
|
|
27
|
+
Read the project's `package.json` to determine:
|
|
28
|
+
- **React** (18+): use the React wrapper (`@scaleflex/uploader/react`)
|
|
29
|
+
- **Vue / Angular / Svelte / vanilla JS**: use the Web Component (`@scaleflex/uploader/define`)
|
|
30
|
+
|
|
31
|
+
## Step 2 — Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install @scaleflex/uploader
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`lit` is bundled — no extra peer deps for vanilla JS / Vue / Angular / Svelte.
|
|
38
|
+
For React, `react` and `react-dom` (v18+) must already be installed (they're optional peer deps).
|
|
39
|
+
|
|
40
|
+
### CDN (no bundler)
|
|
41
|
+
|
|
42
|
+
```html
|
|
43
|
+
<script src="https://scaleflex.cloudimg.io/v7/plugins/scaleflex/uploader/0.2.1/sfx-uploader.min.js"></script>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This registers `<sfx-uploader>` and all sub-components automatically. Replace `0.2.1` with the desired version.
|
|
47
|
+
|
|
48
|
+
## Step 3 — Add the Uploader
|
|
49
|
+
|
|
50
|
+
### Vanilla JS / Web Component
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
import '@scaleflex/uploader/define';
|
|
54
|
+
|
|
55
|
+
const uploader = document.querySelector('sfx-uploader')
|
|
56
|
+
|| document.createElement('sfx-uploader');
|
|
57
|
+
document.body.appendChild(uploader);
|
|
58
|
+
|
|
59
|
+
uploader.config = {
|
|
60
|
+
auth: {
|
|
61
|
+
mode: 'security-template',
|
|
62
|
+
container: 'YOUR_CONTAINER',
|
|
63
|
+
securityTemplateId: 'SECU_YOUR_TEMPLATE_ID',
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
uploader.addEventListener('sfx-all-complete', (e) => {
|
|
68
|
+
const { successful, failed } = e.detail;
|
|
69
|
+
console.log('Uploaded:', successful.map(f => f.response.file.url.cdn));
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
uploader.open();
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### React
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
import { useRef } from 'react';
|
|
79
|
+
import { Uploader, type UploaderRef } from '@scaleflex/uploader/react';
|
|
80
|
+
|
|
81
|
+
function MyComponent() {
|
|
82
|
+
const uploaderRef = useRef<UploaderRef>(null);
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<>
|
|
86
|
+
<button onClick={() => uploaderRef.current?.open()}>Upload files</button>
|
|
87
|
+
<Uploader
|
|
88
|
+
ref={uploaderRef}
|
|
89
|
+
config={{
|
|
90
|
+
auth: {
|
|
91
|
+
mode: 'security-template' as const,
|
|
92
|
+
container: 'YOUR_CONTAINER',
|
|
93
|
+
securityTemplateId: 'SECU_YOUR_TEMPLATE_ID',
|
|
94
|
+
},
|
|
95
|
+
}}
|
|
96
|
+
onUploadComplete={(file, response) => {
|
|
97
|
+
console.log('Uploaded:', response.file.url.cdn);
|
|
98
|
+
}}
|
|
99
|
+
onAllComplete={(successful, failed) => {
|
|
100
|
+
console.log(`Done: ${successful.length} ok, ${failed.length} failed`);
|
|
101
|
+
}}
|
|
102
|
+
/>
|
|
103
|
+
</>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**React controlled mode** (open state managed by parent):
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
112
|
+
|
|
113
|
+
<Uploader
|
|
114
|
+
config={config}
|
|
115
|
+
open={isOpen}
|
|
116
|
+
onAllComplete={(successful) => { handleFiles(successful); setIsOpen(false); }}
|
|
117
|
+
onCancel={() => setIsOpen(false)}
|
|
118
|
+
/>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Vue
|
|
122
|
+
|
|
123
|
+
```vue
|
|
124
|
+
<template>
|
|
125
|
+
<sfx-uploader ref="uploader"></sfx-uploader>
|
|
126
|
+
<button @click="openUploader">Upload files</button>
|
|
127
|
+
</template>
|
|
128
|
+
|
|
129
|
+
<script setup>
|
|
130
|
+
import '@scaleflex/uploader/define';
|
|
131
|
+
import { ref, onMounted } from 'vue';
|
|
132
|
+
|
|
133
|
+
const uploader = ref(null);
|
|
134
|
+
|
|
135
|
+
onMounted(() => {
|
|
136
|
+
const el = uploader.value;
|
|
137
|
+
el.config = {
|
|
138
|
+
auth: {
|
|
139
|
+
mode: 'security-template',
|
|
140
|
+
container: 'YOUR_CONTAINER',
|
|
141
|
+
securityTemplateId: 'SECU_YOUR_TEMPLATE_ID',
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
el.addEventListener('sfx-all-complete', (e) => {
|
|
146
|
+
console.log('Uploaded:', e.detail.successful);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
function openUploader() {
|
|
151
|
+
uploader.value?.open();
|
|
152
|
+
}
|
|
153
|
+
</script>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Step 4 — Authentication
|
|
157
|
+
|
|
158
|
+
Ask the user which auth mode they need:
|
|
159
|
+
|
|
160
|
+
### Security template (recommended for client-side)
|
|
161
|
+
|
|
162
|
+
The uploader auto-exchanges the template ID for a SASS key on init. Safer for client-side code.
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
auth: {
|
|
166
|
+
mode: 'security-template',
|
|
167
|
+
container: string, // Scaleflex container name
|
|
168
|
+
securityTemplateId: string, // From Scaleflex dashboard (SECU_...)
|
|
169
|
+
airboxPuid?: string, // Optional: airbox user ID
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### SASS key (direct)
|
|
174
|
+
|
|
175
|
+
Use when you already have a SASS key (e.g. from your backend or inside Scaleflex Hub).
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
auth: {
|
|
179
|
+
mode: 'sass-key',
|
|
180
|
+
container: string,
|
|
181
|
+
sassKey: string, // X-Filerobot-Key value
|
|
182
|
+
airboxPuid?: string,
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Step 5 — Configure (Optional)
|
|
187
|
+
|
|
188
|
+
All options beyond `auth` are optional. Add only what the user needs:
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
uploader.config = {
|
|
192
|
+
auth: { /* ... */ },
|
|
193
|
+
|
|
194
|
+
// Upload target
|
|
195
|
+
targetFolder: '/user-uploads/', // Filerobot folder path (default: '/')
|
|
196
|
+
|
|
197
|
+
// Display
|
|
198
|
+
mode: 'modal', // 'modal' (default) or 'inline'
|
|
199
|
+
headerButton: 'close', // 'none' | 'close' | 'back'
|
|
200
|
+
// modal default: 'close'
|
|
201
|
+
// inline default: 'none'
|
|
202
|
+
// 'back': for wizard/step flows
|
|
203
|
+
sourcesLayout: 'pills', // 'pills' (default) or 'cards'
|
|
204
|
+
|
|
205
|
+
// Upload behavior
|
|
206
|
+
concurrency: 3, // Max concurrent uploads (default: 3)
|
|
207
|
+
autoProceed: false, // Auto-start upload on file add (default: false)
|
|
208
|
+
|
|
209
|
+
// File restrictions
|
|
210
|
+
restrictions: {
|
|
211
|
+
maxFileSize: 10 * 1024 * 1024, // 10 MB per file
|
|
212
|
+
maxTotalFilesSize: 500 * 1024 * 1024, // 500 MB total
|
|
213
|
+
maxNumberOfFiles: 50,
|
|
214
|
+
minNumberOfFiles: 1,
|
|
215
|
+
allowedFileTypes: ['image/*', 'video/*', 'application/pdf'],
|
|
216
|
+
blockedFileTypes: ['application/exe'],
|
|
217
|
+
},
|
|
218
|
+
|
|
219
|
+
// Cloud connectors (Google Drive, Dropbox, etc.)
|
|
220
|
+
connectors: {
|
|
221
|
+
companionUrl: 'https://eu-on-24001.connector.filerobot.com',
|
|
222
|
+
providers: ['google-drive', 'dropbox', 'onedrive', 'box'],
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
// UI options
|
|
226
|
+
showFillMetadata: false, // Show "Fill Metadata" button (default: false)
|
|
227
|
+
clearOnClose: true, // Clear files on modal close (default: true)
|
|
228
|
+
clearOnComplete: true, // Clear files on "Done" action (default: true)
|
|
229
|
+
rejectedFileAutoRemoveDelay: 4000, // ms before auto-removing rejected files
|
|
230
|
+
// Set to 0 or false to disable
|
|
231
|
+
};
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Events Reference
|
|
235
|
+
|
|
236
|
+
All events bubble and cross Shadow DOM (`composed: true`):
|
|
237
|
+
|
|
238
|
+
| Event | Detail | When |
|
|
239
|
+
|---|---|---|
|
|
240
|
+
| `sfx-file-added` | `{ file: UploadFile }` | File added to queue |
|
|
241
|
+
| `sfx-file-removed` | `{ file: UploadFile }` | File removed from queue |
|
|
242
|
+
| `sfx-file-rejected` | `{ file: UploadFile, reason: string }` | File rejected by restrictions |
|
|
243
|
+
| `sfx-upload-started` | `{ files: UploadFile[] }` | Upload batch started |
|
|
244
|
+
| `sfx-upload-progress` | `{ file: UploadFile, progress: number, speed: number }` | Per-file progress update |
|
|
245
|
+
| `sfx-upload-complete` | `{ file: UploadFile, response: UploadResponse }` | Single file uploaded |
|
|
246
|
+
| `sfx-upload-error` | `{ file: UploadFile, error: Error }` | Single file failed |
|
|
247
|
+
| `sfx-upload-retry` | `{ file: UploadFile, attempt: number }` | File being retried |
|
|
248
|
+
| `sfx-all-complete` | `{ successful: UploadFile[], failed: UploadFile[] }` | All files finished |
|
|
249
|
+
| `sfx-total-progress` | `{ percentage: number, speed: number, eta: number }` | Aggregate progress |
|
|
250
|
+
| `sfx-before-upload` | `{ files: UploadFile[] }` | Before upload starts (cancelable) |
|
|
251
|
+
| `sfx-open` | `{}` | Uploader opened |
|
|
252
|
+
| `sfx-close` | `{}` | Uploader closed |
|
|
253
|
+
| `sfx-cancel` | `{}` | Upload cancelled |
|
|
254
|
+
| `sfx-fill-metadata` | `{ files: UploadFile[] }` | "Fill Metadata" button clicked |
|
|
255
|
+
|
|
256
|
+
## Public Methods
|
|
257
|
+
|
|
258
|
+
| Method | Returns | Description |
|
|
259
|
+
|---|---|---|
|
|
260
|
+
| `open()` | `void` | Open the modal |
|
|
261
|
+
| `close()` | `void` | Close the modal (respects `clearOnClose`) |
|
|
262
|
+
| `upload()` | `void` | Start uploading all queued files |
|
|
263
|
+
| `addFiles(files: File[])` | `void` | Programmatically add files |
|
|
264
|
+
| `resumeUpload(files?: UploadFile[])` | `void` | Resume paused upload with optional metadata updates |
|
|
265
|
+
| `cancelUpload()` | `void` | Cancel active uploads |
|
|
266
|
+
| `getFiles()` | `UploadFile[]` | Get snapshot of all files |
|
|
267
|
+
| `getFile(fileId)` | `UploadFile \| undefined` | Get single file by ID |
|
|
268
|
+
| `updateFileMeta(fileId, meta?, tags?)` | `void` | Update metadata on a queued file |
|
|
269
|
+
| `updateFilesMeta(updates)` | `void` | Batch update metadata |
|
|
270
|
+
|
|
271
|
+
## Config Callbacks (Web Component)
|
|
272
|
+
|
|
273
|
+
For Web Component usage, callbacks can be passed via `config.callbacks`:
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
uploader.config = {
|
|
277
|
+
auth: { /* ... */ },
|
|
278
|
+
callbacks: {
|
|
279
|
+
onFileAdded: (file) => { /* ... */ },
|
|
280
|
+
onAllComplete: (successful, failed) => { /* ... */ },
|
|
281
|
+
onBeforeUpload: (files) => {
|
|
282
|
+
// Return false to cancel the upload
|
|
283
|
+
return true;
|
|
284
|
+
},
|
|
285
|
+
onFillMetadata: (files) => {
|
|
286
|
+
// Called when "Fill Metadata" button is clicked
|
|
287
|
+
},
|
|
288
|
+
onFilePreview: (file) => {
|
|
289
|
+
// Called when a file preview is clicked
|
|
290
|
+
},
|
|
291
|
+
// ... all other callbacks from the Events table
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
`onBeforeUpload`, `onFillMetadata`, and `onFilePreview` are only available via `config.callbacks` (not as React props).
|
|
297
|
+
|
|
298
|
+
## React Wrapper Props
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
interface UploaderProps {
|
|
302
|
+
config: UploaderConfig; // Required
|
|
303
|
+
open?: boolean; // Controlled open state
|
|
304
|
+
|
|
305
|
+
// All event callbacks (mirror DOM events)
|
|
306
|
+
onFileAdded?: (file: UploadFile) => void;
|
|
307
|
+
onFileRemoved?: (file: UploadFile) => void;
|
|
308
|
+
onFileRejected?: (file: UploadFile, reason: string) => void;
|
|
309
|
+
onUploadStarted?: (files: UploadFile[]) => void;
|
|
310
|
+
onUploadProgress?: (file: UploadFile, progress: number, speed: number) => void;
|
|
311
|
+
onUploadComplete?: (file: UploadFile, response: UploadResponse) => void;
|
|
312
|
+
onUploadError?: (file: UploadFile, error: Error) => void;
|
|
313
|
+
onUploadRetry?: (file: UploadFile, attempt: number) => void;
|
|
314
|
+
onAllComplete?: (successful: UploadFile[], failed: UploadFile[]) => void;
|
|
315
|
+
onTotalProgress?: (percentage: number, speed: number, eta: number) => void;
|
|
316
|
+
onOpen?: () => void;
|
|
317
|
+
onClose?: () => void;
|
|
318
|
+
onCancel?: () => void;
|
|
319
|
+
|
|
320
|
+
className?: string;
|
|
321
|
+
style?: CSSProperties;
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
React ref exposes: `open()`, `close()`, `upload()`, `addFiles()`, `resumeUpload()`, `cancelUpload()`, and `element` (direct Web Component access).
|
|
326
|
+
|
|
327
|
+
## UploadResponse Shape
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
interface UploadResponse {
|
|
331
|
+
status: 'success' | 'error';
|
|
332
|
+
file: {
|
|
333
|
+
uuid: string;
|
|
334
|
+
name: string;
|
|
335
|
+
extension: string;
|
|
336
|
+
type: string;
|
|
337
|
+
size: number;
|
|
338
|
+
url: { public: string; cdn: string };
|
|
339
|
+
meta: Record<string, unknown>;
|
|
340
|
+
tags: string[];
|
|
341
|
+
info: { img_w?: number; img_h?: number };
|
|
342
|
+
created_at: string;
|
|
343
|
+
modified_at: string;
|
|
344
|
+
};
|
|
345
|
+
msg?: string;
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Theming
|
|
350
|
+
|
|
351
|
+
Override CSS custom properties on the element or any ancestor. All use `--sfx-up-` prefix:
|
|
352
|
+
|
|
353
|
+
```css
|
|
354
|
+
sfx-uploader {
|
|
355
|
+
/* Colors */
|
|
356
|
+
--sfx-up-primary: #2563eb;
|
|
357
|
+
--sfx-up-primary-hover: #1d4ed8;
|
|
358
|
+
--sfx-up-primary-bg: #eff6ff;
|
|
359
|
+
--sfx-up-success: #16a34a;
|
|
360
|
+
--sfx-up-error: #dc2626;
|
|
361
|
+
|
|
362
|
+
/* Text */
|
|
363
|
+
--sfx-up-text: #1e293b;
|
|
364
|
+
--sfx-up-text-secondary: #475569;
|
|
365
|
+
--sfx-up-text-muted: #94a3b8;
|
|
366
|
+
|
|
367
|
+
/* Background & borders */
|
|
368
|
+
--sfx-up-bg: #ffffff;
|
|
369
|
+
--sfx-up-surface: #f8fafc;
|
|
370
|
+
--sfx-up-border: #e8edf5;
|
|
371
|
+
--sfx-up-backdrop: rgba(0, 0, 0, 0.45);
|
|
372
|
+
|
|
373
|
+
/* Typography & shape */
|
|
374
|
+
--sfx-up-font: 'Inter', system-ui, sans-serif;
|
|
375
|
+
--sfx-up-radius: 16px;
|
|
376
|
+
--sfx-up-max-height: 88vh;
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
## Cloud Connectors
|
|
381
|
+
|
|
382
|
+
Supported providers:
|
|
383
|
+
|
|
384
|
+
| Provider ID | Service |
|
|
385
|
+
|---|---|
|
|
386
|
+
| `'google-drive'` | Google Drive |
|
|
387
|
+
| `'dropbox'` | Dropbox |
|
|
388
|
+
| `'onedrive'` | Microsoft OneDrive |
|
|
389
|
+
| `'box'` | Box |
|
|
390
|
+
| `'instagram'` | Instagram |
|
|
391
|
+
| `'facebook'` | Facebook |
|
|
392
|
+
| `'unsplash'` | Unsplash (search-based) |
|
|
393
|
+
|
|
394
|
+
## Common Patterns
|
|
395
|
+
|
|
396
|
+
### Image-only uploader
|
|
397
|
+
|
|
398
|
+
```ts
|
|
399
|
+
config = {
|
|
400
|
+
auth: { /* ... */ },
|
|
401
|
+
restrictions: {
|
|
402
|
+
allowedFileTypes: ['image/*'],
|
|
403
|
+
maxFileSize: 5 * 1024 * 1024,
|
|
404
|
+
},
|
|
405
|
+
autoProceed: true,
|
|
406
|
+
};
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Inline mode (embedded in page)
|
|
410
|
+
|
|
411
|
+
```ts
|
|
412
|
+
config = {
|
|
413
|
+
auth: { /* ... */ },
|
|
414
|
+
mode: 'inline',
|
|
415
|
+
};
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Step/wizard flow (modal with back button)
|
|
419
|
+
|
|
420
|
+
```ts
|
|
421
|
+
config = {
|
|
422
|
+
auth: { /* ... */ },
|
|
423
|
+
mode: 'modal',
|
|
424
|
+
headerButton: 'back',
|
|
425
|
+
clearOnClose: false, // preserve files across open/close
|
|
426
|
+
};
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### With cloud connectors
|
|
430
|
+
|
|
431
|
+
```ts
|
|
432
|
+
config = {
|
|
433
|
+
auth: { /* ... */ },
|
|
434
|
+
connectors: {
|
|
435
|
+
companionUrl: 'https://eu-on-24001.connector.filerobot.com',
|
|
436
|
+
providers: ['google-drive', 'dropbox'],
|
|
437
|
+
},
|
|
438
|
+
};
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Intercept before upload (metadata dialog)
|
|
442
|
+
|
|
443
|
+
```js
|
|
444
|
+
uploader.addEventListener('sfx-before-upload', (e) => {
|
|
445
|
+
e.preventDefault(); // pause upload
|
|
446
|
+
showMetadataDialog(e.detail.files).then((updatedFiles) => {
|
|
447
|
+
uploader.resumeUpload(updatedFiles);
|
|
448
|
+
});
|
|
449
|
+
});
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
## Troubleshooting
|
|
453
|
+
|
|
454
|
+
- **"customElements is not defined"** — SSR environment. The React wrapper handles this automatically. For vanilla JS, guard the import: `if (typeof customElements !== 'undefined') import('@scaleflex/uploader/define');`
|
|
455
|
+
- **Styles not showing** — The component uses Shadow DOM; external CSS won't penetrate. Use `--sfx-up-*` CSS custom properties for theming.
|
|
456
|
+
- **Auth errors** — Check that the security template ID or SASS key is valid and the container name matches.
|
|
457
|
+
- **`open()` does nothing** — `config` must be set before calling `open()`. Ensure `auth` is provided.
|
|
458
|
+
- **Files rejected silently** — Check `restrictions` config. Listen to `sfx-file-rejected` for the reason.
|
package/README.md
CHANGED
|
@@ -47,6 +47,12 @@
|
|
|
47
47
|
npm install @scaleflex/uploader
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
+
Or use the CDN for a no-bundler setup:
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<script src="https://scaleflex.cloudimg.io/v7/plugins/scaleflex/uploader/0.2.1/sfx-uploader.min.js"></script>
|
|
54
|
+
```
|
|
55
|
+
|
|
50
56
|
## Quick start
|
|
51
57
|
|
|
52
58
|
### Web Component
|
|
@@ -114,6 +120,30 @@ function App() {
|
|
|
114
120
|
| Firefox | 100+ |
|
|
115
121
|
| Safari | 15.4+ |
|
|
116
122
|
|
|
123
|
+
## Claude Code Integration
|
|
124
|
+
|
|
125
|
+
If you use [Claude Code](https://docs.anthropic.com/en/docs/claude-code), this package ships with a ready-made skill that helps Claude add the uploader to your project — detecting your framework, wiring auth, events, theming, and restrictions automatically.
|
|
126
|
+
|
|
127
|
+
**Per-project** (recommended — share with your team via version control):
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
mkdir -p .claude/skills/integrate-uploader
|
|
131
|
+
cp node_modules/@scaleflex/uploader/.claude/skills/integrate-uploader/SKILL.md \
|
|
132
|
+
.claude/skills/integrate-uploader/SKILL.md
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Commit the `.claude/skills/` directory to version control. The skill is now available to everyone on the team.
|
|
136
|
+
|
|
137
|
+
**Per-user** (available across all your projects):
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
mkdir -p ~/.claude/skills/integrate-uploader
|
|
141
|
+
cp node_modules/@scaleflex/uploader/.claude/skills/integrate-uploader/SKILL.md \
|
|
142
|
+
~/.claude/skills/integrate-uploader/SKILL.md
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Then type `/integrate-uploader` in Claude Code and it will walk you through the full integration — install, config, events, and theming — tailored to your stack (React, Vue, vanilla JS, etc.).
|
|
146
|
+
|
|
117
147
|
## Documentation
|
|
118
148
|
|
|
119
149
|
See the full documentation and interactive examples at **[scaleflex.github.io/uploader](https://scaleflex.github.io/uploader/)**.
|
|
@@ -35,6 +35,7 @@ export declare class SfxDropZone extends LitElement {
|
|
|
35
35
|
private _onScrollOrResize;
|
|
36
36
|
private _updateVisiblePills;
|
|
37
37
|
connectedCallback(): void;
|
|
38
|
+
updated(changed: Map<string, unknown>): void;
|
|
38
39
|
disconnectedCallback(): void;
|
|
39
40
|
private _renderPill;
|
|
40
41
|
private _renderCard;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drop-zone.d.ts","sourceRoot":"","sources":["../../src/components/drop-zone.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAKrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAKhD,qBAAa,WAAY,SAAQ,UAAU;IACzC,MAAM,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"drop-zone.d.ts","sourceRoot":"","sources":["../../src/components/drop-zone.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAKrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAKhD,qBAAa,WAAY,SAAQ,UAAU;IACzC,MAAM,CAAC,MAAM,0BA8wBX;IAE0C,OAAO,UAAS;IACE,gBAAgB,UAAS;IAC3D,MAAM,SAAM;IACb,OAAO,EAAE,SAAS,EAAE,CAAM;IACI,aAAa,EAAE,OAAO,GAAG,OAAO,CAAW;IAE3F,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAAiB;IAE7B,OAAO,CAAC,SAAS,CAAe;IACrB,SAAS,EAAG,gBAAgB,CAAC;IAE1D,OAAO,CAAC,YAAY,CAAK;IAEzB,0CAA0C;IAC1C,MAAM;IAMN,OAAO,CAAC,YAAY,CAMlB;IAEF,OAAO,CAAC,WAAW,CAEjB;IAEF,OAAO,CAAC,YAAY,CAOlB;IAEF,OAAO,CAAC,OAAO,CAUb;IAIF,OAAO,CAAC,QAAQ,CAad;IAEF,OAAO,CAAC,UAAU,CAKhB;IAEF,OAAO,CAAC,aAAa,CAQnB;IAIF,OAAO,CAAC,QAAQ,CAkBd;IAEF,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,WAAW;IAQnB,qFAAqF;IACrF,OAAO,CAAC,iBAAiB;IAkCzB,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,WAAW,CAEjB;IAEF,OAAO,CAAC,aAAa,CAInB;IAEF,OAAO,CAAC,YAAY,CAA8C;IAElE,OAAO,CAAC,iBAAiB,CAMvB;IAEF,OAAO,CAAC,mBAAmB;IAqB3B,iBAAiB;IAUjB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAMrC,oBAAoB;IAUpB,OAAO,CAAC,WAAW;IAmBnB,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,eAAe;IAiCvB,OAAO,CAAC,mBAAmB;IA2B3B,MAAM;CAuGP"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-item.d.ts","sourceRoot":"","sources":["../../src/components/file-item.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,OAAO,EAAE,MAAM,KAAK,CAAC;AAErD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvD,qBAAa,WAAY,SAAQ,UAAU;IACzC,MAAM,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"file-item.d.ts","sourceRoot":"","sources":["../../src/components/file-item.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,OAAO,EAAE,MAAM,KAAK,CAAC;AAErD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvD,qBAAa,WAAY,SAAQ,UAAU;IACzC,MAAM,CAAC,MAAM,0BAkXX;IAE8B,IAAI,EAAG,UAAU,CAAC;IAElD,OAAO,CAAC,OAAO;IAUf,OAAO,CAAC,MAAM;IAUd,OAAO,CAAC,QAAQ;IAWhB,MAAM;IAiHN,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,eAAe;CAcxB"}
|
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
import { LitElement } from 'lit';
|
|
2
2
|
import { UploadFile } from '../store/store.types';
|
|
3
|
+
import { SourceDef } from '../types/source.types';
|
|
3
4
|
export declare class SfxFileList extends LitElement {
|
|
4
5
|
static styles: import('lit').CSSResult;
|
|
5
6
|
files: UploadFile[];
|
|
7
|
+
showDropTile: boolean;
|
|
8
|
+
sources: SourceDef[];
|
|
9
|
+
accept: string;
|
|
10
|
+
private _moreOpen;
|
|
11
|
+
private _outsideClickHandler;
|
|
12
|
+
private _onDropTileClick;
|
|
13
|
+
private _onFileInput;
|
|
14
|
+
private _onSourceClick;
|
|
15
|
+
private _toggleMore;
|
|
16
|
+
disconnectedCallback(): void;
|
|
17
|
+
private _onMoreSourceClick;
|
|
18
|
+
private _renderDropTile;
|
|
6
19
|
render(): import('lit-html').TemplateResult<1>;
|
|
7
20
|
}
|
|
8
21
|
//# sourceMappingURL=file-list.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-list.d.ts","sourceRoot":"","sources":["../../src/components/file-list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"file-list.d.ts","sourceRoot":"","sources":["../../src/components/file-list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAqC,MAAM,KAAK,CAAC;AAIpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD,qBAAa,WAAY,SAAQ,UAAU;IACzC,MAAM,CAAC,MAAM,0BA2SX;IAE8B,KAAK,EAAE,UAAU,EAAE,CAAM;IAC5B,YAAY,UAAS;IAClB,OAAO,EAAE,SAAS,EAAE,CAAM;IAC9B,MAAM,SAAM;IAE/B,OAAO,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,oBAAoB,CAO1B;IAEF,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,WAAW;IAUnB,oBAAoB;IAMpB,OAAO,CAAC,kBAAkB;IAM1B,OAAO,CAAC,eAAe;IA6DvB,MAAM;CAUP"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"success-card.d.ts","sourceRoot":"","sources":["../../src/components/success-card.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAOrD,qBAAa,cAAe,SAAQ,UAAU;IAC5C,MAAM,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"success-card.d.ts","sourceRoot":"","sources":["../../src/components/success-card.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAOrD,qBAAa,cAAe,SAAQ,UAAU;IAC5C,MAAM,CAAC,MAAM,4BAyIV;IAEyB,SAAS,SAAK;IACd,SAAS,SAAK;IACf,UAAU,EAAE,MAAM,EAAE,CAAM;IACzB,YAAY,SAAU;IAElD,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,cAAc;IAMtB,MAAM;CAoCP"}
|
package/dist/define.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const e=require("./sfx-uploader-
|
|
1
|
+
"use strict";const e=require("./sfx-uploader-CpKFlkgS.cjs"),s=(f,i)=>{typeof customElements<"u"&&!customElements.get(f)&&customElements.define(f,i)};s("sfx-uploader",e.SfxUploader);s("sfx-drop-zone",e.SfxDropZone);s("sfx-import-divider",e.SfxImportDivider);s("sfx-source-pills",e.SfxSourcePills);s("sfx-file-list",e.SfxFileList);s("sfx-file-item",e.SfxFileItem);s("sfx-success-card",e.SfxSuccessCard);s("sfx-actions-bar",e.SfxActionsBar);s("sfx-url-dialog",e.SfxUrlDialog);s("sfx-camera-dialog",e.SfxCameraDialog);s("sfx-screen-cast-dialog",e.SfxScreenCastDialog);
|
package/dist/define.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { g as f, a as i, d as o, e as r, c as l, b as x, f as t, S as c, n as d, o as S, p as m } from "./sfx-uploader-
|
|
1
|
+
import { g as f, a as i, d as o, e as r, c as l, b as x, f as t, S as c, n as d, o as S, p as m } from "./sfx-uploader-CR48BiYQ.js";
|
|
2
2
|
const s = (e, a) => {
|
|
3
3
|
typeof customElements < "u" && !customElements.get(e) && customElements.define(e, a);
|
|
4
4
|
};
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./sfx-uploader-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./sfx-uploader-CpKFlkgS.cjs");exports.CORE_SOURCES=e.CORE_SOURCES;exports.PublicEvents=e.PublicEvents;exports.SfxActionsBar=e.SfxActionsBar;exports.SfxDropZone=e.SfxDropZone;exports.SfxFileItem=e.SfxFileItem;exports.SfxFileList=e.SfxFileList;exports.SfxImportDivider=e.SfxImportDivider;exports.SfxSourcePills=e.SfxSourcePills;exports.SfxSuccessCard=e.SfxSuccessCard;exports.SfxUploader=e.SfxUploader;exports.Store=e.Store;exports.UploadEngine=e.UploadEngine;exports.buildAuthHeaders=e.buildAuthHeaders;exports.createStore=e.createStore;exports.exchangeSassKey=e.exchangeSassKey;exports.getApiBase=e.getApiBase;exports.getProviderSources=e.getProviderSources;exports.resolveAuth=e.resolveAuth;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as e, P as r, S, a as o, b as i, c as t, d as f, e as l, f as x, g as c, h as d, U as u, i as n, j as p, k as g, l as h, m, r as v } from "./sfx-uploader-
|
|
1
|
+
import { C as e, P as r, S, a as o, b as i, c as t, d as f, e as l, f as x, g as c, h as d, U as u, i as n, j as p, k as g, l as h, m, r as v } from "./sfx-uploader-CR48BiYQ.js";
|
|
2
2
|
export {
|
|
3
3
|
e as CORE_SOURCES,
|
|
4
4
|
r as PublicEvents,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { LitElement as y, css as w, html as s, nothing as h } from "lit";
|
|
2
2
|
import { property as k, state as d } from "lit/decorators.js";
|
|
3
3
|
import { unsafeHTML as b } from "lit/directives/unsafe-html.js";
|
|
4
|
-
import { q as _, t as $, m as z, u as C, A as v, v as I } from "./sfx-uploader-
|
|
4
|
+
import { q as _, t as $, m as z, u as C, A as v, v as I } from "./sfx-uploader-CR48BiYQ.js";
|
|
5
5
|
const x = "sfx-uploader-token:";
|
|
6
6
|
function p(n) {
|
|
7
7
|
try {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("lit"),l=require("lit/decorators.js"),b=require("lit/directives/unsafe-html.js"),c=require("./sfx-uploader-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("lit"),l=require("lit/decorators.js"),b=require("lit/directives/unsafe-html.js"),c=require("./sfx-uploader-CpKFlkgS.cjs"),x="sfx-uploader-token:";function p(o){try{return localStorage.getItem(`${x}${o}`)}catch{return null}}function v(o,e){try{localStorage.setItem(`${x}${o}`,e)}catch{}}function u(o){try{localStorage.removeItem(`${x}${o}`)}catch{}}function k(o,e){const r=i=>{if(i.origin!==new URL(o).origin)return;const t=typeof i.data=="string"?y(i.data):i.data;t!=null&&t.token&&e(t.token)};return window.addEventListener("message",r),()=>window.removeEventListener("message",r)}function y(o){try{return JSON.parse(o)}catch{return null}}var _=Object.defineProperty,d=(o,e,r,i)=>{for(var t=void 0,n=o.length-1,h;n>=0;n--)(h=o[n])&&(t=h(e,r,t)||t);return t&&_(e,r,t),t};const g=class g extends s.LitElement{constructor(){super(...arguments),this.provider="google-drive",this.companionUrl="",this._authenticated=!1,this._loading=!1,this._items=[],this._selectedIds=new Set,this._breadcrumbs=[],this._nextPagePath=null,this._error=null,this._loadingMore=!1,this._username=null,this._cleanupAuthListener=null,this._authWindow=null,this._handleConnect=()=>{var r;const e=c.getAuthUrl(this.companionUrl,this.provider);this._authWindow=window.open(e,"_blank","width=600,height=600"),(r=this._cleanupAuthListener)==null||r.call(this),this._cleanupAuthListener=k(this.companionUrl,i=>{var t,n;(t=this._authWindow)==null||t.close(),this._authWindow=null,(n=this._cleanupAuthListener)==null||n.call(this),this._cleanupAuthListener=null,v(this.provider,i),this._authenticated=!0,this._loadFolder("")})},this._lastClickedIndex=null,this._toggleSelectAll=()=>{const e=this._items.filter(i=>!i.isFolder);e.every(i=>this._selectedIds.has(i.id))?this._selectedIds=new Set:this._selectedIds=new Set(e.map(i=>i.id))},this._onAddSelected=()=>{const e=p(this.provider);if(!e)return;const i=this._items.filter(t=>!t.isFolder&&this._selectedIds.has(t.id)).map(t=>({companionUrl:this.companionUrl,provider:this.provider,token:e,requestPath:t.requestPath,fileId:t.id,name:t.name,mimeType:t.mimeType,size:t.size,thumbnail:t.thumbnail}));this.dispatchEvent(new CustomEvent("connector-files-selected",{detail:{files:i},bubbles:!0,composed:!0}))},this._onClose=()=>{this.dispatchEvent(new CustomEvent("connector-close",{bubbles:!0,composed:!0}))},this._handleLogout=async()=>{const e=p(this.provider);if(e){try{await c.logout(this.companionUrl,this.provider,e)}catch{}u(this.provider)}this._reset()}}connectedCallback(){super.connectedCallback(),this._checkAuth()}disconnectedCallback(){var e;super.disconnectedCallback(),(e=this._cleanupAuthListener)==null||e.call(this),this._cleanupAuthListener=null}updated(e){e.has("provider")&&(this._reset(),this._checkAuth())}_reset(){this._authenticated=!1,this._loading=!1,this._items=[],this._selectedIds=new Set,this._breadcrumbs=[],this._nextPagePath=null,this._error=null,this._username=null}_checkAuth(){p(this.provider)&&(this._authenticated=!0,this._loadFolder(""))}get _providerDef(){return c.getProviderSources([this.provider])[0]??null}get _providerLabel(){var e;return((e=this._providerDef)==null?void 0:e.label)??this.provider}async _loadFolder(e){const r=p(this.provider);if(!r){this._authenticated=!1;return}this.offsetHeight>0&&(this.style.minHeight=`${this.offsetHeight}px`),this._loading=!0,this._error=null,this._items=[],this._selectedIds=new Set,this._lastClickedIndex=null,this._nextPagePath=null;try{const i=await c.listFiles(this.companionUrl,this.provider,r,e);this._items=i.items,this._nextPagePath=i.nextPagePath,i.username&&(this._username=i.username)}catch(i){i instanceof c.AuthExpiredError?(u(this.provider),this._authenticated=!1):this._error=i instanceof Error?i.message:"Failed to load files"}finally{this._loading=!1}}_onFolderClick(e){this._breadcrumbs=[...this._breadcrumbs,{name:e.name,path:e.requestPath}],this._loadFolder(e.requestPath)}_onBreadcrumbClick(e){if(e<0)this._breadcrumbs=[],this._loadFolder("");else{const r=this._breadcrumbs[e];this._breadcrumbs=this._breadcrumbs.slice(0,e+1),this._loadFolder(r.path)}}async _onLoadMore(){const e=p(this.provider);if(!(!e||!this._nextPagePath)){this._loadingMore=!0;try{const r=await c.listNextPage(this.companionUrl,e,this._nextPagePath);this._items=[...this._items,...r.items],this._nextPagePath=r.nextPagePath}catch(r){r instanceof c.AuthExpiredError&&(u(this.provider),this._authenticated=!1)}finally{this._loadingMore=!1}}}_toggleSelect(e,r){const i=this._items.filter(n=>!n.isFolder),t=i.findIndex(n=>n.id===e.id);if(r!=null&&r.shiftKey&&this._lastClickedIndex!==null&&t!==-1){const n=Math.min(this._lastClickedIndex,t),h=Math.max(this._lastClickedIndex,t),m=new Set(this._selectedIds);for(let f=n;f<=h;f++)m.add(i[f].id);this._selectedIds=m}else{const n=new Set(this._selectedIds);n.has(e.id)?n.delete(e.id):n.add(e.id),this._selectedIds=n}t!==-1&&(this._lastClickedIndex=t)}render(){return s.html`
|
|
2
2
|
${this._renderHeader()}
|
|
3
3
|
${this._authenticated?this._loading?this._renderLoading():this._error?this._renderError():this._renderBrowser():this._renderAuthView()}
|
|
4
4
|
`}_renderHeader(){const e=this._providerDef;return s.html`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("lit"),a=require("lit/decorators.js"),l=require("./sfx-uploader-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("lit"),a=require("lit/decorators.js"),l=require("./sfx-uploader-CpKFlkgS.cjs");var u=Object.defineProperty,n=(c,r,e,s)=>{for(var i=void 0,d=c.length-1,h;d>=0;d--)(h=c[d])&&(i=h(r,e,i)||i);return i&&u(r,e,i),i};const p=class p extends t.LitElement{constructor(){super(...arguments),this.provider="unsplash",this.companionUrl="",this._loading=!1,this._loadingMore=!1,this._items=[],this._selectedIds=new Set,this._error=null,this._searchQuery="",this._nextPageQuery=null,this._searched=!1,this._onResultsScroll=r=>{if(!this._nextPageQuery||this._loadingMore)return;const e=r.target;e.scrollHeight-e.scrollTop-e.clientHeight<200&&this._onLoadMore()},this._onSearchInput=r=>{this._searchQuery=r.target.value},this._onSearchKeydown=r=>{r.key==="Enter"&&this._doSearch()},this._onAddSelected=()=>{const e=this._items.filter(s=>this._selectedIds.has(s.id)).map(s=>({companionUrl:this.companionUrl,provider:this.provider,token:"",requestPath:s.requestPath,fileId:s.id,name:s.name||s.id,mimeType:s.mimeType,size:s.size,thumbnail:s.thumbnail}));this.dispatchEvent(new CustomEvent("connector-files-selected",{detail:{files:e},bubbles:!0,composed:!0}))},this._onClose=()=>{this.dispatchEvent(new CustomEvent("connector-close",{bubbles:!0,composed:!0}))}}get _providerLabel(){var e;return((e=l.getProviderSources([this.provider])[0])==null?void 0:e.label)??this.provider}async _doSearch(){const r=this._searchQuery.trim();if(r){this._loading=!0,this._error=null,this._items=[],this._selectedIds=new Set,this._nextPageQuery=null,this._searched=!0;try{const e=await l.searchProvider(this.companionUrl,this.provider,r),s=new Set;this._items=e.items.filter(i=>s.has(i.id)?!1:(s.add(i.id),!0)),this._nextPageQuery=e.nextPageQuery}catch(e){this._error=e instanceof Error?e.message:"Search failed"}finally{this._loading=!1}}}async _onLoadMore(){if(!(!this._nextPageQuery||this._loadingMore)){this._loadingMore=!0;try{const r=await l.searchProvider(this.companionUrl,this.provider,this._searchQuery.trim(),this._nextPageQuery),e=new Set(this._items.map(i=>i.id)),s=r.items.filter(i=>!e.has(i.id));this._items=[...this._items,...s],this._nextPageQuery=r.nextPageQuery}catch{}finally{this._loadingMore=!1}}}_toggleSelect(r){const e=new Set(this._selectedIds);e.has(r.id)?e.delete(r.id):e.add(r.id),this._selectedIds=e}render(){return t.html`
|
|
2
2
|
${this._renderHeader()}
|
|
3
3
|
${this._renderSearchBar()}
|
|
4
4
|
${this._loading?this._renderLoading():this._error?this._renderError():this._renderResults()}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LitElement as x, css as g, html as s, nothing as d } from "lit";
|
|
2
2
|
import { property as f, state as n } from "lit/decorators.js";
|
|
3
|
-
import { m as b, s as u } from "./sfx-uploader-
|
|
3
|
+
import { m as b, s as u } from "./sfx-uploader-CR48BiYQ.js";
|
|
4
4
|
var m = Object.defineProperty, a = (c, r, e, t) => {
|
|
5
5
|
for (var i = void 0, l = c.length - 1, h; l >= 0; l--)
|
|
6
6
|
(h = c[l]) && (i = h(r, e, i) || i);
|