@geotab/zenith 3.17.0-beta.0 → 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 (48) 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/dist/waiting/waiting.js +3 -1
  25. package/esm/dataGrid/withSelectableRows/components/bulkActions/bulkActions.d.ts +1 -1
  26. package/esm/dropdownRaw/dropdownHelper.d.ts +20 -3
  27. package/esm/dropdownRaw/dropdownHelper.js +27 -5
  28. package/esm/dropdownRaw/dropdownRaw.js +23 -7
  29. package/esm/header/components/collapsedItemsControl/collapsedItemsControl.js +1 -1
  30. package/esm/header/headerButton.d.ts +1 -2
  31. package/esm/header/headerButton.js +1 -1
  32. package/esm/pillExpandable/pillExpandable.js +3 -1
  33. package/esm/pillExpandable/pillExpandableSimple.d.ts +2 -1
  34. package/esm/pillExpandable/pillExpandableSimple.js +2 -2
  35. package/esm/table/selectable/interfaces.d.ts +6 -0
  36. package/esm/table/selectable/useDragSelection.d.ts +1 -1
  37. package/esm/table/selectable/useDragSelection.js +4 -4
  38. package/esm/table/selectable/useSelectableRows.d.ts +2 -0
  39. package/esm/table/selectable/useSelectableRows.js +90 -43
  40. package/esm/table/selectable/utils/calculateSelectionState.js +3 -2
  41. package/esm/table/selectable/utils/getCurrentPageSelection.js +9 -4
  42. package/esm/table/selectable/utils/groupExclusions.d.ts +7 -0
  43. package/esm/table/selectable/utils/groupExclusions.js +55 -0
  44. package/esm/table/selectable/utils/isRowChecked.js +1 -1
  45. package/esm/table/table.js +6 -5
  46. package/esm/waiting/waiting.js +3 -1
  47. package/package.json +1 -4
  48. package/README.md.baseline +0 -1
@@ -88,6 +88,7 @@ const getCurrentPageSelection_1 = require("./utils/getCurrentPageSelection");
88
88
  const useDragSelection_1 = require("./useDragSelection");
89
89
  const getDragSelectionRows_1 = require("./utils/getDragSelectionRows");
90
90
  const showMoreRow_1 = require("../../commonHelpers/showMoreRow");
