@geotab/zenith 3.17.0-beta.1 → 3.17.0-beta.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.
Files changed (46) hide show
  1. package/README.md +26 -29
  2. package/dist/dataGrid/withSelectableRows/components/bulkActions/bulkActions.d.ts +1 -1
  3. package/dist/dropdownRaw/dropdownHelper.d.ts +20 -3
  4. package/dist/dropdownRaw/dropdownHelper.js +29 -6
  5. package/dist/dropdownRaw/dropdownRaw.js +22 -6
  6. package/dist/header/components/collapsedItemsControl/collapsedItemsControl.js +1 -1
  7. package/dist/header/headerButton.d.ts +1 -2
  8. package/dist/header/headerButton.js +1 -1
  9. package/dist/index.css +2 -2
  10. package/dist/pillExpandable/pillExpandable.js +3 -1
  11. package/dist/pillExpandable/pillExpandableSimple.d.ts +2 -1
  12. package/dist/pillExpandable/pillExpandableSimple.js +2 -2
  13. package/dist/table/selectable/interfaces.d.ts +6 -0
  14. package/dist/table/selectable/useDragSelection.d.ts +1 -1
  15. package/dist/table/selectable/useDragSelection.js +4 -4
  16. package/dist/table/selectable/useSelectableRows.d.ts +2 -0
  17. package/dist/table/selectable/useSelectableRows.js +88 -41
  18. package/dist/table/selectable/utils/calculateSelectionState.js +3 -2
  19. package/dist/table/selectable/utils/getCurrentPageSelection.js +9 -4
  20. package/dist/table/selectable/utils/groupExclusions.d.ts +7 -0
  21. package/dist/table/selectable/utils/groupExclusions.js +61 -0
  22. package/dist/table/selectable/utils/isRowChecked.js +1 -1
  23. package/dist/table/table.js +6 -5
  24. package/esm/dataGrid/withSelectableRows/components/bulkActions/bulkActions.d.ts +1 -1
  25. package/esm/dropdownRaw/dropdownHelper.d.ts +20 -3
  26. package/esm/dropdownRaw/dropdownHelper.js +27 -5
  27. package/esm/dropdownRaw/dropdownRaw.js +23 -7
  28. package/esm/header/components/collapsedItemsControl/collapsedItemsControl.js +1 -1
  29. package/esm/header/headerButton.d.ts +1 -2
  30. package/esm/header/headerButton.js +1 -1
  31. package/esm/pillExpandable/pillExpandable.js +3 -1
  32. package/esm/pillExpandable/pillExpandableSimple.d.ts +2 -1
  33. package/esm/pillExpandable/pillExpandableSimple.js +2 -2
  34. package/esm/table/selectable/interfaces.d.ts +6 -0
  35. package/esm/table/selectable/useDragSelection.d.ts +1 -1
  36. package/esm/table/selectable/useDragSelection.js +4 -4
  37. package/esm/table/selectable/useSelectableRows.d.ts +2 -0
  38. package/esm/table/selectable/useSelectableRows.js +90 -43
  39. package/esm/table/selectable/utils/calculateSelectionState.js +3 -2
  40. package/esm/table/selectable/utils/getCurrentPageSelection.js +9 -4
  41. package/esm/table/selectable/utils/groupExclusions.d.ts +7 -0
  42. package/esm/table/selectable/utils/groupExclusions.js +55 -0
  43. package/esm/table/selectable/utils/isRowChecked.js +1 -1
  44. package/esm/table/table.js +6 -5
  45. package/package.json +1 -4
  46. 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
 
@@ -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
  }
