diff --git a/packages/s2-core/src/cell/base-cell.ts b/packages/s2-core/src/cell/base-cell.ts index 28467f391f..33c29bfe6e 100644 --- a/packages/s2-core/src/cell/base-cell.ts +++ b/packages/s2-core/src/cell/base-cell.ts @@ -53,6 +53,7 @@ import type { GuiIcon } from '../common/icons/gui-icon'; import { checkIsLinkField } from '../utils/interaction/link-field'; import type { Node } from '../facet/layout/node'; import type { ViewMeta } from '../common/interface/basic'; +import { customMerge } from '../utils'; export abstract class BaseCell extends Group { // cell's data meta info @@ -113,7 +114,7 @@ export abstract class BaseCell extends Group { } public setMeta(viewMeta: T) { - this.meta = viewMeta; + this.meta = customMerge(this.meta, viewMeta); } public getIconStyle() { diff --git a/packages/s2-core/src/cell/data-cell.ts b/packages/s2-core/src/cell/data-cell.ts index 29472a49bb..7c77de9b25 100644 --- a/packages/s2-core/src/cell/data-cell.ts +++ b/packages/s2-core/src/cell/data-cell.ts @@ -180,8 +180,8 @@ export class DataCell extends BaseCell { } } - public setMeta(viewMeta: ViewMeta) { - super.setMeta(viewMeta); + public setMeta(viewMeta: Partial) { + super.setMeta(viewMeta as ViewMeta); this.initCell(); } diff --git a/packages/s2-core/src/cell/table-data-cell.ts b/packages/s2-core/src/cell/table-data-cell.ts index 436794cc6f..1bceb807b8 100644 --- a/packages/s2-core/src/cell/table-data-cell.ts +++ b/packages/s2-core/src/cell/table-data-cell.ts @@ -13,7 +13,6 @@ import { getOrCreateResizeAreaGroupById, getResizeAreaAttrs, } from '../utils/interaction/resize'; -import { checkIsLinkField } from '../utils/interaction/link-field'; export class TableDataCell extends DataCell { protected getLinkFieldStyle() { diff --git a/packages/s2-react/playground/config.ts b/packages/s2-react/playground/config.ts index 899add0ff7..f9eca678eb 100644 --- a/packages/s2-react/playground/config.ts +++ b/packages/s2-react/playground/config.ts @@ -77,7 +77,7 @@ export const pivotSheetDataCfgForCompactMode = customMerge(pivotSheetDataCfg, { }); export const s2Options: SheetComponentOptions = { - debug: false, + debug: true, width: 600, height: 400, frozenFirstRow: false, diff --git a/packages/s2-react/playground/index.tsx b/packages/s2-react/playground/index.tsx index 290dcc68a4..e54366583e 100644 --- a/packages/s2-react/playground/index.tsx +++ b/packages/s2-react/playground/index.tsx @@ -1299,6 +1299,7 @@ function MainLayout() { ref={s2Ref} themeCfg={themeCfg} onMounted={onSheetMounted} + onDataCellEditStart={logHandler('onDataCellEditStart')} onDataCellEditEnd={logHandler('onDataCellEditEnd')} /> diff --git a/packages/s2-react/src/components/sheets/editable-sheet/edit-cell/index.tsx b/packages/s2-react/src/components/sheets/editable-sheet/edit-cell/index.tsx index ee41385145..67e4f31188 100644 --- a/packages/s2-react/src/components/sheets/editable-sheet/edit-cell/index.tsx +++ b/packages/s2-react/src/components/sheets/editable-sheet/edit-cell/index.tsx @@ -1,22 +1,15 @@ import type { Event as CanvasEvent } from '@antv/g-canvas'; import { - BaseCell, S2Event, SpreadSheet, type DataType, type S2CellType, + type TableDataCell, type ViewMeta, } from '@antv/s2'; import { Input } from 'antd'; -import { merge, pick } from 'lodash'; -import React, { - memo, - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; +import { pick } from 'lodash'; +import React from 'react'; import { useS2Event } from '../../../../hooks'; import { useSpreadSheetRef } from '../../../../utils/SpreadSheetContext'; import { @@ -34,9 +27,15 @@ export interface CustomProps { cell: S2CellType; } +type DateCellEdit = (meta: ViewMeta, cell: TableDataCell) => void; + type EditCellProps = { + /** + * @deprecated use `onDataCellEditEnd` instead. + */ onChange?: (data: DataType[]) => void; - onDataCellEditEnd?: (meta: ViewMeta) => void; + onDataCellEditStart?: DateCellEdit; + onDataCellEditEnd?: DateCellEdit; trigger?: number; CustomComponent?: React.FunctionComponent; }; @@ -46,30 +45,32 @@ function EditCellComponent( ) { const { params, resolver } = props; const spreadsheet = useSpreadSheetRef(); - const { event, onChange, onDataCellEditEnd, CustomComponent } = params; - const cell: BaseCell = event.target.cfg.parent; + const { + event, + onChange, + onDataCellEditStart, + onDataCellEditEnd, + CustomComponent, + } = params; - const { left, top, width, height } = useMemo(() => { - const rect = ( - spreadsheet?.container.cfg.container as HTMLElement - ).getBoundingClientRect(); + const cell = spreadsheet.getCell(event.target); + const { left, top, width, height } = React.useMemo>(() => { + const rect = spreadsheet.getCanvasElement()?.getBoundingClientRect(); - const modified = { + return { left: window.scrollX + rect.left, top: window.scrollY + rect.top, width: rect.width, height: rect.height, }; - - return modified; - }, [spreadsheet?.container.cfg.container]); + }, [spreadsheet]); const { x: cellLeft, y: cellTop, width: cellWidth, height: cellHeight, - } = useMemo(() => { + } = React.useMemo(() => { const scroll = spreadsheet.facet.getScrollOffset(); const cellMeta = pick(cell.getMeta(), ['x', 'y', 'width', 'height']); @@ -81,45 +82,46 @@ function EditCellComponent( return cellMeta; }, [cell, spreadsheet]); - const [inputVal, setInputVal] = useState(cell.getMeta().fieldValue); - const inputRef = useRef(null); - const containerRef = useRef(null); + const [inputVal, setInputVal] = React.useState(() => { + return cell.getFieldValue(); + }); - useEffect(() => { - setTimeout(() => { - // 防止触发表格全选 - containerRef.current?.click(); - // 开启 preventScroll, 防止页面有滚动条时触发滚动 - inputRef.current?.focus({ preventScroll: true }); - }); - }, []); + const inputRef = React.useRef(null); + const containerRef = React.useRef(null); const onSave = () => { - const { rowIndex, valueField } = cell.getMeta(); + const { rowIndex, valueField, fieldValue } = cell.getMeta(); + + cell.setMeta({ + fieldValue: inputVal, + originalFieldValue: fieldValue, + data: { + [valueField]: inputVal, + }, + }); + const displayData = spreadsheet.dataSet.getDisplayDataSet(); displayData[rowIndex][valueField] = inputVal; spreadsheet.render(true); - onDataCellEditEnd?.( - merge(cell.getMeta(), { - fieldValue: inputVal, - data: { - [valueField]: inputVal, - }, - }), - ); - + onDataCellEditEnd?.(cell.getMeta(), cell); onChange?.(displayData); resolver(true); }; const onKeyDown: React.KeyboardEventHandler = (e) => { - if (e.keyCode === 13) { + if (e.key === 'Enter') { e.preventDefault(); onSave(); } }; + // 让输入框聚焦时光标在文字的末尾 + const onFocus: React.FocusEventHandler = (e) => { + e.target.selectionStart = e.target.value.length; + e.target.selectionEnd = e.target.value.length; + }; + const styleProps = React.useMemo(() => { return { left: cellLeft, @@ -134,6 +136,16 @@ function EditCellComponent( setInputVal(val); }; + React.useEffect(() => { + onDataCellEditStart?.(cell.getMeta(), cell); + setTimeout(() => { + // 防止触发表格全选 + containerRef.current?.click(); + // 开启 preventScroll, 防止页面有滚动条时触发滚动 + inputRef.current?.focus({ preventScroll: true }); + }); + }, []); + return (
)}
); } -export const EditCell: React.FC = memo( - ({ onChange, onDataCellEditEnd, CustomComponent }) => { - const spreadsheet = useSpreadSheetRef(); - - const cb = useCallback( - (event: CanvasEvent) => { - invokeComponent( - EditCellComponent, - { event, onChange, onDataCellEditEnd, CustomComponent }, - spreadsheet, - ); - }, - [spreadsheet], - ); +export const EditCell: React.FC = React.memo((props) => { + const spreadsheet = useSpreadSheetRef(); + + const cb = React.useCallback( + (event: CanvasEvent) => { + invokeComponent(EditCellComponent, { ...props, event }, spreadsheet); + }, + [spreadsheet], + ); - useS2Event(S2Event.DATA_CELL_DOUBLE_CLICK, cb, spreadsheet); + useS2Event(S2Event.DATA_CELL_DOUBLE_CLICK, cb, spreadsheet); - return null; - }, -); + return null; +}); EditCell.displayName = 'EditCell'; diff --git a/packages/s2-react/src/components/sheets/editable-sheet/index.tsx b/packages/s2-react/src/components/sheets/editable-sheet/index.tsx index 75ef726733..c20a924973 100644 --- a/packages/s2-react/src/components/sheets/editable-sheet/index.tsx +++ b/packages/s2-react/src/components/sheets/editable-sheet/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react'; +import React from 'react'; import { BaseSheet } from '../base-sheet'; import type { SheetComponentsProps } from '../interface'; import { DragCopyPoint } from './drag-copy'; @@ -6,11 +6,10 @@ import { EditCell } from './edit-cell'; export const EditableSheet: React.FC = React.memo( (props) => { - const onChange = useCallback(() => {}, []); return ( diff --git a/packages/s2-shared/src/interface.ts b/packages/s2-shared/src/interface.ts index 9a6b4e0529..594efd20b8 100644 --- a/packages/s2-shared/src/interface.ts +++ b/packages/s2-shared/src/interface.ts @@ -19,6 +19,7 @@ import type { S2RenderOptions, SortParams, SpreadSheet, + TableDataCell, TargetCellInfo, ThemeCfg, TooltipContentType, @@ -121,7 +122,8 @@ export interface BaseSheetComponentProps< onDataCellTrendIconClick?: (meta: ViewMeta) => void; onDataCellBrushSelection?: (brushRangeDataCells: DataCell[]) => void; onDataCellSelectMove?: (metas: CellMeta[]) => void; - onDataCellEditEnd?: (meta: ViewMeta) => void; + onDataCellEditStart?: (meta: ViewMeta, cell: TableDataCell) => void; + onDataCellEditEnd?: (meta: ViewMeta, cell: TableDataCell) => void; // ============== Corner Cell ==================== onCornerCellHover?: (data: TargetCellInfo) => void; diff --git a/s2-site/docs/api/basic-class/base-cell.en.md b/s2-site/docs/api/basic-class/base-cell.en.md index 1dd9727128..bcc5984333 100644 --- a/s2-site/docs/api/basic-class/base-cell.en.md +++ b/s2-site/docs/api/basic-class/base-cell.en.md @@ -7,7 +7,7 @@ Function description: cell base class. [details](https://github.com/antvis/S2/bl | parameter | illustrate | type | | ---------------------- | -------------------------------------- | ----------------------------------------------------------------------- | | getMeta | Get cell metadata | () => [ViewMeta](#viewmeta) | -| setMeta | Set cell metadata | (vieMeta: [ViewMeta](#viewmeta) ) => void | +| setMeta | Set cell metadata | (vieMeta: [Partial](#viewmeta) ) => void | | getIconStyle | Get cell icon style | () => [IconTheme](/docs/api/general/S2Theme#icontheme) | | getStyle | get cell style | () => [DefaultCellTheme](/docs/api/general/S2Theme#defaultcelltheme) | | getTextAndIconPosition | Get the position of cell text and icon | (iconCount: `number` ) => [TextAndIconPosition](#textandiconposition) | diff --git a/s2-site/docs/api/basic-class/base-cell.zh.md b/s2-site/docs/api/basic-class/base-cell.zh.md index 45753b1589..3bd8e03483 100644 --- a/s2-site/docs/api/basic-class/base-cell.zh.md +++ b/s2-site/docs/api/basic-class/base-cell.zh.md @@ -12,7 +12,7 @@ cell.getActualText() | 参数 | 说明 | 类型 | | --- | --- | --- | | getMeta | 获取单元格元数据 | () => [ViewMeta](#viewmeta) | -| setMeta | 设置单元格元数据 | (vieMeta: [ViewMeta](#viewmeta)) => void | +| setMeta | 设置单元格元数据 | (vieMeta: [Partial](#viewmeta)) => void | | getIconStyle | 获取单元格图标样式 | () => [IconTheme](/docs/api/general/S2Theme#icontheme) | | getStyle | 获取单元格样式 | () => [DefaultCellTheme](/docs/api/general/S2Theme#defaultcelltheme) | | getTextAndIconPosition | 获取单元格文本和图标的位置 | (iconCount: `number`) => [TextAndIconPosition](#textandiconposition) | diff --git a/s2-site/docs/api/components/sheet-component.zh.md b/s2-site/docs/api/components/sheet-component.zh.md index d114b3a135..9599998ad6 100644 --- a/s2-site/docs/api/components/sheet-component.zh.md +++ b/s2-site/docs/api/components/sheet-component.zh.md @@ -48,7 +48,8 @@ order: 0 | onDataCellTrendIconClick | 数值单元格的趋势图 icon 点击事件 | (meta: [ViewMeta](/docs/api/basic-class/node)) => void | | | | onDataCellBrushSelection | 数值单元格刷选事件 | ( dataCells: [DataCell](/docs/api/basic-class/base-cell)[] ) => void | | | | onDataCellSelectMove | 数值单元格键盘方向键移动事件 | (metas: CellMeta[]) => void | | | -| onDataCellEditEnd | 数值单元格编辑完成(暂只支持编辑表) | (meta: [ViewMeta](/docs/api/basic-class/node)) => void | | | +| onDataCellEditStart | 数值单元格编辑开始(暂只支持编辑表) | (meta: [ViewMeta](/docs/api/basic-class/node), cell: [S2CellType](/docs/api/basic-class/base-cell)) => void | | | +| onDataCellEditEnd | 数值单元格编辑完成(暂只支持编辑表) | (meta: [ViewMeta](/docs/api/basic-class/node), cell: [S2CellType](/docs/api/basic-class/base-cell)) => void | | | | onCornerCellHover | 角头鼠标悬停事件 | (data: [TargetCellInfo](#targetcellinfo)) => void | | | | onCornerCellClick | 角头鼠标单击事件 | (data: [TargetCellInfo](#targetcellinfo)) => void | | | | onCornerCellDoubleClick | 角头鼠标双击事件 | (data: [TargetCellInfo](#targetcellinfo)) => void | | | diff --git a/s2-site/docs/manual/basic/analysis/editable-mode.zh.md b/s2-site/docs/manual/basic/analysis/editable-mode.zh.md index 813be1d588..165468e54b 100644 --- a/s2-site/docs/manual/basic/analysis/editable-mode.zh.md +++ b/s2-site/docs/manual/basic/analysis/editable-mode.zh.md @@ -167,8 +167,11 @@ ReactDOM.render( sheetType="editable" // 此处指定 sheetType 为 editable dataCfg={s2DataCfg} options={s2Options} - onDataCellEditEnd={(meta) => { - console.log('onDataCellEditEnd', meta); + onDataCellEditStart={(meta, cell) => { + console.log('onDataCellEditStart:', meta, cell); + }} + onDataCellEditEnd={(meta, cell) => { + console.log('onDataCellEditEnd:', meta, cell); }} />, document.getElementById('container') diff --git a/s2-site/examples/react-component/sheet/demo/editable.tsx b/s2-site/examples/react-component/sheet/demo/editable.tsx index fd3025d678..eb053dbfb4 100644 --- a/s2-site/examples/react-component/sheet/demo/editable.tsx +++ b/s2-site/examples/react-component/sheet/demo/editable.tsx @@ -44,8 +44,12 @@ fetch('https://assets.antv.antgroup.com/s2/basic-table-mode.json') frozenTrailingColCount: 1, // 列尾冻结数量 }; - const onDataCellEditEnd = (meta) => { - console.log('onDataCellEditEnd', meta); + const onDataCellEditStart = (meta, cell) => { + console.log('onDataCellEditStart:', meta, cell); + }; + + const onDataCellEditEnd = (meta, cell) => { + console.log('onDataCellEditEnd:', meta, cell); }; ReactDOM.render( @@ -53,6 +57,7 @@ fetch('https://assets.antv.antgroup.com/s2/basic-table-mode.json') dataCfg={s2DataConfig} options={s2Options} sheetType="editable" + onDataCellEditStart={onDataCellEditStart} onDataCellEditEnd={onDataCellEditEnd} />, document.getElementById('container'),