feat: generator add function of getDataSource from ref

This commit is contained in:
helloqian12138 2021-12-20 21:45:05 +08:00
parent 8593e235d7
commit 8fc99d2a25
11 changed files with 128 additions and 95 deletions

View File

@ -1,3 +1,4 @@
/* stylelint-disable selector-class-pattern */
.__dumi-default-layout[data-route="/drip-table-generator/preview"] {
padding-top: 64px;
padding-left: 0;
@ -12,5 +13,9 @@
padding: 16px 16px;
border-bottom: 1px solid #e6e6ee;
box-shadow: 0 1px 4px -1px rgba(0, 0, 0, .05);
.header-button {
margin: 0 12px;
}
}
}

View File

@ -8,7 +8,7 @@ import React from 'react';
import { Button, Row } from 'antd';
import { DripTableSchema } from 'drip-table';
import DripTableDriverAntDesign from 'drip-table-driver-antd';
import DripTableGenerator from 'drip-table-generator';
import DripTableGenerator, { DripTableGeneratorHandler } from 'drip-table-generator';
import 'antd/dist/antd.css';
import 'drip-table-generator/index.css';
@ -53,9 +53,7 @@ const initialSchema: DripTableSchema = {
const Demo = (props: { showHeader: boolean }) => {
const generator: React.MutableRefObject<null | {
getSchemaValue: () => void;
}> = React.useRef(null);
const generator: React.MutableRefObject<DripTableGeneratorHandler | null> = React.useRef(null);
const views = {
demoHeader: props.showHeader !== false,
@ -65,17 +63,18 @@ const Demo = (props: { showHeader: boolean }) => {
<React.Fragment>
{ views.demoHeader && (
<Row className="sample-header-extra-container">
<Button type="primary" onClick={() => { console.log(generator.current?.getSchemaValue()); }}>schema</Button>
<Button className="header-button" type="primary" onClick={() => { console.log(generator.current?.getSchemaValue()); }}>schema</Button>
<Button className="header-button" type="primary" onClick={() => { console.log(generator.current?.getDataSource()); }}>dataSource</Button>
</Row>
)}
<DripTableGenerator
ref={generator}
style={{ height: 720 }}
style={{ height: 640 }}
driver={DripTableDriverAntDesign}
schema={initialSchema}
dataSource={mockData.slice(0, 4)}
dataFields={['id', 'name', 'status', 'description', 'ext.state']}
onExportSchema={(schema) => { console.log(schema); }}
onExportSchema={schema => { console.log(schema); }}
customComponents={{ custom: { TextComponent } }}
customComponentPanel={components}
/>

View File

@ -1,7 +1,4 @@
import { createContext } from 'react';
import { DripTableRecordTypeBase } from 'drip-table';
import { DripTableGeneratorProps } from '@/typing';
export type IDripTableGeneratorContext<RecordType extends DripTableRecordTypeBase> = DripTableGeneratorProps<RecordType>;
export const Ctx = createContext<IDripTableGeneratorContext<Record<string, unknown>>>({});
export const Ctx = createContext<DripTableGeneratorProps>({});

View File

@ -1,17 +1,19 @@
import React, { useState, forwardRef, useImperativeHandle, useRef } from 'react';
import { ConfigProvider } from 'antd';
import { ColumnConfig, DripTableRecordTypeBase, DripTableSchema } from 'drip-table';
import { ColumnConfig, DripTableSchema } from 'drip-table';
import zhCN from 'antd/lib/locale/zh_CN';
import { Ctx } from '@/context';
import { DripTableGeneratorProps, DripTableGeneratorHandler } from '@/typing';
import { defaultState, GlobalStore } from '@/store';
import { defaultState, DripTableGeneratorState, GlobalStore } from '@/store';
import Wrapper from './wrapper';
import Wrapper, { GeneratorWrapperHandler } from './wrapper';
import 'antd/dist/antd.less';
const useTableRoot = (props, store, wrapper) => {
const useTableRoot = (
props: DripTableGeneratorProps,
store: [DripTableGeneratorState, React.Dispatch<React.SetStateAction<DripTableGeneratorState>>],
wrapper: React.MutableRefObject<GeneratorWrapperHandler | null>,
) => {
const [state, setState] = store;
const getSchemaValue = (): DripTableSchema => {
@ -36,24 +38,34 @@ const useTableRoot = (props, store, wrapper) => {
};
};
const getDataSource = () => {
if (wrapper.current) {
const currentState = wrapper.current.getState();
return currentState.previewDataSource;
}
return state.previewDataSource;
};
const context = {
...props,
...state,
getSchemaValue,
getDataSource,
setGlobalData: setState,
};
return context;
};
const Container = <RecordType extends DripTableRecordTypeBase>(props: DripTableGeneratorProps<RecordType>, ref: React.ForwardedRef<DripTableGeneratorHandler>) => {
const wrapper = useRef({});
const Container = (props: DripTableGeneratorProps, ref: React.ForwardedRef<DripTableGeneratorHandler>) => {
const wrapper = useRef(null);
const initialState = defaultState();
const store = useState(initialState);
const context = useTableRoot(props, store, wrapper);
useImperativeHandle(ref, () => context);
const WrapperRef = forwardRef<unknown, DripTableGeneratorProps<RecordType> & { store: GlobalStore }>(Wrapper);
const WrapperRef = forwardRef<GeneratorWrapperHandler, DripTableGeneratorProps & { store: GlobalStore }>(Wrapper);
return (
<ConfigProvider locale={zhCN}>

View File

@ -7,7 +7,7 @@
*/
import React from 'react';
import { Result, Tabs } from 'antd';
import { Alert, Result, Tabs } from 'antd';
import { ExclamationCircleTwoTone } from '@ant-design/icons';
import { DripTableSchema } from 'drip-table';
import MonacoEditor from '@monaco-editor/react';
@ -38,12 +38,14 @@ const { TabPane } = Tabs;
const AttributeLayout = (props: Props & { store: GlobalStore }) => {
const { dataFields } = useGlobalData();
const [state, setState] = props.store;
const store = { state, setState };
const [activeKey, setActiveKey] = React.useState('0');
const [state, actions] = props.store;
const store = { state, setState: actions };
const [codeErrorMessage, setCodeErrorMessage] = React.useState('');
const [code, setCode] = React.useState(JSON.stringify(state.dataSource, null, 4));
const code = JSON.stringify(state.previewDataSource, null, 4);
const getActiveKey = () => {
if (activeKey === '0') {
@ -129,17 +131,22 @@ const AttributeLayout = (props: Props & { store: GlobalStore }) => {
} as DripTableColumn;
};
/** TODO: 报错逻辑后续优化 */
function submitDataWithoutThrottle() {
/** TODO:
*
* @param {string} codeValue JSON
*/
const submitDataWithoutDebounce = (codeValue: string) => {
setCodeErrorMessage('');
try {
state.dataSource = JSON.parse(code);
globalActions.updateDataSource(store);
state.previewDataSource = JSON.parse(codeValue);
setState({ ...state });
globalActions.updatePreviewDataSource(store);
} catch (error) {
console.error(error);
setCodeErrorMessage((error as Error).message);
}
}
};
const submitTableData = debounce(submitDataWithoutThrottle, 1000);
const submitTableData = debounce(submitDataWithoutDebounce, 1000);
const renderGlobalForm = () => (
<CustomForm<GlobalSchema>
@ -169,7 +176,7 @@ const AttributeLayout = (props: Props & { store: GlobalStore }) => {
return;
}
if (uiProps.from === 'dataSource') {
uiProps.options = Object.keys(state.dataSource[0] || {})
uiProps.options = Object.keys(state.previewDataSource[0] || {})
.map(key => ({ label: key, value: key }));
} else if (uiProps.from === 'dataFields') {
uiProps.options = dataFields?.map(key => ({ label: key, value: key })) || [];
@ -181,7 +188,7 @@ const AttributeLayout = (props: Props & { store: GlobalStore }) => {
return;
}
if (itemUiProps.from === 'dataSource') {
itemUiProps.options = Object.keys(state.dataSource[0] || {})
itemUiProps.options = Object.keys(state.previewDataSource[0] || {})
.map(key => ({ label: key, value: key }));
} else if (itemUiProps.from === 'dataFields') {
itemUiProps.options = dataFields?.map(key => ({ label: key, value: key })) || [];
@ -226,13 +233,14 @@ const AttributeLayout = (props: Props & { store: GlobalStore }) => {
</TabPane>
<TabPane tab="表格数据" key="3" style={{ height: tabHeight, overflow: 'auto' }}>
<div className={styles['attributes-code-panel']}>
{ codeErrorMessage && <Alert style={{ margin: '8px 0' }} message={codeErrorMessage} type="error" showIcon /> }
<MonacoEditor
width="100%"
height={400}
language="json"
theme="vs-dark"
value={code || ''}
onChange={(value) => { setCode(value || ''); submitTableData(); }}
onChange={(value) => { submitTableData(value); }}
/>
</div>
</TabPane>

View File

@ -18,12 +18,12 @@ import Draggable from '@/components/Draggable';
import styles from './index.module.less';
import { get } from '@/utils';
interface Props<RecordType extends DripTableRecordTypeBase> {
driver: DripTableDriver<RecordType>;
customComponents: DripTableProps<RecordType>['components'] | undefined;
interface Props {
driver: DripTableDriver<DripTableRecordTypeBase>;
customComponents: DripTableProps<DripTableRecordTypeBase>['components'] | undefined;
}
const EditableTable = <RecordType extends DripTableRecordTypeBase>(props: Props<RecordType> & { store: GlobalStore }) => {
const EditableTable = (props: Props & { store: GlobalStore }) => {
const [state, actions] = props.store;
const store = { state, setState: actions };
@ -36,8 +36,8 @@ const EditableTable = <RecordType extends DripTableRecordTypeBase>(props: Props<
const DripTableComponent = column['ui:type'].startsWith('custom::')
? customComponents[column['ui:type'].replace('custom::', '')]
: builtInComponents[column['ui:type']];
const hasRecord = !(!state.dataSource || state.dataSource.length <= 0);
const record = state.dataSource[0] || {} as Record<string, unknown>;
const hasRecord = !(!state.previewDataSource || state.previewDataSource.length <= 0);
const record = state.previewDataSource[0] || {} as Record<string, unknown>;
const value = column.dataIndex ? get(record, column.dataIndex) : record;
const errorBoundary = () => {
@ -92,23 +92,23 @@ const EditableTable = <RecordType extends DripTableRecordTypeBase>(props: Props<
<div className={styles['column-title']}>{ col.title }</div>
{ previewComponentRender(col) }
{ isCurrent && (
<CloseCircleTwoTone
className={styles['close-icon']}
twoToneColor="#ff4d4f"
onClick={() => {
const index = state.columns.findIndex(item => item.$id === state.currentColumn?.$id);
if (index > -1) {
state.columns.splice(index, 1);
for (let i = index; i < state.columns.length; i++) {
state.columns[i].key = i + 1;
state.columns[i].sort = i + 1;
}
state.currentColumn = void 0;
globalActions.editColumns(store);
globalActions.checkColumn(store);
<CloseCircleTwoTone
className={styles['close-icon']}
twoToneColor="#ff4d4f"
onClick={() => {
const index = state.columns.findIndex(item => item.$id === state.currentColumn?.$id);
if (index > -1) {
state.columns.splice(index, 1);
for (let i = index; i < state.columns.length; i++) {
state.columns[i].key = i + 1;
state.columns[i].sort = i + 1;
}
}}
/>
state.currentColumn = void 0;
globalActions.editColumns(store);
globalActions.checkColumn(store);
}
}}
/>
) }
</div>
);
@ -117,21 +117,21 @@ const EditableTable = <RecordType extends DripTableRecordTypeBase>(props: Props<
return (
<div style={{ padding: '12px 0 12px 12px', overflowX: 'auto' }}>
{
state.columns && state.columns.length > 0
? (
<Draggable<DripTableColumn>
value={(state.columns || []) as DripTableColumn[]}
codeKey="sort"
style={{ position: 'relative' }}
onChange={(data) => {
state.columns = [...data];
globalActions.editColumns(store);
}}
render={renderTableCell}
/>
)
: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无表格配置" />
}
state.columns && state.columns.length > 0
? (
<Draggable<DripTableColumn>
value={(state.columns || []) as DripTableColumn[]}
codeKey="sort"
style={{ position: 'relative' }}
onChange={(data) => {
state.columns = [...data];
globalActions.editColumns(store);
}}
render={renderTableCell}
/>
)
: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无表格配置" />
}
</div>
);
};

View File

@ -13,12 +13,12 @@ import { GlobalStore } from '@/store';
import styles from './index.module.less';
interface Props<RecordType extends DripTableRecordTypeBase> {
driver: DripTableDriver<RecordType>;
customComponents: DripTableProps<RecordType>['components'];
interface Props {
driver: DripTableDriver<DripTableRecordTypeBase>;
customComponents: DripTableProps<DripTableRecordTypeBase>['components'];
}
const PreviewTable = <RecordType extends DripTableRecordTypeBase>(props: Props<RecordType> & { store: GlobalStore }) => {
const PreviewTable = (props: Props & { store: GlobalStore }) => {
const [state] = props.store;
const schema: DripTableSchema = {
@ -28,14 +28,14 @@ const PreviewTable = <RecordType extends DripTableRecordTypeBase>(props: Props<R
},
columns: state.columns as ColumnConfig[],
};
const totalPage = state.globalConfigs?.pagination && state.globalConfigs?.pagination.pageSize ? state.dataSource.length : 1;
const totalPage = state.globalConfigs?.pagination && state.globalConfigs?.pagination.pageSize ? state.previewDataSource.length : 1;
return (
<div className={styles['table-preview-wrapper']}>
<DripTable<RecordType>
<DripTable
driver={(props.driver || DripTableDriverAntDesign)}
schema={schema}
total={totalPage}
dataSource={state.dataSource as RecordType[]}
dataSource={state.previewDataSource}
components={props.customComponents}
/>
</div>

View File

@ -30,9 +30,9 @@ const ToolLayout = (props: { store: GlobalStore }) => {
});
/**
* Modal用来展示JSON Schema配置
* @returns {JSX.Element} React组件
*/
* Modal用来展示JSON Schema配置
* @returns {JSX.Element} React组件
*/
const renderSchemaModal = () => {
if (modalStatus !== 'export' && modalStatus !== 'import') {
return null;
@ -109,6 +109,7 @@ const ToolLayout = (props: { store: GlobalStore }) => {
globalData.onExportSchema(getSchemaValue());
}
}
message.success('复制成功');
setModalStatus('');
setCode('');
}}

View File

@ -23,7 +23,7 @@ export interface DripTableGeneratorState {
currentColumn?: DripTableColumn;
globalConfigs: DripTableSchema['configs'];
/** 表格数据generator不需要知道数据格式是什么直接交给drip-table即可 */
dataSource: Record<string, unknown>[];
previewDataSource: Record<string, unknown>[];
}
export const defaultState: () => DripTableGeneratorState = () => ({
@ -37,7 +37,7 @@ export const defaultState: () => DripTableGeneratorState = () => ({
pagination: false,
},
/** 数据 */
dataSource: [],
previewDataSource: [],
});
export type GlobalStore = [DripTableGeneratorState, React.Dispatch<React.SetStateAction<DripTableGeneratorState>>];
@ -51,7 +51,7 @@ export type GlobalActions = {
toggleEditMode: (store?: GlobalStoreObject) => void;
editColumns: (store?: GlobalStoreObject) => void;
checkColumn: (store?: GlobalStoreObject) => void;
updateDataSource: (store?: GlobalStoreObject) => void;
updatePreviewDataSource: (store?: GlobalStoreObject) => void;
updateGlobalConfig: (store?: GlobalStoreObject) => void;
}
@ -65,8 +65,8 @@ export const globalActions: GlobalActions = {
checkColumn(store) {
store?.setState({ ...store.state, currentColumn: store.state.currentColumn ? { ...store.state?.currentColumn } : void 0 });
},
updateDataSource(store) {
store?.setState({ ...store.state, dataSource: [...store.state.dataSource] });
updatePreviewDataSource(store) {
store?.setState({ ...store.state, previewDataSource: [...store.state.previewDataSource] });
},
updateGlobalConfig(store) {
store?.setState({ ...store.state, globalConfigs: { ...store.state.globalConfigs } });

View File

@ -48,19 +48,27 @@ export interface DripTableGeneratorHandler extends DripTableGeneratorState {
* @returns { DripTableSchema } DripTableSchema配置
*/
getSchemaValue: () => DripTableSchema;
/**
*
*
* @returns {Record<string, unknown>[]}
*/
getDataSource: () => Record<string, unknown>[];
}
export interface DripTableGeneratorProps<RecordType extends DripTableRecordTypeBase, CustomComponentEvent extends EventLike = never, Ext = unknown> {
export interface DripTableGeneratorProps<CustomComponentEvent extends EventLike = never, Ext = unknown> {
style?: CSSProperties;
driver?: DripTableDriver<RecordType>;
driver?: DripTableDriver<DripTableRecordTypeBase>;
showComponentLayout?: boolean;
componentLayoutStyle?: CSSProperties;
rightLayoutStyle?: CSSProperties;
showToolLayout?: boolean;
dataSource?: RecordType[];
/** generator无需关心DataSource数据类型是什么唯一做的是直接传递给drip-table */
dataSource?: DripTableRecordTypeBase[];
dataFields?: string[];
schema?: DripTableSchema;
customComponents?: DripTableProps<RecordType, CustomComponentEvent, Ext>['components'];
customComponents?: DripTableProps<DripTableRecordTypeBase, CustomComponentEvent, Ext>['components'];
customComponentPanel?: {
mode: 'add' | 'replace';
components: DripTableComponentConfig[];

View File

@ -1,5 +1,4 @@
import React, { useState, useImperativeHandle } from 'react';
import { DripTableRecordTypeBase } from 'drip-table';
import DripTableDriverAntDesign from 'drip-table-driver-antd';
import { useGlobalData } from './hooks';
import { defaultState, DripTableGeneratorState, GlobalStore, setState } from './store';
@ -12,9 +11,13 @@ import { DripTableGeneratorProps } from './typing';
import styles from './index.module.less';
const Wrapper = <RecordType extends DripTableRecordTypeBase>(props: DripTableGeneratorProps<RecordType> & {
export type GeneratorWrapperHandler = {
getState: () => DripTableGeneratorState;
}
const Wrapper = (props: DripTableGeneratorProps & {
store: GlobalStore;
}, ref) => {
}, ref: React.ForwardedRef<GeneratorWrapperHandler>) => {
const {
style = {},
driver,
@ -22,12 +25,12 @@ const Wrapper = <RecordType extends DripTableRecordTypeBase>(props: DripTableGen
componentLayoutStyle = {},
rightLayoutStyle = {},
showToolLayout = true,
dataSource = [],
dataSource,
schema,
customComponentPanel,
customComponents,
} = useGlobalData();
const initialData = { dataSource } as DripTableGeneratorState;
const initialData = { previewDataSource: dataSource } as DripTableGeneratorState;
if (schema) {
initialData.globalConfigs = schema.configs;
initialData.columns = schema.columns?.map((item, index) => ({ key: index, sort: index, ...item }));