Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/react-vtable/demo/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import listTable from './list-table/list-table';
import issue5203ViteReact19 from './list-table/issue-5203-vite-react19';
import listOptionRecord from './list-table/list-option-records';
import listComponent from './list-table/list-component';
import listCustomLayout from './list-table/list-custom-layout';
Expand Down Expand Up @@ -32,6 +33,7 @@ import { Component, useEffect, useMemo, useState } from 'react';
declare const globalThis: any;

const demoList = [
{ key: 'issue5203ViteReact19', Comp: issue5203ViteReact19 },
{ key: 'listTable', Comp: listTable },
{ key: 'listEditor', Comp: listEditor },
{ key: 'listOptionRecord', Comp: listOptionRecord },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ListTable } from '../../../src';

declare const globalThis: any;

const columns = [
{
field: 'id',
title: 'ID',
width: 120
},
{
field: 'name',
title: 'Name',
width: 200
},
{
field: 'age',
title: 'Age',
width: 120
}
];

const records = [
{ id: 1, name: 'Alice', age: 28 },
{ id: 2, name: 'Bob', age: 31 },
{ id: 3, name: 'Carol', age: 24 }
];

export default function Issue5203ViteReact19() {
return (
<ListTable
records={records}
columns={columns}
width={'100%'}
height={'100%'}
onReady={table => {
(globalThis as any).tableInstance = table;
(globalThis as any).__issue5203Ready = true;
}}
onError={error => {
(globalThis as any).__issue5203Error = error;
}}
/>
);
}
38 changes: 38 additions & 0 deletions packages/react-vtable/demo/vite.config.issue5203.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
import { fileURLToPath } from 'url';
import { createRequire } from 'module';

const require = createRequire(import.meta.url);
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const react19Root = path.resolve(__dirname, '../../../.react19-deps/node_modules');

export default defineConfig({
plugins: [react({ fastRefresh: false })] as any,
define: {
__DEV__: true,
__VERSION__: JSON.stringify(require('../../vtable/package.json').version)
},
server: {
host: '0.0.0.0',
port: 3102
},
resolve: {
alias: [
{ find: /^react$/, replacement: path.resolve(react19Root, 'react/index.js') },
{ find: /^react\/jsx-runtime(\.js)?$/, replacement: path.resolve(react19Root, 'react/jsx-runtime.js') },
{ find: /^react\/jsx-dev-runtime(\.js)?$/, replacement: path.resolve(react19Root, 'react/jsx-dev-runtime.js') },
{ find: /^react-dom$/, replacement: path.resolve(react19Root, 'react-dom/index.js') },
{ find: /^react-dom\/client(\.js)?$/, replacement: path.resolve(react19Root, 'react-dom/client.js') },
{ find: '@visactor/vtable/es/vrender', replacement: path.resolve(__dirname, '../../vtable/src/vrender.ts') },
{ find: '@visactor/vtable/es', replacement: path.resolve(__dirname, '../../vtable/src/') },
{ find: '@visactor/vtable', replacement: path.resolve(__dirname, '../../vtable/src/index.ts') },
{ find: '@visactor/vtable-plugins', replacement: path.resolve(__dirname, '../../vtable-plugins/src/index.ts') },
{ find: '@src', replacement: path.resolve(__dirname, '../../vtable/src/') },
{ find: '@vutils-extension', replacement: path.resolve(__dirname, '../../vtable/src/vutil-extension-temp') }
]
}
});
75 changes: 64 additions & 11 deletions packages/react-vtable/src/table-components/custom/custom-layout.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
/* eslint-disable react-hooks/rules-of-hooks */
import type { PropsWithChildren, ReactElement } from 'react';
import React, { isValidElement, useCallback, useContext, useLayoutEffect, useRef } from 'react';
import React, { isValidElement, useCallback, useContext, useLayoutEffect, useRef, useState } from 'react';
import RootTableContext from '../../context/table';
import { Group } from '@visactor/vtable/es/vrender';
import type { ICustomLayoutFuc, CustomRenderFunctionArg } from '@visactor/vtable/es/ts-types';
import type { FiberRoot } from 'react-reconciler';
import type { ReconcilerErrorReporter } from './reconciler';
import { reconcilor, createReconcilerContainer } from './reconciler';
import type { ReconcilerErrorReporter, ReconcilerErrorType } from './reconciler';

type CustomLayoutProps = { componentId: string };

Expand All @@ -22,6 +21,8 @@ export const CustomLayout: React.FC<CustomLayoutProps> = (props: PropsWithChildr
}
const context = useContext(RootTableContext);
const { table, onError } = context;
const [reconcilerReady, setReconcilerReady] = useState(false);
const reconcilerModule = useRef<ReconcilerModule | null>(null);

const isHeaderCustomLayout = children.props.role === 'header-custom-layout';

