diff --git a/src/common/utils/collection-functions.ts b/src/common/utils/collection-functions.ts new file mode 100644 index 0000000000..b05186c0cb --- /dev/null +++ b/src/common/utils/collection-functions.ts @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * Get the value behind `key`. If it was not pressent, first insert `value` + * @param map The map to interact with + * @param key The key to insert into the map with + * @param value The value to optional add to the map + * @returns The value in the map + */ +export function getOrInsert(map: Map, key: K, value: V): V { + if (map.has(key)) { + return map.get(key); + } + + return map.set(key, value).get(key); +} + +/** + * Get the value behind `key`. If it was not pressent, first insert the value returned by `getVal` + * + * This function should be used over `getInsertWith` if it is expensive to build a `V` + * @param map The map to interact with + * @param key The key to insert into the map with + * @param builder A function that returns a new instance of `V`. + * @returns The value in the map + */ +export function getOrInsertWith(map: Map, key: K, builder: () => V): V { + if (map.has(key)) { + return map.get(key); + } + + return map.set(key, builder()).get(key); +} + +/** + * Set the value associated with `key` iff there was not a previous value + * @param map The map to interact with + * @throws if `key` already in map + * @returns `this` so that `strictSet` can be chained + */ +export function strictSet(map: Map, key: K, val: V): typeof map { + if (map.has(key)) { + throw new TypeError("Duplicate key in map"); + } + + return map.set(key, val); +} + +/** + * Get the value associated with `key` + * @param map The map to interact with + * @throws if `key` did not a value associated with it + */ +export function strictGet(map: Map, key: K): V { + if (!map.has(key)) { + throw new TypeError("key not in map"); + } + + return map.get(key); +} + +/** + * Toggles the "has" in `set` of `value` + * @param set The set to interact with + * @param value The value to either add if not present or remove if present from `set` + */ +export function toggle(set: Set, value: T): void { + if (!set.delete(value)) { + // Set.prototype.delete returns false if `value` was not in the set + set.add(value); + } +} diff --git a/src/common/utils/index.ts b/src/common/utils/index.ts index 630c93160b..1400608aab 100644 --- a/src/common/utils/index.ts +++ b/src/common/utils/index.ts @@ -10,11 +10,19 @@ export function noop(...args: T): void { return void args; } +/** + * A typecorrect version of .bind() + */ +export function bind(fn: (...args: [...BoundArgs, ...NonBoundArgs]) => ReturnType, thisArg: any, ...boundArgs: BoundArgs): (...args: NonBoundArgs) => ReturnType { + return fn.bind(thisArg, ...boundArgs); +} + export * from "./app-version"; export * from "./autobind"; export * from "./camelCase"; export * from "./cloneJson"; export * from "./cluster-id-url-parsing"; +export * from "./collection-functions"; export * from "./convertCpu"; export * from "./convertMemory"; export * from "./debouncePromise"; diff --git a/src/extensions/lens-renderer-extension.ts b/src/extensions/lens-renderer-extension.ts index 26d2f2d810..fe0795c2c1 100644 --- a/src/extensions/lens-renderer-extension.ts +++ b/src/extensions/lens-renderer-extension.ts @@ -16,6 +16,7 @@ import type { WelcomeMenuRegistration } from "../renderer/components/+welcome/we import type { WelcomeBannerRegistration } from "../renderer/components/+welcome/welcome-banner-items/welcome-banner-registration"; import type { CommandRegistration } from "../renderer/components/command-palette/registered-commands/commands"; import type { AppPreferenceRegistration } from "../renderer/components/+preferences/app-preferences/app-preference-registration"; +import type { AdditionalCategoryColumnRegistration } from "../renderer/components/+catalog/custom-category-columns"; export class LensRendererExtension extends LensExtension { globalPages: registries.PageRegistration[] = []; @@ -33,6 +34,7 @@ export class LensRendererExtension extends LensExtension { welcomeBanners: WelcomeBannerRegistration[] = []; catalogEntityDetailItems: registries.CatalogEntityDetailRegistration[] = []; topBarItems: TopBarRegistration[] = []; + additionalCategoryColumns: AdditionalCategoryColumnRegistration[] = []; async navigate

(pageId?: string, params?: P) { const { navigate } = await import("../renderer/navigation"); diff --git a/src/renderer/components/+catalog/catalog.tsx b/src/renderer/components/+catalog/catalog.tsx index ae6ab3795f..107e02db30 100644 --- a/src/renderer/components/+catalog/catalog.tsx +++ b/src/renderer/components/+catalog/catalog.tsx @@ -28,25 +28,18 @@ import { RenderDelay } from "../render-delay/render-delay"; import { Icon } from "../icon"; import { HotbarToggleMenuItem } from "./hotbar-toggle-menu-item"; import { Avatar } from "../avatar"; -import { KubeObject } from "../../../common/k8s-api/kube-object"; -import { getLabelBadges } from "./helpers"; import { withInjectables } from "@ogre-tools/injectable-react"; -import catalogPreviousActiveTabStorageInjectable - from "./catalog-previous-active-tab-storage/catalog-previous-active-tab-storage.injectable"; +import catalogPreviousActiveTabStorageInjectable from "./catalog-previous-active-tab-storage/catalog-previous-active-tab-storage.injectable"; import catalogEntityStoreInjectable from "./catalog-entity-store/catalog-entity-store.injectable"; - -enum sortBy { - name = "name", - kind = "kind", - source = "source", - status = "status", -} +import type { GetCategoryColumnsParams, CategoryColumns } from "./get-category-columns.injectable"; +import getCategoryColumnsInjectable from "./get-category-columns.injectable"; interface Props extends RouteComponentProps {} interface Dependencies { - catalogPreviousActiveTabStorage: { set: (value: string ) => void } - catalogEntityStore: CatalogEntityStore + catalogPreviousActiveTabStorage: { set: (value: string ) => void }; + catalogEntityStore: CatalogEntityStore; + getCategoryColumns: (params: GetCategoryColumnsParams) => CategoryColumns; } @observer @@ -228,46 +221,23 @@ class NonInjectedCatalog extends React.Component { return null; } + const { sortingCallbacks, searchFilters, renderTableContents, renderTableHeader } = this.props.getCategoryColumns({ activeCategory }); + return ( entity.getName(), - [sortBy.source]: entity => entity.getSource(), - [sortBy.status]: entity => entity.status.phase, - [sortBy.kind]: entity => entity.kind, - }} - searchFilters={[ - entity => [ - entity.getName(), - entity.getId(), - entity.status.phase, - `source=${entity.getSource()}`, - ...KubeObject.stringifyLabels(entity.metadata.labels), - ], - ]} - renderTableHeader={[ - { title: "Name", className: styles.entityName, sortBy: sortBy.name, id: "name" }, - !activeCategory && { title: "Kind", sortBy: sortBy.kind, id: "kind" }, - { title: "Source", className: styles.sourceCell, sortBy: sortBy.source, id: "source" }, - { title: "Labels", className: `${styles.labelsCell} scrollable`, id: "labels" }, - { title: "Status", className: styles.statusCell, sortBy: sortBy.status, id: "status" }, - ].filter(Boolean)} + sortingCallbacks={sortingCallbacks} + searchFilters={searchFilters} + renderTableHeader={renderTableHeader} customizeTableRowProps={entity => ({ disabled: !entity.isEnabled(), })} - renderTableContents={entity => [ - this.renderName(entity), - !activeCategory && entity.kind, - entity.getSource(), - getLabelBadges(entity), - {entity.status.phase}, - ].filter(Boolean)} + renderTableContents={renderTableContents} onDetails={this.onDetails} renderItemMenu={this.renderItemMenu} /> @@ -306,17 +276,11 @@ class NonInjectedCatalog extends React.Component { } } -export const Catalog = withInjectables( - NonInjectedCatalog, - { - getProps: (di, props) => ({ - catalogEntityStore: di.inject(catalogEntityStoreInjectable), - - catalogPreviousActiveTabStorage: di.inject( - catalogPreviousActiveTabStorageInjectable, - ), - - ...props, - }), - }, -); +export const Catalog = withInjectables( NonInjectedCatalog, { + getProps: (di, props) => ({ + catalogEntityStore: di.inject(catalogEntityStoreInjectable), + catalogPreviousActiveTabStorage: di.inject(catalogPreviousActiveTabStorageInjectable), + getCategoryColumns: di.inject(getCategoryColumnsInjectable), + ...props, + }), +}); diff --git a/src/renderer/components/+catalog/custom-category-columns.injectable.tsx b/src/renderer/components/+catalog/custom-category-columns.injectable.tsx new file mode 100644 index 0000000000..0066f5c493 --- /dev/null +++ b/src/renderer/components/+catalog/custom-category-columns.injectable.tsx @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { computed, IComputedValue } from "mobx"; +import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension"; +import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable"; +import { getOrInsert } from "../../utils"; +import type { RegisteredAdditionalCategoryColumn } from "./custom-category-columns"; + +interface Dependencies { + extensions: IComputedValue; +} + +function getAdditionCategoryColumns({ extensions }: Dependencies): IComputedValue>> { + return computed(() => { + const res = new Map>(); + + for (const ext of extensions.get()) { + for (const { renderCell, titleProps, priority = 50, searchFilter, sortCallback, ...registration } of ext.additionalCategoryColumns) { + const byGroup = getOrInsert(res, registration.group, new Map()); + const byKind = getOrInsert(byGroup, registration.kind, []); + const id = `${ext.name}:${registration.id}`; + + byKind.push({ + renderCell, + priority, + id, + titleProps: { + id, + ...titleProps, + sortBy: sortCallback + ? id + : undefined, + }, + searchFilter, + sortCallback, + }); + } + } + + return res; + }); +} + +const categoryColumnsInjectable = getInjectable({ + instantiate: (di) => getAdditionCategoryColumns({ + extensions: di.inject(rendererExtensionsInjectable), + }), + lifecycle: lifecycleEnum.singleton, +}); + +export default categoryColumnsInjectable; diff --git a/src/renderer/components/+catalog/custom-category-columns.ts b/src/renderer/components/+catalog/custom-category-columns.ts new file mode 100644 index 0000000000..a4d4e852fd --- /dev/null +++ b/src/renderer/components/+catalog/custom-category-columns.ts @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import type React from "react"; +import type { CatalogEntity } from "../../../common/catalog"; +import type { TableCellProps } from "../table"; + +/** + * These are the supported props for the title cell + */ +export interface TitleCellProps { + className?: string; + title?: React.ReactNode; +} + +/** + * This is the type used to declare new catalog category columns + */ +export interface AdditionalCategoryColumnRegistration { + /** + * The catalog entity kind that is declared by the category for this registration + * + * e.g. + * - `"KubernetesCluster"` + */ + kind: string; + + /** + * The catalog entity group that is declared by the category for this registration + * + * e.g. + * - `"entity.k8slens.dev"` + */ + group: string; + + /** + * The sorting order value. + * + * @default 50 + */ + priority?: number; + + /** + * This value MUST to be unique to your extension + */ + id: string; + + /** + * This function will be called to generate the cells (on demand) for the column + */ + renderCell: (entity: CatalogEntity) => React.ReactNode; + + /** + * This function will be used to generate the columns title cell. + */ + titleProps: TitleCellProps; + + /** + * If provided then the column will support sorting and this function will be called to + * determine a row's ordering. + * + * strings are sorted ahead of numbers, and arrays determine ordering between equal + * elements of the previous index. + */ + sortCallback?: (entity: CatalogEntity) => string | number | (string | number)[]; + + /** + * If provided then searching is supported on this column and this function will be called + * to determine if the current search string matches for this row. + */ + searchFilter?: (entity: CatalogEntity) => string | string[]; +} + +export interface RegisteredAdditionalCategoryColumn { + id: string; + priority: number; + renderCell: (entity: CatalogEntity) => React.ReactNode; + titleProps: TableCellProps; + sortCallback?: (entity: CatalogEntity) => string | number | (string | number)[]; + searchFilter?: (entity: CatalogEntity) => string | string[]; +} diff --git a/src/renderer/components/+catalog/get-category-columns.injectable.ts b/src/renderer/components/+catalog/get-category-columns.injectable.ts new file mode 100644 index 0000000000..ba722b279f --- /dev/null +++ b/src/renderer/components/+catalog/get-category-columns.injectable.ts @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { orderBy } from "lodash"; +import type { IComputedValue } from "mobx"; +import type { CatalogCategory, CatalogEntity } from "../../../common/catalog"; +import { bind } from "../../utils"; +import type { ItemListLayoutProps } from "../item-object-list"; +import type { RegisteredAdditionalCategoryColumn } from "./custom-category-columns"; +import categoryColumnsInjectable from "./custom-category-columns.injectable"; +import { anyCategoryColumns, browseAllColumns } from "./internal-category-columns"; + +interface Dependencies { + extensionColumns: IComputedValue>>; +} + +export interface GetCategoryColumnsParams { + activeCategory: CatalogCategory | null | undefined; +} + +export type CategoryColumns = Required, "sortingCallbacks" | "searchFilters" | "renderTableContents" | "renderTableHeader">>; + +function getNonGlobalColums(activeCategory: CatalogCategory | null | undefined, extensionColumns: IComputedValue>>) { + if (activeCategory) { + return extensionColumns + .get() + .get(activeCategory.spec.group) + ?.get(activeCategory.spec.names.kind) + ?? []; + } + + return browseAllColumns; +} + +function getCategoryColumns({ extensionColumns }: Dependencies, { activeCategory }: GetCategoryColumnsParams): CategoryColumns { + const allRegistrations = orderBy( + [ + ...getNonGlobalColums(activeCategory, extensionColumns), + ...anyCategoryColumns, + ], + "priority", + "asc", + ); + + const sortingCallbacks: CategoryColumns["sortingCallbacks"] = {}; + const searchFilters: CategoryColumns["searchFilters"] = []; + const renderTableHeader: CategoryColumns["renderTableHeader"] = []; + const tableRowRenderers: ((entity: CatalogEntity) => React.ReactNode)[] = []; + + for (const registration of allRegistrations) { + if (registration.sortCallback) { + sortingCallbacks[registration.id] = registration.sortCallback; + } + + if (registration.searchFilter) { + searchFilters.push(registration.searchFilter); + } + + tableRowRenderers.push(registration.renderCell); + renderTableHeader.push(registration.titleProps); + } + + return { + sortingCallbacks, + renderTableHeader, + renderTableContents: entity => tableRowRenderers.map(fn => fn(entity)), + searchFilters, + }; +} + +const getCategoryColumnsInjectable = getInjectable({ + instantiate: (di) => bind(getCategoryColumns, null, { + extensionColumns: di.inject(categoryColumnsInjectable), + }), + lifecycle: lifecycleEnum.singleton, +}); + +export default getCategoryColumnsInjectable; diff --git a/src/renderer/components/+catalog/internal-category-columns.tsx b/src/renderer/components/+catalog/internal-category-columns.tsx new file mode 100644 index 0000000000..aed4db68cf --- /dev/null +++ b/src/renderer/components/+catalog/internal-category-columns.tsx @@ -0,0 +1,136 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import styles from "./catalog.module.scss"; + +import React from "react"; +import { HotbarStore } from "../../../common/hotbar-store"; +import type { CatalogEntity } from "../../api/catalog-entity"; +import { Avatar } from "../avatar"; +import type { RegisteredAdditionalCategoryColumn } from "./custom-category-columns"; +import { Icon } from "../icon"; +import { prevDefault } from "../../utils"; +import { getLabelBadges } from "./helpers"; +import { KubeObject } from "../../../common/k8s-api/kube-object"; + +function renderEntityName(entity: CatalogEntity) { + const hotbarStore = HotbarStore.getInstance(); + const isItemInHotbar = hotbarStore.isAddedToActive(entity); + const onClick = prevDefault( + isItemInHotbar + ? () => hotbarStore.removeFromHotbar(entity.getId()) + : () => hotbarStore.addToHotbar(entity), + ); + + return ( + <> + + {entity.spec.icon?.material && } + + {entity.getName()} + + + ); +} + +export const browseAllColumns: RegisteredAdditionalCategoryColumn[] = [ + { + id: "kind", + priority: 5, + renderCell: entity => entity.kind, + titleProps: { + id: "kind", + sortBy: "kind", + title: "Kind", + }, + sortCallback: entity => entity.kind, + }, +]; + +export const anyCategoryColumns: RegisteredAdditionalCategoryColumn[] = [ + { + id: "name", + priority: 0, + renderCell: renderEntityName, + titleProps: { + title: "Name", + className: styles.entityName, + id: "name", + sortBy: "name", + }, + searchFilter: entity => entity.getName(), + sortCallback: entity => `name=${entity.getName()}`, + }, + { + id: "source", + priority: 10, + renderCell: entity => entity.getSource(), + titleProps: { + title: "Source", + className: styles.sourceCell, + id: "source", + sortBy: "source", + }, + sortCallback: entity => entity.getSource(), + searchFilter: entity => `source=${entity.getSource()}`, + }, + { + id: "labels", + priority: 20, + renderCell: getLabelBadges, + titleProps: { + id: "labels", + title: "Labels", + className: `${styles.labelsCell} scrollable`, + }, + searchFilter: entity => KubeObject.stringifyLabels(entity.metadata.labels), + }, + { + id: "status", + priority: 30, + renderCell: entity => ( + + {entity.status.phase} + + ), + titleProps: { + title: "Status", + className: styles.statusCell, + id: "status", + sortBy: "status", + }, + searchFilter: entity => entity.status.phase, + }, +]; diff --git a/src/renderer/components/table/table-cell.tsx b/src/renderer/components/table/table-cell.tsx index 6165cd81ed..af6f95eec9 100644 --- a/src/renderer/components/table/table-cell.tsx +++ b/src/renderer/components/table/table-cell.tsx @@ -14,18 +14,66 @@ import { Checkbox } from "../checkbox"; export type TableCellElem = React.ReactElement; export interface TableCellProps extends React.DOMAttributes { - id?: string; // used for configuration visibility of columns + /** + * used for configuration visibility of columns + */ + id?: string; + + /** + * Any css class names for this table cell. Only used if `title` is a "simple" react node + */ className?: string; + + /** + * The actual value of the cell + */ title?: ReactNode; - scrollable?: boolean; // content inside could be scrolled - checkbox?: boolean; // render cell with a checkbox - isChecked?: boolean; // mark checkbox as checked or not - renderBoolean?: boolean; // show "true" or "false" for all of the children elements are "typeof boolean" - sortBy?: TableSortBy; // column name, must be same as key in sortable object - showWithColumn?: string // id of the column which follow same visibility rules - _sorting?: Partial; //
sorting state, don't use this prop outside (!) - _sort?(sortBy: TableSortBy): void; //
sort function, don't use this prop outside (!) - _nowrap?: boolean; // indicator, might come from parent , don't use this prop outside (!) + + /** + * content inside could be scrolled + */ + scrollable?: boolean; + + /** + * render cell with a checkbox + */ + checkbox?: boolean; + + /** + * mark checkbox as checked or not + */ + isChecked?: boolean; + + /** + * show "true" or "false" for all of the children elements are "typeof boolean" + */ + renderBoolean?: boolean; + + /** + * column name, must be same as key in sortable object
+ */ + sortBy?: TableSortBy; + + /** + * id of the column which follow same visibility rules + */ + showWithColumn?: string + + /** + * @internal + */ + _sorting?: Partial; + + /** + * @internal + */ + _sort?(sortBy: TableSortBy): void; + + /** + * @internal + * indicator, might come from parent , don't use this prop outside (!) + */ + _nowrap?: boolean; } export class TableCell extends React.Component {