@@ -8,7 +8,6 @@ export interface IHeaderButton extends Omit<IButton, "onClick"> {
8
8
  inContextMenu?: boolean;
9
9
  icon?: React.FC<IIcon>;
10
10
  iconOpen?: boolean;
11
- destructive?: boolean;
12
11
  iconPosition?: TButtonIconPosition;
13
12
  iconClasses?: string;
14
13
  iconSize?: TSvgIconSize;
@@ -16,6 +15,6 @@ export interface IHeaderButton extends Omit<IButton, "onClick"> {
16
15
  }
17
16
  export declare const HeaderButtonDisplayName = "HeaderButton";
18
17
  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;
18
+ ({ id, children, icon, iconOpen, iconPosition, className, important, onClick, inContextMenu, disabled, focusableWhenDisabled, ...restProps }: IHeaderButton): import("react/jsx-runtime").JSX.Element;
20
19
  displayName: string;
21
20
  };
@@ -18,7 +18,7 @@ const textIconButton_1 = require("../textIconButton/textIconButton");
18
18
  const classNames_1 = require("../commonHelpers/classNames/classNames");
19
19
  exports.HeaderButtonDisplayName = "HeaderButton";
20
20
  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"]);
21
+ var { id, children, icon, iconOpen, iconPosition = textIconButton_1.ButtonIconPosition.Start, className = "", important, onClick, inContextMenu, disabled, focusableWhenDisabled } = _a, restProps = __rest(_a, ["id", "children", "icon", "iconOpen", "iconPosition", "className", "important", "onClick", "inContextMenu", "disabled", "focusableWhenDisabled"]);
22
22
  const fullClassName = (0, classNames_1.classNames)(["zen-header-button", className]);
