From f30eae736639cdb80dd0538216cd279de7197a4a Mon Sep 17 00:00:00 2001 From: devanshu052000 Date: Tue, 7 Jan 2025 16:48:52 +0530 Subject: [PATCH] Added JSON component for rendering JSON column cell with formatting. --- .../Widgets/Table/GenerateEachCellValue.jsx | 2 +- .../src/AppBuilder/Widgets/Table/Json.jsx | 191 ++++++++++++++++++ .../Widgets/Table/columns/index.jsx | 21 +- 3 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 frontend/src/AppBuilder/Widgets/Table/Json.jsx diff --git a/frontend/src/AppBuilder/Widgets/Table/GenerateEachCellValue.jsx b/frontend/src/AppBuilder/Widgets/Table/GenerateEachCellValue.jsx index 312e57b760..0d2d8530c7 100644 --- a/frontend/src/AppBuilder/Widgets/Table/GenerateEachCellValue.jsx +++ b/frontend/src/AppBuilder/Widgets/Table/GenerateEachCellValue.jsx @@ -29,7 +29,7 @@ export default function GenerateEachCellValue({ const [showHighlightedCells, setHighlighterCells] = React.useState(globalFilter ? true : false); // const [isNullCellClicked, setIsNullCellClicked] = React.useState(false); - const columnTypeAllowToRenderMarkElement = ['text', 'string', 'default', 'number', 'json', undefined]; + const columnTypeAllowToRenderMarkElement = ['text', 'string', 'default', 'number', undefined]; const ref = useRef(); const [showOverlay, setShowOverlay] = useState(false); const [hovered, setHovered] = useState(false); diff --git a/frontend/src/AppBuilder/Widgets/Table/Json.jsx b/frontend/src/AppBuilder/Widgets/Table/Json.jsx new file mode 100644 index 0000000000..9eda847d3f --- /dev/null +++ b/frontend/src/AppBuilder/Widgets/Table/Json.jsx @@ -0,0 +1,191 @@ +/* eslint-disable no-undef */ +import React, { useState, useEffect } from 'react'; +import { determineJustifyContentValue } from '@/_helpers/utils'; +import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; + +const Json = ({ + isEditable, + darkMode, + handleCellValueChange, + cellTextColor, + cellValue, + column, + containerWidth, + cell, + horizontalAlignment, + isMaxRowHeightAuto, + cellSize, + maxRowHeightValue, +}) => { + const jsonIndentation = false; + const ref = React.useRef(null); + + const [hovered, setHovered] = useState(false); + const [isEditing, setIsEditing] = useState(false); + + const cellStyles = { + color: cellTextColor ?? 'inherit', + }; + + useEffect(() => { + if (!isEditable && isEditing) { + setIsEditing(false); + } + }, [isEditable]); + + function format(obj) { + if (typeof obj !== 'object' || obj === null) { + return typeof obj === 'string' ? `"${obj}"` : obj; + } + if (Array.isArray(obj)) { + return `[ ${obj.map(format).join(', ')} ]`; + } + return `{ ${Object.entries(obj) + .map(([key, value]) => `"${key}": ${format(value)}`) + .join(', ')} }`; + } + + const formatCellValue = (value, overlay = false) => { + try { + if (typeof value === 'object') { + if (jsonIndentation === true && !overlay) { + return JSON.stringify(value, null, 4).replace(/":/g, '": '); + } + const formattedJSON = format(value); + return formattedJSON; + } else { + if (jsonIndentation === true && !overlay) { + return JSON.stringify(JSON.parse(value), null, 4).replace(/":/g, '": '); + } + const formattedJSON = format(JSON.parse(value)); + return formattedJSON; + } + } catch (error) { + return value; + } + }; + + const _renderString = () => ( +
{ + setIsEditing(false); + if (cellValue !== e.target.textContent) { + const value = JSON.stringify(JSON.parse(e.target.textContent.replace(/\n/g, ''))); + handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original); + } + }} + onKeyDown={(e) => { + if (e.key === 'Enter') { + ref.current.blur(); + if (cellValue !== e.target.textContent) { + const value = JSON.stringify(JSON.parse(e.target.textContent.replace(/\n/g, ''))); + handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original); + } + } + }} + onFocus={(e) => { + setIsEditing(true); + e.stopPropagation(); + }} + > + {String(formatCellValue(cellValue))} +
+ ); + + const getOverlay = () => { + return ( +
setHovered(true)} + onMouseLeave={() => setHovered(false)} + style={{ color: 'var(--text-primary)' }} + > + + {String(formatCellValue(cellValue, true))} + +
+ ); + }; + + const _showOverlay = + ref?.current && + (ref?.current?.clientWidth < ref?.current?.children[0]?.offsetWidth || + ref?.current?.clientHeight < ref?.current?.children[0]?.offsetHeight); + + return ( + <> + } + trigger={_showOverlay && ['hover', 'focus']} + rootClose={true} + show={_showOverlay && hovered && !isEditing} + > + {!isEditable ? ( +
{ + if (!hovered) setHovered(true); + }} + onMouseLeave={() => { + setHovered(false); + }} + ref={ref} + > + + {String(formatCellValue(cellValue))} + +
+ ) : ( +
+
{ + if (!hovered) setHovered(true); + }} + onMouseLeave={() => setHovered(false)} + className={`${isEditing ? 'h-100 content-editing' : ''} h-100`} + > + {_renderString()} +
+
+ )} +
+ + ); +}; + +export default Json; diff --git a/frontend/src/AppBuilder/Widgets/Table/columns/index.jsx b/frontend/src/AppBuilder/Widgets/Table/columns/index.jsx index c6a5945a49..e7013d0b1e 100644 --- a/frontend/src/AppBuilder/Widgets/Table/columns/index.jsx +++ b/frontend/src/AppBuilder/Widgets/Table/columns/index.jsx @@ -14,6 +14,7 @@ import { CustomSelect } from '../CustomSelect'; import SolidIcon from '@/_ui/Icon/SolidIcons'; import Text from '../Text'; import StringColumn from '../String'; +import Json from '../Json'; export default function generateColumnsData({ columnProperties, @@ -186,7 +187,6 @@ export default function generateColumnsData({ switch (columnType) { case 'string': - case 'json': case undefined: case 'default': { const cellTextColor = getResolvedValue(column.textColor, { cellValue, rowData }) ?? ''; @@ -715,6 +715,25 @@ export default function generateColumnsData({ ); } + case 'json': { + return ( + + ); + } } return cellValue || ''; },