vintage-frames 0.15.0 → 0.16.0

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 (47) hide show
  1. package/custom-elements.json +681 -65
  2. package/dist/chrome.d.ts +10 -1
  3. package/dist/chrome.js +8 -3
  4. package/dist/components/vf-desktop.d.ts +5 -2
  5. package/dist/components/vf-desktop.js +5 -5
  6. package/dist/components/vf-dialog.d.ts +7 -2
  7. package/dist/components/vf-dialog.js +27 -27
  8. package/dist/components/vf-menu-bar.js +9 -10
  9. package/dist/components/vf-menu-item.js +18 -13
  10. package/dist/components/vf-menu.d.ts +8 -3
  11. package/dist/components/vf-menu.js +85 -61
  12. package/dist/components/vf-number-field.d.ts +4 -0
  13. package/dist/components/vf-number-field.js +28 -25
  14. package/dist/components/vf-paragraph.d.ts +3 -1
  15. package/dist/components/vf-paragraph.js +7 -0
  16. package/dist/components/vf-scroll-area.d.ts +16 -1
  17. package/dist/components/vf-scroll-area.js +39 -29
  18. package/dist/components/vf-select.d.ts +5 -4
  19. package/dist/components/vf-select.js +26 -17
  20. package/dist/components/vf-text-field.d.ts +5 -5
  21. package/dist/components/vf-text-field.js +1 -1
  22. package/dist/components/vf-window.d.ts +22 -5
  23. package/dist/components/vf-window.js +113 -98
  24. package/dist/cursor.d.ts +8 -0
  25. package/dist/cursor.js +70 -73
  26. package/dist/index.d.ts +2 -1
  27. package/dist/modal-dialog.d.ts +20 -2
  28. package/dist/modal-dialog.js +63 -51
  29. package/dist/shell/catalog.d.ts +1 -1
  30. package/dist/shell/catalog.js +32 -18
  31. package/dist/shell/finder.d.ts +6 -1
  32. package/dist/shell/finder.js +182 -176
  33. package/dist/shell/index.d.ts +1 -1
  34. package/dist/shell/pure.d.ts +10 -0
  35. package/dist/shell/pure.js +4 -0
  36. package/dist/shell/shell.d.ts +16 -3
  37. package/dist/shell/shell.js +141 -126
  38. package/dist/shell/windows.d.ts +48 -10
  39. package/dist/shell/windows.js +142 -85
  40. package/dist/text-control.d.ts +24 -1
  41. package/dist/text-control.js +25 -4
  42. package/dist/top-layer.d.ts +13 -0
  43. package/dist/top-layer.js +16 -0
  44. package/docs/SPEC.md +23 -22
  45. package/editor/vscode.html-custom-data.json +21 -11
  46. package/editor/web-types.json +51 -23
  47. package/package.json +5 -1
package/dist/cursor.js CHANGED
@@ -2,14 +2,10 @@ import { truePixelRatio as e } from "./zoom.js";
2
2
  import { effectiveScale as t, onScaleChange as n } from "./scale.js";
3
3
  import { prefersReducedMotion as r } from "./motion.js";
4
4
  import { VfModalDialog as i } from "./modal-dialog.js";
5
- import { CURSOR_ARROW as a, CURSOR_CROSSHAIR as o, CURSOR_I_BEAM as s, CURSOR_WAIT as c } from "./cursor-art.js";
5
+ import { onTopLayerEntry as a } from "./top-layer.js";
6
+ import { CURSOR_ARROW as o, CURSOR_CROSSHAIR as s, CURSOR_I_BEAM as c, CURSOR_WAIT as l } from "./cursor-art.js";
6
7
  //#region src/cursor.ts
