@masters-union/union-stack 0.1.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.
@@ -0,0 +1,489 @@
1
+ 'use strict';
2
+
3
+ // src/picker/styles.ts
4
+ var STYLE_ID = "unionstack-picker-styles";
5
+ function ensureStyles() {
6
+ if (typeof document === "undefined") return;
7
+ if (document.getElementById(STYLE_ID)) return;
8
+ const el2 = document.createElement("style");
9
+ el2.id = STYLE_ID;
10
+ el2.textContent = BASE_CSS;
11
+ document.head.appendChild(el2);
12
+ }
13
+ function themeToCssVars(theme) {
14
+ const mode = theme?.mode || "light";
15
+ const defaults = mode === "dark" ? DARK_DEFAULTS : LIGHT_DEFAULTS;
16
+ return {
17
+ "--us-primary": theme?.primary ?? defaults.primary,
18
+ "--us-bg": theme?.background ?? defaults.background,
19
+ "--us-fg": theme?.foreground ?? defaults.foreground,
20
+ "--us-muted": theme?.muted ?? defaults.muted,
21
+ "--us-border": theme?.border ?? defaults.border,
22
+ "--us-radius": theme?.radius ?? "12px"
23
+ };
24
+ }
25
+ var LIGHT_DEFAULTS = {
26
+ primary: "#4f46e5",
27
+ background: "#ffffff",
28
+ foreground: "#0f172a",
29
+ muted: "#64748b",
30
+ border: "#e2e8f0"
31
+ };
32
+ var DARK_DEFAULTS = {
33
+ primary: "#6366f1",
34
+ background: "#0f172a",
35
+ foreground: "#f1f5f9",
36
+ muted: "#94a3b8",
37
+ border: "#1e293b"
38
+ };
39
+ var BASE_CSS = `
40
+ .us-picker-backdrop {
41
+ position: fixed; inset: 0; z-index: 2147483000;
42
+ background: rgba(2, 6, 23, 0.55);
43
+ display: flex; align-items: center; justify-content: center;
44
+ padding: 16px; font-family: ui-sans-serif, system-ui, sans-serif;
45
+ animation: us-fade 120ms ease-out;
46
+ }
47
+ @keyframes us-fade { from { opacity: 0; } to { opacity: 1; } }
48
+ .us-picker {
49
+ background: var(--us-bg); color: var(--us-fg);
50
+ border-radius: var(--us-radius);
51
+ width: 100%; max-width: 480px; max-height: calc(100vh - 32px);
52
+ display: flex; flex-direction: column;
53
+ box-shadow: 0 25px 50px -12px rgba(0,0,0,0.4);
54
+ overflow: hidden;
55
+ }
56
+ .us-picker * { box-sizing: border-box; }
57
+ .us-picker-header {
58
+ display: flex; align-items: center; gap: 12px;
59
+ padding: 16px 20px; border-bottom: 1px solid var(--us-border);
60
+ }
61
+ .us-picker-header img { height: 24px; }
62
+ .us-picker-title { font-weight: 600; font-size: 16px; flex: 1; }
63
+ .us-picker-close {
64
+ background: none; border: 0; cursor: pointer;
65
+ color: var(--us-muted); font-size: 22px; line-height: 1;
66
+ padding: 4px 8px; border-radius: 6px;
67
+ }
68
+ .us-picker-close:hover { background: var(--us-border); color: var(--us-fg); }
69
+ .us-picker-body { padding: 20px; overflow-y: auto; }
70
+ .us-dropzone {
71
+ border: 2px dashed var(--us-border); border-radius: var(--us-radius);
72
+ padding: 32px 20px; text-align: center; cursor: pointer;
73
+ transition: border-color 120ms, background 120ms;
74
+ }
75
+ .us-dropzone:hover, .us-dropzone[data-drag="over"] {
76
+ border-color: var(--us-primary);
77
+ background: color-mix(in srgb, var(--us-primary) 5%, transparent);
78
+ }
79
+ .us-dropzone-title { font-weight: 500; margin-bottom: 4px; }
80
+ .us-dropzone-hint { color: var(--us-muted); font-size: 13px; }
81
+ .us-file-list { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
82
+ .us-file {
83
+ display: flex; align-items: center; gap: 12px;
84
+ padding: 10px 12px; border: 1px solid var(--us-border);
85
+ border-radius: 10px; font-size: 14px;
86
+ }
87
+ .us-file-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
88
+ .us-file-meta { color: var(--us-muted); font-size: 12px; }
89
+ .us-file-progress {
90
+ height: 4px; background: var(--us-border); border-radius: 999px; overflow: hidden;
91
+ margin-top: 6px;
92
+ }
93
+ .us-file-progress-bar {
94
+ height: 100%; background: var(--us-primary);
95
+ width: 0%; transition: width 200ms;
96
+ }
97
+ .us-file[data-state="done"] .us-file-progress-bar { background: #16a34a; width: 100%; }
98
+ .us-file[data-state="failed"] .us-file-progress-bar { background: #dc2626; }
99
+ .us-actions {
100
+ display: flex; gap: 8px; justify-content: flex-end;
101
+ padding: 14px 20px; border-top: 1px solid var(--us-border);
102
+ }
103
+ .us-btn {
104
+ padding: 8px 14px; border-radius: 8px; border: 1px solid var(--us-border);
105
+ background: transparent; color: var(--us-fg); cursor: pointer; font-size: 14px;
106
+ font-weight: 500;
107
+ }
108
+ .us-btn:hover { background: var(--us-border); }
109
+ .us-btn-primary {
110
+ background: var(--us-primary); color: white; border-color: var(--us-primary);
111
+ }
112
+ .us-btn-primary:hover { filter: brightness(0.95); }
113
+ .us-btn[disabled] { opacity: 0.5; cursor: not-allowed; }
114
+ .us-footer {
115
+ padding: 8px 20px; font-size: 11px; color: var(--us-muted); text-align: center;
116
+ }
117
+ .us-footer a { color: var(--us-muted); }
118
+ `;
119
+
120
+ // src/picker/picker.ts
121
+ function mergeConfig(server, runtime) {
122
+ const merged = { ...runtime };
123
+ const runtimeBranding = runtime.branding || {};
124
+ merged.branding = {
125
+ logoUrl: runtimeBranding.logoUrl ?? server.branding.logoUrl ?? void 0,
126
+ title: runtimeBranding.title ?? server.branding.title ?? void 0,
127
+ hideFooter: runtimeBranding.hideFooter ?? server.branding.hideFooter
128
+ // PickerBranding has no footerText today — keep server's value internal.
129
+ };
130
+ const runtimeTheme = runtime.theme || {};
131
+ merged.theme = {
132
+ primary: runtimeTheme.primary ?? server.theme.primary ?? void 0,
133
+ background: runtimeTheme.background ?? server.theme.background ?? void 0,
134
+ foreground: runtimeTheme.foreground ?? server.theme.foreground ?? void 0,
135
+ border: runtimeTheme.border ?? server.theme.border ?? void 0,
136
+ radius: runtimeTheme.radius ?? server.theme.radius ?? void 0,
137
+ mode: runtimeTheme.mode ?? server.theme.mode ?? void 0
138
+ };
139
+ merged.maxFileSize = runtime.maxFileSize ?? server.constraints.maxFileSizeBytes;
140
+ merged.maxFiles = runtime.maxFiles ?? server.constraints.maxFilesPerUpload;
141
+ if (!runtime.accept && server.constraints.allowedMimeTypes?.length) {
142
+ const types = server.constraints.allowedMimeTypes.filter((t) => t !== "*/*");
143
+ if (types.length > 0) merged.accept = types.join(",");
144
+ }
145
+ return merged;
146
+ }
147
+ var DEFAULT_TITLE = "Upload files";
148
+ var FOOTER_LINK = "https://unionstack.mastersunion.link";
149
+ var Picker = class {
150
+ constructor(client, opts) {
151
+ this.client = client;
152
+ this.opts = opts;
153
+ this.$backdrop = null;
154
+ this.$list = null;
155
+ this.$confirm = null;
156
+ this.$cancel = null;
157
+ this.$closeBtn = null;
158
+ this.$input = null;
159
+ this.items = [];
160
+ this.abortCtrl = new AbortController();
161
+ this.uploadStarted = false;
162
+ this.resolved = false;
163
+ this.donePromise = new Promise((res) => {
164
+ this.resolvePromise = res;
165
+ });
166
+ }
167
+ // ---- public api ---------------------------------------------------------
168
+ async open() {
169
+ if (typeof document === "undefined") {
170
+ throw new Error("[union-stack] Picker requires a browser environment.");
171
+ }
172
+ try {
173
+ const serverConfig = await this.client.pickerConfigPromise;
174
+ if (serverConfig) this.opts = mergeConfig(serverConfig, this.opts);
175
+ } catch {
176
+ }
177
+ ensureStyles();
178
+ this.mount();
179
+ this.opts.onOpen?.();
180
+ return this.donePromise;
181
+ }
182
+ close() {
183
+ this.unmount();
184
+ if (!this.resolved) this.resolveResult();
185
+ }
186
+ cancel() {
187
+ this.abortCtrl.abort();
188
+ this.opts.onCancel?.();
189
+ this.close();
190
+ }
191
+ // ---- mount / dom --------------------------------------------------------
192
+ mount() {
193
+ const root = document.createElement("div");
194
+ root.className = "us-picker-backdrop";
195
+ Object.entries(themeToCssVars(this.opts.theme)).forEach(([k, v]) => {
196
+ root.style.setProperty(k, v);
197
+ });
198
+ root.addEventListener("click", (e) => {
199
+ if (e.target === root && !this.uploadStarted) this.cancel();
200
+ });
201
+ const panel = el("div", "us-picker");
202
+ const header = el("div", "us-picker-header");
203
+ if (this.opts.branding?.logoUrl) {
204
+ const logo = document.createElement("img");
205
+ logo.src = this.opts.branding.logoUrl;
206
+ logo.alt = "logo";
207
+ header.appendChild(logo);
208
+ }
209
+ const title = el("div", "us-picker-title", this.opts.branding?.title ?? DEFAULT_TITLE);
210
+ header.appendChild(title);
211
+ this.$closeBtn = document.createElement("button");
212
+ this.$closeBtn.type = "button";
213
+ this.$closeBtn.className = "us-picker-close";
214
+ this.$closeBtn.setAttribute("aria-label", "Close");
215
+ this.$closeBtn.textContent = "\xD7";
216
+ this.$closeBtn.onclick = () => this.cancel();
217
+ header.appendChild(this.$closeBtn);
218
+ panel.appendChild(header);
219
+ const body = el("div", "us-picker-body");
220
+ body.appendChild(this.renderDropzone());
221
+ this.$list = el("div", "us-file-list");
222
+ body.appendChild(this.$list);
223
+ panel.appendChild(body);
224
+ const actions = el("div", "us-actions");
225
+ this.$cancel = document.createElement("button");
226
+ this.$cancel.type = "button";
227
+ this.$cancel.className = "us-btn";
228
+ this.$cancel.textContent = "Cancel";
229
+ this.$cancel.onclick = () => this.cancel();
230
+ this.$confirm = document.createElement("button");
231
+ this.$confirm.type = "button";
232
+ this.$confirm.className = "us-btn us-btn-primary";
233
+ this.$confirm.textContent = "Upload";
234
+ this.$confirm.disabled = true;
235
+ this.$confirm.onclick = () => this.startUpload();
236
+ actions.appendChild(this.$cancel);
237
+ actions.appendChild(this.$confirm);
238
+ panel.appendChild(actions);
239
+ if (!this.opts.branding?.hideFooter) {
240
+ const footer = el("div", "us-footer");
241
+ footer.innerHTML = `Powered by <a href="${FOOTER_LINK}" target="_blank" rel="noopener">UnionStack</a>`;
242
+ panel.appendChild(footer);
243
+ }
244
+ root.appendChild(panel);
245
+ (this.opts.container ?? document.body).appendChild(root);
246
+ this.$backdrop = root;
247
+ }
248
+ renderDropzone() {
249
+ const dz = el("div", "us-dropzone");
250
+ dz.setAttribute("role", "button");
251
+ dz.setAttribute("tabindex", "0");
252
+ dz.appendChild(el("div", "us-dropzone-title", "Drag files here"));
253
+ dz.appendChild(el("div", "us-dropzone-hint", "or click to browse"));
254
+ const input = document.createElement("input");
255
+ input.type = "file";
256
+ input.multiple = (this.opts.maxFiles ?? 10) > 1;
257
+ if (this.opts.accept) input.accept = this.opts.accept;
258
+ input.style.display = "none";
259
+ input.onchange = () => {
260
+ if (input.files) this.addFiles(Array.from(input.files));
261
+ input.value = "";
262
+ };
263
+ this.$input = input;
264
+ dz.appendChild(input);
265
+ dz.onclick = () => input.click();
266
+ dz.onkeydown = (e) => {
267
+ if (e.key === "Enter" || e.key === " ") {
268
+ e.preventDefault();
269
+ input.click();
270
+ }
271
+ };
272
+ dz.addEventListener("dragover", (e) => {
273
+ e.preventDefault();
274
+ dz.setAttribute("data-drag", "over");
275
+ });
276
+ dz.addEventListener("dragleave", () => dz.removeAttribute("data-drag"));
277
+ dz.addEventListener("drop", (e) => {
278
+ e.preventDefault();
279
+ dz.removeAttribute("data-drag");
280
+ const dropped = e.dataTransfer?.files;
281
+ if (dropped) this.addFiles(Array.from(dropped));
282
+ });
283
+ return dz;
284
+ }
285
+ unmount() {
286
+ if (this.$backdrop?.parentNode) this.$backdrop.parentNode.removeChild(this.$backdrop);
287
+ this.$backdrop = null;
288
+ this.opts.onClose?.();
289
+ }
290
+ // ---- file selection -----------------------------------------------------
291
+ addFiles(files) {
292
+ const cap = this.opts.maxFiles ?? Infinity;
293
+ const remaining = cap - this.items.length;
294
+ if (remaining <= 0) return;
295
+ const chosen = files.slice(0, remaining);
296
+ for (const file of chosen) {
297
+ if (this.opts.maxFileSize && file.size > this.opts.maxFileSize) {
298
+ const item2 = {
299
+ uploadId: cryptoId(),
300
+ file,
301
+ state: "failed",
302
+ progress: 0,
303
+ error: `File exceeds ${formatBytes(this.opts.maxFileSize)} limit`
304
+ };
305
+ this.items.push(item2);
306
+ this.renderItem(item2);
307
+ continue;
308
+ }
309
+ const item = {
310
+ uploadId: cryptoId(),
311
+ file,
312
+ state: "queued",
313
+ progress: 0
314
+ };
315
+ this.items.push(item);
316
+ this.renderItem(item);
317
+ }
318
+ this.refreshConfirm();
319
+ }
320
+ renderItem(item) {
321
+ if (!this.$list) return;
322
+ const row = el("div", "us-file");
323
+ row.dataset.state = item.state;
324
+ row.dataset.uploadId = item.uploadId;
325
+ const main = el("div", "", "");
326
+ main.style.flex = "1";
327
+ main.style.minWidth = "0";
328
+ main.appendChild(el("div", "us-file-name", item.file.name));
329
+ const meta = el("div", "us-file-meta", formatBytes(item.file.size));
330
+ main.appendChild(meta);
331
+ const progress = el("div", "us-file-progress");
332
+ const bar = el("div", "us-file-progress-bar");
333
+ progress.appendChild(bar);
334
+ main.appendChild(progress);
335
+ row.appendChild(main);
336
+ const status = el("div", "us-file-meta");
337
+ status.style.minWidth = "60px";
338
+ status.style.textAlign = "right";
339
+ status.textContent = item.state === "failed" ? item.error || "failed" : item.state === "done" ? "done" : "0%";
340
+ row.appendChild(status);
341
+ item.$row = row;
342
+ item.$bar = bar;
343
+ item.$status = status;
344
+ this.$list.appendChild(row);
345
+ }
346
+ setItemState(item, state, progress) {
347
+ item.state = state;
348
+ if (progress !== void 0) item.progress = progress;
349
+ if (item.$row) item.$row.dataset.state = state;
350
+ if (item.$bar) item.$bar.style.width = `${item.progress}%`;
351
+ if (item.$status) {
352
+ if (state === "failed") item.$status.textContent = item.error || "failed";
353
+ else if (state === "done") item.$status.textContent = "done";
354
+ else item.$status.textContent = `${Math.round(item.progress)}%`;
355
+ }
356
+ }
357
+ refreshConfirm() {
358
+ if (!this.$confirm) return;
359
+ const queued = this.items.filter((i) => i.state === "queued").length;
360
+ this.$confirm.disabled = queued === 0 || this.uploadStarted;
361
+ }
362
+ // ---- upload -------------------------------------------------------------
363
+ async startUpload() {
364
+ if (this.uploadStarted) return;
365
+ const queued = this.items.filter((i) => i.state === "queued");
366
+ if (queued.length === 0) return;
367
+ this.uploadStarted = true;
368
+ if (this.$confirm) {
369
+ this.$confirm.disabled = true;
370
+ this.$confirm.textContent = "Uploading\u2026";
371
+ }
372
+ if (this.$cancel) this.$cancel.textContent = "Stop";
373
+ if (this.$closeBtn) this.$closeBtn.disabled = true;
374
+ if (this.$input) this.$input.disabled = true;
375
+ const itemByUploadId = /* @__PURE__ */ new Map();
376
+ const filesToUpload = queued.map((i) => i.file);
377
+ try {
378
+ await this.client.uploadMany(filesToUpload, {
379
+ ...this.opts,
380
+ signal: this.abortCtrl.signal,
381
+ onUploadStarted: (pickedFiles) => {
382
+ pickedFiles.forEach((p, idx) => {
383
+ const item = queued[idx];
384
+ if (item) {
385
+ item.uploadId = p.uploadId;
386
+ if (item.$row) item.$row.dataset.uploadId = p.uploadId;
387
+ itemByUploadId.set(p.uploadId, item);
388
+ }
389
+ });
390
+ this.opts.onUploadStarted?.(pickedFiles);
391
+ },
392
+ onFileUploadStarted: (p) => {
393
+ const item = itemByUploadId.get(p.uploadId);
394
+ if (item) this.setItemState(item, "uploading", 0);
395
+ this.opts.onFileUploadStarted?.(p);
396
+ },
397
+ onFileUploadProgress: (p, ev) => {
398
+ const item = itemByUploadId.get(p.uploadId);
399
+ if (item) this.setItemState(item, "uploading", ev.totalPercent);
400
+ this.opts.onFileUploadProgress?.(p, ev);
401
+ },
402
+ onFileUploadFinished: (f) => {
403
+ const item = itemByUploadId.get(f.uploadId);
404
+ if (item) {
405
+ item.uploaded = f;
406
+ this.setItemState(item, "done", 100);
407
+ }
408
+ this.opts.onFileUploadFinished?.(f);
409
+ },
410
+ onFileUploadFailed: (p, err) => {
411
+ const item = itemByUploadId.get(p.uploadId);
412
+ if (item) {
413
+ item.error = err.message;
414
+ this.setItemState(item, "failed");
415
+ }
416
+ this.opts.onFileUploadFailed?.(p, err);
417
+ },
418
+ onUploadDone: (r) => {
419
+ this.opts.onUploadDone?.(r);
420
+ this.resolveResult(r);
421
+ this.unmount();
422
+ },
423
+ onError: (err) => {
424
+ this.opts.onError?.(err);
425
+ this.resolveResult();
426
+ this.unmount();
427
+ }
428
+ });
429
+ } catch {
430
+ if (!this.resolved) {
431
+ this.resolveResult();
432
+ this.unmount();
433
+ }
434
+ }
435
+ }
436
+ resolveResult(result) {
437
+ if (this.resolved) return;
438
+ this.resolved = true;
439
+ const fallback = result ?? {
440
+ filesUploaded: this.items.filter((i) => i.uploaded).map((i) => i.uploaded),
441
+ filesFailed: this.items.filter((i) => i.state === "failed").map((i) => ({
442
+ file: {
443
+ uploadId: i.uploadId,
444
+ filename: i.file.name,
445
+ mimetype: i.file.type || "application/octet-stream",
446
+ size: i.file.size,
447
+ source: "local"
448
+ },
449
+ error: {
450
+ code: "VALIDATION",
451
+ message: i.error || "failed",
452
+ retryable: false
453
+ }
454
+ }))
455
+ };
456
+ this.resolvePromise(fallback);
457
+ }
458
+ };
459
+ function el(tag, className, text) {
460
+ const node = document.createElement(tag);
461
+ if (className) node.className = className;
462
+ if (text !== void 0) node.textContent = text;
463
+ return node;
464
+ }
465
+ function formatBytes(n) {
466
+ if (n < 1024) return `${n} B`;
467
+ if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
468
+ if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)} MB`;
469
+ return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
470
+ }
471
+ function cryptoId() {
472
+ if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
473
+ return crypto.randomUUID().replace(/-/g, "");
474
+ }
475
+ return Math.random().toString(36).slice(2) + Date.now().toString(36);
476
+ }
477
+ function openPicker(client, opts) {
478
+ const picker = new Picker(client, opts);
479
+ return {
480
+ open: () => picker.open(),
481
+ close: () => picker.close(),
482
+ cancel: () => picker.cancel()
483
+ };
484
+ }
485
+
486
+ exports.Picker = Picker;
487
+ exports.openPicker = openPicker;
488
+ //# sourceMappingURL=picker.cjs.map
489
+ //# sourceMappingURL=picker.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/picker/styles.ts","../src/picker/picker.ts"],"names":["el","item"],"mappings":"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","file":"picker.cjs","sourcesContent":["import type { PickerTheme } from './types.js';\n\nconst STYLE_ID = 'unionstack-picker-styles';\n\n/** Inject a single <style> tag once. Idempotent — safe to call repeatedly. */\nexport function ensureStyles(): void {\n if (typeof document === 'undefined') return;\n if (document.getElementById(STYLE_ID)) return;\n const el = document.createElement('style');\n el.id = STYLE_ID;\n el.textContent = BASE_CSS;\n document.head.appendChild(el);\n}\n\n/** Resolve a partial theme into CSS variables on the modal root. */\nexport function themeToCssVars(theme: PickerTheme | undefined): Record<string, string> {\n const mode = theme?.mode || 'light';\n const defaults = mode === 'dark' ? DARK_DEFAULTS : LIGHT_DEFAULTS;\n return {\n '--us-primary': theme?.primary ?? defaults.primary,\n '--us-bg': theme?.background ?? defaults.background,\n '--us-fg': theme?.foreground ?? defaults.foreground,\n '--us-muted': theme?.muted ?? defaults.muted,\n '--us-border': theme?.border ?? defaults.border,\n '--us-radius': theme?.radius ?? '12px',\n };\n}\n\nconst LIGHT_DEFAULTS = {\n primary: '#4f46e5',\n background: '#ffffff',\n foreground: '#0f172a',\n muted: '#64748b',\n border: '#e2e8f0',\n};\nconst DARK_DEFAULTS = {\n primary: '#6366f1',\n background: '#0f172a',\n foreground: '#f1f5f9',\n muted: '#94a3b8',\n border: '#1e293b',\n};\n\n// All selectors namespaced under .us-picker to avoid bleed into host page.\nconst BASE_CSS = `\n.us-picker-backdrop {\n position: fixed; inset: 0; z-index: 2147483000;\n background: rgba(2, 6, 23, 0.55);\n display: flex; align-items: center; justify-content: center;\n padding: 16px; font-family: ui-sans-serif, system-ui, sans-serif;\n animation: us-fade 120ms ease-out;\n}\n@keyframes us-fade { from { opacity: 0; } to { opacity: 1; } }\n.us-picker {\n background: var(--us-bg); color: var(--us-fg);\n border-radius: var(--us-radius);\n width: 100%; max-width: 480px; max-height: calc(100vh - 32px);\n display: flex; flex-direction: column;\n box-shadow: 0 25px 50px -12px rgba(0,0,0,0.4);\n overflow: hidden;\n}\n.us-picker * { box-sizing: border-box; }\n.us-picker-header {\n display: flex; align-items: center; gap: 12px;\n padding: 16px 20px; border-bottom: 1px solid var(--us-border);\n}\n.us-picker-header img { height: 24px; }\n.us-picker-title { font-weight: 600; font-size: 16px; flex: 1; }\n.us-picker-close {\n background: none; border: 0; cursor: pointer;\n color: var(--us-muted); font-size: 22px; line-height: 1;\n padding: 4px 8px; border-radius: 6px;\n}\n.us-picker-close:hover { background: var(--us-border); color: var(--us-fg); }\n.us-picker-body { padding: 20px; overflow-y: auto; }\n.us-dropzone {\n border: 2px dashed var(--us-border); border-radius: var(--us-radius);\n padding: 32px 20px; text-align: center; cursor: pointer;\n transition: border-color 120ms, background 120ms;\n}\n.us-dropzone:hover, .us-dropzone[data-drag=\"over\"] {\n border-color: var(--us-primary);\n background: color-mix(in srgb, var(--us-primary) 5%, transparent);\n}\n.us-dropzone-title { font-weight: 500; margin-bottom: 4px; }\n.us-dropzone-hint { color: var(--us-muted); font-size: 13px; }\n.us-file-list { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }\n.us-file {\n display: flex; align-items: center; gap: 12px;\n padding: 10px 12px; border: 1px solid var(--us-border);\n border-radius: 10px; font-size: 14px;\n}\n.us-file-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.us-file-meta { color: var(--us-muted); font-size: 12px; }\n.us-file-progress {\n height: 4px; background: var(--us-border); border-radius: 999px; overflow: hidden;\n margin-top: 6px;\n}\n.us-file-progress-bar {\n height: 100%; background: var(--us-primary);\n width: 0%; transition: width 200ms;\n}\n.us-file[data-state=\"done\"] .us-file-progress-bar { background: #16a34a; width: 100%; }\n.us-file[data-state=\"failed\"] .us-file-progress-bar { background: #dc2626; }\n.us-actions {\n display: flex; gap: 8px; justify-content: flex-end;\n padding: 14px 20px; border-top: 1px solid var(--us-border);\n}\n.us-btn {\n padding: 8px 14px; border-radius: 8px; border: 1px solid var(--us-border);\n background: transparent; color: var(--us-fg); cursor: pointer; font-size: 14px;\n font-weight: 500;\n}\n.us-btn:hover { background: var(--us-border); }\n.us-btn-primary {\n background: var(--us-primary); color: white; border-color: var(--us-primary);\n}\n.us-btn-primary:hover { filter: brightness(0.95); }\n.us-btn[disabled] { opacity: 0.5; cursor: not-allowed; }\n.us-footer {\n padding: 8px 20px; font-size: 11px; color: var(--us-muted); text-align: center;\n}\n.us-footer a { color: var(--us-muted); }\n`;\n","import type { UnionStackClient } from '../client.js';\nimport type { PickerConfig, UploadError } from '../types.js';\nimport type {\n PickResponse,\n PickedFile,\n PickerHandle,\n PickerOptions,\n UploadedFile,\n} from './types.js';\nimport { ensureStyles, themeToCssVars } from './styles.js';\n\n// Merge server-managed picker config with runtime options. Runtime options\n// always win — they're the explicit dev escape hatch.\nfunction mergeConfig(server: PickerConfig, runtime: PickerOptions): PickerOptions {\n const merged: PickerOptions = { ...runtime };\n\n // Branding: shallow merge, runtime wins per-field.\n const runtimeBranding = runtime.branding || {};\n merged.branding = {\n logoUrl: runtimeBranding.logoUrl ?? server.branding.logoUrl ?? undefined,\n title: runtimeBranding.title ?? server.branding.title ?? undefined,\n hideFooter: runtimeBranding.hideFooter ?? server.branding.hideFooter,\n // PickerBranding has no footerText today — keep server's value internal.\n };\n\n // Theme: shallow merge.\n const runtimeTheme = runtime.theme || {};\n merged.theme = {\n primary: runtimeTheme.primary ?? server.theme.primary ?? undefined,\n background: runtimeTheme.background ?? server.theme.background ?? undefined,\n foreground: runtimeTheme.foreground ?? server.theme.foreground ?? undefined,\n border: runtimeTheme.border ?? server.theme.border ?? undefined,\n radius: runtimeTheme.radius ?? server.theme.radius ?? undefined,\n mode: runtimeTheme.mode ?? server.theme.mode ?? undefined,\n };\n\n // Constraints inform the dropzone (accept attr, size cap).\n merged.maxFileSize = runtime.maxFileSize ?? server.constraints.maxFileSizeBytes;\n merged.maxFiles = runtime.maxFiles ?? server.constraints.maxFilesPerUpload;\n if (!runtime.accept && server.constraints.allowedMimeTypes?.length) {\n const types = server.constraints.allowedMimeTypes.filter(t => t !== '*/*');\n if (types.length > 0) merged.accept = types.join(',');\n }\n return merged;\n}\n\ntype FileItemState = 'queued' | 'uploading' | 'done' | 'failed' | 'cancelled';\n\ninterface FileItem {\n uploadId: string; // matches PickedFile.uploadId once described\n file: File;\n state: FileItemState;\n progress: number; // 0-100\n error?: string;\n uploaded?: UploadedFile;\n // DOM refs we mutate on progress.\n $row?: HTMLElement;\n $bar?: HTMLElement;\n $status?: HTMLElement;\n}\n\nconst DEFAULT_TITLE = 'Upload files';\nconst FOOTER_LINK = 'https://unionstack.mastersunion.link';\n\n/**\n * Single-shot file picker modal. Built imperatively (no framework) so it can\n * be lazy-loaded by either the vanilla SDK or the React wrapper.\n */\nexport class Picker {\n private $backdrop: HTMLElement | null = null;\n private $list: HTMLElement | null = null;\n private $confirm: HTMLButtonElement | null = null;\n private $cancel: HTMLButtonElement | null = null;\n private $closeBtn: HTMLButtonElement | null = null;\n private $input: HTMLInputElement | null = null;\n\n private items: FileItem[] = [];\n private abortCtrl = new AbortController();\n private uploadStarted = false;\n private resolved = false;\n private resolvePromise!: (r: PickResponse) => void;\n private donePromise: Promise<PickResponse>;\n\n constructor(\n private client: UnionStackClient,\n private opts: PickerOptions,\n ) {\n this.donePromise = new Promise<PickResponse>(res => { this.resolvePromise = res; });\n }\n\n // ---- public api ---------------------------------------------------------\n\n async open(): Promise<PickResponse> {\n if (typeof document === 'undefined') {\n throw new Error('[union-stack] Picker requires a browser environment.');\n }\n // Pull server-side branding/theme defaults before painting the modal.\n // Runtime opts win over server config — see mergeConfig below.\n try {\n const serverConfig = await this.client.pickerConfigPromise;\n if (serverConfig) this.opts = mergeConfig(serverConfig, this.opts);\n } catch { /* fall through with whatever opts the caller passed */ }\n\n ensureStyles();\n this.mount();\n this.opts.onOpen?.();\n return this.donePromise;\n }\n\n close(): void {\n this.unmount();\n // Resolve with whatever was collected so callers awaiting open() never hang.\n if (!this.resolved) this.resolveResult();\n }\n\n cancel(): void {\n this.abortCtrl.abort();\n this.opts.onCancel?.();\n this.close();\n }\n\n // ---- mount / dom --------------------------------------------------------\n\n private mount() {\n const root = document.createElement('div');\n root.className = 'us-picker-backdrop';\n Object.entries(themeToCssVars(this.opts.theme)).forEach(([k, v]) => {\n root.style.setProperty(k, v);\n });\n root.addEventListener('click', e => {\n if (e.target === root && !this.uploadStarted) this.cancel();\n });\n\n const panel = el('div', 'us-picker');\n\n // Header\n const header = el('div', 'us-picker-header');\n if (this.opts.branding?.logoUrl) {\n const logo = document.createElement('img');\n logo.src = this.opts.branding.logoUrl;\n logo.alt = 'logo';\n header.appendChild(logo);\n }\n const title = el('div', 'us-picker-title', this.opts.branding?.title ?? DEFAULT_TITLE);\n header.appendChild(title);\n this.$closeBtn = document.createElement('button');\n this.$closeBtn.type = 'button';\n this.$closeBtn.className = 'us-picker-close';\n this.$closeBtn.setAttribute('aria-label', 'Close');\n this.$closeBtn.textContent = '×';\n this.$closeBtn.onclick = () => this.cancel();\n header.appendChild(this.$closeBtn);\n panel.appendChild(header);\n\n // Body\n const body = el('div', 'us-picker-body');\n body.appendChild(this.renderDropzone());\n this.$list = el('div', 'us-file-list');\n body.appendChild(this.$list);\n panel.appendChild(body);\n\n // Actions\n const actions = el('div', 'us-actions');\n this.$cancel = document.createElement('button');\n this.$cancel.type = 'button';\n this.$cancel.className = 'us-btn';\n this.$cancel.textContent = 'Cancel';\n this.$cancel.onclick = () => this.cancel();\n this.$confirm = document.createElement('button');\n this.$confirm.type = 'button';\n this.$confirm.className = 'us-btn us-btn-primary';\n this.$confirm.textContent = 'Upload';\n this.$confirm.disabled = true;\n this.$confirm.onclick = () => this.startUpload();\n actions.appendChild(this.$cancel);\n actions.appendChild(this.$confirm);\n panel.appendChild(actions);\n\n if (!this.opts.branding?.hideFooter) {\n const footer = el('div', 'us-footer');\n footer.innerHTML = `Powered by <a href=\"${FOOTER_LINK}\" target=\"_blank\" rel=\"noopener\">UnionStack</a>`;\n panel.appendChild(footer);\n }\n\n root.appendChild(panel);\n (this.opts.container ?? document.body).appendChild(root);\n this.$backdrop = root;\n }\n\n private renderDropzone(): HTMLElement {\n const dz = el('div', 'us-dropzone');\n dz.setAttribute('role', 'button');\n dz.setAttribute('tabindex', '0');\n dz.appendChild(el('div', 'us-dropzone-title', 'Drag files here'));\n dz.appendChild(el('div', 'us-dropzone-hint', 'or click to browse'));\n\n const input = document.createElement('input');\n input.type = 'file';\n input.multiple = (this.opts.maxFiles ?? 10) > 1;\n if (this.opts.accept) input.accept = this.opts.accept;\n input.style.display = 'none';\n input.onchange = () => {\n if (input.files) this.addFiles(Array.from(input.files));\n input.value = '';\n };\n this.$input = input;\n dz.appendChild(input);\n\n dz.onclick = () => input.click();\n dz.onkeydown = e => {\n if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); input.click(); }\n };\n\n dz.addEventListener('dragover', e => {\n e.preventDefault();\n dz.setAttribute('data-drag', 'over');\n });\n dz.addEventListener('dragleave', () => dz.removeAttribute('data-drag'));\n dz.addEventListener('drop', e => {\n e.preventDefault();\n dz.removeAttribute('data-drag');\n const dropped = e.dataTransfer?.files;\n if (dropped) this.addFiles(Array.from(dropped));\n });\n\n return dz;\n }\n\n private unmount() {\n if (this.$backdrop?.parentNode) this.$backdrop.parentNode.removeChild(this.$backdrop);\n this.$backdrop = null;\n this.opts.onClose?.();\n }\n\n // ---- file selection -----------------------------------------------------\n\n private addFiles(files: File[]) {\n const cap = this.opts.maxFiles ?? Infinity;\n const remaining = cap - this.items.length;\n if (remaining <= 0) return;\n const chosen = files.slice(0, remaining);\n\n for (const file of chosen) {\n if (this.opts.maxFileSize && file.size > this.opts.maxFileSize) {\n // Skip the file but surface a row in the list so the user sees why.\n const item: FileItem = {\n uploadId: cryptoId(),\n file, state: 'failed', progress: 0,\n error: `File exceeds ${formatBytes(this.opts.maxFileSize)} limit`,\n };\n this.items.push(item);\n this.renderItem(item);\n continue;\n }\n\n const item: FileItem = {\n uploadId: cryptoId(),\n file, state: 'queued', progress: 0,\n };\n this.items.push(item);\n this.renderItem(item);\n }\n\n this.refreshConfirm();\n }\n\n private renderItem(item: FileItem) {\n if (!this.$list) return;\n const row = el('div', 'us-file');\n row.dataset.state = item.state;\n row.dataset.uploadId = item.uploadId;\n\n const main = el('div', '', '');\n main.style.flex = '1';\n main.style.minWidth = '0';\n main.appendChild(el('div', 'us-file-name', item.file.name));\n\n const meta = el('div', 'us-file-meta', formatBytes(item.file.size));\n main.appendChild(meta);\n\n const progress = el('div', 'us-file-progress');\n const bar = el('div', 'us-file-progress-bar');\n progress.appendChild(bar);\n main.appendChild(progress);\n row.appendChild(main);\n\n const status = el('div', 'us-file-meta');\n status.style.minWidth = '60px';\n status.style.textAlign = 'right';\n status.textContent = item.state === 'failed'\n ? (item.error || 'failed')\n : item.state === 'done' ? 'done' : '0%';\n row.appendChild(status);\n\n item.$row = row;\n item.$bar = bar;\n item.$status = status;\n\n this.$list.appendChild(row);\n }\n\n private setItemState(item: FileItem, state: FileItemState, progress?: number) {\n item.state = state;\n if (progress !== undefined) item.progress = progress;\n if (item.$row) item.$row.dataset.state = state;\n if (item.$bar) item.$bar.style.width = `${item.progress}%`;\n if (item.$status) {\n if (state === 'failed') item.$status.textContent = item.error || 'failed';\n else if (state === 'done') item.$status.textContent = 'done';\n else item.$status.textContent = `${Math.round(item.progress)}%`;\n }\n }\n\n private refreshConfirm() {\n if (!this.$confirm) return;\n const queued = this.items.filter(i => i.state === 'queued').length;\n this.$confirm.disabled = queued === 0 || this.uploadStarted;\n }\n\n // ---- upload -------------------------------------------------------------\n\n private async startUpload() {\n if (this.uploadStarted) return;\n const queued = this.items.filter(i => i.state === 'queued');\n if (queued.length === 0) return;\n\n this.uploadStarted = true;\n if (this.$confirm) {\n this.$confirm.disabled = true;\n this.$confirm.textContent = 'Uploading…';\n }\n if (this.$cancel) this.$cancel.textContent = 'Stop';\n if (this.$closeBtn) this.$closeBtn.disabled = true;\n if (this.$input) this.$input.disabled = true;\n\n // Map PickedFile.uploadId → FileItem so callbacks update the right row.\n const itemByUploadId = new Map<string, FileItem>();\n const filesToUpload = queued.map(i => i.file);\n\n try {\n await this.client.uploadMany(filesToUpload, {\n ...this.opts,\n signal: this.abortCtrl.signal,\n onUploadStarted: (pickedFiles: PickedFile[]) => {\n // Pair each PickedFile to the queued FileItem in order.\n pickedFiles.forEach((p, idx) => {\n const item = queued[idx];\n if (item) {\n item.uploadId = p.uploadId;\n if (item.$row) item.$row.dataset.uploadId = p.uploadId;\n itemByUploadId.set(p.uploadId, item);\n }\n });\n this.opts.onUploadStarted?.(pickedFiles);\n },\n onFileUploadStarted: p => {\n const item = itemByUploadId.get(p.uploadId);\n if (item) this.setItemState(item, 'uploading', 0);\n this.opts.onFileUploadStarted?.(p);\n },\n onFileUploadProgress: (p, ev) => {\n const item = itemByUploadId.get(p.uploadId);\n if (item) this.setItemState(item, 'uploading', ev.totalPercent);\n this.opts.onFileUploadProgress?.(p, ev);\n },\n onFileUploadFinished: f => {\n const item = itemByUploadId.get(f.uploadId);\n if (item) {\n item.uploaded = f;\n this.setItemState(item, 'done', 100);\n }\n this.opts.onFileUploadFinished?.(f);\n },\n onFileUploadFailed: (p, err) => {\n const item = itemByUploadId.get(p.uploadId);\n if (item) {\n item.error = err.message;\n this.setItemState(item, 'failed');\n }\n this.opts.onFileUploadFailed?.(p, err);\n },\n onUploadDone: r => {\n this.opts.onUploadDone?.(r);\n this.resolveResult(r);\n this.unmount();\n },\n onError: (err: UploadError) => {\n this.opts.onError?.(err);\n this.resolveResult();\n this.unmount();\n },\n });\n } catch {\n // Errors already surfaced via onError / onFileUploadFailed.\n if (!this.resolved) {\n this.resolveResult();\n this.unmount();\n }\n }\n }\n\n private resolveResult(result?: PickResponse) {\n if (this.resolved) return;\n this.resolved = true;\n const fallback: PickResponse = result ?? {\n filesUploaded: this.items.filter(i => i.uploaded).map(i => i.uploaded!),\n filesFailed: this.items\n .filter(i => i.state === 'failed')\n .map(i => ({\n file: {\n uploadId: i.uploadId,\n filename: i.file.name,\n mimetype: i.file.type || 'application/octet-stream',\n size: i.file.size,\n source: 'local',\n },\n error: {\n code: 'VALIDATION',\n message: i.error || 'failed',\n retryable: false,\n },\n })),\n };\n this.resolvePromise(fallback);\n }\n}\n\n// ---- helpers --------------------------------------------------------------\n\nfunction el(tag: string, className: string, text?: string): HTMLElement {\n const node = document.createElement(tag);\n if (className) node.className = className;\n if (text !== undefined) node.textContent = text;\n return node;\n}\n\nfunction formatBytes(n: number): string {\n if (n < 1024) return `${n} B`;\n if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;\n if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)} MB`;\n return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;\n}\n\nfunction cryptoId(): string {\n if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {\n return (crypto as { randomUUID: () => string }).randomUUID().replace(/-/g, '');\n }\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n}\n\nexport function openPicker(client: UnionStackClient, opts: PickerOptions): PickerHandle {\n const picker = new Picker(client, opts);\n return {\n open: () => picker.open(),\n close: () => picker.close(),\n cancel: () => picker.cancel(),\n };\n}\n"]}
@@ -0,0 +1,39 @@
1
+ import { U as UnionStackClient, g as PickerOptions, P as PickResponse, h as PickerHandle } from './client-xFfk4uu4.cjs';
2
+ export { i as PickerBranding, j as PickerTheme } from './client-xFfk4uu4.cjs';
3
+
4
+ /**
5
+ * Single-shot file picker modal. Built imperatively (no framework) so it can
6
+ * be lazy-loaded by either the vanilla SDK or the React wrapper.
7
+ */
8
+ declare class Picker {
9
+ private client;
10
+ private opts;
11
+ private $backdrop;
12
+ private $list;
13
+ private $confirm;
14
+ private $cancel;
15
+ private $closeBtn;
16
+ private $input;
17
+ private items;
18
+ private abortCtrl;
19
+ private uploadStarted;
20
+ private resolved;
21
+ private resolvePromise;
22
+ private donePromise;
23
+ constructor(client: UnionStackClient, opts: PickerOptions);
24
+ open(): Promise<PickResponse>;
25
+ close(): void;
26
+ cancel(): void;
27
+ private mount;
28
+ private renderDropzone;
29
+ private unmount;
30
+ private addFiles;
31
+ private renderItem;
32
+ private setItemState;
33
+ private refreshConfirm;
34
+ private startUpload;
35
+ private resolveResult;
36
+ }
37
+ declare function openPicker(client: UnionStackClient, opts: PickerOptions): PickerHandle;
38
+
39
+ export { Picker, PickerHandle, PickerOptions, openPicker };
@@ -0,0 +1,39 @@
1
+ import { U as UnionStackClient, g as PickerOptions, P as PickResponse, h as PickerHandle } from './client-xFfk4uu4.js';
2
+ export { i as PickerBranding, j as PickerTheme } from './client-xFfk4uu4.js';
3
+
4
+ /**
5
+ * Single-shot file picker modal. Built imperatively (no framework) so it can
6
+ * be lazy-loaded by either the vanilla SDK or the React wrapper.
7
+ */
8
+ declare class Picker {
9
+ private client;
10
+ private opts;
11
+ private $backdrop;
12
+ private $list;
13
+ private $confirm;
14
+ private $cancel;
15
+ private $closeBtn;
16
+ private $input;
17
+ private items;
18
+ private abortCtrl;
19
+ private uploadStarted;
20
+ private resolved;
21
+ private resolvePromise;
22
+ private donePromise;
23
+ constructor(client: UnionStackClient, opts: PickerOptions);
24
+ open(): Promise<PickResponse>;
25
+ close(): void;
26
+ cancel(): void;
27
+ private mount;
28
+ private renderDropzone;
29
+ private unmount;
30
+ private addFiles;
31
+ private renderItem;
32
+ private setItemState;
33
+ private refreshConfirm;
34
+ private startUpload;
35
+ private resolveResult;
36
+ }
37
+ declare function openPicker(client: UnionStackClient, opts: PickerOptions): PickerHandle;
38
+
39
+ export { Picker, PickerHandle, PickerOptions, openPicker };