@/store#globalActions TypeScript Examples
The following examples show how to use
@/store#globalActions.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.tsx From drip-table with MIT License | 4 votes |
AttributeLayout = (props: Props & { store: GlobalStore }) => {
const { dataFields, mockDataSource: isDemo, slots } = useGlobalData();
const [state, setState] = props.store;
const store = { state, setState };
const [activeKey, setActiveKey] = React.useState('0');
const [formDisplayMode, setFormDisplayMode] = React.useState('tabs' as 'collapse' | 'tabs');
const [codeErrorMessage, setCodeErrorMessage] = React.useState('');
const [code, setCode] = React.useState(JSON.stringify(state.previewDataSource, null, 4));
const getActiveKey = () => {
if (activeKey === '0') {
return state.currentColumn ? '1' : '2';
}
return activeKey;
};
const getComponents = () => {
let componentsToUse = components;
if (props.customComponentPanel) {
const customComponents = props.customComponentPanel.configs;
componentsToUse = props.customComponentPanel.mode === 'add' ? [...components, ...customComponents] : [...customComponents];
}
return [...componentsToUse];
};
/**
* 将全局配置转换成FormData
* @param {GlobalSchema} globalConfigs 全局配置
* @returns {Record<string, unknown>} 表单数据
*/
const decodeGlobalConfigs = (globalConfigs?: GlobalSchema) => {
const formData: Record<string, unknown> = { ...filterAttributes(globalConfigs, 'header') };
if (typeof globalConfigs?.header === 'object') {
formData.header = true;
const headerElements = globalConfigs?.header?.elements || [];
for (const headerItem of headerElements) {
if (headerItem.type === 'spacer') {
headerItem['style.width'] = headerItem.style?.width;
}
if (headerItem.type === 'search') {
headerItem['wrapperStyle.width'] = headerItem.wrapperStyle?.width;
}
}
formData['header.items'] = headerElements;
}
if (typeof globalConfigs?.footer === 'object') {
formData.footer = true;
const footerElements = globalConfigs?.footer?.elements || [];
for (const footerItem of footerElements) {
if (footerItem.type === 'text') {
footerItem['style.width'] = footerItem.style?.width;
}
if (footerItem.type === 'search') {
footerItem['wrapperStyle.width'] = footerItem.wrapperStyle?.width;
}
}
formData['footer.items'] = footerElements;
}
if (typeof globalConfigs?.pagination === 'object') {
formData.pagination = true;
Object.keys(globalConfigs?.pagination || {}).forEach((key) => {
formData[`pagination.${key}`] = globalConfigs?.pagination?.[key];
});
}
return formData;
};
const encodeGlobalConfigs = (formData: { [key: string]: unknown }): GlobalSchema => {
const formatElement = (element) => {
if (element.type === 'display-column-selector') {
return {
type: 'display-column-selector',
selectorButtonType: element.selectorButtonType,
selectorButtonText: element.selectorButtonText,
};
}
if (element.type === 'spacer') {
const width = element['style.width'];
element['style.width'] = void 0;
return {
type: 'spacer',
style: { width },
};
}
if (element.type === 'text') {
return {
type: 'text',
span: element.span,
align: element.align,
text: element.text,
};
}
if (element.type === 'search') {
const width = element['wrapperStyle.width'];
element['wrapperStyle.width'] = void 0;
return {
type: 'search',
wrapperStyle: { width },
align: element.align,
placeholder: element.placeholder,
allowClear: element.allowClear,
searchButtonText: element.searchButtonText,
searchKeys: element.searchKeys,
searchKeyDefaultValue: element.searchKeyDefaultValue,
};
}
if (element.type === 'insert-button') {
return {
type: 'insert-button',
align: element.align,
insertButtonText: element.insertButtonText,
showIcon: element.showIcon,
};
}
return { ...element };
};
return {
...filterAttributesByRegExp(formData, /^(footer|header|pagination)\./u),
$version: formData.$version as number,
bordered: formData.bordered as boolean,
size: formData.size as 'small' | 'middle' | 'large' | undefined,
ellipsis: formData.ellipsis as boolean,
sticky: formData.sticky as boolean,
rowSelection: formData.rowSelection as boolean,
virtual: formData.virtual as boolean,
scroll: {
x: formData.scrollX as number,
y: formData.scrollY as number,
},
header: formData.header
? {
style: { margin: '0', padding: '12px 0' },
elements: (formData['header.items'] as DripTableGenericRenderElement[] || []).map(item => ({ ...formatElement(item) })),
}
: false,
footer: formData.footer
? {
style: { margin: '0', padding: '12px 0' },
elements: (formData['footer.items'] as DripTableGenericRenderElement[] || []).map(item => ({ ...formatElement(item) })),
}
: void 0,
pagination: formData.pagination
? {
size: formData['pagination.size'] as 'small' | 'default',
pageSize: formData['pagination.pageSize'] as number,
position: formData['pagination.position'] as 'bottomLeft' | 'bottomCenter' | 'bottomRight',
showQuickJumper: formData['pagination.showQuickJumper'] as boolean,
showSizeChanger: formData['pagination.showSizeChanger'] as boolean,
showTotal: formData['pagination.showTotal'] as string,
}
: false,
};
};
const encodeColumnConfigs = (formData: { [key: string]: unknown }): DripTableColumn => {
const uiProps: Record<string, unknown> = {};
const dataProps = {};
Object.keys(formData).forEach((key) => {
if (key.startsWith('options.')) {
uiProps[key.replace('options.', '')] = formData[key];
} else if (key.startsWith('ui:props.')) {
uiProps[key.replace('ui:props.', '')] = formData[key];
} else {
dataProps[key] = formData[key];
}
});
if (state.currentColumn?.component === 'group') {
const length = (uiProps.layout as number[])?.reduce((p, v) => p + v, 0) || 0;
uiProps.items = Array.from({ length }, _ => null);
if (state.currentColumn.options.items) {
(state.currentColumn.options.items as Record<string, unknown>[])
.slice(0, length)
.forEach((item, i) => { (uiProps.items as Record<string, unknown>[])[i] = item; });
}
}
// const columnConfig = getComponents().find(item => item['ui:type'] === state.currentColumn?.component);
return {
...filterAttributes(dataProps, [
'options',
'component',
'ui:props',
'ui:type',
'type',
'name',
'dataIndex',
'title',
'width',
'group',
]),
key: state.currentColumn?.key ?? '',
index: state.currentColumn?.index ?? 0,
sort: state.currentColumn?.sort ?? 0,
dataIndex: formData.dataIndex as string | string[],
title: formData.title as string,
width: formData.width as string,
component: state.currentColumn?.component ?? '',
options: uiProps,
};
};
const encodeColumnConfigsByPath = (formData: { [key: string]: unknown }): DripTableColumnSchema | DripTableBuiltInColumnSchema | null => {
const uiProps: Record<string, unknown> = {};
const dataProps = {};
Object.keys(formData).forEach((key) => {
if (key.startsWith('options.')) {
uiProps[key.replace('options.', '')] = formData[key];
} else if (key.startsWith('ui:props.')) {
uiProps[key.replace('ui:props.', '')] = formData[key];
} else {
dataProps[key] = formData[key];
}
});
if (state.currentColumn) {
const currentColumnItem = getColumnItemByPath(state.currentColumn, state.currentColumnPath || []);
if (currentColumnItem?.component === 'group') {
const length = (uiProps.layout as number[])?.reduce((p, v) => p + v, 0) || 0;
uiProps.items = Array.from({ length }, _ => null);
if (currentColumnItem.options.items) {
currentColumnItem.options.items.slice(0, length).forEach((item, i) => {
(uiProps.items as Record<string, unknown>[])[i] = item;
});
}
}
return {
...filterAttributes(dataProps, [
'options',
'component',
'ui:props',
'ui:type',
'type',
'name',
'dataIndex',
'title',
'width',
'group',
]),
key: currentColumnItem.key,
title: '',
dataIndex: formData.dataIndex as string | string[],
component: currentColumnItem.component ?? '',
options: uiProps,
};
}
return null;
};
const submitTableData = (codeValue?: string) => {
setCodeErrorMessage('');
setCode(codeValue || '');
try {
state.previewDataSource = JSON.parse(codeValue || '');
setState({ ...state });
globalActions.updatePreviewDataSource(store);
} catch (error) {
setCodeErrorMessage((error as Error).message);
}
};
const getGlobalFormConfigs = () => {
let globalFormConfigs = GlobalAttrFormConfigs;
if (props.customGlobalConfigPanel) {
globalFormConfigs = props.customGlobalConfigPanel?.mode === 'add'
? [
...GlobalAttrFormConfigs,
...props.customGlobalConfigPanel?.configs || [],
]
: [...props.customGlobalConfigPanel?.configs || []];
}
globalFormConfigs = globalFormConfigs.map((config) => {
const uiProps = config['ui:props'];
if (uiProps?.optionsParam === '$$SLOT_NAME_OPTIONS$$') {
config = {
...config,
'ui:props': {
...uiProps,
options: Object.keys(slots || {}).map(key => ({ label: key, value: key })),
},
};
}
if (uiProps?.items) {
const uiPropsItems = (uiProps.items as DTGComponentPropertySchema[])?.map((item, index) => {
const itemUiProps = item['ui:props'];
if (itemUiProps?.optionsParam === '$$SLOT_NAME_OPTIONS$$') {
itemUiProps.options = Object.keys(slots || {}).map(key => ({ label: key, value: key }));
}
return {
...item,
'ui:props': itemUiProps,
};
});
config = {
...config,
'ui:props': {
...uiProps,
items: [...uiPropsItems],
},
};
}
return config;
});
return globalFormConfigs;
};
const renderGlobalForm = () => (
<CustomForm<GlobalSchema>
primaryKey="$version"
configs={getGlobalFormConfigs()}
data={cloneDeep(state.globalConfigs)}
decodeData={decodeGlobalConfigs}
encodeData={encodeGlobalConfigs}
groupType={formDisplayMode}
theme={props.driver}
onChange={(data) => {
store.state.globalConfigs = cloneDeep({ ...data });
globalActions.updateGlobalConfig(store);
}}
/>
);
const getColumnConfigs = (componentType: string) => {
const columnConfig = getComponents().find(schema => schema['ui:type'] === componentType);
columnConfig?.attrSchema.forEach((schema) => {
const uiProps = schema['ui:props'];
if (!uiProps) {
return;
}
if (uiProps.optionsParam === '$$FIELD_KEY_OPTIONS$$') {
uiProps.options = isDemo
? Object.keys(state.previewDataSource[0] || {}).map(key => ({ label: key, value: key }))
: dataFields?.map(key => ({ label: key, value: key })) || [];
}
if (uiProps.items) {
(uiProps.items as DTGComponentPropertySchema[])?.forEach((item, index) => {
const itemUiProps = item['ui:props'];
if (!itemUiProps) {
return;
}
if (itemUiProps.optionsParam === '$$FIELD_KEY_OPTIONS$$') {
itemUiProps.options = isDemo
? Object.keys(state.previewDataSource[0] || {}).map(key => ({ label: key, value: key }))
: dataFields?.map(key => ({ label: key, value: key })) || [];
}
});
}
});
return columnConfig;
};
const errorBoundary = (message?: string) => (
<Result
icon={<ExclamationCircleTwoTone />}
title={<div style={{ color: '#999' }}>{ message }</div>}
/>
);
const renderColumnForm = () => {
if (!state.currentColumn) {
return errorBoundary('请点击选择要编辑的列');
}
const columnConfig = getColumnConfigs(state.currentColumn?.component || '');
return (
<CustomForm<DripTableColumn>
primaryKey="key"
configs={columnConfig ? columnConfig.attrSchema || [] : []}
data={state.currentColumn}
encodeData={encodeColumnConfigs}
extendKeys={['ui:props', 'options']}
groupType={formDisplayMode}
theme={props.driver}
onChange={(data) => {
state.currentColumn = Object.assign(state.currentColumn, data);
const idx = state.columns.findIndex(item => item.key === state.currentColumn?.key);
if (idx > -1 && state.currentColumn) {
state.columns[idx] = state.currentColumn;
}
globalActions.editColumns(store);
globalActions.checkColumn(store);
}}
/>
);
};
const renderColumnFormItem = () => {
if (!state.currentColumn || (state.currentColumnPath?.length || 0) <= 0) {
return errorBoundary('请点击选择要编辑的子元素');
}
const currentColumnItem = getColumnItemByPath(state.currentColumn, state.currentColumnPath || []);
const columnConfig = getColumnConfigs(currentColumnItem?.component || '');
const tableCellBaseProps = new Set(basicColumnAttrComponents.map(prop => prop.name));
if (columnConfig) {
columnConfig.attrSchema = columnConfig.attrSchema.filter(item => !tableCellBaseProps.has(item.name));
}
if (!currentColumnItem || !currentColumnItem.component) {
return errorBoundary('子元素暂未配置组件');
}
return (
<CustomForm<DripTableColumnSchema | DripTableBuiltInColumnSchema | null>
primaryKey="key"
configs={columnConfig ? columnConfig.attrSchema || [] : []}
data={currentColumnItem}
encodeData={encodeColumnConfigsByPath}
extendKeys={['ui:props', 'options']}
groupType={formDisplayMode}
theme={props.driver}
onChange={(data) => {
if (data && state.currentColumn) {
const schema = { ...filterAttributes(data, ['index', 'sort', 'title']) };
updateColumnItemByPath(state.currentColumn, state.currentColumnPath || [], schema);
globalActions.editColumns(store);
}
}}
/>
);
};
return (
<div className={styles['attributes-wrapper']}>
<div className={styles['attributes-container']}>
<Tabs
activeKey={getActiveKey()}
type="card"
onChange={(key) => { setActiveKey(key); }}
tabBarExtraContent={activeKey !== '3'
? (
<Tooltip title={formDisplayMode === 'tabs' ? '折叠面板' : '标签面板'}>
<Button
style={{ borderRadius: 2 }}
size="small"
onClick={() => { setFormDisplayMode(formDisplayMode === 'collapse' ? 'tabs' : 'collapse'); }}
icon={formDisplayMode === 'tabs' ? <CollapseIcon style={{ marginTop: 4 }} /> : <TabsIcon style={{ marginTop: 4 }} />}
/>
</Tooltip>
)
: null}
>
<TabPane tab="属性配置" key="1" className={styles['attribute-panel']}>
<div className={styles['attributes-form-panel']}>
{ renderColumnForm() }
</div>
</TabPane>
{ (state.currentColumnPath?.length || 0) > 0 && (
<TabPane tab="子组件属性" key="4" className={styles['attribute-panel']}>
<div className={styles['attributes-form-panel']}>
{ renderColumnFormItem() }
</div>
</TabPane>
) }
<TabPane tab="全局设置" key="2" className={styles['attribute-panel']}>
<div className={styles['attributes-form-panel']}>
{ renderGlobalForm() }
</div>
</TabPane>
{ isDemo && (
<TabPane tab="表格数据" key="3" className={styles['attribute-panel']}>
<div className={styles['attributes-code-panel']}>
{ codeErrorMessage && <Alert style={{ margin: '8px 0' }} message={codeErrorMessage} type="error" showIcon /> }
<MonacoEditor
width="100%"
height={428}
language="json"
theme="vs-dark"
value={code || ''}
onChange={(value) => { submitTableData(value); }}
/>
</div>
</TabPane>
) }
</Tabs>
</div>
</div>
);
}
Example #2
Source File: index.tsx From drip-table with MIT License | 4 votes |
ComponentLayout = (props: Props & { store: GlobalStore }) => {
const getGroups = () => {
let groups = [
'基础组件',
'容器组件',
'自定义组件',
];
if (props.customComponentPanel) {
const customGroups = props.customComponentPanel.configs.map(item => item.group);
if (props.customComponentPanel.mode === 'add') {
const theSet = new Set([...groups, ...customGroups]);
groups = [...theSet];
} else {
groups = props.customComponentPanel.orders ?? [...new Set(customGroups)];
}
}
return groups;
};
const getComponents = (groupName) => {
let componentsToUse = components;
if (props.customComponentPanel) {
const customComponents = props.customComponentPanel.configs;
componentsToUse = props.customComponentPanel.mode === 'add' ? [...components, ...customComponents] : [...customComponents];
}
return [...componentsToUse].filter(item => item.group === groupName);
};
const [state, actions] = props.store;
const store = { state, setState: actions };
const componentCell = (item: DripTableComponentAttrConfig) => (
<div
className={styles['component-container']}
onClick={() => {
if (state.currentColumn && (state.currentColumnPath?.length || 0) > 0) {
const columnItemSchema: DripTableColumnSchema = {
key: `${item['ui:type']}_${mockId()}`,
dataIndex: '',
title: item.title,
width: void 0,
description: '',
component: item['ui:type'],
options: {},
};
updateColumnItemByPath(state.currentColumn, state.currentColumnPath || [], columnItemSchema);
globalActions.editColumns(store);
return;
}
const columnSchema: DripTableColumn = {
key: `${item['ui:type']}_${mockId()}`,
dataIndex: '',
title: item.title,
width: void 0,
description: '',
component: item['ui:type'],
options: {},
index: state.columns.length + 1,
sort: state.columns.length + 1,
};
item.attrSchema.forEach((schema) => {
if (typeof schema.default !== 'undefined') {
if (schema.name.startsWith('ui:props')) {
const key = schema.name.replace('ui:props.', '');
if (!columnSchema.options) {
columnSchema.options = {};
}
columnSchema.options[key] = schema.default;
} else {
columnSchema[schema.name] = schema.default;
}
}
});
state.columns.push(columnSchema);
globalActions.editColumns(store);
}}
>
<div>
<Icon svg={item.icon || defaultComponentIcon} className={styles['component-icon']} />
<span>{ item.title }</span>
</div>
</div>
);
return (
<div className={styles['layout-left-wrapper']} style={{ width: props.width }}>
<div className={styles['layout-left-title']}>组件区</div>
<div className={styles['layout-left-components-container']}>
<table style={{ width: '100%' }}>
{ getGroups().map((name, index) => (
<React.Fragment key={index}>
<thead key={`thead.${index}`}>
<tr><td colSpan={2}><div className={styles['group-title']}>{ name }</div></td></tr>
</thead>
<tbody key={`tbody.${index}`}>
{ chunk(getComponents(name), 2).map((items, i, componentsInLayout) => (
<React.Fragment key={i}>
{
i === 0
? (
<tr key={`groupItem.group.${index}.${i}.gutter.before`}>
<td colSpan={2}>
<div className={styles['components-line__gutter']} />
</td>
</tr>
)
: null
}
<tr key={`groupItem.group.${index}.${i}`} className={styles['components-line']}>
{ items.map((item, idx) => <td key={idx}>{ componentCell(item) }</td>) }
</tr>
{
i === componentsInLayout.length - 1
? (
<tr key={`groupItem.group.${index}.${i}.gutter.after`}>
<td colSpan={2}>
<div className={styles['components-line__gutter']} />
</td>
</tr>
)
: null
}
</React.Fragment>
)) }
</tbody>
</React.Fragment>
)) }
</table>
</div>
</div>
);
}
Example #3
Source File: footer.tsx From drip-table with MIT License | 4 votes |
EditableFooter = (props: EditableFooterProps & { store: GlobalStore }) => {
const {
slots,
} = useGlobalData();
const [state, setState] = props.store;
const store = { state, setState };
const { driver } = props;
const Button = driver.components.Button;
const DownOutlined = driver.icons.DownOutlined;
const Input = driver.components.Input;
const PlusOutlined = driver.icons.PlusOutlined;
const Select = driver.components.Select;
const [currentCellIndex, setCurrentCellIndex] = useState(-1);
const [currentCell, setCurrentCell] = useState<DripTableGenericRenderElement>();
const renderColumnContent = (config: DripTableGenericRenderElement) => {
if (config.type === 'spacer') {
return null;
}
if (config.type === 'text') {
return <h3 className={styles['generic-render-text-element']}>{ config.text }</h3>;
}
if (config.type === 'html') {
return <RichText className={styles['generic-render-html-element']} html={config.html} />;
}
if (config.type === 'search') {
return (
<div style={config.wrapperStyle} className={classNames(styles['generic-render-search-element'], config.wrapperClassName)}>
{ config.searchKeys && (
<Select
defaultValue={config.searchKeyDefaultValue}
className={styles['generic-render-search-element__select']}
>
{ config.searchKeys.map((item, i) => <Select.Option key={i} value={item.value}>{ item.label }</Select.Option>) }
</Select>
) }
<Input.Search
allowClear={config.allowClear}
placeholder={config.placeholder}
enterButton={config.searchButtonText || true}
size={config.searchButtonSize}
/>
</div>
);
}
if (config.type === 'slot') {
const Slot = slots?.[config.slot] || slots?.default;
if (Slot) {
return (
<Slot
{...config.props}
className={classNames(styles['generic-render-slot-element'], typeof config.props?.className === 'string' ? config.props.className : '')}
slotType={config.slot}
driver={driver}
schema={{ ...state.globalConfigs, columns: state.columns }}
dataSource={state.previewDataSource || []}
onSearch={() => void 0}
/>
);
}
return <span className={styles['generic-render-slot-element__error']}>{ `自定义插槽组件渲染函数 tableProps.slots['${config.slot}'] 不存在` }</span>;
}
if (config.type === 'insert-button') {
return (
<Button
className={classNames(styles['generic-render-insert-button-element'], config.insertButtonClassName)}
type="primary"
icon={config.showIcon && <PlusOutlined />}
style={config.insertButtonStyle}
>
{ config.insertButtonText }
</Button>
);
}
if (config.type === 'display-column-selector') {
return (
<Button style={{ margin: '12px 0' }} type={config.selectorButtonType}>
{ config.selectorButtonText || '展示列' }
<DownOutlined />
</Button>
);
}
return null;
};
const startDragCell = (element: DripTableGenericRenderElement, index: number) => {
setCurrentCellIndex(index);
setCurrentCell(element);
};
const dropFooterCell = (element: DripTableGenericRenderElement, index: number) => {
if (currentCellIndex === -1 || !currentCell) {
return;
}
if (typeof state.globalConfigs.footer === 'object') {
const columns = state.globalConfigs.footer.elements ? [...state.globalConfigs.footer.elements] : [];
if (currentCellIndex !== index) {
columns.splice(currentCellIndex, 1, element);
columns.splice(index, 1, currentCell);
setCurrentCellIndex(-1);
setCurrentCell(void 0);
store.state.globalConfigs = cloneDeep({ ...state.globalConfigs,
footer: {
...state.globalConfigs.footer,
elements: columns,
} });
globalActions.updateGlobalConfig(store);
}
}
};
return (
<div className={styles['draggable-container']} style={{ padding: '12px 0 0 12px', overflowX: 'auto' }}>
{
typeof state.globalConfigs.footer === 'object'
&& state.globalConfigs.footer.elements?.map((element, index) => (
<div
draggable
onDragStart={e => startDragCell(element, index)}
onDrop={(e) => { e.preventDefault(); dropFooterCell(element, index); }}
onDragOver={e => e.preventDefault()}
key={index}
className={styles['draggable-cell']}
style={element.style}
>
{ renderColumnContent(element) }
</div>
))
}
</div>
);
}
Example #4
Source File: header.tsx From drip-table with MIT License | 4 votes |
EditableHeader = (props: EditableHeaderProps & { store: GlobalStore }) => {
const {
slots,
} = useGlobalData();
const [state, setState] = props.store;
const store = { state, setState };
const { driver } = props;
const Button = driver.components.Button;
const DownOutlined = driver.icons.DownOutlined;
const Input = driver.components.Input;
const PlusOutlined = driver.icons.PlusOutlined;
const Select = driver.components.Select;
const [currentCellIndex, setCurrentCellIndex] = useState(-1);
const [currentCell, setCurrentCell] = useState<DripTableGenericRenderElement>();
const renderColumnContent = (config: DripTableGenericRenderElement) => {
if (config.type === 'spacer') {
return null;
}
if (config.type === 'text') {
return <h3 className={styles['generic-render-text-element']}>{ config.text }</h3>;
}
if (config.type === 'html') {
return <RichText className={styles['generic-render-html-element']} html={config.html} />;
}
if (config.type === 'search') {
return (
<div style={config.wrapperStyle} className={classNames(styles['generic-render-search-element'], config.wrapperClassName)}>
{ config.searchKeys && (
<Select
defaultValue={config.searchKeyDefaultValue}
className={styles['generic-render-search-element__select']}
>
{ config.searchKeys.map((item, i) => <Select.Option key={i} value={item.value}>{ item.label }</Select.Option>) }
</Select>
) }
<Input.Search
allowClear={config.allowClear}
placeholder={config.placeholder}
enterButton={config.searchButtonText || true}
size={config.searchButtonSize}
/>
</div>
);
}
if (config.type === 'slot') {
const Slot = slots?.[config.slot] || slots?.default;
if (Slot) {
return (
<Slot
{...config.props}
className={classNames(styles['generic-render-slot-element'], typeof config.props?.className === 'string' ? config.props.className : '')}
slotType={config.slot}
driver={driver}
schema={{ ...state.globalConfigs, columns: state.columns }}
dataSource={state.previewDataSource || []}
onSearch={() => void 0}
/>
);
}
return <span className={styles['generic-render-slot-element__error']}>{ `自定义插槽组件渲染函数 tableProps.slots['${config.slot}'] 不存在` }</span>;
}
if (config.type === 'insert-button') {
return (
<Button
className={classNames(styles['generic-render-insert-button-element'], config.insertButtonClassName)}
type="primary"
icon={config.showIcon && <PlusOutlined />}
style={config.insertButtonStyle}
>
{ config.insertButtonText }
</Button>
);
}
if (config.type === 'display-column-selector') {
return (
<Button style={{ margin: '12px 0' }} type={config.selectorButtonType}>
{ config.selectorButtonText || '展示列' }
<DownOutlined />
</Button>
);
}
return null;
};
const startDragCell = (element: DripTableGenericRenderElement, index: number) => {
setCurrentCellIndex(index);
setCurrentCell(element);
};
const dropHeaderCell = (element: DripTableGenericRenderElement, index: number) => {
if (currentCellIndex === -1 || !currentCell) {
return;
}
if (typeof state.globalConfigs.header === 'object') {
const columns = state.globalConfigs.header.elements ? [...state.globalConfigs.header.elements] : [];
if (currentCellIndex !== index) {
columns.splice(currentCellIndex, 1, element);
columns.splice(index, 1, currentCell);
setCurrentCellIndex(-1);
setCurrentCell(void 0);
store.state.globalConfigs = cloneDeep({ ...state.globalConfigs,
header: {
...state.globalConfigs.header,
elements: columns,
} });
globalActions.updateGlobalConfig(store);
}
}
};
return (
<div className={styles['draggable-container']} style={{ padding: '12px 0 0 12px', overflowX: 'auto' }}>
{
typeof state.globalConfigs.header === 'object'
&& state.globalConfigs.header.elements?.map((element, index) => (
<div
draggable
onDragStart={e => startDragCell(element, index)}
onDrop={(e) => { e.preventDefault(); dropHeaderCell(element, index); }}
onDragOver={e => e.preventDefault()}
key={index}
className={styles['draggable-cell']}
style={element.style}
>
{ renderColumnContent(element) }
</div>
))
}
</div>
);
}
Example #5
Source File: index.tsx From drip-table with MIT License | 4 votes |
EditableTable = (props: Props & { store: GlobalStore }) => {
const { noDataFeedBack } = useGlobalData();
const [state, actions] = props.store;
const store = { state, setState: actions };
const hasRecord = !(!state.previewDataSource || state.previewDataSource.length <= 0);
const previewComponentRender = (
column: DripTableBuiltInColumnSchema | null,
extraOptions?: {
isCurrentColumn?: boolean;
parentIndex?: number[];
isChildren?: boolean;
},
) => {
const [libName, componentName] = column?.component?.includes('::') ? column.component.split('::') : ['', column?.component || ''];
const DripTableComponent = libName ? props.customComponents?.[libName]?.[componentName] : builtInComponents[componentName];
const record = state.previewDataSource?.[0] || {} as Record<string, unknown>;
const value = column?.dataIndex ? get(record, column.dataIndex) : record;
const isChecked = (currentCheckedIndex: number) => {
const currentColumnPath = [...extraOptions?.parentIndex || [], currentCheckedIndex];
const stateColumnPath = state.currentColumnPath || [];
return extraOptions?.isCurrentColumn && currentColumnPath.join('.') === stateColumnPath.join('.');
};
// render group column and handler options.items
if (column?.component === 'group') {
const gutter = column.options.gutter ?? [0, 0];
return (
<div style={{ height: extraOptions?.isChildren ? '100%' : '120px', overflow: 'hidden' }}>
<div
className={extraOptions?.isChildren ? '' : styles['table-cell']}
style={{ width: extraOptions?.isChildren ? '100%' : column?.width || 120 }}
>
{ column.options.layout?.map((layout, index) => (
<Row
key={index}
className={styles['row-margin']}
style={{
flexFlow: column.options.wrap ? 'row wrap' : 'nowrap',
justifyContent: column.options.horizontalAlign,
width: 'calc(100% - 4px)',
height: 120 / column.options.layout.length - gutter[1],
}}
gutter={gutter}
justify={column.options.horizontalAlign}
wrap={column.options.wrap}
>
{ Array.from({ length: layout }, (v, i) => i).map((col, i) => {
const currentCheckedIndex = column.options.layout.slice(0, index).reduce((sum, j) => sum + j, i);
return (
<Col
className={classnames(styles['linear-stripe'], isChecked(currentCheckedIndex) ? styles['checked-stripe'] : '')}
key={i}
style={{
width: (Number(column?.width) || 120) / layout - gutter[0],
height: '100%',
overflow: 'auto',
}}
onClick={(e) => {
e.stopPropagation();
if (!extraOptions?.isCurrentColumn) { return; }
state.currentColumnPath = isChecked(currentCheckedIndex)
? []
: [...extraOptions?.parentIndex || [], currentCheckedIndex];
globalActions.updateColumnPath(store);
}}
>
{ column.options.items[currentCheckedIndex]
? previewComponentRender(column.options.items[currentCheckedIndex], {
isCurrentColumn: extraOptions?.isCurrentColumn,
parentIndex: [...extraOptions?.parentIndex || [], currentCheckedIndex],
isChildren: true,
})
: null }
</Col>
);
}) }
</Row>
)) }
</div>
</div>
);
}
// render normal column and preview component
const componentPreviewCell = DripTableComponent
? (
<DripTableComponent
driver={props.driver || DripTableDriverAntDesign}
value={value as unknown}
data={record}
schema={column}
preview={{}}
/>
)
: <Alert type="error" message="未知组件" />;
return extraOptions?.isChildren
? componentPreviewCell
: (
<div style={{ height: '120px', overflow: 'auto' }}>
<div className={styles['table-cell']} style={{ width: column?.width || 120 }}>
{ componentPreviewCell }
</div>
</div>
);
};
const renderTableCell = (col: DripTableColumn) => {
const isCurrent = state.currentColumn && state.currentColumn.index === col.index;
let width = String(col.width).trim() || '120';
if ((/^[0-9]+$/gui).test(width)) {
width += 'px';
}
return (
<div
style={{ width }}
className={classnames(styles.column, { checked: isCurrent })}
onClick={() => {
if (col.key !== state.currentColumn?.key) {
state.currentColumnPath = [];
}
state.currentColumn = isCurrent ? void 0 : col;
globalActions.checkColumn(store);
}}
>
<div className={styles['column-title']}>{ col.title }</div>
{ hasRecord
? previewComponentRender(col as DripTableBuiltInColumnSchema,
{
isCurrentColumn: isCurrent ?? false,
})
: null }
{ isCurrent && (
<CloseCircleTwoTone
className={styles['close-icon']}
twoToneColor="#ff4d4f"
onClick={() => {
const index = state.columns.findIndex(item => item.key === state.currentColumn?.key);
if (index > -1) {
state.columns.splice(index, 1);
for (let i = index; i < state.columns.length; i++) {
state.columns[i].index = i + 1;
state.columns[i].sort = i + 1;
}
state.currentColumn = void 0;
globalActions.editColumns(store);
globalActions.checkColumn(store);
}
}}
/>
) }
</div>
);
};
const emptyFeedBack = () => {
let message = '';
if (!hasRecord) {
if (noDataFeedBack) { return noDataFeedBack; }
message = '暂无数据';
}
if (!state.columns || state.columns?.length <= 0) { message = '暂无表格配置'; }
let width: number = 0;
state.columns?.forEach((item) => {
width += Number(String(item.width).replace(/(px|%|r?em|pt|vw|cm|in|pc)$/ui, '')) || 120;
});
return message ? <Empty style={{ width: width > 0 ? width : void 0 }} image={Empty.PRESENTED_IMAGE_SIMPLE} description={message} /> : null;
};
return (
<React.Fragment>
{ state.globalConfigs.header && <EditableHeader driver={props.driver} store={props.store} /> }
<div style={{ padding: '12px 0 12px 12px', overflowX: 'auto' }}>
{
state.columns && state.columns.length > 0 && (
<Draggable<DripTableColumn>
value={(state.columns || [])}
codeKey="sort"
style={{ position: 'relative' }}
onChange={(data) => {
state.columns = [...data];
globalActions.editColumns(store);
}}
render={renderTableCell}
/>
)
}
{ emptyFeedBack() }
</div>
{ state.globalConfigs.footer && <EditableFooter driver={props.driver} store={props.store} /> }
</React.Fragment>
);
}
Example #6
Source File: index.tsx From drip-table with MIT License | 4 votes |
ToolLayout = (props: { store: GlobalStore }) => {
const globalData = useGlobalData();
const [state, actions] = props.store;
const store = { state, setState: actions };
const [modalStatus, setModalStatus] = useState('');
const [code, setCode] = useState('');
const getSchemaValue = (): DripTableSchema<DripTableColumnSchema> => ({
...filterAttributes(state.globalConfigs, '$version'),
columns: state.columns.map(item => generateColumn(item)),
});
/**
* 渲染一个Modal用来展示JSON Schema配置
* @returns {JSX.Element} 返回React组件
*/
const renderSchemaModal = () => {
if (modalStatus !== 'export' && modalStatus !== 'import') {
return null;
}
const defaultValue = modalStatus === 'export'
? JSON.stringify(getSchemaValue(), null, 4)
: code || '';
return (
<Input.TextArea
style={{ minHeight: '560px' }}
value={defaultValue}
onChange={(e) => {
if (modalStatus === 'import') { setCode(e.target.value); }
}}
/>
);
};
return (
<React.Fragment>
<Button
style={{ margin: '0 12px' }}
size="small"
onClick={() => { globalActions.toggleEditMode(store); }}
>
{ state.isEdit ? '预览模式' : '编辑模式' }
</Button>
<Button
style={{ margin: '0 12px' }}
size="small"
onClick={() => setModalStatus('import')}
>
导入配置
</Button>
<Button
style={{ margin: '0 12px' }}
size="small"
onClick={() => {
setModalStatus('export');
}}
>
导出配置
</Button>
<Modal
width={720}
title={modalStatus === 'export' ? '导出数据' : '导入数据'}
visible={modalStatus === 'export' || modalStatus === 'import'}
cancelText={modalStatus === 'export' ? '确认' : '取消'}
okText={modalStatus === 'export' ? '复制文本' : '确认导入'}
onCancel={() => setModalStatus('')}
onOk={() => {
if (modalStatus === 'import') { // 导入解析
const value = (code || '').trim();
let hasError = false;
try {
const json = JSON.parse(value);
state.globalConfigs = filterAttributes(json, ['columns']);
state.columns = json.columns?.map((item, index) => ({ index, sort: index, ...item })) as DripTableColumn[];
state.currentColumn = void 0;
} catch {
hasError = true;
message.error('解析出错, 请传入正确的格式');
} finally {
if (!hasError) {
globalActions.updateGlobalConfig(store);
message.success('数据导入成功');
}
}
} else { // 导出复制
const aux = document.createElement('input');
aux.setAttribute('value', JSON.stringify(getSchemaValue()));
document.body.append(aux);
aux.select();
document.execCommand('copy');
aux.remove();
if (globalData.onExportSchema) {
globalData.onExportSchema(getSchemaValue());
}
message.success('复制成功');
}
setModalStatus('');
setCode('');
}}
>
{ renderSchemaModal() }
</Modal>
</React.Fragment>
);
}