Expand All @@ -45,22 +46,48 @@ export const CustomLayout: React.FC<CustomLayoutProps> = (props: PropsWithChildr
[onError]
);

useLayoutEffect(() => {
let released = false;
// Load the custom-layout reconciler only when CustomLayout is actually used.
import('./reconciler')
.then(module => {
if (released) {
return;
}
reconcilerModule.current = module;
setReconcilerReady(true);
})
.catch(error => {
reportReconcilerError('uncaught', error);
});
return () => {
released = true;
};
}, [reportReconcilerError]);

// customLayout function for vtable
const createGraphic: ICustomLayoutFuc = useCallback(
args => {
(args: any) => {
const module = reconcilerModule.current;
if (!module) {
return {
rootContainer: new Group({}),
renderDefault: !!children.props.renderDefault
};
}
const key = `${args.originCol ?? args.col}-${args.originRow ?? args.row}${
args.forComputation ? '-forComputation' : ''
}`;
let group;
if (container.current.has(key)) {
const currentContainer = container.current.get(key);
reconcilorUpdateContainer(children, currentContainer, args);
reconcilorUpdateContainer(module, children, currentContainer, args);
group = currentContainer.containerInfo;
} else {
group = new Group({});
const currentContainer = createReconcilerContainer(group as any, 'custom', reportReconcilerError);
const currentContainer = module.createReconcilerContainer(group as any, 'custom', reportReconcilerError);
container.current.set(key, currentContainer);
reconcilorUpdateContainer(children, currentContainer, args);
reconcilorUpdateContainer(module, children, currentContainer, args);
}

return {
Expand All @@ -72,20 +99,29 @@ export const CustomLayout: React.FC<CustomLayoutProps> = (props: PropsWithChildr
);

const removeContainer = useCallback((col: number, row: number) => {
const module = reconcilerModule.current;
if (!module) {
return;
}
const key = `${col}-${row}`;
if (container.current.has(key)) {
const currentContainer = container.current.get(key);
reconcilor.updateContainer(null, currentContainer, null);
module.reconcilor.updateContainer(null, currentContainer, null);
// group = currentContainer.containerInfo;
currentContainer.containerInfo.delete();
container.current.delete(key);
}
}, []);

const removeAllContainer = useCallback(() => {
const module = reconcilerModule.current;
if (!module) {
container.current.clear();
return;
}
container.current.forEach((value, key) => {
const currentContainer = value;
reconcilor.updateContainer(null, currentContainer, null);
module.reconcilor.updateContainer(null, currentContainer, null);
currentContainer.containerInfo.delete();
});
container.current.clear();
Expand All @@ -108,6 +144,9 @@ export const CustomLayout: React.FC<CustomLayoutProps> = (props: PropsWithChildr
// eslint-disable-next-line no-undef
console.log('update props', props, table);

if (!reconcilerReady) {
return;
}
table?.checkReactCustomLayout(); // init reactCustomLayout component
table?.reactCustomLayout?.setReactRemoveAllGraphic(componentId, removeAllContainer, isHeaderCustomLayout); // set customLayout function

Expand All @@ -129,6 +168,10 @@ export const CustomLayout: React.FC<CustomLayoutProps> = (props: PropsWithChildr
); // update customLayout function
// update all container
container.current.forEach((value, key) => {
const module = reconcilerModule.current;
if (!module) {
return;
}
const [col, row] = key.split('-').map(Number);
// const width = table.getColWidth(col); // to be fixed: may be merge cell
// const height = table.getRowHeight(row); // to be fixed: may be merge cell
Expand All @@ -151,7 +194,7 @@ export const CustomLayout: React.FC<CustomLayoutProps> = (props: PropsWithChildr
};
// update element in container
const group = currentContainer.containerInfo;
reconcilorUpdateContainer(children, currentContainer, args);
reconcilorUpdateContainer(module, children, currentContainer, args);
// reconcilor.updateContainer(React.cloneElement(children, { ...args }), currentContainer, null);
table.scenegraph.updateNextFrame();
});
Expand All @@ -161,8 +204,18 @@ export const CustomLayout: React.FC<CustomLayoutProps> = (props: PropsWithChildr
return null;
};

function reconcilorUpdateContainer(children: ReactElement, currentContainer: any, args: any) {
type ReconcilerModule = {
reconcilor: any;
createReconcilerContainer: (
container: any,
identifierPrefix?: string,
reportError?: (type: ReconcilerErrorType, error: unknown) => void
) => FiberRoot;
};

function reconcilorUpdateContainer(module: ReconcilerModule, children: ReactElement, currentContainer: any, args: any) {
const element = React.cloneElement(children, { ...args });
const { reconcilor } = module;
const updateContainerSync = (reconcilor as any).updateContainerSync;
if (typeof updateContainerSync === 'function') {
updateContainerSync(element, currentContainer, null);
Expand Down
Loading