91
+ const groupExclusions_1 = require("./utils/groupExclusions");
91
92
  const getEmptySelection = () => ({
92
93
  all: false,
93
94
  selected: []
@@ -98,13 +99,14 @@ exports.isAllSelection = isAllSelection;
98
99
  exports.SelectableWrapperName = "Selectable";
99
100
  // Infinite scroll has no pages, so the header checkbox always means "select all" (never current page).
100
101
  const resolveCheckboxEmitsCurrentPage = (isInfinite, checkboxSelectsCurrentPage, turnOffSelectAll) => isInfinite ? false : checkboxSelectsCurrentPage !== null && checkboxSelectsCurrentPage !== void 0 ? checkboxSelectsCurrentPage : turnOffSelectAll;
101
- const getSelectionMenuState = (allSelected, selections, pageState) => allSelected ? selections.size === 0 ? interfaces_1.SelectionState.All : interfaces_1.SelectionState.Partial : pageState;
102
102
  const useSelectableRows = (columns, entities, isMobile, selectMode, setSelectMode, selectable, isInfinite = false, gridRef, expandedRows) => {
103
103
  var _a;
104
104
  const {
105
105
  translate
106
106
  } = (0, useLanguage_1.useLanguage)();
107
- const selections = (0, react_1.useMemo)(() => (0, getSelection_1.getSelections)(selectable === null || selectable === void 0 ? void 0 : selectable.selection), [selectable === null || selectable === void 0 ? void 0 : selectable.selection]);
107
+ const previousDatasetId = (0, react_1.useRef)(selectable === null || selectable === void 0 ? void 0 : selectable.selectionDatasetId);
108
+ const datasetWasReplaced = (selectable === null || selectable === void 0 ? void 0 : selectable.selectionDatasetId) !== undefined && previousDatasetId.current !== undefined && selectable.selectionDatasetId !== previousDatasetId.current;
109
+ const appliedSelections = (0, react_1.useMemo)(() => (0, getSelection_1.getSelections)(selectable === null || selectable === void 0 ? void 0 : selectable.selection), [selectable === null || selectable === void 0 ? void 0 : selectable.selection]);
108
110
  const [state, setState] = (0, react_1.useState)(interfaces_1.SelectionState.None);
109
111
  const checkboxVisible = (0, react_1.useMemo)(() => {
110
112
  const base = (selectable === null || selectable === void 0 ? void 0 : selectable.checkboxVisible) ? selectable.checkboxVisible : (selectable === null || selectable === void 0 ? void 0 : selectable.strategy) !== "nested" ? (_, isNested) => !isNested : () => true;
@@ -120,16 +122,19 @@ const useSelectableRows = (columns, entities, isMobile, selectMode, setSelectMod
120
122
  }
121
123
  if (selectable === null || selectable === void 0 ? void 0 : selectable.selectionLimit) {
122
124
  const limit = selectable.selectionLimit;
123
- return entity => selections.size >= limit && !selections.has(entity.id);
125
+ return entity => appliedSelections.size >= limit && !appliedSelections.has(entity.id);
124
126
  }
125
127
  return () => false;
126
- }, [selectable, selections]);
128
+ }, [selectable, appliedSelections]);
127
129
  // `checkRow` and `getCurrentPageSelection` read callbacks off `selectable`, so hand them the
128
130
  // wrapped ones to apply the same exclusions.
129
131
  const wrappedSelectable = (0, react_1.useMemo)(() => selectable ? Object.assign(Object.assign({}, selectable), {
130
132
  checkboxVisible,
131
133
  checkboxDisabled
132
134
  }) : undefined, [selectable, checkboxVisible, checkboxDisabled]);
135
+ const resolvedSelection = (0, react_1.useMemo)(() => (0, groupExclusions_1.resolveGroupExclusions)(entities, wrappedSelectable, datasetWasReplaced), [datasetWasReplaced, entities, wrappedSelectable]);
136
+ const selections = (0, react_1.useMemo)(() => (0, getSelection_1.getSelections)(resolvedSelection), [resolvedSelection]);
137
+ const excludedGroupIds = (0, react_1.useMemo)(() => new Set((selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions) && selectable.strategy === "nested" && (resolvedSelection === null || resolvedSelection === void 0 ? void 0 : resolvedSelection.all) ? resolvedSelection.excludedGroupIds : []), [selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions, selectable === null || selectable === void 0 ? void 0 : selectable.strategy, resolvedSelection === null || resolvedSelection === void 0 ? void 0 : resolvedSelection.all, resolvedSelection === null || resolvedSelection === void 0 ? void 0 : resolvedSelection.excludedGroupIds]);
133
138
  const checkboxPlaceholder = (0, react_1.useMemo)(() => {
134
139
  if ((selectable === null || selectable === void 0 ? void 0 : selectable.checkboxPlaceholder) !== undefined) {
135
140
  return selectable.checkboxPlaceholder;
@@ -144,21 +149,21 @@ const useSelectableRows = (columns, entities, isMobile, selectMode, setSelectMod
144
149
  var _a;
145
150
  return (0, exports.isAllSelection)((_a = selectable === null || selectable === void 0 ? void 0 : selectable.selection) === null || _a === void 0 ? void 0 : _a.all, selections.size, selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions);
146
151
  }, [(_a = selectable === null || selectable === void 0 ? void 0 : selectable.selection) === null || _a === void 0 ? void 0 : _a.all, selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions, selections.size]);
147
- const selectableState = (0, react_1.useMemo)(() => (0, calculateSelectionState_1.calculateSelectionState)(entities, selections, isAllSelected, checkboxVisible, checkboxDisabled, selectable === null || selectable === void 0 ? void 0 : selectable.strategy), [checkboxDisabled, checkboxVisible, entities, isAllSelected, selections, selectable === null || selectable === void 0 ? void 0 : selectable.strategy]);
148
- const selectionMenuState = getSelectionMenuState(isAllSelected, selections, selectableState);
152
+ const selectableState = (0, react_1.useMemo)(() => (0, calculateSelectionState_1.calculateSelectionState)(entities, new Set([...selections, ...excludedGroupIds]), isAllSelected, checkboxVisible, checkboxDisabled, selectable === null || selectable === void 0 ? void 0 : selectable.strategy), [checkboxDisabled, checkboxVisible, entities, isAllSelected, selections, excludedGroupIds, selectable === null || selectable === void 0 ? void 0 : selectable.strategy]);
149
153
  const firstColumnId = (0, react_1.useMemo)(() => columns.length > 0 ? columns[0].id : null, [columns]);
150
154
  (0, react_1.useEffect)(() => {
151
155
  if (selectable && selectable.selection) {
152
- const newSelections = new Set(selectable.selection.selected);
153
- const newState = (0, calculateSelectionState_1.calculateSelectionState)(entities, newSelections, isAllSelected, checkboxVisible, checkboxDisabled, selectable.strategy);
154
- setState(newState);
156
+ setState(selectableState);
155
157
  }
156
- }, [checkboxDisabled, checkboxVisible, entities, selectable, setSelectMode, selectable === null || selectable === void 0 ? void 0 : selectable.strategy, isAllSelected]);
158
+ }, [selectable, selectableState]);
157
159
  const getNewSelection = (0, react_1.useCallback)((id, checked) => {
158
160
  if (isAllSelected && !(selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions)) {
159
161
  const included = (0, getCurrentPageSelection_1.getCurrentPageSelection)(entities, wrappedSelectable);
160
162
  (0, checkRow_1.checkRow)(included, wrappedSelectable, entities, id, checked);
161
- return included;
163
+ return {
164
+ selected: included,
165
+ groups: new Set()
166
+ };
162
167
  }
163
168
  if (isAllSelected) {
164
169
  // MYG-142234: `selections` holds the ids excluded from the "all" selection.
@@ -166,22 +171,29 @@ const useSelectableRows = (columns, entities, isMobile, selectMode, setSelectMod
166
171
  // exclusion set - it must never be rebuilt from `entities` (the current page),
167
172
  // otherwise every other page's rows would silently fall out of the selection.
168
173
  const excluded = new Set(selections);
169
- (0, checkRow_1.checkRow)(excluded, wrappedSelectable, entities, id, !checked);
170
- return excluded;
174
+ const groups = new Set(excludedGroupIds);
175
+ (0, groupExclusions_1.checkExcludedRow)(excluded, groups, wrappedSelectable, entities, id, checked);
176
+ return {
177
+ selected: excluded,
178
+ groups
179
+ };
171
180
  }
172
181
  (0, checkRow_1.checkRow)(selections, wrappedSelectable, entities, id, checked);
173
- return selections;
174
- }, [entities, selectable, wrappedSelectable, selections, isAllSelected]);
182
+ return {
183
+ selected: selections,
184
+ groups: new Set()
185
+ };
186
+ }, [entities, selectable, wrappedSelectable, selections, excludedGroupIds, isAllSelected]);
175
187
  const onSelectListener = (0, react_1.useCallback)((id, checked) => {
176
188
  const wasAllSelected = !!(selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions) && isAllSelected;
177
- const newSelection = getNewSelection(id, checked);
189
+ const {
190
+ selected: newSelection,
191
+ groups
192
+ } = getNewSelection(id, checked);
178
193
  const newState = (0, calculateSelectionState_1.calculateSelectionState)(entities, newSelection, wasAllSelected, checkboxVisible, checkboxDisabled, selectable === null || selectable === void 0 ? void 0 : selectable.strategy);
179
194
  // MYG-142234: once selection spans "all" pages, unchecking a row must keep `all: true`
180
195
  // and only record the exclusion - it must not collapse to the current page's ids.
181
- const newValue = wasAllSelected ? {
182
- selected: [...newSelection],
183
- all: true
184
- } : {
196
+ const newValue = wasAllSelected ? (0, groupExclusions_1.createExcludedSelection)(newSelection, groups) : {
185
197
  selected: [...newSelection],
186
198
  all: newState === interfaces_1.SelectionState.All
187
199
  };
@@ -190,24 +202,43 @@ const useSelectableRows = (columns, entities, isMobile, selectMode, setSelectMod
190
202
  setSelectMode(newValue.all || newValue.selected.length > 0);
191
203
  }, [checkboxDisabled, checkboxVisible, entities, selectable, setSelectMode, setState, getNewSelection, isAllSelected]);
192
204
  const onBatchSelect = (0, react_1.useCallback)((rangeIds, select, base) => {
193
- const result = new Set(base);
205
+ const result = new Set(base.selected);
206
+ const groups = new Set(base.excludedGroupIds);
194
207
  const limit = selectable === null || selectable === void 0 ? void 0 : selectable.selectionLimit;
195
208
  const wasAllSelected = !!(selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions) && isAllSelected;
196
209
  for (const id of rangeIds) {
197
210
  if (!wasAllSelected && select && limit !== undefined && result.size >= limit) {
198
211
  break;
199
212
  }
200
- (0, checkRow_1.checkRow)(result, wrappedSelectable, entities, id, wasAllSelected ? !select : select);
213
+ if (wasAllSelected) {
214
+ (0, groupExclusions_1.checkExcludedRow)(result, groups, wrappedSelectable, entities, id, select);
215
+ } else {
216
+ (0, checkRow_1.checkRow)(result, wrappedSelectable, entities, id, select);
217
+ }
201
218
  }
202
219
  const newState = (0, calculateSelectionState_1.calculateSelectionState)(entities, result, wasAllSelected, checkboxVisible, checkboxDisabled, selectable === null || selectable === void 0 ? void 0 : selectable.strategy);
203
- const newValue = {
220
+ const newValue = wasAllSelected ? (0, groupExclusions_1.createExcludedSelection)(result, groups) : {
204
221
  selected: [...result],
205
- all: wasAllSelected || newState === interfaces_1.SelectionState.All
222
+ all: newState === interfaces_1.SelectionState.All
206
223
  };
207
224
  setState(newState);
208
225
  selectable === null || selectable === void 0 ? void 0 : selectable.onSelect(newValue);
209
226
  setSelectMode(newValue.all || newValue.selected.length > 0);
210
227
  }, [entities, selectable, wrappedSelectable, checkboxVisible, checkboxDisabled, setSelectMode, isAllSelected]);
228
+ // Persist group intent separately from concrete IDs so remounts and incremental batches agree.
229
+ (0, react_1.useEffect)(() => {
230
+ previousDatasetId.current = selectable === null || selectable === void 0 ? void 0 : selectable.selectionDatasetId;
231
+ }, [selectable === null || selectable === void 0 ? void 0 : selectable.selectionDatasetId]);
232
+ (0, react_1.useEffect)(() => {
233
+ const applied = selectable === null || selectable === void 0 ? void 0 : selectable.selection;
234
+ if (!applied || !resolvedSelection || !selectable.allowExclusions || !applied.all || selectable.strategy !== "nested") {
235
+ return;
236
+ }
237
+ const sameIds = (left = [], right = []) => left.length === right.length && left.every((id, index) => id === right[index]);
238
+ if (!sameIds(applied.selected, resolvedSelection.selected) || !sameIds(applied.excludedGroupIds, resolvedSelection.excludedGroupIds)) {
239
+ selectable.onSelect(resolvedSelection);
240
+ }
241
+ }, [selectable, resolvedSelection]);
211
242
  // Deliberately independent of the current selection, so the drag anchor survives the
212
243
  // selection changes a drag itself produces.
213
244
  const {
@@ -218,7 +249,12 @@ const useSelectableRows = (columns, entities, isMobile, selectMode, setSelectMod
218
249
  const entity = entityById.get(row.id);
219
250
  return !!entity && checkboxVisible(entity, row.isNested) && !checkboxDisabled(entity, row.isNested);
220
251
  }, [entityById, checkboxVisible, checkboxDisabled]);
221
- const getBaseSelection = (0, react_1.useCallback)(() => isAllSelected && !(selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions) ? (0, getCurrentPageSelection_1.getCurrentPageSelection)(entities, wrappedSelectable) : new Set(selections), [isAllSelected, selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions, entities, wrappedSelectable, selections]);
252
+ const getBaseSelection = (0, react_1.useCallback)(() => Object.assign({
253
+ all: isAllSelected,
254
+ selected: [...(isAllSelected && !(selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions) ? (0, getCurrentPageSelection_1.getCurrentPageSelection)(entities, wrappedSelectable) : selections)]
255
+ }, excludedGroupIds.size > 0 ? {
256
+ excludedGroupIds: [...excludedGroupIds]
257
+ } : {}), [isAllSelected, selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions, entities, wrappedSelectable, selections, excludedGroupIds]);
222
258
  const {
223
259
  onCheckboxMouseDown,
224
260
  suppressNextChangeRef
@@ -231,7 +267,7 @@ const useSelectableRows = (columns, entities, isMobile, selectMode, setSelectMod
231
267
  onSelectListener(id, checked);
232
268
  }, [onSelectListener, suppressNextChangeRef]);
233
269
  const onChangeSelectionState = (0, react_1.useCallback)(newState => {
234
- var _a, _b, _c;
270
+ var _a;
235
271
  if (newState === interfaces_1.SelectionState.All) {
236
272
  (selectable === null || selectable === void 0 ? void 0 : selectable.onSelect) && selectable.onSelect({
237
273
  selected: [],
@@ -250,17 +286,16 @@ const useSelectableRows = (columns, entities, isMobile, selectMode, setSelectMod
250
286
  }
251
287
  const newSelections = (0, getCurrentPageSelection_1.getCurrentPageSelection)(entities, wrappedSelectable);
252
288
  setState(newState);
253
- const previousSelections = (_b = (_a = selectable === null || selectable === void 0 ? void 0 : selectable.selection) === null || _a === void 0 ? void 0 : _a.selected) !== null && _b !== void 0 ? _b : [];
254
- const newValue = (selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions) && ((_c = selectable.selection) === null || _c === void 0 ? void 0 : _c.all) ? {
255
- selected: previousSelections.filter(id => !newSelections.has(id)),
256
- all: true
257
- } : {
289
+ const previousSelections = [...selections];
290
+ const groups = new Set(excludedGroupIds);
291
+ entities.forEach(entity => groups.delete(entity.id));
292
+ const newValue = (selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions) && ((_a = selectable.selection) === null || _a === void 0 ? void 0 : _a.all) ? (0, groupExclusions_1.createExcludedSelection)(new Set(previousSelections.filter(id => !newSelections.has(id))), groups) : {
258
293
  selected: [...new Set([...previousSelections, ...newSelections])],
259
294
  all: false
260
295
  };
261
296
  selectable === null || selectable === void 0 ? void 0 : selectable.onSelect(newValue);
262
297
  setSelectMode(newValue.all || newValue.selected.length > 0);
263
- }, [selectable, wrappedSelectable, entities, setSelectMode]);
298
+ }, [selectable, wrappedSelectable, entities, setSelectMode, selections, excludedGroupIds]);
264
299
  const checkboxTitle = (0, react_1.useCallback)(entity => (selectable === null || selectable === void 0 ? void 0 : selectable.checkboxTitle) ? selectable.checkboxTitle(entity) : firstColumnId ? entity[firstColumnId] || entity.id : entity.id, [firstColumnId, selectable]);
265
300
  const headerTitle = (0, react_1.useCallback)(() => {
266
301
  if (selectable === null || selectable === void 0 ? void 0 : selectable.headerTitle) {
@@ -276,7 +311,15 @@ const useSelectableRows = (columns, entities, isMobile, selectMode, setSelectMod
276
311
  // row must be evaluated through the SelectionState.All (exclusion-aware) branch of
277
312
  // isRowChecked - not the per-page `state`, which can be Partial/None for a page that
278
313
  // contains some of those exclusions.
279
- (entity, isNested) => (0, isRowChecked_1.isRowChecked)(selections, isAllSelected ? interfaces_1.SelectionState.All : state, selectable === null || selectable === void 0 ? void 0 : selectable.strategy, entity, isNested, checkboxVisible, checkboxDisabled), [selections, isAllSelected, state, selectable === null || selectable === void 0 ? void 0 : selectable.strategy, checkboxVisible, checkboxDisabled]);
314
+ (entity, isNested) => {
315
+ if (!isNested && isAllSelected && excludedGroupIds.has(entity.id)) {
316
+ return {
317
+ indeterminate: false,
318
+ checked: false
319
+ };
320
+ }
321
+ return (0, isRowChecked_1.isRowChecked)(selections, isAllSelected ? interfaces_1.SelectionState.All : state, selectable === null || selectable === void 0 ? void 0 : selectable.strategy, entity, isNested, checkboxVisible, checkboxDisabled);
322
+ }, [selections, excludedGroupIds, isAllSelected, state, selectable === null || selectable === void 0 ? void 0 : selectable.strategy, checkboxVisible, checkboxDisabled]);
280
323
  const hasSelectable = !!selectable;
281
324
  const selectableCheckboxSelectsCurrentPage = selectable === null || selectable === void 0 ? void 0 : selectable.checkboxSelectsCurrentPage;
282
325
  const selectableTurnOffSelectAll = selectable === null || selectable === void 0 ? void 0 : selectable.turnOffSelectAll;
@@ -342,13 +385,17 @@ const useSelectableRows = (columns, entities, isMobile, selectMode, setSelectMod
342
385
  // keep `all: true` - it must never be treated as an inclusion list, or
343
386
  // every other page's selection would be silently wiped out.
344
387
  const currentPageIds = (0, getCurrentPageSelection_1.getCurrentPageSelection)(entities, wrappedSelectable);
345
- const newExcluded = new Set([...selectable.selection.selected, ...currentPageIds]);
346
- const newState = (0, calculateSelectionState_1.calculateSelectionState)(entities, newExcluded, true, checkboxVisible, checkboxDisabled, selectable.strategy);
388
+ const newExcluded = new Set([...selections, ...currentPageIds]);
389
+ const groups = new Set(excludedGroupIds);
390
+ if (selectable.strategy === "nested") {
391
+ entities.forEach(entity => {
392
+ var _a;
393
+ if ((_a = (0, showMoreRow_1.getChildrenForSelection)(entity)) === null || _a === void 0 ? void 0 : _a.length) groups.add(entity.id);
394
+ });
395
+ }
396
+ const newState = (0, calculateSelectionState_1.calculateSelectionState)(entities, new Set([...newExcluded, ...groups]), true, checkboxVisible, checkboxDisabled, selectable.strategy);
347
397
  setState(newState);
348
- selectable.onSelect({
349
- selected: [...newExcluded],
350
- all: true
351
- });
398
+ selectable.onSelect((0, groupExclusions_1.createExcludedSelection)(newExcluded, groups));
352
399
  setSelectMode(true);
353
400
  } else {
354
401
  // Intentionally different from onSelect(None) ("Deselect all" menu item, which clears everything).
@@ -364,7 +411,7 @@ const useSelectableRows = (columns, entities, isMobile, selectMode, setSelectMod
364
411
  setSelectMode(remaining.length > 0);
365
412
  }
366
413
  },
367
- calculateSelectionState: () => selectionMenuState,
414
+ calculateSelectionState: () => isAllSelected ? selections.size === 0 && excludedGroupIds.size === 0 ? interfaces_1.SelectionState.All : interfaces_1.SelectionState.Partial : selections.size === 0 ? interfaces_1.SelectionState.None : selectableState === interfaces_1.SelectionState.CurrentPage ? interfaces_1.SelectionState.CurrentPage : interfaces_1.SelectionState.Partial,
368
415
  checkboxInHeader: checkboxInHeaderValue,
369
416
  header: selectableHeader,
370
417
  onSelect: newState => {
@@ -377,7 +424,7 @@ const useSelectableRows = (columns, entities, isMobile, selectMode, setSelectMod
377
424
  return cell;
378
425
  }
379
426
  };
380
- }, [checkboxDisabled, checkboxTitle, checkboxVisible, headerTitle, onChangeSelectionState, onCheckboxChange, onCheckboxMouseDown, hasSelectable, checkboxEmitsCurrentPage, checkboxInHeaderValue, selectableHeader, selectableTurnOffSelectAll, isInfinite, selectable, wrappedSelectable, selectionMenuState, state, isMobile, checkboxPlaceholder, isEntityChecked, entities, setSelectMode]);
427
+ }, [checkboxDisabled, checkboxTitle, checkboxVisible, headerTitle, onChangeSelectionState, onCheckboxChange, onCheckboxMouseDown, hasSelectable, checkboxEmitsCurrentPage, checkboxInHeaderValue, selectableHeader, selectableTurnOffSelectAll, isInfinite, selectable, wrappedSelectable, selectableState, isAllSelected, selections, excludedGroupIds, state, isMobile, checkboxPlaceholder, isEntityChecked, entities, setSelectMode]);
381
428
  const selectableFeedWrapper = (0, react_1.useCallback)((primaryData, secondaryData, entity, isNested) => {
382
429
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
383
430
  if (!entity) {
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.calculateSelectionState = void 0;
4
4
  const showMoreRow_1 = require("../../../commonHelpers/showMoreRow");
5
5
  const interfaces_1 = require("../interfaces");
6
+ const areAllLoading = (children) => children.every(child => child.isLoading);
6
7
  const calculateSelectionState = (entities, selections, all, checkboxVisible, checkboxDisabled, strategy) => {
7
8
  if (selections.size === 0) {
8
9
  return all ? interfaces_1.SelectionState.All : interfaces_1.SelectionState.None;
@@ -12,7 +13,7 @@ const calculateSelectionState = (entities, selections, all, checkboxVisible, che
12
13
  // otherwise-fully-selected set (see ISelectionValue), rather than included ids.
13
14
  const isExcluded = (entity) => {
14
15
  const children = strategy === "nested" ? (0, showMoreRow_1.getChildrenForSelection)(entity) : undefined;
15
- if (children && children.length > 0) {
16
+ if (children && children.length > 0 && !areAllLoading(children)) {
16
17
  return children.every(child => {
17
18
  if (!checkboxVisible(child, true)) {
18
19
  return true;
@@ -27,7 +28,7 @@ const calculateSelectionState = (entities, selections, all, checkboxVisible, che
27
28
  };
28
29
  const isPartiallyExcluded = (entity) => {
29
30
  const children = strategy === "nested" ? (0, showMoreRow_1.getChildrenForSelection)(entity) : undefined;
30
- if (children && children.length > 0) {
31
+ if (children && children.length > 0 && !areAllLoading(children)) {
31
32
  return children.some(child => selections.has(child.id));
32
33
  }
33
34
  return selections.has(entity.id);
@@ -2,17 +2,22 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getCurrentPageSelection = void 0;
4
4
  const showMoreRow_1 = require("../../../commonHelpers/showMoreRow");
5
+ const isLoadingRow = (entity) => !!entity.isLoading;
5
6
  const getCurrentPageSelection = (entities, selectable) => {
6
7
  const newSelections = new Set();
7
8
  entities.forEach(entity => {
8
- const isVisible = !entity.isLoading && !(0, showMoreRow_1.isShowMoreRow)(entity) && ((selectable === null || selectable === void 0 ? void 0 : selectable.checkboxVisible) ? selectable.checkboxVisible(entity) : true);
9
+ if (isLoadingRow(entity) || (0, showMoreRow_1.isShowMoreRow)(entity)) {
10
+ return;
11
+ }
12
+ const isVisible = (selectable === null || selectable === void 0 ? void 0 : selectable.checkboxVisible) ? selectable.checkboxVisible(entity) : true;
9
13
  const isEnabled = !(selectable === null || selectable === void 0 ? void 0 : selectable.checkboxDisabled) || !selectable.checkboxDisabled(entity);
10
14
  const children = (selectable === null || selectable === void 0 ? void 0 : selectable.strategy) === "nested" ? (0, showMoreRow_1.getChildrenForSelection)(entity) : undefined;
11
15
  if (children && children.length > 0) {
12
16
  children.forEach(child => {
13
- const isChildVisible = !child.isLoading &&
14
- !(0, showMoreRow_1.isShowMoreRow)(child) &&
15
- ((selectable === null || selectable === void 0 ? void 0 : selectable.checkboxVisible) ? selectable.checkboxVisible(child, true) : true);
17
+ if (isLoadingRow(child) || (0, showMoreRow_1.isShowMoreRow)(child)) {
18
+ return;
19
+ }
20
+ const isChildVisible = (selectable === null || selectable === void 0 ? void 0 : selectable.checkboxVisible) ? selectable.checkboxVisible(child, true) : true;
16
21
  const isChildEnabled = !(selectable === null || selectable === void 0 ? void 0 : selectable.checkboxDisabled) || !selectable.checkboxDisabled(child, true);
17
22
  if (isChildVisible && isChildEnabled) {
18
23
  newSelections.add(child.id);
@@ -0,0 +1,7 @@
1
+ import { IEntityWithId } from "../../../commonHelpers/entity";
2
+ import { IRowEntity } from "../../../dataGrid/row/row";
3
+ import { ISelectionValue } from "../interfaces";
4
+ import { ISelectableRowsOptions } from "../useSelectableRows";
5
+ export declare const createExcludedSelection: (selected: Set<string>, groups: Set<string>) => ISelectionValue;
6
+ export declare const resolveGroupExclusions: <T extends IRowEntity<N>, N extends IEntityWithId>(entities: T[], selectable: ISelectableRowsOptions<T, N> | undefined, pruneMissingGroups?: boolean) => ISelectionValue | undefined;
7
+ export declare const checkExcludedRow: <T extends IRowEntity<N>, N extends IEntityWithId>(selected: Set<string>, groups: Set<string>, selectable: ISelectableRowsOptions<T, N> | undefined, entities: T[], id: string, checked: boolean) => void;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkExcludedRow = exports.resolveGroupExclusions = exports.createExcludedSelection = void 0;
4
+ const showMoreRow_1 = require("../../../commonHelpers/showMoreRow");
5
+ const checkRow_1 = require("./checkRow");
6
+ const getCurrentPageSelection_1 = require("./getCurrentPageSelection");
7
+ const createExcludedSelection = (selected, groups) => (Object.assign({ all: true, selected: [...selected] }, (groups.size > 0 ? { excludedGroupIds: [...groups] } : {})));
8
+ exports.createExcludedSelection = createExcludedSelection;
9
+ const resolveGroupExclusions = (entities, selectable, pruneMissingGroups = false) => {
10
+ var _a, _b;
11
+ const selection = selectable === null || selectable === void 0 ? void 0 : selectable.selection;
12
+ if (!(selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions) || !(selection === null || selection === void 0 ? void 0 : selection.all) || selectable.strategy !== "nested") {
13
+ return selection;
14
+ }
15
+ const selected = new Set(selection.selected);
16
+ const groups = new Set(selection.excludedGroupIds);
17
+ for (const entity of entities) {
18
+ if (selected.has(entity.id) && ((_a = (0, showMoreRow_1.getChildrenForSelection)(entity)) === null || _a === void 0 ? void 0 : _a.length)) {
19
+ groups.add(entity.id);
20
+ }
21
+ }
22
+ for (const id of groups) {
23
+ const entity = entities.find(item => item.id === id);
24
+ if (!entity && pruneMissingGroups) {
25
+ selected.delete(id);
26
+ groups.delete(id);
27
+ continue;
28
+ }
29
+ if (!entity)
30
+ continue;
31
+ if ((_b = (0, showMoreRow_1.getChildrenForSelection)(entity)) === null || _b === void 0 ? void 0 : _b.length) {
32
+ selected.delete(id);
33
+ }
34
+ for (const childId of (0, getCurrentPageSelection_1.getCurrentPageSelection)([entity], selectable)) {
35
+ selected.add(childId);
36
+ }
37
+ }
38
+ return (0, exports.createExcludedSelection)(selected, groups);
39
+ };
40
+ exports.resolveGroupExclusions = resolveGroupExclusions;
41
+ const checkExcludedRow = (selected, groups, selectable, entities, id, checked) => {
42
+ var _a, _b;
43
+ if ((selectable === null || selectable === void 0 ? void 0 : selectable.strategy) === "nested") {
44
+ const entity = entities.find(item => item.id === id);
45
+ if (checked) {
46
+ groups.delete(id);
47
+ selected.delete(id);
48
+ // Selecting a child overrides the whole-group intent, retaining the other concrete exclusions.
49
+ for (const parent of entities) {
50
+ if ((_a = (0, showMoreRow_1.getChildrenForSelection)(parent)) === null || _a === void 0 ? void 0 : _a.some(child => child.id === id)) {
51
+ groups.delete(parent.id);
52
+ }
53
+ }
54
+ }
55
+ else if (entity && ((_b = (0, showMoreRow_1.getChildrenForSelection)(entity)) === null || _b === void 0 ? void 0 : _b.length)) {
56
+ groups.add(id);
57
+ }
58
+ }
59
+ (0, checkRow_1.checkRow)(selected, selectable, entities, id, !checked);
60
+ };
61
+ exports.checkExcludedRow = checkExcludedRow;
@@ -27,7 +27,7 @@ const isRowChecked = (selections, state, strategy, entity, isNested, checkboxVis
27
27
  if (children && children.length > 0) {
28
28
  const selectableChildren = filterSelectable(children, checkboxVisible, checkboxDisabled);
29
29
  if (selectableChildren.length === 0) {
30
- return { indeterminate: false, checked: true };
30
+ return { indeterminate: false, checked: !selections.has(entity.id) };
31
31
  }
32
32
  const allChildrenChecked = selectableChildren.every(child => !selections.has(child.id));
33
33
  const someChildrenChecked = selectableChildren.some(child => !selections.has(child.id));
@@ -148,6 +148,7 @@ const useTableChildren_1 = require("./children/useTableChildren");
148
148
  const deviceType_1 = require("../commonHelpers/hooks/deviceType");
149
149
  const useMobile_1 = require("../commonHelpers/hooks/useMobile");
150
150
  const getCurrentPageSelection_1 = require("./selectable/utils/getCurrentPageSelection");
151
+ const groupExclusions_1 = require("./selectable/utils/groupExclusions");
151
152
  const tableFullscreen_1 = require("./children/tableFullscreen");
152
153
  const tableBulkActions_1 = require("./children/tableBulkActions");
153
154
  const tablePagination_1 = require("./children/tablePagination");
@@ -235,7 +236,7 @@ const TableInner = ({
235
236
  active,
236
237
  onClick
237
238
  }) => {
238
- var _a, _b, _c, _d, _e;
239
+ var _a, _b, _c, _d, _e, _f;
239
240
  const isMobile = (0, useMobile_1.useMobile)();
240
241
  const {
241
242
  translate
@@ -329,15 +330,15 @@ const TableInner = ({
329
330
  var _a, _b;
330
331
  return (0, useSelectableRows_1.isAllSelection)((_a = selectable === null || selectable === void 0 ? void 0 : selectable.selection) === null || _a === void 0 ? void 0 : _a.all, (_b = selectable === null || selectable === void 0 ? void 0 : selectable.selection) === null || _b === void 0 ? void 0 : _b.selected.length, selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions);
331
332
  }, [(_c = selectable === null || selectable === void 0 ? void 0 : selectable.selection) === null || _c === void 0 ? void 0 : _c.all, selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions, (_d = selectable === null || selectable === void 0 ? void 0 : selectable.selection) === null || _d === void 0 ? void 0 : _d.selected.length]);
333
+ const resolvedSelection = (0, react_1.useMemo)(() => (0, groupExclusions_1.resolveGroupExclusions)(entities, selectable), [entities, selectable]);
332
334
  const selectionQty = (0, react_1.useMemo)(() => {
333
- var _a;
334
335
  if (!selectable) {
335
336
  return 0;
336
337
  }
337
- const ids = new Set((_a = selectable.selection) === null || _a === void 0 ? void 0 : _a.selected);
338
+ const ids = new Set(resolvedSelection === null || resolvedSelection === void 0 ? void 0 : resolvedSelection.selected);
338
339
  const base = entities.length > 0 ? entities : loadingEntities;
339
340
  return allSelected ? [...(0, getCurrentPageSelection_1.getCurrentPageSelection)(base, selectable)].filter(id => !selectable.allowExclusions || !ids.has(id)).length : ids.size;
340
- }, [selectable, entities, loadingEntities, allSelected]);
341
+ }, [selectable, resolvedSelection, entities, loadingEntities, allSelected]);
341
342
  const clearSelection = (0, react_1.useCallback)(() => {
342
343
  setSelectMode(false);
343
344
  selectable === null || selectable === void 0 ? void 0 : selectable.onSelect({
@@ -363,7 +364,7 @@ const TableInner = ({
363
364
  detailPanel,
364
365
  footer,
365
366
  other
366
- } = (0, useTableChildren_1.useTableChildren)(children, isMobile, selectionQty, allSelected, clearSelection, activeId, gridRef, onCloseDetailPanel, isInfinite, entities.length, (selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions) && ((_e = selectable.selection) === null || _e === void 0 ? void 0 : _e.all) ? new Set(selectable.selection.selected).size : 0, (selectable === null || selectable === void 0 ? void 0 : selectable.strategy) !== "nested" && !(selectable === null || selectable === void 0 ? void 0 : selectable.checkboxDisabled) && !(selectable === null || selectable === void 0 ? void 0 : selectable.checkboxVisible), !!feedEndCallback || !!isLoading);
367
+ } = (0, useTableChildren_1.useTableChildren)(children, isMobile, selectionQty, allSelected, clearSelection, activeId, gridRef, onCloseDetailPanel, isInfinite, entities.length, (selectable === null || selectable === void 0 ? void 0 : selectable.allowExclusions) && (resolvedSelection === null || resolvedSelection === void 0 ? void 0 : resolvedSelection.all) ? new Set(resolvedSelection.selected).size + ((_f = (_e = resolvedSelection.excludedGroupIds) === null || _e === void 0 ? void 0 : _e.length) !== null && _f !== void 0 ? _f : 0) : 0, (selectable === null || selectable === void 0 ? void 0 : selectable.strategy) !== "nested" && !(selectable === null || selectable === void 0 ? void 0 : selectable.checkboxDisabled) && !(selectable === null || selectable === void 0 ? void 0 : selectable.checkboxVisible), !!feedEndCallback || !!isLoading);
367
368
  const hasDetailPanel = !!detailPanel;
368
369
  const shouldEnableRowClick = hasDetailPanel || !!onClick;
369
370
  const activeIdForRow = shouldEnableRowClick ? activeId : undefined;
@@ -21,7 +21,9 @@ const Waiting = ({ isLoading = false, delay = 400, hideOverlay = false, classNam
21
21
  // its coverage layer to the parent's scrollHeight would incorrectly block clicks below the spinner
22
22
  // and grow the parent's own scrollHeight on every re-measure (a feedback loop).
23
23
  const isInline = className.split(/\s+/).includes("zen-waiting--inline");
24
- const canMeasureCoverage = showWaiting && !isInline;
24
+ // Callers using hideOverlay intentionally keep the surrounding controls interactive (for
25
+ // example, the MiniMap address textarea while its preview is loading).
26
+ const canMeasureCoverage = showWaiting && !isInline && !hideOverlay;
25
27
  const measureCoverage = (0, react_1.useCallback)(() => {
26
28
  var _a;
27
29
  const parent = (_a = waitingRef.current) === null || _a === void 0 ? void 0 : _a.parentElement;
@@ -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 {};
@@ -81,21 +81,43 @@ export const sortDropdownItemArray = (arr, sortFn) => {
81
81
  }
82
82
  return arr;
83
83
  };
84
+ /**
85
+ * An item is half selected when at least one of its direct children is selected — the state its checkbox
86
+ * shows as indeterminate (see `prepareComboOptionsForList`). `selectedSet` is passed in so the sort can use
87
+ * its own snapshot of the selection instead of the live one.
88
+ */
89
+ export const hasSelectedChild = (groupsMap, itemId, selectedSet) => {
90
+ const group = groupsMap[itemId];
91
+ return group ? getChildList(group.children).some(child => selectedSet.has(child.id)) : false;
92
+ };
84
93
  /**
85
94
  * Floats selected items to the top of the list while preserving the relative order within each
86
- * group (selected and unselected). When `selectedIds` is empty the original array is returned
87
- * unchanged (no allocation). Uses a single-pass partition to avoid traversing `items` twice.
95
+ * group. The order is: selected items that can be toggled, then selected items that are disabled
96
+ * (so a newly selected item is never pushed down by disabled ones), then everything else. An item
97
+ * counts as selected when its own id is in `selectedIds` or when `isPartiallySelected` returns true
98
+ * for it, so half selected items sit with the selected ones. When `selectedIds` is empty the original
99
+ * array is returned unchanged (no allocation). Uses a single-pass partition to avoid traversing
100
+ * `items` more than once.
88
101
  */
89
- export const applySortSelectedToTop = (items, selectedIds) => {
102
+ export const applySortSelectedToTop = (items, selectedIds, { isPartiallySelected, isDisabled } = {}) => {
90
103
  if (!selectedIds.length)
91
104
  return items;
92
105
  const selectedSet = new Set(selectedIds);
93
106
  const selected = [];
107
+ const selectedDisabled = [];
94
108
  const unselected = [];
95
109
  for (const item of items) {
96
- (selectedSet.has(item.id) ? selected : unselected).push(item);
110
+ if (!selectedSet.has(item.id) && !(isPartiallySelected === null || isPartiallySelected === void 0 ? void 0 : isPartiallySelected(item))) {
111
+ unselected.push(item);
112
+ }
113
+ else if (isDisabled === null || isDisabled === void 0 ? void 0 : isDisabled(item)) {
114
+ selectedDisabled.push(item);
115
+ }
116
+ else {
117
+ selected.push(item);
118
+ }
97
119
  }
98
- return [...selected, ...unselected];
120
+ return [...selected, ...selectedDisabled, ...unselected];
99
121
  };
100
122
  export const createSingleListOptions = (stateObj, translate, listItem) => {
101
123
  const preparedOptions = listItem && listItem.length
@@ -8,7 +8,7 @@ import { getInitialState, stateReducer, ROOT_GROUP_ID } from "./stateReducer/sta
8
8
  import { StateActionType } from "./stateReducer/stateActionType";
9
9
  import { FOCUSABLE_SELECTOR } from "../utils/focusableSelector";
10
10
  import { useEscape } from "../commonHelpers/hooks/useEscape";
11
- import { applySortSelectedToTop, checkIsDataProblem, compareObjectsArrays, compareStringsArrays, createListDataOptions, getDataForChange, getListDataWithDisabled, getSelectedCount, getStringFromAllSelected, getStringFromSelected, isAllChildrenSelected, isAllItemsHasOneParent, isEveryItemSelected, prepareSelectedIdsToItems, sortDropdownItemArray } from "./dropdownHelper";
11
+ import { applySortSelectedToTop, hasSelectedChild, checkIsDataProblem, compareObjectsArrays, compareStringsArrays, createListDataOptions, getDataForChange, getListDataWithDisabled, getSelectedCount, getStringFromAllSelected, getStringFromSelected, isAllChildrenSelected, isAllItemsHasOneParent, isEveryItemSelected, prepareSelectedIdsToItems, sortDropdownItemArray } from "./dropdownHelper";
12
12
  import { DropdownTrigger } from "./dropdownTrigger";
13
13
  import { isFocusable } from "../dialog/dialogHelpers";
14
14
  import { useLanguage } from "../utils/localization/useLanguage";
@@ -1147,20 +1147,36 @@ export const DropdownRaw = props => {
1147
1147
  // hasApplyButton=true: uses globalSelectedIds (committed selection) — reordering
1148
1148
  // only takes effect after the user clicks "Apply".
1149
1149
  const idsForSort = hasApplyButton ? state.globalSelectedIds : state.sortedTopSnapshot;
1150
- const floated = sortSelectedToTop ? applySortSelectedToTop(children, idsForSort) : children;
1151
- rawItems = floated.slice(0, listLimit);
1152
1150
  if (sortSelectedToTop) {
1151
+ // Half selected items (a direct child is selected, shown as indeterminate) float with the selected ones.
1152
+ // Disabled selected items follow the selectable ones; in a nested list `disabled` is not enforced (see createOptions).
1153
1153
  const selectedSet = new Set(idsForSort);
1154
- pinnedCount = rawItems.filter(item => selectedSet.has(item.id)).length;
1154
+ const isPartiallySelected = item => hasSelectedChild(state.groupsMap, item.id, selectedSet);
1155
+ const isDisabled = item => Boolean(item.disabled) && !state.isNestedList;
1156
+ rawItems = applySortSelectedToTop(children, idsForSort, {
1157
+ isPartiallySelected,
1158
+ isDisabled
1159
+ }).slice(0, listLimit);
1160
+ pinnedCount = rawItems.filter(item => selectedSet.has(item.id) || isPartiallySelected(item)).length;
1161
+ } else {
1162
+ rawItems = children.slice(0, listLimit);
1155
1163
  }
1156
1164
  }
1157
1165
  const listData = createListDataOptions(state, translate, rawItems, multiselect, currentAllSelected);
1158
1166
  // Explicit bounds check (rather than relying on CheckboxListWithAction's own
1159
1167
  // `ind < optionsArr.length - 1` guard) keeps the "don't draw a trailing divider"
1160
1168
  // intent local to where the boundary is computed.
1161
- const currentListData = multiselect && sortSelectedToTop && pinnedCount > 0 && pinnedCount < listData.length ? listData.map((opt, ind) => ind === pinnedCount - 1 ? Object.assign(Object.assign({}, opt), {
1162
- divider: true
1163
- }) : opt) : listData;
1169
+ // Floated items drop their own dividers, so the boundary divider is the only one drawn between them and the rest.
1170
+ const currentListData = multiselect && sortSelectedToTop && pinnedCount > 0 && pinnedCount < listData.length ? listData.map((opt, ind) => {
1171
+ if (ind === pinnedCount - 1) {
1172
+ return Object.assign(Object.assign({}, opt), {
1173
+ divider: true
1174
+ });
1175
+ }
1176
+ return ind < pinnedCount && opt.divider ? Object.assign(Object.assign({}, opt), {
1177
+ divider: false
1178
+ }) : opt;
1179
+ }) : listData;
1164
1180
  return _jsx(DropdownList, {
1165
1181
  onBackButtonClick: handleBackButtonClick,
1166
1182
  onSelectAllClick: isSearchMode ? handleSelectAllInSearch : handleSelectAllClick,