1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Only group delete visible items (#4798)

This commit is contained in:
Sebastian Malton 2022-02-07 10:40:20 -05:00 committed by GitHub
parent 06ec1b39a4
commit 4abe0d6c20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 57 additions and 45 deletions

View File

@ -21,7 +21,7 @@ export abstract class ItemStore<Item extends ItemObject> {
@observable isLoading = false;
@observable isLoaded = false;
@observable items = observable.array<Item>([], { deep: false });
@observable selectedItemsIds = observable.map<string, boolean>();
@observable selectedItemsIds = observable.set<string>();
constructor() {
makeObservable(this);
@ -29,7 +29,11 @@ export abstract class ItemStore<Item extends ItemObject> {
}
@computed get selectedItems(): Item[] {
return this.items.filter(item => this.selectedItemsIds.get(item.getId()));
return this.pickOnlySelected(this.items);
}
public pickOnlySelected(items: Item[]): Item[] {
return items.filter(item => this.selectedItemsIds.has(item.getId()));
}
public getItems(): Item[] {
@ -152,12 +156,12 @@ export abstract class ItemStore<Item extends ItemObject> {
}
isSelected(item: Item) {
return !!this.selectedItemsIds.get(item.getId());
return this.selectedItemsIds.has(item.getId());
}
@action
select(item: Item) {
this.selectedItemsIds.set(item.getId(), true);
this.selectedItemsIds.add(item.getId());
}
@action
@ -207,6 +211,8 @@ export abstract class ItemStore<Item extends ItemObject> {
async removeSelectedItems?(): Promise<any>;
async removeItems?(items: Item[]): Promise<void>;
* [Symbol.iterator]() {
yield* this.items;
}

View File

@ -369,6 +369,10 @@ export abstract class KubeObjectStore<T extends KubeObject> extends ItemStore<T>
return Promise.all(this.selectedItems.map(this.remove));
}
async removeItems(items: T[]) {
await Promise.all(items.map(this.remove));
}
// collect items from watch-api events to avoid UI blowing up with huge streams of data
protected eventsBuffer = observable.array<IKubeWatchEvent<KubeJsonApiData>>([], { deep: false });

View File

@ -134,10 +134,12 @@ class NonInjectedHelmReleases extends Component<Dependencies & Props> {
return releases.get().filter((release) => release.isSelected);
},
removeSelectedItems() {
return Promise.all(
releases.get().filter((release) => release.isSelected).map((release) => release.delete()),
);
pickOnlySelected: (releases) => {
return releases.filter(release => release.isSelected);
},
removeItems: async (releases) => {
await Promise.all(releases.map(release => release.delete()));
},
} as ItemStore<RemovableHelmRelease>;

View File

@ -136,19 +136,27 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
}
@boundMethod
removeItemsDialog() {
removeItemsDialog(selectedItems: I[]) {
const { customizeRemoveDialog, store } = this.props;
const { selectedItems, removeSelectedItems } = store;
const visibleMaxNamesCount = 5;
const selectedNames = selectedItems.map(ns => ns.getName()).slice(0, visibleMaxNamesCount).join(", ");
const dialogCustomProps = customizeRemoveDialog ? customizeRemoveDialog(selectedItems) : {};
const selectedCount = selectedItems.length;
const tailCount = selectedCount > visibleMaxNamesCount ? selectedCount - visibleMaxNamesCount : 0;
const tail = tailCount > 0 ? <>, and <b>{tailCount}</b> more</> : null;
const message = selectedCount <= 1 ? <p>Remove item <b>{selectedNames}</b>?</p> : <p>Remove <b>{selectedCount}</b> items <b>{selectedNames}</b>{tail}?</p>;
const tailCount = selectedCount > visibleMaxNamesCount
? selectedCount - visibleMaxNamesCount
: 0;
const tail = tailCount > 0
? <>, and <b>{tailCount}</b> more</>
: null;
const message = selectedCount <= 1
? <p>Remove item <b>{selectedNames}</b>?</p>
: <p>Remove <b>{selectedCount}</b> items <b>{selectedNames}</b>{tail}?</p>;
const onConfirm = store.removeItems
? () => store.removeItems(selectedItems)
: store.removeSelectedItems;
ConfirmDialog.open({
ok: removeSelectedItems,
ok: onConfirm,
labelOk: "Remove",
message,
...dialogCustomProps,
@ -225,10 +233,12 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
render() {
const {
store, hasDetailsView, addRemoveButtons = {}, virtual, sortingCallbacks,
detailsItem, className, tableProps = {}, tableId,
detailsItem, className, tableProps = {}, tableId, getItems,
} = this.props;
const selectedItemId = detailsItem && detailsItem.getId();
const classNames = cssNames(className, "box", "grow", ThemeStore.getInstance().activeTheme.type);
const items = getItems();
const selectedItems = store.pickOnlySelected(items);
return (
<div className="items box grow flex column">
@ -238,7 +248,7 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
selectable={hasDetailsView}
sortable={sortingCallbacks}
getTableRow={this.getRow}
items={this.props.getItems()}
items={items}
selectedItemId={selectedItemId}
noItems={this.renderNoItems()}
className={classNames}
@ -252,9 +262,11 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
{() => (
<AddRemoveButtons
onRemove={
store.selectedItems.length ? this.removeItemsDialog : null
(store.removeItems || store.removeSelectedItems) && selectedItems.length > 0
? () => this.removeItemsDialog(selectedItems)
: null
}
removeTooltip={`Remove selected items (${store.selectedItems.length})`}
removeTooltip={`Remove selected items (${selectedItems.length})`}
{...addRemoveButtons}
/>
)}

View File

@ -13,7 +13,7 @@ import { boundMethod, cssNames, IClassName, noop, StorageHelper } from "../../ut
import type { AddRemoveButtonsProps } from "../add-remove-buttons";
import type { ItemObject, ItemStore } from "../../../common/item.store";
import type { SearchInputUrlProps } from "../input";
import { Filter, FilterType, pageFilters } from "./page-filters.store";
import { FilterType, pageFilters } from "./page-filters.store";
import { PageFiltersList } from "./page-filters-list";
import type { NamespaceStore } from "../+namespaces/namespace-store/namespace.store";
import namespaceStoreInjectable from "../+namespaces/namespace-store/namespace-store.injectable";
@ -202,19 +202,18 @@ class NonInjectedItemListLayout<I extends ItemObject> extends React.Component<It
};
@computed get items() {
const filterGroups = groupBy<Filter>(this.filters, ({ type }) => type);
const filterGroups = groupBy(this.filters, ({ type }) => type);
const filterItems: ItemsFilter<I>[] = [];
Object.entries(filterGroups).forEach(([type, filtersGroup]) => {
for (const [type, filtersGroup] of Object.entries(filterGroups)) {
const filterCallback = this.filterCallbacks[type] ?? this.props.filterCallbacks?.[type];
if (filterCallback && filtersGroup.length > 0) {
filterItems.push(filterCallback);
}
});
}
const items = this.props.getItems ? this.props.getItems() : (this.props.items ?? this.props.store.items);
const items = this.props.getItems?.() ?? this.props.items ?? this.props.store.items;
return applyFilters(filterItems.concat(this.props.filterItems), items);
}

View File

@ -138,25 +138,14 @@ class NonInjectedKubeObjectListLayout<K extends KubeObject> extends React.Compon
}
}
const InjectedKubeObjectListLayout = withInjectables<
Dependencies,
KubeObjectListLayoutProps<KubeObject>
>(
NonInjectedKubeObjectListLayout,
{
getProps: (di, props) => ({
clusterFrameContext: di.inject(clusterFrameContextInjectable),
subscribeToStores: di.inject(kubeWatchApiInjectable).subscribeStores,
...props,
}),
},
);
export function KubeObjectListLayout<K extends KubeObject>(
props: KubeObjectListLayoutProps<K>,
) {
const InjectedKubeObjectListLayout = withInjectables<Dependencies, KubeObjectListLayoutProps<KubeObject>>(NonInjectedKubeObjectListLayout, {
getProps: (di, props) => ({
clusterFrameContext: di.inject(clusterFrameContextInjectable),
subscribeToStores: di.inject(kubeWatchApiInjectable).subscribeStores,
...props,
}),
});
export function KubeObjectListLayout<K extends KubeObject>(props: KubeObjectListLayoutProps<K>) {
return <InjectedKubeObjectListLayout {...props} />;
}

View File

@ -73,8 +73,8 @@ export class PortForwardStore extends ItemStore<PortForwardItem> {
});
}
async removeSelectedItems() {
return Promise.all(this.selectedItems.map(this.remove));
async removeItems(items: PortForwardItem[]) {
await Promise.all(items.map(this.remove));
}
getById(id: string) {