@wildwinter/app-shell 0.35.1 → 0.37.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,18 @@ 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
+
109
121
  - **`--ok`** joins `--danger` and `--warn` in `tokens.css`, with a `light-dark()`
110
122
  default every palette should override in its own `theme.css`. It is the only
111
123
  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);
@@ -335,7 +335,11 @@ var openVar = { nav: "--nav-open-w", inspector: "--insp-open-w" };
335
335
  var collapseClass = { nav: "no-nav", inspector: "no-inspector" };
336
336
  function mountPaneShell(host, opts) {
337
337
  const cfg = { nav: opts.nav, inspector: opts.inspector };
338
- const open = { nav: opts.initial?.open?.nav ?? true, inspector: opts.initial?.open?.inspector ?? true };
338
+ const offered = (side) => cfg[side].offered !== false;
339
+ const open = {
340
+ nav: offered("nav") && (opts.initial?.open?.nav ?? true),
341
+ inspector: offered("inspector") && (opts.initial?.open?.inspector ?? true)
342
+ };
339
343
  const width = { ...opts.initial?.width ?? {} };
340
344
  const el2 = (tag, cls) => {
341
345
  const n = document.createElement(tag);
@@ -365,6 +369,8 @@ function mountPaneShell(host, opts) {
365
369
  const root = el2("div", "pane-shell");
366
370
  root.append(topbar, panes);
367
371
  host.append(root);
372
+ if (!offered("nav")) navToggle.remove();
373
+ if (!offered("inspector")) inspToggle.remove();
368
374
  function applyToggleGlyph(side, button) {
369
375
  const isOpen = open[side];
370
376
  button.textContent = side === "nav" ? isOpen ? "\u2039" : "\u203A" : isOpen ? "\u203A" : "\u2039";
@@ -381,8 +387,8 @@ function mountPaneShell(host, opts) {
381
387
  panes.style.setProperty(openVar.inspector, width.inspector !== void 0 ? `${width.inspector}px` : cfg.inspector.defaultWidth);
382
388
  applyToggleGlyph("nav", navToggle);
383
389
  applyToggleGlyph("inspector", inspToggle);
384
- navResizer.style.display = cfg.nav.resizable === false ? "none" : "";
385
- inspResizer.style.display = cfg.inspector.resizable === false ? "none" : "";
390
+ navResizer.style.display = cfg.nav.resizable === false || !offered("nav") ? "none" : "";
391
+ inspResizer.style.display = cfg.inspector.resizable === false || !offered("inspector") ? "none" : "";
386
392
  }
387
393
  const snapshot = () => ({ open: { ...open }, width: { ...width } });
388
394
  const persist = () => opts.onChange?.(snapshot());
@@ -393,7 +399,7 @@ function mountPaneShell(host, opts) {
393
399
  persist();
394
400
  }
395
401
  function togglePane(side) {
396
- setPaneOpen(side, !open[side]);
402
+ if (offered(side)) setPaneOpen(side, !open[side]);
397
403
  }
398
404
  function resetWidths() {
399
405
  delete width.nav;
@@ -674,14 +680,14 @@ function closeWithExit(element, done) {
674
680
  if (fired) return;
675
681
  fired = true;
676
682
  element.removeEventListener("animationend", onEnd);
677
- clearTimeout(timer2);
683
+ clearTimeout(timer3);
678
684
  done();
679
685
  };
680
686
  const onEnd = (e) => {
681
687
  if (e.target === element) finish();
682
688
  };
683
689
  element.addEventListener("animationend", onEnd);
684
- const timer2 = setTimeout(finish, seconds * 1e3 + 120);
690
+ const timer3 = setTimeout(finish, seconds * 1e3 + 120);
685
691
  }
686
692
 
687
693
  // src/confirm.ts
@@ -1524,7 +1530,7 @@ function createSaveController(opts) {
1524
1530
  let auto = true;
1525
1531
  let dirty = false;
1526
1532
  let dirtySince = 0;
1527
- let timer2;
1533
+ let timer3;
1528
1534
  let inFlight;
1529
1535
  const setStatus = (next) => {
1530
1536
  if (next === status) return;
@@ -1532,17 +1538,17 @@ function createSaveController(opts) {
1532
1538
  opts.onStatus?.(status);
1533
1539
  };
1534
1540
  const clearTimer = () => {
1535
- if (timer2 === void 0) return;
1536
- clearTimeout(timer2);
1537
- timer2 = void 0;
1541
+ if (timer3 === void 0) return;
1542
+ clearTimeout(timer3);
1543
+ timer3 = void 0;
1538
1544
  };
1539
1545
  const arm = () => {
1540
1546
  clearTimer();
1541
1547
  if (!auto) return;
1542
1548
  const deadline = dirtySince + maxWaitMs;
1543
1549
  const wait = Math.max(0, Math.min(delayMs, deadline - Date.now()));
1544
- timer2 = setTimeout(() => {
1545
- timer2 = void 0;
1550
+ timer3 = setTimeout(() => {
1551
+ timer3 = void 0;
1546
1552
  void run();
1547
1553
  }, wait);
1548
1554
  };
@@ -1616,13 +1622,13 @@ var WORDS = {
1616
1622
  unsaved: "Unsaved"
1617
1623
  };
1618
1624
  function saveIndicator(initial = "saved") {
1619
- const node = el("span");
1625
+ const node2 = el("span");
1620
1626
  const set = (status) => {
1621
- node.className = `savestat ${status}`;
1622
- node.textContent = WORDS[status];
1627
+ node2.className = `savestat ${status}`;
1628
+ node2.textContent = WORDS[status];
1623
1629
  };
1624
1630
  set(initial);
1625
- return { el: node, set };
1631
+ return { el: node2, set };
1626
1632
  }
1627
1633
 
1628
1634
  // src/stale.ts
@@ -1643,6 +1649,43 @@ function staleBar(opts) {
1643
1649
  );
1644
1650
  }
1645
1651
 
1652
+ // src/toast.ts
1653
+ var DURATION = { info: 4e3, ok: 4e3, error: 7e3 };
1654
+ var node;
1655
+ var timer2 = 0;
1656
+ var seq = 0;
1657
+ function mount() {
1658
+ if (node && node.isConnected) return node;
1659
+ node = el("div", { className: "shell-toast" });
1660
+ node.setAttribute("role", "status");
1661
+ node.setAttribute("aria-live", "polite");
1662
+ node.hidden = true;
1663
+ document.body.append(node);
1664
+ return node;
1665
+ }
1666
+ function toast(message, kind = "info", opts = {}) {
1667
+ const t = mount();
1668
+ const id = ++seq;
1669
+ t.textContent = message;
1670
+ t.className = `shell-toast ${kind}`;
1671
+ t.hidden = false;
1672
+ window.clearTimeout(timer2);
1673
+ timer2 = window.setTimeout(() => {
1674
+ closeWithExit(t, () => {
1675
+ if (seq === id) t.hidden = true;
1676
+ });
1677
+ }, opts.duration ?? DURATION[kind]);
1678
+ }
1679
+ function dismissToast() {
1680
+ if (!node || node.hidden) return;
1681
+ window.clearTimeout(timer2);
1682
+ const id = seq;
1683
+ const t = node;
1684
+ closeWithExit(t, () => {
1685
+ if (seq === id) t.hidden = true;
1686
+ });
1687
+ }
1688
+
1646
1689
  // src/stepper.ts
1647
1690
  function renderStepperBar(host, opts) {
1648
1691
  const { items } = opts;
@@ -1783,6 +1826,7 @@ exports.colourIndex = colourIndex;
1783
1826
  exports.confirmDialog = confirmDialog;
1784
1827
  exports.createNavHistory = createNavHistory;
1785
1828
  exports.createSaveController = createSaveController;
1829
+ exports.dismissToast = dismissToast;
1786
1830
  exports.dupGuard = dupGuard;
1787
1831
  exports.el = el;
1788
1832
  exports.expandableRow = expandableRow;
@@ -1824,6 +1868,7 @@ exports.staleBar = staleBar;
1824
1868
  exports.tagChips = tagChips;
1825
1869
  exports.tipAt = tipAt;
1826
1870
  exports.tipBold = tipBold;
1871
+ exports.toast = toast;
1827
1872
  exports.vcBadgeFor = vcBadgeFor;
1828
1873
  //# sourceMappingURL=index.cjs.map
1829
1874
  //# sourceMappingURL=index.cjs.map