From da3946b986e659947e90af44a8967ca4729bd840 Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Fri, 2 Jun 2023 11:27:33 +0300 Subject: [PATCH] Adding tableStateInjectable and context Signed-off-by: Alex Andreev --- .../components/table/table-data-context.ts | 43 +++++ .../table/table-state.injectable.tsx | 177 ++++++++++++++++++ 2 files changed, 220 insertions(+) create mode 100644 packages/core/src/renderer/components/table/table-data-context.ts create mode 100644 packages/core/src/renderer/components/table/table-state.injectable.tsx diff --git a/packages/core/src/renderer/components/table/table-data-context.ts b/packages/core/src/renderer/components/table/table-data-context.ts new file mode 100644 index 0000000000..e84e536676 --- /dev/null +++ b/packages/core/src/renderer/components/table/table-data-context.ts @@ -0,0 +1,43 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import React from "react"; +import type { KubeObject } from "@k8slens/kube-object"; +import type { + BaseKubeObjectListLayoutColumn, + GeneralKubeObjectListLayoutColumn, + SpecificKubeListLayoutColumn, +} from "@k8slens/list-layout"; +import type { ItemListLayoutContentProps } from "../item-object-list/content"; + +export type TableContextRequiredDataFromComponentsLayerAbove< + K extends KubeObject +> = Pick< + ItemListLayoutContentProps, + | "tableId" + | "getFilters" + | "renderItemMenu" + | "store" + | "onDetails" + | "hasDetailsView" + | "getItems" + | "renderTableHeader" + | "renderTableContents" + | "sortingCallbacks" + | "isSelectable" +>; + +export interface TableDataContextValue + extends TableContextRequiredDataFromComponentsLayerAbove { + columns?: ( + | BaseKubeObjectListLayoutColumn + | SpecificKubeListLayoutColumn + | GeneralKubeObjectListLayoutColumn + )[]; +} + +export const TableDataContext = React.createContext< + TableDataContextValue +>({} as any); \ No newline at end of file diff --git a/packages/core/src/renderer/components/table/table-state.injectable.tsx b/packages/core/src/renderer/components/table/table-state.injectable.tsx new file mode 100644 index 0000000000..b6eb0a0d2f --- /dev/null +++ b/packages/core/src/renderer/components/table/table-state.injectable.tsx @@ -0,0 +1,177 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { MenuActions, MenuItem } from "../menu"; +import React from "react"; +import { action, computed } from "mobx"; +import { isReactNode, stopPropagation } from "@k8slens/utilities"; +import type { KubeObject } from "@k8slens/kube-object"; +import { Checkbox } from "../checkbox"; +import type { TableCellProps } from "@k8slens/list-layout"; +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import type { TableDataContextValue } from "./table-data-context"; +import { CreateTableState, createTableStateInjectionToken } from "@k8slens/table"; + +type TableDataColumn = object; + +export function createLensTableState({ + tableId, + getFilters, + renderItemMenu, + store, + onDetails, + hasDetailsView, + getItems, + renderTableHeader, + renderTableContents, + sortingCallbacks, + isSelectable, + columns: contextColumns, +}: TableDataContextValue, createState: CreateTableState) { + const headers = renderTableHeader as Required[]; + + + let headingColumns: TableDataColumn[] = headers.map( + ({ id: columnId, className, title }, index) => ({ + id: columnId ?? className, + className, + resizable: !!columnId, + sortable: !!columnId, + draggable: !!columnId, // e.g. warning-icon column in pods + title, + renderValue(row: any, col: any) { + const content = + renderTableContents?.(row.data)[index] ?? + contextColumns + ?.find((col) => col.id === columnId) + ?.content?.(row.data); + + if (isReactNode(content)) { + return content; + } else { + const { className, title } = content as TableCellProps; + + return
{title}
; + } + }, + sortValue(row: any, col: any) { + return sortingCallbacks?.[col.id]?.(row.data) as string; + }, + }) + ); + + const checkboxColumn: TableDataColumn = { + id: "checkbox", + className: "checkbox", + draggable: false, + sortable: false, + resizable: false, + get title() { + return ( +
+ tableState.toggleRowSelectionAll()} + /> +
+ ); + }, + renderValue({ id: rowId }: { id: string | number | symbol }) { + return ( +
+ { + if (tableState.selectedRowsId.has(rowId)) { + tableState.selectedRowsId.delete(rowId); + } else { + tableState.selectedRowsId.add(rowId); + } + })} + /> +
+ ); + }, + }; + + const menuColumn: TableDataColumn = { + id: "menu", + className: "menu", + resizable: false, + sortable: false, + draggable: false, + get title() { + return ( + + {headers + .filter((headerCell) => !headerCell.showWithColumn) + .map(({ id: columnId, title, className }) => ( + + { + if (tableState.hiddenColumns.has(columnId)) { + tableState.hiddenColumns.delete(columnId); + } else { + tableState.hiddenColumns.add(columnId); + } + })} + /> + + ))} + + ); + }, + renderValue(row: any, col: any) { + return ( +
{renderItemMenu?.(row.data, store)}
+ ); + }, + }; + + if (isSelectable) { + headingColumns = [checkboxColumn, ...headingColumns]; + } + + const tableState = createState({ + dataItems: computed(getItems), + headingColumns: [...headingColumns, menuColumn], + + searchBox: computed(() => { + return getFilters().find((item) => item.type === "search")?.value ?? ""; + }), + + customizeRows: () => ({ + className: `${hasDetailsView ? "withDetails" : ""}`, + onSelect(row: any, evt: any) { + if (evt.isPropagationStopped()) { + return; // e.g. click on `checkbox` (== select row) + } + + evt.stopPropagation(); + evt.preventDefault(); + onDetails?.(row.data); + }, + }), + }); + + return tableState; +} + +export const tableStateInjectable = getInjectable({ + id: "table-state", + instantiate(di, context: TableDataContextValue) { + const createState = di.inject(createTableStateInjectionToken); + + return createLensTableState(context, createState); + }, + lifecycle: lifecycleEnum.transient, +}); \ No newline at end of file