@masters-union/union-stack 0.1.4 → 0.1.6
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/cdn/loader.v1.global.js +234 -42
- package/dist/cdn/loader.v1.global.js.map +1 -1
- package/dist/{chunk-445C5G3Q.cjs → chunk-35YVTMRN.cjs} +2 -2
- package/dist/{chunk-445C5G3Q.cjs.map → chunk-35YVTMRN.cjs.map} +1 -1
- package/dist/{chunk-I4NHLGGL.js → chunk-JVO23MPP.js} +2 -2
- package/dist/{chunk-I4NHLGGL.js.map → chunk-JVO23MPP.js.map} +1 -1
- package/dist/{client-DrOHTZ94.d.cts → client-AMBRkgCm.d.cts} +8 -0
- package/dist/{client-DrOHTZ94.d.ts → client-AMBRkgCm.d.ts} +8 -0
- package/dist/index.cjs +3 -3
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/picker.cjs +432 -73
- package/dist/picker.cjs.map +1 -1
- package/dist/picker.d.cts +6 -2
- package/dist/picker.d.ts +6 -2
- package/dist/picker.js +432 -73
- package/dist/picker.js.map +1 -1
- package/dist/react.cjs +2 -2
- package/dist/react.d.cts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +1 -1
- package/package.json +1 -1
package/dist/picker.cjs
CHANGED
|
@@ -17,104 +17,322 @@ function themeToCssVars(theme) {
|
|
|
17
17
|
"--us-primary": theme?.primary ?? defaults.primary,
|
|
18
18
|
"--us-bg": theme?.background ?? defaults.background,
|
|
19
19
|
"--us-fg": theme?.foreground ?? defaults.foreground,
|
|
20
|
-
"--us-muted":
|
|
20
|
+
"--us-muted": defaults.muted,
|
|
21
|
+
"--us-subtle": defaults.subtle,
|
|
21
22
|
"--us-border": theme?.border ?? defaults.border,
|
|
23
|
+
"--us-border-strong": defaults.borderStrong,
|
|
24
|
+
"--us-elevated": defaults.elevated,
|
|
25
|
+
"--us-success": defaults.success,
|
|
26
|
+
"--us-danger": defaults.danger,
|
|
22
27
|
"--us-radius": theme?.radius ?? "12px"
|
|
23
28
|
};
|
|
24
29
|
}
|
|
25
30
|
var LIGHT_DEFAULTS = {
|
|
26
31
|
primary: "#4f46e5",
|
|
32
|
+
// indigo-600 — confident, restrained
|
|
27
33
|
background: "#ffffff",
|
|
28
34
|
foreground: "#0f172a",
|
|
35
|
+
// slate-900 — 15.4:1 on white
|
|
29
36
|
muted: "#64748b",
|
|
30
|
-
|
|
37
|
+
// slate-500 — 4.7:1 on white
|
|
38
|
+
subtle: "#f8fafc",
|
|
39
|
+
// slate-50 — chips, icon wells
|
|
40
|
+
border: "#e2e8f0",
|
|
41
|
+
// slate-200
|
|
42
|
+
borderStrong: "#cbd5e1",
|
|
43
|
+
// slate-300 — drag-over emphasis
|
|
44
|
+
elevated: "#ffffff",
|
|
45
|
+
success: "#16a34a",
|
|
46
|
+
danger: "#dc2626"
|
|
31
47
|
};
|
|
32
48
|
var DARK_DEFAULTS = {
|
|
33
|
-
primary: "#
|
|
34
|
-
|
|
49
|
+
primary: "#818cf8",
|
|
50
|
+
// indigo-400 — desaturated for dark mode
|
|
51
|
+
background: "#0b0f1a",
|
|
52
|
+
// near-black, not pure
|
|
35
53
|
foreground: "#f1f5f9",
|
|
54
|
+
// slate-100 — 15:1 on bg
|
|
36
55
|
muted: "#94a3b8",
|
|
37
|
-
|
|
56
|
+
// slate-400 — 6.4:1 on bg
|
|
57
|
+
subtle: "#111827",
|
|
58
|
+
// slightly elevated surface
|
|
59
|
+
border: "#1f2937",
|
|
60
|
+
borderStrong: "#334155",
|
|
61
|
+
elevated: "#0f1625",
|
|
62
|
+
success: "#4ade80",
|
|
63
|
+
danger: "#f87171"
|
|
38
64
|
};
|
|
39
65
|
var BASE_CSS = `
|
|
40
66
|
.us-picker-backdrop {
|
|
41
67
|
position: fixed; inset: 0; z-index: 2147483000;
|
|
42
|
-
background:
|
|
68
|
+
background: color-mix(in srgb, #02060f 55%, transparent);
|
|
69
|
+
-webkit-backdrop-filter: blur(8px);
|
|
70
|
+
backdrop-filter: blur(8px);
|
|
43
71
|
display: flex; align-items: center; justify-content: center;
|
|
44
|
-
padding: 16px;
|
|
45
|
-
|
|
72
|
+
padding: 16px;
|
|
73
|
+
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
74
|
+
animation: us-fade 140ms ease-out;
|
|
46
75
|
}
|
|
47
76
|
@keyframes us-fade { from { opacity: 0; } to { opacity: 1; } }
|
|
77
|
+
|
|
48
78
|
.us-picker {
|
|
49
79
|
background: var(--us-bg); color: var(--us-fg);
|
|
80
|
+
border: 1px solid var(--us-border);
|
|
50
81
|
border-radius: var(--us-radius);
|
|
51
|
-
width: 100%; max-width: 480px;
|
|
82
|
+
width: 100%; max-width: 480px;
|
|
83
|
+
max-height: min(calc(100dvh - 32px), 680px);
|
|
52
84
|
display: flex; flex-direction: column;
|
|
53
|
-
box-shadow:
|
|
85
|
+
box-shadow:
|
|
86
|
+
0 1px 1px rgba(0,0,0,0.04),
|
|
87
|
+
0 18px 40px -8px rgba(0,0,0,0.18),
|
|
88
|
+
0 32px 80px -16px rgba(0,0,0,0.22);
|
|
54
89
|
overflow: hidden;
|
|
90
|
+
animation: us-rise 240ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
91
|
+
}
|
|
92
|
+
@keyframes us-rise {
|
|
93
|
+
from { opacity: 0; transform: translateY(8px) scale(0.985); }
|
|
94
|
+
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
55
95
|
}
|
|
56
96
|
.us-picker * { box-sizing: border-box; }
|
|
97
|
+
|
|
98
|
+
/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
57
99
|
.us-picker-header {
|
|
58
100
|
display: flex; align-items: center; gap: 12px;
|
|
59
|
-
padding: 16px
|
|
101
|
+
padding: 14px 16px;
|
|
102
|
+
border-bottom: 1px solid var(--us-border);
|
|
103
|
+
}
|
|
104
|
+
.us-picker-header-logo {
|
|
105
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
106
|
+
width: 28px; height: 28px; flex-shrink: 0;
|
|
107
|
+
border-radius: 8px;
|
|
108
|
+
background: var(--us-subtle);
|
|
109
|
+
color: var(--us-primary);
|
|
60
110
|
}
|
|
61
|
-
.us-picker-header
|
|
62
|
-
.us-picker-
|
|
111
|
+
.us-picker-header-logo svg { width: 16px; height: 16px; }
|
|
112
|
+
.us-picker-header-logo img { width: 100%; height: 100%; border-radius: inherit; object-fit: cover; }
|
|
113
|
+
.us-picker-title { font-weight: 600; font-size: 14px; letter-spacing: -0.01em; flex: 1; }
|
|
63
114
|
.us-picker-close {
|
|
64
115
|
background: none; border: 0; cursor: pointer;
|
|
65
|
-
|
|
66
|
-
|
|
116
|
+
width: 32px; height: 32px;
|
|
117
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
118
|
+
color: var(--us-muted); border-radius: 8px;
|
|
119
|
+
transition: color 140ms, background 140ms;
|
|
67
120
|
}
|
|
68
|
-
.us-picker-close:hover { background: var(--us-
|
|
69
|
-
.us-picker-
|
|
121
|
+
.us-picker-close:hover { background: var(--us-subtle); color: var(--us-fg); }
|
|
122
|
+
.us-picker-close:focus-visible { outline: 2px solid var(--us-primary); outline-offset: 1px; }
|
|
123
|
+
.us-picker-close svg { width: 16px; height: 16px; }
|
|
124
|
+
|
|
125
|
+
/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 body / dropzone \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
126
|
+
.us-picker-body { padding: 16px; overflow-y: auto; }
|
|
127
|
+
|
|
70
128
|
.us-dropzone {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
129
|
+
position: relative;
|
|
130
|
+
border: 1.5px dashed var(--us-border-strong);
|
|
131
|
+
border-radius: calc(var(--us-radius) - 2px);
|
|
132
|
+
padding: 28px 20px;
|
|
133
|
+
text-align: center;
|
|
134
|
+
cursor: pointer;
|
|
135
|
+
transition: border-color 160ms, background 160ms, transform 200ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
136
|
+
background: color-mix(in srgb, var(--us-subtle) 60%, transparent);
|
|
74
137
|
}
|
|
75
|
-
.us-dropzone:hover
|
|
138
|
+
.us-dropzone:hover {
|
|
76
139
|
border-color: var(--us-primary);
|
|
77
|
-
background: color-mix(in srgb, var(--us-primary)
|
|
140
|
+
background: color-mix(in srgb, var(--us-primary) 4%, var(--us-bg));
|
|
141
|
+
}
|
|
142
|
+
.us-dropzone:focus-visible {
|
|
143
|
+
outline: 2px solid var(--us-primary); outline-offset: 2px;
|
|
144
|
+
}
|
|
145
|
+
.us-dropzone[data-drag="over"] {
|
|
146
|
+
border-style: solid;
|
|
147
|
+
border-color: var(--us-primary);
|
|
148
|
+
background: color-mix(in srgb, var(--us-primary) 8%, var(--us-bg));
|
|
149
|
+
transform: scale(1.005);
|
|
150
|
+
}
|
|
151
|
+
.us-dropzone-icon {
|
|
152
|
+
width: 44px; height: 44px;
|
|
153
|
+
border-radius: 12px;
|
|
154
|
+
background: color-mix(in srgb, var(--us-primary) 12%, var(--us-bg));
|
|
155
|
+
color: var(--us-primary);
|
|
156
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
157
|
+
margin-bottom: 12px;
|
|
158
|
+
transition: transform 240ms cubic-bezier(0.16, 1.4, 0.3, 1);
|
|
159
|
+
}
|
|
160
|
+
.us-dropzone:hover .us-dropzone-icon { transform: translateY(-2px); }
|
|
161
|
+
.us-dropzone[data-drag="over"] .us-dropzone-icon {
|
|
162
|
+
transform: translateY(-3px) scale(1.06);
|
|
163
|
+
background: color-mix(in srgb, var(--us-primary) 18%, var(--us-bg));
|
|
164
|
+
}
|
|
165
|
+
.us-dropzone-icon svg { width: 22px; height: 22px; }
|
|
166
|
+
.us-dropzone-title {
|
|
167
|
+
font-size: 14px; font-weight: 600; letter-spacing: -0.01em;
|
|
168
|
+
margin-bottom: 2px;
|
|
169
|
+
}
|
|
170
|
+
.us-dropzone-hint { color: var(--us-muted); font-size: 12.5px; }
|
|
171
|
+
.us-dropzone-constraints {
|
|
172
|
+
margin-top: 10px;
|
|
173
|
+
font-size: 11px; color: var(--us-muted);
|
|
174
|
+
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
175
|
+
letter-spacing: 0.02em;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.us-dropzone--compact {
|
|
179
|
+
padding: 12px 16px;
|
|
180
|
+
display: flex; align-items: center; gap: 12px;
|
|
181
|
+
text-align: left;
|
|
182
|
+
}
|
|
183
|
+
.us-dropzone--compact .us-dropzone-icon {
|
|
184
|
+
width: 32px; height: 32px; border-radius: 8px; margin-bottom: 0;
|
|
78
185
|
}
|
|
79
|
-
.us-dropzone-
|
|
80
|
-
.us-dropzone-
|
|
81
|
-
.us-
|
|
186
|
+
.us-dropzone--compact .us-dropzone-icon svg { width: 16px; height: 16px; }
|
|
187
|
+
.us-dropzone--compact .us-dropzone-title { font-size: 13px; margin: 0; }
|
|
188
|
+
.us-dropzone--compact .us-dropzone-hint { display: none; }
|
|
189
|
+
.us-dropzone--compact .us-dropzone-constraints { display: none; }
|
|
190
|
+
|
|
191
|
+
/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 file list \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
192
|
+
.us-file-list { display: flex; flex-direction: column; gap: 8px; margin-top: 14px; }
|
|
193
|
+
|
|
82
194
|
.us-file {
|
|
83
195
|
display: flex; align-items: center; gap: 12px;
|
|
84
|
-
padding: 10px 12px;
|
|
85
|
-
|
|
196
|
+
padding: 10px 12px;
|
|
197
|
+
background: var(--us-elevated);
|
|
198
|
+
border: 1px solid var(--us-border);
|
|
199
|
+
border-radius: 10px;
|
|
200
|
+
transition: border-color 140ms, background 140ms;
|
|
201
|
+
animation: us-row-in 280ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
|
|
202
|
+
position: relative;
|
|
203
|
+
}
|
|
204
|
+
@keyframes us-row-in {
|
|
205
|
+
from { opacity: 0; transform: translateY(6px); }
|
|
206
|
+
to { opacity: 1; transform: translateY(0); }
|
|
207
|
+
}
|
|
208
|
+
.us-file[data-state="done"] { border-color: color-mix(in srgb, var(--us-success) 30%, var(--us-border)); }
|
|
209
|
+
.us-file[data-state="failed"] { border-color: color-mix(in srgb, var(--us-danger) 30%, var(--us-border)); }
|
|
210
|
+
|
|
211
|
+
.us-file-thumb {
|
|
212
|
+
width: 40px; height: 40px; flex-shrink: 0;
|
|
213
|
+
border-radius: 8px;
|
|
214
|
+
background: var(--us-subtle);
|
|
215
|
+
background-size: cover; background-position: center;
|
|
216
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
217
|
+
color: var(--us-muted);
|
|
218
|
+
overflow: hidden;
|
|
86
219
|
}
|
|
87
|
-
.us-file-
|
|
88
|
-
.us-file-
|
|
220
|
+
.us-file-thumb[data-image="true"] { color: transparent; }
|
|
221
|
+
.us-file-thumb svg { width: 18px; height: 18px; }
|
|
222
|
+
|
|
223
|
+
.us-file-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
|
|
224
|
+
.us-file-row1 { display: flex; align-items: center; gap: 8px; }
|
|
225
|
+
.us-file-name {
|
|
226
|
+
font-size: 13px; font-weight: 500;
|
|
227
|
+
flex: 1; min-width: 0;
|
|
228
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
229
|
+
letter-spacing: -0.005em;
|
|
230
|
+
}
|
|
231
|
+
.us-file-meta { color: var(--us-muted); font-size: 11.5px; flex-shrink: 0; font-variant-numeric: tabular-nums; }
|
|
232
|
+
|
|
89
233
|
.us-file-progress {
|
|
90
|
-
height:
|
|
91
|
-
|
|
234
|
+
height: 3px; background: var(--us-border); border-radius: 999px; overflow: hidden;
|
|
235
|
+
position: relative;
|
|
92
236
|
}
|
|
93
237
|
.us-file-progress-bar {
|
|
94
|
-
height: 100%;
|
|
95
|
-
|
|
238
|
+
height: 100%; width: 0%;
|
|
239
|
+
background: var(--us-primary);
|
|
240
|
+
border-radius: inherit;
|
|
241
|
+
transition: width 260ms cubic-bezier(0.4, 0.0, 0.2, 1);
|
|
242
|
+
position: relative;
|
|
243
|
+
}
|
|
244
|
+
/* Shimmer overlay while uploading. Stops on terminal states. */
|
|
245
|
+
.us-file[data-state="uploading"] .us-file-progress-bar::after {
|
|
246
|
+
content: ""; position: absolute; inset: 0;
|
|
247
|
+
background: linear-gradient(
|
|
248
|
+
90deg,
|
|
249
|
+
transparent 0%,
|
|
250
|
+
color-mix(in srgb, #fff 35%, transparent) 50%,
|
|
251
|
+
transparent 100%
|
|
252
|
+
);
|
|
253
|
+
animation: us-shimmer 1.4s linear infinite;
|
|
254
|
+
}
|
|
255
|
+
@keyframes us-shimmer {
|
|
256
|
+
from { transform: translateX(-100%); }
|
|
257
|
+
to { transform: translateX(100%); }
|
|
258
|
+
}
|
|
259
|
+
.us-file[data-state="done"] .us-file-progress-bar { width: 100% !important; background: var(--us-success); }
|
|
260
|
+
.us-file[data-state="failed"] .us-file-progress-bar { background: var(--us-danger); }
|
|
261
|
+
|
|
262
|
+
.us-file-status {
|
|
263
|
+
flex-shrink: 0;
|
|
264
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
265
|
+
width: 24px; height: 24px;
|
|
266
|
+
color: var(--us-muted);
|
|
96
267
|
}
|
|
97
|
-
.us-file
|
|
98
|
-
.us-file[data-state="
|
|
268
|
+
.us-file-status svg { width: 18px; height: 18px; }
|
|
269
|
+
.us-file[data-state="done"] .us-file-status { color: var(--us-success); animation: us-pop 320ms cubic-bezier(0.16, 1.4, 0.3, 1); }
|
|
270
|
+
.us-file[data-state="failed"] .us-file-status { color: var(--us-danger); animation: us-pop 320ms cubic-bezier(0.16, 1.4, 0.3, 1); }
|
|
271
|
+
.us-file[data-state="uploading"] .us-file-status { animation: us-spin 0.9s linear infinite; }
|
|
272
|
+
@keyframes us-pop {
|
|
273
|
+
from { transform: scale(0.5); opacity: 0; }
|
|
274
|
+
to { transform: scale(1); opacity: 1; }
|
|
275
|
+
}
|
|
276
|
+
@keyframes us-spin {
|
|
277
|
+
from { transform: rotate(0deg); }
|
|
278
|
+
to { transform: rotate(360deg); }
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 actions / footer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
99
282
|
.us-actions {
|
|
100
|
-
display: flex; gap: 8px; justify-content:
|
|
101
|
-
padding:
|
|
283
|
+
display: flex; gap: 8px; justify-content: space-between; align-items: center;
|
|
284
|
+
padding: 12px 16px;
|
|
285
|
+
border-top: 1px solid var(--us-border);
|
|
102
286
|
}
|
|
287
|
+
.us-actions-summary { font-size: 12px; color: var(--us-muted); font-variant-numeric: tabular-nums; }
|
|
288
|
+
.us-actions-buttons { display: flex; gap: 8px; }
|
|
289
|
+
|
|
103
290
|
.us-btn {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
291
|
+
appearance: none;
|
|
292
|
+
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
|
|
293
|
+
padding: 8px 14px; min-height: 36px;
|
|
294
|
+
border-radius: 8px; border: 1px solid var(--us-border);
|
|
295
|
+
background: transparent; color: var(--us-fg);
|
|
296
|
+
cursor: pointer; font-size: 13px; font-weight: 500;
|
|
297
|
+
font-family: inherit; letter-spacing: -0.005em;
|
|
298
|
+
transition: background 140ms, border-color 140ms, transform 80ms ease-out;
|
|
107
299
|
}
|
|
108
|
-
.us-btn:hover { background: var(--us-border); }
|
|
300
|
+
.us-btn:hover { background: var(--us-subtle); border-color: var(--us-border-strong); }
|
|
301
|
+
.us-btn:active { transform: scale(0.98); }
|
|
302
|
+
.us-btn:focus-visible { outline: 2px solid var(--us-primary); outline-offset: 2px; }
|
|
109
303
|
.us-btn-primary {
|
|
110
|
-
background: var(--us-primary); color: white;
|
|
304
|
+
background: var(--us-primary); color: white;
|
|
305
|
+
border-color: var(--us-primary);
|
|
111
306
|
}
|
|
112
|
-
.us-btn-primary:hover { filter: brightness(0.95); }
|
|
307
|
+
.us-btn-primary:hover { filter: brightness(0.95); background: var(--us-primary); }
|
|
113
308
|
.us-btn[disabled] { opacity: 0.5; cursor: not-allowed; }
|
|
309
|
+
.us-btn[disabled]:hover { transform: none; }
|
|
310
|
+
.us-btn svg { width: 14px; height: 14px; }
|
|
311
|
+
|
|
114
312
|
.us-footer {
|
|
115
|
-
padding: 8px
|
|
313
|
+
padding: 8px 16px 12px;
|
|
314
|
+
font-size: 11px; color: var(--us-muted); text-align: center;
|
|
315
|
+
display: flex; align-items: center; justify-content: center; gap: 6px;
|
|
316
|
+
}
|
|
317
|
+
.us-footer svg { width: 11px; height: 11px; opacity: 0.7; }
|
|
318
|
+
.us-footer a { color: var(--us-muted); text-decoration: none; font-weight: 500; }
|
|
319
|
+
.us-footer a:hover { color: var(--us-fg); }
|
|
320
|
+
|
|
321
|
+
/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 reduced motion \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
322
|
+
@media (prefers-reduced-motion: reduce) {
|
|
323
|
+
.us-picker-backdrop,
|
|
324
|
+
.us-picker,
|
|
325
|
+
.us-file,
|
|
326
|
+
.us-dropzone,
|
|
327
|
+
.us-dropzone-icon,
|
|
328
|
+
.us-btn,
|
|
329
|
+
.us-file-status,
|
|
330
|
+
.us-file-progress-bar { animation: none !important; transition: none !important; }
|
|
331
|
+
.us-file[data-state="uploading"] .us-file-progress-bar::after { animation: none; opacity: 0; }
|
|
116
332
|
}
|
|
117
|
-
|
|
333
|
+
|
|
334
|
+
/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 empty state niceties \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
335
|
+
.us-file-list:empty { display: none; }
|
|
118
336
|
`;
|
|
119
337
|
|
|
120
338
|
// src/picker/picker.ts
|
|
@@ -145,7 +363,30 @@ function mergeConfig(server, runtime) {
|
|
|
145
363
|
return merged;
|
|
146
364
|
}
|
|
147
365
|
var DEFAULT_TITLE = "Upload files";
|
|
148
|
-
var FOOTER_LINK = "https://unionstack.
|
|
366
|
+
var FOOTER_LINK = "https://unionstack.link";
|
|
367
|
+
var ICON = {
|
|
368
|
+
upload: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>`,
|
|
369
|
+
close: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>`,
|
|
370
|
+
check: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>`,
|
|
371
|
+
alert: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>`,
|
|
372
|
+
spinner: `<svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="9" stroke="currentColor" opacity="0.2"/><path d="M21 12a9 9 0 0 0-9-9" stroke="currentColor"/></svg>`,
|
|
373
|
+
image: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>`,
|
|
374
|
+
video: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2"/></svg>`,
|
|
375
|
+
audio: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/></svg>`,
|
|
376
|
+
pdf: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="9" y1="13" x2="15" y2="13"/><line x1="9" y1="17" x2="13" y2="17"/></svg>`,
|
|
377
|
+
archive: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="4" width="20" height="5" rx="2"/><path d="M4 9v9a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9"/><line x1="10" y1="13" x2="14" y2="13"/></svg>`,
|
|
378
|
+
file: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>`,
|
|
379
|
+
zap: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>`
|
|
380
|
+
};
|
|
381
|
+
function iconForMime(mime) {
|
|
382
|
+
if (!mime) return ICON.file;
|
|
383
|
+
if (mime.startsWith("image/")) return ICON.image;
|
|
384
|
+
if (mime.startsWith("video/")) return ICON.video;
|
|
385
|
+
if (mime.startsWith("audio/")) return ICON.audio;
|
|
386
|
+
if (mime === "application/pdf") return ICON.pdf;
|
|
387
|
+
if (mime.startsWith("application/zip") || mime.includes("compressed") || mime === "application/x-tar" || mime === "application/gzip") return ICON.archive;
|
|
388
|
+
return ICON.file;
|
|
389
|
+
}
|
|
149
390
|
var Picker = class {
|
|
150
391
|
constructor(client, opts) {
|
|
151
392
|
this.client = client;
|
|
@@ -160,6 +401,8 @@ var Picker = class {
|
|
|
160
401
|
this.abortCtrl = new AbortController();
|
|
161
402
|
this.uploadStarted = false;
|
|
162
403
|
this.resolved = false;
|
|
404
|
+
// ---- mount / dom --------------------------------------------------------
|
|
405
|
+
this.$summary = null;
|
|
163
406
|
this.donePromise = new Promise((res) => {
|
|
164
407
|
this.resolvePromise = res;
|
|
165
408
|
});
|
|
@@ -188,7 +431,6 @@ var Picker = class {
|
|
|
188
431
|
this.opts.onCancel?.();
|
|
189
432
|
this.close();
|
|
190
433
|
}
|
|
191
|
-
// ---- mount / dom --------------------------------------------------------
|
|
192
434
|
mount() {
|
|
193
435
|
const root = document.createElement("div");
|
|
194
436
|
root.className = "us-picker-backdrop";
|
|
@@ -200,19 +442,23 @@ var Picker = class {
|
|
|
200
442
|
});
|
|
201
443
|
const panel = el("div", "us-picker");
|
|
202
444
|
const header = el("div", "us-picker-header");
|
|
445
|
+
const logoWrap = el("div", "us-picker-header-logo");
|
|
203
446
|
if (this.opts.branding?.logoUrl) {
|
|
204
447
|
const logo = document.createElement("img");
|
|
205
448
|
logo.src = this.opts.branding.logoUrl;
|
|
206
|
-
logo.alt = "
|
|
207
|
-
|
|
449
|
+
logo.alt = "";
|
|
450
|
+
logoWrap.appendChild(logo);
|
|
451
|
+
} else {
|
|
452
|
+
logoWrap.innerHTML = ICON.zap;
|
|
208
453
|
}
|
|
454
|
+
header.appendChild(logoWrap);
|
|
209
455
|
const title = el("div", "us-picker-title", this.opts.branding?.title ?? DEFAULT_TITLE);
|
|
210
456
|
header.appendChild(title);
|
|
211
457
|
this.$closeBtn = document.createElement("button");
|
|
212
458
|
this.$closeBtn.type = "button";
|
|
213
459
|
this.$closeBtn.className = "us-picker-close";
|
|
214
460
|
this.$closeBtn.setAttribute("aria-label", "Close");
|
|
215
|
-
this.$closeBtn.
|
|
461
|
+
this.$closeBtn.innerHTML = ICON.close;
|
|
216
462
|
this.$closeBtn.onclick = () => this.cancel();
|
|
217
463
|
header.appendChild(this.$closeBtn);
|
|
218
464
|
panel.appendChild(header);
|
|
@@ -221,24 +467,31 @@ var Picker = class {
|
|
|
221
467
|
this.$list = el("div", "us-file-list");
|
|
222
468
|
body.appendChild(this.$list);
|
|
223
469
|
panel.appendChild(body);
|
|
470
|
+
const autoUpload = this.opts.autoUpload !== false;
|
|
224
471
|
const actions = el("div", "us-actions");
|
|
472
|
+
this.$summary = el("div", "us-actions-summary", "");
|
|
473
|
+
actions.appendChild(this.$summary);
|
|
474
|
+
const buttons = el("div", "us-actions-buttons");
|
|
225
475
|
this.$cancel = document.createElement("button");
|
|
226
476
|
this.$cancel.type = "button";
|
|
227
477
|
this.$cancel.className = "us-btn";
|
|
228
478
|
this.$cancel.textContent = "Cancel";
|
|
229
479
|
this.$cancel.onclick = () => this.cancel();
|
|
230
|
-
this.$
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
480
|
+
buttons.appendChild(this.$cancel);
|
|
481
|
+
if (!autoUpload) {
|
|
482
|
+
this.$confirm = document.createElement("button");
|
|
483
|
+
this.$confirm.type = "button";
|
|
484
|
+
this.$confirm.className = "us-btn us-btn-primary";
|
|
485
|
+
this.$confirm.innerHTML = `${ICON.upload} <span>Upload</span>`;
|
|
486
|
+
this.$confirm.disabled = true;
|
|
487
|
+
this.$confirm.onclick = () => this.startUpload();
|
|
488
|
+
buttons.appendChild(this.$confirm);
|
|
489
|
+
}
|
|
490
|
+
actions.appendChild(buttons);
|
|
238
491
|
panel.appendChild(actions);
|
|
239
492
|
if (!this.opts.branding?.hideFooter) {
|
|
240
493
|
const footer = el("div", "us-footer");
|
|
241
|
-
footer.innerHTML =
|
|
494
|
+
footer.innerHTML = `${ICON.zap} <span>Powered by <a href="${FOOTER_LINK}" target="_blank" rel="noopener">UnionStack</a></span>`;
|
|
242
495
|
panel.appendChild(footer);
|
|
243
496
|
}
|
|
244
497
|
root.appendChild(panel);
|
|
@@ -249,8 +502,23 @@ var Picker = class {
|
|
|
249
502
|
const dz = el("div", "us-dropzone");
|
|
250
503
|
dz.setAttribute("role", "button");
|
|
251
504
|
dz.setAttribute("tabindex", "0");
|
|
252
|
-
dz.
|
|
253
|
-
|
|
505
|
+
dz.setAttribute("aria-label", "Drop files here or click to browse");
|
|
506
|
+
const icon = el("div", "us-dropzone-icon");
|
|
507
|
+
icon.innerHTML = ICON.upload;
|
|
508
|
+
dz.appendChild(icon);
|
|
509
|
+
dz.appendChild(el("div", "us-dropzone-title", "Drop files to upload"));
|
|
510
|
+
dz.appendChild(el("div", "us-dropzone-hint", "or click to browse from your device"));
|
|
511
|
+
const constraintBits = [];
|
|
512
|
+
if (this.opts.maxFileSize) constraintBits.push(`max ${formatBytes(this.opts.maxFileSize)}`);
|
|
513
|
+
if (this.opts.accept) {
|
|
514
|
+
const types = this.opts.accept.split(",").map((s) => s.trim()).filter((s) => s && s !== "*/*").map((s) => s.replace("/*", ""));
|
|
515
|
+
if (types.length > 0 && types.length <= 4) {
|
|
516
|
+
constraintBits.push(types.join(" \xB7 "));
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
if (constraintBits.length > 0) {
|
|
520
|
+
dz.appendChild(el("div", "us-dropzone-constraints", constraintBits.join(" \xB7 ")));
|
|
521
|
+
}
|
|
254
522
|
const input = document.createElement("input");
|
|
255
523
|
input.type = "file";
|
|
256
524
|
input.multiple = (this.opts.maxFiles ?? 10) > 1;
|
|
@@ -285,6 +553,9 @@ var Picker = class {
|
|
|
285
553
|
unmount() {
|
|
286
554
|
if (this.$backdrop?.parentNode) this.$backdrop.parentNode.removeChild(this.$backdrop);
|
|
287
555
|
this.$backdrop = null;
|
|
556
|
+
for (const item of this.items) {
|
|
557
|
+
if (item.objectUrl) URL.revokeObjectURL(item.objectUrl);
|
|
558
|
+
}
|
|
288
559
|
this.opts.onClose?.();
|
|
289
560
|
}
|
|
290
561
|
// ---- file selection -----------------------------------------------------
|
|
@@ -316,32 +587,84 @@ var Picker = class {
|
|
|
316
587
|
this.renderItem(item);
|
|
317
588
|
}
|
|
318
589
|
this.refreshConfirm();
|
|
590
|
+
const autoUpload = this.opts.autoUpload !== false;
|
|
591
|
+
const hasQueued = this.items.some((i) => i.state === "queued");
|
|
592
|
+
if (autoUpload && hasQueued && !this.uploadStarted) {
|
|
593
|
+
requestAnimationFrame(() => {
|
|
594
|
+
if (!this.uploadStarted) this.startUpload();
|
|
595
|
+
});
|
|
596
|
+
}
|
|
319
597
|
}
|
|
320
598
|
renderItem(item) {
|
|
321
599
|
if (!this.$list) return;
|
|
322
600
|
const row = el("div", "us-file");
|
|
323
601
|
row.dataset.state = item.state;
|
|
324
602
|
row.dataset.uploadId = item.uploadId;
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
603
|
+
const idx = Math.min(this.items.length - 1, 8);
|
|
604
|
+
row.style.animationDelay = `${idx * 35}ms`;
|
|
605
|
+
const thumb = el("div", "us-file-thumb");
|
|
606
|
+
if (item.file.type.startsWith("image/") && typeof URL !== "undefined" && URL.createObjectURL) {
|
|
607
|
+
try {
|
|
608
|
+
const objectUrl = URL.createObjectURL(item.file);
|
|
609
|
+
item.objectUrl = objectUrl;
|
|
610
|
+
thumb.style.backgroundImage = `url("${objectUrl}")`;
|
|
611
|
+
thumb.dataset.image = "true";
|
|
612
|
+
} catch {
|
|
613
|
+
thumb.innerHTML = iconForMime(item.file.type);
|
|
614
|
+
}
|
|
615
|
+
} else {
|
|
616
|
+
thumb.innerHTML = iconForMime(item.file.type);
|
|
617
|
+
}
|
|
618
|
+
row.appendChild(thumb);
|
|
619
|
+
const main = el("div", "us-file-main");
|
|
620
|
+
const row1 = el("div", "us-file-row1");
|
|
621
|
+
row1.appendChild(el("div", "us-file-name", item.file.name));
|
|
329
622
|
const meta = el("div", "us-file-meta", formatBytes(item.file.size));
|
|
330
|
-
|
|
623
|
+
row1.appendChild(meta);
|
|
624
|
+
main.appendChild(row1);
|
|
331
625
|
const progress = el("div", "us-file-progress");
|
|
332
626
|
const bar = el("div", "us-file-progress-bar");
|
|
333
627
|
progress.appendChild(bar);
|
|
334
628
|
main.appendChild(progress);
|
|
335
629
|
row.appendChild(main);
|
|
336
|
-
const status = el("div", "us-file-
|
|
337
|
-
status.
|
|
338
|
-
status.
|
|
339
|
-
status.textContent = item.state === "failed" ? item.error || "failed" : item.state === "done" ? "done" : "0%";
|
|
630
|
+
const status = el("div", "us-file-status");
|
|
631
|
+
status.setAttribute("aria-label", this.statusLabel(item));
|
|
632
|
+
status.innerHTML = this.statusIcon(item.state);
|
|
340
633
|
row.appendChild(status);
|
|
341
634
|
item.$row = row;
|
|
342
635
|
item.$bar = bar;
|
|
343
636
|
item.$status = status;
|
|
637
|
+
item.$meta = meta;
|
|
344
638
|
this.$list.appendChild(row);
|
|
639
|
+
this.updateSummary();
|
|
640
|
+
}
|
|
641
|
+
statusIcon(state) {
|
|
642
|
+
switch (state) {
|
|
643
|
+
case "uploading":
|
|
644
|
+
return ICON.spinner;
|
|
645
|
+
case "done":
|
|
646
|
+
return ICON.check;
|
|
647
|
+
case "failed":
|
|
648
|
+
return ICON.alert;
|
|
649
|
+
case "cancelled":
|
|
650
|
+
return ICON.alert;
|
|
651
|
+
default:
|
|
652
|
+
return ICON.spinner;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
statusLabel(item) {
|
|
656
|
+
switch (item.state) {
|
|
657
|
+
case "queued":
|
|
658
|
+
return "Waiting to upload";
|
|
659
|
+
case "uploading":
|
|
660
|
+
return `Uploading ${Math.round(item.progress)} percent`;
|
|
661
|
+
case "done":
|
|
662
|
+
return "Upload complete";
|
|
663
|
+
case "failed":
|
|
664
|
+
return item.error ? `Failed: ${item.error}` : "Upload failed";
|
|
665
|
+
case "cancelled":
|
|
666
|
+
return "Cancelled";
|
|
667
|
+
}
|
|
345
668
|
}
|
|
346
669
|
setItemState(item, state, progress) {
|
|
347
670
|
item.state = state;
|
|
@@ -349,9 +672,45 @@ var Picker = class {
|
|
|
349
672
|
if (item.$row) item.$row.dataset.state = state;
|
|
350
673
|
if (item.$bar) item.$bar.style.width = `${item.progress}%`;
|
|
351
674
|
if (item.$status) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
675
|
+
item.$status.innerHTML = this.statusIcon(state);
|
|
676
|
+
item.$status.setAttribute("aria-label", this.statusLabel(item));
|
|
677
|
+
}
|
|
678
|
+
if (item.$meta) {
|
|
679
|
+
const size = formatBytes(item.file.size);
|
|
680
|
+
switch (state) {
|
|
681
|
+
case "uploading":
|
|
682
|
+
item.$meta.textContent = `${size} \xB7 ${Math.round(item.progress)}%`;
|
|
683
|
+
break;
|
|
684
|
+
case "done":
|
|
685
|
+
item.$meta.textContent = size;
|
|
686
|
+
break;
|
|
687
|
+
case "failed":
|
|
688
|
+
item.$meta.textContent = item.error || "Failed";
|
|
689
|
+
break;
|
|
690
|
+
default:
|
|
691
|
+
item.$meta.textContent = size;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
this.updateSummary();
|
|
695
|
+
}
|
|
696
|
+
updateSummary() {
|
|
697
|
+
if (!this.$summary) return;
|
|
698
|
+
const total = this.items.length;
|
|
699
|
+
if (total === 0) {
|
|
700
|
+
this.$summary.textContent = "";
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
const done = this.items.filter((i) => i.state === "done").length;
|
|
704
|
+
const failed = this.items.filter((i) => i.state === "failed").length;
|
|
705
|
+
const active = this.items.filter((i) => i.state === "uploading" || i.state === "queued").length;
|
|
706
|
+
if (active > 0) {
|
|
707
|
+
this.$summary.textContent = `Uploading ${done + 1} of ${total}`;
|
|
708
|
+
} else if (failed > 0) {
|
|
709
|
+
this.$summary.textContent = `${done} of ${total} uploaded \xB7 ${failed} failed`;
|
|
710
|
+
} else if (done === total) {
|
|
711
|
+
this.$summary.textContent = `${total} file${total === 1 ? "" : "s"} uploaded`;
|
|
712
|
+
} else {
|
|
713
|
+
this.$summary.textContent = `${total} file${total === 1 ? "" : "s"} ready`;
|
|
355
714
|
}
|
|
356
715
|
}
|
|
357
716
|
refreshConfirm() {
|