@homebound/beam 2.220.0 → 2.221.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.
@@ -3,6 +3,7 @@ import { OverlayTriggerProps } from "./internal/OverlayTrigger";
3
3
  interface ButtonMenuProps extends Pick<OverlayTriggerProps, "trigger" | "placement" | "disabled" | "tooltip" | "showActiveBorder"> {
4
4
  items: MenuItem[];
5
5
  persistentItems?: MenuItem[];
6
+ searchable?: boolean;
6
7
  defaultOpen?: boolean;
7
8
  }
8
9
  export declare function ButtonMenu(props: ButtonMenuProps): import("@emotion/react/jsx-runtime").JSX.Element;
@@ -9,11 +9,11 @@ const Menu_1 = require("./internal/Menu");
9
9
  const OverlayTrigger_1 = require("./internal/OverlayTrigger");
10
10
  const utils_1 = require("../utils");
11
11
  function ButtonMenu(props) {
12
- const { defaultOpen, disabled, items, persistentItems, trigger } = props;
12
+ const { defaultOpen, disabled, items, persistentItems, trigger, searchable } = props;
13
13
  const state = (0, react_stately_1.useMenuTriggerState)({ isOpen: defaultOpen });
14
14
  const buttonRef = (0, react_1.useRef)(null);
15
15
  const { menuTriggerProps, menuProps } = (0, react_aria_1.useMenuTrigger)({ isDisabled: !!disabled }, state, buttonRef);
16
16
  const tid = (0, utils_1.useTestIds)(props, (0, OverlayTrigger_1.isTextButton)(trigger) ? trigger.label : (0, OverlayTrigger_1.isIconButton)(trigger) ? trigger.icon : trigger.name);
17
- return ((0, jsx_runtime_1.jsx)(OverlayTrigger_1.OverlayTrigger, Object.assign({}, props, { menuTriggerProps: menuTriggerProps, state: state, buttonRef: buttonRef }, tid, { children: (0, jsx_runtime_1.jsx)(Menu_1.Menu, Object.assign({ ariaMenuProps: menuProps, onClose: () => state.close(), items: items, persistentItems: persistentItems }, tid), void 0) }), void 0));
17
+ return ((0, jsx_runtime_1.jsx)(OverlayTrigger_1.OverlayTrigger, Object.assign({}, props, { menuTriggerProps: menuTriggerProps, state: state, buttonRef: buttonRef }, tid, { children: (0, jsx_runtime_1.jsx)(Menu_1.Menu, Object.assign({ ariaMenuProps: menuProps, onClose: () => state.close(), items: items, persistentItems: persistentItems, searchable: searchable }, tid), void 0) }), void 0));
18
18
  }
19
19
  exports.ButtonMenu = ButtonMenu;
@@ -4,6 +4,7 @@ interface MenuProps<T> {
4
4
  ariaMenuProps: HTMLAttributes<HTMLElement>;
5
5
  onClose: VoidFunction;
6
6
  items: MenuItem[];
7
+ searchable?: boolean;
7
8
  persistentItems?: MenuItem[];
8
9
  }
9
10
  export declare function Menu<T>(props: PropsWithChildren<MenuProps<T>>): import("@emotion/react/jsx-runtime").JSX.Element;
@@ -8,9 +8,10 @@ const react_aria_1 = require("react-aria");
8
8
  const react_stately_1 = require("react-stately");
9
9
  const MenuSection_1 = require("./MenuSection");
10
10
  const Css_1 = require("../../Css");
11
+ const MenuSearchField_1 = require("../../inputs/internal/MenuSearchField");
11
12
  const utils_1 = require("../../utils");
