diff --git a/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Table/ColumnManager/PropertiesTabElements.jsx b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Table/ColumnManager/PropertiesTabElements.jsx index 2e975109d2..0568b85ccd 100644 --- a/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Table/ColumnManager/PropertiesTabElements.jsx +++ b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Table/ColumnManager/PropertiesTabElements.jsx @@ -53,6 +53,7 @@ export const PropertiesTabElements = ({ { label: 'Image', value: 'image' }, { label: 'Link', value: 'link' }, { label: 'JSON', value: 'json' }, + { label: 'Markdown', value: 'markdown' }, // Following column types are deprecated { label: 'Default', value: 'default' }, { label: 'Dropdown', value: 'dropdown' }, diff --git a/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Table/ColumnManager/StylesTabElements.jsx b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Table/ColumnManager/StylesTabElements.jsx index 5091e56c6c..2b91a8dd15 100644 --- a/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Table/ColumnManager/StylesTabElements.jsx +++ b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Table/ColumnManager/StylesTabElements.jsx @@ -128,6 +128,7 @@ export const StylesTabElements = ({ undefined, 'number', 'json', + 'markdown', 'boolean', 'select', 'text', diff --git a/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Table/Table.jsx b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Table/Table.jsx index 3aca83b046..bb6c1d94bb 100644 --- a/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Table/Table.jsx +++ b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Table/Table.jsx @@ -631,6 +631,8 @@ class TableComponent extends React.Component { return 'Multiselect'; case 'json': return 'JSON'; + case 'markdown': + return 'Markdown'; default: capitalize(text ?? ''); } diff --git a/frontend/src/AppBuilder/Widgets/Table/Markdown.jsx b/frontend/src/AppBuilder/Widgets/Table/Markdown.jsx new file mode 100644 index 0000000000..abd121182a --- /dev/null +++ b/frontend/src/AppBuilder/Widgets/Table/Markdown.jsx @@ -0,0 +1,166 @@ +import React, { useState, useEffect } from 'react'; +import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; +import { determineJustifyContentValue } from '@/_helpers/utils'; +import { default as ReactMarkdown } from 'react-markdown'; +import DOMPurify from 'dompurify'; + +const Markdown = ({ + isEditable, + darkMode, + handleCellValueChange, + cellTextColor, + cellValue, + column, + containerWidth, + cell, + horizontalAlignment, + isMaxRowHeightAuto, + cellSize, + maxRowHeightValue, +}) => { + const ref = React.useRef(null); + + const [hovered, setHovered] = useState(false); + const [isEditing, setIsEditing] = useState(false); + + const cellStyles = { + color: cellTextColor ?? 'inherit', + }; + + const getCellValue = (value) => { + return DOMPurify.sanitize(value); + }; + + useEffect(() => { + if (!isEditable && isEditing) { + setIsEditing(false); + } + }, [isEditable]); + + const getOverlay = () => { + return ( +
setHovered(true)} + onMouseLeave={() => setHovered(false)} + style={{ color: 'var(--text-primary)' }} + > + + {getCellValue(cellValue)} + +
+ ); + }; + + const renderEditable = () => { + const onChange = (e) => { + if (cellValue !== e.target.textContent) { + const value = e.target.textContent.replace(/\n/g, ''); + handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original); + } + }; + + return ( +
{ + setIsEditing(false); + onChange(e); + }} + onKeyDown={(e) => { + if (e.key === 'Enter') { + ref.current.blur(); + onChange(e); + } + }} + onFocus={(e) => { + setIsEditing(true); + e.stopPropagation(); + }} + > + {isEditing ? cellValue : {getCellValue(cellValue)}} +
+ ); + }; + + 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} + > + + {getCellValue(cellValue)} + +
+ ) : ( +
+
{ + if (!hovered) setHovered(true); + }} + onMouseLeave={() => setHovered(false)} + className={`${isEditing ? 'h-100 content-editing' : ''} h-100`} + > + {renderEditable()} +
+
+ )} +
+ + ); +}; + +export default Markdown; diff --git a/frontend/src/AppBuilder/Widgets/Table/columns/index.jsx b/frontend/src/AppBuilder/Widgets/Table/columns/index.jsx index 257de7c4ae..6e56e6e6ef 100644 --- a/frontend/src/AppBuilder/Widgets/Table/columns/index.jsx +++ b/frontend/src/AppBuilder/Widgets/Table/columns/index.jsx @@ -15,6 +15,7 @@ import SolidIcon from '@/_ui/Icon/SolidIcons'; import Text from '../Text'; import StringColumn from '../String'; import Json from '../Json'; +import Markdown from '../Markdown'; export default function generateColumnsData({ columnProperties, @@ -736,6 +737,25 @@ export default function generateColumnsData({ /> ); } + case 'markdown': { + return ( + + ); + } } return cellValue || ''; },