@jobber/components 7.10.0 → 7.10.1-TAYLORmen-7785412.2

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.
@@ -21,10 +21,12 @@ require('../floating-ui.react-dom-cjs.js');
21
21
  require('../useFormFieldFocus-cjs.js');
22
22
  require('../maxHeight-cjs.js');
23
23
  require('../useRenderElement-cjs.js');
24
- require('../BottomSheet-cjs.js');
25
- require('../DrawerRoot-cjs.js');
26
24
  require('../OverlaySeparator-cjs.js');
27
25
  require('../Separator-cjs.js');
26
+ require('../BottomSheet-cjs.js');
27
+ require('../DrawerRoot-cjs.js');
28
+ require('../HelperText-cjs.js');
29
+ require('../Text-cjs.js');
28
30
  require('../MenuSubmenuTrigger-cjs.js');
29
31
 
30
32
 
@@ -19,8 +19,10 @@ import '../floating-ui.react-dom-es.js';
19
19
  import '../useFormFieldFocus-es.js';
20
20
  import '../maxHeight-es.js';
21
21
  import '../useRenderElement-es.js';
22
- import '../BottomSheet-es.js';
23
- import '../DrawerRoot-es.js';
24
22
  import '../OverlaySeparator-es.js';
25
23
  import '../Separator-es.js';
24
+ import '../BottomSheet-es.js';
25
+ import '../DrawerRoot-es.js';
26
+ import '../HelperText-es.js';
27
+ import '../Text-es.js';
26
28
  import '../MenuSubmenuTrigger-es.js';
@@ -22,10 +22,12 @@ require('../floating-ui.react-dom-cjs.js');
22
22
  require('../useFormFieldFocus-cjs.js');
23
23
  require('../maxHeight-cjs.js');
24
24
  require('../useRenderElement-cjs.js');
25
- require('../BottomSheet-cjs.js');
26
- require('../DrawerRoot-cjs.js');
27
25
  require('../OverlaySeparator-cjs.js');
28
26
  require('../Separator-cjs.js');
27
+ require('../BottomSheet-cjs.js');
28
+ require('../DrawerRoot-cjs.js');
29
+ require('../HelperText-cjs.js');
30
+ require('../Text-cjs.js');
29
31
  require('../MenuSubmenuTrigger-cjs.js');
30
32
  require('../Content-cjs.js');
31
33
  require('../Emphasis-cjs.js');
@@ -20,10 +20,12 @@ import '../floating-ui.react-dom-es.js';
20
20
  import '../useFormFieldFocus-es.js';
21
21
  import '../maxHeight-es.js';
22
22
  import '../useRenderElement-es.js';
23
- import '../BottomSheet-es.js';
24
- import '../DrawerRoot-es.js';
25
23
  import '../OverlaySeparator-es.js';
26
24
  import '../Separator-es.js';
25
+ import '../BottomSheet-es.js';
26
+ import '../DrawerRoot-es.js';
27
+ import '../HelperText-es.js';
28
+ import '../Text-es.js';
27
29
  import '../MenuSubmenuTrigger-es.js';
28
30
  import '../Content-es.js';
29
31
  import '../Emphasis-es.js';
@@ -19,10 +19,12 @@ require('../Typography-cjs.js');
19
19
  require('../useFormFieldFocus-cjs.js');
20
20
  require('../maxHeight-cjs.js');
21
21
  require('../useRenderElement-cjs.js');
22
- require('../BottomSheet-cjs.js');
23
- require('../DrawerRoot-cjs.js');
24
22
  require('../OverlaySeparator-cjs.js');
25
23
  require('../Separator-cjs.js');
24
+ require('../BottomSheet-cjs.js');
25
+ require('../DrawerRoot-cjs.js');
26
+ require('../HelperText-cjs.js');
27
+ require('../Text-cjs.js');
26
28
  require('../MenuSubmenuTrigger-cjs.js');
27
29
 
28
30
 
@@ -17,8 +17,10 @@ import '../Typography-es.js';
17
17
  import '../useFormFieldFocus-es.js';
18
18
  import '../maxHeight-es.js';
19
19
  import '../useRenderElement-es.js';
20
- import '../BottomSheet-es.js';
21
- import '../DrawerRoot-es.js';
22
20
  import '../OverlaySeparator-es.js';
23
21
  import '../Separator-es.js';
