selfstore 1.8.19 → 1.8.20

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/widgets.js CHANGED
@@ -43,8 +43,13 @@ var baseStyles = `
43
43
  --_radius: var(--selfstore-radius, 12px);
44
44
  --_gap: var(--selfstore-gap, 0.6rem);
45
45
  --_danger: var(--selfstore-danger, #dc2626);
46
- --_ok: var(--selfstore-ok, #16a34a);
47
- --_warn: var(--selfstore-warn, #d97706);
46
+ /* Severity colors are read as TEXT (see sev-* below), so they answer to the
47
+ 4.5:1 text threshold, not the 3:1 one for a graphic. The obvious green and
48
+ amber of a palette sit at 3.3:1 and 3.2:1 on white and fail it: a status
49
+ line is exactly the sentence someone needs to read. One step darker each
50
+ clears it at 5.0:1 without changing which color anyone thinks they see. */
51
+ --_ok: var(--selfstore-ok, #15803d);
52
+ --_warn: var(--selfstore-warn, #b45309);
48
53
  }
49
54
  /* Status severity, as a text color the dot inherits (background: currentColor). */
50
55
  [part~='sev-ok'] { color: var(--_ok); }
@@ -155,6 +160,12 @@ span[part~='status-glyph'] {
155
160
  animation: ss-spin 0.8s linear infinite;
156
161
  }
157
162
  @keyframes ss-spin { to { transform: rotate(360deg); } }
163
+ /* An endless spin is the one motion here, and it runs during the wait someone
164
+ with vestibular trouble cannot escape. Keep the shape as the busy signal,
165
+ drop the rotation. */
166
+ @media (prefers-reduced-motion: reduce) {
167
+ [part='spinner'] { animation: none; border-top-color: var(--_border); }
168
+ }
158
169
  button[part~='button'] {
159
170
  font: inherit;
160
171
  padding: 0.45rem 0.9rem;
@@ -1019,7 +1030,7 @@ var SelfstoreConnectElement = class extends FlowWidget {
1019
1030
  h("div", { part: "title" }, this.t("connect.password.title")),
1020
1031
  h("div", { part: "hint" }, this.t("connect.password.hint")),
1021
1032
  field,
1022
- s.passwordError ? h("div", { part: "error-note" }, this.t("connect.password.wrong")) : null,
1033
+ s.passwordError ? h("div", { part: "error-note", role: "alert" }, this.t("connect.password.wrong")) : null,
1023
1034
  h(
1024
1035
  "div",
1025
1036
  { part: "row" },
@@ -1632,7 +1643,9 @@ var SelfstoreShareElement = class extends FlowWidget {
1632
1643
  put(into, this.heading("title", "share.title"));
1633
1644
  if (s.stale) into.append(h("div", { part: "hint" }, this.t("share.stale")));
1634
1645
  if (s.error && !s.stale) {
1635
- into.append(h("div", { part: "error-note" }, this.errorText(s.error.labelKey)));
1646
+ into.append(
1647
+ h("div", { part: "error-note", role: "alert" }, this.errorText(s.error.labelKey))
1648
+ );
1636
1649
  }
1637
1650
  if (this.#withCreate) {
1638
1651
  const missing = this.#levels.filter((l) => !s.links.some((k) => k.level === l));
@@ -2118,6 +2131,7 @@ var FR5 = {
2118
2131
  "status.action.reconnect": "Retrouver l'acc\xE8s",
2119
2132
  "status.action.unlock": "D\xE9verrouiller"
2120
2133
  };
2134
+ var GLYPH = { ok: "\u2713", warn: "!", danger: "!", info: "" };
2121
2135
  var SelfstoreStatusElement = class extends FlowWidget {
2122
2136
  static get observedAttributes() {
2123
2137
  return ["variant"];
@@ -2146,6 +2160,7 @@ var SelfstoreStatusElement = class extends FlowWidget {
2146
2160
  }
2147
2161
  connectedCallback() {
2148
2162
  super.connectedCallback();
2163
+ if (!this.hasAttribute("role")) this.setAttribute("role", "status");
2149
2164
  if (this.hasAttribute("variant"))
2150
2165
  this.variant = this.getAttribute("variant");
2151
2166
  this.follow(this.hostOf());
@@ -2174,7 +2189,6 @@ var SelfstoreStatusElement = class extends FlowWidget {
2174
2189
  part: `status-dot sev-${status.severity}`,
2175
2190
  "aria-hidden": "true"
2176
2191
  });
2177
- const GLYPH = { ok: "\u2713", warn: "!", danger: "!", info: "" };
2178
2192
  const glyph = GLYPH[status.severity];
2179
2193
  const sign = glyph ? h("span", { part: `status-glyph sev-${status.severity}`, "aria-hidden": "true" }, glyph) : dot;
2180
2194
  if (this.#variant === "dot") {
@@ -2944,7 +2958,7 @@ var SelfstoreBackupsElement = class extends FlowWidget {
2944
2958
  this.rerender();
2945
2959
  }
2946
2960
  }),
2947
- taken ? h("div", { part: "error-note" }, this.t("backups.taken")) : clean ? h(
2961
+ taken ? h("div", { part: "error-note", role: "alert" }, this.t("backups.taken")) : clean ? h(
2948
2962
  "div",
2949
2963
  { part: "sub" },
2950
2964
  this.ts("backups.willBe", { file: this.#manager?.fileNameFor(clean) ?? "" })
@@ -3002,7 +3016,7 @@ var SelfstoreBackupsElement = class extends FlowWidget {
3002
3016
  this.rerender();
3003
3017
  }
3004
3018
  }),
3005
- taken ? h("div", { part: "error-note" }, this.t("backups.taken")) : clean ? h(
3019
+ taken ? h("div", { part: "error-note", role: "alert" }, this.t("backups.taken")) : clean ? h(
3006
3020
  "div",
3007
3021
  { part: "sub" },
3008
3022
  this.ts("backups.willBe", { file: this.#manager?.fileNameFor(clean) ?? "" })
@@ -3109,7 +3123,7 @@ var SelfstoreBackupsElement = class extends FlowWidget {
3109
3123
  this.t(`backups.replica.dest.${kind}`)
3110
3124
  )
3111
3125
  ),
3112
- snap.error ? h("div", { part: "error-note" }, this.t("error.generic")) : null,
3126
+ snap.error ? h("div", { part: "error-note", role: "alert" }, this.t("error.generic")) : null,
3113
3127
  h(
3114
3128
  "button",
3115
3129
  {
@@ -3161,7 +3175,7 @@ var SelfstoreBackupsElement = class extends FlowWidget {
3161
3175
  c.password,
3162
3176
  (v) => c.password = v
3163
3177
  ),
3164
- snap.error ? h("div", { part: "error-note" }, this.t("error.generic")) : null,
3178
+ snap.error ? h("div", { part: "error-note", role: "alert" }, this.t("error.generic")) : null,
3165
3179
  h(
3166
3180
  "div",
3167
3181
  { part: "row", style: "margin-top:0.45rem" },
@@ -3218,7 +3232,7 @@ var SelfstoreBackupsElement = class extends FlowWidget {
3218
3232
  c.secretAccessKey,
3219
3233
  (v) => c.secretAccessKey = v
3220
3234
  ),
3221
- snap.error ? h("div", { part: "error-note" }, this.t("error.generic")) : null,
3235
+ snap.error ? h("div", { part: "error-note", role: "alert" }, this.t("error.generic")) : null,
3222
3236
  h(
3223
3237
  "div",
3224
3238
  { part: "row", style: "margin-top:0.45rem" },
@@ -3628,6 +3642,11 @@ var SelfstoreGateElement = class extends FlowWidget {
3628
3642
  if (deferrable !== null) this.deferrable = deferrable;
3629
3643
  this.wire();
3630
3644
  }
3645
+ disconnectedCallback() {
3646
+ super.disconnectedCallback();
3647
+ document.removeEventListener("focusin", this.#keepFocus);
3648
+ this.#focusBefore = null;
3649
+ }
3631
3650
  host() {
3632
3651
  const s = this.#store;
3633
3652
  if (!s) return null;
@@ -3650,13 +3669,46 @@ var SelfstoreGateElement = class extends FlowWidget {
3650
3669
  if (!host.engine.state.ready) return false;
3651
3670
  return host.engine.state.status.action === "choose-destination";
3652
3671
  }
3672
+ /** Where focus was when the gate took over, so it can be handed back. */
3673
+ #focusBefore = null;
3674
+ /** `aria-modal` tells assistive tech this is modal; it does NOT stop the Tab
3675
+ * key. Without this, a keyboard user tabs straight out of a screen whose
3676
+ * whole purpose is to block, and lands in the application behind it -
3677
+ * invisibly, since the gate still covers what they are now driving.
3678
+ *
3679
+ * A guard rather than a cycle through the focusable elements: the gate
3680
+ * contains a `<selfstore-connect>` with its own shadow root, and walking
3681
+ * focusables across shadow boundaries is the kind of code that quietly
3682
+ * stops matching the DOM it walks. Anything landing outside comes straight
3683
+ * back to the card, which is correct whatever the widget grows inside. */
3684
+ #pulling = false;
3685
+ #keepFocus = (e) => {
3686
+ if (!this.#open || this.#pulling) return;
3687
+ if (e.composedPath().includes(this)) return;
3688
+ const card = this.root.querySelector('[part~="gate-card"]');
3689
+ if (!card) return;
3690
+ this.#pulling = true;
3691
+ try {
3692
+ card.focus();
3693
+ } finally {
3694
+ this.#pulling = false;
3695
+ }
3696
+ };
3653
3697
  sync() {
3654
3698
  const open = this.shouldOpen();
3655
3699
  if (open === this.#open) return;
3656
3700
  this.#open = open;
3657
3701
  if (!open) this.#connect = null;
3658
3702
  super.rerender();
3659
- if (open) this.root.querySelector('[part~="gate-card"]')?.focus();
3703
+ if (open) {
3704
+ this.#focusBefore = document.activeElement ?? null;
3705
+ document.addEventListener("focusin", this.#keepFocus);
3706
+ this.root.querySelector('[part~="gate-card"]')?.focus();
3707
+ return;
3708
+ }
3709
+ document.removeEventListener("focusin", this.#keepFocus);
3710
+ this.#focusBefore?.focus?.();
3711
+ this.#focusBefore = null;
3660
3712
  }
3661
3713
  /** Rebuilding the frame would re-append the child connect element, and a
3662
3714
  * custom element that leaves the DOM and comes back rebuilds its flow: a
@@ -4529,21 +4581,62 @@ var SelfstoreAccountElement = class extends FlowWidget {
4529
4581
  };
4530
4582
 
4531
4583
  // src/entries/widgets.ts
4584
+ function register(prefix, tags) {
4585
+ for (const [name, ctor] of Object.entries(tags)) {
4586
+ const tag = `${prefix}-${name}`;
4587
+ if (!customElements.get(tag)) customElements.define(tag, ctor);
4588
+ }
4589
+ }
4590
+ function defineConnect(prefix = "selfstore") {
4591
+ register(prefix, { connect: SelfstoreConnectElement });
4592
+ }
4593
+ function defineStatus(prefix = "selfstore") {
4594
+ register(prefix, { status: SelfstoreStatusElement });
4595
+ }
4596
+ function defineShare(prefix = "selfstore") {
4597
+ register(prefix, { share: SelfstoreShareElement });
4598
+ }
4599
+ function defineJoin(prefix = "selfstore") {
4600
+ register(prefix, { join: SelfstoreJoinElement });
4601
+ }
4602
+ function defineBackups(prefix = "selfstore") {
4603
+ register(prefix, { backups: SelfstoreBackupsElement });
4604
+ }
4605
+ function defineGate(prefix = "selfstore") {
4606
+ register(prefix, { connect: SelfstoreConnectElement, gate: SelfstoreGateElement });
4607
+ }
4608
+ function defineDestination(prefix = "selfstore") {
4609
+ register(prefix, {
4610
+ connect: SelfstoreConnectElement,
4611
+ status: SelfstoreStatusElement,
4612
+ destination: SelfstoreDestinationElement
4613
+ });
4614
+ }
4615
+ function defineStorage(prefix = "selfstore") {
4616
+ defineGate(prefix);
4617
+ defineDestination(prefix);
4618
+ register(prefix, { storage: SelfstoreStorageElement });
4619
+ }
4620
+ function defineAccount(prefix = "selfstore") {
4621
+ register(prefix, {
4622
+ status: SelfstoreStatusElement,
4623
+ account: SelfstoreAccountElement
4624
+ });
4625
+ }
4532
4626
  function defineSelfstoreWidgets(prefix = "selfstore") {
4533
- const define = (name, ctor) => {
4534
- if (!customElements.get(name)) customElements.define(name, ctor);
4535
- };
4536
- define(`${prefix}-connect`, SelfstoreConnectElement);
4537
- define(`${prefix}-share`, SelfstoreShareElement);
4538
- define(`${prefix}-join`, SelfstoreJoinElement);
4539
- define(`${prefix}-status`, SelfstoreStatusElement);
4540
- define(`${prefix}-backups`, SelfstoreBackupsElement);
4541
- define(`${prefix}-gate`, SelfstoreGateElement);
4542
- define(`${prefix}-destination`, SelfstoreDestinationElement);
4543
- define(`${prefix}-storage`, SelfstoreStorageElement);
4544
- define(`${prefix}-account`, SelfstoreAccountElement);
4627
+ register(prefix, {
4628
+ connect: SelfstoreConnectElement,
4629
+ share: SelfstoreShareElement,
4630
+ join: SelfstoreJoinElement,
4631
+ status: SelfstoreStatusElement,
4632
+ backups: SelfstoreBackupsElement,
4633
+ gate: SelfstoreGateElement,
4634
+ destination: SelfstoreDestinationElement,
4635
+ storage: SelfstoreStorageElement,
4636
+ account: SelfstoreAccountElement
4637
+ });
4545
4638
  }
4546
4639
 
4547
- export { FlowWidget, SelfstoreAccountElement, SelfstoreBackupsElement, SelfstoreConnectElement, SelfstoreDestinationElement, SelfstoreGateElement, SelfstoreJoinElement, SelfstoreShareElement, SelfstoreStatusElement, SelfstoreStorageElement, defaultIcons, defineSelfstoreWidgets };
4640
+ export { FlowWidget, SelfstoreAccountElement, SelfstoreBackupsElement, SelfstoreConnectElement, SelfstoreDestinationElement, SelfstoreGateElement, SelfstoreJoinElement, SelfstoreShareElement, SelfstoreStatusElement, SelfstoreStorageElement, defaultIcons, defineAccount, defineBackups, defineConnect, defineDestination, defineGate, defineJoin, defineSelfstoreWidgets, defineShare, defineStatus, defineStorage };
4548
4641
  //# sourceMappingURL=widgets.js.map
4549
4642
  //# sourceMappingURL=widgets.js.map