From 8147846457aa9e06b944873958d5381fbd04e0f4 Mon Sep 17 00:00:00 2001 From: Dzmitry Tamashevich Date: Thu, 9 Nov 2023 14:56:01 +0300 Subject: [PATCH] implement resize --- .../default/plate-ui/table-cell-element.tsx | 4 +- .../plate-utils/src/components/PortalBody.tsx | 2 +- .../resizable/src/components/ResizeHandle.tsx | 9 +++- .../useTableCellElementResizable.ts | 12 ++++- .../useTableCellElementState.ts | 44 +++++++++++++++--- .../TableElement/useTableElement.ts | 46 +++++++++++-------- packages/table/src/stores/tableStore.ts | 7 +++ packages/table/src/types.ts | 4 ++ .../plate-ui/table-cell-element.tsx | 2 + 9 files changed, 101 insertions(+), 29 deletions(-) diff --git a/apps/www/src/registry/default/plate-ui/table-cell-element.tsx b/apps/www/src/registry/default/plate-ui/table-cell-element.tsx index 46d843c2f7..fb6f8652d6 100644 --- a/apps/www/src/registry/default/plate-ui/table-cell-element.tsx +++ b/apps/www/src/registry/default/plate-ui/table-cell-element.tsx @@ -34,15 +34,15 @@ const TableCellElement = React.forwardRef< rowSize, borders, isSelectingCell, + colSpan, } = useTableCellElementState(); const { props: cellProps } = useTableCellElement({ element: props.element }); const resizableState = useTableCellElementResizableState({ colIndex, rowIndex, + colSpan, }); - - const { rightProps, bottomProps, leftProps, hiddenLeft } = useTableCellElementResizable(resizableState); diff --git a/packages/plate-utils/src/components/PortalBody.tsx b/packages/plate-utils/src/components/PortalBody.tsx index de162963d5..08c15724b3 100644 --- a/packages/plate-utils/src/components/PortalBody.tsx +++ b/packages/plate-utils/src/components/PortalBody.tsx @@ -14,5 +14,5 @@ export const PortalBody: ({ element || typeof window !== 'undefined' ? document.body : undefined; if (!container) return (<>{children}) as any; - return ReactDOM.createPortal(children as any, element || document.body); + return ReactDOM.createPortal(children, element || document.body); }; diff --git a/packages/resizable/src/components/ResizeHandle.tsx b/packages/resizable/src/components/ResizeHandle.tsx index 4d71263674..74088de05d 100644 --- a/packages/resizable/src/components/ResizeHandle.tsx +++ b/packages/resizable/src/components/ResizeHandle.tsx @@ -51,6 +51,7 @@ export const ResizeHandleProvider = ({ export type ResizeHandleOptions = { direction?: ResizeDirection; + initialSize?: number; onResize?: (event: ResizeEvent) => void; onMouseDown?: MouseEventHandler; onTouchStart?: TouchEventHandler; @@ -60,6 +61,7 @@ export type ResizeHandleOptions = { export const useResizeHandleState = ({ direction = 'left', + initialSize: _initialSize, onResize, onMouseDown, onTouchStart, @@ -88,7 +90,12 @@ export const useResizeHandleState = ({ const currentPosition = isHorizontal ? clientX : clientY; const delta = currentPosition - initialPosition; - onResize?.({ initialSize, delta, finished, direction }); + onResize?.({ + initialSize: _initialSize || initialSize, + delta, + finished, + direction, + }); }; const handleMouseMove = (event: MouseEvent | TouchEvent) => diff --git a/packages/table/src/components/TableCellElement/useTableCellElementResizable.ts b/packages/table/src/components/TableCellElement/useTableCellElementResizable.ts index 7a08cdcb61..8010f3315c 100644 --- a/packages/table/src/components/TableCellElement/useTableCellElementResizable.ts +++ b/packages/table/src/components/TableCellElement/useTableCellElementResizable.ts @@ -30,7 +30,7 @@ import { TableCellElementState } from './useTableCellElementState'; export type TableCellElementResizableOptions = Pick< TableCellElementState, - 'colIndex' | 'rowIndex' + 'colIndex' | 'rowIndex' | 'colSpan' > & { /** * Resize by step instead of by pixel. @@ -50,6 +50,7 @@ export const useTableCellElementResizableState = ({ step, stepX = step, stepY = step, + colSpan, }: TableCellElementResizableOptions) => { const editor = useEditorRef(); const { disableMarginLeft } = getPluginOptions( @@ -63,6 +64,7 @@ export const useTableCellElementResizableState = ({ rowIndex, stepX, stepY, + colSpan, }; }; @@ -72,6 +74,7 @@ export const useTableCellElementResizable = ({ rowIndex, stepX, stepY, + colSpan, }: ReturnType): { rightProps: ComponentPropsWithoutRef; bottomProps: ComponentPropsWithoutRef; @@ -86,6 +89,12 @@ export const useTableCellElementResizable = ({ ELEMENT_TABLE ); + // MERGE: override width for horizontally merged cell + let initialWidth: number | undefined; + if (colSpan > 1) { + initialWidth = tableElement.colSizes?.[colIndex]; + } + const [hoveredColIndex, setHoveredColIndex] = useTableStore().use.hoveredColIndex(); @@ -246,6 +255,7 @@ export const useTableCellElementResizable = ({ rightProps: { options: { direction: 'right', + initialSize: initialWidth, onResize: handleResizeRight, ...getHandleHoverProps(colIndex), }, diff --git a/packages/table/src/components/TableCellElement/useTableCellElementState.ts b/packages/table/src/components/TableCellElement/useTableCellElementState.ts index 78aee680d0..ff45a0de15 100644 --- a/packages/table/src/components/TableCellElement/useTableCellElementState.ts +++ b/packages/table/src/components/TableCellElement/useTableCellElementState.ts @@ -26,6 +26,25 @@ export type TableCellElementState = { rowSize: number | undefined; borders: BorderStylesDefault; isSelectingCell: boolean; + colSpan: number; +}; + +/** + * Returns the colspan attribute of the table cell element. + * @default 1 if undefined. + */ +export const getColSpan = (cellElem: TTableCellElement) => { + const attrColSpan = Number(cellElem.attributes?.colspan); + return cellElem.colSpan || attrColSpan || 1; +}; + +/** + * Returns the rowspan attribute of the table cell element. + * @default 1 if undefined + */ +export const getRowSpan = (cellElem: TTableCellElement) => { + const attrRowSpan = Number(cellElem.attributes?.rowspan); + return cellElem.rowSpan || attrRowSpan || 1; }; export const useTableCellElementState = ({ @@ -39,20 +58,32 @@ export const useTableCellElementState = ({ const editor = useEditorRef(); const cellElement = useElement(); - const colIndex = getTableColumnIndex(editor, cellElement); - const rowIndex = getTableRowIndex(editor, cellElement); + const colSpan = getColSpan(cellElement); + const rowSpan = getRowSpan(cellElement); + + const defaultColIndex = getTableColumnIndex(editor, cellElement); + const defaultRowIndex = getTableRowIndex(editor, cellElement); const readOnly = useReadOnly(); const isCellSelected = useIsCellSelected(cellElement); const hoveredColIndex = useTableStore().get.hoveredColIndex(); const selectedCells = useTableStore().get.selectedCells(); + const cellAttributes = useTableStore().get.cellAttributes(); const tableElement = useElement(ELEMENT_TABLE); const rowElement = useElement(ELEMENT_TR); + + const x = cellAttributes.get(cellElement); + const colIndex = x?.col ?? defaultColIndex; + const rowIndex = x?.row ?? defaultRowIndex; + + const endingRowIndex = rowIndex + rowSpan - 1; + const endingColIndex = colIndex + colSpan - 1; + const rowSizeOverrides = useTableStore().get.rowSizeOverrides(); const rowSize = - rowSizeOverrides.get(rowIndex) ?? rowElement?.size ?? undefined; + rowSizeOverrides.get(endingRowIndex) ?? rowElement?.size ?? undefined; const isFirstCell = colIndex === 0; const isFirstRow = tableElement.children?.[0] === rowElement; @@ -63,15 +94,16 @@ export const useTableCellElementState = ({ }); return { - colIndex, - rowIndex, + colIndex: endingColIndex, + rowIndex: endingRowIndex, readOnly: !ignoreReadOnly && readOnly, selected: isCellSelected, - hovered: hoveredColIndex === colIndex, + hovered: hoveredColIndex === endingColIndex, hoveredLeft: isFirstCell && hoveredColIndex === -1, rowSize, borders, isSelectingCell: !!selectedCells, + colSpan, }; }; diff --git a/packages/table/src/components/TableElement/useTableElement.ts b/packages/table/src/components/TableElement/useTableElement.ts index bb139cf579..9cea6ba0f4 100644 --- a/packages/table/src/components/TableElement/useTableElement.ts +++ b/packages/table/src/components/TableElement/useTableElement.ts @@ -12,7 +12,10 @@ import { import { Path } from 'slate'; import { ELEMENT_TABLE } from '../../createTablePlugin'; -import { useTableStore } from '../../stores/tableStore'; +import { + TableStoreCellAttributes, + useTableStore, +} from '../../stores/tableStore'; import { TablePlugin, TTableCellElement, @@ -47,11 +50,13 @@ export const useTableElementState = ({ const element = useElement(); const selectedCells = useTableStore().get.selectedCells(); const marginLeftOverride = useTableStore().get.marginLeftOverride(); + const setCellAttributes = useTableStore().set.cellAttributes(); - // initial calc, than it will be calculated when each individual cell updated useEffect(() => { - calculateCellIndexes(editor, element); - }, [editor, element]); + const newAttributes = new WeakMap() as TableStoreCellAttributes; + calculateCellIndexes(editor, element, newAttributes); + setCellAttributes(newAttributes); + }, [editor, element, setCellAttributes]); const marginLeft = disableMarginLeft ? 0 @@ -98,13 +103,13 @@ export const useTableElement = () => { }; }; -const cellAttributes = new WeakMap< - TTableCellElement, - { row: number; col: number } ->(); +// const cellAttributes = new WeakMap< +// TTableCellElement, +// { row: number; col: number } +// >(); function getCellIndices( - editor: PlateEditor, + cellAttributes: TableStoreCellAttributes, tableEl: TTableElement, tablePath: Path, cellPath: Path @@ -161,7 +166,8 @@ function getCellIndices( const calculateCellIndexes = ( editor: PlateEditor, - tableNode: TTableElement + tableNode: TTableElement, + cellAttributes: TableStoreCellAttributes ) => { // (Place the `getCellIndices()` function from the previous response here) @@ -180,19 +186,23 @@ const calculateCellIndexes = ( const cell = row.children[c] as TTableCellElement; // Get cell indices and store them in the row's array - // const cellPath = findNodePath(editor, cell)!; // TODO: use concat instead of findNodePath const cellPath = [r, c]; - console.log( - 'searching for', - cell.children.map((m) => { - return (m as any).children[0].text; - }), + // console.log( + // 'searching for', + // cell.children.map((m) => { + // return (m as any).children[0].text; + // }), + // tableNode, + // tablePath, + // cellPath + // ); + + const indices = getCellIndices( + cellAttributes, tableNode, tablePath, cellPath ); - - const indices = getCellIndices(editor, tableNode, tablePath, cellPath); if (indices) { cellAttributes.set(cell, indices); } diff --git a/packages/table/src/stores/tableStore.ts b/packages/table/src/stores/tableStore.ts index 91842d9a93..676bf5fcee 100644 --- a/packages/table/src/stores/tableStore.ts +++ b/packages/table/src/stores/tableStore.ts @@ -2,13 +2,20 @@ import { useCallback } from 'react'; import { createAtomStore, TElement } from '@udecode/plate-common'; import { ELEMENT_TABLE } from '../createTablePlugin'; +import { TTableCellElement } from '../types'; export type TableStoreSizeOverrides = Map; +export type TableStoreCellAttributes = Map< + TTableCellElement, + { row: number; col: number } +>; + export const { tableStore, useTableStore } = createAtomStore( { colSizeOverrides: new Map() as TableStoreSizeOverrides, rowSizeOverrides: new Map() as TableStoreSizeOverrides, + cellAttributes: new WeakMap() as TableStoreCellAttributes, marginLeftOverride: null as number | null, hoveredColIndex: null as number | null, selectedCells: null as TElement[] | null, diff --git a/packages/table/src/types.ts b/packages/table/src/types.ts index 3e4ecc9d42..1f21d5b7c8 100644 --- a/packages/table/src/types.ts +++ b/packages/table/src/types.ts @@ -95,6 +95,10 @@ export interface TTableCellElement extends TElement { */ right?: BorderStyle; }; + attributes: { + colspan?: string; + rowspan?: string; + } } export type BorderDirection = 'top' | 'left' | 'bottom' | 'right'; diff --git a/templates/plate-playground-template/src/components/plate-ui/table-cell-element.tsx b/templates/plate-playground-template/src/components/plate-ui/table-cell-element.tsx index 0effb3e381..b367f41877 100644 --- a/templates/plate-playground-template/src/components/plate-ui/table-cell-element.tsx +++ b/templates/plate-playground-template/src/components/plate-ui/table-cell-element.tsx @@ -34,11 +34,13 @@ const TableCellElement = React.forwardRef< rowSize, borders, isSelectingCell, + colSpan, } = useTableCellElementState(); const { props: cellProps } = useTableCellElement({ element: props.element }); const resizableState = useTableCellElementResizableState({ colIndex, rowIndex, + colSpan, }); const { rightProps, bottomProps, leftProps, hiddenLeft } = useTableCellElementResizable(resizableState);