@broberg/app-shell 0.10.0 → 0.11.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/dist/preact.d.cts CHANGED
@@ -465,8 +465,15 @@ interface UserMenuProps {
465
465
  items?: UserMenuItem[];
466
466
  onSignOut?: () => void | Promise<void>;
467
467
  onNavigate?: Navigate;
468
- /** Show Theme · Palette · Surfaces · Backdrop. Default true. */
469
- appearance?: boolean;
468
+ /**
469
+ * Show Theme · Palette · Surfaces. Default true; false hides the block.
470
+ * F001.23 — the Backdrop row (Neurons · Plain) is opt-in: pass
471
+ * `{ backdrop: true }`. Only apps that draw the neuron canvas
472
+ * (mountConstellation — trail, cardmem) have anything for it to switch.
473
+ */
474
+ appearance?: boolean | {
475
+ backdrop?: boolean;
476
+ };
470
477
  /**
471
478
  * F092.11 — a «Sprog»/«Language» row with a segmented control, like the
472
479
  * appearance rows. The app owns the language; the shell shows and reports it.
package/dist/preact.d.ts CHANGED
@@ -465,8 +465,15 @@ interface UserMenuProps {
465
465
  items?: UserMenuItem[];
466
466
  onSignOut?: () => void | Promise<void>;
467
467
  onNavigate?: Navigate;
468
- /** Show Theme · Palette · Surfaces · Backdrop. Default true. */
469
- appearance?: boolean;
468
+ /**
469
+ * Show Theme · Palette · Surfaces. Default true; false hides the block.
470
+ * F001.23 — the Backdrop row (Neurons · Plain) is opt-in: pass
471
+ * `{ backdrop: true }`. Only apps that draw the neuron canvas
472
+ * (mountConstellation — trail, cardmem) have anything for it to switch.
473
+ */
474
+ appearance?: boolean | {
475
+ backdrop?: boolean;
476
+ };
470
477
  /**
471
478
  * F092.11 — a «Sprog»/«Language» row with a segmented control, like the
472
479
  * appearance rows. The app owns the language; the shell shows and reports it.
package/dist/preact.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { useState, useEffect, useRef, useMemo, useLayoutEffect } from 'preact/hooks';
2
2
  import { createBellShell } from '@broberg/notifications/shell';
3
+ import { selectKeyReducer } from '@broberg/ui-controls-core';
3
4
  import { getPreference, getPalette, getSurfaces, getBackdrop, onThemeChange, onPaletteChange, onSurfacesChange, onBackdropChange, setPreference, setPalette, PALETTES, PALETTE_LABELS, setSurfaces, setBackdrop } from '@broberg/theme';
4
5
  import { jsxs, Fragment, jsx } from 'preact/jsx-runtime';
5
6
 
@@ -635,7 +636,7 @@ function Sidebar(p) {
635
636
  "aria-label": "Navigation",
636
637
  children: [
637
638
  p.header || p.brandMark ? /* @__PURE__ */ jsxs("div", { class: "bas-sidebar__head", children: [
638
- p.header ? /* @__PURE__ */ jsx("div", { class: "bas-sidebar__brand", children: p.homeHref ? /* @__PURE__ */ jsx(NavLink, { href: p.homeHref, onNavigate: p.onNavigate, onAfter: close, class: "bas-home", "data-testid": p.brandTestId ?? "sidebar-brand-home", children: p.header }) : p.header }) : null,
639
+ p.header ? /* @__PURE__ */ jsx("div", { class: "bas-sidebar__brand", children: p.homeHref ? /* @__PURE__ */ jsx(NavLink, { href: p.homeHref, onNavigate: p.onNavigate, onAfter: close, class: "bas-home", "data-testid": p.brandTestId ?? "sidebar-brand-home", "aria-label": p.homeLabel, children: p.header }) : p.header }) : null,
639
640
  p.brandMark ? /* @__PURE__ */ jsx("div", { class: "bas-sidebar__mark", "data-testid": "sidebar-brand-mark", children: p.homeHref ? /* @__PURE__ */ jsx(
640
641
  NavLink,
641
642
  {
@@ -842,6 +843,63 @@ function Segmented(props) {
842
843
  };
843
844
  return /* @__PURE__ */ jsx("div", { class: "bas-seg", "data-testid": props.testid, role: "group", "aria-label": props.label, onKeyDown, children: props.options.map(([v, label]) => /* @__PURE__ */ jsx("button", { type: "button", "aria-pressed": props.value === v, "data-testid": `${props.testid}-${v}`, onClick: () => props.onChange(v), children: label }, v)) });
844
845
  }
846
+ function Dropdown(props) {
847
+ const [state, setState] = useState({ open: false, highlighted: -1 });
848
+ const current = Math.max(0, props.options.findIndex(([v]) => v === props.value));
849
+ const choose = (i) => {
850
+ setState({ open: false, highlighted: i });
851
+ props.onChange(props.options[i][0]);
852
+ };
853
+ const onKeyDown = (e) => {
854
+ if (e.key === "Tab") return setState((st) => ({ ...st, open: false }));
855
+ const from = state.open ? state : { open: false, highlighted: current };
856
+ const { state: next, intent } = selectKeyReducer(from, e.key, props.options.length);
857
+ if (intent.type === "none" && next === from) return;
858
+ e.preventDefault();
859
+ if (state.open || intent.type !== "close") e.stopPropagation();
860
+ if (intent.type === "select") return choose(intent.index);
861
+ setState(next);
862
+ };
863
+ const optId = (v) => `${props.testid}-opt-${v}`;
864
+ return /* @__PURE__ */ jsxs("div", { class: "bas-dd", onFocusOut: (e) => !e.currentTarget.contains(e.relatedTarget) && setState((st) => ({ ...st, open: false })), children: [
865
+ /* @__PURE__ */ jsxs(
866
+ "button",
867
+ {
868
+ type: "button",
869
+ class: "bas-dd__btn",
870
+ "data-testid": props.testid,
871
+ "aria-haspopup": "listbox",
872
+ "aria-expanded": state.open,
873
+ "aria-label": props.label,
874
+ "aria-activedescendant": state.open && state.highlighted >= 0 ? optId(props.options[state.highlighted][0]) : void 0,
875
+ onClick: () => setState((st) => ({ open: !st.open, highlighted: current })),
876
+ onKeyDown,
877
+ children: [
878
+ /* @__PURE__ */ jsx("span", { children: props.options[current]?.[1] }),
879
+ /* @__PURE__ */ jsx("span", { class: "bas-dd__caret", "aria-hidden": "true", children: "\u25BE" })
880
+ ]
881
+ }
882
+ ),
883
+ state.open ? /* @__PURE__ */ jsx("ul", { class: "bas-dd__list", role: "listbox", "aria-label": props.label, "data-testid": `${props.testid}-list`, children: props.options.map(([v, label], i) => /* @__PURE__ */ jsxs(
884
+ "li",
885
+ {
886
+ id: optId(v),
887
+ role: "option",
888
+ "aria-selected": props.value === v,
889
+ class: "bas-dd__opt" + (i === state.highlighted ? " is-active" : ""),
890
+ "data-testid": `${props.testid}-${v}`,
891
+ onMouseDown: (e) => e.preventDefault(),
892
+ onMouseEnter: () => setState((st) => ({ ...st, highlighted: i })),
893
+ onClick: () => choose(i),
894
+ children: [
895
+ /* @__PURE__ */ jsx("span", { class: "bas-mi__check", "aria-hidden": "true", children: props.value === v ? "\u2713" : "" }),
896
+ label
897
+ ]
898
+ },
899
+ v
900
+ )) }) : null
901
+ ] });
902
+ }
845
903
  function UserMenu(p) {
846
904
  const t = TEXT[p.lang];
847
905
  const [open, setOpen] = useState(false);
@@ -1002,16 +1060,16 @@ function UserMenu(p) {
1002
1060
  ] }),
1003
1061
  /* @__PURE__ */ jsxs("div", { class: "bas-pref", children: [
1004
1062
  /* @__PURE__ */ jsx("span", { children: t.palette }),
1005
- /* @__PURE__ */ jsx(Segmented, { testid: "user-menu-palette", value: palette, options: PALETTES.map((x) => [x, PALETTE_LABELS[x]]), onChange: setPalette })
1063
+ /* @__PURE__ */ jsx(Dropdown, { testid: "user-menu-palette", label: t.palette, value: palette, options: PALETTES.map((x) => [x, PALETTE_LABELS[x]]), onChange: setPalette })
1006
1064
  ] }),
1007
1065
  /* @__PURE__ */ jsxs("div", { class: "bas-pref", children: [
1008
1066
  /* @__PURE__ */ jsx("span", { children: t.surfaces }),
1009
1067
  /* @__PURE__ */ jsx(Segmented, { testid: "user-menu-surfaces", value: surfaces, options: [["flat", t.flat], ["layered", t.layered]], onChange: setSurfaces })
1010
1068
  ] }),
1011
- /* @__PURE__ */ jsxs("div", { class: "bas-pref", children: [
1069
+ typeof p.appearance === "object" && p.appearance.backdrop ? /* @__PURE__ */ jsxs("div", { class: "bas-pref", children: [
1012
1070
  /* @__PURE__ */ jsx("span", { children: t.backdrop }),
1013
1071
  /* @__PURE__ */ jsx(Segmented, { testid: "user-menu-backdrop", value: backdrop, options: [["neurons", t.neurons], ["plain", t.plain]], onChange: setBackdrop })
1014
- ] })
1072
+ ] }) : null
1015
1073
  ] })
1016
1074
  ] }) : null,
1017
1075
  p.language ? /* @__PURE__ */ jsxs(Fragment, { children: [