@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.
- package/README.md +26 -29
- package/dist/dataGrid/withSelectableRows/components/bulkActions/bulkActions.d.ts +1 -1
- package/dist/dropdownRaw/dropdownHelper.d.ts +20 -3
- package/dist/dropdownRaw/dropdownHelper.js +29 -6
- package/dist/dropdownRaw/dropdownRaw.js +22 -6
- package/dist/header/components/collapsedItemsControl/collapsedItemsControl.js +1 -1
- package/dist/header/headerButton.d.ts +1 -2
- package/dist/header/headerButton.js +1 -1
- package/dist/index.css +2 -2
- package/dist/pillExpandable/pillExpandable.js +3 -1
- package/dist/pillExpandable/pillExpandableSimple.d.ts +2 -1
- package/dist/pillExpandable/pillExpandableSimple.js +2 -2
- package/dist/table/selectable/interfaces.d.ts +6 -0
- package/dist/table/selectable/useDragSelection.d.ts +1 -1
- package/dist/table/selectable/useDragSelection.js +4 -4
- package/dist/table/selectable/useSelectableRows.d.ts +2 -0
- package/dist/table/selectable/useSelectableRows.js +88 -41
- package/dist/table/selectable/utils/calculateSelectionState.js +3 -2
- package/dist/table/selectable/utils/getCurrentPageSelection.js +9 -4
- package/dist/table/selectable/utils/groupExclusions.d.ts +7 -0
- package/dist/table/selectable/utils/groupExclusions.js +61 -0
- package/dist/table/selectable/utils/isRowChecked.js +1 -1
- package/dist/table/table.js +6 -5
- package/esm/dataGrid/withSelectableRows/components/bulkActions/bulkActions.d.ts +1 -1
- package/esm/dropdownRaw/dropdownHelper.d.ts +20 -3
- package/esm/dropdownRaw/dropdownHelper.js +27 -5
- package/esm/dropdownRaw/dropdownRaw.js +23 -7
- package/esm/header/components/collapsedItemsControl/collapsedItemsControl.js +1 -1
- package/esm/header/headerButton.d.ts +1 -2
- package/esm/header/headerButton.js +1 -1
- package/esm/pillExpandable/pillExpandable.js +3 -1
- package/esm/pillExpandable/pillExpandableSimple.d.ts +2 -1
- package/esm/pillExpandable/pillExpandableSimple.js +2 -2
- package/esm/table/selectable/interfaces.d.ts +6 -0
- package/esm/table/selectable/useDragSelection.d.ts +1 -1
- package/esm/table/selectable/useDragSelection.js +4 -4
- package/esm/table/selectable/useSelectableRows.d.ts +2 -0
- package/esm/table/selectable/useSelectableRows.js +90 -43
- package/esm/table/selectable/utils/calculateSelectionState.js +3 -2
- package/esm/table/selectable/utils/getCurrentPageSelection.js +9 -4
- package/esm/table/selectable/utils/groupExclusions.d.ts +7 -0
- package/esm/table/selectable/utils/groupExclusions.js +55 -0
- package/esm/table/selectable/utils/isRowChecked.js +1 -1
- package/esm/table/table.js +6 -5
- package/package.json +1 -4
- 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
|
|
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 =>
|
|
125
|
+
return entity => appliedSelections.size >= limit && !appliedSelections.has(entity.id);
|
|
124
126
|
}
|
|
125
127
|
return () => false;
|
|
126
|
-
}, [selectable,
|
|
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
|
-
|
|
153
|
-
const newState = (0, calculateSelectionState_1.calculateSelectionState)(entities, newSelections, isAllSelected, checkboxVisible, checkboxDisabled, selectable.strategy);
|
|
154
|
-
setState(newState);
|
|
156
|
+
setState(selectableState);
|
|
155
157
|
}
|
|
156
|
-
}, [
|
|
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
|
|
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
|
-
|
|
170
|
-
|
|
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
|
|
174
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
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)(() =>
|
|
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
|
|
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 =
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
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) =>
|
|
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([...
|
|
346
|
-
const
|
|
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: () =>
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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:
|
|
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));
|
package/dist/table/table.js
CHANGED
|
@@ -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(
|
|
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 =
|
|
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;
|
|
@@ -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
|
-
/**
|
|
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
|
|
18
|
-
*
|
|
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
|
|
87
|
-
*
|
|
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) ?
|
|
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
|
-
|
|
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
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
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,
|
|
@@ -102,7 +102,7 @@ export const PageToolbarCollapsedItemsControl = ({
|
|
|
102
102
|
rel: child.props.rel,
|
|
103
103
|
icon: child.props.icon,
|
|
104
104
|
iconOpen: child.props.iconOpen,
|
|
105
|
-
destructive: child.props.
|
|
105
|
+
destructive: child.props.type === ButtonType.Destructive || child.props.type === ButtonType.TertiaryDestructive
|
|
106
106
|
});
|
|
107
107
|
items.push(el);
|
|
108
108
|
}
|