7
- var l = [
8
- "arrow",
9
- "text",
10
- "crosshair",
11
- "wait"
12
- ], u = typeof navigator < "u" && (navigator.vendor ?? "").startsWith("Apple"), d = /* @__PURE__ */ new Set([
8
+ var u = typeof navigator < "u" && (navigator.vendor ?? "").startsWith("Apple"), d = /* @__PURE__ */ new Set([
13
9
  "text",
14
10
  "password",
15
11
  "search",
@@ -21,7 +17,7 @@ var l = [
21
17
  function f(e) {
22
18
  return e instanceof HTMLTextAreaElement ? !e.disabled : e instanceof HTMLInputElement ? !e.disabled && d.has(e.type) : e instanceof HTMLElement && e.isContentEditable;
23
19
  }
24
- function p(e, t) {
20
+ function ee(e, t) {
25
21
  let n = document.elementFromPoint(e, t);
26
22
  for (; n?.shadowRoot;) {
27
23
  let r = n.shadowRoot.elementFromPoint(e, t);
@@ -30,7 +26,7 @@ function p(e, t) {
30
26
  }
31
27
  return n;
32
28
  }
33
- function m(e) {
29
+ function p(e) {
34
30
  let t = [];
35
31
  for (; e;) {
36
32
  t.push(e);
@@ -39,22 +35,23 @@ function m(e) {
39
35
  }
40
36
  return t;
41
37
  }
42
- function h() {
38
+ function m() {
43
39
  for (let e of document.querySelectorAll("[aria-busy=\"true\"]")) if (e instanceof i && e.open) return !0;
44
40
  return !1;
45
41
  }
46
- function g(d = {}) {
47
- let { anchor: g = document.querySelector("vf-desktop") ?? document.documentElement, waitFrameMs: _ = 250, arrow: v = a, text: y = s, crosshair: b = o, wait: x = c } = d, S = {
48
- arrow: v,
49
- text: y,
50
- crosshair: b,
51
- wait: x
52
- }, C = document.documentElement, w = new AbortController(), { signal: T } = w, E = document.createElement("style");
53
- E.textContent = "* { cursor: none !important }";
54
- let D = document.createElement("img");
55
- D.alt = "", D.setAttribute("aria-hidden", "true");
56
- let O = "showPopover" in D;
57
- O && D.setAttribute("popover", "manual"), Object.assign(D.style, {
42
+ function h(d = {}) {
43
+ let { anchor: h = document.querySelector("vf-desktop") ?? document.documentElement, waitFrameMs: g = 250, arrow: _ = o, text: v = c, crosshair: y = s, wait: b = l, kinds: x = {} } = d, S = {
44
+ ...x,
45
+ arrow: _,
46
+ text: v,
47
+ crosshair: y,
48
+ wait: b
49
+ }, C = new Set(Object.keys(S)), w = document.documentElement, T = new AbortController(), { signal: E } = T, D = document.createElement("style");
50
+ D.textContent = "* { cursor: none !important }";
51
+ let O = document.createElement("img");
52
+ O.alt = "", O.setAttribute("aria-hidden", "true");
53
+ let k = "showPopover" in O;
54
+ k && O.setAttribute("popover", "manual"), Object.assign(O.style, {
58
55
  position: "fixed",
59
56
  inset: "0 auto auto 0",
60
57
  margin: "0",
@@ -67,15 +64,15 @@ function g(d = {}) {
67
64
  visibility: "hidden",
68
65
  zIndex: "2147483647"
69
66
  });
70
- let k = /* @__PURE__ */ new Map();
71
- for (let e of l) {
67
+ let A = /* @__PURE__ */ new Map();
68
+ for (let e of C) {
72
69
  let t = S[e];
73
70
  if (!t) continue;
74
71
  let n = t.invert && u ? t.staticSrc ?? t.src : t.src, r = Array.isArray(n) ? n : [n], i = r.map((e) => {
75
72
  let t = new Image();
76
73
  return t.src = e, t;
77
74
  });
78
- k.set(e, {
75
+ A.set(e, {
79
76
  art: t,
80
77
  frames: r,
81
78
  xor: !!t.invert && !u,
@@ -83,68 +80,68 @@ function g(d = {}) {
83
80
  ready: !1
84
81
  });
85
82
  }
86
- let A = (e) => Promise.all(e.images.map((e) => e.decode())).then(() => {
83
+ let j = (e) => Promise.all(e.images.map((e) => e.decode())).then(() => {
87
84
  e.ready = !0;
88
- }), j = 1, M = 3, N = 0, P = 0, F = -1, I = -1, L = "arrow", R = k.get("arrow"), z = 0, B = 0, V = () => {
89
- if (F < 0) return;
90
- let { hotspotX: e = 0, hotspotY: t = 0 } = R.art, n = Math.floor((F * j - N) / M), r = Math.floor((I * j - P) / M), i = (N + (n - e) * M) / j, a = (P + (r - t) * M) / j;
91
- D.style.transform = `translate(${i}px, ${a}px)`;
92
- }, H = () => {
93
- j = e() || 1, M = Math.max(1, Math.round(t(g) * j));
94
- let n = g.getBoundingClientRect();
95
- N = Math.round(n.left * j), P = Math.round(n.top * j), D.style.width = `${R.art.width * M / j}px`, D.style.height = `${R.art.height * M / j}px`, V();
96
- }, U = (e) => {
97
- let t = k.get(e), n = t?.ready ? t : k.get("arrow");
98
- (e !== L || n !== R) && (L = e, R = n, D.dataset.kind = e, window.clearInterval(B), z = 0, D.src = R.frames[0], D.style.filter = R.xor ? "invert(1)" : "", D.style.mixBlendMode = R.xor ? "difference" : "", R.frames.length > 1 && !r() && (B = window.setInterval(() => {
99
- z = (z + 1) % R.frames.length, D.src = R.frames[z];
100
- }, _)), H());
101
- }, W = () => {
102
- if (h()) return "wait";
103
- let e = m(p(F, I));
85
+ }), M = 1, N = 3, P = 0, F = 0, I = -1, L = -1, R = "arrow", z = A.get("arrow"), B = 0, V = 0, H = () => {
86
+ if (I < 0) return;
87
+ let { hotspotX: e = 0, hotspotY: t = 0 } = z.art, n = Math.floor((I * M - P) / N), r = Math.floor((L * M - F) / N), i = (P + (n - e) * N) / M, a = (F + (r - t) * N) / M;
88
+ O.style.transform = `translate(${i}px, ${a}px)`;
89
+ }, U = () => {
90
+ M = e() || 1, N = Math.max(1, Math.round(t(h) * M));
91
+ let n = h.getBoundingClientRect();
92
+ P = Math.round(n.left * M), F = Math.round(n.top * M), O.style.width = `${z.art.width * N / M}px`, O.style.height = `${z.art.height * N / M}px`, H();
93
+ }, W = (e) => {
94
+ let t = A.get(e), n = t?.ready ? t : A.get("arrow");
95
+ (e !== R || n !== z) && (R = e, z = n, O.dataset.kind = e, window.clearInterval(V), B = 0, O.src = z.frames[0], O.style.filter = z.xor ? "invert(1)" : "", O.style.mixBlendMode = z.xor ? "difference" : "", z.frames.length > 1 && !r() && (V = window.setInterval(() => {
96
+ B = (B + 1) % z.frames.length, O.src = z.frames[B];
97
+ }, g)), U());
98
+ }, G = () => {
99
+ if (m()) return "wait";
100
+ let e = p(ee(I, L));
104
101
  if (e.some((e) => e.getAttribute("aria-busy") === "true")) return "wait";
105
102
  for (let t of e) {
106
103
  let e = t.getAttribute("data-vf-cursor");
107
- if (e && l.includes(e)) return e;
104
+ if (e && C.has(e)) return e;
108
105
  }
109
106
  return e[0] && f(e[0]) ? "text" : "arrow";
110
- }, G = () => {
111
- F < 0 || U(W());
112
- }, K = 0, q = () => {
113
- cancelAnimationFrame(K), K = requestAnimationFrame(() => {
114
- K = requestAnimationFrame(H);
107
+ }, K = () => {
108
+ I < 0 || W(G());
109
+ }, q = 0, J = () => {
110
+ cancelAnimationFrame(q), q = requestAnimationFrame(() => {
111
+ q = requestAnimationFrame(U);
115
112
  });
116
- }, J = () => {
117
- if (!(!O || !D.isConnected)) try {
118
- D.hidePopover(), D.showPopover();
113
+ }, Y = () => {
114
+ if (!(!k || !O.isConnected)) try {
115
+ O.hidePopover(), O.showPopover();
119
116
  } catch {}
120
- }, Y = new MutationObserver((e) => {
117
+ }, X = new MutationObserver((e) => {
121
118
  for (let t of e) {
122
119
  let e = t.target;
123
120
  if (t.attributeName === "open" && e instanceof i && e.open) {
124
- J();
121
+ Y();
125
122
  break;
126
123
  }
127
124
  }
128
- G();
129
- }), X = () => {
130
- D.style.visibility = "hidden";
131
- }, Z = (e) => {
125
+ K();
126
+ }), Z = () => {
127
+ O.style.visibility = "hidden";
128
+ }, Q = (e) => {
132
129
  if (e.pointerType === "touch") {
133
- X();
130
+ Z();
134
131
  return;
135
132
  }
136
- F = e.clientX, I = e.clientY, G(), V(), D.style.visibility = "visible";
137
- }, Q = !1, $ = () => {
138
- if (Q) return;
139
- document.body.append(D), O && D.showPopover(), D.dataset.kind = "arrow", H(), document.head.append(E), C.style.setProperty("--vf-cursor", "none");
133
+ I = e.clientX, L = e.clientY, K(), H(), O.style.visibility = "visible";
134
+ }, $ = !1, te = () => {
135
+ if ($) return;
136
+ document.body.append(O), k && O.showPopover(), O.dataset.kind = "arrow", U(), document.head.append(D), w.style.setProperty("--vf-cursor", "none");
140
137
  let e = {
141
138
  capture: !0,
142
139
  passive: !0,
143
- signal: T
140
+ signal: E
144
141
  };
145
- window.addEventListener("pointermove", Z, e), window.addEventListener("pointerdown", Z, e), window.addEventListener("pointercancel", X, e), document.addEventListener("mouseleave", X, { signal: T }), window.addEventListener("blur", X, { signal: T }), document.addEventListener("visibilitychange", () => {
146
- document.hidden && X();
147
- }, { signal: T }), window.addEventListener("resize", q, { signal: T }), Y.observe(document.body, {
142
+ window.addEventListener("pointermove", Q, e), window.addEventListener("pointerdown", Q, e), window.addEventListener("pointercancel", Z, e), document.addEventListener("mouseleave", Z, { signal: E }), window.addEventListener("blur", Z, { signal: E }), document.addEventListener("visibilitychange", () => {
143
+ document.hidden && Z();
144
+ }, { signal: E }), window.addEventListener("resize", J, { signal: E }), X.observe(document.body, {
148
145
  subtree: !0,
149
146
  attributes: !0,
150
147
  attributeFilter: [
@@ -153,19 +150,19 @@ function g(d = {}) {
153
150
  "data-vf-cursor"
154
151
  ]
155
152
  });
156
- }, ee = n(q);
157
- A(k.get("arrow")).then(() => (D.src = R.frames[0], D.decode())).then($, () => {
153
+ }, ne = n(J), re = a(Y);
154
+ j(A.get("arrow")).then(() => (O.src = z.frames[0], O.decode())).then(te, () => {
158
155
  console.warn("applyCursor: could not load the arrow art; keeping the native pointer");
159
156
  });
160
- for (let [e, t] of k) e !== "arrow" && A(t).then(() => G(), () => {
157
+ for (let [e, t] of A) e !== "arrow" && j(t).then(() => K(), () => {
161
158
  console.warn(`applyCursor: could not load the "${e}" art; it will fall back to the arrow`);
162
159
  });
163
160
  return () => {
164
- if (Q = !0, w.abort(), Y.disconnect(), ee(), cancelAnimationFrame(K), window.clearInterval(B), O && D.isConnected) try {
165
- D.hidePopover();
161
+ if ($ = !0, T.abort(), X.disconnect(), ne(), re(), cancelAnimationFrame(q), window.clearInterval(V), k && O.isConnected) try {
162
+ O.hidePopover();
166
163
  } catch {}
167
- D.remove(), E.remove(), C.style.removeProperty("--vf-cursor");
164
+ O.remove(), D.remove(), w.style.removeProperty("--vf-cursor");
168
165
  };
169
166
  }
170
167
  //#endregion
171
- export { g as applyCursor };
168
+ export { h as applyCursor };
package/dist/index.d.ts CHANGED
@@ -174,7 +174,8 @@ export type { PlacementBounds, PlacementClamp, PlacementOrigin, VfPositionedInte
174
174
  export { VfSized } from './size.js';
175
175
  export type { VfSizedInterface } from './size.js';
176
176
  export { VfModalDialog, modalDialogStyles } from './modal-dialog.js';
177
- export type { VfCloseDetail, VfCloseReason } from './modal-dialog.js';
177
+ export type { VfCloseDetail, VfCloseReason, VfShowDetail } from './modal-dialog.js';
178
+ export type { VfCloseKeys } from './chrome.js';
178
179
  /** The embedded System 7 bitmap faces and the registration helper. */
179
180
  export { registerEmbeddedFont, PIXEL_GRID_METRICS } from './styles/register-embedded-font.js';
180
181
  export type { EmbeddedFontMetrics } from './styles/register-embedded-font.js';
@@ -1,14 +1,25 @@
1
1
  import { LitElement } from 'lit';
2
2
  import type { PropertyValues } from 'lit';
3
3
  import { ScaleController } from './scale.js';
4
+ import type { VfCloseKeys } from './chrome.js';
4
5
  /**
5
6
  * Reason a modal closed, carried by the `vf-close` event's detail: the Escape
6
7
  * key, the close box or `close()`, or — with {@link VfModalDialog.lightDismiss}
7
8
  * — a click outside the frame.
8
9
  */
9
10
  export type VfCloseReason = 'escape' | 'close' | 'outside';
10
- /** The `vf-close` event's detail. */
11
- export interface VfCloseDetail {
11
+ /**
12
+ * The `vf-show` event's detail: the element the modal gave its initial focus
13
+ * to, or null where it left the browser's choice alone.
14
+ */
15
+ export interface VfShowDetail {
16
+ focus: HTMLElement | null;
17
+ }
18
+ /**
19
+ * The `vf-close` event's detail. A close box's close also carries the
20
+ * modifier keys held on its click; no other close does.
21
+ */
22
+ export interface VfCloseDetail extends Partial<VfCloseKeys> {
12
23
  reason: VfCloseReason;
13
24
  /**
14
25
  * The value this close carried — the submitting button's `value`, or
@@ -69,6 +80,11 @@ export declare const modalDialogStyles: import("lit").CSSResult;
69
80
  * `<dialog @cancel=${this._onNativeCancel} @close=${this._onNativeClose}>` with
70
81
  * their role/ARIA and body, and {@link modalDialogStyles} in `static styles`.
71
82
  *
83
+ * @fires vf-show - The modal opened: it is in the top layer, placed, and its
84
+ * initial focus is set. `detail: { focus }`, the element focused. The page's
85
+ * moment to finish composing it — select a field's default text, measure
86
+ * copy that varies. Fired inside `show()` once the modal has rendered, else
87
+ * at its first render.
72
88
  * @fires vf-close - The modal closed. `detail: { reason: 'escape' | 'close' |
73
89
  * 'outside', returnValue: string | null }`.
74
90
  */
@@ -188,6 +204,8 @@ export declare class VfModalDialog extends LitElement {
188
204
  * Fires `vf-close` with `{ reason: 'close', returnValue }`.
189
205
  */
190
206
  close(returnValue?: string): void;
207
+ /** Close from a close box, its `vf-close` carrying the modifier keys held on the click. */
208
+ protected closeFromBox(keys: VfCloseKeys): void;
191
209
  protected updated(changed: PropertyValues<this>): void;
192
210
  /**
193
211
  * State the origin, in system px — clamped on screen and snapped onto the
@@ -12,7 +12,7 @@ var p = 24, m = /* @__PURE__ */ new Set([
12
12
  "email",
13
13
  "password",
14
14
  "number"
15
- ]), h = (e) => e instanceof HTMLInputElement ? m.has(e.type) : !0, g = u`
15
+ ]), h = (e) => e instanceof HTMLInputElement ? m.has(e.type) : !0, g = "vf-text-field, vf-number-field, vf-text-area, textarea, input", _ = u`
16
16
  dialog {
17
17
  padding: 0;
18
18
  margin: auto;
@@ -51,7 +51,7 @@ var p = 24, m = /* @__PURE__ */ new Set([
51
51
  the fallback is the arrow it always showed. */
52
52
  cursor: var(--vf-cursor, default);
53
53
  }
54
- `, _ = class extends l {
54
+ `, v = class extends l {
55
55
  get dialogSize() {
56
56
  return {
57
57
  width: a(this.width ?? 260),
@@ -69,100 +69,105 @@ var p = 24, m = /* @__PURE__ */ new Set([
69
69
  }
70
70
  #n;
71
71
  #r;
72
+ #i;
72
73
  constructor() {
73
- super(), this.scale = new n(this), this.open = !1, this.returnValue = "", this.lightDismiss = !1, this.#e = !1, this.#n = null, this.#r = null, this.#i = (t) => {
74
+ super(), this.scale = new n(this), this.open = !1, this.returnValue = "", this.lightDismiss = !1, this.#e = !1, this.#n = null, this.#r = null, this.#i = null, this.#a = (t) => {
74
75
  let n = t.target;
75
- if (!(n instanceof HTMLFormElement) || v(n) !== this) return;
76
+ if (!(n instanceof HTMLFormElement) || y(n) !== this) return;
76
77
  let r = t.submitter;
77
78
  if ((r?.hasAttribute("formmethod") ? r.getAttribute("formmethod") : n.getAttribute("method"))?.toLowerCase() !== "dialog") return;
78
79
  let i = r instanceof HTMLButtonElement || r instanceof HTMLInputElement ? r.value : void 0;
79
80
  e(this, t, () => this.close(i));
80
- }, this.#a = null, this.#o = new c(this, () => [
81
+ }, this.#o = null, this.#s = new c(this, () => [
81
82
  [
82
83
  this._dialog,
83
84
  "pointerdown",
84
- this.#l
85
+ this.#u
85
86
  ],
86
87
  [
87
88
  this._dialog,
88
89
  "pointerup",
89
- this.#u
90
+ this.#d
90
91
  ],
91
92
  [
92
93
  this._dialog,
93
94
  "pointercancel",
94
- this.#d
95
+ this.#f
95
96
  ],
96
97
  [
97
98
  this._dialog,
98
99
  "keydown",
99
- this.#c
100
+ this.#l
100
101
  ]
101
- ]), this.#c = (e) => {
102
+ ]), this.#l = (e) => {
102
103
  if (e.key !== "Enter" || e.defaultPrevented || e.isComposing || e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return;
103
104
  let t = e.composedPath();
104
105
  if (t.some((e) => e instanceof HTMLAnchorElement && e.hasAttribute("href")) || t.some((e) => e instanceof HTMLTextAreaElement || e instanceof HTMLElement && e.isContentEditable) && e.code !== "NumpadEnter") return;
105
106
  let n = this.defaultButton;
106
107
  n && (e.preventDefault(), n.click());
107
- }, this.#l = (e) => {
108
- this.#a = e.target === this._dialog ? e.pointerId : null;
109
108
  }, this.#u = (e) => {
110
- let t = this.#a === e.pointerId;
111
- this.#a = null, !(!t || e.target !== this._dialog || !this.lightDismiss) && (this.#n = "outside", this.close());
112
- }, this.#d = () => {
113
- this.#a = null;
114
- }, this.#f = null, this.#v = () => this.settle(), this.addEventListener("submit", this.#i);
109
+ this.#o = e.target === this._dialog ? e.pointerId : null;
110
+ }, this.#d = (e) => {
111
+ let t = this.#o === e.pointerId;
112
+ this.#o = null, !(!t || e.target !== this._dialog || !this.lightDismiss) && (this.#n = "outside", this.close());
113
+ }, this.#f = () => {
114
+ this.#o = null;
115
+ }, this.#p = null, this.#y = () => this.settle(), this.addEventListener("submit", this.#a);
115
116
  }
116
- #i;
117
117
  #a;
118
118
  #o;
119
+ #s;
119
120
  get defaultButton() {
120
121
  let e = "vf-button[variant=\"default\"]";
121
122
  return [...this.querySelectorAll(e), ...this.renderRoot.querySelectorAll(e)].find((e) => !e.matches(":disabled")) ?? null;
122
123
  }
123
124
  get initialFocusTarget() {
124
- return this.querySelector("[autofocus]") || ([...this.querySelectorAll("vf-text-field, vf-number-field, vf-text-area, textarea, input")].find((e) => h(e) && !e.matches(":disabled")) ?? this.defaultButton);
125
+ return this.querySelector("[autofocus]") || ([...this.querySelectorAll(g)].find((e) => h(e) && !e.matches(":disabled")) ?? this.defaultButton);
125
126
  }
126
- #s() {
127
- this.initialFocusTarget?.focus();
127
+ #c() {
128
+ let e = this.initialFocusTarget;
129
+ return e?.focus(), e;
128
130
  }
129
- #c;
130
131
  #l;
131
132
  #u;
132
133
  #d;
133
134
  #f;
135
+ #p;
134
136
  show() {
135
- this.open = !0, this.hasUpdated && this.#_();
137
+ this.open = !0, this.hasUpdated && this.#v();
136
138
  }
137
139
  close(e) {
138
- this.open && e !== void 0 && (this.#r = e), this.open = !1, this.hasUpdated && this.#_();
140
+ this.open && e !== void 0 && (this.#r = e), this.open = !1, this.hasUpdated && this.#v();
141
+ }
142
+ closeFromBox(e) {
143
+ this.open && (this.#i = e), this.close();
139
144
  }
140
145
  updated(e) {
141
- e.has("open") && this.#_(), (e.has("top") || e.has("left")) && this.settle(), this.#t();
146
+ e.has("open") && this.#v(), (e.has("top") || e.has("left")) && this.settle(), this.#t();
142
147
  }
143
- #p() {
148
+ #m() {
144
149
  let e = document.documentElement;
145
150
  return {
146
151
  width: o(e.clientWidth || window.innerWidth, this),
147
152
  height: o(e.clientHeight || window.innerHeight, this)
148
153
  };
149
154
  }
150
- #m(e, t) {
151
- let n = this.#p(), r = o(this._dialog?.offsetWidth ?? 0, this);
155
+ #h(e, t) {
156
+ let n = this.#m(), r = o(this._dialog?.offsetWidth ?? 0, this);
152
157
  return {
153
158
  x: Math.min(Math.max(e, p - r), n.width - p),
154
159
  y: Math.min(Math.max(t, 0), Math.max(0, n.height - p))
155
160
  };
156
161
  }
157
- #h() {
158
- let e = this.#p(), t = this._dialog?.getBoundingClientRect();
162
+ #g() {
163
+ let e = this.#m(), t = this._dialog?.getBoundingClientRect();
159
164
  return {
160
165
  x: (e.width - o(t?.width ?? 0, this)) / 2,
161
166
  y: (e.height - o(t?.height ?? 0, this)) / 2
162
167
  };
163
168
  }
164
169
  placeAt(e, t) {
165
- let n = this.#m(e, t);
170
+ let n = this.#h(e, t);
166
171
  this.left = i(n.x, this), this.top = i(n.y, this);
167
172
  }
168
173
  settle() {
@@ -171,10 +176,10 @@ var p = 24, m = /* @__PURE__ */ new Set([
171
176
  let t = this.left != null || this.top != null ? {
172
177
  x: this.left ?? 0,
173
178
  y: this.top ?? 0
174
- } : this.#h(), n = this.#m(t.x, t.y);
179
+ } : this.#g(), n = this.#h(t.x, t.y);
175
180
  e.style.left = a(i(n.x, this)), e.style.top = a(i(n.y, this)), e.style.right = "auto", e.style.bottom = "auto", e.style.margin = "0";
176
181
  }
177
- #g() {
182
+ #_() {
178
183
  let e = this._dialog?.style;
179
184
  if (e) for (let t of [
180
185
  "left",
@@ -184,51 +189,58 @@ var p = 24, m = /* @__PURE__ */ new Set([
184
189
  "margin"
185
190
  ]) e.removeProperty(t);
186
191
  }
187
- #_() {
192
+ #v() {
188
193
  let e = this._dialog;
189
- e && (this.open && !e.open ? (this.#f = document.activeElement, this.returnValue = "", e.showModal(), this.#s(), this.settle(), this.#x(e), this.#o.attach()) : !this.open && e.open && e.close());
194
+ if (e) {
195
+ if (this.open && !e.open) {
196
+ this.#p = document.activeElement, this.returnValue = "", e.showModal();
197
+ let n = this.#c();
198
+ this.settle(), this.#S(e), this.#s.attach(), t(this, "vf-show", { focus: n });
199
+ } else !this.open && e.open && e.close();
200
+ }
190
201
  }
191
- #v;
192
202
  #y;
193
203
  #b;
194
- #x(e) {
195
- typeof ResizeObserver < "u" && !this.#y && (this.#y = new ResizeObserver(this.#v), this.#y.observe(e)), window.addEventListener("resize", this.#v), this.#b ??= r(this.#v);
204
+ #x;
205
+ #S(e) {
206
+ typeof ResizeObserver < "u" && !this.#b && (this.#b = new ResizeObserver(this.#y), this.#b.observe(e)), window.addEventListener("resize", this.#y), this.#x ??= r(this.#y);
196
207
  }
197
- #S() {
198
- this.#y?.disconnect(), this.#y = void 0, window.removeEventListener("resize", this.#v), this.#b?.(), this.#b = void 0;
208
+ #C() {
209
+ this.#b?.disconnect(), this.#b = void 0, window.removeEventListener("resize", this.#y), this.#x?.(), this.#x = void 0;
199
210
  }
200
211
  disconnectedCallback() {
201
- super.disconnectedCallback(), this.#S();
212
+ super.disconnectedCallback(), this.#C();
202
213
  let e = this._dialog;
203
214
  if (e?.open) {
204
215
  e.close();
205
- let t = this.#f, n = document.activeElement;
216
+ let t = this.#p, n = document.activeElement;
206
217
  t instanceof HTMLElement && t.isConnected && (n === null || n === document.body) && t.focus();
207
218
  }
208
- this.#f = null;
219
+ this.#p = null;
209
220
  }
210
221
  _onNativeCancel() {
211
222
  this.#n = "escape";
212
223
  }
213
224
  _onNativeClose() {
214
- let e = this.#n ?? "close", n = this.#r;
215
- this.#n = null, this.#r = null, n !== null && (this.returnValue = n), this.#f = null, this.#a = null, this.#o.detach(), this.#S(), this.open = !1, this.#g(), t(this, "vf-close", {
225
+ let e = this.#n ?? "close", n = this.#r, r = this.#i;
226
+ this.#n = null, this.#r = null, this.#i = null, n !== null && (this.returnValue = n), this.#p = null, this.#o = null, this.#s.detach(), this.#C(), this.open = !1, this.#_(), t(this, "vf-close", {
216
227
  reason: e,
217
- returnValue: n
228
+ returnValue: n,
229
+ ...r
218
230
  });
219
231
  }
220
232
  };
221
233
  s([d({
222
234
  type: Boolean,
223
235
  reflect: !0
224
- })], _.prototype, "open", void 0), s([d({ type: Number })], _.prototype, "width", void 0), s([d({ type: Number })], _.prototype, "height", void 0), s([d({ type: Number })], _.prototype, "top", void 0), s([d({ type: Number })], _.prototype, "left", void 0), s([d({
236
+ })], v.prototype, "open", void 0), s([d({ type: Number })], v.prototype, "width", void 0), s([d({ type: Number })], v.prototype, "height", void 0), s([d({ type: Number })], v.prototype, "top", void 0), s([d({ type: Number })], v.prototype, "left", void 0), s([d({
225
237
  type: Boolean,
226
238
  reflect: !0,
227
239
  attribute: "light-dismiss"
228
- })], _.prototype, "lightDismiss", void 0), s([f("dialog")], _.prototype, "_dialog", void 0);
229
- function v(e) {
230
- for (let t = e.parentElement; t; t = t.parentElement) if (t instanceof _) return t;
240
+ })], v.prototype, "lightDismiss", void 0), s([f("dialog")], v.prototype, "_dialog", void 0);
241
+ function y(e) {
242
+ for (let t = e.parentElement; t; t = t.parentElement) if (t instanceof v) return t;
231
243
  return null;
232
244
  }
233
245
  //#endregion
234
- export { _ as VfModalDialog, g as modalDialogStyles };
246
+ export { v as VfModalDialog, _ as modalDialogStyles };
@@ -158,7 +158,7 @@ export interface Catalog {
158
158
  clear(): Promise<void>;
159
159
  import(archive: CatalogArchive, options?: {
160
160
  mode?: 'merge' | 'replace';
161
- }): Promise<number>;
161
+ }): Promise<Map<string, Item>>;
162
162
  dispose(): Promise<void>;
163
163
  }
164
164
  export declare function createCatalog(options: CatalogOptions): Catalog;
@@ -119,7 +119,13 @@ function x(n) {
119
119
  ...t,
120
120
  name: ""
121
121
  };
122
- }, I = {
122
+ };
123
+ async function I() {
124
+ M.clear();
125
+ for (let e of await A().list()) e.id !== i && (await A().remove(e.id), C.delete(e.id));
126
+ C = /* @__PURE__ */ new Map();
127
+ }
128
+ let L = {
123
129
  get: () => E,
124
130
  subscribe(e) {
125
131
  return D.add(e), () => {
@@ -143,7 +149,7 @@ function x(n) {
143
149
  k();
144
150
  },
145
151
  async seed(e) {
146
- return !r || !w || T ? !1 : (await e(I), await r.put({
152
+ return !r || !w || T ? !1 : (await e(L), await r.put({
147
153
  id: i,
148
154
  name: "",
149
155
  kind: i,
@@ -240,30 +246,38 @@ function x(n) {
240
246
  return { items: [...C.values()] };
241
247
  },
242
248
  async clear() {
243
- if (r) {
244
- for (let e of await r.list()) e.id !== i && await r.remove(e.id);
245
- C = /* @__PURE__ */ new Map(), M.clear(), k();
249
+ if (r) try {
250
+ await I();
251
+ } finally {
252
+ k();
246
253
  }
247
254
  },
248
255
  async import(e, { mode: t = "merge" } = {}) {
249
- let n = e.items.filter((e) => e.id !== i && !o(e.id));
250
- if (!n.length || !r) return 0;
251
- let a = t === "replace", s = /* @__PURE__ */ new Map();
252
- for (let e of n) s.set(e.id, a ? e.id : x());
253
- let c = (e) => e == null || o(e) ? e : s.get(e) ?? null;
254
- a && await I.clear();
255
- for (let e of n) await j({
256
- ...e,
257
- id: s.get(e.id),
258
- parent: c(e.parent)
259
- });
260
- return k(), n.length;
256
+ let n = /* @__PURE__ */ new Map(), a = e.items.filter((e) => e.id !== i && !o(e.id));
257
+ if (!a.length || !r) return n;
258
+ let s = t === "replace", c = /* @__PURE__ */ new Map();
259
+ for (let e of a) c.set(e.id, s ? e.id : x());
260
+ let l = (e) => e == null || o(e) ? e : c.get(e) ?? null;
261
+ try {
262
+ s && await I();
263
+ for (let e of a) {
264
+ let t = {
265
+ ...e,
266
+ id: c.get(e.id),
267
+ parent: l(e.parent)
268
+ };
269
+ await j(t), n.set(e.id, t);
270
+ }
271
+ } finally {
272
+ k();
273
+ }
274
+ return n;
261
275
  },
262
276
  async dispose() {
263
277
  await P(), D.clear();
264
278
  }
265
279
  };
266
- return I;
280
+ return L;
267
281
  }
268
282
  function S(e = []) {
269
283
  let t = new Map(e.map((e) => [e.id, e]));
@@ -58,7 +58,7 @@ export interface FinderAlerts {
58
58
  count: number;
59
59
  bytes: number;
60
60
  };
61
- /** New Folder, Paste or a dropped file, without storage. */
61
+ /** New Folder, Paste, a dropped file or `storageReady()`, without storage. */
62
62
  'storage-unavailable': Record<string, never>;
63
63
  /** A rename past the limit. */
64
64
  'name-too-long': {
@@ -123,6 +123,11 @@ export interface FinderApi {
123
123
  addCommand(spec: FinderCommandSpec): VfMenuItem;
124
124
  /** Answer one of the Finder's alerts with the site's own dialog. Call it from `extend`. */
125
125
  alertWith<K extends FinderAlert>(id: K, handler: FinderAlertHandler<K>): void;
126
+ /**
127
+ * Whether storage answers. When it doesn't, raises Storage Unavailable —
128
+ * the site's answer to it, where `alertWith` gave one — and returns false.
129
+ */
130
+ storageReady(): boolean;
126
131
  }
127
132
  export interface FinderOptions {
128
133
  /** Where the catalog is kept: `memoryStorage()`, `indexedDbStorage(name)`, the site's own, or null for nothing. */