22
+ import '../BottomSheet-es.js';
23
+ import '../DrawerRoot-es.js';
24
+ import '../HelperText-es.js';
25
+ import '../Text-es.js';
24
26
  import '../MenuSubmenuTrigger-es.js';
package/dist/Menu-cjs.js CHANGED
@@ -13,8 +13,9 @@ var maxHeight = require('./maxHeight-cjs.js');
13
13
  var floatingUi_reactDom = require('./floating-ui.react-dom-cjs.js');
14
14
  var tslib_es6 = require('./tslib.es6-cjs.js');
15
15
  var useRenderElement = require('./useRenderElement-cjs.js');
16
- var BottomSheet = require('./BottomSheet-cjs.js');
17
16
  var OverlaySeparator = require('./OverlaySeparator-cjs.js');
17
+ var BottomSheet = require('./BottomSheet-cjs.js');
18
+ require('./HelperText-cjs.js');
18
19
  var MenuSubmenuTrigger$1 = require('./MenuSubmenuTrigger-cjs.js');
19
20
 
20
21
  var styles$2 = {"wrapper":"h-nk89U2n6Q-","floatingContainer":"krpkWzoaVcc-","menu":"_7sU7mp1w0jM-","legacySection":"_4x-vvEmn4gM-","separator":"i0QNcs4S-wk-","triggerWrapper":"_7CsejipGrzM-","ariaMenu":"bO7lGVQC42c-","ariaItem":"u6fDgbWmPck-","ariaSection":"-TSNrZBKZJI-","ariaSectionHeader":"zs7dS2pKjEw-","sectionHeader":"MZJgunQcmdQ-","legacyAction":"_-1GOlrzd098-","action":"NYXV5w4pd0c-","destructive":"baYK2zQ6-GA-","overlay":"A7T14ALVEKg-","fullWidth":"Lgbn9IRPI5Y-","screenReaderOnly":"o-ps580KdWI-","spinning":"f1oTdSTeUOM-"};
@@ -367,6 +368,17 @@ function getTextContent(node) {
367
368
  }
368
369
  return getTextContent(node.props.children);
369
370
  }
371
+ function getBottomSheetContentStyle(style) {
372
+ if (!style) {
373
+ return undefined;
374
+ }
375
+ const safeStyle = Object.assign({}, style);
376
+ delete safeStyle.height;
377
+ delete safeStyle.maxHeight;
378
+ delete safeStyle.minHeight;
379
+ delete safeStyle.width;
380
+ return safeStyle;
381
+ }
370
382
  // Bottom sheet menu interaction helpers. These adapt Drawer semantics into the
371
383
  // menu-style keyboard and focus behavior we want for the mobile shell.
372
384
  function getNavigableMenuItems(target) {
@@ -514,11 +526,12 @@ function BottomSheetContent({ children, style, className, UNSAFE_style, UNSAFE_c
514
526
  UNSAFE_className,
515
527
  UNSAFE_style,
516
528
  });
529
+ const bottomSheetStyle = getBottomSheetContentStyle(resolvedBaseProps.style);
517
530
  return (React.createElement(BottomSheet.BottomSheet.Popup, { initialFocus: getInitialBottomSheetFocus(menuRef, focusModalityRef), className: classnames({
518
531
  [styles.menuHidden]: submenuOpen,
519
532
  }) },
520
533
  React.createElement(BottomSheet.BottomSheet.Content, null,
521
- React.createElement(BottomSheetMenuSurface, { menuRef: menuRef, className: classnames(styles.bottomSheetMenuContent, resolvedBaseProps.className), style: resolvedBaseProps.style }, children))));
534
+ React.createElement(BottomSheetMenuSurface, { menuRef: menuRef, className: classnames(styles.bottomSheetMenuContent, resolvedBaseProps.className), style: bottomSheetStyle }, children))));
522
535
  }
