mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-23 08:58:26 +00:00
Merge pull request #12439 from ToolJet/feat/table-markdown-mod
Markdown column added in table
This commit is contained in:
commit
0907e07482
8 changed files with 195 additions and 2 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit d93ee7e1318f044ef2327671b8b257648071453d
|
||||
Subproject commit 4b950ed3d0ba15edddf217936e9c9ae1ca3cf11a
|
||||
|
|
@ -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 ?? '');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<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;
|
||||
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${
|
||||
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%',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
readOnly={!isEditable}
|
||||
onBlur={(e) => {
|
||||
setIsEditing(false);
|
||||
onChange(e);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
ref.current.blur();
|
||||
onChange(e);
|
||||
}
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
setIsEditing(true);
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<div>{isEditing ? cellValue : <ReactMarkdown>{getCellValue(cellValue)}</ReactMarkdown>}</div>
|
||||
</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: getMaxHeight(isMaxRowHeightAuto, maxRowHeightValue, cellSize),
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
|
@ -13,3 +13,4 @@ export { CustomDropdownColumn } from './CustomDropdown';
|
|||
// export { SelectColumn } from './Select';
|
||||
export { TextColumn } from './Text';
|
||||
export { JsonColumn } from './JSON';
|
||||
export { MarkdownColumn } from './Markdown';
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<MarkdownColumn
|
||||
isEditable={isEditable}
|
||||
darkMode={darkMode}
|
||||
handleCellValueChange={handleCellValueChange}
|
||||
horizontalAlignment={column?.horizontalAlignment}
|
||||
textColor={getResolvedValue(column.textColor, { cellValue, rowData })}
|
||||
cellValue={cellValue}
|
||||
column={column}
|
||||
containerWidth={columnSize}
|
||||
cell={cell}
|
||||
id={id}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
default:
|
||||
return cellValue || '';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 1da04eef696345ce9f35d42af92e5d6de992cd85
|
||||
Subproject commit 683647f83d3efeeadbe69c40b8e8dd5ba4e8ea06
|
||||
Loading…
Reference in a new issue