@geotab/zenith 3.17.0-beta.1 → 3.17.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.
Files changed (64) hide show
  1. package/README.md +26 -29
  2. package/dist/button/button.d.ts +1 -2
  3. package/dist/button/button.js +5 -22
  4. package/dist/button/buttonDisabledProps.d.ts +35 -0
  5. package/dist/button/buttonDisabledProps.js +33 -0
  6. package/dist/dataGrid/withSelectableRows/components/bulkActions/bulkActions.d.ts +1 -1
  7. package/dist/dropdownRaw/dropdownHelper.d.ts +20 -3
  8. package/dist/dropdownRaw/dropdownHelper.js +29 -6
  9. package/dist/dropdownRaw/dropdownRaw.js +22 -6
  10. package/dist/header/components/collapsedItemsControl/collapsedItemsControl.js +1 -1
  11. package/dist/header/headerButton.d.ts +15 -2
  12. package/dist/header/headerButton.js +11 -6
  13. package/dist/index.css +4 -8
  14. package/dist/menu/components/menuButton.js +40 -58
  15. package/dist/pageHeader/pageHeaderButton.d.ts +15 -1
  16. package/dist/pageHeader/pageHeaderButton.js +8 -6
  17. package/dist/pillExpandable/pillExpandable.js +3 -1
  18. package/dist/pillExpandable/pillExpandableSimple.d.ts +2 -1
  19. package/dist/pillExpandable/pillExpandableSimple.js +2 -2
  20. package/dist/table/selectable/interfaces.d.ts +6 -0
  21. package/dist/table/selectable/useDragSelection.d.ts +1 -1
  22. package/dist/table/selectable/useDragSelection.js +4 -4
  23. package/dist/table/selectable/useSelectableRows.d.ts +2 -0
  24. package/dist/table/selectable/useSelectableRows.js +88 -41
  25. package/dist/table/selectable/utils/calculateSelectionState.js +3 -2
  26. package/dist/table/selectable/utils/getCurrentPageSelection.js +9 -4
  27. package/dist/table/selectable/utils/groupExclusions.d.ts +7 -0
  28. package/dist/table/selectable/utils/groupExclusions.js +61 -0
  29. package/dist/table/selectable/utils/isRowChecked.js +1 -1
  30. package/dist/table/table.js +6 -5
  31. package/dist/tooltip/tooltip.d.ts +5 -3
  32. package/dist/tooltip/tooltip.js +11 -9
  33. package/esm/button/button.d.ts +1 -2
  34. package/esm/button/button.js +5 -22
  35. package/esm/button/buttonDisabledProps.d.ts +35 -0
  36. package/esm/button/buttonDisabledProps.js +29 -0
  37. package/esm/dataGrid/withSelectableRows/components/bulkActions/bulkActions.d.ts +1 -1
  38. package/esm/dropdownRaw/dropdownHelper.d.ts +20 -3
  39. package/esm/dropdownRaw/dropdownHelper.js +27 -5
  40. package/esm/dropdownRaw/dropdownRaw.js +23 -7
  41. package/esm/header/components/collapsedItemsControl/collapsedItemsControl.js +1 -1
  42. package/esm/header/headerButton.d.ts +15 -2
  43. package/esm/header/headerButton.js +11 -6
  44. package/esm/menu/components/menuButton.js +40 -58
  45. package/esm/pageHeader/pageHeaderButton.d.ts +15 -1
  46. package/esm/pageHeader/pageHeaderButton.js +8 -6
  47. package/esm/pillExpandable/pillExpandable.js +3 -1
  48. package/esm/pillExpandable/pillExpandableSimple.d.ts +2 -1
  49. package/esm/pillExpandable/pillExpandableSimple.js +2 -2
  50. package/esm/table/selectable/interfaces.d.ts +6 -0
  51. package/esm/table/selectable/useDragSelection.d.ts +1 -1
  52. package/esm/table/selectable/useDragSelection.js +4 -4
  53. package/esm/table/selectable/useSelectableRows.d.ts +2 -0
  54. package/esm/table/selectable/useSelectableRows.js +90 -43
  55. package/esm/table/selectable/utils/calculateSelectionState.js +3 -2
  56. package/esm/table/selectable/utils/getCurrentPageSelection.js +9 -4
  57. package/esm/table/selectable/utils/groupExclusions.d.ts +7 -0
  58. package/esm/table/selectable/utils/groupExclusions.js +55 -0
  59. package/esm/table/selectable/utils/isRowChecked.js +1 -1
  60. package/esm/table/table.js +6 -5
  61. package/esm/tooltip/tooltip.d.ts +5 -3
  62. package/esm/tooltip/tooltip.js +11 -9
  63. package/package.json +1 -1
  64. package/README.md.baseline +0 -1
package/README.md CHANGED
@@ -45,39 +45,36 @@ Now components are ready for usage.
45
45
 
