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:
parent
06ec1b39a4
commit
4abe0d6c20
@ -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;
|
||||
}
|
||||
|
||||
@ -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 });
|
||||
|
||||
|
||||
@ -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>;
|
||||
|
||||
|
||||
@ -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}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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} />;
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user