diff --git a/frontend/ee b/frontend/ee index d93ee7e131..4b950ed3d0 160000 --- a/frontend/ee +++ b/frontend/ee @@ -1 +1 @@ -Subproject commit d93ee7e1318f044ef2327671b8b257648071453d +Subproject commit 4b950ed3d0ba15edddf217936e9c9ae1ca3cf11a 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/NewTable/_components/DataTypes/Markdown.jsx b/frontend/src/AppBuilder/Widgets/NewTable/_components/DataTypes/Markdown.jsx new file mode 100644 index 0000000000..a8bb593409 --- /dev/null +++ b/frontend/src/AppBuilder/Widgets/NewTable/_components/DataTypes/Markdown.jsx @@ -0,0 +1,170 @@ +import React, { useState, useEffect, useRef } from 'react'; +import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; +import { determineJustifyContentValue } from '@/_helpers/utils'; +import { default as ReactMarkdown } from 'react-markdown'; +import useTextColor from '../DataTypes/_hooks/useTextColor'; +import useTableStore from '../../_stores/tableStore'; +import { getMaxHeight } from '../../_utils/helper'; +import { shallow } from 'zustand/shallow'; +import DOMPurify from 'dompurify'; + +export const MarkdownColumn = ({ + id, + isEditable, + darkMode, + handleCellValueChange, + textColor, + cellValue, + column, + containerWidth, + cell, + horizontalAlignment, + cellSize, +}) => { + const ref = useRef(null); + const cellTextColor = useTextColor(id, textColor); + const isMaxRowHeightAuto = useTableStore((state) => state.getTableStyles(id)?.isMaxRowHeightAuto, shallow); + const maxRowHeightValue = useTableStore((state) => state.getTableStyles(id)?.maxRowHeightValue, shallow); + const [hovered, setHovered] = useState(false); + const [isEditing, setIsEditing] = useState(false); + + const cellStyles = { + color: cellTextColor ?? 'inherit', + }; + + const getCellValue = (value) => { + let transformedValue = value; + if (typeof value !== 'string') { + try { + transformedValue = String(value); + } catch (e) { + transformedValue = ''; + } + } + return DOMPurify.sanitize(transformedValue.trim()); + }; + + 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; + handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original); + } + }; + + return ( +
{ + setIsEditing(false); + onChange(e); + }} + onKeyDown={(e) => { + if (e.key === 'Enter' && !e.shiftKey) { + 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()} +
+
+ )} +
+ ); +}; diff --git a/frontend/src/AppBuilder/Widgets/NewTable/_components/DataTypes/index.js b/frontend/src/AppBuilder/Widgets/NewTable/_components/DataTypes/index.js index 0801f2a2dc..ee2fb701a0 100644 --- a/frontend/src/AppBuilder/Widgets/NewTable/_components/DataTypes/index.js +++ b/frontend/src/AppBuilder/Widgets/NewTable/_components/DataTypes/index.js @@ -13,3 +13,4 @@ export { CustomDropdownColumn } from './CustomDropdown'; // export { SelectColumn } from './Select'; export { TextColumn } from './Text'; export { JsonColumn } from './JSON'; +export { MarkdownColumn } from './Markdown'; diff --git a/frontend/src/AppBuilder/Widgets/NewTable/_utils/generateColumnsData.js b/frontend/src/AppBuilder/Widgets/NewTable/_utils/generateColumnsData.js index 45e4b8be15..817493a4d7 100644 --- a/frontend/src/AppBuilder/Widgets/NewTable/_utils/generateColumnsData.js +++ b/frontend/src/AppBuilder/Widgets/NewTable/_utils/generateColumnsData.js @@ -19,6 +19,7 @@ import { CustomDropdownColumn, TextColumn, JsonColumn, + MarkdownColumn, } from '../_components/DataTypes'; import useTableStore from '../_stores/tableStore'; @@ -339,6 +340,23 @@ export default function generateColumnsData({ /> ); + case 'markdown': { + return ( + + ); + } + default: return cellValue || ''; } diff --git a/server/ee b/server/ee index 1da04eef69..683647f83d 160000 --- a/server/ee +++ b/server/ee @@ -1 +1 @@ -Subproject commit 1da04eef696345ce9f35d42af92e5d6de992cd85 +Subproject commit 683647f83d3efeeadbe69c40b8e8dd5ba4e8ea06