@masters-union/union-stack 0.3.16 → 0.3.18

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.
Files changed (39) hide show
  1. package/dist/cdn/loader.v1.global.js +45 -3
  2. package/dist/cdn/loader.v1.global.js.map +1 -1
  3. package/dist/{chunk-EBE7EIOZ.cjs → chunk-4DBTQE3U.cjs} +14 -14
  4. package/dist/{chunk-EBE7EIOZ.cjs.map → chunk-4DBTQE3U.cjs.map} +1 -1
  5. package/dist/{chunk-3FCZBKNN.cjs → chunk-7LKSR4EY.cjs} +8 -8
  6. package/dist/{chunk-3FCZBKNN.cjs.map → chunk-7LKSR4EY.cjs.map} +1 -1
  7. package/dist/{chunk-XBQENFBZ.js → chunk-G2H5WZDV.js} +14 -12
  8. package/dist/chunk-G2H5WZDV.js.map +1 -0
  9. package/dist/{chunk-OZGVTEMZ.js → chunk-KFGLJY75.js} +4 -4
  10. package/dist/{chunk-OZGVTEMZ.js.map → chunk-KFGLJY75.js.map} +1 -1
  11. package/dist/{chunk-GTE65IAM.cjs → chunk-TQDC5KIC.cjs} +14 -12
  12. package/dist/chunk-TQDC5KIC.cjs.map +1 -0
  13. package/dist/{chunk-7NJTA5ZD.js → chunk-VDOTBC6Y.js} +4 -4
  14. package/dist/{chunk-7NJTA5ZD.js.map → chunk-VDOTBC6Y.js.map} +1 -1
  15. package/dist/{client-BCLIIiX2.d.ts → client-BofkorBV.d.ts} +1 -1
  16. package/dist/{client-CSZ-4GVl.d.cts → client-C_pEvbZS.d.cts} +1 -1
  17. package/dist/index.cjs +5 -5
  18. package/dist/index.d.cts +3 -3
  19. package/dist/index.d.ts +3 -3
  20. package/dist/index.js +4 -4
  21. package/dist/node.cjs +8 -8
  22. package/dist/node.d.cts +2 -2
  23. package/dist/node.d.ts +2 -2
  24. package/dist/node.js +2 -2
  25. package/dist/picker.cjs +110 -5
  26. package/dist/picker.cjs.map +1 -1
  27. package/dist/picker.d.cts +4 -3
  28. package/dist/picker.d.ts +4 -3
  29. package/dist/picker.js +108 -3
  30. package/dist/picker.js.map +1 -1
  31. package/dist/react.cjs +4 -4
  32. package/dist/react.d.cts +4 -4
  33. package/dist/react.d.ts +4 -4
  34. package/dist/react.js +3 -3
  35. package/dist/{types-BWs1slkL.d.cts → types-BsGu9Oi3.d.cts} +2 -0
  36. package/dist/{types-BWs1slkL.d.ts → types-BsGu9Oi3.d.ts} +2 -0
  37. package/package.json +1 -1
  38. package/dist/chunk-GTE65IAM.cjs.map +0 -1
  39. package/dist/chunk-XBQENFBZ.js.map +0 -1
package/dist/picker.d.cts CHANGED
@@ -1,6 +1,6 @@
1
- import { U as UnionStackClient, P as PickerOptions, a as PickerHandle } from './client-CSZ-4GVl.cjs';
2
- export { b as PickerBranding, c as PickerTheme } from './client-CSZ-4GVl.cjs';
3
- import { P as PickResponse } from './types-BWs1slkL.cjs';
1
+ import { U as UnionStackClient, P as PickerOptions, a as PickerHandle } from './client-C_pEvbZS.cjs';
2
+ export { b as PickerBranding, c as PickerTheme } from './client-C_pEvbZS.cjs';
3
+ import { P as PickResponse } from './types-BsGu9Oi3.cjs';
4
4
 
