mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 09:38:51 +00:00
refactor(DataBlock): list block (#3779)
* refactor: refactor list block initializer and record association list block initializer * refactor: add useListBlockDecoratorProps * chore: fix import path to avoid build error * refactor: should not get collection on getting association in UISchema * refactor: use x-use-component-props instead of useProps * chore: fix unit test
This commit is contained in:
parent
c6922b071d
commit
76cd3474c3
@ -21,6 +21,7 @@ import { TableFieldProvider, useTableFieldProps } from './TableFieldProvider';
|
||||
import { TableSelectorProvider, useTableSelectorProps } from './TableSelectorProvider';
|
||||
import * as bp from './hooks';
|
||||
import { useTableBlockDecoratorProps } from '../modules/blocks/data-blocks/table/hooks/useTableBlockDecoratorProps';
|
||||
import { useListBlockDecoratorProps } from '../modules/blocks/data-blocks/list/hooks/useListBlockDecoratorProps';
|
||||
|
||||
// TODO: delete this, replaced by `BlockSchemaComponentPlugin`
|
||||
export const BlockSchemaComponentProvider: React.FC = (props) => {
|
||||
@ -52,6 +53,7 @@ export const BlockSchemaComponentProvider: React.FC = (props) => {
|
||||
useTableBlockProps,
|
||||
useTableSelectorProps,
|
||||
useTableBlockDecoratorProps,
|
||||
useListBlockDecoratorProps,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
@ -100,6 +102,7 @@ export class BlockSchemaComponentPlugin extends Plugin {
|
||||
useTableBlockProps,
|
||||
useTableSelectorProps,
|
||||
useTableBlockDecoratorProps,
|
||||
useListBlockDecoratorProps,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { OrderedListOutlined } from '@ant-design/icons';
|
||||
import React from 'react';
|
||||
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
||||
import { useCollectionManager_deprecated } from '../../../../collection-manager';
|
||||
import { createListBlockSchema } from '../../../../schema-initializer/utils';
|
||||
import { createListBlockSchema } from './createListBlockSchema';
|
||||
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
|
||||
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
|
||||
|
||||
@ -48,10 +48,9 @@ export const ListBlockInitializer = ({
|
||||
|
||||
const collection = getCollection(item.name, item.dataSource);
|
||||
const schema = createListBlockSchema({
|
||||
collection: item.name,
|
||||
collectionName: item.name,
|
||||
dataSource: item.dataSource,
|
||||
rowKey: collection.filterTargetKey || 'id',
|
||||
settings: 'blockSettings:list',
|
||||
});
|
||||
insert(schema);
|
||||
}}
|
||||
|
@ -0,0 +1,97 @@
|
||||
import { createListBlockSchema } from '../createListBlockSchema';
|
||||
|
||||
describe('createListBlockSchema', () => {
|
||||
test('should return the correct schema', () => {
|
||||
const options = {
|
||||
collectionName: 'users',
|
||||
dataSource: 'users',
|
||||
association: 'user',
|
||||
templateSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
age: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
},
|
||||
rowKey: 'id',
|
||||
};
|
||||
|
||||
const schema = createListBlockSchema(options);
|
||||
|
||||
expect(schema).toMatchInlineSnapshot(`
|
||||
{
|
||||
"properties": {
|
||||
"actionBar": {
|
||||
"type": "void",
|
||||
"x-component": "ActionBar",
|
||||
"x-component-props": {
|
||||
"style": {
|
||||
"marginBottom": "var(--nb-spacing)",
|
||||
},
|
||||
},
|
||||
"x-initializer": "list:configureActions",
|
||||
},
|
||||
"list": {
|
||||
"properties": {
|
||||
"item": {
|
||||
"properties": {
|
||||
"actionBar": {
|
||||
"type": "void",
|
||||
"x-align": "left",
|
||||
"x-component": "ActionBar",
|
||||
"x-component-props": {
|
||||
"layout": "one-column",
|
||||
},
|
||||
"x-initializer": "list:configureItemActions",
|
||||
"x-use-component-props": "useListActionBarProps",
|
||||
},
|
||||
"grid": {
|
||||
"properties": {
|
||||
"age": {
|
||||
"type": "number",
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
"x-component": "List.Item",
|
||||
"x-read-pretty": true,
|
||||
"x-use-component-props": "useListItemProps",
|
||||
},
|
||||
},
|
||||
"type": "array",
|
||||
"x-component": "List",
|
||||
"x-use-component-props": "useListBlockProps",
|
||||
},
|
||||
},
|
||||
"type": "void",
|
||||
"x-acl-action": "user:view",
|
||||
"x-component": "CardItem",
|
||||
"x-decorator": "List.Decorator",
|
||||
"x-decorator-props": {
|
||||
"action": "list",
|
||||
"association": "user",
|
||||
"collection": "users",
|
||||
"dataSource": "users",
|
||||
"params": {
|
||||
"pageSize": 10,
|
||||
},
|
||||
"readPretty": true,
|
||||
"rowKey": "id",
|
||||
"runWhenParamsChanged": true,
|
||||
},
|
||||
"x-settings": "blockSettings:list",
|
||||
"x-toolbar": "BlockSchemaToolbar",
|
||||
"x-use-decorator-props": "useListBlockDecoratorProps",
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
@ -0,0 +1,76 @@
|
||||
import { ISchema } from '@formily/react';
|
||||
|
||||
export const createListBlockSchema = (options: {
|
||||
dataSource: string;
|
||||
collectionName?: string;
|
||||
association?: string;
|
||||
templateSchema?: ISchema;
|
||||
rowKey?: string;
|
||||
}): ISchema => {
|
||||
const { collectionName, dataSource, association, templateSchema, rowKey } = options;
|
||||
const resourceName = association || collectionName;
|
||||
|
||||
return {
|
||||
type: 'void',
|
||||
'x-acl-action': `${resourceName}:view`,
|
||||
'x-decorator': 'List.Decorator',
|
||||
'x-use-decorator-props': 'useListBlockDecoratorProps',
|
||||
'x-decorator-props': {
|
||||
collection: collectionName,
|
||||
dataSource,
|
||||
association,
|
||||
readPretty: true,
|
||||
action: 'list',
|
||||
params: {
|
||||
pageSize: 10,
|
||||
},
|
||||
runWhenParamsChanged: true,
|
||||
rowKey,
|
||||
},
|
||||
'x-component': 'CardItem',
|
||||
'x-toolbar': 'BlockSchemaToolbar',
|
||||
'x-settings': 'blockSettings:list',
|
||||
properties: {
|
||||
actionBar: {
|
||||
type: 'void',
|
||||
'x-initializer': 'list:configureActions',
|
||||
'x-component': 'ActionBar',
|
||||
'x-component-props': {
|
||||
style: {
|
||||
marginBottom: 'var(--nb-spacing)',
|
||||
},
|
||||
},
|
||||
},
|
||||
list: {
|
||||
type: 'array',
|
||||
'x-component': 'List',
|
||||
'x-use-component-props': 'useListBlockProps',
|
||||
properties: {
|
||||
item: {
|
||||
type: 'object',
|
||||
'x-component': 'List.Item',
|
||||
'x-read-pretty': true,
|
||||
'x-use-component-props': 'useListItemProps',
|
||||
properties: {
|
||||
grid: templateSchema || {
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': 'details:configureFields',
|
||||
},
|
||||
actionBar: {
|
||||
type: 'void',
|
||||
'x-align': 'left',
|
||||
'x-initializer': 'list:configureItemActions',
|
||||
'x-component': 'ActionBar',
|
||||
'x-use-component-props': 'useListActionBarProps',
|
||||
'x-component-props': {
|
||||
layout: 'one-column',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
@ -0,0 +1,15 @@
|
||||
import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId';
|
||||
|
||||
export function useListBlockDecoratorProps(props) {
|
||||
let sourceId;
|
||||
|
||||
// 因为 association 的值是固定的,所以这里可以使用 hooks
|
||||
if (props.association) {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
sourceId = useDataBlockSourceId({ association: props.association });
|
||||
}
|
||||
|
||||
return {
|
||||
sourceId,
|
||||
};
|
||||
}
|
@ -6,6 +6,7 @@ import { createPortal } from 'react-dom';
|
||||
import { DndContext } from '../../common';
|
||||
import { useDesignable, useProps } from '../../hooks';
|
||||
import { useSchemaInitializerRender } from '../../../application';
|
||||
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
|
||||
|
||||
interface ActionBarContextForceProps {
|
||||
layout?: 'one-column' | 'tow-columns';
|
||||
@ -46,10 +47,13 @@ const Portal: React.FC = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const ActionBar = observer(
|
||||
(props: any) => {
|
||||
export const ActionBar = withDynamicSchemaProps(
|
||||
observer((props: any) => {
|
||||
const { forceProps = {} } = useActionBarContext();
|
||||
|
||||
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||
const { layout = 'tow-columns', style, spaceProps, ...others } = { ...useProps(props), ...forceProps } as any;
|
||||
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { render } = useSchemaInitializerRender(fieldSchema['x-initializer'], fieldSchema['x-initializer-props']);
|
||||
const { designable } = useDesignable();
|
||||
@ -128,6 +132,6 @@ export const ActionBar = observer(
|
||||
{render()}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
}),
|
||||
{ displayName: 'ActionBar' },
|
||||
);
|
||||
|
@ -5,6 +5,7 @@ import { FormContext, useField } from '@formily/react';
|
||||
import _ from 'lodash';
|
||||
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
||||
import { BlockProvider, useBlockRequestContext, useParsedFilter } from '../../../block-provider';
|
||||
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
|
||||
|
||||
export const ListBlockContext = createContext<any>({});
|
||||
ListBlockContext.displayName = 'ListBlockContext';
|
||||
@ -52,7 +53,7 @@ const InternalListBlockProvider = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const ListBlockProvider = (props) => {
|
||||
export const ListBlockProvider = withDynamicSchemaProps((props) => {
|
||||
const { params } = props;
|
||||
const { filter: parsedFilter } = useParsedFilter({
|
||||
filterOption: params?.filter,
|
||||
@ -75,7 +76,7 @@ export const ListBlockProvider = (props) => {
|
||||
<InternalListBlockProvider {...props} />
|
||||
</BlockProvider>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export const useListBlockContext = () => {
|
||||
return useContext(ListBlockContext);
|
||||
|
@ -7,8 +7,9 @@ import { useDesignable } from '../../hooks';
|
||||
|
||||
import { useCollectionParentRecordData } from '../../../data-source/collection-record/CollectionRecordProvider';
|
||||
import { RecordProvider } from '../../../record-provider';
|
||||
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
|
||||
|
||||
export const ListItem = (props) => {
|
||||
export const ListItem = withDynamicSchemaProps((props) => {
|
||||
const field = useField<ObjectField>();
|
||||
const { designable } = useDesignable();
|
||||
const parentRecordData = useCollectionParentRecordData();
|
||||
@ -29,4 +30,4 @@ export const ListItem = (props) => {
|
||||
</RecordProvider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
@ -11,6 +11,7 @@ import { ListDesigner } from './List.Designer';
|
||||
import { ListItem } from './List.Item';
|
||||
import useStyles from './List.style';
|
||||
import { useListActionBarProps } from './hooks';
|
||||
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
|
||||
|
||||
const InternalList = (props) => {
|
||||
const { service } = useListBlockContext();
|
||||
@ -93,7 +94,7 @@ const InternalList = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const List = InternalList as typeof InternalList & {
|
||||
export const List = withDynamicSchemaProps(InternalList) as typeof InternalList & {
|
||||
Item: typeof ListItem;
|
||||
Designer: typeof ListDesigner;
|
||||
Decorator: typeof ListBlockProvider;
|
||||
|
@ -4,7 +4,8 @@ import React, { useCallback } from 'react';
|
||||
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
|
||||
import { useCollectionManager_deprecated } from '../../collection-manager';
|
||||
import { useSchemaTemplateManager } from '../../schema-templates';
|
||||
import { createListBlockSchema, useRecordCollectionDataSourceItems } from '../utils';
|
||||
import { useRecordCollectionDataSourceItems } from '../utils';
|
||||
import { createListBlockSchema } from '../../modules/blocks/data-blocks/list/createListBlockSchema';
|
||||
|
||||
export const RecordAssociationListBlockInitializer = () => {
|
||||
const itemConfig = useSchemaInitializerItem();
|
||||
@ -28,11 +29,8 @@ export const RecordAssociationListBlockInitializer = () => {
|
||||
insert(
|
||||
createListBlockSchema({
|
||||
rowKey: collection.filterTargetKey,
|
||||
collection: field.target,
|
||||
resource,
|
||||
dataSource: collection.dataSource,
|
||||
association: resource,
|
||||
settings: 'blockSettings:list',
|
||||
}),
|
||||
);
|
||||
}
|
||||
@ -54,10 +52,8 @@ export function useCreateAssociationListBlock() {
|
||||
insert(
|
||||
createListBlockSchema({
|
||||
rowKey: collection.filterTargetKey,
|
||||
collection: field.target,
|
||||
dataSource: collection.dataSource,
|
||||
association: `${field.collectionName}.${field.name}`,
|
||||
settings: 'blockSettings:list',
|
||||
}),
|
||||
);
|
||||
},
|
||||
|
@ -972,94 +972,6 @@ export const createDetailsBlockSchema = (options) => {
|
||||
return schema;
|
||||
};
|
||||
|
||||
export const createListBlockSchema = (options) => {
|
||||
const {
|
||||
formItemInitializers = 'details:configureFields',
|
||||
actionInitializers = 'list:configureActions',
|
||||
itemActionInitializers = 'list:configureItemActions',
|
||||
collection,
|
||||
dataSource,
|
||||
association,
|
||||
template,
|
||||
settings,
|
||||
...others
|
||||
} = options;
|
||||
const resourceName = association || collection;
|
||||
const schema: ISchema = {
|
||||
type: 'void',
|
||||
'x-acl-action': `${resourceName}:view`,
|
||||
'x-decorator': 'List.Decorator',
|
||||
'x-decorator-props': {
|
||||
collection,
|
||||
dataSource,
|
||||
association,
|
||||
readPretty: true,
|
||||
action: 'list',
|
||||
params: {
|
||||
pageSize: 10,
|
||||
},
|
||||
runWhenParamsChanged: true,
|
||||
...others,
|
||||
},
|
||||
'x-component': 'CardItem',
|
||||
'x-toolbar': 'BlockSchemaToolbar',
|
||||
'x-settings': settings,
|
||||
properties: {
|
||||
actionBar: {
|
||||
type: 'void',
|
||||
'x-initializer': actionInitializers,
|
||||
'x-component': 'ActionBar',
|
||||
'x-component-props': {
|
||||
style: {
|
||||
marginBottom: 'var(--nb-spacing)',
|
||||
},
|
||||
},
|
||||
properties: {},
|
||||
},
|
||||
list: {
|
||||
type: 'array',
|
||||
'x-component': 'List',
|
||||
'x-component-props': {
|
||||
props: '{{ useListBlockProps }}',
|
||||
},
|
||||
properties: {
|
||||
item: {
|
||||
type: 'object',
|
||||
'x-component': 'List.Item',
|
||||
'x-read-pretty': true,
|
||||
'x-component-props': {
|
||||
useProps: '{{ useListItemProps }}',
|
||||
},
|
||||
properties: {
|
||||
grid: template || {
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': formItemInitializers,
|
||||
'x-initializer-props': {
|
||||
useProps: '{{ useListItemInitializerProps }}',
|
||||
},
|
||||
properties: {},
|
||||
},
|
||||
actionBar: {
|
||||
type: 'void',
|
||||
'x-align': 'left',
|
||||
'x-initializer': itemActionInitializers,
|
||||
'x-component': 'ActionBar',
|
||||
'x-component-props': {
|
||||
useProps: '{{ useListActionBarProps }}',
|
||||
layout: 'one-column',
|
||||
},
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
return schema;
|
||||
};
|
||||
|
||||
export const createGridCardBlockSchema = (options) => {
|
||||
const {
|
||||
formItemInitializers = 'details:configureFields',
|
||||
|
Loading…
Reference in New Issue
Block a user