12
13
  function Menu(props) {
13
- const { ariaMenuProps, items, persistentItems, onClose } = props;
14
+ const { ariaMenuProps, items, persistentItems, onClose, searchable } = props;
14
15
  // Build out the Menu's Tree data to include the Persistent Action, if any. This is a collection of Nodes that is used
15
16
  // by React-Aria to keep track of item states such as focus, and provide hooks for calling those actions.
16
17
  const tree = (0, react_stately_1.useTreeData)({
@@ -18,20 +19,49 @@ function Menu(props) {
18
19
  getKey: (item) => (0, change_case_1.camelCase)(item.label),
19
20
  getChildren: (item) => { var _a; return (_a = item.items) !== null && _a !== void 0 ? _a : []; },
20
21
  });
22
+ const [search, setSearch] = (0, react_1.useState)(undefined);
23
+ let { contains } = (0, react_aria_1.useFilter)({
24
+ sensitivity: "base",
25
+ });
26
+ // Filter our tree data items based on the search term
27
+ const filteredTree = (0, react_1.useMemo)(() => {
28
+ const { items, ...others } = tree;
29
+ const [itemsSection, persistentSection] = items;
30
+ if (search) {
31
+ const filteredChildren = itemsSection.children.filter((item) => contains(item.value.label, search));
32
+ const { items, ...otherValues } = itemsSection.value;
33
+ const filteredValue = items === null || items === void 0 ? void 0 : items.filter((item) => contains(item.label, search));
34
+ return {
35
+ ...others,
36
+ items: [
37
+ { ...itemsSection, value: { ...otherValues, children: filteredChildren, items: filteredValue } },
38
+ persistentSection,
39
+ ],
40
+ };
41
+ }
42
+ else {
43
+ return tree;
44
+ }
45
+ }, [tree, search, contains]);
21
46
  const menuChildren = (0, react_1.useMemo)(() => {
22
- return tree.items.map(({ value: s }) => ((0, jsx_runtime_1.jsx)(react_stately_1.Section, Object.assign({ title: s.label, items: s.items }, { children: (item) => (0, jsx_runtime_1.jsx)(react_stately_1.Item, { children: item.label }, item.label.replace(/"/g, "")) }), s.label.replace(/"/g, ""))));
23
- }, [tree]);
24
- const state = (0, react_stately_1.useTreeState)({ children: menuChildren, items: tree.items.map((i) => i.value), selectionMode: "none" });
47
+ return filteredTree.items.map(({ value: s }) => ((0, jsx_runtime_1.jsx)(react_stately_1.Section, Object.assign({ title: s.label, items: s.items }, { children: (item) => (0, jsx_runtime_1.jsx)(react_stately_1.Item, { children: item.label }, item.label.replace(/"/g, "")) }), s.label.replace(/"/g, ""))));
48
+ }, [filteredTree]);
49
+ const state = (0, react_stately_1.useTreeState)({
50
+ children: menuChildren,
51
+ items: filteredTree.items.map((i) => i.value),
52
+ selectionMode: "none",
53
+ });
25
54
  const menuRef = (0, react_1.useRef)(null);
26
- const { menuProps } = (0, react_aria_1.useMenu)({ ...ariaMenuProps, autoFocus: true }, state, menuRef);
55
+ const { menuProps } = (0, react_aria_1.useMenu)({ ...ariaMenuProps, autoFocus: searchable ? false : true }, state, menuRef);
27
56
  const tid = (0, utils_1.useTestIds)(props);
28
57
  // Bulk updates of MenuItems below. If we find this to be of sluggish performance, then we can change to be more surgical in our updating.
29
58
  // If our list of items change, update the "items" menu section. (key is based on label in `getKey` above)
30
- (0, react_1.useEffect)(() => tree.update("items", { label: "items", items }), [items]);
31
- return ((0, jsx_runtime_1.jsx)(react_aria_1.FocusScope, { children: (0, jsx_runtime_1.jsx)("ul", Object.assign({ css: {
32
- // Using `max-height: inherit` allows us to take advantage of the height set on the overlay container, which updates based on the available space for the overlay within the viewport
33
- ...Css_1.Css.df.fdc.myPx(4).bgWhite.outline0.br4.bshBasic.listReset.maxh("inherit").overflowAuto.$,
34
- "&:hover, &:focus": Css_1.Css.bshHover.$,
35
- } }, menuProps, { ref: menuRef }, tid.menu, { children: [...state.collection].map((item) => ((0, jsx_runtime_1.jsx)(MenuSection_1.MenuSectionImpl, Object.assign({ section: item, state: state, onClose: onClose }, tid), item.key))) }), void 0) }, void 0));
59
+ (0, react_1.useEffect)(() => filteredTree.update("items", { label: "items", items }), [items]);
60
+ return ((0, jsx_runtime_1.jsx)(react_aria_1.FocusScope, { children: (0, jsx_runtime_1.jsxs)("div", Object.assign({
61
+ // Using `max-height: inherit` allows us to take advantage of the height set on the overlay container, which updates based on the available space for the overlay within the viewport
62
+ css: {
63
+ ...Css_1.Css.df.fdc.myPx(4).bgWhite.outline0.br4.bshBasic.maxh("inherit").overflowAuto.$,
64
+ "&:hover": Css_1.Css.bshHover.$,
65
+ } }, { children: [searchable && ((0, jsx_runtime_1.jsx)(MenuSearchField_1.MenuSearchField, Object.assign({ label: "", value: search, placeholder: "Search...", inlineLabel: true, onChange: setSearch }, tid), void 0)), (0, jsx_runtime_1.jsx)("ul", Object.assign({ css: Css_1.Css.listReset.$ }, menuProps, { ref: menuRef }, tid.menu, { children: [...state.collection].map((item) => ((0, jsx_runtime_1.jsx)(MenuSection_1.MenuSectionImpl, Object.assign({ section: item, state: state, onClose: onClose }, tid), item.key))) }), void 0)] }), void 0) }, void 0));
36
66
  }
37
67
  exports.Menu = Menu;
@@ -0,0 +1,7 @@
1
+ import { Only } from "../../Css";
2
+ import { BeamTextFieldProps, TextFieldXss } from "../../interfaces";
3
+ interface TextFieldProps<X> extends BeamTextFieldProps<X> {
4
+ inlineLabel: boolean;
5
+ }
6
+ export declare function MenuSearchField<X extends Only<TextFieldXss, X>>(props: TextFieldProps<X>): import("@emotion/react/jsx-runtime").JSX.Element;
7
+ export {};
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MenuSearchField = void 0;
4
+ const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
5
+ const textfield_1 = require("@react-aria/textfield");
6
+ const react_1 = require("react");
7
+ const components_1 = require("../../components");
8
+ const utils_1 = require("../../utils");
9
+ const TextFieldBase_1 = require("../TextFieldBase");
10
+ function MenuSearchField(props) {
11
+ const tid = (0, utils_1.useTestIds)(props);
12
+ const inputRef = (0, react_1.useRef)(null);
13
+ const { labelProps, inputProps } = (0, textfield_1.useTextField)({ ...props }, inputRef);
14
+ return ((0, jsx_runtime_1.jsx)(TextFieldBase_1.TextFieldBase, Object.assign({ label: "", labelProps: labelProps, inputProps: inputProps, startAdornment: (0, jsx_runtime_1.jsx)(components_1.Icon, { icon: "search" }, void 0) }, tid.search), void 0));
15
+ }
16
+ exports.MenuSearchField = MenuSearchField;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homebound/beam",
3
- "version": "2.220.0",
3
+ "version": "2.221.0",
4
4
  "author": "Homebound",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",