5
5
  /**
6
6
  * Single-shot file picker modal. Built imperatively (no framework) so it can
@@ -52,6 +52,7 @@ declare class Picker {
52
52
  private addFiles;
53
53
  private renderItem;
54
54
  private replaceItemFile;
55
+ private removeItem;
55
56
  private openEditor;
56
57
  private statusIcon;
57
58
  private statusLabel;
package/dist/picker.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { U as UnionStackClient, P as PickerOptions, a as PickerHandle } from './client-BCLIIiX2.js';
2
- export { b as PickerBranding, c as PickerTheme } from './client-BCLIIiX2.js';
3
- import { P as PickResponse } from './types-BWs1slkL.js';
1
+ import { U as UnionStackClient, P as PickerOptions, a as PickerHandle } from './client-BofkorBV.js';
2
+ export { b as PickerBranding, c as PickerTheme } from './client-BofkorBV.js';
3
+ import { P as PickResponse } from './types-BsGu9Oi3.js';
4
4
 
5
5
  /**
6
6
  * Single-shot file picker modal. Built imperatively (no framework) so it can
@@ -52,6 +52,7 @@ declare class Picker {
52
52
  private addFiles;
53
53
  private renderItem;
54
54
  private replaceItemFile;
55
+ private removeItem;
55
56
  private openEditor;
56
57
  private statusIcon;
57
58
  private statusLabel;
package/dist/picker.js CHANGED
@@ -1,4 +1,4 @@
1
- import { EMPTY_FILE_MESSAGE } from './chunk-XBQENFBZ.js';
1
+ import { EMPTY_FILE_MESSAGE } from './chunk-G2H5WZDV.js';
2
2
 
3
3
  // src/picker/styles.ts
4
4
  var STYLE_ID = "unionstack-picker-styles";
@@ -450,14 +450,56 @@ var BASE_CSS = `
450
450
  width: 28px; height: 28px;
451
451
  display: inline-flex; align-items: center; justify-content: center;
452
452
  color: var(--us-muted); border-radius: 6px;
453
- transition: color 100ms, background 0ms;
453
+ transition: color 100ms, background 0ms, transform 90ms ease;
454
454
  }
455
455
  .us-file-action:hover { background: var(--us-overlay); color: var(--us-fg); }
456
456
  .us-file-action:focus-visible { outline: 2px solid var(--us-primary); outline-offset: 1px; }
457
+ .us-file-action:active { transform: scale(0.92); }
457
458
  .us-file-action svg { width: 15px; height: 15px; }
458
459
  .us-file-action[data-edited="true"] { color: var(--us-accent); }
459
460
  .us-file:not([data-state="queued"]) .us-file-action { display: none; }
460
461
 
462
+ /* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 per-row remove button \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
463
+ /* Destructive affordance \u2014 reads as danger on hover, not before. */
464
+ .us-file-remove:hover {
465
+ color: var(--us-danger);
466
+ background: color-mix(in srgb, var(--us-danger) 12%, transparent);
467
+ }
468
+ /* Rows that failed validation at selection time keep their remove button so
469
+ the user can clear them (the generic rule above hides non-queued actions). */
470
+ .us-file[data-state="failed"] .us-file-remove { display: inline-flex; }
471
+ /* Once upload starts the batch is locked \u2014 no row removal anywhere. */
472
+ .us-picker[data-uploading="true"] .us-file-remove { display: none; }
473
+
474
+ /* Row exit: content slips up and fades (130ms), then the emptied slot
475
+ collapses (190ms, same spring-out curve as the entrance) so neighbors
476
+ glide into place instead of snapping. Total \u2248240ms \u2014 faster than the
477
+ 280ms entrance, per exit-faster-than-enter. */
478
+ .us-file[data-removing="true"] {
479
+ animation: none;
480
+ pointer-events: none;
481
+ opacity: 0;
482
+ transform: translateY(-4px) scale(0.98);
483
+ height: 0 !important;
484
+ min-height: 0 !important;
485
+ padding-top: 0 !important;
486
+ padding-bottom: 0 !important;
487
+ border-top-width: 0 !important;
488
+ border-bottom-width: 0 !important;
489
+ overflow: hidden;
490
+ transition:
491
+ opacity 130ms cubic-bezier(0.4, 0, 1, 1),
492
+ transform 130ms cubic-bezier(0.4, 0, 1, 1),
493
+ height 190ms cubic-bezier(0.16, 1, 0.3, 1) 50ms,
494
+ padding 190ms cubic-bezier(0.16, 1, 0.3, 1) 50ms,
495
+ margin 190ms cubic-bezier(0.16, 1, 0.3, 1) 50ms,
496
+ border-width 190ms cubic-bezier(0.16, 1, 0.3, 1) 50ms;
497
+ }
498
+ /* Absorb the flex gap the row leaves behind so the list height lands exactly
499
+ where it will be once the node is detached \u2014 no end-of-animation snap. */
500
+ .us-file[data-removing="true"]:not(:last-child) { margin-bottom: -8px !important; }
501
+ .us-file[data-removing="true"]:last-child:not(:only-child) { margin-top: -8px !important; }
502
+
461
503
  /* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 image editor overlay \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
462
504
  .us-editor {
463
505
  position: absolute; inset: 0; z-index: 2;
@@ -1269,7 +1311,8 @@ var ICON2 = {
1269
1311
  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>`,
1270
1312
  device: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>`,
1271
1313
  link: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72"/></svg>`,
1272
- pencil: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>`
1314
+ pencil: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>`,
1315
+ trash: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>`
1273
1316
  };
1274
1317
  function iconForMime(mime) {
1275
1318
  if (!mime) return ICON2.file;
@@ -1799,6 +1842,16 @@ var Picker = class {
1799
1842
  row.appendChild(edit);
1800
1843
  item.$edit = edit;
1801
1844
  }
1845
+ if (item.state === "queued" || item.state === "failed") {
1846
+ const remove = document.createElement("button");
1847
+ remove.type = "button";
1848
+ remove.className = "us-file-action us-file-remove";
1849
+ remove.setAttribute("aria-label", `Remove ${item.file.name}`);
1850
+ remove.title = "Remove file";
1851
+ remove.innerHTML = ICON2.trash;
1852
+ remove.onclick = () => this.removeItem(item);
1853
+ row.appendChild(remove);
1854
+ }
1802
1855
  const status = el2("div", "us-file-status");
1803
1856
  status.setAttribute("aria-label", this.statusLabel(item));
1804
1857
  status.innerHTML = this.statusIcon(item.state);
@@ -1844,6 +1897,57 @@ var Picker = class {
1844
1897
  }
1845
1898
  if (item.$edit) item.$edit.dataset.edited = edited ? "true" : "false";
1846
1899
  }
1900
+ // Drop a file from the batch before upload starts. The row animates out
1901
+ // (content fades, then the slot collapses so neighbors glide up) unless the
1902
+ // user prefers reduced motion. State is pruned synchronously — only the DOM
1903
+ // exit is deferred.
1904
+ removeItem(item) {
1905
+ if (this.uploadStarted) return;
1906
+ const idx = this.items.indexOf(item);
1907
+ if (idx === -1) return;
1908
+ this.items.splice(idx, 1);
1909
+ item.validateSeq = ++this.validateSeqCounter;
1910
+ item.validating = false;
1911
+ if (item.objectUrl) {
1912
+ URL.revokeObjectURL(item.objectUrl);
1913
+ item.objectUrl = void 0;
1914
+ }
1915
+ const row = item.$row;
1916
+ item.$row = void 0;
1917
+ if (row) {
1918
+ if (row.contains(document.activeElement)) {
1919
+ const rows = Array.from(
1920
+ this.$list?.querySelectorAll(".us-file:not([data-removing])") ?? []
1921
+ );
1922
+ const pos = rows.indexOf(row);
1923
+ const neighbor = rows[pos + 1] ?? rows[pos - 1];
1924
+ const target = neighbor?.querySelector(".us-file-remove") ?? this.$panel?.querySelector('.us-source-tab[data-active="true"]') ?? this.$panel;
1925
+ target?.focus();
1926
+ }
1927
+ const reduceMotion = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
1928
+ if (reduceMotion) {
1929
+ row.remove();
1930
+ } else {
1931
+ row.style.height = `${row.offsetHeight}px`;
1932
+ void row.offsetHeight;
1933
+ row.dataset.removing = "true";
1934
+ let removed = false;
1935
+ const finalize = () => {
1936
+ if (!removed) {
1937
+ removed = true;
1938
+ row.remove();
1939
+ }
1940
+ };
1941
+ row.addEventListener("transitionend", (e) => {
1942
+ if (e.target === row && e.propertyName === "height") finalize();
1943
+ });
1944
+ setTimeout(finalize, 400);
1945
+ }
1946
+ }
1947
+ this.updateSummary();
1948
+ this.refreshConfirm();
1949
+ this.maybeAutoUpload();
1950
+ }
1847
1951
  // Mount the image editor overlay on top of the picker panel for the given
1848
1952
  // queued item. The editor calls back with either an edited File (Apply) or
1849
1953
  // nothing (Cancel/Back).
@@ -2063,6 +2167,7 @@ var Picker = class {
2063
2167
  const queued = this.items.filter((i) => i.state === "queued");
2064
2168
  if (queued.length === 0) return;
2065
2169
  this.uploadStarted = true;
2170
+ this.$panel?.setAttribute("data-uploading", "true");
2066
2171
  if (this.$confirm) {
2067
2172
  this.$confirm.disabled = true;
2068
2173
  this.$confirm.textContent = "Uploading\u2026";