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