@broberg/app-shell 0.20.1 → 0.22.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
@@ -49,6 +49,10 @@ interface NavAction {
49
49
  confirm?: {
50
50
  label: string;
51
51
  };
52
+ /** F092.32 — a dimmed value right-aligned before the chevron («Type …… All ›»). */
53
+ value?: string;
54
+ /** F092.32 — a divider line before this item, to separate groups. */
55
+ separator?: boolean;
52
56
  }
53
57
  interface NavSection<Icon = unknown> {
54
58
  /** A sub-heading inside one group, e.g. "I dag" / "I går". */
@@ -471,8 +475,12 @@ interface ItemActionsMenuProps {
471
475
  lang: Lang;
472
476
  /** The menu's accessible name, e.g. the conversation's title. */
473
477
  label: string;
474
- /** "auto" (default): below the anchor if it fits, else above. "down": always below. */
475
- placement?: "auto" | "down";
478
+ /**
479
+ * "auto" (default): right-aligned to the anchor, below it if it fits, else above.
480
+ * "down": right-aligned, always below. "up-start" (F092.31): ABOVE the anchor and
481
+ * left-aligned to its left edge (e.g. a «+» in a composer); below if there is no room above.
482
+ */
483
+ placement?: "auto" | "down" | "up-start";
476
484
  /** testids: `<prefix>-menu`, `<prefix>-action-<id>`, `<prefix>-back`, `<prefix>-input`, `<prefix>-save`. Default "item-actions". */
477
485
  testidPrefix?: string;
478
486
  }
package/dist/preact.d.ts CHANGED
@@ -49,6 +49,10 @@ interface NavAction {
49
49
  confirm?: {
50
50
  label: string;
51
51
  };
52
+ /** F092.32 — a dimmed value right-aligned before the chevron («Type …… All ›»). */
53
+ value?: string;
54
+ /** F092.32 — a divider line before this item, to separate groups. */
55
+ separator?: boolean;
52
56
  }
53
57
  interface NavSection<Icon = unknown> {
54
58
  /** A sub-heading inside one group, e.g. "I dag" / "I går". */
@@ -471,8 +475,12 @@ interface ItemActionsMenuProps {
471
475
  lang: Lang;
472
476
  /** The menu's accessible name, e.g. the conversation's title. */
473
477
  label: string;
474
- /** "auto" (default): below the anchor if it fits, else above. "down": always below. */
475
- placement?: "auto" | "down";
478
+ /**
479
+ * "auto" (default): right-aligned to the anchor, below it if it fits, else above.
480
+ * "down": right-aligned, always below. "up-start" (F092.31): ABOVE the anchor and
481
+ * left-aligned to its left edge (e.g. a «+» in a composer); below if there is no room above.
482
+ */
483
+ placement?: "auto" | "down" | "up-start";
476
484
  /** testids: `<prefix>-menu`, `<prefix>-action-<id>`, `<prefix>-back`, `<prefix>-input`, `<prefix>-save`. Default "item-actions". */
477
485
  testidPrefix?: string;
478
486
  }
package/dist/preact.js CHANGED
@@ -544,6 +544,7 @@ function ItemActionsMenu(props) {
544
544
  const [pos, setPos] = useState(void 0);
545
545
  const view = stack[stack.length - 1];
546
546
  const closed = useRef(false);
547
+ const [kbd, setKbd] = useState(() => props.anchor.matches(":focus-visible"));
547
548
  const close = (refocus = true) => {
548
549
  if (closed.current) return;
549
550
  closed.current = true;
@@ -577,9 +578,12 @@ function ItemActionsMenu(props) {
577
578
  const vw = window.innerWidth, vh = window.innerHeight, m = 8;
578
579
  const w = Math.min(240, vw - 2 * m);
579
580
  const h = menu.current?.offsetHeight ?? 0;
580
- const left = Math.max(m, Math.min(r.right - w, vw - w - m));
581
+ const start = props.placement === "up-start";
582
+ const left = Math.max(m, Math.min(start ? r.left : r.right - w, vw - w - m));
581
583
  const fitsBelow = r.bottom + 4 + h <= vh - m;
582
- const top = props.placement === "down" || fitsBelow || r.top - 4 - h < m ? Math.min(r.bottom + 4, vh - m - h) : r.top - 4 - h;
584
+ const fitsAbove = r.top - 4 - h >= m;
585
+ const below = start ? !fitsAbove : props.placement === "down" || fitsBelow || !fitsAbove;
586
+ const top = below ? Math.min(r.bottom + 4, vh - m - h) : r.top - 4 - h;
583
587
  setPos({ position: "fixed", left: `${left}px`, top: `${Math.max(m, top)}px`, width: `${w}px` });
584
588
  }, [stack.length]);
585
589
  useLayoutEffect(() => {
@@ -616,6 +620,7 @@ function ItemActionsMenu(props) {
616
620
  const view2 = stack2[stack2.length - 1];
617
621
  if (!view2) return;
618
622
  e.stopPropagation();
623
+ setKbd(true);
619
624
  if (e.key === "Escape") {
620
625
  e.preventDefault();
621
626
  return close();
@@ -658,7 +663,7 @@ function ItemActionsMenu(props) {
658
663
  "div",
659
664
  {
660
665
  ref: menu,
661
- class: "bas-rowmenu",
666
+ class: "bas-rowmenu" + (kbd ? " is-kbd" : ""),
662
667
  role: "menu",
663
668
  tabIndex: -1,
664
669
  "aria-label": view.kind === "list" && view.title ? view.title : props.label,
@@ -670,28 +675,32 @@ function ItemActionsMenu(props) {
670
675
  /* @__PURE__ */ jsx(IconChevronLeft, {}),
671
676
  /* @__PURE__ */ jsx("span", { children: view.kind === "list" ? view.title : view.action.label })
672
677
  ] }) : null,
673
- view.kind === "list" ? view.actions.map((a, i) => /* @__PURE__ */ jsxs(
674
- "button",
675
- {
676
- type: "button",
677
- role: "menuitem",
678
- ref: (el) => {
679
- items.current[i] = el;
678
+ view.kind === "list" ? view.actions.map((a, i) => [
679
+ a.separator ? /* @__PURE__ */ jsx("div", { class: "bas-rowmenu__sep", role: "separator" }, `${a.id}-sep`) : null,
680
+ /* @__PURE__ */ jsxs(
681
+ "button",
682
+ {
683
+ type: "button",
684
+ role: "menuitem",
685
+ ref: (el) => {
686
+ items.current[i] = el;
687
+ },
688
+ tabIndex: i === hi ? 0 : -1,
689
+ class: "bas-rowmenu__item" + (i === hi ? " is-active" : "") + (a.danger ? " is-danger" : "") + (armed === a.id ? " is-armed" : ""),
690
+ "data-testid": `${pre}-action-${a.id}`,
691
+ onMouseEnter: () => setHi(i),
692
+ onFocus: () => setHi(i),
693
+ onClick: () => pick(a),
694
+ children: [
695
+ a.icon ? /* @__PURE__ */ jsx("span", { class: "bas-rowmenu__icon", children: a.icon }) : null,
696
+ /* @__PURE__ */ jsx("span", { class: "bas-rowmenu__label", children: armed === a.id && a.confirm ? a.confirm.label : a.label }),
697
+ a.value ? /* @__PURE__ */ jsx("span", { class: "bas-rowmenu__value", "data-testid": `${pre}-action-${a.id}-value`, children: a.value }) : null,
698
+ a.submenu?.length ? /* @__PURE__ */ jsx(IconChevronRight, {}) : a.kbd ? /* @__PURE__ */ jsx("kbd", { class: "bas-rowmenu__kbd", children: a.kbd.toUpperCase() }) : null
699
+ ]
680
700
  },
681
- tabIndex: i === hi ? 0 : -1,
682
- class: "bas-rowmenu__item" + (i === hi ? " is-active" : "") + (a.danger ? " is-danger" : "") + (armed === a.id ? " is-armed" : ""),
683
- "data-testid": `${pre}-action-${a.id}`,
684
- onMouseEnter: () => setHi(i),
685
- onFocus: () => setHi(i),
686
- onClick: () => pick(a),
687
- children: [
688
- a.icon ? /* @__PURE__ */ jsx("span", { class: "bas-rowmenu__icon", children: a.icon }) : null,
689
- /* @__PURE__ */ jsx("span", { class: "bas-rowmenu__label", children: armed === a.id && a.confirm ? a.confirm.label : a.label }),
690
- a.submenu?.length ? /* @__PURE__ */ jsx(IconChevronRight, {}) : a.kbd ? /* @__PURE__ */ jsx("kbd", { class: "bas-rowmenu__kbd", children: a.kbd.toUpperCase() }) : null
691
- ]
692
- },
693
- a.id
694
- )) : /* @__PURE__ */ jsxs(
701
+ a.id
702
+ )
703
+ ]) : /* @__PURE__ */ jsxs(
695
704
  "form",
696
705
  {
697
706
  class: "bas-rowmenu__form",