perf: add skeleton component for other blocks

This commit is contained in:
Zeke Zhang 2024-11-04 18:13:24 +08:00
parent f395e814f2
commit ff5d8a5f96
9 changed files with 479 additions and 453 deletions

View File

@ -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

View File

@ -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)`;

View File

@ -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';

View File

@ -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) => {
// 新版 UISchema1.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) => {
// 新版 UISchema1.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;

View File

@ -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;

View File

@ -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({

View File

@ -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();
// 新版 UISchema1.0 之后)中已经废弃了 useProps这里之所以继续保留是为了兼容旧版的 UISchema
const { groupField, onCardDragEnd, dataSource, setDataSource, ...restProps } = useProps(props);
// 新版 UISchema1.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' },
);

View File

@ -9,8 +9,8 @@
import {
PopupContextProvider,
useCollection_deprecated,
useCollectionManager_deprecated,
useCollection,
useCollectionManager,
usePopupUtils,
useProps,
withDynamicSchemaProps,
@ -35,10 +35,10 @@ export const MapBlock = withDynamicSchemaProps((props) => {
// 新版 UISchema1.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'];

View File

@ -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',
},
);