@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/README.md +24 -1
- package/css/app-shell.css +28 -0
- package/dist/preact.cjs +62 -4
- package/dist/preact.cjs.map +1 -1
- package/dist/preact.d.cts +9 -2
- package/dist/preact.d.ts +9 -2
- package/dist/preact.js +62 -4
- package/dist/preact.js.map +1 -1
- package/package.json +4 -1
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
|
-
/**
|
|
469
|
-
|
|
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
|
-
/**
|
|
469
|
-
|
|
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(
|
|
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: [
|