mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 23:46:02 +00:00
perf: add skeleton component for other blocks
This commit is contained in:
parent
f395e814f2
commit
ff5d8a5f96
@ -16,16 +16,16 @@ import {
|
||||
useCollectionManager,
|
||||
useCollectionParentRecordData,
|
||||
useCollectionRecord,
|
||||
useCollectionRecordData,
|
||||
} from '../data-source';
|
||||
import { withDynamicSchemaProps } from '../hoc/withDynamicSchemaProps';
|
||||
import { withSkeletonComponent } from '../hoc/withSkeletonComponent';
|
||||
import { useTreeParentRecord } from '../modules/blocks/data-blocks/table/TreeRecordProvider';
|
||||
import { RecordProvider } from '../record-provider';
|
||||
import { useActionContext } from '../schema-component';
|
||||
import { useActionContext, useDesignable } from '../schema-component';
|
||||
import { BlockProvider, useBlockRequestContext } from './BlockProvider';
|
||||
import { TemplateBlockProvider } from './TemplateBlockProvider';
|
||||
import { FormActiveFieldsProvider } from './hooks/useFormActiveFields';
|
||||
import { useDesignable } from '../schema-component';
|
||||
import { useCollectionRecordData } from '../data-source';
|
||||
|
||||
export const FormBlockContext = createContext<{
|
||||
form?: any;
|
||||
@ -43,64 +43,69 @@ export const FormBlockContext = createContext<{
|
||||
}>({});
|
||||
FormBlockContext.displayName = 'FormBlockContext';
|
||||
|
||||
const InternalFormBlockProvider = (props) => {
|
||||
const cm = useCollectionManager();
|
||||
const ctx = useFormBlockContext();
|
||||
const { action, readPretty, params, collection, association } = props;
|
||||
const field = useField();
|
||||
const form = useMemo(
|
||||
() =>
|
||||
createForm({
|
||||
readPretty,
|
||||
}),
|
||||
[readPretty],
|
||||
);
|
||||
const { resource, service, updateAssociationValues } = useBlockRequestContext();
|
||||
const formBlockRef = useRef();
|
||||
const record = useCollectionRecord();
|
||||
const formBlockValue: any = useMemo(() => {
|
||||
return {
|
||||
...ctx,
|
||||
params,
|
||||
const InternalFormBlockProvider = withSkeletonComponent(
|
||||
(props) => {
|
||||
const cm = useCollectionManager();
|
||||
const ctx = useFormBlockContext();
|
||||
const { action, readPretty, params, collection, association } = props;
|
||||
const field = useField();
|
||||
const form = useMemo(
|
||||
() =>
|
||||
createForm({
|
||||
readPretty,
|
||||
}),
|
||||
[readPretty],
|
||||
);
|
||||
const { resource, service, updateAssociationValues } = useBlockRequestContext();
|
||||
const formBlockRef = useRef();
|
||||
const record = useCollectionRecord();
|
||||
const formBlockValue: any = useMemo(() => {
|
||||
return {
|
||||
...ctx,
|
||||
params,
|
||||
action,
|
||||
form,
|
||||
// update 表示是表单编辑区块,create 表示是表单新增区块
|
||||
type: action === 'get' ? 'update' : 'create',
|
||||
field,
|
||||
service,
|
||||
resource,
|
||||
updateAssociationValues,
|
||||
formBlockRef,
|
||||
collectionName: collection || cm.getCollectionField(association)?.target,
|
||||
formRecord: record,
|
||||
};
|
||||
}, [
|
||||
action,
|
||||
form,
|
||||
// update 表示是表单编辑区块,create 表示是表单新增区块
|
||||
type: action === 'get' ? 'update' : 'create',
|
||||
association,
|
||||
cm,
|
||||
collection,
|
||||
ctx,
|
||||
field,
|
||||
service,
|
||||
form,
|
||||
params,
|
||||
record,
|
||||
resource,
|
||||
service,
|
||||
updateAssociationValues,
|
||||
formBlockRef,
|
||||
collectionName: collection || cm.getCollectionField(association)?.target,
|
||||
formRecord: record,
|
||||
};
|
||||
}, [
|
||||
action,
|
||||
association,
|
||||
cm,
|
||||
collection,
|
||||
ctx,
|
||||
field,
|
||||
form,
|
||||
params,
|
||||
record,
|
||||
resource,
|
||||
service,
|
||||
updateAssociationValues,
|
||||
]);
|
||||
]);
|
||||
|
||||
if (service.loading && Object.keys(form?.initialValues || {})?.length === 0 && action) {
|
||||
return <Spin />;
|
||||
}
|
||||
if (service.loading && Object.keys(form?.initialValues || {})?.length === 0 && action) {
|
||||
return <Spin />;
|
||||
}
|
||||
|
||||
return (
|
||||
<FormBlockContext.Provider value={formBlockValue}>
|
||||
<RecordProvider isNew={record?.isNew} parent={record?.parentRecord?.data} record={record?.data}>
|
||||
<div ref={formBlockRef}>{props.children}</div>
|
||||
</RecordProvider>
|
||||
</FormBlockContext.Provider>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<FormBlockContext.Provider value={formBlockValue}>
|
||||
<RecordProvider isNew={record?.isNew} parent={record?.parentRecord?.data} record={record?.data}>
|
||||
<div ref={formBlockRef}>{props.children}</div>
|
||||
</RecordProvider>
|
||||
</FormBlockContext.Provider>
|
||||
);
|
||||
},
|
||||
{
|
||||
displayName: 'InternalFormBlockProvider',
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
|
@ -32,7 +32,7 @@ const useDefaultLoading = () => {
|
||||
export const withSkeletonComponent = (Component: React.ComponentType<any>, options?: Options) => {
|
||||
const { useLoading = useDefaultLoading, displayName, SkeletonComponent = Skeleton } = options || {};
|
||||
|
||||
const Result = (props: any) => {
|
||||
const Result = React.memo((props: any) => {
|
||||
const loading = useLoading();
|
||||
const mountedRef = useRef(false);
|
||||
const deferredLoading = useDeferredValue(loading);
|
||||
@ -44,7 +44,7 @@ export const withSkeletonComponent = (Component: React.ComponentType<any>, optio
|
||||
mountedRef.current = true;
|
||||
|
||||
return <Component {...props} />;
|
||||
};
|
||||
});
|
||||
|
||||
Result.displayName =
|
||||
displayName || `${Component.displayName}(withSkeletonComponent)` || `${Component.name}(withSkeletonComponent)`;
|
||||
|
@ -61,7 +61,7 @@ export * from './user';
|
||||
export * from './variables';
|
||||
|
||||
export { withDynamicSchemaProps } from './hoc/withDynamicSchemaProps';
|
||||
|
||||
export { withSkeletonComponent } from './hoc/withSkeletonComponent';
|
||||
export { SchemaSettingsActionLinkItem } from './modules/actions/link/customizeLinkActionSettings';
|
||||
export { useURLAndHTMLSchema } from './modules/actions/link/useURLAndHTMLSchema';
|
||||
export * from './modules/blocks/BlockSchemaToolbar';
|
||||
|
@ -8,12 +8,14 @@
|
||||
*/
|
||||
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { ArrayField } from '@formily/core';
|
||||
import { FormLayout } from '@formily/antd-v5';
|
||||
import { ArrayField } from '@formily/core';
|
||||
import { RecursionField, Schema, useField, useFieldSchema } from '@formily/react';
|
||||
import { List as AntdList, Col, PaginationProps } from 'antd';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { getCardItemSchema } from '../../../block-provider';
|
||||
import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps';
|
||||
import { withSkeletonComponent } from '../../../hoc/withSkeletonComponent';
|
||||
import { SortableItem } from '../../common';
|
||||
import { SchemaComponentOptions } from '../../core';
|
||||
import { useDesigner, useProps } from '../../hooks';
|
||||
@ -22,7 +24,6 @@ import { GridCardDesigner } from './GridCard.Designer';
|
||||
import { GridCardItem } from './GridCard.Item';
|
||||
import { useGridCardActionBarProps, useGridCardBodyHeight } from './hooks';
|
||||
import { defaultColumnCount, pageSizeOptions } from './options';
|
||||
import { getCardItemSchema } from '../../../block-provider';
|
||||
|
||||
const rowGutter = {
|
||||
md: 12,
|
||||
@ -114,126 +115,131 @@ const usePaginationProps = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const InternalGridCard = (props: GridCardProps) => {
|
||||
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||
const { columnCount: columnCountProp, pagination } = useProps(props);
|
||||
const { service, columnCount: _columnCount = defaultColumnCount } = useGridCardBlockContext();
|
||||
const columnCount = columnCountProp || _columnCount;
|
||||
const { run, params } = service;
|
||||
const meta = service?.data?.meta;
|
||||
const fieldSchema = useFieldSchema();
|
||||
const field = useField<ArrayField>();
|
||||
const Designer = useDesigner();
|
||||
const height = useGridCardBodyHeight();
|
||||
const [schemaMap] = useState(new Map());
|
||||
const getSchema = useCallback(
|
||||
(key) => {
|
||||
if (!schemaMap.has(key)) {
|
||||
schemaMap.set(
|
||||
key,
|
||||
new Schema({
|
||||
type: 'object',
|
||||
properties: {
|
||||
[key]: {
|
||||
...fieldSchema.properties['item'],
|
||||
const InternalGridCard = withSkeletonComponent(
|
||||
(props: GridCardProps) => {
|
||||
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||
const { columnCount: columnCountProp, pagination } = useProps(props);
|
||||
const { service, columnCount: _columnCount = defaultColumnCount } = useGridCardBlockContext();
|
||||
const columnCount = columnCountProp || _columnCount;
|
||||
const { run, params } = service;
|
||||
const meta = service?.data?.meta;
|
||||
const fieldSchema = useFieldSchema();
|
||||
const field = useField<ArrayField>();
|
||||
const Designer = useDesigner();
|
||||
const height = useGridCardBodyHeight();
|
||||
const [schemaMap] = useState(new Map());
|
||||
const getSchema = useCallback(
|
||||
(key) => {
|
||||
if (!schemaMap.has(key)) {
|
||||
schemaMap.set(
|
||||
key,
|
||||
new Schema({
|
||||
type: 'object',
|
||||
properties: {
|
||||
[key]: {
|
||||
...fieldSchema.properties['item'],
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
return schemaMap.get(key);
|
||||
},
|
||||
[fieldSchema.properties, schemaMap],
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
return schemaMap.get(key);
|
||||
},
|
||||
[fieldSchema.properties, schemaMap],
|
||||
);
|
||||
|
||||
const onPaginationChange: PaginationProps['onChange'] = useCallback(
|
||||
(page, pageSize) => {
|
||||
run({
|
||||
...params?.[0],
|
||||
page: page,
|
||||
pageSize: pageSize,
|
||||
});
|
||||
},
|
||||
[run, params],
|
||||
);
|
||||
const gridCardProps = {
|
||||
...usePaginationProps(),
|
||||
...pagination,
|
||||
onChange: onPaginationChange,
|
||||
};
|
||||
const cardItemSchema = getCardItemSchema?.(fieldSchema);
|
||||
const {
|
||||
layout = 'vertical',
|
||||
labelAlign = 'left',
|
||||
labelWidth = 120,
|
||||
labelWrap = true,
|
||||
} = cardItemSchema?.['x-component-props'] || {};
|
||||
const onPaginationChange: PaginationProps['onChange'] = useCallback(
|
||||
(page, pageSize) => {
|
||||
run({
|
||||
...params?.[0],
|
||||
page: page,
|
||||
pageSize: pageSize,
|
||||
});
|
||||
},
|
||||
[run, params],
|
||||
);
|
||||
const gridCardProps = {
|
||||
...usePaginationProps(),
|
||||
...pagination,
|
||||
onChange: onPaginationChange,
|
||||
};
|
||||
const cardItemSchema = getCardItemSchema?.(fieldSchema);
|
||||
const {
|
||||
layout = 'vertical',
|
||||
labelAlign = 'left',
|
||||
labelWidth = 120,
|
||||
labelWrap = true,
|
||||
} = cardItemSchema?.['x-component-props'] || {};
|
||||
|
||||
return (
|
||||
<SchemaComponentOptions
|
||||
scope={{
|
||||
useGridCardItemProps,
|
||||
useGridCardActionBarProps,
|
||||
}}
|
||||
>
|
||||
<SortableItem
|
||||
className={cx(
|
||||
'nb-card-list',
|
||||
designerCss,
|
||||
css`
|
||||
.ant-spin-nested-loading {
|
||||
height: ${height ? height + `px` : '100%'};
|
||||
overflow-y: ${height ? 'auto' : null};
|
||||
overflow-x: clip;
|
||||
.nb-action-bar {
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
}
|
||||
`,
|
||||
)}
|
||||
return (
|
||||
<SchemaComponentOptions
|
||||
scope={{
|
||||
useGridCardItemProps,
|
||||
useGridCardActionBarProps,
|
||||
}}
|
||||
>
|
||||
<FormLayout
|
||||
layout={layout}
|
||||
labelAlign={labelAlign}
|
||||
labelWidth={layout === 'horizontal' ? labelWidth : null}
|
||||
labelWrap={labelWrap}
|
||||
<SortableItem
|
||||
className={cx(
|
||||
'nb-card-list',
|
||||
designerCss,
|
||||
css`
|
||||
.ant-spin-nested-loading {
|
||||
height: ${height ? height + `px` : '100%'};
|
||||
overflow-y: ${height ? 'auto' : null};
|
||||
overflow-x: clip;
|
||||
.nb-action-bar {
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
}
|
||||
`,
|
||||
)}
|
||||
>
|
||||
<AntdList
|
||||
pagination={
|
||||
!meta || meta.count <= meta.pageSize
|
||||
? false
|
||||
: {
|
||||
...gridCardProps,
|
||||
}
|
||||
}
|
||||
dataSource={field.value}
|
||||
grid={{
|
||||
...columnCount,
|
||||
sm: columnCount.xs,
|
||||
xl: columnCount.lg,
|
||||
gutter: [rowGutter, rowGutter],
|
||||
}}
|
||||
renderItem={(item, index) => {
|
||||
return (
|
||||
<Col style={{ height: '100%' }}>
|
||||
<RecursionField
|
||||
key={index}
|
||||
basePath={field.address}
|
||||
name={index}
|
||||
onlyRenderProperties
|
||||
schema={getSchema(index)}
|
||||
></RecursionField>
|
||||
</Col>
|
||||
);
|
||||
}}
|
||||
loading={service?.loading}
|
||||
/>
|
||||
</FormLayout>
|
||||
<Designer />
|
||||
</SortableItem>
|
||||
</SchemaComponentOptions>
|
||||
);
|
||||
};
|
||||
<FormLayout
|
||||
layout={layout}
|
||||
labelAlign={labelAlign}
|
||||
labelWidth={layout === 'horizontal' ? labelWidth : null}
|
||||
labelWrap={labelWrap}
|
||||
>
|
||||
<AntdList
|
||||
pagination={
|
||||
!meta || meta.count <= meta.pageSize
|
||||
? false
|
||||
: {
|
||||
...gridCardProps,
|
||||
}
|
||||
}
|
||||
dataSource={field.value}
|
||||
grid={{
|
||||
...columnCount,
|
||||
sm: columnCount.xs,
|
||||
xl: columnCount.lg,
|
||||
gutter: [rowGutter, rowGutter],
|
||||
}}
|
||||
renderItem={(item, index) => {
|
||||
return (
|
||||
<Col style={{ height: '100%' }}>
|
||||
<RecursionField
|
||||
key={index}
|
||||
basePath={field.address}
|
||||
name={index}
|
||||
onlyRenderProperties
|
||||
schema={getSchema(index)}
|
||||
></RecursionField>
|
||||
</Col>
|
||||
);
|
||||
}}
|
||||
loading={service?.loading}
|
||||
/>
|
||||
</FormLayout>
|
||||
<Designer />
|
||||
</SortableItem>
|
||||
</SchemaComponentOptions>
|
||||
);
|
||||
},
|
||||
{
|
||||
displayName: 'InternalGridCard',
|
||||
},
|
||||
);
|
||||
|
||||
export const GridCard = withDynamicSchemaProps(InternalGridCard) as typeof InternalGridCard & {
|
||||
Item: typeof GridCardItem;
|
||||
|
@ -8,12 +8,14 @@
|
||||
*/
|
||||
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { ArrayField } from '@formily/core';
|
||||
import { FormLayout } from '@formily/antd-v5';
|
||||
import { ArrayField } from '@formily/core';
|
||||
import { RecursionField, Schema, useField, useFieldSchema } from '@formily/react';
|
||||
import { List as AntdList, PaginationProps, theme } from 'antd';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { getCardItemSchema } from '../../../block-provider';
|
||||
import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps';
|
||||
import { withSkeletonComponent } from '../../../hoc/withSkeletonComponent';
|
||||
import { SortableItem } from '../../common';
|
||||
import { SchemaComponentOptions } from '../../core';
|
||||
import { useDesigner } from '../../hooks';
|
||||
@ -22,159 +24,163 @@ import { ListDesigner } from './List.Designer';
|
||||
import { ListItem } from './List.Item';
|
||||
import useStyles from './List.style';
|
||||
import { useListActionBarProps, useListBlockHeight } from './hooks';
|
||||
import { getCardItemSchema } from '../../../block-provider';
|
||||
|
||||
const InternalList = (props) => {
|
||||
const { service } = useListBlockContext();
|
||||
const { run, params } = service;
|
||||
const fieldSchema = useFieldSchema();
|
||||
const Designer = useDesigner();
|
||||
const meta = service?.data?.meta;
|
||||
const { pageSize, count, hasNext, page } = meta || {};
|
||||
const field = useField<ArrayField>();
|
||||
const [schemaMap] = useState(new Map());
|
||||
const { wrapSSR, componentCls, hashId } = useStyles();
|
||||
const height = useListBlockHeight();
|
||||
const { token } = theme.useToken();
|
||||
const getSchema = useCallback(
|
||||
(key) => {
|
||||
if (!schemaMap.has(key)) {
|
||||
schemaMap.set(
|
||||
key,
|
||||
new Schema({
|
||||
type: 'object',
|
||||
properties: {
|
||||
[key]: fieldSchema.properties['item'],
|
||||
},
|
||||
}),
|
||||
);
|
||||
const InternalList = withSkeletonComponent(
|
||||
(props) => {
|
||||
const { service } = useListBlockContext();
|
||||
const { run, params } = service;
|
||||
const fieldSchema = useFieldSchema();
|
||||
const Designer = useDesigner();
|
||||
const meta = service?.data?.meta;
|
||||
const { pageSize, count, hasNext, page } = meta || {};
|
||||
const field = useField<ArrayField>();
|
||||
const [schemaMap] = useState(new Map());
|
||||
const { wrapSSR, componentCls, hashId } = useStyles();
|
||||
const height = useListBlockHeight();
|
||||
const { token } = theme.useToken();
|
||||
const getSchema = useCallback(
|
||||
(key) => {
|
||||
if (!schemaMap.has(key)) {
|
||||
schemaMap.set(
|
||||
key,
|
||||
new Schema({
|
||||
type: 'object',
|
||||
properties: {
|
||||
[key]: fieldSchema.properties['item'],
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
return schemaMap.get(key);
|
||||
},
|
||||
[fieldSchema.properties, schemaMap],
|
||||
);
|
||||
|
||||
const pageSizeOptions = [5, 10, 20, 50, 100, 200];
|
||||
|
||||
const onPaginationChange: PaginationProps['onChange'] = useCallback(
|
||||
(page, pageSize) => {
|
||||
run({
|
||||
...params?.[0],
|
||||
page: page,
|
||||
pageSize: pageSize,
|
||||
});
|
||||
},
|
||||
[run, params],
|
||||
);
|
||||
const cardItemSchema = getCardItemSchema?.(fieldSchema);
|
||||
const {
|
||||
layout = 'vertical',
|
||||
labelAlign = 'left',
|
||||
labelWidth = 120,
|
||||
labelWrap = true,
|
||||
} = cardItemSchema?.['x-component-props'] || {};
|
||||
const usePagination = () => {
|
||||
if (!count) {
|
||||
return {
|
||||
onChange: onPaginationChange,
|
||||
total: count || field.value?.length < pageSize || !hasNext ? pageSize * page : pageSize * page + 1,
|
||||
pageSize: pageSize || 10,
|
||||
current: page || 1,
|
||||
showSizeChanger: true,
|
||||
pageSizeOptions,
|
||||
simple: true,
|
||||
className: css`
|
||||
.ant-pagination-simple-pager {
|
||||
display: none !important;
|
||||
}
|
||||
`,
|
||||
itemRender: (_, type, originalElement) => {
|
||||
if (type === 'prev') {
|
||||
return (
|
||||
<div
|
||||
style={{ display: 'flex' }}
|
||||
className={css`
|
||||
.ant-pagination-item-link {
|
||||
min-width: ${token.controlHeight}px;
|
||||
}
|
||||
`}
|
||||
>
|
||||
{originalElement} <div style={{ marginLeft: '7px' }}>{page}</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return originalElement;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
return schemaMap.get(key);
|
||||
},
|
||||
[fieldSchema.properties, schemaMap],
|
||||
);
|
||||
|
||||
const pageSizeOptions = [5, 10, 20, 50, 100, 200];
|
||||
|
||||
const onPaginationChange: PaginationProps['onChange'] = useCallback(
|
||||
(page, pageSize) => {
|
||||
run({
|
||||
...params?.[0],
|
||||
page: page,
|
||||
pageSize: pageSize,
|
||||
});
|
||||
},
|
||||
[run, params],
|
||||
);
|
||||
const cardItemSchema = getCardItemSchema?.(fieldSchema);
|
||||
const {
|
||||
layout = 'vertical',
|
||||
labelAlign = 'left',
|
||||
labelWidth = 120,
|
||||
labelWrap = true,
|
||||
} = cardItemSchema?.['x-component-props'] || {};
|
||||
const usePagination = () => {
|
||||
if (!count) {
|
||||
return {
|
||||
onChange: onPaginationChange,
|
||||
total: count || field.value?.length < pageSize || !hasNext ? pageSize * page : pageSize * page + 1,
|
||||
total: count || 0,
|
||||
pageSize: pageSize || 10,
|
||||
current: page || 1,
|
||||
showSizeChanger: true,
|
||||
pageSizeOptions,
|
||||
simple: true,
|
||||
className: css`
|
||||
.ant-pagination-simple-pager {
|
||||
display: none !important;
|
||||
}
|
||||
`,
|
||||
itemRender: (_, type, originalElement) => {
|
||||
if (type === 'prev') {
|
||||
return (
|
||||
<div
|
||||
style={{ display: 'flex' }}
|
||||
className={css`
|
||||
.ant-pagination-item-link {
|
||||
min-width: ${token.controlHeight}px;
|
||||
}
|
||||
`}
|
||||
>
|
||||
{originalElement} <div style={{ marginLeft: '7px' }}>{page}</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return originalElement;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
onChange: onPaginationChange,
|
||||
total: count || 0,
|
||||
pageSize: pageSize || 10,
|
||||
current: page || 1,
|
||||
showSizeChanger: true,
|
||||
pageSizeOptions,
|
||||
};
|
||||
};
|
||||
const paginationProps = usePagination();
|
||||
return wrapSSR(
|
||||
<SchemaComponentOptions
|
||||
scope={{
|
||||
useListItemProps,
|
||||
useListActionBarProps,
|
||||
}}
|
||||
>
|
||||
<SortableItem
|
||||
className={cx(
|
||||
'nb-list',
|
||||
componentCls,
|
||||
hashId,
|
||||
css`
|
||||
.nb-list-container {
|
||||
height: ${height ? height + 'px' : '100%'};
|
||||
overflow-y: ${height ? 'auto' : null};
|
||||
margin-left: -${token.marginLG}px;
|
||||
margin-right: -${token.marginLG}px;
|
||||
padding-left: ${token.marginLG}px;
|
||||
padding-right: ${token.marginLG}px;
|
||||
}
|
||||
`,
|
||||
)}
|
||||
const paginationProps = usePagination();
|
||||
return wrapSSR(
|
||||
<SchemaComponentOptions
|
||||
scope={{
|
||||
useListItemProps,
|
||||
useListActionBarProps,
|
||||
}}
|
||||
>
|
||||
<div className="nb-list-container">
|
||||
<FormLayout
|
||||
layout={layout}
|
||||
labelAlign={labelAlign}
|
||||
labelWidth={layout === 'horizontal' ? labelWidth : null}
|
||||
labelWrap={labelWrap}
|
||||
>
|
||||
<AntdList
|
||||
{...props}
|
||||
pagination={!meta || !field.value?.length ? false : paginationProps}
|
||||
loading={service?.loading}
|
||||
<SortableItem
|
||||
className={cx(
|
||||
'nb-list',
|
||||
componentCls,
|
||||
hashId,
|
||||
css`
|
||||
.nb-list-container {
|
||||
height: ${height ? height + 'px' : '100%'};
|
||||
overflow-y: ${height ? 'auto' : null};
|
||||
margin-left: -${token.marginLG}px;
|
||||
margin-right: -${token.marginLG}px;
|
||||
padding-left: ${token.marginLG}px;
|
||||
padding-right: ${token.marginLG}px;
|
||||
}
|
||||
`,
|
||||
)}
|
||||
>
|
||||
<div className="nb-list-container">
|
||||
<FormLayout
|
||||
layout={layout}
|
||||
labelAlign={labelAlign}
|
||||
labelWidth={layout === 'horizontal' ? labelWidth : null}
|
||||
labelWrap={labelWrap}
|
||||
>
|
||||
{field.value?.length
|
||||
? field.value.map((item, index) => {
|
||||
return (
|
||||
<RecursionField
|
||||
basePath={field.address}
|
||||
key={index}
|
||||
name={index}
|
||||
onlyRenderProperties
|
||||
schema={getSchema(index)}
|
||||
></RecursionField>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</AntdList>
|
||||
</FormLayout>
|
||||
</div>
|
||||
<Designer />
|
||||
</SortableItem>
|
||||
</SchemaComponentOptions>,
|
||||
);
|
||||
};
|
||||
<AntdList
|
||||
{...props}
|
||||
pagination={!meta || !field.value?.length ? false : paginationProps}
|
||||
loading={service?.loading}
|
||||
>
|
||||
{field.value?.length
|
||||
? field.value.map((item, index) => {
|
||||
return (
|
||||
<RecursionField
|
||||
basePath={field.address}
|
||||
key={index}
|
||||
name={index}
|
||||
onlyRenderProperties
|
||||
schema={getSchema(index)}
|
||||
></RecursionField>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</AntdList>
|
||||
</FormLayout>
|
||||
</div>
|
||||
<Designer />
|
||||
</SortableItem>
|
||||
</SchemaComponentOptions>,
|
||||
);
|
||||
},
|
||||
{
|
||||
displayName: 'InternalList',
|
||||
},
|
||||
);
|
||||
|
||||
export const List = withDynamicSchemaProps(InternalList) as typeof InternalList & {
|
||||
Item: typeof ListItem;
|
||||
|
@ -8,24 +8,25 @@
|
||||
*/
|
||||
|
||||
import { LeftOutlined, RightOutlined } from '@ant-design/icons';
|
||||
import { RecursionField, Schema, observer, useFieldSchema } from '@formily/react';
|
||||
import { RecursionField, Schema, useFieldSchema } from '@formily/react';
|
||||
import {
|
||||
PopupContextProvider,
|
||||
RecordProvider,
|
||||
getLabelFormatValue,
|
||||
useACLRoleContext,
|
||||
useCollection,
|
||||
useCollectionParentRecordData,
|
||||
usePopupUtils,
|
||||
useProps,
|
||||
useToken,
|
||||
withDynamicSchemaProps,
|
||||
useACLRoleContext,
|
||||
withSkeletonComponent,
|
||||
} from '@nocobase/client';
|
||||
import { parseExpression } from 'cron-parser';
|
||||
import type { Dayjs } from 'dayjs';
|
||||
import dayjs from 'dayjs';
|
||||
import get from 'lodash/get';
|
||||
import React, { useMemo, useState, useEffect } from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { Calendar as BigCalendar, View, dayjsLocalizer } from 'react-big-calendar';
|
||||
import * as dates from 'react-big-calendar/lib/utils/dates';
|
||||
import { i18nt, useTranslation } from '../../locale';
|
||||
@ -228,7 +229,7 @@ function findCreateSchema(schema): Schema {
|
||||
}
|
||||
|
||||
export const Calendar: any = withDynamicSchemaProps(
|
||||
observer(
|
||||
withSkeletonComponent(
|
||||
(props: any) => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const { openPopup } = usePopupUtils({
|
||||
|
@ -17,6 +17,7 @@ import {
|
||||
useCollectionParentRecordData,
|
||||
useProps,
|
||||
withDynamicSchemaProps,
|
||||
withSkeletonComponent,
|
||||
} from '@nocobase/client';
|
||||
import { Card, Skeleton, Spin, Tag } from 'antd';
|
||||
import React, { useCallback, useContext, useMemo, useRef, useState } from 'react';
|
||||
@ -72,120 +73,122 @@ const MemorizedRecursionField = React.memo(RecursionField);
|
||||
MemorizedRecursionField.displayName = 'MemorizedRecursionField';
|
||||
|
||||
export const Kanban: any = withDynamicSchemaProps(
|
||||
observer((props: any) => {
|
||||
const { styles } = useStyles();
|
||||
withSkeletonComponent(
|
||||
observer((props: any) => {
|
||||
const { styles } = useStyles();
|
||||
|
||||
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||
const { groupField, onCardDragEnd, dataSource, setDataSource, ...restProps } = useProps(props);
|
||||
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||
const { groupField, onCardDragEnd, dataSource, setDataSource, ...restProps } = useProps(props);
|
||||
|
||||
const collection = useCollection();
|
||||
const primaryKey = collection.getPrimaryKey();
|
||||
const parentRecordData = useCollectionParentRecordData();
|
||||
const field = useField<ArrayField>();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const [disableCardDrag, setDisableCardDrag] = useState(false);
|
||||
const schemas = useMemo(
|
||||
() =>
|
||||
fieldSchema.reduceProperties(
|
||||
(buf, current) => {
|
||||
if (current['x-component'].endsWith('.Card')) {
|
||||
buf.card = current;
|
||||
} else if (current['x-component'].endsWith('.CardAdder')) {
|
||||
buf.cardAdder = current;
|
||||
} else if (current['x-component'].endsWith('.CardViewer')) {
|
||||
buf.cardViewer = current;
|
||||
}
|
||||
return buf;
|
||||
},
|
||||
{ card: null, cardAdder: null, cardViewer: null },
|
||||
),
|
||||
[],
|
||||
);
|
||||
const handleCardRemove = useCallback(
|
||||
(card, column) => {
|
||||
const updatedBoard = Board.removeCard({ columns: field.value }, column, card);
|
||||
field.value = updatedBoard.columns;
|
||||
setDataSource(updatedBoard.columns);
|
||||
},
|
||||
[field],
|
||||
);
|
||||
const lastDraggedCard = useRef(null);
|
||||
const handleCardDragEnd = useCallback(
|
||||
(card, fromColumn, toColumn) => {
|
||||
lastDraggedCard.current = card[primaryKey];
|
||||
onCardDragEnd?.({ columns: field.value, groupField }, fromColumn, toColumn);
|
||||
const updatedBoard = Board.moveCard({ columns: field.value }, fromColumn, toColumn);
|
||||
field.value = updatedBoard.columns;
|
||||
setDataSource(updatedBoard.columns);
|
||||
},
|
||||
[field],
|
||||
);
|
||||
const collection = useCollection();
|
||||
const primaryKey = collection.getPrimaryKey();
|
||||
const parentRecordData = useCollectionParentRecordData();
|
||||
const field = useField<ArrayField>();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const [disableCardDrag, setDisableCardDrag] = useState(false);
|
||||
const schemas = useMemo(
|
||||
() =>
|
||||
fieldSchema.reduceProperties(
|
||||
(buf, current) => {
|
||||
if (current['x-component'].endsWith('.Card')) {
|
||||
buf.card = current;
|
||||
} else if (current['x-component'].endsWith('.CardAdder')) {
|
||||
buf.cardAdder = current;
|
||||
} else if (current['x-component'].endsWith('.CardViewer')) {
|
||||
buf.cardViewer = current;
|
||||
}
|
||||
return buf;
|
||||
},
|
||||
{ card: null, cardAdder: null, cardViewer: null },
|
||||
),
|
||||
[],
|
||||
);
|
||||
const handleCardRemove = useCallback(
|
||||
(card, column) => {
|
||||
const updatedBoard = Board.removeCard({ columns: field.value }, column, card);
|
||||
field.value = updatedBoard.columns;
|
||||
setDataSource(updatedBoard.columns);
|
||||
},
|
||||
[field],
|
||||
);
|
||||
const lastDraggedCard = useRef(null);
|
||||
const handleCardDragEnd = useCallback(
|
||||
(card, fromColumn, toColumn) => {
|
||||
lastDraggedCard.current = card[primaryKey];
|
||||
onCardDragEnd?.({ columns: field.value, groupField }, fromColumn, toColumn);
|
||||
const updatedBoard = Board.moveCard({ columns: field.value }, fromColumn, toColumn);
|
||||
field.value = updatedBoard.columns;
|
||||
setDataSource(updatedBoard.columns);
|
||||
},
|
||||
[field],
|
||||
);
|
||||
|
||||
return (
|
||||
<Spin wrapperClassName={styles.nbKanban} spinning={field.loading || false}>
|
||||
<Board
|
||||
{...restProps}
|
||||
allowAddCard={!!schemas.cardAdder}
|
||||
disableColumnDrag
|
||||
cardAdderPosition={'bottom'}
|
||||
disableCardDrag={restProps.disableCardDrag || disableCardDrag}
|
||||
onCardRemove={handleCardRemove}
|
||||
onCardDragEnd={handleCardDragEnd}
|
||||
renderColumnHeader={({ title, color }) => (
|
||||
<div className={'react-kanban-column-header'}>
|
||||
<Tag color={color}>{title}</Tag>
|
||||
</div>
|
||||
)}
|
||||
renderCard={(card, { column }) => {
|
||||
const columnIndex = dataSource?.indexOf(column);
|
||||
const cardIndex = column?.cards?.indexOf(card);
|
||||
const { ref, inView } = useInView({
|
||||
threshold: 0,
|
||||
triggerOnce: true,
|
||||
initialInView: lastDraggedCard.current && lastDraggedCard.current === card[primaryKey],
|
||||
});
|
||||
return (
|
||||
<Spin wrapperClassName={styles.nbKanban} spinning={field.loading || false}>
|
||||
<Board
|
||||
{...restProps}
|
||||
allowAddCard={!!schemas.cardAdder}
|
||||
disableColumnDrag
|
||||
cardAdderPosition={'bottom'}
|
||||
disableCardDrag={restProps.disableCardDrag || disableCardDrag}
|
||||
onCardRemove={handleCardRemove}
|
||||
onCardDragEnd={handleCardDragEnd}
|
||||
renderColumnHeader={({ title, color }) => (
|
||||
<div className={'react-kanban-column-header'}>
|
||||
<Tag color={color}>{title}</Tag>
|
||||
</div>
|
||||
)}
|
||||
renderCard={(card, { column }) => {
|
||||
const columnIndex = dataSource?.indexOf(column);
|
||||
const cardIndex = column?.cards?.indexOf(card);
|
||||
const { ref, inView } = useInView({
|
||||
threshold: 0,
|
||||
triggerOnce: true,
|
||||
initialInView: lastDraggedCard.current && lastDraggedCard.current === card[primaryKey],
|
||||
});
|
||||
|
||||
return (
|
||||
schemas.card && (
|
||||
<RecordProvider record={card} parent={parentRecordData}>
|
||||
<KanbanCardContext.Provider
|
||||
value={{
|
||||
setDisableCardDrag,
|
||||
}}
|
||||
>
|
||||
<div ref={ref}>
|
||||
{inView ? (
|
||||
<MemorizedRecursionField name={'card'} schema={fieldSchema.properties.card} />
|
||||
) : (
|
||||
<Card bordered={false}>
|
||||
<Skeleton paragraph={{ rows: 4 }} />
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
</KanbanCardContext.Provider>
|
||||
</RecordProvider>
|
||||
)
|
||||
);
|
||||
}}
|
||||
renderCardAdder={({ column }) => {
|
||||
if (!schemas.cardAdder) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<KanbanColumnContext.Provider value={{ column, groupField }}>
|
||||
<SchemaComponentOptions scope={{ useCreateActionProps }}>
|
||||
<MemorizedRecursionField name={schemas.cardAdder.name} schema={schemas.cardAdder} />
|
||||
</SchemaComponentOptions>
|
||||
</KanbanColumnContext.Provider>
|
||||
);
|
||||
}}
|
||||
>
|
||||
{{
|
||||
columns: dataSource || [],
|
||||
}}
|
||||
</Board>
|
||||
</Spin>
|
||||
);
|
||||
}),
|
||||
return (
|
||||
schemas.card && (
|
||||
<RecordProvider record={card} parent={parentRecordData}>
|
||||
<KanbanCardContext.Provider
|
||||
value={{
|
||||
setDisableCardDrag,
|
||||
}}
|
||||
>
|
||||
<div ref={ref}>
|
||||
{inView ? (
|
||||
<MemorizedRecursionField name={'card'} schema={fieldSchema.properties.card} />
|
||||
) : (
|
||||
<Card bordered={false}>
|
||||
<Skeleton paragraph={{ rows: 4 }} />
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
</KanbanCardContext.Provider>
|
||||
</RecordProvider>
|
||||
)
|
||||
);
|
||||
}}
|
||||
renderCardAdder={({ column }) => {
|
||||
if (!schemas.cardAdder) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<KanbanColumnContext.Provider value={{ column, groupField }}>
|
||||
<SchemaComponentOptions scope={{ useCreateActionProps }}>
|
||||
<MemorizedRecursionField name={schemas.cardAdder.name} schema={schemas.cardAdder} />
|
||||
</SchemaComponentOptions>
|
||||
</KanbanColumnContext.Provider>
|
||||
);
|
||||
}}
|
||||
>
|
||||
{{
|
||||
columns: dataSource || [],
|
||||
}}
|
||||
</Board>
|
||||
</Spin>
|
||||
);
|
||||
}),
|
||||
),
|
||||
{ displayName: 'Kanban' },
|
||||
);
|
||||
|
@ -9,8 +9,8 @@
|
||||
|
||||
import {
|
||||
PopupContextProvider,
|
||||
useCollection_deprecated,
|
||||
useCollectionManager_deprecated,
|
||||
useCollection,
|
||||
useCollectionManager,
|
||||
usePopupUtils,
|
||||
useProps,
|
||||
withDynamicSchemaProps,
|
||||
@ -35,10 +35,10 @@ export const MapBlock = withDynamicSchemaProps((props) => {
|
||||
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||
const { fieldNames } = useProps(props);
|
||||
|
||||
const { getCollectionJoinField } = useCollectionManager_deprecated();
|
||||
const { name } = useCollection_deprecated();
|
||||
const cm = useCollectionManager();
|
||||
const { name } = useCollection() || {};
|
||||
const collectionField = useMemo(() => {
|
||||
return getCollectionJoinField([name, fieldNames?.field].flat().join('.'));
|
||||
return cm?.getCollectionField([name, fieldNames?.field].flat().join('.'));
|
||||
}, [name, fieldNames?.field]);
|
||||
|
||||
const fieldComponentProps = collectionField?.uiSchema?.['x-component-props'];
|
||||
|
@ -7,7 +7,7 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { PopupContextProvider } from '@nocobase/client';
|
||||
import { PopupContextProvider, withSkeletonComponent } from '@nocobase/client';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useMapTranslation } from '../locale';
|
||||
import { AMapBlock } from './AMap';
|
||||
@ -18,21 +18,26 @@ const MapBlocks = {
|
||||
google: GoogleMapsBlock,
|
||||
};
|
||||
|
||||
export const MapBlockComponent: React.FC<any> = (props) => {
|
||||
const { t } = useMapTranslation();
|
||||
const { mapType } = props;
|
||||
export const MapBlockComponent: React.FC<any> = withSkeletonComponent(
|
||||
(props) => {
|
||||
const { t } = useMapTranslation();
|
||||
const { mapType } = props;
|
||||
|
||||
const Component = useMemo(() => {
|
||||
return MapBlocks[mapType];
|
||||
}, [mapType]);
|
||||
const Component = useMemo(() => {
|
||||
return MapBlocks[mapType];
|
||||
}, [mapType]);
|
||||
|
||||
if (!Component) {
|
||||
return <div>{t(`The ${mapType} cannot found`)}</div>;
|
||||
}
|
||||
if (!Component) {
|
||||
return <div>{t(`The ${mapType} cannot found`)}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<PopupContextProvider>
|
||||
<Component {...props} />
|
||||
</PopupContextProvider>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<PopupContextProvider>
|
||||
<Component {...props} />
|
||||
</PopupContextProvider>
|
||||
);
|
||||
},
|
||||
{
|
||||
displayName: 'MapBlockComponent',
|
||||
},
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user