23
23
  const props = Object.assign(Object.assign({}, restProps), { disabled, "data-important": important ? "1" : "0",
24
24
  // Defaults on when disabled; pass `focusableWhenDisabled={false}` to opt back into native `disabled`.
package/dist/index.css CHANGED
@@ -4931,8 +4931,8 @@ html:lang(ar) .zen-mobile-sheet__title {
4931
4931
  gap: 1.5rem;
4932
4932
  height: 100%;
4933
4933
  }
4934
- .zen-form-section .zen-form-field-label--top + .zen-form-control {
4935
- margin-block-start: -1.25rem;
4934
+ .zen-form-section .zen-form-field-label--top {
4935
+ margin-block-end: -1.25rem;
4936
4936
  }
4937
4937
  .zen-form-section--desktop.zen-form-section--one-column {
4938
4938
  grid-template-columns: repeat(8, 1fr);
@@ -428,6 +428,7 @@ const PillExpandableBase = ({
428
428
  }) => {
429
429
  const popupId = (0, react_1.useId)();
430
430
  const descriptionId = (0, react_1.useId)();
431
+ const counterId = (0, react_1.useId)();
431
432
  const [descriptionElement, setDescriptionElement] = (0, react_1.useState)(null);
432
433
  const isDrive = (0, useDrive_1.useDrive)();
433
434
  const [isMobile, setIsMobile] = (0, react_1.useState)(false);
@@ -596,10 +597,11 @@ const PillExpandableBase = ({
596
597
  errorHandler: errorHandler,
597
598
  isBeta: isBeta,
598
599
  isMobile: isMobile,
599
- descriptionId: isPopupOpen && descriptionElement ? descriptionId : undefined,
600
+ descriptionId: [uniqueChildren && !hideCounterNumber ? counterId : undefined, isPopupOpen && descriptionElement ? descriptionId : undefined].filter(Boolean).join(" ") || undefined,
600
601
  ariaHasPopup: "dialog",
601
602
  dataAnalyticsId: dataAnalyticsId
602
603
  }), uniqueChildren && (0, jsx_runtime_1.jsx)(pillExpandableSimple_1.PillExpandableSimple, {
604
+ id: counterId,
603
605
  icon: secondaryIcon || effectiveIcon,
604
606
  text: hideCounterNumber ? "" : counterText,
605
607
  includeExpandIcon: false,
@@ -2,6 +2,7 @@ import { FC, RefObject } from "react";
2
2
  import { IPillExpandableBase, TPillType } from "./pillExpandable";
3
3
  import { IIcon } from "../icons/icon";
4
4
  interface IPillExpandableSimple extends IPillExpandableBase {
5
+ id?: string;
5
6
  includeExpandIcon?: boolean;
6
7
  expanded?: boolean;
7
8
  onExpand?: () => void;
@@ -15,7 +16,7 @@ interface IPillExpandableSimple extends IPillExpandableBase {
15
16
  type?: TPillType;
16
17
  }
17
18
  export declare const PillExpandableSimple: {
18
- ({ onExpand, text, expanded, includeExpandIcon, type, icon, isBeta, isMobile, ref, className, descriptionId, ariaHasPopup, isDecorative, dataAnalyticsId }: IPillExpandableSimple): import("react/jsx-runtime").JSX.Element;
19
+ ({ id, onExpand, text, expanded, includeExpandIcon, type, icon, isBeta, isMobile, ref, className, descriptionId, ariaHasPopup, isDecorative, dataAnalyticsId }: IPillExpandableSimple): import("react/jsx-runtime").JSX.Element;
19
20
  displayName: string;
20
21
  };
21
22
  export {};
@@ -8,7 +8,7 @@ const iconChevronTop_1 = require("../icons/iconChevronTop");
8
8
  const iconChevronBottom_1 = require("../icons/iconChevronBottom");
9
9
  const iconException_1 = require("../icons/iconException");
10
10
  const classNames_1 = require("../commonHelpers/classNames/classNames");
11
- const PillExpandableSimple = ({ onExpand, text, expanded, includeExpandIcon = true, type = "warning", icon = iconException_1.IconException, isBeta, isMobile, ref, className, descriptionId, ariaHasPopup, isDecorative, dataAnalyticsId
11
+ const PillExpandableSimple = ({ id, onExpand, text, expanded, includeExpandIcon = true, type = "warning", icon = iconException_1.IconException, isBeta, isMobile, ref, className, descriptionId, ariaHasPopup, isDecorative, dataAnalyticsId
12
12
  // eslint-disable-next-line complexity
13
13
  }) => {
14
14
  const iconDriveClassName = (0, useDriveClassName_1.useDriveClassName)("icon");
@@ -37,7 +37,7 @@ const PillExpandableSimple = ({ onExpand, text, expanded, includeExpandIcon = tr
37
37
  size: iconSize
38
38
  }) }) }))] }));
39
39
  if (isDecorative) {
40
- return ((0, jsx_runtime_1.jsx)("span", { className: (0, classNames_1.classNames)([
40
+ return ((0, jsx_runtime_1.jsx)("span", { id: id, className: (0, classNames_1.classNames)([
41
41
  "zen-status-pill__container",
42
42
  `zen-status-pill__container--${type}`,
43
43
  containerDriveClassName || "",
@@ -15,6 +15,12 @@ export interface ISelectionValue {
15
15
  * Consumers opting in must resolve the effective selection before running bulk actions.
16
16
  */
17
17
  selected: string[];
18
+ /**
19
+ * Excluded nested groups, including children that have not loaded yet. Only used with
20
+ * allowExclusions and all: true. Preserve this field when saving controlled selection;
21
+ * resolve these groups as well as selected before running a bulk action.
22
+ */
23
+ excludedGroupIds?: string[];
18
24
  }
19
25
  /**
20
26
  * One checkbox row in the order it is rendered. Expanded nested rows are part of this list,
@@ -4,7 +4,7 @@ import { IDragSelectionRow } from "./interfaces";
4
4
  * @param rows every checkbox row in render order, expanded nested rows included, so ranges can
5
5
  * span nesting levels.
6
6
  */
7
- export declare const useDragSelection: (rows: IDragSelectionRow[], gridRef: RefObject<HTMLElement | null> | undefined, onBatchSelect: (rangeIds: string[], select: boolean, base: Set<string>) => void, getBaseSelection: () => Set<string>, isSelectable: (row: IDragSelectionRow) => boolean, isEnabled: boolean) => {
7
+ export declare const useDragSelection: <TSelection = Set<string>>(rows: IDragSelectionRow[], gridRef: RefObject<HTMLElement | null> | undefined, onBatchSelect: (rangeIds: string[], select: boolean, base: TSelection) => void, getBaseSelection: () => TSelection, isSelectable: (row: IDragSelectionRow) => boolean, isEnabled: boolean) => {
8
8
  onCheckboxMouseDown: (id: string, checked: boolean, e: ReactMouseEvent) => void;
9
9
  suppressNextChangeRef: RefObject<boolean>;
10
10
  };
@@ -22,7 +22,7 @@ const useDragSelection = (rows, gridRef, onBatchSelect, getBaseSelection, isSele
22
22
  isEnabledRef.current = isEnabled;
23
23
  const anchorIdRef = (0, react_1.useRef)(null);
24
24
  const anchorSelectRef = (0, react_1.useRef)(true);
25
- const baseSelectedRef = (0, react_1.useRef)(new Set());
25
+ const baseSelectedRef = (0, react_1.useRef)(null);
26
26
  const isDraggingRef = (0, react_1.useRef)(false);
27
27
  const movedRef = (0, react_1.useRef)(false);
28
28
  const lastResultKeyRef = (0, react_1.useRef)("");
@@ -31,7 +31,7 @@ const useDragSelection = (rows, gridRef, onBatchSelect, getBaseSelection, isSele
31
31
  const attachedGridElementRef = (0, react_1.useRef)(null);
32
32
  const emitRange = (0, react_1.useCallback)((currentId) => {
33
33
  const anchorId = anchorIdRef.current;
34
- if (anchorId === null) {
34
+ if (anchorId === null || baseSelectedRef.current === null) {
35
35
  return;
36
36
  }
37
37
  const range = (0, getRangeIds_1.getRangeIds)(rowsRef.current, anchorId, currentId, isSelectableRef.current);
@@ -85,7 +85,7 @@ const useDragSelection = (rows, gridRef, onBatchSelect, getBaseSelection, isSele
85
85
  (_c = attachedGridElementRef.current) === null || _c === void 0 ? void 0 : _c.removeEventListener("scroll", onScroll);
86
86
  (_d = attachedGridElementRef.current) === null || _d === void 0 ? void 0 : _d.classList.remove(DRAG_CLASS);
87
87
  attachedGridElementRef.current = null;
88
- }, [onMouseMove, onScroll]); // eslint-disable-line react-hooks/exhaustive-deps
88
+ }, [onMouseMove, onScroll]);
89
89
  const onCheckboxMouseDown = (0, react_1.useCallback)((id, checked, e) => {
90
90
  var _a, _b, _c, _d, _e;
91
91
  // Right-click between drag-end and the trailing click must not clear suppression.
@@ -95,7 +95,7 @@ const useDragSelection = (rows, gridRef, onBatchSelect, getBaseSelection, isSele
95
95
  if (!isEnabledRef.current || e.button !== 0) {
96
96
  return;
97
97
  }
98
- if (e.shiftKey && anchorIdRef.current !== null) {
98
+ if (e.shiftKey && anchorIdRef.current !== null && baseSelectedRef.current !== null) {
99
99
  e.preventDefault();
100
100
  const anchorId = anchorIdRef.current;
101
101
  const anchorInList = rowsRef.current.some(row => row.id === anchorId);
@@ -10,6 +10,8 @@ export interface ISelectableRowsOptions<T extends IRowEntity<N>, N extends IEnti
10
10
  onSelect: (selection: ISelectionValue) => void;
11
11
  /** Preserve cross-page Select All using excluded IDs. Consumers must resolve exclusions before running bulk actions. Defaults to false. */
12
12
  allowExclusions?: boolean;
13
+ /** Changes when the dataset represented by the selection is replaced, rather than paged. */
14
+ selectionDatasetId?: string | number;
13
15
  checkboxDisabled?: (entity: T | N, isNested?: boolean) => boolean;
14
16
  checkboxVisible?: (entity: T | N, isNested?: boolean) => boolean;
15
17
  checkboxInHeader?: boolean;