523
536
  function BottomSheetSeparator({ style, className, UNSAFE_style, UNSAFE_className, }) {
524
537
  const resolvedBaseProps = resolveComposableBaseProps({
@@ -666,8 +679,9 @@ function BottomSheetSubmenuContent({ children, className, style, UNSAFE_style, U
666
679
  UNSAFE_className,
667
680
  UNSAFE_style,
668
681
  });
682
+ const bottomSheetStyle = getBottomSheetContentStyle(resolvedBaseProps.style);
669
683
  return (React.createElement(BottomSheet.BottomSheet.Popup, { initialFocus: getInitialBottomSheetFocus(menuRef, focusModalityRef), className: classnames(styles.menu, styles.bottomSheetSubmenuPopup) },
670
- React.createElement(BottomSheet.BottomSheet.Content, { style: resolvedBaseProps.style },
684
+ React.createElement(BottomSheet.BottomSheet.Content, { style: bottomSheetStyle },
671
685
  title ? (React.createElement(MenuSubmenuTrigger$1.DrawerTitle, { className: styles.screenReaderOnly }, title)) : null,
672
686
  React.createElement("div", { className: styles$1.submenuHeader },
673
687
  React.createElement(MenuSubmenuTrigger$1.DrawerClose, { "aria-label": title ? `Back to ${title}` : "Back", render: React.createElement(Button.Button, { ariaLabel: title ? `Back to ${title}` : "Back", icon: "arrowLeft", type: "tertiary", variation: "subtle" }) }),
@@ -721,6 +735,9 @@ function getFloatingLayerStyle(style) {
721
735
  return undefined;
722
736
  return { zIndex: style.zIndex };
723
737
  }
738
+ function getMenuPopupStyle(style) {
739
+ return Object.assign({ maxHeight: `min(${MENU_MAX_HEIGHT_PERCENTAGE}vh, max(0px, calc(var(--available-height) - var(--space-base))))` }, style);
740
+ }
724
741
  function MenuDropdownRoot({ children, onOpenChange, open, defaultOpen, }) {
725
742
  const actionsRef = React.useRef(null);
726
743
  return (React.createElement(MenuDropdownContext.Provider, { value: {
@@ -754,9 +771,10 @@ function MenuDropdownContent({ children, style, className, preferredPlacement, U
754
771
  });
755
772
  const [side = "bottom", align = "start"] = (_a = preferredPlacement === null || preferredPlacement === void 0 ? void 0 : preferredPlacement.split(" ")) !== null && _a !== void 0 ? _a : [];
756
773
  const floatingLayerStyle = getFloatingLayerStyle(resolvedBaseProps.style);
774
+ const popupStyle = getMenuPopupStyle(resolvedBaseProps.style);
757
775
  return (React.createElement(MenuSubmenuTrigger$1.MenuPortal, null,
758
776
  React.createElement(MenuSubmenuTrigger$1.MenuPositioner, { className: styles.floatingContainer, style: floatingLayerStyle, align: align, side: side, sideOffset: MENU_OFFSET },
759
- React.createElement(MenuSubmenuTrigger$1.MenuPopup, { className: classnames(styles.menu, styles.ariaMenu, resolvedBaseProps.className), style: resolvedBaseProps.style }, children))));
777
+ React.createElement(MenuSubmenuTrigger$1.MenuPopup, { className: classnames(styles.menu, styles.ariaMenu, resolvedBaseProps.className), style: popupStyle }, children))));
760
778
  }
761
779
  function MenuDropdownRadioGroup({ children, value, defaultValue, onValueChange, className, style, UNSAFE_style, UNSAFE_className, }) {
762
780
  const resolvedBaseProps = resolveComposableBaseProps({
@@ -847,9 +865,10 @@ function MenuDropdownSubmenuContent({ children, className, style, UNSAFE_style,
847
865
  UNSAFE_style,
848
866
  });
849
867
  const floatingLayerStyle = getFloatingLayerStyle(resolvedBaseProps.style);
868
+ const popupStyle = getMenuPopupStyle(resolvedBaseProps.style);
850
869
  return (React.createElement(MenuSubmenuTrigger$1.MenuPortal, null,
851
870
  React.createElement(MenuSubmenuTrigger$1.MenuPositioner, { className: styles.floatingContainer, style: floatingLayerStyle, align: "start", side: "right", sideOffset: MENU_OFFSET },
852
- React.createElement(MenuSubmenuTrigger$1.MenuPopup, { className: classnames(styles.menu, styles.ariaMenu, resolvedBaseProps.className), style: resolvedBaseProps.style }, children))));
871
+ React.createElement(MenuSubmenuTrigger$1.MenuPopup, { className: classnames(styles.menu, styles.ariaMenu, resolvedBaseProps.className), style: popupStyle }, children))));
853
872
  }
854
873
  function MenuDropdownGroupLabel(props) {
855
874
  const { className, style } = resolveComposableBaseProps(props);
package/dist/Menu-es.js CHANGED
@@ -11,8 +11,9 @@ import { c as calculateMaxHeight } from './maxHeight-es.js';
11
11
  import { o as offset, f as flip, d as size, b as autoUpdate } from './floating-ui.react-dom-es.js';
12
12
  import { _ as __rest } from './tslib.es6-es.js';
13
13
  import { m as mergeProps } from './useRenderElement-es.js';
14
- import { B as BottomSheet } from './BottomSheet-es.js';
15
14
  import { O as OverlaySeparator } from './OverlaySeparator-es.js';
15
+ import { B as BottomSheet } from './BottomSheet-es.js';
16
+ import './HelperText-es.js';
16
17
  import { D as DrawerTrigger, a as DrawerTitle, b as DrawerClose, M as MenuTrigger$1, c as MenuLinkItem, d as MenuItem$1, e as MenuRadioItem$1, f as MenuRadioItemIndicator, g as MenuSubmenuTrigger$1, h as MenuRoot$1, i as MenuPortal$1, j as MenuPositioner, k as MenuPopup, l as MenuRadioGroup$1, m as MenuGroup$1, n as MenuSubmenuRoot, o as MenuGroupLabel$1 } from './MenuSubmenuTrigger-es.js';
17
18
 
18
19
  var styles$2 = {"wrapper":"h-nk89U2n6Q-","floatingContainer":"krpkWzoaVcc-","menu":"_7sU7mp1w0jM-","legacySection":"_4x-vvEmn4gM-","separator":"i0QNcs4S-wk-","triggerWrapper":"_7CsejipGrzM-","ariaMenu":"bO7lGVQC42c-","ariaItem":"u6fDgbWmPck-","ariaSection":"-TSNrZBKZJI-","ariaSectionHeader":"zs7dS2pKjEw-","sectionHeader":"MZJgunQcmdQ-","legacyAction":"_-1GOlrzd098-","action":"NYXV5w4pd0c-","destructive":"baYK2zQ6-GA-","overlay":"A7T14ALVEKg-","fullWidth":"Lgbn9IRPI5Y-","screenReaderOnly":"o-ps580KdWI-","spinning":"f1oTdSTeUOM-"};
@@ -365,6 +366,17 @@ function getTextContent(node) {
365
366
  }
366
367
  return getTextContent(node.props.children);
367
368
  }
369
+ function getBottomSheetContentStyle(style) {
370
+ if (!style) {
371
+ return undefined;
372
+ }
373
+ const safeStyle = Object.assign({}, style);
374
+ delete safeStyle.height;
375
+ delete safeStyle.maxHeight;
376
+ delete safeStyle.minHeight;
377
+ delete safeStyle.width;
378
+ return safeStyle;
379
+ }
368
380
  // Bottom sheet menu interaction helpers. These adapt Drawer semantics into the
369
381
  // menu-style keyboard and focus behavior we want for the mobile shell.
370
382
  function getNavigableMenuItems(target) {
@@ -512,11 +524,12 @@ function BottomSheetContent({ children, style, className, UNSAFE_style, UNSAFE_c
512
524
  UNSAFE_className,
513
525
  UNSAFE_style,
514
526
  });
527
+ const bottomSheetStyle = getBottomSheetContentStyle(resolvedBaseProps.style);
515
528
  return (React__default.createElement(BottomSheet.Popup, { initialFocus: getInitialBottomSheetFocus(menuRef, focusModalityRef), className: classnames({
516
529
  [styles.menuHidden]: submenuOpen,
517
530
  }) },
518
531
  React__default.createElement(BottomSheet.Content, null,
519
- React__default.createElement(BottomSheetMenuSurface, { menuRef: menuRef, className: classnames(styles.bottomSheetMenuContent, resolvedBaseProps.className), style: resolvedBaseProps.style }, children))));
532
+ React__default.createElement(BottomSheetMenuSurface, { menuRef: menuRef, className: classnames(styles.bottomSheetMenuContent, resolvedBaseProps.className), style: bottomSheetStyle }, children))));
520
533
  }
521
534
  function BottomSheetSeparator({ style, className, UNSAFE_style, UNSAFE_className, }) {
522
535
  const resolvedBaseProps = resolveComposableBaseProps({
@@ -664,8 +677,9 @@ function BottomSheetSubmenuContent({ children, className, style, UNSAFE_style, U
664
677
  UNSAFE_className,
665
678
  UNSAFE_style,
666
679
  });
680
+ const bottomSheetStyle = getBottomSheetContentStyle(resolvedBaseProps.style);
667
681
  return (React__default.createElement(BottomSheet.Popup, { initialFocus: getInitialBottomSheetFocus(menuRef, focusModalityRef), className: classnames(styles.menu, styles.bottomSheetSubmenuPopup) },
668
- React__default.createElement(BottomSheet.Content, { style: resolvedBaseProps.style },
682
+ React__default.createElement(BottomSheet.Content, { style: bottomSheetStyle },
669
683
  title ? (React__default.createElement(DrawerTitle, { className: styles.screenReaderOnly }, title)) : null,
670
684
  React__default.createElement("div", { className: styles$1.submenuHeader },
671
685
  React__default.createElement(DrawerClose, { "aria-label": title ? `Back to ${title}` : "Back", render: React__default.createElement(Button, { ariaLabel: title ? `Back to ${title}` : "Back", icon: "arrowLeft", type: "tertiary", variation: "subtle" }) }),
@@ -719,6 +733,9 @@ function getFloatingLayerStyle(style) {
719
733
  return undefined;
720
734
  return { zIndex: style.zIndex };
721
735
  }
736
+ function getMenuPopupStyle(style) {
737
+ return Object.assign({ maxHeight: `min(${MENU_MAX_HEIGHT_PERCENTAGE}vh, max(0px, calc(var(--available-height) - var(--space-base))))` }, style);
738
+ }
722
739
  function MenuDropdownRoot({ children, onOpenChange, open, defaultOpen, }) {
723
740
  const actionsRef = React__default.useRef(null);
724
741
  return (React__default.createElement(MenuDropdownContext.Provider, { value: {
@@ -752,9 +769,10 @@ function MenuDropdownContent({ children, style, className, preferredPlacement, U
752
769
  });
753
770
  const [side = "bottom", align = "start"] = (_a = preferredPlacement === null || preferredPlacement === void 0 ? void 0 : preferredPlacement.split(" ")) !== null && _a !== void 0 ? _a : [];
754
771
  const floatingLayerStyle = getFloatingLayerStyle(resolvedBaseProps.style);
772
+ const popupStyle = getMenuPopupStyle(resolvedBaseProps.style);
755
773
  return (React__default.createElement(MenuPortal$1, null,
756
774
  React__default.createElement(MenuPositioner, { className: styles.floatingContainer, style: floatingLayerStyle, align: align, side: side, sideOffset: MENU_OFFSET },
757
- React__default.createElement(MenuPopup, { className: classnames(styles.menu, styles.ariaMenu, resolvedBaseProps.className), style: resolvedBaseProps.style }, children))));
775
+ React__default.createElement(MenuPopup, { className: classnames(styles.menu, styles.ariaMenu, resolvedBaseProps.className), style: popupStyle }, children))));
758
776
  }
759
777
  function MenuDropdownRadioGroup({ children, value, defaultValue, onValueChange, className, style, UNSAFE_style, UNSAFE_className, }) {
760
778
  const resolvedBaseProps = resolveComposableBaseProps({
@@ -845,9 +863,10 @@ function MenuDropdownSubmenuContent({ children, className, style, UNSAFE_style,
845
863
  UNSAFE_style,
846
864
  });
847
865
  const floatingLayerStyle = getFloatingLayerStyle(resolvedBaseProps.style);
866
+ const popupStyle = getMenuPopupStyle(resolvedBaseProps.style);
848
867
  return (React__default.createElement(MenuPortal$1, null,
849
868
  React__default.createElement(MenuPositioner, { className: styles.floatingContainer, style: floatingLayerStyle, align: "start", side: "right", sideOffset: MENU_OFFSET },
850
- React__default.createElement(MenuPopup, { className: classnames(styles.menu, styles.ariaMenu, resolvedBaseProps.className), style: resolvedBaseProps.style }, children))));
869
+ React__default.createElement(MenuPopup, { className: classnames(styles.menu, styles.ariaMenu, resolvedBaseProps.className), style: popupStyle }, children))));
851
870
  }
852
871
  function MenuDropdownGroupLabel(props) {
853
872
  const { className, style } = resolveComposableBaseProps(props);
@@ -26,10 +26,11 @@ require('../floating-ui.react-dom-cjs.js');
26
26
  require('../useFormFieldFocus-cjs.js');
27
27
  require('../maxHeight-cjs.js');
28
28
  require('../useRenderElement-cjs.js');
29
- require('../BottomSheet-cjs.js');
30
- require('../DrawerRoot-cjs.js');
31
29
  require('../OverlaySeparator-cjs.js');
32
30
  require('../Separator-cjs.js');
31
+ require('../BottomSheet-cjs.js');
32
+ require('../DrawerRoot-cjs.js');
33
+ require('../HelperText-cjs.js');
33
34
  require('../MenuSubmenuTrigger-cjs.js');
34
35
  require('../filterDataAttributes-cjs.js');
35
36
 
@@ -24,9 +24,10 @@ import '../floating-ui.react-dom-es.js';
24
24
  import '../useFormFieldFocus-es.js';
25
25
  import '../maxHeight-es.js';
26
26
  import '../useRenderElement-es.js';
27
- import '../BottomSheet-es.js';
28
- import '../DrawerRoot-es.js';
29
27
  import '../OverlaySeparator-es.js';
30
28
  import '../Separator-es.js';
29
+ import '../BottomSheet-es.js';
30
+ import '../DrawerRoot-es.js';
31
+ import '../HelperText-es.js';
31
32
  import '../MenuSubmenuTrigger-es.js';
32
33
  import '../filterDataAttributes-es.js';
@@ -1,55 +1,215 @@
1
1
  # Menu
2
2
 
3
- A Menu is made up of 2 primary parts: the trigger, typically a Button, and the
4
- Menu container.
3
+ ## Summary
5
4
 
6
- The Menu container is not visible by default, interacting with the trigger will
7
- open the Menu.
5
+ The menu displays a list of options for a user to choose from. It is activated
6
+ by an interactive element such as a button or icon button.
8
7
 
9
- The Menu is meant to offer a list of actions for a user to choose from, though
10
- only one may be used at a time. The Menu will close after an action is invoked,
11
- the ESC key is pressed, or a click outside the Menu is registered.
8
+ ## Anatomy
12
9
 
13
- Menu does not have a selected state. The actions are ephemeral with no state
14
- persisting between opens making them well suited for actions such as navigating
15
- to a new page, or launching a Modal.
10
+ | Part | Description |
11
+ | ---------- | ----------------------------------------------------------- |
12
+ | Trigger | An interactive element that opens the menu |
13
+ | Container | The floating panel or sheet that contains the list of items |
14
+ | Label | Primary text of an item |
15
+ | Prefix | Optional leading slot |
16
+ | Suffix | Optional trailing slot |
17
+ | GroupLabel | Optional subdued heading used to group related items |
18
+ | Separator | Optional horizontal divider used to separate group of items |
16
19
 
17
- Menu is navigable by keyboard with arrow keys. Because of this only one action
18
- may occur per row. Nesting additional interactive elements within a single Menu
19
- action prevents keyboard access. When the Trigger is activated by keyboard, we
20
- move focus to the first item in the Menu upon opening.
20
+ ## Behavior
21
21
 
22
- The other elements of a Menu are optional Sections, Separators and Headers.
23
- These elements are non interactive, nor focusable.
22
+ #### Opening and closing the menu
24
23
 
25
- > Keep Menu item content simple. Aim for at most three columns per row: a
26
- > leading icon, a label, and one simple trailing element (e.g., a
27
- > `StatusIndicator`). More complex layouts increase cognitive load and can
28
- > introduce complexity with the Menu's built in alignment features.
24
+ The menu opens when:
29
25
 
30
- ### Small screens
26
+ * The trigger is clicked or tapped
27
+ * The trigger is triggered by keyboard E.g. Via Enter or Space keys
31
28
 
32
- When a user is on a small screen device (less than \~500px), the Menu when opened
33
- will display as a tray or bottom-sheet like interface, opening upward from the
34
- bottom of the screen.
29
+ The menu closes when:
35
30
 
36
- It will have all the same elements as the standard version, and an additional
37
- Overlay covering the non-Menu area.
31
+ * A action menu item is selected
32
+ * A click or tap occurs outside the menu
33
+ * The ESC key is pressed
38
34
 
39
- ## Content guidelines
35
+ Radio menu items remain open by default, but can be configured to close on
36
+ selection.
40
37
 
41
- Menu action labels should be sentence-cased. This means in general, capitalize
42
- only the first letter of the label unless there is a proper noun (such as a
43
- person's name) in the label.
38
+ #### Width
44
39
 
45
- Jobber features, such as jobs, quotes, and invoices, are not proper nouns and
46
- [should not be capitalized.](/content/product-vocabulary)
40
+ Menu width should by default match the trigger's width **or** hug the width of
41
+ the menu items so that the label doesn't wrap. The menu's width can by adjusted
42
+ manually however.
47
43
 
48
- | ✅ Do | ❌ Don't |
49
- | -------------------------------- | -------------------------------- |
50
- | Send as text message | Send As Text Message |
51
- | Collect signature | COLLECT SIGNATURE |
52
- | Assign Jasmine Williams to visit | Assign jasmine williams to visit |
44
+ #### Positioning
45
+
46
+ Menu is positioned below the element that triggered it by default. It should
47
+ automatically reposition to appear on the left, right, above, or bottom of the
48
+ element that triggers it if the menu is cut off.
49
+
50
+ #### Submenus
51
+
52
+ Submenu opens next to the main menu item. On small screens, tapping a submenu
53
+ opens up a second sheet that displays the nested menu items.
54
+
55
+ #### Small screens
56
+
57
+ When a user is on a small screen such as a mobile device, the menu opens up as a
58
+ sheet anchored to the bottom of the screen.
59
+
60
+ ## Variants
61
+
62
+ #### Menu Items
63
+
64
+ **1. Action:** Standard one-time actions such as navigating or triggering a
65
+ modal
66
+
67
+ **2. Radio:** Persistent choices like a filter of a view preference. Use when
68
+ choice persists and the user needs to see which option is active.
69
+
70
+ **3. Submenu:** Opens a nested menu of options on click and on hover. Use when
71
+ related actions are better surfaced one level deeper.
72
+
73
+ #### Menu Items: States
74
+
75
+ * Default
76
+ * Highlighted
77
+ * Selected (radio items only)
78
+
79
+ #### Group Label and Separator
80
+
81
+ Group labels and separators can be used to separate and group menu items into
82
+ scannable sections.
83
+
84
+ #### Custom Content
85
+
86
+ * Menus have custom slots that support more flexible item layouts
87
+ * Custom menu items can appear anywhere in a menu
88
+ * The padding of the menu container should not be altered
89
+ * Menu items must represent a single action. Avoid nesting interactive elements
90
+ inside a menu item
91
+
92
+ ## Content Guidelines
93
+
94
+ #### Sentence case
95
+
96
+ Menu action labels should be sentence-cased. Capitalize only the first letter of
97
+ the label unless there is a proper noun (such as a person's name). Jobber
98
+ features like jobs, quotes, and invoices are not proper nouns and should not be
99
+ capitalized.
100
+
101
+ | ✅ Do | ❌ Don't |
102
+ | ----------------------- | ----------------------- |
103
+ | Send text message | Send Text Message |
104
+ | Collect signature | COLLECT SIGNATURE |
105
+ | Assign Jasmine Williams | Assign jasmine williams |
106
+
107
+ #### Verb-first labels
108
+
109
+ Menu items represent actions. Lead with the verb so the SP can scan quickly.
110
+
111
+ | ✅ Do | ❌ Don't |
112
+ | -------------------- | -------------------- |
113
+ | Edit client | Client editing |
114
+ | Send as text message | Text message options |
115
+ | Archive quote | Quote archiving |
116
+
117
+ #### Keep labels concise
118
+
119
+ Aim for 2-4 words. Menu items are scanned, not read carefully.
120
+
121
+ | ✅ Do | ❌ Don't |
122
+ | ----------------- | ----------------------------------- |
123
+ | Delete job | Delete this job permanently |
124
+ | Collect signature | Collect a signature from the client |
125
+ | Mark as complete | Mark this visit as complete |
126
+
127
+ #### Don't repeat the trigger context
128
+
129
+ If the menu is triggered from a button on a job card, the items don't need to
130
+ say "job" in every label.
131
+
132
+ | ✅ Do | ❌ Don't |
133
+ | ------- | ----------- |
134
+ | Edit | Edit job |
135
+ | Archive | Archive job |
136
+ | Delete | Delete job |
137
+
138
+ #### Use consistent verb tense
139
+
140
+ All items in a single menu should use the same grammatical form.
141
+
142
+ | ✅ Do | ❌ Don't |
143
+ | --------------------------------------- | ---------------------------------------- |
144
+ | Edit client / Send invoice / Delete job | Edit client / Sending invoice / Archived |
145
+ | Mark as complete / Assign to team | Completed / Assign to team |
146
+
147
+ #### Group labels should describe, not instruct
148
+
149
+ Keep group labels to 1-2 words that name the category.
150
+
151
+ | ✅ Do | ❌ Don't |
152
+ | ------------- | ------------------------------- |
153
+ | Communication | Choose how to communicate |
154
+ | Actions | Things you can do |
155
+ | Scheduling | Schedule options for this visit |
156
+
157
+ #### Destructive items should be explicit
158
+
159
+ Name what's being removed so the SP isn't guessing, especially when the menu
160
+ acts on multiple object types.
161
+
162
+ | ✅ Do | ❌ Don't |
163
+ | ---------------- | ------- |
164
+ | Delete job | Delete |
165
+ | Remove line item | Remove |
166
+ | Cancel visit | Cancel |
167
+
168
+ ## Do's and Don'ts
169
+
170
+ #### Do
171
+
172
+ * ✅ Sentence case for menu item labels and group labels
173
+ * ✅ Labels should be short and action oriented
174
+ * ✅ Keep menu items simple
175
+ * ✅ Use icons only when they add clarity
176
+ * ✅ Cluster similar menu items together
177
+
178
+ #### Don't
179
+
180
+ * ❌ Avoid adding too many items in a menu
181
+ * ❌ Don't mix unrelated actions in the same menu, or use grouping when needed
182
+
183
+ ## Accessibility
184
+
185
+ Users need to be able to navigate to, open, select menu items, and close a menu
186
+ with assistive technology.
187
+
188
+ #### Keyboard navigation
189
+
190
+ | Key | Behavior |
191
+ | --------------------- | ------------------------------------------------------------------ |
192
+ | Tab | Moves focus to the trigger |
193
+ | Up and Down arrows | Closed menus: Opens menu. Opened menus: Moves up and down the list |
194
+ | Left and Right arrows | Opens and closes a submenu |
195
+ | Enter or Space | Closed menus: Opens menu. Opened menus: Selects menu item |
196
+ | Esc | Closes the menu |
197
+
198
+ #### Touch target
199
+
200
+ Minimum menu item height:
201
+
202
+ * **Large screens:** 40px
203
+ * **Small screens:** 48px
204
+
205
+ ## Related components
206
+
207
+ * To trigger a single action rather than presenting a list of options, use a
208
+ [Button](../Button/Button.md) or [IconButton](/components/IconButton)
209
+ * To allow the user to choose a value from a predefined list within a form, use
210
+ a [Select](../Select/Select.md)
211
+ * To allow the user to search and filter through a list of options, use a
212
+ [Combobox](../Combobox/Combobox.md)
53
213
 
54
214
 
55
215
  ## Composable Version (Web Only)
package/dist/index.cjs CHANGED
@@ -183,10 +183,11 @@ require('filesize');
183
183
  require('./GridCell-cjs.js');
184
184
  require('axios');
185
185
  require('./useRenderElement-cjs.js');
186
- require('./BottomSheet-cjs.js');
187
- require('./DrawerRoot-cjs.js');
188
186
  require('./OverlaySeparator-cjs.js');
189
187
  require('./Separator-cjs.js');
188
+ require('./BottomSheet-cjs.js');
189
+ require('./DrawerRoot-cjs.js');
190
+ require('./HelperText-cjs.js');
190
191
  require('./MenuSubmenuTrigger-cjs.js');
191
192
  require('./AtlantisPortalContent-cjs.js');
192
193
  require('@jobber/formatters');
package/dist/index.mjs CHANGED
@@ -181,10 +181,11 @@ import 'filesize';
181
181
  import './GridCell-es.js';
182
182
  import 'axios';
183
183
  import './useRenderElement-es.js';
184
- import './BottomSheet-es.js';
185
- import './DrawerRoot-es.js';
186
184
  import './OverlaySeparator-es.js';
187
185
  import './Separator-es.js';
186
+ import './BottomSheet-es.js';
187
+ import './DrawerRoot-es.js';
188
+ import './HelperText-es.js';
188
189
  import './MenuSubmenuTrigger-es.js';
189
190
  import './AtlantisPortalContent-es.js';
190
191
  import '@jobber/formatters';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components",
3
- "version": "7.10.0",
3
+ "version": "7.10.1-TAYLORmen-7785412.2+778541237",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -583,5 +583,5 @@
583
583
  "> 1%",
584
584
  "IE 10"
585
585
  ],
586
- "gitHead": "348d1293520051687a9b2d178cf4c5757136e9bf"
586
+ "gitHead": "778541237e17e103a98fb5141ddba23602a143b6"
587
587
  }