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 isLoading = false;
|
||||||
@observable isLoaded = false;
|
@observable isLoaded = false;
|
||||||
@observable items = observable.array<Item>([], { deep: false });
|
@observable items = observable.array<Item>([], { deep: false });
|
||||||
@observable selectedItemsIds = observable.map<string, boolean>();
|
@observable selectedItemsIds = observable.set<string>();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
@ -29,7 +29,11 @@ export abstract class ItemStore<Item extends ItemObject> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@computed get selectedItems(): Item[] {
|
@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[] {
|
public getItems(): Item[] {
|
||||||
@ -152,12 +156,12 @@ export abstract class ItemStore<Item extends ItemObject> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isSelected(item: Item) {
|
isSelected(item: Item) {
|
||||||
return !!this.selectedItemsIds.get(item.getId());
|
return this.selectedItemsIds.has(item.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
select(item: Item) {
|
select(item: Item) {
|
||||||
this.selectedItemsIds.set(item.getId(), true);
|
this.selectedItemsIds.add(item.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
@ -207,6 +211,8 @@ export abstract class ItemStore<Item extends ItemObject> {
|
|||||||
|
|
||||||
async removeSelectedItems?(): Promise<any>;
|
async removeSelectedItems?(): Promise<any>;
|
||||||
|
|
||||||
|
async removeItems?(items: Item[]): Promise<void>;
|
||||||
|
|
||||||
* [Symbol.iterator]() {
|
* [Symbol.iterator]() {
|
||||||
yield* this.items;
|
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));
|
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
|
// collect items from watch-api events to avoid UI blowing up with huge streams of data
|
||||||
protected eventsBuffer = observable.array<IKubeWatchEvent<KubeJsonApiData>>([], { deep: false });
|
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);
|
return releases.get().filter((release) => release.isSelected);
|
||||||
},
|
},
|
||||||
|
|
||||||
removeSelectedItems() {
|
pickOnlySelected: (releases) => {
|
||||||
return Promise.all(
|
return releases.filter(release => release.isSelected);
|
||||||
releases.get().filter((release) => release.isSelected).map((release) => release.delete()),
|
},
|
||||||
);
|
|
||||||
|
removeItems: async (releases) => {
|
||||||
|
await Promise.all(releases.map(release => release.delete()));
|
||||||
},
|
},
|
||||||
} as ItemStore<RemovableHelmRelease>;
|
} as ItemStore<RemovableHelmRelease>;
|
||||||
|
|
||||||
|
|||||||
@ -136,19 +136,27 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
removeItemsDialog() {
|
removeItemsDialog(selectedItems: I[]) {
|
||||||
const { customizeRemoveDialog, store } = this.props;
|
const { customizeRemoveDialog, store } = this.props;
|
||||||
const { selectedItems, removeSelectedItems } = store;
|
|
||||||
const visibleMaxNamesCount = 5;
|
const visibleMaxNamesCount = 5;
|
||||||
const selectedNames = selectedItems.map(ns => ns.getName()).slice(0, visibleMaxNamesCount).join(", ");
|
const selectedNames = selectedItems.map(ns => ns.getName()).slice(0, visibleMaxNamesCount).join(", ");
|
||||||
const dialogCustomProps = customizeRemoveDialog ? customizeRemoveDialog(selectedItems) : {};
|
const dialogCustomProps = customizeRemoveDialog ? customizeRemoveDialog(selectedItems) : {};
|
||||||
const selectedCount = selectedItems.length;
|
const selectedCount = selectedItems.length;
|
||||||
const tailCount = selectedCount > visibleMaxNamesCount ? selectedCount - visibleMaxNamesCount : 0;
|
const tailCount = selectedCount > visibleMaxNamesCount
|
||||||
const tail = tailCount > 0 ? <>, and <b>{tailCount}</b> more</> : null;
|
? selectedCount - visibleMaxNamesCount
|
||||||
const message = selectedCount <= 1 ? <p>Remove item <b>{selectedNames}</b>?</p> : <p>Remove <b>{selectedCount}</b> items <b>{selectedNames}</b>{tail}?</p>;
|
: 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({
|
ConfirmDialog.open({
|
||||||
ok: removeSelectedItems,
|
ok: onConfirm,
|
||||||
labelOk: "Remove",
|
labelOk: "Remove",
|
||||||
message,
|
message,
|
||||||
...dialogCustomProps,
|
...dialogCustomProps,
|
||||||
@ -225,10 +233,12 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
|
|||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
store, hasDetailsView, addRemoveButtons = {}, virtual, sortingCallbacks,
|
store, hasDetailsView, addRemoveButtons = {}, virtual, sortingCallbacks,
|
||||||
detailsItem, className, tableProps = {}, tableId,
|
detailsItem, className, tableProps = {}, tableId, getItems,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const selectedItemId = detailsItem && detailsItem.getId();
|
const selectedItemId = detailsItem && detailsItem.getId();
|
||||||
const classNames = cssNames(className, "box", "grow", ThemeStore.getInstance().activeTheme.type);
|
const classNames = cssNames(className, "box", "grow", ThemeStore.getInstance().activeTheme.type);
|
||||||
|
const items = getItems();
|
||||||
|
const selectedItems = store.pickOnlySelected(items);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="items box grow flex column">
|
<div className="items box grow flex column">
|
||||||
@ -238,7 +248,7 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
|
|||||||
selectable={hasDetailsView}
|
selectable={hasDetailsView}
|
||||||
sortable={sortingCallbacks}
|
sortable={sortingCallbacks}
|
||||||
getTableRow={this.getRow}
|
getTableRow={this.getRow}
|
||||||
items={this.props.getItems()}
|
items={items}
|
||||||
selectedItemId={selectedItemId}
|
selectedItemId={selectedItemId}
|
||||||
noItems={this.renderNoItems()}
|
noItems={this.renderNoItems()}
|
||||||
className={classNames}
|
className={classNames}
|
||||||
@ -252,9 +262,11 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
|
|||||||
{() => (
|
{() => (
|
||||||
<AddRemoveButtons
|
<AddRemoveButtons
|
||||||
onRemove={
|
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}
|
{...addRemoveButtons}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import { boundMethod, cssNames, IClassName, noop, StorageHelper } from "../../ut
|
|||||||
import type { AddRemoveButtonsProps } from "../add-remove-buttons";
|
import type { AddRemoveButtonsProps } from "../add-remove-buttons";
|
||||||
import type { ItemObject, ItemStore } from "../../../common/item.store";
|
import type { ItemObject, ItemStore } from "../../../common/item.store";
|
||||||
import type { SearchInputUrlProps } from "../input";
|
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 { PageFiltersList } from "./page-filters-list";
|
||||||
import type { NamespaceStore } from "../+namespaces/namespace-store/namespace.store";
|
import type { NamespaceStore } from "../+namespaces/namespace-store/namespace.store";
|
||||||
import namespaceStoreInjectable from "../+namespaces/namespace-store/namespace-store.injectable";
|
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() {
|
@computed get items() {
|
||||||
const filterGroups = groupBy<Filter>(this.filters, ({ type }) => type);
|
const filterGroups = groupBy(this.filters, ({ type }) => type);
|
||||||
|
|
||||||
const filterItems: ItemsFilter<I>[] = [];
|
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];
|
const filterCallback = this.filterCallbacks[type] ?? this.props.filterCallbacks?.[type];
|
||||||
|
|
||||||
if (filterCallback && filtersGroup.length > 0) {
|
if (filterCallback && filtersGroup.length > 0) {
|
||||||
filterItems.push(filterCallback);
|
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);
|
return applyFilters(filterItems.concat(this.props.filterItems), items);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -138,25 +138,14 @@ class NonInjectedKubeObjectListLayout<K extends KubeObject> extends React.Compon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const InjectedKubeObjectListLayout = withInjectables<
|
const InjectedKubeObjectListLayout = withInjectables<Dependencies, KubeObjectListLayoutProps<KubeObject>>(NonInjectedKubeObjectListLayout, {
|
||||||
Dependencies,
|
getProps: (di, props) => ({
|
||||||
KubeObjectListLayoutProps<KubeObject>
|
clusterFrameContext: di.inject(clusterFrameContextInjectable),
|
||||||
>(
|
subscribeToStores: di.inject(kubeWatchApiInjectable).subscribeStores,
|
||||||
NonInjectedKubeObjectListLayout,
|
...props,
|
||||||
|
}),
|
||||||
{
|
});
|
||||||
getProps: (di, props) => ({
|
|
||||||
clusterFrameContext: di.inject(clusterFrameContextInjectable),
|
|
||||||
subscribeToStores: di.inject(kubeWatchApiInjectable).subscribeStores,
|
|
||||||
...props,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
export function KubeObjectListLayout<K extends KubeObject>(
|
|
||||||
props: KubeObjectListLayoutProps<K>,
|
|
||||||
) {
|
|
||||||
|
|
||||||
|
export function KubeObjectListLayout<K extends KubeObject>(props: KubeObjectListLayoutProps<K>) {
|
||||||
return <InjectedKubeObjectListLayout {...props} />;
|
return <InjectedKubeObjectListLayout {...props} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -73,8 +73,8 @@ export class PortForwardStore extends ItemStore<PortForwardItem> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeSelectedItems() {
|
async removeItems(items: PortForwardItem[]) {
|
||||||
return Promise.all(this.selectedItems.map(this.remove));
|
await Promise.all(items.map(this.remove));
|
||||||
}
|
}
|
||||||
|
|
||||||
getById(id: string) {
|
getById(id: string) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user