tableUtils.tsx 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. /*
  2. * Copyright 2021-2024 Avaiga Private Limited
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  5. * the License. You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  10. * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  11. * specific language governing permissions and limitations under the License.
  12. */
  13. import React, {
  14. useState,
  15. useCallback,
  16. useEffect,
  17. useMemo,
  18. CSSProperties,
  19. MouseEvent,
  20. ChangeEvent,
  21. SyntheticEvent,
  22. } from "react";
  23. import { FilterOptionsState } from "@mui/material";
  24. import Autocomplete, { createFilterOptions } from "@mui/material/Autocomplete";
  25. import Badge from "@mui/material/Badge";
  26. import Box from "@mui/material/Box";
  27. import Button from "@mui/material/Button";
  28. import IconButton from "@mui/material/IconButton";
  29. import Input from "@mui/material/Input";
  30. import Switch from "@mui/material/Switch";
  31. import TableCell, { TableCellProps } from "@mui/material/TableCell";
  32. import TextField from "@mui/material/TextField";
  33. import CheckIcon from "@mui/icons-material/Check";
  34. import ClearIcon from "@mui/icons-material/Clear";
  35. import EditIcon from "@mui/icons-material/Edit";
  36. import DeleteIcon from "@mui/icons-material/Delete";
  37. import { DatePicker } from "@mui/x-date-pickers/DatePicker";
  38. import { DateTimePicker } from "@mui/x-date-pickers/DateTimePicker";
  39. import { BaseDateTimePickerSlotProps } from "@mui/x-date-pickers/DateTimePicker/shared";
  40. import { isValid } from "date-fns";
  41. import { FormatConfig } from "../../context/taipyReducers";
  42. import { dateToString, getDateTime, getDateTimeString, getNumberString, getTimeZonedDate } from "../../utils/index";
  43. import { TaipyActiveProps, TaipyMultiSelectProps, getSuffixedClassNames } from "./utils";
  44. /**
  45. * A column description as received by the backend.
  46. */
  47. /**
  48. * Generates a CSS class name for a table header.
  49. * @param columnName - The name of the column.
  50. * @returns for CSS class name.
  51. */
  52. export const generateHeaderClassName = (columnName: string | undefined): string => {
  53. // logic for the css header classname
  54. if (!columnName){
  55. // return an empty string if columname is undefined or empty
  56. return "";
  57. }
  58. return '-' + columnName.replace(/\W+/g, '-').replace(/-+/g, '-').toLowerCase();
  59. };
  60. export interface ColumnDesc {
  61. /** The unique column identifier. */
  62. dfid: string;
  63. /** The column type. */
  64. type: string;
  65. /** The value format. */
  66. format?: string;
  67. /** The column title. */
  68. title?: string;
  69. /** The order of the column. */
  70. index: number;
  71. /** The column width. */
  72. width?: number | string;
  73. /** If true, the column cannot be edited. */
  74. notEditable?: boolean;
  75. /** The name of the column that holds the CSS className to
  76. * apply to the cells. */
  77. className?: string;
  78. /** The name of the column that holds the tooltip to
  79. * show on the cells. */
  80. tooltip?: string;
  81. /** The name of the column that holds the formatted value to
  82. * show on the cells. */
  83. formatFn?: string;
  84. /** The value that would replace a NaN value. */
  85. nanValue?: string;
  86. /** The TimeZone identifier used if the type is `date`. */
  87. tz?: string;
  88. /** The flag that allows filtering. */
  89. filter?: boolean;
  90. /** The name of the aggregation function. */
  91. apply?: string;
  92. /** The flag that allows the user to aggregate the column. */
  93. groupBy?: boolean;
  94. widthHint?: number;
  95. /** The list of values that can be used on edit. */
  96. lov?: string[];
  97. /** If true the user can enter any value besides the lov values. */
  98. freeLov?: boolean;
  99. }
  100. export const DEFAULT_SIZE = "small";
  101. export type Order = "asc" | "desc";
  102. /**
  103. * A cell value type.
  104. */
  105. export type RowValue = string | number | boolean | null;
  106. /**
  107. * The definition of a table row.
  108. *
  109. * A row definition associates a name (a string) to a type (a {@link RowValue}).
  110. */
  111. export type RowType = Record<string, RowValue>;
  112. export const EDIT_COL = "taipy_edit";
  113. export const ROW_CLASS_NAME = "__taipyRowClassName__";
  114. export const defaultDateFormat = "yyyy/MM/dd";
  115. const imgButtonRe = /^(!)?\[([^\]]*)]\(([^)]*)\)$/;
  116. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  117. export type TableValueType = Record<string, Record<string, any>>;
  118. export interface TaipyTableProps extends TaipyActiveProps, TaipyMultiSelectProps {
  119. data?: TableValueType;
  120. columns?: string;
  121. defaultColumns: string;
  122. height?: string;
  123. width?: string;
  124. pageSize?: number;
  125. onEdit?: string;
  126. onDelete?: string;
  127. onAdd?: string;
  128. onAction?: string;
  129. editable?: boolean;
  130. defaultEditable?: boolean;
  131. rowClassName?: string;
  132. tooltip?: string;
  133. cellTooltip?: string;
  134. nanValue?: string;
  135. filter?: boolean;
  136. size?: "small" | "medium";
  137. defaultKey?: string; // for testing purposes only
  138. userData?: unknown;
  139. downloadable?: boolean;
  140. onCompare?: string;
  141. compare?: boolean;
  142. useCheckbox?: boolean;
  143. }
  144. export const DownloadAction = "__Taipy__download_csv";
  145. export type PageSizeOptionsType = (
  146. | number
  147. | {
  148. value: number;
  149. label: string;
  150. }
  151. )[];
  152. export interface TaipyPaginatedTableProps extends TaipyTableProps {
  153. pageSizeOptions?: string;
  154. allowAllRows?: boolean;
  155. showAll?: boolean;
  156. }
  157. export const baseBoxSx = { width: "100%" };
  158. export const paperSx = { width: "100%", mb: 2 };
  159. export const tableSx = { minWidth: 250 };
  160. export const headBoxSx = { display: "flex", alignItems: "flex-start" };
  161. export const iconInRowSx = { fontSize: "body2.fontSize" };
  162. export const iconsWrapperSx = { gridColumnStart: 2, display: "flex", alignItems: "center" } as CSSProperties;
  163. const cellBoxSx = { display: "grid", gridTemplateColumns: "1fr auto", alignItems: "center" } as CSSProperties;
  164. const tableFontSx = { fontSize: "body2.fontSize" };
  165. const ButtonSx = { minHeight: "unset", mb: "unset", padding: "unset", lineHeight: "unset" };
  166. export interface OnCellValidation {
  167. (value: RowValue, rowIndex: number, colName: string, userValue: string, tz?: string): void;
  168. }
  169. export interface OnRowDeletion {
  170. (rowIndex: number): void;
  171. }
  172. export interface OnRowSelection {
  173. (rowIndex: number, colName?: string, value?: string): void;
  174. }
  175. export interface OnRowClick {
  176. (e: MouseEvent<HTMLTableRowElement>): void;
  177. }
  178. interface EditableCellProps {
  179. rowIndex: number;
  180. value: RowValue;
  181. colDesc: ColumnDesc;
  182. formatConfig: FormatConfig;
  183. onValidation?: OnCellValidation;
  184. onDeletion?: OnRowDeletion;
  185. onSelection?: OnRowSelection;
  186. nanValue?: string;
  187. className?: string;
  188. tableClassName?: string;
  189. tooltip?: string;
  190. tableCellProps?: Partial<TableCellProps>;
  191. comp?: RowValue;
  192. useCheckbox?: boolean;
  193. formattedVal?: string;
  194. }
  195. export interface FilterDesc {
  196. col: string;
  197. action: string;
  198. value: string | number | boolean | Date;
  199. type: string;
  200. matchcase?: boolean;
  201. params?: number[];
  202. }
  203. export const defaultColumns = {} as Record<string, ColumnDesc>;
  204. export const getSortByIndex = (cols: Record<string, ColumnDesc>) => (key1: string, key2: string) =>
  205. cols[key1].index < cols[key2].index ? -1 : cols[key1].index > cols[key2].index ? 1 : 0;
  206. const formatValue = (val: RowValue, col: ColumnDesc, formatConf: FormatConfig, nanValue?: string): string => {
  207. if (val === undefined) {
  208. return "";
  209. }
  210. switch (col.type) {
  211. case "datetime":
  212. if (val === "NaT") {
  213. return nanValue || "";
  214. }
  215. return val ? getDateTimeString(val as string, col.format || defaultDateFormat, formatConf, col.tz) : "";
  216. case "int":
  217. case "float":
  218. if (val === null) {
  219. return nanValue || "";
  220. }
  221. return getNumberString(val as number, col.format, formatConf);
  222. default:
  223. return val ? (val as string) : "";
  224. }
  225. };
  226. const VALID_BOOLEAN_STRINGS = ["true", "1", "t", "y", "yes", "yeah", "sure"];
  227. const isBooleanTrue = (val: RowValue) =>
  228. typeof val == "string" ? VALID_BOOLEAN_STRINGS.some((s) => s == val.trim().toLowerCase()) : !!val;
  229. const defaultCursor = { cursor: "default" };
  230. const defaultCursorIcon = { ...iconInRowSx, "& .MuiSwitch-input": defaultCursor };
  231. const getCellProps = (col: ColumnDesc, base: Partial<TableCellProps> = {}): Partial<TableCellProps> => {
  232. switch (col.type) {
  233. case "bool":
  234. base.align = "center";
  235. break;
  236. }
  237. if (col.width) {
  238. base.width = col.width;
  239. }
  240. return base;
  241. };
  242. export const getRowIndex = (row: Record<string, RowValue>, rowIndex: number, startIndex = 0) =>
  243. typeof row["_tp_index"] === "number" ? row["_tp_index"] : rowIndex + startIndex;
  244. export const addActionColumn = (nbToRender: number, columns: Record<string, ColumnDesc>) => {
  245. if (nbToRender) {
  246. Object.keys(columns).forEach((key) => columns[key].index++);
  247. columns[EDIT_COL] = {
  248. dfid: EDIT_COL,
  249. type: "",
  250. format: "",
  251. title: "",
  252. index: 0,
  253. width: nbToRender * 4 + "em",
  254. filter: false,
  255. };
  256. }
  257. return columns;
  258. };
  259. export const getClassName = (row: Record<string, unknown>, className?: string, col?: string) =>
  260. getToolFn("tps", row, className, col);
  261. export const getTooltip = (row: Record<string, unknown>, tooltip?: string, col?: string) =>
  262. getToolFn("tpt", row, tooltip, col);
  263. export const getFormatFn = (row: Record<string, unknown>, formatFn?: string, col?: string) =>
  264. getToolFn("tpf", row, formatFn, col);
  265. const getToolFn = (prefix: string, row: Record<string, unknown>, toolFn?: string, col?: string) =>
  266. toolFn ? (((col && row[`${prefix}__${col}__${toolFn}`]) || row[toolFn]) as string) : undefined;
  267. export const getPageKey = (
  268. columns: Record<string, ColumnDesc>,
  269. prefix: string,
  270. cols: string[],
  271. orderBy: string,
  272. order: string,
  273. filters: FilterDesc[],
  274. aggregates?: string[],
  275. cellClassNames?: Record<string, string>,
  276. tooltips?: Record<string, string>,
  277. formats?: Record<string, string>
  278. ) =>
  279. [
  280. prefix,
  281. cols.join(),
  282. orderBy,
  283. order,
  284. aggregates?.length
  285. ? cols.reduce((pv, col, idx) => {
  286. if (aggregates.includes(col)) {
  287. return `${pv}${idx}`;
  288. }
  289. return pv;
  290. }, "-")
  291. : undefined,
  292. filters.map((filter) => `${filter.col}${filter.action}${filter.value}`).join(),
  293. [
  294. cellClassNames &&
  295. Object.entries(cellClassNames)
  296. .map((col, className) => `${col}:${className}`)
  297. .join(),
  298. tooltips &&
  299. Object.entries(tooltips)
  300. .map((col, tooltip) => `${col}:${tooltip}`)
  301. .join(),
  302. formats &&
  303. Object.entries(formats)
  304. .map((col, format) => `${col}:${format}`)
  305. .join(),
  306. ]
  307. .filter((v) => v)
  308. .join(";"),
  309. ]
  310. .filter((v) => v)
  311. .join("-");
  312. const setInputFocus = (input: HTMLInputElement) => input && input.focus();
  313. const textFieldProps = { textField: { margin: "dense" } } as BaseDateTimePickerSlotProps<Date>;
  314. const filter = createFilterOptions<string>();
  315. const getOptionKey = (option: string) => (Array.isArray(option) ? option[0] : option);
  316. const getOptionLabel = (option: string) => (Array.isArray(option) ? option[1] : option);
  317. const onCompleteClose = (evt: SyntheticEvent) => evt.stopPropagation();
  318. const emptyObject = {};
  319. export const EditableCell = (props: EditableCellProps) => {
  320. const {
  321. onValidation,
  322. value,
  323. colDesc,
  324. formatConfig,
  325. rowIndex,
  326. onDeletion,
  327. onSelection,
  328. nanValue,
  329. className,
  330. tableClassName,
  331. tooltip,
  332. tableCellProps = emptyObject,
  333. comp,
  334. useCheckbox = false,
  335. formattedVal: formattedValue,
  336. } = props;
  337. const [val, setVal] = useState<RowValue | Date>(value);
  338. const [edit, setEdit] = useState(false);
  339. const [deletion, setDeletion] = useState(false);
  340. const onChange = useCallback((e: ChangeEvent<HTMLInputElement>) => setVal(e.target.value), []);
  341. const onCompleteChange = useCallback((e: SyntheticEvent, value: string | null) => {
  342. e.stopPropagation();
  343. setVal(value);
  344. }, []);
  345. const onBoolChange = useCallback((e: ChangeEvent<HTMLInputElement>) => setVal(e.target.checked), []);
  346. const onDateChange = useCallback((date: Date | null) => setVal(date), []);
  347. const boolVal = colDesc.type?.startsWith("bool") && (val as boolean);
  348. const withTime = useMemo(() => !!colDesc.format && colDesc.format.toLowerCase().includes("h"), [colDesc.format]);
  349. const buttonImg = useMemo(() => {
  350. let m;
  351. if (typeof value == "string" && (m = imgButtonRe.exec(value)) !== null) {
  352. return {
  353. text: !!m[1] ? m[3] : m[2],
  354. value: !!m[1] ? m[2] : m[3],
  355. img: !!m[1],
  356. action: !!onSelection,
  357. };
  358. }
  359. return undefined;
  360. }, [value, onSelection]);
  361. const onCheckClick = useCallback(
  362. (evt?: MouseEvent<HTMLElement>) => {
  363. evt && evt.stopPropagation();
  364. let castVal = val;
  365. switch (colDesc.type) {
  366. case "bool":
  367. castVal = isBooleanTrue(val as RowValue);
  368. break;
  369. case "int":
  370. try {
  371. castVal = parseInt(val as string, 10);
  372. } catch {
  373. // ignore
  374. }
  375. break;
  376. case "float":
  377. try {
  378. castVal = parseFloat(val as string);
  379. } catch {
  380. // ignore
  381. }
  382. break;
  383. case "datetime":
  384. if (val === null) {
  385. castVal = val;
  386. } else if (isValid(val)) {
  387. castVal = dateToString(
  388. getTimeZonedDate(val as Date, formatConfig.timeZone, withTime),
  389. withTime
  390. );
  391. } else {
  392. return;
  393. }
  394. break;
  395. }
  396. onValidation &&
  397. onValidation(
  398. castVal as RowValue,
  399. rowIndex,
  400. colDesc.dfid,
  401. val as string,
  402. colDesc.type == "datetime" ? formatConfig.timeZone : undefined
  403. );
  404. setEdit((e) => !e);
  405. },
  406. [onValidation, val, rowIndex, colDesc.dfid, colDesc.type, formatConfig.timeZone, withTime]
  407. );
  408. const onEditClick = useCallback(
  409. (evt?: MouseEvent<HTMLElement>) => {
  410. evt && evt.stopPropagation();
  411. colDesc.type?.startsWith("date")
  412. ? setVal(getDateTime(value as string, formatConfig.timeZone, withTime))
  413. : setVal(value);
  414. onValidation && setEdit((e) => !e);
  415. },
  416. [onValidation, value, formatConfig.timeZone, colDesc.type, withTime]
  417. );
  418. const onKeyDown = useCallback(
  419. (e: React.KeyboardEvent<HTMLElement>) => {
  420. switch (e.key) {
  421. case "Enter":
  422. onCheckClick();
  423. break;
  424. case "Escape":
  425. onEditClick();
  426. break;
  427. }
  428. },
  429. [onCheckClick, onEditClick]
  430. );
  431. const onDeleteCheckClick = useCallback(
  432. (evt?: MouseEvent<HTMLElement>) => {
  433. evt && evt.stopPropagation();
  434. onDeletion && onDeletion(rowIndex);
  435. setDeletion((d) => !d);
  436. },
  437. [onDeletion, rowIndex]
  438. );
  439. const onDeleteClick = useCallback(
  440. (evt?: MouseEvent) => {
  441. evt && evt.stopPropagation();
  442. onDeletion && setDeletion((d) => !d);
  443. },
  444. [onDeletion]
  445. );
  446. const onDeleteKeyDown = useCallback(
  447. (e: React.KeyboardEvent<HTMLInputElement>) => {
  448. switch (e.key) {
  449. case "Enter":
  450. onDeleteCheckClick();
  451. break;
  452. case "Escape":
  453. onDeleteClick();
  454. break;
  455. }
  456. },
  457. [onDeleteCheckClick, onDeleteClick]
  458. );
  459. const onSelect = useCallback(
  460. (e: MouseEvent<HTMLElement>) => {
  461. e.stopPropagation();
  462. onSelection && onSelection(rowIndex, colDesc.dfid, buttonImg && buttonImg.value);
  463. },
  464. [onSelection, rowIndex, colDesc.dfid, buttonImg]
  465. );
  466. const filterOptions = useCallback(
  467. (options: string[], params: FilterOptionsState<string>) => {
  468. const filtered = filter(options, params);
  469. if (colDesc.freeLov) {
  470. const { inputValue } = params;
  471. if (
  472. inputValue &&
  473. !options.some((option) => inputValue == (Array.isArray(option) ? option[1] : option))
  474. ) {
  475. filtered.push(inputValue);
  476. }
  477. }
  478. return filtered;
  479. },
  480. [colDesc.freeLov]
  481. );
  482. const boolTitle = useMemo(() => {
  483. if (!colDesc.type?.startsWith("bool") || !colDesc.lov || colDesc.lov.length == 0) {
  484. return boolVal ? "True" : "False";
  485. }
  486. return colDesc.lov[boolVal ? 1 : 0];
  487. }, [colDesc.type, boolVal, colDesc.lov]);
  488. useEffect(() => {
  489. !onValidation && setEdit(false);
  490. }, [onValidation]);
  491. return (
  492. <TableCell
  493. {...getCellProps(colDesc, tableCellProps)}
  494. className={
  495. onValidation ? getSuffixedClassNames(className || "tpc", edit ? "-editing" : "-editable") : className
  496. }
  497. title={
  498. tooltip || comp
  499. ? `${tooltip ? tooltip : ""}${
  500. comp ? " " + formatValue(comp as RowValue, colDesc, formatConfig, nanValue) : ""
  501. }`
  502. : undefined
  503. }
  504. >
  505. <Badge color="primary" variant="dot" invisible={comp === undefined || comp === null}>
  506. {edit ? (
  507. colDesc.type?.startsWith("bool") ? (
  508. <Box sx={cellBoxSx}>
  509. {useCheckbox ? (
  510. <input
  511. type="checkbox"
  512. checked={val as boolean}
  513. title={boolTitle}
  514. style={iconInRowSx}
  515. className={getSuffixedClassNames(tableClassName, "-bool")}
  516. ref={setInputFocus}
  517. onChange={onBoolChange}
  518. />
  519. ) : (
  520. <Switch
  521. checked={val as boolean}
  522. size="small"
  523. title={boolTitle}
  524. sx={iconInRowSx}
  525. onChange={onBoolChange}
  526. inputRef={setInputFocus}
  527. className={getSuffixedClassNames(tableClassName, "-bool")}
  528. />
  529. )}
  530. <Box sx={iconsWrapperSx}>
  531. <IconButton onClick={onCheckClick} size="small" sx={iconInRowSx}>
  532. <CheckIcon fontSize="inherit" />
  533. </IconButton>
  534. <IconButton onClick={onEditClick} size="small" sx={iconInRowSx}>
  535. <ClearIcon fontSize="inherit" />
  536. </IconButton>
  537. </Box>
  538. </Box>
  539. ) : colDesc.type?.startsWith("date") ? (
  540. <Box sx={cellBoxSx}>
  541. {withTime ? (
  542. <DateTimePicker
  543. value={val as Date}
  544. onChange={onDateChange}
  545. slotProps={textFieldProps}
  546. inputRef={setInputFocus}
  547. sx={tableFontSx}
  548. className={getSuffixedClassNames(tableClassName, "-date")}
  549. />
  550. ) : (
  551. <DatePicker
  552. value={val as Date}
  553. onChange={onDateChange}
  554. slotProps={textFieldProps}
  555. inputRef={setInputFocus}
  556. sx={tableFontSx}
  557. className={getSuffixedClassNames(tableClassName, "-date")}
  558. />
  559. )}
  560. <Box sx={iconsWrapperSx}>
  561. <IconButton onClick={onCheckClick} size="small" sx={iconInRowSx}>
  562. <CheckIcon fontSize="inherit" />
  563. </IconButton>
  564. <IconButton onClick={onEditClick} size="small" sx={iconInRowSx}>
  565. <ClearIcon fontSize="inherit" />
  566. </IconButton>
  567. </Box>
  568. </Box>
  569. ) : colDesc.lov ? (
  570. <Box sx={cellBoxSx}>
  571. <Autocomplete
  572. autoComplete={true}
  573. fullWidth
  574. selectOnFocus={!!colDesc.freeLov}
  575. clearOnBlur={!!colDesc.freeLov}
  576. handleHomeEndKeys={!!colDesc.freeLov}
  577. options={colDesc.lov}
  578. getOptionKey={getOptionKey}
  579. getOptionLabel={getOptionLabel}
  580. filterOptions={filterOptions}
  581. freeSolo={!!colDesc.freeLov}
  582. value={val as string}
  583. onChange={onCompleteChange}
  584. onOpen={onCompleteClose}
  585. renderInput={(params) => (
  586. <TextField
  587. {...params}
  588. fullWidth
  589. inputRef={setInputFocus}
  590. onChange={colDesc.freeLov ? onChange : undefined}
  591. margin="dense"
  592. variant="standard"
  593. sx={tableFontSx}
  594. className={getSuffixedClassNames(tableClassName, "-input")}
  595. />
  596. )}
  597. disableClearable={!colDesc.freeLov}
  598. />
  599. <Box sx={iconsWrapperSx}>
  600. <IconButton onClick={onCheckClick} size="small" sx={iconInRowSx}>
  601. <CheckIcon fontSize="inherit" />
  602. </IconButton>
  603. <IconButton onClick={onEditClick} size="small" sx={iconInRowSx}>
  604. <ClearIcon fontSize="inherit" />
  605. </IconButton>
  606. </Box>
  607. </Box>
  608. ) : (
  609. <Input
  610. value={val}
  611. onChange={onChange}
  612. onKeyDown={onKeyDown}
  613. inputRef={setInputFocus}
  614. margin="dense"
  615. sx={tableFontSx}
  616. className={getSuffixedClassNames(tableClassName, "-input")}
  617. endAdornment={
  618. <Box sx={iconsWrapperSx}>
  619. <IconButton onClick={onCheckClick} size="small" sx={iconInRowSx}>
  620. <CheckIcon fontSize="inherit" />
  621. </IconButton>
  622. <IconButton onClick={onEditClick} size="small" sx={iconInRowSx}>
  623. <ClearIcon fontSize="inherit" />
  624. </IconButton>
  625. </Box>
  626. }
  627. />
  628. )
  629. ) : EDIT_COL === colDesc.dfid ? (
  630. deletion ? (
  631. <Input
  632. value="Confirm"
  633. onKeyDown={onDeleteKeyDown}
  634. inputRef={setInputFocus}
  635. sx={tableFontSx}
  636. className={getSuffixedClassNames(tableClassName, "-delete")}
  637. endAdornment={
  638. <Box sx={iconsWrapperSx}>
  639. <IconButton onClick={onDeleteCheckClick} size="small" sx={iconInRowSx}>
  640. <CheckIcon fontSize="inherit" />
  641. </IconButton>
  642. <IconButton onClick={onDeleteClick} size="small" sx={iconInRowSx}>
  643. <ClearIcon fontSize="inherit" />
  644. </IconButton>
  645. </Box>
  646. }
  647. />
  648. ) : onDeletion ? (
  649. <Box sx={iconsWrapperSx}>
  650. <IconButton onClick={onDeleteClick} size="small" sx={iconInRowSx}>
  651. <DeleteIcon fontSize="inherit" />
  652. </IconButton>
  653. </Box>
  654. ) : null
  655. ) : (
  656. <Box sx={cellBoxSx} onClick={onSelect}>
  657. {buttonImg ? (
  658. buttonImg.img ? (
  659. <img
  660. src={buttonImg.text}
  661. className={getSuffixedClassNames(tableClassName, "-img")}
  662. alt={buttonImg.value}
  663. onClick={onSelect}
  664. title={buttonImg.value}
  665. />
  666. ) : (
  667. <Button
  668. size="small"
  669. onClick={onSelect}
  670. sx={ButtonSx}
  671. className={getSuffixedClassNames(tableClassName, "-btn")}
  672. disabled={!buttonImg.action}
  673. title={buttonImg.value}
  674. >
  675. {formatValue(buttonImg.text, colDesc, formatConfig, nanValue)}
  676. </Button>
  677. )
  678. ) : value !== null && value !== undefined && colDesc.type && colDesc.type.startsWith("bool") ? (
  679. useCheckbox ? (
  680. <input
  681. type="checkbox"
  682. checked={value as boolean}
  683. title={boolTitle}
  684. style={defaultCursor}
  685. readOnly
  686. className={getSuffixedClassNames(tableClassName, "-bool")}
  687. />
  688. ) : (
  689. <Switch
  690. checked={value as boolean}
  691. size="small"
  692. title={boolTitle}
  693. sx={defaultCursorIcon}
  694. className={getSuffixedClassNames(tableClassName, "-bool")}
  695. />
  696. )
  697. ) : (
  698. <span style={defaultCursor}>
  699. {formatValue(
  700. formattedValue == undefined ? value : (formattedValue as RowValue),
  701. colDesc,
  702. formatConfig,
  703. nanValue
  704. )}
  705. </span>
  706. )}
  707. {onValidation && !buttonImg ? (
  708. <Box sx={iconsWrapperSx}>
  709. <IconButton onClick={onEditClick} size="small" sx={iconInRowSx}>
  710. <EditIcon fontSize="inherit" />
  711. </IconButton>
  712. </Box>
  713. ) : null}
  714. </Box>
  715. )}
  716. </Badge>
  717. </TableCell>
  718. );
  719. };