46
46
  Zenith library provides components defined in Zenith Design System. It includes very basic blocks like colors, icons, font presets, atom components like buttons, checkboxes etc and high order components - header, table, filters bar etc. List of existing components can be found in [Storybook](https://developers.geotab.com/zenith-storybook/index.html).
47
47
 
48
- ## Translations
49
-
50
- Zenith has English and 32 non-English locale dictionaries, including Arabic (`ar-001`) with right-to-left support. The [translation process guide](./docs/TRANSLATIONS.md) covers adding strings, Tech Content and Localization handoffs, spreadsheet sync, build-time injection, and validation. Read it before adding or changing a user-facing string, reviewing English copy, or running the sync. Locale availability does not imply that every string has a reviewed translation.
51
-
52
48
  ## Change log
53
49
 
54
50
  ### 3.17.0
55
51
 
56
- - [MYG-138984](https://geotab.atlassian.net/browse/MYG-138984): Add Primary, Destructive, and TertiaryDestructive trigger variants to `Dropdown` / `FilterButton`
57
- - [MYG-138985](https://geotab.atlassian.net/browse/MYG-138985): Add optional separators below single- and multi-select `Dropdown` items
58
- - [MYG-138983](https://geotab.atlassian.net/browse/MYG-138983): Add destructive styling to single-select `Dropdown` items
59
- - [MYG-105567](https://geotab.atlassian.net/browse/MYG-105567): Add `sortSelectedToTop` to float selected `Dropdown` options to the top when opened
60
- - [MYG-147075](https://geotab.atlassian.net/browse/MYG-147075): Show full single-select `Dropdown` option labels on hover
61
- - [MYG-147879](https://geotab.atlassian.net/browse/MYG-147879): Add loaded/total counts and an end-of-results message for desktop infinite-scroll tables; improve row-loading announcements
62
- - [MYG-130553](https://geotab.atlassian.net/browse/MYG-130553): Add progressive loading of nested `Table` rows with a +N more control, plus the `useShowMore` hook and `DEFAULT_SHOW_MORE_CAP` export
63
- - [MYG-148443](https://geotab.atlassian.net/browse/MYG-148443): Add seven sequential and seven diverging data-visualization color tokens
64
- - [MYG-129023](https://geotab.atlassian.net/browse/MYG-129023): Support destructive menu items and open-in-new-page indicators, including collapsed `Header` actions
65
- - [MYG-142469](https://geotab.atlassian.net/browse/MYG-142469): Export `PillBox`, `DEFAULT_MAX_PILLS`, `IPillData`, and `IPillBox` from the package entry point
66
- - [MYG-139327](https://geotab.atlassian.net/browse/MYG-139327): Add mouse-drag and Shift-click range selection to `Table` and selectable `DataGrid` rows, including expanded nested rows
67
- - [MYG-142234](https://geotab.atlassian.net/browse/MYG-142234): Add opt-in cross-page Select All exclusions when individual rows or the current-page header are unchecked
68
- - [MYG-149449](https://geotab.atlassian.net/browse/MYG-149449): Preserve cross-page Select All and existing exclusions during drag and Shift-click range selection; correct exclusion counts, childless nested-row selection, and global selection commands; exclude loading and show-more placeholders from fully loaded selection counts
69
- - [MYG-148015](https://geotab.atlassian.net/browse/MYG-148015): Make cross-page exclusions opt-in through `selectable.allowExclusions` while preserving the existing selection contract by default; correct selection feedback during reloads and empty-selection bulk actions
70
- - [MYG-147924](https://geotab.atlassian.net/browse/MYG-147924): Preserve `Table` selection during append loading and refreshes
71
- - [MYG-143552](https://geotab.atlassian.net/browse/MYG-143552): Keep `Waiting` overlays covering scrollable content, improve spinner positioning, and block keyboard interaction with sibling content while the overlay is visible
72
- - [MYG-148751](https://geotab.atlassian.net/browse/MYG-148751): Remove special bold styling from the first `Table` / `DataGrid` column header
73
- - [MYG-113389](https://geotab.atlassian.net/browse/MYG-113389): Reduce form label/control gaps and correct `Dropdown` form-field styling and width
74
- - [MYG-148954](https://geotab.atlassian.net/browse/MYG-148954): Close `PillExpandable` popovers on user scrolling while ignoring browser scroll-anchoring adjustments
75
- - [MYG-149705](https://geotab.atlassian.net/browse/MYG-149705): Fix RTL tab keyboard navigation, focus scrolling, and tab/pill/chip/accordion styling
76
- - [MYG-145500](https://geotab.atlassian.net/browse/MYG-145500): Allow `Tabs` to associate tabs with explicitly named panels through `ariaControls`, including panels outside the default tab layout
77
- - [MYG-148568](https://geotab.atlassian.net/browse/MYG-148568): Forward ARIA attributes to the appropriate input elements in `DropdownRaw` and `TextInputRaw`
78
- - [MYG-150227](https://geotab.atlassian.net/browse/MYG-150227): Make `FormLayout` positioning deterministic for fractional and invalid positions
79
- - [MYG-145929](https://geotab.atlassian.net/browse/MYG-145929): Keep disabled `Button` and related menu/header buttons keyboard-focusable while preventing activation
80
- - [MYG-148696](https://geotab.atlassian.net/browse/MYG-148696): Improve `PillExpandable` popover focus management and accessible trigger/content relationships
52
+ - Add Primary, Destructive, and TertiaryDestructive trigger variants to `Dropdown` / `FilterButton`
53
+ - Add optional separators below single- and multi-select `Dropdown` items
54
+ - Add destructive styling to single-select `Dropdown` items
55
+ - Add `sortSelectedToTop` to float selected `Dropdown` options to the top when opened
56
+ - Show full single-select `Dropdown` option labels on hover
57
+ - Add loaded/total counts and an end-of-results message for desktop infinite-scroll tables; improve row-loading announcements
58
+ - Add progressive loading of nested `Table` rows with a +N more control, plus the `useShowMore` hook and `DEFAULT_SHOW_MORE_CAP` export
59
+ - Add seven sequential and seven diverging data-visualization color tokens
60
+ - Support destructive menu items and open-in-new-page indicators, including collapsed `Header` actions
61
+ - Export `PillBox`, `DEFAULT_MAX_PILLS`, `IPillData`, and `IPillBox` from the package entry point
62
+ - Add mouse-drag and Shift-click range selection to `Table` and selectable `DataGrid` rows, including expanded nested rows
63
+ - Add opt-in cross-page Select All exclusions when individual rows or the current-page header are unchecked
64
+ - Preserve cross-page Select All and existing exclusions during drag and Shift-click range selection; correct exclusion counts, childless nested-row selection, and global selection commands; exclude loading and show-more placeholders from fully loaded selection counts
65
+ - Make cross-page exclusions opt-in through `selectable.allowExclusions` while preserving the existing selection contract by default; correct selection feedback during reloads and empty-selection bulk actions
66
+ - Preserve `Table` selection during append loading and refreshes
67
+ - Keep `Waiting` overlays covering scrollable content, improve spinner positioning, and block keyboard interaction with sibling content while the overlay is visible
68
+ - Remove special bold styling from the first `Table` / `DataGrid` column header
69
+ - Reduce form label/control gaps and correct `Dropdown` form-field styling and width
70
+ - Close `PillExpandable` popovers on user scrolling while ignoring browser scroll-anchoring adjustments
71
+ - Fix RTL tab keyboard navigation, focus scrolling, and tab/pill/chip/accordion styling
72
+ - Allow `Tabs` to associate tabs with explicitly named panels through `ariaControls`, including panels outside the default tab layout
73
+ - Forward ARIA attributes to the appropriate input elements in `DropdownRaw` and `TextInputRaw`
74
+ - Make `FormLayout` positioning deterministic for fractional and invalid positions
75
+ - Keep disabled `Button` and related menu/header buttons keyboard-focusable while preventing activation
76
+ - Improve `PillExpandable` popover focus management and accessible trigger/content relationships
77
+ - Keep `Waiting` content interactive when `hideOverlay` is enabled while loading
81
78
 
82
79
  ### 3.16.0
83
80
 
@@ -9,7 +9,6 @@ export interface IButton extends IZenComponentProps, IZenGridItem {
9
9
  title?: string;
10
10
  type?: TButton;
11
11
  disabled?: boolean;
12
- focusableWhenDisabled?: boolean;
13
12
  onClick?: React.MouseEventHandler;
14
13
  onBlur?: React.FocusEventHandler;
15
14
  onMouseOver?: (e: React.MouseEvent<HTMLElement>) => void;
@@ -28,6 +27,6 @@ export interface IButton extends IZenComponentProps, IZenGridItem {
28
27
  dataAnalyticsId?: string;
29
28
  }
30
29
  export declare const Button: {
31
- ({ className, type, disabled, focusableWhenDisabled, id, title, onClick, onMouseOver, onMouseOut, onMouseDown, onMouseUp, onKeyDown, onKeyUp, children, link, target, htmlType, rel, ref, name, onBlur, role, ariaLabel, dataAnalyticsId, ...other }: IButton): import("react/jsx-runtime").JSX.Element;
30
+ ({ className, type, disabled, id, title, onClick, onMouseOver, onMouseOut, onMouseDown, onMouseUp, onKeyDown, onKeyUp, children, link, target, htmlType, rel, ref, name, onBlur, role, ariaLabel, dataAnalyticsId, ...other }: IButton): import("react/jsx-runtime").JSX.Element;
32
31
  displayName: string;
33
32
  };
@@ -18,36 +18,19 @@ const classNames_1 = require("../commonHelpers/classNames/classNames");
18
18
  const useDriveClassName_1 = require("../utils/theme/useDriveClassName");
19
19
  const filterGridProps_1 = require("../gridLayout/utils/filterGridProps");
20
20
  const useMobileClassName_1 = require("../utils/theme/useMobileClassName");
21
- const blockActivation = (event) => {
22
- event.preventDefault();
23
- event.stopPropagation();
24
- };
25
- // Guards on `disabled` alone, not just `focusableWhenDisabled`, so clicks are blocked in JS regardless of
26
- // CSS. `onKeyDown`/`onKeyUp` stay unguarded — Tooltip needs them.
27
- const getDisabledGuardedHandlers = (disabled, isFocusableWhenDisabled, onClick, onMouseDown, onMouseUp) => ({
28
- onClick: disabled ? blockActivation : onClick,
29
- onMouseDown: isFocusableWhenDisabled ? blockActivation : onMouseDown,
30
- onMouseUp: isFocusableWhenDisabled ? blockActivation : onMouseUp
31
- });
32
- const getButtonClassName = (type, disabled, isFocusableWhenDisabled, mobileOrDriveClasses, className) => {
21
+ const Button = (_a) => {
22
+ var { className, type, disabled, id, title, onClick, onMouseOver, onMouseOut, onMouseDown, onMouseUp, onKeyDown, onKeyUp, children, link, target, htmlType, rel, ref, name, onBlur, role, ariaLabel, dataAnalyticsId } = _a, other = __rest(_a, ["className", "type", "disabled", "id", "title", "onClick", "onMouseOver", "onMouseOut", "onMouseDown", "onMouseUp", "onKeyDown", "onKeyUp", "children", "link", "target", "htmlType", "rel", "ref", "name", "onBlur", "role", "ariaLabel", "dataAnalyticsId"]);
33
23
  const modifierClassName = type && type !== buttonType_1.ButtonType.Secondary ? `zen-button--${type}` : "";
34
24
  const disabledClassName = disabled ? "zen-button--disabled" : "";
35
- const disabledFocusableClassName = isFocusableWhenDisabled ? "zen-button--disabled-focusable" : "";
36
- return (0, classNames_1.classNames)(["zen-button", mobileOrDriveClasses, modifierClassName, disabledClassName, disabledFocusableClassName, className !== null && className !== void 0 ? className : ""]);
37
- };
38
- const Button = (_a) => {
39
- var { className, type, disabled, focusableWhenDisabled, id, title, onClick, onMouseOver, onMouseOut, onMouseDown, onMouseUp, onKeyDown, onKeyUp, children, link, target, htmlType, rel, ref, name, onBlur, role, ariaLabel, dataAnalyticsId } = _a, other = __rest(_a, ["className", "type", "disabled", "focusableWhenDisabled", "id", "title", "onClick", "onMouseOver", "onMouseOut", "onMouseDown", "onMouseUp", "onKeyDown", "onKeyUp", "children", "link", "target", "htmlType", "rel", "ref", "name", "onBlur", "role", "ariaLabel", "dataAnalyticsId"]);
40
- const isFocusableWhenDisabled = Boolean(disabled) && Boolean(focusableWhenDisabled);
41
25
  const driveClasses = (0, useDriveClassName_1.useDriveClassName)("zen-button");
42
26
  const ariaLabelValue = ariaLabel || title;
43
27
  const mobileClasses = (0, useMobileClassName_1.useMobileClassName)("zen-button");
44
28
  const commonArgs = Object.assign(Object.assign(Object.assign({ id,
45
- title }, (ariaLabelValue ? { "aria-label": ariaLabelValue } : {})), (dataAnalyticsId ? { "data-analytics-id": dataAnalyticsId } : {})), { className: getButtonClassName(type, disabled, isFocusableWhenDisabled, mobileClasses || driveClasses || "", className) });
46
- const { onClick: onClickWithDisabledGuard, onMouseDown: onMouseDownWithDisabledGuard, onMouseUp: onMouseUpWithDisabledGuard } = getDisabledGuardedHandlers(disabled, isFocusableWhenDisabled, onClick, onMouseDown, onMouseUp);
29
+ title }, (ariaLabelValue ? { "aria-label": ariaLabelValue } : {})), (dataAnalyticsId ? { "data-analytics-id": dataAnalyticsId } : {})), { className: (0, classNames_1.classNames)(["zen-button", mobileClasses || driveClasses || "", modifierClassName, disabledClassName, className !== null && className !== void 0 ? className : ""]) });
47
30
  if (link) {
48
- return ((0, jsx_runtime_1.jsx)("a", Object.assign({ role: "link", rel: rel, target: target, href: disabled ? undefined : link, tabIndex: isFocusableWhenDisabled ? 0 : undefined, "aria-disabled": disabled ? true : undefined, onKeyDown: onKeyDown, onKeyUp: onKeyUp, onMouseOver: onMouseOver, onMouseOut: onMouseOut, onClick: onClickWithDisabledGuard }, commonArgs, (0, filterGridProps_1.filterGridProps)(other), { children: children })));
31
+ return ((0, jsx_runtime_1.jsx)("a", Object.assign({ role: "link", rel: rel, target: target, href: disabled ? undefined : link, "aria-disabled": disabled, onKeyDown: onKeyDown, onKeyUp: onKeyUp, onMouseOver: onMouseOver, onMouseOut: onMouseOut, onClick: onClick }, commonArgs, (0, filterGridProps_1.filterGridProps)(other), { children: children })));
49
32
  }
50
- return ((0, jsx_runtime_1.jsx)("button", Object.assign({ name: name, onBlur: onBlur, onClick: onClickWithDisabledGuard, onMouseOver: onMouseOver, onMouseOut: onMouseOut, onMouseDown: onMouseDownWithDisabledGuard, onMouseUp: onMouseUpWithDisabledGuard, onKeyDown: onKeyDown, onKeyUp: onKeyUp, ref: ref, type: htmlType, disabled: isFocusableWhenDisabled ? undefined : disabled, "aria-disabled": isFocusableWhenDisabled ? true : undefined, role: role }, commonArgs, (0, filterGridProps_1.filterGridProps)(other), { children: children })));
33
+ return ((0, jsx_runtime_1.jsx)("button", Object.assign({ name: name, onBlur: onBlur, onClick: onClick, onMouseOver: onMouseOver, onMouseOut: onMouseOut, onMouseDown: onMouseDown, onMouseUp: onMouseUp, onKeyDown: onKeyDown, onKeyUp: onKeyUp, ref: ref, type: htmlType, disabled: disabled, role: role }, commonArgs, (0, filterGridProps_1.filterGridProps)(other), { children: children })));
51
34
  };
52
35
  exports.Button = Button;
53
36
  exports.Button.displayName = "Button";
@@ -0,0 +1,35 @@
1
+ import * as React from "react";
2
+ export declare const BUTTON_DISABLED_CLASS = "zen-button--disabled";
3
+ export declare const BUTTON_DISABLED_FOCUSABLE_CLASS = "zen-button--disabled-focusable";
4
+ export interface IButtonDisabledOptions {
5
+ disabled?: boolean;
6
+ /**
7
+ * Keeps a disabled button in the tab order and hoverable, using `aria-disabled` instead of the native
8
+ * `disabled` attribute. Has no effect while the button is enabled.
9
+ */
10
+ focusable?: boolean;
11
+ /** The `link` prop of the button, a link without `href` is not tabbable, so it needs an explicit `tabIndex`. */
12
+ link?: string;
13
+ }
14
+ export interface IButtonDisabledProps {
15
+ disabled?: boolean;
16
+ "aria-disabled"?: boolean;
17
+ tabIndex?: number;
18
+ className?: string;
19
+ onClick?: (event: React.SyntheticEvent) => void;
20
+ onMouseDown?: (event: React.SyntheticEvent) => void;
21
+ onMouseUp?: (event: React.SyntheticEvent) => void;
22
+ }
23
+ /**
24
+ * Props that make `Button` (or `TextIconButton`) disabled. `Button` itself only applies them.
25
+ *
26
+ * - enabled: `{ disabled }`
27
+ * - disabled: native `disabled`, the button leaves the tab order
28
+ * - disabled and `focusable`: `aria-disabled`, activation is blocked, the button stays focusable and hoverable
29
+ *
30
+ * `onKeyDown`/`onKeyUp` are left untouched: Tooltip needs them. Enter/Space on a focused button without the native
31
+ * `disabled` attribute fire a click, which is blocked here.
32
+ *
33
+ * The caller merges the returned `className` with its own and wraps the button in whatever explains the state.
34
+ */
35
+ export declare const getButtonDisabledProps: ({ disabled, focusable, link }: IButtonDisabledOptions) => IButtonDisabledProps;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getButtonDisabledProps = exports.BUTTON_DISABLED_FOCUSABLE_CLASS = exports.BUTTON_DISABLED_CLASS = void 0;
4
+ const classNames_1 = require("../commonHelpers/classNames/classNames");
5
+ // `button.less` styles these classes: `--disabled-focusable` keeps pointer events so hover/tooltip still work.
6
+ exports.BUTTON_DISABLED_CLASS = "zen-button--disabled";
7
+ exports.BUTTON_DISABLED_FOCUSABLE_CLASS = "zen-button--disabled-focusable";
8
+ const blockActivation = (event) => {
9
+ event.preventDefault();
10
+ event.stopPropagation();
11
+ };
12
+ /**
13
+ * Props that make `Button` (or `TextIconButton`) disabled. `Button` itself only applies them.
14
+ *
15
+ * - enabled: `{ disabled }`
16
+ * - disabled: native `disabled`, the button leaves the tab order
17
+ * - disabled and `focusable`: `aria-disabled`, activation is blocked, the button stays focusable and hoverable
18
+ *
19
+ * `onKeyDown`/`onKeyUp` are left untouched: Tooltip needs them. Enter/Space on a focused button without the native
20
+ * `disabled` attribute fire a click, which is blocked here.
21
+ *
22
+ * The caller merges the returned `className` with its own and wraps the button in whatever explains the state.
23
+ */
24
+ const getButtonDisabledProps = ({ disabled, focusable, link }) => {
25
+ if (!disabled || !focusable) {
26
+ return { disabled };
27
+ }
28
+ const hasLink = Boolean(link);
29
+ return Object.assign(Object.assign({}, (hasLink ? { disabled: true, tabIndex: 0 } : { "aria-disabled": true })), {
30
+ // Button adds `zen-button--disabled` to a disabled link itself.
31
+ className: (0, classNames_1.classNames)([hasLink ? "" : exports.BUTTON_DISABLED_CLASS, exports.BUTTON_DISABLED_FOCUSABLE_CLASS]), onClick: blockActivation, onMouseDown: blockActivation, onMouseUp: blockActivation });
32
+ };
33
+ exports.getButtonDisabledProps = getButtonDisabledProps;
@@ -12,7 +12,7 @@ export interface IBulkActionsProps extends IZenComponentProps {
12
12
  * even when `selected` is 0 (i.e., cross-page "select all" is active).
13
13
  */
14
14
  allSelected?: boolean;
15
- /** Show a selection label without a count when the selected total is unknown. */
15
+ /** The selection spans unloaded rows and its total is not available. */
16
16
  selectionCountUnknown?: boolean;
17
17
  onClearSelection: () => void;
18
18
  onSelectAll?: () => void;
@@ -12,12 +12,28 @@ export declare const isAllItemsHasOneParent: (listData: IDropdownRawItem[], chil
12
12
  export declare function prepareComboOptionsForList(actionListData: Set<string>, translate: (s: string) => string, options: IDropdownRawItem[], getSelectedFn: () => string[], getSelectedChildFn: (optionId: any) => string[], isFlatList: boolean, getTitle?: (itemId: string) => string, isAllSelected?: boolean): ICheckboxListWithActionOption[];
13
13
  export declare const createOptions: (stateObj: IDropdownState, translate: (s: string) => string, listItem: IDropdownRawItem[], isAllSelected?: boolean) => ICheckboxListWithActionOption[];
14
14
  export declare const sortDropdownItemArray: (arr: IDropdownRawItem[], sortFn?: (a: IDropdownRawItem, b: IDropdownRawItem) => number) => IDropdownRawItem[];
15
+ /**
16
+ * An item is half selected when at least one of its direct children is selected — the state its checkbox
17
+ * shows as indeterminate (see `prepareComboOptionsForList`). `selectedSet` is passed in so the sort can use
18
+ * its own snapshot of the selection instead of the live one.
19
+ */
20
+ export declare const hasSelectedChild: (groupsMap: IDropdownState["groupsMap"], itemId: string, selectedSet: Set<string>) => boolean;
21
+ interface ISortSelectedToTopOptions {
22
+ /** Items that count as selected although their own id is not, e.g. a half selected parent. */
23
+ isPartiallySelected?: (item: IDropdownRawItem) => boolean;
24
+ /** Disabled items cannot be toggled, so they follow the selectable ones. */
25
+ isDisabled?: (item: IDropdownRawItem) => boolean;
26
+ }
15
27
  /**
16
28
  * Floats selected items to the top of the list while preserving the relative order within each
17
- * group (selected and unselected). When `selectedIds` is empty the original array is returned
18
- * unchanged (no allocation). Uses a single-pass partition to avoid traversing `items` twice.
29
+ * group. The order is: selected items that can be toggled, then selected items that are disabled
30
+ * (so a newly selected item is never pushed down by disabled ones), then everything else. An item
31
+ * counts as selected when its own id is in `selectedIds` or when `isPartiallySelected` returns true
32
+ * for it, so half selected items sit with the selected ones. When `selectedIds` is empty the original
33
+ * array is returned unchanged (no allocation). Uses a single-pass partition to avoid traversing
34
+ * `items` more than once.
19
35
  */
20
- export declare const applySortSelectedToTop: (items: IDropdownRawItem[], selectedIds: string[]) => IDropdownRawItem[];
36
+ export declare const applySortSelectedToTop: (items: IDropdownRawItem[], selectedIds: string[], { isPartiallySelected, isDisabled }?: ISortSelectedToTopOptions) => IDropdownRawItem[];
21
37
  export declare const createSingleListOptions: (stateObj: IDropdownState, translate: (s: string) => string, listItem: IDropdownRawItem[]) => {
22
38
  destructive: boolean | undefined;
23
39
  divider?: boolean | undefined;
@@ -56,3 +72,4 @@ export declare const getAllyTriggerProps: (allyPattern: "selectOnlyCombobox" | "
56
72
  readonly role: "button";
57
73
  readonly "aria-haspopup": "menu";
58
74
  };
75
+ export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getAllyTriggerProps = exports.getSelectedCount = exports.getStringFromAllSelected = exports.getStringFromSelected = exports.checkIsDataProblem = exports.prepareSelectedIdsToItems = exports.compareObjectsArrays = exports.compareStringsArrays = exports.createListDataOptions = exports.createSingleListOptions = exports.applySortSelectedToTop = exports.sortDropdownItemArray = exports.createOptions = exports.isAllItemsHasOneParent = exports.getDataForChange = exports.isEveryItemSelected = exports.isAllChildrenSelected = exports.getListDataWithDisabled = exports.getGroupsTreeItem = void 0;
3
+ exports.getAllyTriggerProps = exports.getSelectedCount = exports.getStringFromAllSelected = exports.getStringFromSelected = exports.checkIsDataProblem = exports.prepareSelectedIdsToItems = exports.compareObjectsArrays = exports.compareStringsArrays = exports.createListDataOptions = exports.createSingleListOptions = exports.applySortSelectedToTop = exports.hasSelectedChild = exports.sortDropdownItemArray = exports.createOptions = exports.isAllItemsHasOneParent = exports.getDataForChange = exports.isEveryItemSelected = exports.isAllChildrenSelected = exports.getListDataWithDisabled = exports.getGroupsTreeItem = void 0;
4
4
  exports.prepareComboOptionsForList = prepareComboOptionsForList;
5
5
  const groupsHelper_1 = require("../groupsFilterRaw/groupsHelper");
6
6
  const stateReducerHelper_1 = require("./stateReducer/stateReducerHelper");
@@ -93,21 +93,44 @@ const sortDropdownItemArray = (arr, sortFn) => {
93
93
  return arr;
94
94
  };
95
95
  exports.sortDropdownItemArray = sortDropdownItemArray;
96
+ /**
97
+ * An item is half selected when at least one of its direct children is selected — the state its checkbox
98
+ * shows as indeterminate (see `prepareComboOptionsForList`). `selectedSet` is passed in so the sort can use
99
+ * its own snapshot of the selection instead of the live one.
100
+ */
101
+ const hasSelectedChild = (groupsMap, itemId, selectedSet) => {
102
+ const group = groupsMap[itemId];
103
+ return group ? (0, stateReducerHelper_1.getChildList)(group.children).some(child => selectedSet.has(child.id)) : false;
104
+ };
105
+ exports.hasSelectedChild = hasSelectedChild;
96
106
  /**
97
107
  * Floats selected items to the top of the list while preserving the relative order within each
98
- * group (selected and unselected). When `selectedIds` is empty the original array is returned
99
- * unchanged (no allocation). Uses a single-pass partition to avoid traversing `items` twice.
108
+ * group. The order is: selected items that can be toggled, then selected items that are disabled
109
+ * (so a newly selected item is never pushed down by disabled ones), then everything else. An item
110
+ * counts as selected when its own id is in `selectedIds` or when `isPartiallySelected` returns true
111
+ * for it, so half selected items sit with the selected ones. When `selectedIds` is empty the original
112
+ * array is returned unchanged (no allocation). Uses a single-pass partition to avoid traversing
113
+ * `items` more than once.
100
114
  */
101
- const applySortSelectedToTop = (items, selectedIds) => {
115
+ const applySortSelectedToTop = (items, selectedIds, { isPartiallySelected, isDisabled } = {}) => {
102
116
  if (!selectedIds.length)
103
117
  return items;
104
118
  const selectedSet = new Set(selectedIds);
105
119
  const selected = [];
120
+ const selectedDisabled = [];
106
121
  const unselected = [];
107
122
  for (const item of items) {
108
- (selectedSet.has(item.id) ? selected : unselected).push(item);
123
+ if (!selectedSet.has(item.id) && !(isPartiallySelected === null || isPartiallySelected === void 0 ? void 0 : isPartiallySelected(item))) {
124
+ unselected.push(item);
125
+ }
126
+ else if (isDisabled === null || isDisabled === void 0 ? void 0 : isDisabled(item)) {
127
+ selectedDisabled.push(item);
128
+ }
129
+ else {
130
+ selected.push(item);
131
+ }
109
132
  }
110
- return [...selected, ...unselected];
133
+ return [...selected, ...selectedDisabled, ...unselected];
111
134
  };
112
135
  exports.applySortSelectedToTop = applySortSelectedToTop;
113
136
  const createSingleListOptions = (stateObj, translate, listItem) => {
@@ -1155,20 +1155,36 @@ const DropdownRaw = props => {
1155
1155
  // hasApplyButton=true: uses globalSelectedIds (committed selection) — reordering
1156
1156
  // only takes effect after the user clicks "Apply".
1157
1157
  const idsForSort = hasApplyButton ? state.globalSelectedIds : state.sortedTopSnapshot;
1158
- const floated = sortSelectedToTop ? (0, dropdownHelper_1.applySortSelectedToTop)(children, idsForSort) : children;
1159
- rawItems = floated.slice(0, listLimit);
1160
1158
  if (sortSelectedToTop) {
1159
+ // Half selected items (a direct child is selected, shown as indeterminate) float with the selected ones.
1160
+ // Disabled selected items follow the selectable ones; in a nested list `disabled` is not enforced (see createOptions).
1161
1161
  const selectedSet = new Set(idsForSort);
1162
- pinnedCount = rawItems.filter(item => selectedSet.has(item.id)).length;
1162
+ const isPartiallySelected = item => (0, dropdownHelper_1.hasSelectedChild)(state.groupsMap, item.id, selectedSet);
1163
+ const isDisabled = item => Boolean(item.disabled) && !state.isNestedList;
1164
+ rawItems = (0, dropdownHelper_1.applySortSelectedToTop)(children, idsForSort, {
1165
+ isPartiallySelected,
1166
+ isDisabled
1167
+ }).slice(0, listLimit);
1168
+ pinnedCount = rawItems.filter(item => selectedSet.has(item.id) || isPartiallySelected(item)).length;
1169
+ } else {
1170
+ rawItems = children.slice(0, listLimit);
1163
1171
  }
1164
1172
  }
1165
1173
  const listData = (0, dropdownHelper_1.createListDataOptions)(state, translate, rawItems, multiselect, currentAllSelected);
1166
1174
  // Explicit bounds check (rather than relying on CheckboxListWithAction's own
1167
1175
  // `ind < optionsArr.length - 1` guard) keeps the "don't draw a trailing divider"
1168
1176
  // intent local to where the boundary is computed.
1169
- const currentListData = multiselect && sortSelectedToTop && pinnedCount > 0 && pinnedCount < listData.length ? listData.map((opt, ind) => ind === pinnedCount - 1 ? Object.assign(Object.assign({}, opt), {
1170
- divider: true
1171
- }) : opt) : listData;
1177
+ // Floated items drop their own dividers, so the boundary divider is the only one drawn between them and the rest.
1178
+ const currentListData = multiselect && sortSelectedToTop && pinnedCount > 0 && pinnedCount < listData.length ? listData.map((opt, ind) => {
1179
+ if (ind === pinnedCount - 1) {
1180
+ return Object.assign(Object.assign({}, opt), {
1181
+ divider: true
1182
+ });
1183
+ }
1184
+ return ind < pinnedCount && opt.divider ? Object.assign(Object.assign({}, opt), {
1185
+ divider: false
1186
+ }) : opt;
1187
+ }) : listData;
1172
1188
  return (0, jsx_runtime_1.jsx)(dropdownList_1.DropdownList, {
1173
1189
  onBackButtonClick: handleBackButtonClick,
1174
1190
  onSelectAllClick: isSearchMode ? handleSelectAllInSearch : handleSelectAllClick,
@@ -111,7 +111,7 @@ const PageToolbarCollapsedItemsControl = ({
111
111
  rel: child.props.rel,
112
112
  icon: child.props.icon,
113
113
  iconOpen: child.props.iconOpen,
114
- destructive: child.props.destructive
114
+ destructive: child.props.type === buttonType_1.ButtonType.Destructive || child.props.type === buttonType_1.ButtonType.TertiaryDestructive
115
115
  });
116
116
  items.push(el);
117
117
  }
@@ -2,20 +2,33 @@ import { IButton } from "../button/button";
2
2
  import { TButtonIconPosition } from "../textIconButton/textIconButton";
3
3
  import { TSvgIconSize } from "../svgIcon/svgIconSize";
4
4
  import { IIcon } from "../icons/icon";
5
+ import { TAlignment } from "../absolute/absolute";
5
6
  export interface IHeaderButton extends Omit<IButton, "onClick"> {
6
7
  id: string;
7
8
  important?: boolean;
8
9
  inContextMenu?: boolean;
9
10
  icon?: React.FC<IIcon>;
10
11
  iconOpen?: boolean;
11
- destructive?: boolean;
12
12
  iconPosition?: TButtonIconPosition;
13
13
  iconClasses?: string;
14
14
  iconSize?: TSvgIconSize;
15
+ /**
16
+ * Tooltip content shown only while the button is `disabled`.
17
+ *
18
+ * When `disabled` and `disabledTooltip` are both set, the button uses `aria-disabled` instead of the native
19
+ * `disabled` attribute, so it stays focusable and hoverable and the tooltip can explain why it is unavailable.
20
+ * In every other case the standard `disabled` attribute is used, and `disabledTooltip` is ignored while the
21
+ * button is enabled.
22
+ */
23
+ disabledTooltip?: React.ReactNode;
15
24
  onClick?: (id: string, event: React.MouseEvent) => void;
16
25
  }
26
+ interface IHeaderButtonInternal {
27
+ tooltipAlignment?: TAlignment;
28
+ }
17
29
  export declare const HeaderButtonDisplayName = "HeaderButton";
18
30
  export declare const HeaderButton: {
19
- ({ id, children, icon, iconOpen, destructive, iconPosition, className, important, onClick, inContextMenu, disabled, focusableWhenDisabled, ...restProps }: IHeaderButton): import("react/jsx-runtime").JSX.Element;
31
+ ({ id, children, icon, iconOpen, iconPosition, className, important, onClick, inContextMenu, disabledTooltip, tooltipAlignment, disabled, link, ...restProps }: IHeaderButton & IHeaderButtonInternal): import("react/jsx-runtime").JSX.Element;
20
32
  displayName: string;
21
33
  };
34
+ export {};
@@ -16,14 +16,19 @@ const jsx_runtime_1 = require("react/jsx-runtime");
16
16
  const button_1 = require("../button/button");
17
17
  const textIconButton_1 = require("../textIconButton/textIconButton");
18
18
  const classNames_1 = require("../commonHelpers/classNames/classNames");
19
+ const tooltip_1 = require("../tooltip/tooltip");
20
+ const buttonDisabledProps_1 = require("../button/buttonDisabledProps");
19
21
  exports.HeaderButtonDisplayName = "HeaderButton";
20
22
  const HeaderButton = (_a) => {
21
- var { id, children, icon, iconOpen, destructive, iconPosition = textIconButton_1.ButtonIconPosition.Start, className = "", important, onClick, inContextMenu, disabled, focusableWhenDisabled } = _a, restProps = __rest(_a, ["id", "children", "icon", "iconOpen", "destructive", "iconPosition", "className", "important", "onClick", "inContextMenu", "disabled", "focusableWhenDisabled"]);
22
- const fullClassName = (0, classNames_1.classNames)(["zen-header-button", className]);
23
- const props = Object.assign(Object.assign({}, restProps), { disabled, "data-important": important ? "1" : "0",
24
- // Defaults on when disabled; pass `focusableWhenDisabled={false}` to opt back into native `disabled`.
25
- focusableWhenDisabled: focusableWhenDisabled !== null && focusableWhenDisabled !== void 0 ? focusableWhenDisabled : Boolean(disabled) });
26
- return icon ? ((0, jsx_runtime_1.jsx)(textIconButton_1.TextIconButton, Object.assign({ id: id, icon: icon, iconPosition: iconPosition, className: fullClassName, onClick: onClick ? e => onClick(id, e) : undefined }, props, { children: children }))) : ((0, jsx_runtime_1.jsx)(button_1.Button, Object.assign({ id: id, className: fullClassName, onClick: onClick ? e => onClick(id, e) : undefined }, props, { children: children })));
23
+ var { id, children, icon, iconOpen, iconPosition = textIconButton_1.ButtonIconPosition.Start, className = "", important, onClick, inContextMenu, disabledTooltip, tooltipAlignment = "bottom", disabled, link } = _a, restProps = __rest(_a, ["id", "children", "icon", "iconOpen", "iconPosition", "className", "important", "onClick", "inContextMenu", "disabledTooltip", "tooltipAlignment", "disabled", "link"]);
24
+ const tooltip = disabled ? disabledTooltip : undefined;
25
+ // Native `disabled` takes a button out of the tab order and blocks hover, so a tooltip could never be reached.
26
+ // Only a disabled button that has a tooltip to show is kept focusable, via `aria-disabled`.
27
+ const _b = (0, buttonDisabledProps_1.getButtonDisabledProps)({ disabled, focusable: Boolean(tooltip), link }), { className: disabledClassName } = _b, disabledProps = __rest(_b, ["className"]);
28
+ const fullClassName = (0, classNames_1.classNames)(["zen-header-button", className, disabledClassName !== null && disabledClassName !== void 0 ? disabledClassName : ""]);
29
+ const props = Object.assign(Object.assign(Object.assign({}, restProps), { link, "data-important": important ? "1" : "0" }), disabledProps);
30
+ const button = icon ? ((0, jsx_runtime_1.jsx)(textIconButton_1.TextIconButton, Object.assign({ id: id, icon: icon, iconPosition: iconPosition, className: fullClassName, onClick: onClick ? e => onClick(id, e) : undefined }, props, { children: children }))) : ((0, jsx_runtime_1.jsx)(button_1.Button, Object.assign({ id: id, className: fullClassName, onClick: onClick ? e => onClick(id, e) : undefined }, props, { children: children })));
31
+ return tooltip ? ((0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { trigger: button, alignment: tooltipAlignment, children: tooltip })) : (button);
27
32
  };
28
33
  exports.HeaderButton = HeaderButton;
29
34
  exports.HeaderButton.displayName = exports.HeaderButtonDisplayName;
package/dist/index.css CHANGED
@@ -4007,8 +4007,8 @@ html:lang(ar) .zen-menu-button__action {
4007
4007
  padding-block: 0.75rem;
4008
4008
  padding-inline: 1rem 0.5rem;
4009
4009
  }
4010
- .zen-menu-button__action:focus:not(.zen-menu-button__action--disabled),
4011
- .zen-menu-button__action:focus-visible:not(.zen-menu-button__action--disabled),
4010
+ .zen-menu-button__action:focus,
4011
+ .zen-menu-button__action:focus-visible,
4012
4012
  .zen-menu-button__action:hover:not(.zen-menu-button__action--disabled):not(.zen-menu-button__action--selected):not(.zen-menu-button__action--active) {
4013
4013
  color: var(--zen-menu-action-color);
4014
4014
  fill: var(--zen-menu-action-fill);
@@ -4083,10 +4083,6 @@ html:lang(ar) .zen-menu-button__action {
4083
4083
  .zen-menu-button__action.zen-menu-button__action {
4084
4084
  outline: none;
4085
4085
  }
4086
- .zen-menu-button__action--disabled:focus-visible {
4087
- outline: 1px solid var(--borders-form-field--active);
4088
- outline-offset: -2px;
4089
- }
4090
4086
  .zen-menu-button__icon {
4091
4087
  flex: 0 0 auto;
4092
4088
  }
@@ -4931,8 +4927,8 @@ html:lang(ar) .zen-mobile-sheet__title {
4931
4927
  gap: 1.5rem;
4932
4928
  height: 100%;
4933
4929
  }
4934
- .zen-form-section .zen-form-field-label--top + .zen-form-control {
4935
- margin-block-start: -1.25rem;
4930
+ .zen-form-section .zen-form-field-label--top {
4931
+ margin-block-end: -1.25rem;
4936
4932
  }
4937
4933
  .zen-form-section--desktop.zen-form-section--one-column {
4938
4934
  grid-template-columns: repeat(8, 1fr);
@@ -13,60 +13,6 @@ const useDrive_1 = require("../../utils/theme/useDrive");
13
13
  const getMenuButtonState_1 = require("../utils/getMenuButtonState");
14
14
  const isSafeHref_1 = require("../utils/isSafeHref");
15
15
  const getSafeRel_1 = require("../utils/getSafeRel");
16
- // Templates below drop native `disabled` to stay focusable/hoverable; this guard blocks activation instead.
17
- const handleDisabledGuardedClick = (e, disabled, id, onClick) => {
18
- if (disabled) {
19
- e.preventDefault();
20
- e.stopPropagation();
21
- return;
22
- }
23
- onClick === null || onClick === void 0 ? void 0 : onClick(id, e);
24
- };
25
- // Skip Space-to-activate when disabled, so it doesn't flash `--active` or self-click via `linkEl.click()`.
26
- const createDisabledGuardedLinkKeyEvents = (disabled) => ({
27
- onKeyDown: (e) => {
28
- if (disabled || e.key !== " ") {
29
- return;
30
- }
31
- e.preventDefault();
32
- const linkEl = e.target;
33
- linkEl.dataset.spaceDown = "1";
34
- linkEl.classList.add("zen-menu-button__action--active");
35
- },
36
- onKeyUp: (e) => {
37
- if (disabled || e.key !== " ") {
38
- return;
39
- }
40
- e.preventDefault();
41
- const linkEl = e.target;
42
- linkEl.classList.remove("zen-menu-button__action--active");
43
- if (linkEl.dataset.spaceDown) {
44
- delete linkEl.dataset.spaceDown;
45
- linkEl.click();
46
- }
47
- }
48
- });
49
- const getMenuButtonActionClassName = (hasChildren, hasState, isActive, disabled, destructiveClass, driveClasses) => (0, classNames_1.classNames)([
50
- "zen-menu-button__action",
51
- "zen-button",
52
- "zen-caption",
53
- hasChildren ? "zen-menu-button__action--has-children" : "",
54
- hasState && isActive ? "zen-menu-button__action--active" : "",
55
- disabled ? "zen-menu-button__action--disabled" : "",
56
- disabled ? "zen-button--disabled" : "",
57
- disabled ? "zen-button--disabled-focusable" : "",
58
- destructiveClass,
59
- driveClasses || ""
60
- ]);
61
- const getMenuLinkActionClassName = (disabled, destructiveClass, driveClasses) => (0, classNames_1.classNames)(["zen-menu-button__action", "zen-caption", disabled ? "zen-menu-button__action--disabled" : "", destructiveClass, driveClasses || ""]);
62
- // An <a> without `href` isn't tabbable, so `tabIndex="0"` keeps a disabled link item in the tab order.
63
- const getMenuLinkAccessibilityProps = (disabled, link) => ({
64
- href: disabled || !(0, isSafeHref_1.isSafeHref)(link) ? undefined : link,
65
- tabIndex: disabled ? 0 : undefined,
66
- "aria-disabled": disabled
67
- });
68
- const getMenuItemRole = (hasState) => (hasState ? "menuitemcheckbox" : "menuitem");
69
- const getMenuItemAriaChecked = (hasState, isActive) => (hasState ? isActive : undefined);
70
16
  const createMenuIcon = (iconComponent, size, className) => iconComponent ? (0, react_1.createElement)(iconComponent, { size, className }) : null;
71
17
  const resolveTarget = (target, iconOpen) => target !== null && target !== void 0 ? target : (iconOpen ? "_blank" : undefined);
72
18
  const MenuButton = ({ id, onClick, hasChildren, disabled, icon, iconOpen, destructive, name, link, target, rel, className = "", active = null, ref, dataAnalyticsId }) => {
@@ -77,11 +23,47 @@ const MenuButton = ({ id, onClick, hasChildren, disabled, icon, iconOpen, destru
77
23
  const destructiveClass = destructive ? "zen-menu-button__action--destructive" : "";
78
24
  const effectiveTarget = resolveTarget(target, iconOpen);
79
25
  const ChevronIcon = (0, useDirectionalIcon_1.useDirectionalIcon)(iconChevronRight_1.IconChevronRight, iconChevronLeft_1.IconChevronLeft);
80
- const linkEvents = (0, react_1.useMemo)(() => createDisabledGuardedLinkKeyEvents(disabled), [disabled]);
81
- const onClickHandler = (0, react_1.useCallback)((e) => handleDisabledGuardedClick(e, disabled, id, onClick), [id, onClick, disabled]);
26
+ const linkEvents = (0, react_1.useMemo)(() => ({
27
+ onKeyDown: e => {
28
+ if (e.key === " ") {
29
+ e.preventDefault();
30
+ const linkEl = e.target;
31
+ linkEl.dataset.spaceDown = "1";
32
+ linkEl.classList.add("zen-menu-button__action--active");
33
+ }
34
+ },
35
+ onKeyUp: e => {
36
+ if (e.key === " ") {
37
+ e.preventDefault();
38
+ const linkEl = e.target;
39
+ linkEl.classList.remove("zen-menu-button__action--active");
40
+ if (linkEl.dataset.spaceDown) {
41
+ delete linkEl.dataset.spaceDown;
42
+ linkEl.click();
43
+ }
44
+ }
45
+ }
46
+ }), []);
47
+ const onClickHandler = (0, react_1.useCallback)((e) => {
48
+ onClick === null || onClick === void 0 ? void 0 : onClick(id, e);
49
+ }, [id, onClick]);
82
50
  const iconAfterElement = createMenuIcon(iconOpen ? iconOpen_1.IconOpen : undefined, iconSize, "zen-menu-button__icon-after");
83
- const buttonTemplate = ((0, jsx_runtime_1.jsxs)("button", { type: "button", "data-id": id, ref: ref, title: name, className: getMenuButtonActionClassName(hasChildren, hasState, isActive, disabled, destructiveClass, driveMenuButtonActionClasses), role: getMenuItemRole(hasState), onClick: onClickHandler, "aria-disabled": disabled ? true : undefined, "aria-checked": getMenuItemAriaChecked(hasState, isActive), "data-analytics-id": dataAnalyticsId, children: [createMenuIcon(icon, iconSize, (0, classNames_1.classNames)(["zen-caption__pre-content", "zen-menu-button__icon"])), (0, jsx_runtime_1.jsx)("span", { className: "zen-menu-button__action-text", children: name }), iconAfterElement, !!hasChildren && (0, jsx_runtime_1.jsx)(ChevronIcon, { size: iconSize, className: (0, classNames_1.classNames)(["zen-caption__pre-content", "zen-menu-button__more-icon"]) })] }));
84
- const linkTemplate = ((0, jsx_runtime_1.jsxs)("a", Object.assign({ "data-id": id, role: "menuitem", className: getMenuLinkActionClassName(disabled, destructiveClass, driveMenuButtonActionClasses) }, getMenuLinkAccessibilityProps(disabled, link), { onClick: onClickHandler, target: effectiveTarget, rel: (0, getSafeRel_1.getSafeRel)(rel, effectiveTarget), "data-analytics-id": dataAnalyticsId }, linkEvents, { children: [createMenuIcon(icon, iconSize, "zen-caption__pre-content"), iconAfterElement ? (0, jsx_runtime_1.jsx)("span", { className: "zen-menu-button__action-text", children: name }) : name, iconAfterElement] })));
51
+ const buttonTemplate = ((0, jsx_runtime_1.jsxs)("button", { type: "button", "data-id": id, ref: ref, disabled: !!disabled, title: name, className: (0, classNames_1.classNames)([
52
+ "zen-menu-button__action",
53
+ "zen-button",
54
+ "zen-caption",
55
+ hasChildren ? "zen-menu-button__action--has-children" : "",
56
+ hasState && isActive ? "zen-menu-button__action--active" : "",
57
+ destructiveClass,
58
+ driveMenuButtonActionClasses || ""
59
+ ]), role: hasState ? "menuitemcheckbox" : "menuitem", onClick: onClickHandler, "aria-checked": hasState ? isActive : undefined, "data-analytics-id": dataAnalyticsId, children: [createMenuIcon(icon, iconSize, (0, classNames_1.classNames)(["zen-caption__pre-content", "zen-menu-button__icon"])), (0, jsx_runtime_1.jsx)("span", { className: "zen-menu-button__action-text", children: name }), iconAfterElement, !!hasChildren && (0, jsx_runtime_1.jsx)(ChevronIcon, { size: iconSize, className: (0, classNames_1.classNames)(["zen-caption__pre-content", "zen-menu-button__more-icon"]) })] }));
60
+ const linkTemplate = ((0, jsx_runtime_1.jsxs)("a", Object.assign({ "data-id": id, role: "menuitem", className: (0, classNames_1.classNames)([
61
+ "zen-menu-button__action",
62
+ "zen-caption",
63
+ disabled ? "zen-menu-button__action--disabled" : "",
64
+ destructiveClass,
65
+ driveMenuButtonActionClasses || ""
66
+ ]), href: disabled || !(0, isSafeHref_1.isSafeHref)(link) ? undefined : link, "aria-disabled": disabled, onClick: onClickHandler, target: effectiveTarget, rel: (0, getSafeRel_1.getSafeRel)(rel, effectiveTarget), "data-analytics-id": dataAnalyticsId }, linkEvents, { children: [createMenuIcon(icon, iconSize, "zen-caption__pre-content"), iconAfterElement ? (0, jsx_runtime_1.jsx)("span", { className: "zen-menu-button__action-text", children: name }) : name, iconAfterElement] })));
85
67
  return ((0, jsx_runtime_1.jsx)("li", { role: "presentation", className: (0, classNames_1.classNames)(["zen-menu-button", className]), children: link ? linkTemplate : buttonTemplate }));
86
68
  };
87
69
  exports.MenuButton = MenuButton;