mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-23 17:08:34 +00:00
Markdown column added in table
This commit is contained in:
parent
a31a25510d
commit
15cc6bddc6
5 changed files with 190 additions and 0 deletions
|
|
@ -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' },
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@ export const StylesTabElements = ({
|
|||
undefined,
|
||||
'number',
|
||||
'json',
|
||||
'markdown',
|
||||
'boolean',
|
||||
'select',
|
||||
'text',
|
||||
|
|
|
|||
|
|
@ -631,6 +631,8 @@ class TableComponent extends React.Component {
|
|||
return 'Multiselect';
|
||||
case 'json':
|
||||
return 'JSON';
|
||||
case 'markdown':
|
||||
return 'Markdown';
|
||||
default:
|
||||
capitalize(text ?? '');
|
||||
}
|
||||
|
|
|
|||
166
frontend/src/AppBuilder/Widgets/Table/Markdown.jsx
Normal file
166
frontend/src/AppBuilder/Widgets/Table/Markdown.jsx
Normal file
|
|
@ -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 (
|
||||
<div
|
||||
className={`overlay-cell-table ${darkMode && 'dark-theme'}`}
|
||||
onMouseEnter={() => setHovered(true)}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
style={{ color: 'var(--text-primary)' }}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
width: `${containerWidth}px`,
|
||||
whiteSpace: 'pre-wrap',
|
||||
}}
|
||||
>
|
||||
<ReactMarkdown>{getCellValue(cellValue)}</ReactMarkdown>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
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 (
|
||||
<div
|
||||
ref={ref}
|
||||
contentEditable={'true'}
|
||||
className={`h-100 text-container long-text-input d-flex align-items-center ${
|
||||
darkMode ? ' textarea-dark-theme' : ''
|
||||
} justify-content-${determineJustifyContentValue(horizontalAlignment)}`}
|
||||
tabIndex={-1}
|
||||
style={{
|
||||
color: cellTextColor ? cellTextColor : 'inherit',
|
||||
outline: 'none',
|
||||
border: 'none',
|
||||
background: 'inherit',
|
||||
position: 'relative',
|
||||
height: '100%',
|
||||
}}
|
||||
readOnly={!isEditable}
|
||||
onBlur={(e) => {
|
||||
setIsEditing(false);
|
||||
onChange(e);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
ref.current.blur();
|
||||
onChange(e);
|
||||
}
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
setIsEditing(true);
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{isEditing ? cellValue : <ReactMarkdown>{getCellValue(cellValue)}</ReactMarkdown>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const _showOverlay =
|
||||
ref?.current &&
|
||||
(ref?.current?.clientWidth < ref?.current?.children[0]?.offsetWidth ||
|
||||
ref?.current?.clientHeight < ref?.current?.children[0]?.offsetHeight);
|
||||
|
||||
return (
|
||||
<>
|
||||
<OverlayTrigger
|
||||
placement="bottom"
|
||||
overlay={_showOverlay ? getOverlay() : <div></div>}
|
||||
trigger={_showOverlay && ['hover', 'focus']}
|
||||
rootClose={true}
|
||||
show={_showOverlay && hovered && !isEditing}
|
||||
>
|
||||
{!isEditable ? (
|
||||
<div
|
||||
className={`d-flex align-items-center h-100 w-100 justify-content-${determineJustifyContentValue(
|
||||
horizontalAlignment
|
||||
)}`}
|
||||
style={cellStyles}
|
||||
onMouseMove={() => {
|
||||
if (!hovered) setHovered(true);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setHovered(false);
|
||||
}}
|
||||
ref={ref}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
maxHeight: isMaxRowHeightAuto
|
||||
? 'fit-content'
|
||||
: maxRowHeightValue
|
||||
? `${maxRowHeightValue}px`
|
||||
: cellSize === 'condensed'
|
||||
? '39px'
|
||||
: '45px',
|
||||
whiteSpace: 'pre-wrap',
|
||||
}}
|
||||
>
|
||||
<ReactMarkdown>{getCellValue(cellValue)}</ReactMarkdown>
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="h-100 d-flex flex-column justify-content-center position-relative">
|
||||
<div
|
||||
onMouseMove={() => {
|
||||
if (!hovered) setHovered(true);
|
||||
}}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
className={`${isEditing ? 'h-100 content-editing' : ''} h-100`}
|
||||
>
|
||||
{renderEditable()}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</OverlayTrigger>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Markdown;
|
||||
|
|
@ -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 (
|
||||
<Markdown
|
||||
isEditable={isEditable}
|
||||
darkMode={darkMode}
|
||||
handleCellValueChange={handleCellValueChange}
|
||||
cellTextColor={cellTextColor}
|
||||
horizontalAlignment={horizontalAlignment}
|
||||
cellValue={cellValue}
|
||||
column={column}
|
||||
currentState={currentState}
|
||||
containerWidth={width}
|
||||
cell={cell}
|
||||
isMaxRowHeightAuto={isMaxRowHeightAuto}
|
||||
cellSize={cellSize}
|
||||
maxRowHeightValue={maxRowHeightValue}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
return cellValue || '';
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue