@wildwinter/app-shell 0.36.0 → 0.38.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.
package/README.md CHANGED
@@ -106,6 +106,31 @@ needing and never had.
106
106
  staleBar({ subject: "The project", onRestart: () => void rebuild() });
107
107
  ```
108
108
 
109
+ - **`toast`** - a transient remark: "Bundle published", "Save refused: ...".
110
+ Bottom-right, an outlined card on the surface colour with the kind in the
111
+ border, on the shared panel motion; one at a time, 4s (7s for an error).
112
+ Both apps had one, and they agreed on nothing but the sentences - place,
113
+ colour, timing, motion and ARIA all differed. It mounts its own node, so a
114
+ second window gets it by importing it. Bring `toast.css`.
115
+
116
+ ```ts
117
+ toast(`Bundle published\n${where}`, "ok");
118
+ toast(`Save refused: ${err}`, "error");
119
+ ```
120
+
121
+ - **`revealRow`** - land on one row of a settings list. `expandableRow` takes a
122
+ `name` and stamps it on the row; `revealRow(within, name)` opens that row's
123
+ details, brings it to the middle of its scroller and lights it for a moment.
124
+ "Go to definition" in both apps opened the page a property is declared on and
125
+ stopped there, with the row off screen. It returns false when no row carries
126
+ the name, so a host whose page fills in late asks again on a later frame.
127
+ Brings nothing new: the light is in `settings.css`.
128
+
129
+ ```ts
130
+ list.append(expandableRow({ line, details, name: decl.name }));
131
+ revealRow(document, "mood");
132
+ ```
133
+
109
134
  - **`--ok`** joins `--danger` and `--warn` in `tokens.css`, with a `light-dark()`
110
135
  default every palette should override in its own `theme.css`. It is the only
111
136
  colour in the grammar layer, and it is there because it kept not being
package/dist/index.cjs CHANGED
@@ -151,23 +151,23 @@ function initTooltips(opts = {}) {
151
151
 
152
152
  // src/dom.ts
153
153
  function el(tag, classNameOrProps, ...children) {
154
- const node = document.createElement(tag);
154
+ const node2 = document.createElement(tag);
155
155
  const props = typeof classNameOrProps === "string" ? { className: classNameOrProps } : classNameOrProps ?? {};
156
- if (props.className) node.className = props.className;
157
- if (props.text !== void 0 && props.text !== null) node.textContent = props.text;
158
- if (props.title !== void 0) node.title = props.title;
156
+ if (props.className) node2.className = props.className;
157
+ if (props.text !== void 0 && props.text !== null) node2.textContent = props.text;
158
+ if (props.title !== void 0) node2.title = props.title;
159
159
  if (props.tip !== void 0) {
160
- node.dataset["tip"] = props.tip;
161
- node.setAttribute("aria-label", props.tip);
160
+ node2.dataset["tip"] = props.tip;
161
+ node2.setAttribute("aria-label", props.tip);
162
162
  ensureTooltipHost();
163
163
  }
164
- if (props.onClick) node.addEventListener("click", props.onClick);
164
+ if (props.onClick) node2.addEventListener("click", props.onClick);
165
165
  for (const child of children) {
166
166
  if (child === null || child === void 0) continue;
167
- node.append(child);
167
+ node2.append(child);
168
168
  }
169
169
  checkTooltipHost();
170
- return node;
170
+ return node2;
171
171
  }
172
172
  function iconBtn(glyph, title, onClick, disabled = false, danger = false) {
173
173
  const b = el("button", `shell-icon${danger ? " danger" : ""}`);
@@ -199,7 +199,7 @@ function tagChips(holder, onChange) {
199
199
  const wrap = el("div", "shell-tags");
200
200
  const input = el("input", "shell-tag-input");
201
201
  input.type = "text";
202
- input.placeholder = "add value";
202
+ input.placeholder = "Add value";
203
203
  input.spellcheck = false;
204
204
  const makeChip = (v) => {
205
205
  const chip = el("span", "shell-tag", v);
@@ -486,6 +486,7 @@ var PANE_MENU = {
486
486
  // src/settings.ts
487
487
  function expandableRow(opts) {
488
488
  const row = el("div", "set-row");
489
+ if (opts.name !== void 0) row.dataset.name = opts.name;
489
490
  const line = el("div", "set-rowline");
490
491
  if (opts.details && opts.details.length) {
491
492
  const details = el("div", "set-details");
@@ -511,6 +512,19 @@ function expandableRow(opts) {
511
512
  }
512
513
  return row;
513
514
  }
515
+ function revealRow(within, name) {
516
+ const row = [...within.querySelectorAll(".set-row")].find((r) => r.dataset.name === name);
517
+ if (!row) return false;
518
+ const toggle = row.querySelector(".set-rowline > .set-expand");
519
+ if (toggle && toggle.getAttribute("aria-expanded") !== "true") toggle.click();
520
+ const still = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
521
+ row.scrollIntoView({ block: "center", behavior: still ? "auto" : "smooth" });
522
+ row.classList.remove("landed");
523
+ void row.offsetWidth;
524
+ row.classList.add("landed");
525
+ row.addEventListener("animationend", () => row.classList.remove("landed"), { once: true });
526
+ return true;
527
+ }
514
528
  function focusNewRow(listEl) {
515
529
  const last = listEl?.lastElementChild;
516
530
  if (!(last instanceof HTMLElement)) return;
@@ -680,14 +694,14 @@ function closeWithExit(element, done) {
680
694
  if (fired) return;
681
695
  fired = true;
682
696
  element.removeEventListener("animationend", onEnd);
683
- clearTimeout(timer2);
697
+ clearTimeout(timer3);
684
698
  done();
685
699
  };
686
700
  const onEnd = (e) => {
687
701
  if (e.target === element) finish();
688
702
  };
689
703
  element.addEventListener("animationend", onEnd);
690
- const timer2 = setTimeout(finish, seconds * 1e3 + 120);
704
+ const timer3 = setTimeout(finish, seconds * 1e3 + 120);
691
705
  }
692
706
 
693
707
  // src/confirm.ts
@@ -1530,7 +1544,7 @@ function createSaveController(opts) {
1530
1544
  let auto = true;
1531
1545
  let dirty = false;
1532
1546
  let dirtySince = 0;
1533
- let timer2;
1547
+ let timer3;
1534
1548
  let inFlight;
1535
1549
  const setStatus = (next) => {
1536
1550
  if (next === status) return;
@@ -1538,17 +1552,17 @@ function createSaveController(opts) {
1538
1552
  opts.onStatus?.(status);
1539
1553
  };
1540
1554
  const clearTimer = () => {
1541
- if (timer2 === void 0) return;
1542
- clearTimeout(timer2);
1543
- timer2 = void 0;
1555
+ if (timer3 === void 0) return;
1556
+ clearTimeout(timer3);
1557
+ timer3 = void 0;
1544
1558
  };
1545
1559
  const arm = () => {
1546
1560
  clearTimer();
1547
1561
  if (!auto) return;
1548
1562
  const deadline = dirtySince + maxWaitMs;
1549
1563
  const wait = Math.max(0, Math.min(delayMs, deadline - Date.now()));
1550
- timer2 = setTimeout(() => {
1551
- timer2 = void 0;
1564
+ timer3 = setTimeout(() => {
1565
+ timer3 = void 0;
1552
1566
  void run();
1553
1567
  }, wait);
1554
1568
  };
@@ -1622,13 +1636,13 @@ var WORDS = {
1622
1636
  unsaved: "Unsaved"
1623
1637
  };
1624
1638
  function saveIndicator(initial = "saved") {
1625
- const node = el("span");
1639
+ const node2 = el("span");
1626
1640
  const set = (status) => {
1627
- node.className = `savestat ${status}`;
1628
- node.textContent = WORDS[status];
1641
+ node2.className = `savestat ${status}`;
1642
+ node2.textContent = WORDS[status];
1629
1643
  };
1630
1644
  set(initial);
1631
- return { el: node, set };
1645
+ return { el: node2, set };
1632
1646
  }
1633
1647
 
1634
1648
  // src/stale.ts
@@ -1649,6 +1663,43 @@ function staleBar(opts) {
1649
1663
  );
1650
1664
  }
1651
1665
 
1666
+ // src/toast.ts
1667
+ var DURATION = { info: 4e3, ok: 4e3, error: 7e3 };
1668
+ var node;
1669
+ var timer2 = 0;
1670
+ var seq = 0;
1671
+ function mount() {
1672
+ if (node && node.isConnected) return node;
1673
+ node = el("div", { className: "shell-toast" });
1674
+ node.setAttribute("role", "status");
1675
+ node.setAttribute("aria-live", "polite");
1676
+ node.hidden = true;
1677
+ document.body.append(node);
1678
+ return node;
1679
+ }
1680
+ function toast(message, kind = "info", opts = {}) {
1681
+ const t = mount();
1682
+ const id = ++seq;
1683
+ t.textContent = message;
1684
+ t.className = `shell-toast ${kind}`;
1685
+ t.hidden = false;
1686
+ window.clearTimeout(timer2);
1687
+ timer2 = window.setTimeout(() => {
1688
+ closeWithExit(t, () => {
1689
+ if (seq === id) t.hidden = true;
1690
+ });
1691
+ }, opts.duration ?? DURATION[kind]);
1692
+ }
1693
+ function dismissToast() {
1694
+ if (!node || node.hidden) return;
1695
+ window.clearTimeout(timer2);
1696
+ const id = seq;
1697
+ const t = node;
1698
+ closeWithExit(t, () => {
1699
+ if (seq === id) t.hidden = true;
1700
+ });
1701
+ }
1702
+
1652
1703
  // src/stepper.ts
1653
1704
  function renderStepperBar(host, opts) {
1654
1705
  const { items } = opts;
@@ -1789,6 +1840,7 @@ exports.colourIndex = colourIndex;
1789
1840
  exports.confirmDialog = confirmDialog;
1790
1841
  exports.createNavHistory = createNavHistory;
1791
1842
  exports.createSaveController = createSaveController;
1843
+ exports.dismissToast = dismissToast;
1792
1844
  exports.dupGuard = dupGuard;
1793
1845
  exports.el = el;
1794
1846
  exports.expandableRow = expandableRow;
@@ -1824,12 +1876,14 @@ exports.propertyNameify = propertyNameify;
1824
1876
  exports.propertyRefProblem = propertyRefProblem;
1825
1877
  exports.renderStepperBar = renderStepperBar;
1826
1878
  exports.revalidatePropertyRefs = revalidatePropertyRefs;
1879
+ exports.revealRow = revealRow;
1827
1880
  exports.saveIndicator = saveIndicator;
1828
1881
  exports.showAbout = showAbout;
1829
1882
  exports.staleBar = staleBar;
1830
1883
  exports.tagChips = tagChips;
1831
1884
  exports.tipAt = tipAt;
1832
1885
  exports.tipBold = tipBold;
1886
+ exports.toast = toast;
1833
1887
  exports.vcBadgeFor = vcBadgeFor;
1834
1888
  //# sourceMappingURL=index.cjs.map
1835
1889
  //# sourceMappingURL=index.cjs.map