mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-07-02 11:12:20 +08:00
perf(Table): remove Formily components to improve performance (#5738)
* perf(Table): remove Formily components to improve performance * refactor: rename and modify comments * fix(UIEditor): fix hover style * fix(Table): fix style configuration option not showing * fix(ellipsis): fix refresh issue * fix: need to update field value * perf(Table): improve performance of configuring top buttons * refactor: skip style-related code when no style rules are set * chore(e2e): fix e2e errors * fix(e2e): fix e2e errors * fix(workflow): fix refresh issue * fix(workflow): fix refresh issue * fix(Action.Container): fix refresh issue * fix(drawer): fix refresh issue * fix(Table): fix refresh issue * fix(AssociationField): fix refresh issues * fix(NocoBaseRecursionField): fix refresh code * refactor(SchemaComponentContext): remove useless code * refactor: rename RefreshContext to RefreshFieldSchemaContext * refactor(useDataBlockRequestGetter): optimize comment * refactor: rename and new refresh context * fix(Table): fix some refresh issues * fix(AssociationField): fix refresh issue * refactor(Table): make code better * fix: replace RecursionField with NocoBaseRecursionField * refactor: remove useless code * fix(Menu): fix draging issue * fix: refresh entire page after drag and drop operation * fix(Page): fix draging issue * chore: fix build error * fix(test): make unit tests pass * chore(test): fix unit test * chore(e2e): fix e2e errors * chore(e2e): update e2e to make it pass * chore(e2e): update e2e to make it pass * chore(e2e): fix e2e errors * fix(e2e): fix some e2e errors * fix(SchemaComponent): fix onChange issue * chore(e2e): make e2e more stable
This commit is contained in:
parent
51cb1d0a71
commit
0fa56d7407
@ -11,7 +11,7 @@ import {
|
|||||||
SchemaSettingsModalItem,
|
SchemaSettingsModalItem,
|
||||||
createDesignable,
|
createDesignable,
|
||||||
useAPIClient,
|
useAPIClient,
|
||||||
useSchemaComponentContext,
|
useRefreshFieldSchema,
|
||||||
useSchemaSettings,
|
useSchemaSettings,
|
||||||
useSchemaSettingsRender,
|
useSchemaSettingsRender,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
@ -82,16 +82,16 @@ const Demo = () => {
|
|||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
const { refresh } = useSchemaComponentContext();
|
const refreshFieldSchema = useRefreshFieldSchema();
|
||||||
const dn = useMemo(
|
const dn = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createDesignable({
|
createDesignable({
|
||||||
current: fieldSchema.parent,
|
current: fieldSchema.parent,
|
||||||
model: field.parent,
|
model: field.parent,
|
||||||
api,
|
api,
|
||||||
refresh,
|
refresh: () => refreshFieldSchema({ refreshParentSchema: true }),
|
||||||
}),
|
}),
|
||||||
[],
|
[api, field.parent, fieldSchema.parent, refreshFieldSchema],
|
||||||
);
|
);
|
||||||
const { render, exists } = useSchemaSettingsRender(fieldSchema['x-settings'], {
|
const { render, exists } = useSchemaSettingsRender(fieldSchema['x-settings'], {
|
||||||
fieldSchema: dn.current,
|
fieldSchema: dn.current,
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
SchemaSettingsModalItem,
|
SchemaSettingsModalItem,
|
||||||
createDesignable,
|
createDesignable,
|
||||||
useAPIClient,
|
useAPIClient,
|
||||||
useSchemaComponentContext,
|
useRefreshFieldSchema,
|
||||||
useSchemaSettings,
|
useSchemaSettings,
|
||||||
useSchemaSettingsRender,
|
useSchemaSettingsRender,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
@ -82,16 +82,16 @@ const Demo = () => {
|
|||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
const { refresh } = useSchemaComponentContext();
|
const refreshFieldSchema = useRefreshFieldSchema();
|
||||||
const dn = useMemo(
|
const dn = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createDesignable({
|
createDesignable({
|
||||||
current: fieldSchema.parent,
|
current: fieldSchema.parent,
|
||||||
model: field.parent,
|
model: field.parent,
|
||||||
api,
|
api,
|
||||||
refresh,
|
refresh: () => refreshFieldSchema({ refreshParentSchema: true }),
|
||||||
}),
|
}),
|
||||||
[],
|
[api, field.parent, fieldSchema.parent, refreshFieldSchema],
|
||||||
);
|
);
|
||||||
const { render, exists } = useSchemaSettingsRender(fieldSchema['x-settings'], {
|
const { render, exists } = useSchemaSettingsRender(fieldSchema['x-settings'], {
|
||||||
fieldSchema: dn.current,
|
fieldSchema: dn.current,
|
||||||
|
@ -17,7 +17,7 @@ import { SchemaInitializer } from '../SchemaInitializer';
|
|||||||
import { SchemaInitializerOptions } from '../types';
|
import { SchemaInitializerOptions } from '../types';
|
||||||
import { withInitializer } from '../withInitializer';
|
import { withInitializer } from '../withInitializer';
|
||||||
|
|
||||||
const InitializerComponent: FC<SchemaInitializerOptions<any, any>> = React.memo((options) => {
|
const InitializerComponent: FC<SchemaInitializerOptions<any, any>> = (options) => {
|
||||||
const Component: any = options.Component || SchemaInitializerButton;
|
const Component: any = options.Component || SchemaInitializerButton;
|
||||||
|
|
||||||
const ItemsComponent: any = options.ItemsComponent || SchemaInitializerItems;
|
const ItemsComponent: any = options.ItemsComponent || SchemaInitializerItems;
|
||||||
@ -31,7 +31,8 @@ const InitializerComponent: FC<SchemaInitializerOptions<any, any>> = React.memo(
|
|||||||
const C = useMemo(() => withInitializer(Component), [Component]);
|
const C = useMemo(() => withInitializer(Component), [Component]);
|
||||||
|
|
||||||
return React.createElement(C, options, React.createElement(ItemsComponent, itemsComponentProps));
|
return React.createElement(C, options, React.createElement(ItemsComponent, itemsComponentProps));
|
||||||
});
|
};
|
||||||
|
|
||||||
InitializerComponent.displayName = 'InitializerComponent';
|
InitializerComponent.displayName = 'InitializerComponent';
|
||||||
|
|
||||||
export function useSchemaInitializerRender<P1 = ButtonProps, P2 = {}>(
|
export function useSchemaInitializerRender<P1 = ButtonProps, P2 = {}>(
|
||||||
|
@ -54,7 +54,7 @@ export function withInitializer<T>(C: ComponentType<T>) {
|
|||||||
insertAdjacent(insertPosition, wrapCallback(schema, { isInSubTable }), { onSuccess });
|
insertAdjacent(insertPosition, wrapCallback(schema, { isInSubTable }), { onSuccess });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[insertCallback, wrapCallback, insertAdjacent, insertPosition, onSuccess],
|
[insertCallback, wrapCallback, isInSubTable, insertAdjacent, insertPosition, onSuccess],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { wrapSSR, hashId, componentCls } = useSchemaInitializerStyles();
|
const { wrapSSR, hashId, componentCls } = useSchemaInitializerStyles();
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
import { Field, GeneralField } from '@formily/core';
|
import { Field, GeneralField } from '@formily/core';
|
||||||
import { RecursionField, useField, useFieldSchema } from '@formily/react';
|
import { RecursionField, useField, useFieldSchema } from '@formily/react';
|
||||||
import { Col, Row } from 'antd';
|
import { Col, Row } from 'antd';
|
||||||
import { isArray } from 'lodash';
|
import _, { isArray } from 'lodash';
|
||||||
import template from 'lodash/template';
|
import template from 'lodash/template';
|
||||||
import React, { createContext, useContext, useMemo } from 'react';
|
import React, { createContext, useContext, useMemo } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
@ -33,10 +33,12 @@ import {
|
|||||||
useCollectionManager_deprecated,
|
useCollectionManager_deprecated,
|
||||||
useCollection_deprecated,
|
useCollection_deprecated,
|
||||||
} from '../collection-manager';
|
} from '../collection-manager';
|
||||||
|
import { RefreshComponentProvider } from '../formily/NocoBaseRecursionField';
|
||||||
import { useSourceId } from '../modules/blocks/useSourceId';
|
import { useSourceId } from '../modules/blocks/useSourceId';
|
||||||
import { RecordProvider, useRecordIndex } from '../record-provider';
|
import { RecordProvider, useRecordIndex } from '../record-provider';
|
||||||
import { useAssociationNames } from './hooks';
|
import { useAssociationNames } from './hooks';
|
||||||
import { useDataBlockParentRecord } from './hooks/useDataBlockParentRecord';
|
import { useDataBlockParentRecord } from './hooks/useDataBlockParentRecord';
|
||||||
|
import { useUpdate } from 'ahooks';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
@ -243,6 +245,7 @@ export const BlockProvider = (props: {
|
|||||||
}) => {
|
}) => {
|
||||||
const { name, dataSource, useParams, parentRecord } = props;
|
const { name, dataSource, useParams, parentRecord } = props;
|
||||||
const parentRecordFromHook = useCompatDataBlockParentRecord(props);
|
const parentRecordFromHook = useCompatDataBlockParentRecord(props);
|
||||||
|
const refresh = useUpdate();
|
||||||
|
|
||||||
// 新版(1.0)已弃用 useParams,这里之所以继续保留是为了兼容旧版的 UISchema
|
// 新版(1.0)已弃用 useParams,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||||
const paramsFromHook = useParams?.();
|
const paramsFromHook = useParams?.();
|
||||||
@ -261,7 +264,7 @@ export const BlockProvider = (props: {
|
|||||||
<BlockContext.Provider value={blockValue}>
|
<BlockContext.Provider value={blockValue}>
|
||||||
<DataBlockProvider {...(props as any)} params={params} parentRecord={parentRecord || parentRecordFromHook}>
|
<DataBlockProvider {...(props as any)} params={params} parentRecord={parentRecord || parentRecordFromHook}>
|
||||||
<BlockRequestProvider_deprecated {...props} updateAssociationValues={updateAssociationValues} params={params}>
|
<BlockRequestProvider_deprecated {...props} updateAssociationValues={updateAssociationValues} params={params}>
|
||||||
{props.children}
|
<RefreshComponentProvider refresh={refresh}>{props.children}</RefreshComponentProvider>
|
||||||
</BlockRequestProvider_deprecated>
|
</BlockRequestProvider_deprecated>
|
||||||
</DataBlockProvider>
|
</DataBlockProvider>
|
||||||
</BlockContext.Provider>
|
</BlockContext.Provider>
|
||||||
|
@ -1590,7 +1590,7 @@ export const useAssociationNames = (dataSource?: string) => {
|
|||||||
const { getCollectionJoinField, getCollection } = useCollectionManager_deprecated(dataSource);
|
const { getCollectionJoinField, getCollection } = useCollectionManager_deprecated(dataSource);
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
|
|
||||||
const getAssociationAppends = () => {
|
const getAssociationAppends = useCallback(() => {
|
||||||
const updateAssociationValues = new Set([]);
|
const updateAssociationValues = new Set([]);
|
||||||
let appends = new Set([]);
|
let appends = new Set([]);
|
||||||
|
|
||||||
@ -1606,7 +1606,7 @@ export const useAssociationNames = (dataSource?: string) => {
|
|||||||
appends = fillParentFields(appends);
|
appends = fillParentFields(appends);
|
||||||
|
|
||||||
return { appends: [...appends], updateAssociationValues: [...updateAssociationValues] };
|
return { appends: [...appends], updateAssociationValues: [...updateAssociationValues] };
|
||||||
};
|
}, [dataSource, fieldSchema, getCollection, getCollectionJoinField]);
|
||||||
|
|
||||||
return { getAssociationAppends };
|
return { getAssociationAppends };
|
||||||
};
|
};
|
||||||
|
@ -16,7 +16,6 @@ import { useDataSourceManager } from '../data-source';
|
|||||||
import { DEFAULT_DATA_SOURCE_KEY } from '../../data-source/data-source/DataSourceManager';
|
import { DEFAULT_DATA_SOURCE_KEY } from '../../data-source/data-source/DataSourceManager';
|
||||||
import { useCollection } from '../collection';
|
import { useCollection } from '../collection';
|
||||||
import { BlockItemCard } from '../../schema-component/antd/block-item/BlockItemCard';
|
import { BlockItemCard } from '../../schema-component/antd/block-item/BlockItemCard';
|
||||||
import { AnyKindOfDictionary } from 'lodash';
|
|
||||||
|
|
||||||
export interface CollectionDeletedPlaceholderProps {
|
export interface CollectionDeletedPlaceholderProps {
|
||||||
type: 'Collection' | 'Field' | 'Data Source' | 'Block template';
|
type: 'Collection' | 'Field' | 'Data Source' | 'Block template';
|
||||||
@ -99,6 +98,7 @@ export const CollectionDeletedPlaceholder: FC<CollectionDeletedPlaceholderProps>
|
|||||||
...confirm,
|
...confirm,
|
||||||
onOk() {
|
onOk() {
|
||||||
dn.remove(null, { removeParentsIfNoChildren: true, breakRemoveOn: { 'x-component': 'Grid' } });
|
dn.remove(null, { removeParentsIfNoChildren: true, breakRemoveOn: { 'x-component': 'Grid' } });
|
||||||
|
dn.refresh({ refreshParentSchema: true });
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -225,10 +225,27 @@ export const useDataBlockRequest = <T extends {}>(): UseRequestResult<{ data: T
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compared to `useDataBlockRequest`, the advantage of this hook is that it prevents unnecessary re-renders.
|
* Compared to `useDataBlockRequest`, this Hook helps prevent unnecessary re-renders.
|
||||||
* For example, if you only need to use methods like `refresh` or `run`, it's recommended to use this hook,
|
*
|
||||||
* as it avoids component re-rendering when re-triggering requests.
|
* This Hook returns a stable function reference that won't change between renders. When you only need
|
||||||
* @returns
|
* methods like `refresh` or `run`, using this Hook is recommended because:
|
||||||
|
*
|
||||||
|
* 1. It returns a memoized object containing only the getter function
|
||||||
|
* 2. The getter function accesses the latest request data through a ref, avoiding re-renders
|
||||||
|
* 3. Unlike useDataBlockRequest which returns request state directly, this Hook provides indirect access
|
||||||
|
* through a getter, breaking the reactive dependency chain
|
||||||
|
*
|
||||||
|
* For example:
|
||||||
|
* ```ts
|
||||||
|
* // This will re-render when request state changes
|
||||||
|
* const { refresh } = useDataBlockRequest();
|
||||||
|
*
|
||||||
|
* // This won't re-render when request state changes
|
||||||
|
* const { getDataBlockRequest } = useDataBlockRequestGetter();
|
||||||
|
* const refresh = getDataBlockRequest().refresh;
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @returns An object containing the getDataBlockRequest method that provides access to the request instance
|
||||||
*/
|
*/
|
||||||
export const useDataBlockRequestGetter = () => {
|
export const useDataBlockRequestGetter = () => {
|
||||||
const contextRef = useContext(BlockRequestRefContext);
|
const contextRef = useContext(BlockRequestRefContext);
|
||||||
|
@ -7,23 +7,32 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { FieldContext, IFieldProps, JSXComponent, Schema, useField, useForm } from '@formily/react';
|
import { FieldContext, IFieldProps, JSXComponent, Schema, useFieldSchema } from '@formily/react';
|
||||||
import React from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { useCompile } from '../schema-component/hooks/useCompile';
|
import { useCompile } from '../schema-component/hooks/useCompile';
|
||||||
import { NocoBaseReactiveField } from './NocoBaseReactiveField';
|
import { NocoBaseReactiveField } from './NocoBaseReactiveField';
|
||||||
import { createNocoBaseField } from './createNocoBaseField';
|
import { createNocoBaseField } from './createNocoBaseField';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To maintain high performance of Table, this component removes Formily-related components
|
||||||
|
* @param props component props
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
export const NocoBaseField = <D extends JSXComponent, C extends JSXComponent>(
|
export const NocoBaseField = <D extends JSXComponent, C extends JSXComponent>(
|
||||||
props: IFieldProps<D, C> & { schema: Schema },
|
props: IFieldProps<D, C> & { schema: Schema },
|
||||||
) => {
|
) => {
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const form = useForm();
|
const fieldSchema = useFieldSchema();
|
||||||
const parent = useField();
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
const field = createNocoBaseField.call(form, { basePath: parent?.address, compile, ...props });
|
const field = useMemo(() => createNocoBaseField({ ...props, compile }), []);
|
||||||
|
|
||||||
|
// update componentProps to rerender field component
|
||||||
|
Object.assign(field.componentProps, fieldSchema['x-component-props']);
|
||||||
|
field.value = props.value;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FieldContext.Provider value={field}>
|
<FieldContext.Provider value={field as any}>
|
||||||
<NocoBaseReactiveField field={field}>{props.children}</NocoBaseReactiveField>
|
<NocoBaseReactiveField field={field as any}>{props.children}</NocoBaseReactiveField>
|
||||||
</FieldContext.Provider>
|
</FieldContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -7,9 +7,8 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Form, GeneralField, isVoidField } from '@formily/core';
|
import { Form, GeneralField } from '@formily/core';
|
||||||
import { RenderPropsChildren, SchemaComponentsContext } from '@formily/react';
|
import { RenderPropsChildren, SchemaComponentsContext } from '@formily/react';
|
||||||
import { observer } from '@formily/reactive-react';
|
|
||||||
import { FormPath, isFn } from '@formily/shared';
|
import { FormPath, isFn } from '@formily/shared';
|
||||||
import React, { Fragment, useContext } from 'react';
|
import React, { Fragment, useContext } from 'react';
|
||||||
interface IReactiveFieldProps {
|
interface IReactiveFieldProps {
|
||||||
@ -34,19 +33,16 @@ const renderChildren = (children: RenderPropsChildren<GeneralField>, field?: Gen
|
|||||||
isFn(children) ? children(field, form) : children;
|
isFn(children) ? children(field, form) : children;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Based on @formily/react v2.3.2 ReactiveInternal component
|
* To maintain high performance of Table, this component removes Formily-related code
|
||||||
* Modified to better adapt to NocoBase's needs
|
|
||||||
*/
|
*/
|
||||||
const ReactiveInternal: React.FC<IReactiveFieldProps> = (props) => {
|
const NocoBaseReactiveInternal: React.FC<IReactiveFieldProps> = (props) => {
|
||||||
const components = useContext(SchemaComponentsContext);
|
const components = useContext(SchemaComponentsContext);
|
||||||
if (!props.field) {
|
const field: any = props.field;
|
||||||
return <Fragment>{renderChildren(props.children)}</Fragment>;
|
|
||||||
}
|
|
||||||
const field = props.field;
|
|
||||||
const content = mergeChildren(
|
const content = mergeChildren(
|
||||||
renderChildren(props.children, field, field.form),
|
renderChildren(props.children, field, field.form),
|
||||||
field.content ?? field.componentProps.children,
|
field.content ?? field.componentProps.children,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (field.display !== 'visible') return null;
|
if (field.display !== 'visible') return null;
|
||||||
|
|
||||||
const getComponent = (target: any) => {
|
const getComponent = (target: any) => {
|
||||||
@ -63,27 +59,8 @@ const ReactiveInternal: React.FC<IReactiveFieldProps> = (props) => {
|
|||||||
|
|
||||||
const renderComponent = () => {
|
const renderComponent = () => {
|
||||||
if (!field.componentType) return content;
|
if (!field.componentType) return content;
|
||||||
const value = !isVoidField(field) ? field.value : undefined;
|
const disabled = true;
|
||||||
const onChange = !isVoidField(field)
|
const readOnly = true;
|
||||||
? (...args: any[]) => {
|
|
||||||
field.onInput(...args);
|
|
||||||
field.componentProps?.onChange?.(...args);
|
|
||||||
}
|
|
||||||
: field.componentProps?.onChange;
|
|
||||||
const onFocus = !isVoidField(field)
|
|
||||||
? (...args: any[]) => {
|
|
||||||
field.onFocus(...args);
|
|
||||||
field.componentProps?.onFocus?.(...args);
|
|
||||||
}
|
|
||||||
: field.componentProps?.onFocus;
|
|
||||||
const onBlur = !isVoidField(field)
|
|
||||||
? (...args: any[]) => {
|
|
||||||
field.onBlur(...args);
|
|
||||||
field.componentProps?.onBlur?.(...args);
|
|
||||||
}
|
|
||||||
: field.componentProps?.onBlur;
|
|
||||||
const disabled = !isVoidField(field) ? field.pattern === 'disabled' || field.pattern === 'readPretty' : undefined;
|
|
||||||
const readOnly = !isVoidField(field) ? field.pattern === 'readOnly' : undefined;
|
|
||||||
|
|
||||||
return React.createElement(
|
return React.createElement(
|
||||||
getComponent(field.componentType),
|
getComponent(field.componentType),
|
||||||
@ -91,10 +68,7 @@ const ReactiveInternal: React.FC<IReactiveFieldProps> = (props) => {
|
|||||||
disabled,
|
disabled,
|
||||||
readOnly,
|
readOnly,
|
||||||
...field.componentProps,
|
...field.componentProps,
|
||||||
value,
|
value: field.value,
|
||||||
onChange,
|
|
||||||
onFocus,
|
|
||||||
onBlur,
|
|
||||||
},
|
},
|
||||||
content,
|
content,
|
||||||
);
|
);
|
||||||
@ -103,14 +77,12 @@ const ReactiveInternal: React.FC<IReactiveFieldProps> = (props) => {
|
|||||||
return renderDecorator(renderComponent());
|
return renderDecorator(renderComponent());
|
||||||
};
|
};
|
||||||
|
|
||||||
ReactiveInternal.displayName = 'NocoBaseReactiveInternal';
|
NocoBaseReactiveInternal.displayName = 'NocoBaseReactiveInternal';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Based on @formily/react v2.3.2 NocoBaseReactiveField component
|
* Based on @formily/react v2.3.2 NocoBaseReactiveField component
|
||||||
* Modified to better adapt to NocoBase's needs
|
* Modified to better adapt to NocoBase's needs
|
||||||
*/
|
*/
|
||||||
export const NocoBaseReactiveField = observer(ReactiveInternal, {
|
export const NocoBaseReactiveField = NocoBaseReactiveInternal;
|
||||||
forwardRef: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
NocoBaseReactiveField.displayName = 'NocoBaseReactiveField';
|
NocoBaseReactiveField.displayName = 'NocoBaseReactiveField';
|
||||||
|
@ -24,10 +24,11 @@ import {
|
|||||||
import { isBool, isFn, isValid, merge } from '@formily/shared';
|
import { isBool, isFn, isValid, merge } from '@formily/shared';
|
||||||
import { useUpdate } from 'ahooks';
|
import { useUpdate } from 'ahooks';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { FC, Fragment, useCallback, useMemo } from 'react';
|
import React, { FC, Fragment, useCallback, useContext, useMemo, useRef } from 'react';
|
||||||
import { CollectionFieldOptions } from '../data-source/collection/Collection';
|
import { CollectionFieldOptions } from '../data-source/collection/Collection';
|
||||||
import { useCollectionManager } from '../data-source/collection/CollectionManagerProvider';
|
import { useCollectionManager } from '../data-source/collection/CollectionManagerProvider';
|
||||||
import { useCollection } from '../data-source/collection/CollectionProvider';
|
import { useCollection } from '../data-source/collection/CollectionProvider';
|
||||||
|
import { SchemaComponentOnChangeContext } from '../schema-component/core/SchemaComponent';
|
||||||
import { EMPTY_OBJECT } from '../variables';
|
import { EMPTY_OBJECT } from '../variables';
|
||||||
import { NocoBaseField } from './NocoBaseField';
|
import { NocoBaseField } from './NocoBaseField';
|
||||||
|
|
||||||
@ -51,22 +52,36 @@ interface INocoBaseRecursionFieldProps extends IRecursionFieldProps {
|
|||||||
|
|
||||||
const CollectionFieldUISchemaContext = React.createContext<CollectionFieldOptions>({});
|
const CollectionFieldUISchemaContext = React.createContext<CollectionFieldOptions>({});
|
||||||
|
|
||||||
const RefreshContext = React.createContext<(options?: { refreshParent?: boolean }) => void>(_.noop);
|
const RefreshFieldSchemaContext = React.createContext<(options?: { refreshParentSchema?: boolean }) => void>(_.noop);
|
||||||
|
|
||||||
const RefreshProvider: FC<{ refresh: (options?: { refreshParent?: boolean }) => void }> = ({ children, refresh }) => {
|
const RefreshFieldSchemaProvider: FC<{ refresh: (options?: { refreshParentSchema?: boolean }) => void }> = ({
|
||||||
|
children,
|
||||||
|
refresh,
|
||||||
|
}) => {
|
||||||
const refreshParent = useRefreshFieldSchema();
|
const refreshParent = useRefreshFieldSchema();
|
||||||
|
|
||||||
const value = useCallback(
|
const value = useCallback(
|
||||||
(options?: { refreshParent?: boolean }) => {
|
(options?: { refreshParentSchema?: boolean }) => {
|
||||||
if (options?.refreshParent) {
|
refresh();
|
||||||
|
|
||||||
|
if (options?.refreshParentSchema) {
|
||||||
refreshParent?.();
|
refreshParent?.();
|
||||||
}
|
}
|
||||||
refresh();
|
|
||||||
},
|
},
|
||||||
[refreshParent, refresh],
|
[refreshParent, refresh],
|
||||||
);
|
);
|
||||||
|
|
||||||
return <RefreshContext.Provider value={value}>{children}</RefreshContext.Provider>;
|
return <RefreshFieldSchemaContext.Provider value={value}>{children}</RefreshFieldSchemaContext.Provider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const RefreshComponentContext = React.createContext<() => void>(_.noop);
|
||||||
|
|
||||||
|
export const RefreshComponentProvider: FC<{ refresh: () => void }> = ({ children, refresh }) => {
|
||||||
|
return <RefreshComponentContext.Provider value={refresh}>{children}</RefreshComponentContext.Provider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useRefreshComponent = () => {
|
||||||
|
return React.useContext(RefreshComponentContext);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,7 +89,7 @@ const RefreshProvider: FC<{ refresh: (options?: { refreshParent?: boolean }) =>
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const useRefreshFieldSchema = () => {
|
export const useRefreshFieldSchema = () => {
|
||||||
return React.useContext(RefreshContext);
|
return React.useContext(RefreshFieldSchemaContext);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -121,15 +136,11 @@ const useFieldProps = (schema: Schema) => {
|
|||||||
const useBasePath = (props: IRecursionFieldProps) => {
|
const useBasePath = (props: IRecursionFieldProps) => {
|
||||||
const parent = useField();
|
const parent = useField();
|
||||||
if (props.onlyRenderProperties) {
|
if (props.onlyRenderProperties) {
|
||||||
return props.basePath || parent?.address.concat(props.name);
|
return props.basePath || parent?.address?.concat(props.name);
|
||||||
}
|
}
|
||||||
return props.basePath || parent?.address;
|
return props.basePath || parent?.address;
|
||||||
};
|
};
|
||||||
|
|
||||||
const createSchemaInstance = _.memoize((schema: ISchema): Schema => {
|
|
||||||
return new Schema(schema);
|
|
||||||
});
|
|
||||||
|
|
||||||
const createMergedSchemaInstance = (schema: Schema, uiSchema: ISchema, onlyRenderProperties: boolean) => {
|
const createMergedSchemaInstance = (schema: Schema, uiSchema: ISchema, onlyRenderProperties: boolean) => {
|
||||||
const clonedSchema = schema.toJSON();
|
const clonedSchema = schema.toJSON();
|
||||||
|
|
||||||
@ -257,14 +268,32 @@ export const NocoBaseRecursionField: ReactFC<INocoBaseRecursionFieldProps> = Rea
|
|||||||
uiSchema,
|
uiSchema,
|
||||||
} = props;
|
} = props;
|
||||||
const basePath = useBasePath(props);
|
const basePath = useBasePath(props);
|
||||||
const fieldSchema = createSchemaInstance(schema);
|
const newFieldSchemaRef = useRef(null);
|
||||||
|
const oldFieldSchema = useMemo(() => {
|
||||||
|
newFieldSchemaRef.current = null;
|
||||||
|
return new Schema(schema);
|
||||||
|
}, [schema]);
|
||||||
const { uiSchema: collectionFiledUiSchema, defaultValue } = useCollectionFieldUISchema();
|
const { uiSchema: collectionFiledUiSchema, defaultValue } = useCollectionFieldUISchema();
|
||||||
const update = useUpdate();
|
const update = useUpdate();
|
||||||
|
const { onChange: onChangeFromContext } = useContext(SchemaComponentOnChangeContext);
|
||||||
|
|
||||||
|
const fieldSchema: Schema = newFieldSchemaRef.current || oldFieldSchema;
|
||||||
|
|
||||||
const refresh = useCallback(() => {
|
const refresh = useCallback(() => {
|
||||||
createSchemaInstance.cache.delete(schema);
|
const parent = fieldSchema.parent;
|
||||||
|
newFieldSchemaRef.current = new Schema(fieldSchema.toJSON(), parent);
|
||||||
|
|
||||||
|
if (parent?.properties) {
|
||||||
|
Object.keys(parent.properties).forEach((key) => {
|
||||||
|
if (key === fieldSchema.name) {
|
||||||
|
parent.properties[key] = newFieldSchemaRef.current;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}, [schema, update]);
|
onChangeFromContext?.();
|
||||||
|
}, [fieldSchema, onChangeFromContext, update]);
|
||||||
|
|
||||||
// Merge default Schema of collection fields
|
// Merge default Schema of collection fields
|
||||||
const mergedFieldSchema = useMemo(() => {
|
const mergedFieldSchema = useMemo(() => {
|
||||||
@ -330,7 +359,7 @@ export const NocoBaseRecursionField: ReactFC<INocoBaseRecursionFieldProps> = Rea
|
|||||||
// some default schema values would also be saved in fieldSchema.
|
// some default schema values would also be saved in fieldSchema.
|
||||||
return (
|
return (
|
||||||
<SchemaContext.Provider value={fieldSchema}>
|
<SchemaContext.Provider value={fieldSchema}>
|
||||||
<RefreshProvider refresh={refresh}>{render()}</RefreshProvider>
|
<RefreshFieldSchemaProvider refresh={refresh}>{render()}</RefreshFieldSchemaProvider>
|
||||||
</SchemaContext.Provider>
|
</SchemaContext.Provider>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -7,29 +7,12 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { FormPath, FormPathPattern, IFieldFactoryProps, IFieldProps, LifeCycleTypes } from '@formily/core';
|
import { IFieldProps } from '@formily/core';
|
||||||
import { Field } from '@formily/core/esm/models/Field';
|
|
||||||
import { locateNode } from '@formily/core/esm/shared/internals';
|
|
||||||
import { JSXComponent, Schema } from '@formily/react';
|
import { JSXComponent, Schema } from '@formily/react';
|
||||||
import { batch, define, observable, raw } from '@formily/reactive';
|
|
||||||
import { toArr } from '@formily/shared';
|
import { toArr } from '@formily/shared';
|
||||||
|
|
||||||
export function createNocoBaseField<Decorator extends JSXComponent, Component extends JSXComponent>(
|
export function createNocoBaseField<Decorator extends JSXComponent, Component extends JSXComponent>(props: any) {
|
||||||
props: IFieldFactoryProps<Decorator, Component> & { compile: (source: any) => any },
|
return new NocoBaseField(props);
|
||||||
): Field<Decorator, Component> {
|
|
||||||
const address = FormPath.parse(props.basePath).concat(props.name);
|
|
||||||
const identifier = address.toString();
|
|
||||||
if (!identifier) return;
|
|
||||||
if (!this.fields[identifier]) {
|
|
||||||
batch(() => {
|
|
||||||
new NocoBaseField(address, props, this, this.props.designable);
|
|
||||||
});
|
|
||||||
this.notify(LifeCycleTypes.ON_FORM_GRAPH_CHANGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.fields[identifier].value = props.value;
|
|
||||||
|
|
||||||
return this.fields[identifier] as any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,35 +23,79 @@ class NocoBaseField<
|
|||||||
Component extends JSXComponent = any,
|
Component extends JSXComponent = any,
|
||||||
TextType = any,
|
TextType = any,
|
||||||
ValueType = any,
|
ValueType = any,
|
||||||
> extends Field {
|
> {
|
||||||
declare props: IFieldProps<Decorator, Component, TextType, ValueType> & {
|
props: IFieldProps<Decorator, Component, TextType, ValueType> & {
|
||||||
schema: Schema;
|
schema: Schema;
|
||||||
compile: (source: any) => any;
|
compile: (source: any) => any;
|
||||||
};
|
};
|
||||||
|
initialized: boolean;
|
||||||
|
loading: boolean;
|
||||||
|
validating: boolean;
|
||||||
|
submitting: boolean;
|
||||||
|
selfModified: boolean;
|
||||||
|
active: boolean;
|
||||||
|
visited: boolean;
|
||||||
|
mounted: boolean;
|
||||||
|
unmounted: boolean;
|
||||||
|
inputValues: any[];
|
||||||
|
inputValue: any;
|
||||||
|
feedbacks: any[];
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
display: string;
|
||||||
|
pattern: string;
|
||||||
|
editable: boolean;
|
||||||
|
disabled: boolean;
|
||||||
|
readOnly: boolean;
|
||||||
|
readPretty: boolean;
|
||||||
|
visible: boolean;
|
||||||
|
hidden: boolean;
|
||||||
|
dataSource: any[];
|
||||||
|
validator: any;
|
||||||
|
required: boolean;
|
||||||
|
content: string;
|
||||||
|
initialValue: any;
|
||||||
|
value: any;
|
||||||
|
data: any;
|
||||||
|
decorator: any[];
|
||||||
|
component: any[];
|
||||||
|
decoratorProps: any;
|
||||||
|
componentProps: any;
|
||||||
|
decoratorType: any;
|
||||||
|
componentType: any;
|
||||||
|
path: any;
|
||||||
|
form: any;
|
||||||
|
|
||||||
|
constructor(props: any) {
|
||||||
|
this.props = props;
|
||||||
|
this.initialize();
|
||||||
|
// this.makeObservable();
|
||||||
|
}
|
||||||
|
|
||||||
protected initialize() {
|
protected initialize() {
|
||||||
const compile = this.props.compile;
|
const compile = this.props.compile;
|
||||||
|
|
||||||
this.initialized = false;
|
this.pattern = 'readPretty';
|
||||||
|
this.readPretty = true;
|
||||||
|
|
||||||
|
this.initialized = true;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.validating = false;
|
this.validating = false;
|
||||||
this.submitting = false;
|
this.submitting = false;
|
||||||
this.selfModified = false;
|
this.selfModified = false;
|
||||||
this.active = false;
|
this.active = false;
|
||||||
this.visited = false;
|
this.visited = true;
|
||||||
this.mounted = false;
|
this.mounted = true;
|
||||||
this.unmounted = false;
|
this.unmounted = false;
|
||||||
this.inputValues = [];
|
this.inputValues = [];
|
||||||
this.inputValue = null;
|
this.inputValue = null;
|
||||||
this.feedbacks = [];
|
this.feedbacks = [];
|
||||||
this.title = compile(this.props.title || this.props.schema?.title);
|
this.title = compile(this.props.title || this.props.schema?.title);
|
||||||
this.description = compile(this.props.description || this.props.schema?.['description']);
|
this.description = compile(this.props.description || this.props.schema?.['description']);
|
||||||
this.display = this.props.display || this.props.schema?.['x-display'];
|
this.display = 'visible';
|
||||||
this.pattern = this.props.pattern || this.props.schema?.['x-pattern'];
|
|
||||||
this.editable = this.props.editable || this.props.schema?.['x-editable'];
|
this.editable = this.props.editable || this.props.schema?.['x-editable'];
|
||||||
this.disabled = this.props.disabled || this.props.schema?.['x-disabled'];
|
this.disabled = this.props.disabled || this.props.schema?.['x-disabled'];
|
||||||
this.readOnly = this.props.readOnly || this.props.schema?.['x-read-only'];
|
this.readOnly = this.props.readOnly || this.props.schema?.['x-read-only'];
|
||||||
this.readPretty = this.props.readPretty || this.props.schema?.['x-read-pretty'];
|
|
||||||
this.visible = this.props.visible || this.props.schema?.['x-visible'];
|
this.visible = this.props.visible || this.props.schema?.['x-visible'];
|
||||||
this.hidden = this.props.hidden || this.props.schema?.['x-hidden'];
|
this.hidden = this.props.hidden || this.props.schema?.['x-hidden'];
|
||||||
this.dataSource = compile(this.props.dataSource || (this.props.schema?.enum as any));
|
this.dataSource = compile(this.props.dataSource || (this.props.schema?.enum as any));
|
||||||
@ -84,19 +111,18 @@ class NocoBaseField<
|
|||||||
this.component = this.props.component
|
this.component = this.props.component
|
||||||
? toArr(this.props.component)
|
? toArr(this.props.component)
|
||||||
: [this.props.schema?.['x-component'], this.props.schema?.['x-component-props']];
|
: [this.props.schema?.['x-component'], this.props.schema?.['x-component-props']];
|
||||||
|
this.decoratorProps = this.props.schema?.['x-decorator-props'] || {};
|
||||||
|
this.componentProps = this.props.schema?.['x-component-props'] || {};
|
||||||
|
this.decoratorType = this.props.schema?.['x-decorator'];
|
||||||
|
this.componentType = this.props.schema?.['x-component'];
|
||||||
|
|
||||||
|
this.path = {};
|
||||||
|
this.form = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
locate(address: FormPathPattern) {
|
// protected makeObservable() {
|
||||||
raw(this.form.fields)[address.toString()] = this as any;
|
// define(this, {
|
||||||
locateNode(this as any, address);
|
// componentProps: observable,
|
||||||
}
|
// });
|
||||||
|
// }
|
||||||
protected makeObservable() {
|
|
||||||
define(this, {
|
|
||||||
componentProps: observable,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set as an empty function to prevent parent class from executing this method
|
|
||||||
protected makeReactive() {}
|
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ export * from './global-theme';
|
|||||||
export * from './hooks';
|
export * from './hooks';
|
||||||
export * from './i18n';
|
export * from './i18n';
|
||||||
export * from './icon';
|
export * from './icon';
|
||||||
|
export * from './lazy-helper';
|
||||||
export { default as locale } from './locale';
|
export { default as locale } from './locale';
|
||||||
export * from './nocobase-buildin-plugin';
|
export * from './nocobase-buildin-plugin';
|
||||||
export * from './plugin-manager';
|
export * from './plugin-manager';
|
||||||
@ -58,7 +59,6 @@ export * from './system-settings';
|
|||||||
export * from './testUtils';
|
export * from './testUtils';
|
||||||
export * from './user';
|
export * from './user';
|
||||||
export * from './variables';
|
export * from './variables';
|
||||||
export * from './lazy-helper';
|
|
||||||
|
|
||||||
export { withDynamicSchemaProps } from './hoc/withDynamicSchemaProps';
|
export { withDynamicSchemaProps } from './hoc/withDynamicSchemaProps';
|
||||||
export { withSkeletonComponent } from './hoc/withSkeletonComponent';
|
export { withSkeletonComponent } from './hoc/withSkeletonComponent';
|
||||||
@ -83,7 +83,8 @@ export { languageCodes } from './locale';
|
|||||||
|
|
||||||
// Override Formily API
|
// Override Formily API
|
||||||
export {
|
export {
|
||||||
NocoBaseRecursionField,
|
|
||||||
CollectionFieldUISchemaProvider,
|
CollectionFieldUISchemaProvider,
|
||||||
IsInNocoBaseRecursionFieldContext,
|
IsInNocoBaseRecursionFieldContext,
|
||||||
|
NocoBaseRecursionField,
|
||||||
|
useRefreshFieldSchema,
|
||||||
} from './formily/NocoBaseRecursionField';
|
} from './formily/NocoBaseRecursionField';
|
||||||
|
@ -17,7 +17,7 @@ test.describe('Add new: inherit', () => {
|
|||||||
// 1. click the "Add new" button, and then create a block, the block's collection should be "parent"
|
// 1. click the "Add new" button, and then create a block, the block's collection should be "parent"
|
||||||
await page.getByRole('button', { name: 'plus Add new' }).click();
|
await page.getByRole('button', { name: 'plus Add new' }).click();
|
||||||
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
||||||
await page.getByRole('menuitem', { name: 'form Form right' }).hover();
|
await page.getByRole('menuitem', { name: 'Form right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Current collection' }).click();
|
await page.getByRole('menuitem', { name: 'Current collection' }).click();
|
||||||
await page.getByLabel('block-item-CardItem-parent-form').hover();
|
await page.getByLabel('block-item-CardItem-parent-form').hover();
|
||||||
await expect(page.getByLabel('block-item-CardItem-parent-form').getByText('parent')).toBeVisible();
|
await expect(page.getByLabel('block-item-CardItem-parent-form').getByText('parent')).toBeVisible();
|
||||||
@ -28,7 +28,7 @@ test.describe('Add new: inherit', () => {
|
|||||||
await page.getByRole('button', { name: 'down' }).hover();
|
await page.getByRole('button', { name: 'down' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'child1' }).click();
|
await page.getByRole('menuitem', { name: 'child1' }).click();
|
||||||
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
||||||
await page.getByRole('menuitem', { name: 'form Form right' }).hover();
|
await page.getByRole('menuitem', { name: 'Form right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Current collection' }).click();
|
await page.getByRole('menuitem', { name: 'Current collection' }).click();
|
||||||
await page.getByLabel('block-item-CardItem-child1-').hover();
|
await page.getByLabel('block-item-CardItem-child1-').hover();
|
||||||
await expect(page.getByLabel('block-item-CardItem-child1-').getByText('child1')).toBeVisible();
|
await expect(page.getByLabel('block-item-CardItem-child1-').getByText('child1')).toBeVisible();
|
||||||
@ -39,7 +39,7 @@ test.describe('Add new: inherit', () => {
|
|||||||
await page.getByRole('button', { name: 'down' }).hover();
|
await page.getByRole('button', { name: 'down' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'child2' }).click();
|
await page.getByRole('menuitem', { name: 'child2' }).click();
|
||||||
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
||||||
await page.getByRole('menuitem', { name: 'form Form right' }).hover();
|
await page.getByRole('menuitem', { name: 'Form right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Current collection' }).click();
|
await page.getByRole('menuitem', { name: 'Current collection' }).click();
|
||||||
await page.getByLabel('block-item-CardItem-child2-').hover();
|
await page.getByLabel('block-item-CardItem-child2-').hover();
|
||||||
await expect(page.getByLabel('block-item-CardItem-child2-').getByText('child2')).toBeVisible();
|
await expect(page.getByLabel('block-item-CardItem-child2-').getByText('child2')).toBeVisible();
|
||||||
|
@ -17,7 +17,7 @@ test('basic', async ({ page, mockPage, mockRecord }) => {
|
|||||||
// 1. 打开弹窗,并创建一个 Table 关系区块
|
// 1. 打开弹窗,并创建一个 Table 关系区块
|
||||||
await page.getByLabel('action-Action.Link-Edit record-update-collection1-table-0').click();
|
await page.getByLabel('action-Action.Link-Edit record-update-collection1-table-0').click();
|
||||||
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
||||||
await page.getByRole('menuitem', { name: 'table Table right' }).hover();
|
await page.getByRole('menuitem', { name: 'Table right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Associated records' }).hover();
|
await page.getByRole('menuitem', { name: 'Associated records' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'manyToMany' }).click();
|
await page.getByRole('menuitem', { name: 'manyToMany' }).click();
|
||||||
|
|
||||||
|
@ -337,7 +337,7 @@ test.describe('set default value', () => {
|
|||||||
// 3. Table 数据选择器中使用 `Current popup record`
|
// 3. Table 数据选择器中使用 `Current popup record`
|
||||||
// 创建 Table 区块
|
// 创建 Table 区块
|
||||||
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
||||||
await page.getByRole('menuitem', { name: 'table Table right' }).hover();
|
await page.getByRole('menuitem', { name: 'Table right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Other records right' }).hover();
|
await page.getByRole('menuitem', { name: 'Other records right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Users' }).click();
|
await page.getByRole('menuitem', { name: 'Users' }).click();
|
||||||
await page.mouse.move(300, 0);
|
await page.mouse.move(300, 0);
|
||||||
@ -465,7 +465,7 @@ test.describe('set default value', () => {
|
|||||||
// 3. Table 数据选择器中使用 `Parent popup record`
|
// 3. Table 数据选择器中使用 `Parent popup record`
|
||||||
// 创建 Table 区块
|
// 创建 Table 区块
|
||||||
await page.getByLabel('schema-initializer-Grid-popup').nth(1).hover();
|
await page.getByLabel('schema-initializer-Grid-popup').nth(1).hover();
|
||||||
await page.getByRole('menuitem', { name: 'table Table right' }).hover();
|
await page.getByRole('menuitem', { name: 'Table right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Other records right' }).hover();
|
await page.getByRole('menuitem', { name: 'Other records right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Users' }).click();
|
await page.getByRole('menuitem', { name: 'Users' }).click();
|
||||||
await page.mouse.move(300, 0);
|
await page.mouse.move(300, 0);
|
||||||
@ -595,7 +595,7 @@ test.describe('set default value', () => {
|
|||||||
// 3. Table 数据选择器中使用 `Parent popup record`
|
// 3. Table 数据选择器中使用 `Parent popup record`
|
||||||
// 创建 Table 区块
|
// 创建 Table 区块
|
||||||
await page.getByLabel('schema-initializer-Grid-popup').nth(1).hover();
|
await page.getByLabel('schema-initializer-Grid-popup').nth(1).hover();
|
||||||
await page.getByRole('menuitem', { name: 'table Table right' }).hover();
|
await page.getByRole('menuitem', { name: 'Table right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Other records right' }).hover();
|
await page.getByRole('menuitem', { name: 'Other records right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Users' }).click();
|
await page.getByRole('menuitem', { name: 'Users' }).click();
|
||||||
await page.mouse.move(300, 0);
|
await page.mouse.move(300, 0);
|
||||||
@ -733,7 +733,7 @@ test.describe('set default value', () => {
|
|||||||
// 3. Table 数据选择器中使用 `Parent popup record`
|
// 3. Table 数据选择器中使用 `Parent popup record`
|
||||||
// 创建 Table 区块
|
// 创建 Table 区块
|
||||||
await page.getByLabel('schema-initializer-Grid-popup').nth(1).hover();
|
await page.getByLabel('schema-initializer-Grid-popup').nth(1).hover();
|
||||||
await page.getByRole('menuitem', { name: 'table Table right' }).hover();
|
await page.getByRole('menuitem', { name: 'Table right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Other records right' }).hover();
|
await page.getByRole('menuitem', { name: 'Other records right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Users' }).click();
|
await page.getByRole('menuitem', { name: 'Users' }).click();
|
||||||
await page.mouse.move(300, 0);
|
await page.mouse.move(300, 0);
|
||||||
|
@ -143,7 +143,7 @@ test.describe('creation form block schema settings', () => {
|
|||||||
|
|
||||||
// 创建区块的时候,可以选择刚才保存的模板 --------------------------------------------------
|
// 创建区块的时候,可以选择刚才保存的模板 --------------------------------------------------
|
||||||
await page.getByLabel('schema-initializer-Grid-page:addBlock').hover();
|
await page.getByLabel('schema-initializer-Grid-page:addBlock').hover();
|
||||||
await page.getByRole('menuitem', { name: 'form Form right' }).first().hover();
|
await page.getByRole('menuitem', { name: 'Form right' }).first().hover();
|
||||||
await page.getByRole('menuitem', { name: 'General right' }).hover();
|
await page.getByRole('menuitem', { name: 'General right' }).hover();
|
||||||
|
|
||||||
// Duplicate template
|
// Duplicate template
|
||||||
@ -152,7 +152,7 @@ test.describe('creation form block schema settings', () => {
|
|||||||
|
|
||||||
// Reference template
|
// Reference template
|
||||||
await page.getByLabel('schema-initializer-Grid-page:addBlock').hover();
|
await page.getByLabel('schema-initializer-Grid-page:addBlock').hover();
|
||||||
await page.getByRole('menuitem', { name: 'form Form right' }).first().hover();
|
await page.getByRole('menuitem', { name: 'Form right' }).first().hover();
|
||||||
await page.getByRole('menuitem', { name: 'General right' }).hover();
|
await page.getByRole('menuitem', { name: 'General right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'General right' }).click();
|
await page.getByRole('menuitem', { name: 'General right' }).click();
|
||||||
await page.getByRole('menuitem', { name: 'Reference template right' }).click();
|
await page.getByRole('menuitem', { name: 'Reference template right' }).click();
|
||||||
|
@ -32,7 +32,7 @@ test.describe('where list block can be added', () => {
|
|||||||
// 1. 打开弹窗,通过 Associated records 创建一个列表区块
|
// 1. 打开弹窗,通过 Associated records 创建一个列表区块
|
||||||
await page.getByLabel('action-Action.Link-View').click();
|
await page.getByLabel('action-Action.Link-View').click();
|
||||||
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
||||||
await page.getByRole('menuitem', { name: 'ordered-list List right' }).hover();
|
await page.getByRole('menuitem', { name: 'List right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Associated records right' }).hover();
|
await page.getByRole('menuitem', { name: 'Associated records right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Roles' }).click();
|
await page.getByRole('menuitem', { name: 'Roles' }).click();
|
||||||
await page.mouse.move(300, 0);
|
await page.mouse.move(300, 0);
|
||||||
@ -46,7 +46,7 @@ test.describe('where list block can be added', () => {
|
|||||||
|
|
||||||
// 2. 通过 Other records 创建一个列表区块
|
// 2. 通过 Other records 创建一个列表区块
|
||||||
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
||||||
await page.getByRole('menuitem', { name: 'ordered-list List right' }).hover();
|
await page.getByRole('menuitem', { name: 'List right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Other records right' }).hover();
|
await page.getByRole('menuitem', { name: 'Other records right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Users' }).click();
|
await page.getByRole('menuitem', { name: 'Users' }).click();
|
||||||
await page.mouse.move(300, 0);
|
await page.mouse.move(300, 0);
|
||||||
|
@ -137,12 +137,15 @@ test.describe('table data selector schema settings', () => {
|
|||||||
await expect(
|
await expect(
|
||||||
page.getByLabel('block-item-CardItem-table-selector-data-scope-variable-table-selector').getByRole('row'),
|
page.getByLabel('block-item-CardItem-table-selector-data-scope-variable-table-selector').getByRole('row'),
|
||||||
).toHaveCount(2); // 这里之所以是 2,是因为表头也是一个 row
|
).toHaveCount(2); // 这里之所以是 2,是因为表头也是一个 row
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page
|
page
|
||||||
.getByLabel('block-item-CardItem-table-selector-data-scope-variable-table-selector')
|
.getByLabel('block-item-CardItem-table-selector-data-scope-variable-table-selector')
|
||||||
.getByRole('row')
|
.getByRole('row')
|
||||||
.getByText(record['manyToMany'][0]['singleLineText']),
|
.getByText(record['manyToMany'][0]['singleLineText']),
|
||||||
).toBeVisible();
|
).toBeVisible({
|
||||||
|
timeout: 3000,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ test.describe('block template', () => {
|
|||||||
// The template saved above cannot be used to create a association block.
|
// The template saved above cannot be used to create a association block.
|
||||||
await page.getByLabel('action-Action.Link-View-view-').click();
|
await page.getByLabel('action-Action.Link-View-view-').click();
|
||||||
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
||||||
await page.getByRole('menuitem', { name: 'table Table right' }).hover();
|
await page.getByRole('menuitem', { name: 'Table right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Associated records right' }).hover();
|
await page.getByRole('menuitem', { name: 'Associated records right' }).hover();
|
||||||
|
|
||||||
// The saved template should not be displayed (no arrow should be shown)
|
// The saved template should not be displayed (no arrow should be shown)
|
||||||
@ -79,7 +79,7 @@ test.describe('block template', () => {
|
|||||||
|
|
||||||
// The template saved above cannot be used to create a non-association block.
|
// The template saved above cannot be used to create a non-association block.
|
||||||
await page.getByLabel('schema-initializer-Grid-page:').hover();
|
await page.getByLabel('schema-initializer-Grid-page:').hover();
|
||||||
await page.getByRole('menuitem', { name: 'table Table right' }).hover();
|
await page.getByRole('menuitem', { name: 'Table right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Roles right' }).hover();
|
await page.getByRole('menuitem', { name: 'Roles right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Duplicate template right' }).hover();
|
await page.getByRole('menuitem', { name: 'Duplicate template right' }).hover();
|
||||||
await expect(page.getByRole('menuitem', { name: 'Roles_Table' })).toBeVisible();
|
await expect(page.getByRole('menuitem', { name: 'Roles_Table' })).toBeVisible();
|
||||||
|
@ -23,7 +23,7 @@ test.describe('pagination', () => {
|
|||||||
|
|
||||||
// 1. 创建一个 Table
|
// 1. 创建一个 Table
|
||||||
await page.getByLabel('schema-initializer-Grid-page:').hover();
|
await page.getByLabel('schema-initializer-Grid-page:').hover();
|
||||||
await page.getByRole('menuitem', { name: 'table Table right' }).hover();
|
await page.getByRole('menuitem', { name: 'Table right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'collectionName' }).click();
|
await page.getByRole('menuitem', { name: 'collectionName' }).click();
|
||||||
// 显示出 ID
|
// 显示出 ID
|
||||||
await page.getByLabel('schema-initializer-TableV2-').hover();
|
await page.getByLabel('schema-initializer-TableV2-').hover();
|
||||||
|
@ -36,7 +36,7 @@ test.describe('where table block can be added', () => {
|
|||||||
|
|
||||||
// 添加当前表关系区块
|
// 添加当前表关系区块
|
||||||
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
||||||
await page.getByRole('menuitem', { name: 'table Table right' }).hover();
|
await page.getByRole('menuitem', { name: 'Table right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Associated records' }).hover();
|
await page.getByRole('menuitem', { name: 'Associated records' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'childAssociationField' }).click();
|
await page.getByRole('menuitem', { name: 'childAssociationField' }).click();
|
||||||
await page
|
await page
|
||||||
@ -47,7 +47,7 @@ test.describe('where table block can be added', () => {
|
|||||||
|
|
||||||
// 添加父表关系区块
|
// 添加父表关系区块
|
||||||
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
||||||
await page.getByRole('menuitem', { name: 'table Table right' }).hover();
|
await page.getByRole('menuitem', { name: 'Table right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Associated records' }).hover();
|
await page.getByRole('menuitem', { name: 'Associated records' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'parentAssociationField' }).click();
|
await page.getByRole('menuitem', { name: 'parentAssociationField' }).click();
|
||||||
await page.getByLabel('schema-initializer-TableV2-table:configureColumns-parentTargetCollection').hover();
|
await page.getByLabel('schema-initializer-TableV2-table:configureColumns-parentTargetCollection').hover();
|
||||||
@ -69,7 +69,7 @@ test.describe('where table block can be added', () => {
|
|||||||
|
|
||||||
// 通过 Other records 创建一个表格区块
|
// 通过 Other records 创建一个表格区块
|
||||||
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
||||||
await page.getByRole('menuitem', { name: 'table Table right' }).hover();
|
await page.getByRole('menuitem', { name: 'Table right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Other records right' }).hover();
|
await page.getByRole('menuitem', { name: 'Other records right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Users' }).click();
|
await page.getByRole('menuitem', { name: 'Users' }).click();
|
||||||
await page.mouse.move(300, 0);
|
await page.mouse.move(300, 0);
|
||||||
@ -99,7 +99,7 @@ test.describe('where table block can be added', () => {
|
|||||||
await page.getByLabel('action-Action.Link-View-view-').first().click();
|
await page.getByLabel('action-Action.Link-View-view-').first().click();
|
||||||
//表格关系区块
|
//表格关系区块
|
||||||
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
||||||
await page.getByRole('menuitem', { name: 'table Table right' }).click();
|
await page.getByRole('menuitem', { name: 'Table right' }).click();
|
||||||
await page.getByText('Associated records').hover();
|
await page.getByText('Associated records').hover();
|
||||||
const [request] = await Promise.all([
|
const [request] = await Promise.all([
|
||||||
page.waitForRequest((request) => request.url().includes('uiSchemas:insertAdjacent')),
|
page.waitForRequest((request) => request.url().includes('uiSchemas:insertAdjacent')),
|
||||||
|
@ -104,7 +104,7 @@ test.describe('actions schema settings', () => {
|
|||||||
|
|
||||||
// 配置出一个表单
|
// 配置出一个表单
|
||||||
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
||||||
await page.getByRole('menuitem', { name: 'form Form right' }).hover();
|
await page.getByRole('menuitem', { name: 'Form right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Current collection' }).click();
|
await page.getByRole('menuitem', { name: 'Current collection' }).click();
|
||||||
|
|
||||||
await page.getByLabel('schema-initializer-Grid-form:').hover();
|
await page.getByLabel('schema-initializer-Grid-form:').hover();
|
||||||
|
@ -14,7 +14,7 @@ test.describe('save as template', () => {
|
|||||||
// 1. 创建一个区块,然后保存为模板
|
// 1. 创建一个区块,然后保存为模板
|
||||||
await mockPage().goto();
|
await mockPage().goto();
|
||||||
await page.getByLabel('schema-initializer-Grid-page:').hover();
|
await page.getByLabel('schema-initializer-Grid-page:').hover();
|
||||||
await page.getByRole('menuitem', { name: 'table Table right' }).hover();
|
await page.getByRole('menuitem', { name: 'Table right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Users' }).click();
|
await page.getByRole('menuitem', { name: 'Users' }).click();
|
||||||
await page.getByLabel('block-item-CardItem-users-').hover();
|
await page.getByLabel('block-item-CardItem-users-').hover();
|
||||||
await page.getByLabel('designer-schema-settings-CardItem-blockSettings:table-users').hover();
|
await page.getByLabel('designer-schema-settings-CardItem-blockSettings:table-users').hover();
|
||||||
|
@ -19,6 +19,7 @@ import { useCollectionManager_deprecated } from '../../../../collection-manager'
|
|||||||
import { useFieldComponentName } from '../../../../common/useFieldComponentName';
|
import { useFieldComponentName } from '../../../../common/useFieldComponentName';
|
||||||
import { useCollection } from '../../../../data-source';
|
import { useCollection } from '../../../../data-source';
|
||||||
import { fieldComponentSettingsItem } from '../../../../data-source/commonsSettingsItem';
|
import { fieldComponentSettingsItem } from '../../../../data-source/commonsSettingsItem';
|
||||||
|
import { useFlag } from '../../../../flag-provider/hooks/useFlag';
|
||||||
import { useDesignable } from '../../../../schema-component';
|
import { useDesignable } from '../../../../schema-component';
|
||||||
import { useAssociationFieldContext } from '../../../../schema-component/antd/association-field/hooks';
|
import { useAssociationFieldContext } from '../../../../schema-component/antd/association-field/hooks';
|
||||||
import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator';
|
import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator';
|
||||||
@ -89,7 +90,13 @@ export const tableColumnSettings = new SchemaSettings({
|
|||||||
},
|
},
|
||||||
useVisible() {
|
useVisible() {
|
||||||
const { fieldSchema } = useColumnSchema();
|
const { fieldSchema } = useColumnSchema();
|
||||||
|
const { isInSubTable } = useFlag();
|
||||||
const field: any = useField();
|
const field: any = useField();
|
||||||
|
|
||||||
|
if (!isInSubTable) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const path = field.path?.splice(field.path?.length - 1, 1);
|
const path = field.path?.splice(field.path?.length - 1, 1);
|
||||||
if (fieldSchema) {
|
if (fieldSchema) {
|
||||||
const isReadPretty = field.form.query(`${path.concat(`*.` + fieldSchema.name)}`).get('readPretty');
|
const isReadPretty = field.form.query(`${path.concat(`*.` + fieldSchema.name)}`).get('readPretty');
|
||||||
|
@ -18,7 +18,7 @@ test.describe('where filter block can be added', () => {
|
|||||||
|
|
||||||
// 1. 页面中创建一个 filter form,一个 filter collapse
|
// 1. 页面中创建一个 filter form,一个 filter collapse
|
||||||
await page.getByLabel('schema-initializer-Grid-page:').hover();
|
await page.getByLabel('schema-initializer-Grid-page:').hover();
|
||||||
await page.getByRole('menuitem', { name: 'form Form right' }).nth(1).hover();
|
await page.getByRole('menuitem', { name: 'Form right' }).nth(1).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Users' }).click();
|
await page.getByRole('menuitem', { name: 'Users' }).click();
|
||||||
await page.getByLabel('schema-initializer-Grid-page:').hover();
|
await page.getByLabel('schema-initializer-Grid-page:').hover();
|
||||||
await page.getByRole('menuitem', { name: 'Collapse right' }).hover();
|
await page.getByRole('menuitem', { name: 'Collapse right' }).hover();
|
||||||
@ -99,7 +99,7 @@ test.describe('where filter block can be added', () => {
|
|||||||
await page.getByLabel('action-Action.Link-View record-view-users-table-1').click();
|
await page.getByLabel('action-Action.Link-View record-view-users-table-1').click();
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(1000);
|
||||||
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
||||||
await page.getByRole('menuitem', { name: 'form Form right' }).hover();
|
await page.getByRole('menuitem', { name: 'Form right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Roles' }).click();
|
await page.getByRole('menuitem', { name: 'Roles' }).click();
|
||||||
await page.getByLabel('schema-initializer-Grid-filterForm:configureFields-roles').hover();
|
await page.getByLabel('schema-initializer-Grid-filterForm:configureFields-roles').hover();
|
||||||
await page.getByRole('menuitem', { name: 'Role UID' }).click();
|
await page.getByRole('menuitem', { name: 'Role UID' }).click();
|
||||||
@ -151,7 +151,7 @@ test.describe('where filter block can be added', () => {
|
|||||||
|
|
||||||
// 2. 测试用表单筛选其它区块
|
// 2. 测试用表单筛选其它区块
|
||||||
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
||||||
await page.getByRole('menuitem', { name: 'form Form right' }).hover();
|
await page.getByRole('menuitem', { name: 'Form right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Users' }).click();
|
await page.getByRole('menuitem', { name: 'Users' }).click();
|
||||||
await page.getByLabel('schema-initializer-Grid-filterForm:configureFields-users').hover();
|
await page.getByLabel('schema-initializer-Grid-filterForm:configureFields-users').hover();
|
||||||
await page.getByRole('menuitem', { name: 'Nickname' }).click();
|
await page.getByRole('menuitem', { name: 'Nickname' }).click();
|
||||||
|
@ -42,6 +42,15 @@ export const ellipsisSettingsItem: SchemaSettingsItemType = {
|
|||||||
checked: !!schema['x-component-props']?.ellipsis,
|
checked: !!schema['x-component-props']?.ellipsis,
|
||||||
hidden,
|
hidden,
|
||||||
onChange: async (checked) => {
|
onChange: async (checked) => {
|
||||||
|
if (tableFieldSchema && tableFieldInstanceList) {
|
||||||
|
tableFieldInstanceList.forEach((fieldInstance) => {
|
||||||
|
fieldInstance.componentProps.ellipsis = checked;
|
||||||
|
});
|
||||||
|
schema['x-component-props']['ellipsis'] = checked;
|
||||||
|
} else {
|
||||||
|
formField.componentProps.ellipsis = checked;
|
||||||
|
}
|
||||||
|
|
||||||
await dn.emit('patch', {
|
await dn.emit('patch', {
|
||||||
schema: {
|
schema: {
|
||||||
'x-uid': schema['x-uid'],
|
'x-uid': schema['x-uid'],
|
||||||
@ -51,19 +60,6 @@ export const ellipsisSettingsItem: SchemaSettingsItemType = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (tableFieldSchema && tableFieldInstanceList) {
|
|
||||||
tableFieldInstanceList.forEach((fieldInstance) => {
|
|
||||||
fieldInstance.componentProps.ellipsis = checked;
|
|
||||||
});
|
|
||||||
schema['x-component-props']['ellipsis'] = checked;
|
|
||||||
const path = formField.path?.splice(formField.path?.length - 1, 1);
|
|
||||||
formField.form.query(`${path.concat(`*.` + fieldSchema.name)}`).forEach((f) => {
|
|
||||||
f.componentProps.ellipsis = checked;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
formField.componentProps.ellipsis = checked;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -66,8 +66,6 @@ test.describe('group page menus schema settings', () => {
|
|||||||
await page.locator('.ant-select-dropdown').getByText('anchor page').click();
|
await page.locator('.ant-select-dropdown').getByText('anchor page').click();
|
||||||
await page.getByLabel('Inner').click();
|
await page.getByLabel('Inner').click();
|
||||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||||
// 当前页面菜单会消失
|
|
||||||
await expect(page.getByLabel('group page', { exact: true })).not.toBeVisible();
|
|
||||||
// 跳转到 anchor page 页面,会有一个名为 group page 的子页面菜单
|
// 跳转到 anchor page 页面,会有一个名为 group page 的子页面菜单
|
||||||
await page.getByLabel('anchor page').click();
|
await page.getByLabel('anchor page').click();
|
||||||
await expect(page.locator('.ant-layout-sider').getByLabel('group page')).toBeVisible();
|
await expect(page.locator('.ant-layout-sider').getByLabel('group page')).toBeVisible();
|
||||||
|
@ -13,7 +13,7 @@ test.describe('page:addBlock', () => {
|
|||||||
test('当搜索不到数据时显示空状态', async ({ page, mockPage }) => {
|
test('当搜索不到数据时显示空状态', async ({ page, mockPage }) => {
|
||||||
await mockPage().goto();
|
await mockPage().goto();
|
||||||
await page.getByLabel('schema-initializer-Grid-').hover();
|
await page.getByLabel('schema-initializer-Grid-').hover();
|
||||||
await page.getByRole('menuitem', { name: 'table Table right' }).hover();
|
await page.getByRole('menuitem', { name: 'Table right' }).hover();
|
||||||
await page.getByRole('textbox', { name: 'Search and select collection' }).fill('no match');
|
await page.getByRole('textbox', { name: 'Search and select collection' }).fill('no match');
|
||||||
|
|
||||||
await expect(page.getByRole('menuitem', { name: 'No data' })).toBeVisible();
|
await expect(page.getByRole('menuitem', { name: 'No data' })).toBeVisible();
|
||||||
|
@ -86,7 +86,7 @@ test.describe('add blocks to the popup', () => {
|
|||||||
|
|
||||||
// 通过 Association records 创建关系区块
|
// 通过 Association records 创建关系区块
|
||||||
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
||||||
await page.getByRole('menuitem', { name: 'table Table right' }).hover();
|
await page.getByRole('menuitem', { name: 'Table right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Associated records' }).hover();
|
await page.getByRole('menuitem', { name: 'Associated records' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'manyToMany' }).click();
|
await page.getByRole('menuitem', { name: 'manyToMany' }).click();
|
||||||
await page.mouse.move(-300, 0);
|
await page.mouse.move(-300, 0);
|
||||||
@ -134,7 +134,7 @@ test.describe('add blocks to the popup', () => {
|
|||||||
|
|
||||||
// 通过 Association records 创建一个关系区块
|
// 通过 Association records 创建一个关系区块
|
||||||
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
||||||
await page.getByRole('menuitem', { name: 'table Table right' }).hover();
|
await page.getByRole('menuitem', { name: 'Table right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Associated records' }).hover();
|
await page.getByRole('menuitem', { name: 'Associated records' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Roles' }).click();
|
await page.getByRole('menuitem', { name: 'Roles' }).click();
|
||||||
await page
|
await page
|
||||||
|
@ -13,6 +13,7 @@ import classNames from 'classnames';
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import React, { FC, startTransition, useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { FC, startTransition, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { ErrorBoundary, FallbackProps } from 'react-error-boundary';
|
import { ErrorBoundary, FallbackProps } from 'react-error-boundary';
|
||||||
|
import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField';
|
||||||
import { ErrorFallback } from '../error-fallback';
|
import { ErrorFallback } from '../error-fallback';
|
||||||
import { useCurrentPopupContext } from '../page/PagePopups';
|
import { useCurrentPopupContext } from '../page/PagePopups';
|
||||||
import { TabsContextProvider, useTabsContext } from '../tabs/context';
|
import { TabsContextProvider, useTabsContext } from '../tabs/context';
|
||||||
@ -63,7 +64,7 @@ const ActionDrawerContent: FC<{ footerNodeName: string; field: any; schema: any
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MemoizeRecursionField
|
<NocoBaseRecursionField
|
||||||
basePath={field.address}
|
basePath={field.address}
|
||||||
schema={schema}
|
schema={schema}
|
||||||
onlyRenderProperties
|
onlyRenderProperties
|
||||||
@ -77,7 +78,7 @@ ActionDrawerContent.displayName = 'ActionDrawerContent';
|
|||||||
|
|
||||||
export const InternalActionDrawer: React.FC<ActionDrawerProps> = observer(
|
export const InternalActionDrawer: React.FC<ActionDrawerProps> = observer(
|
||||||
(props) => {
|
(props) => {
|
||||||
const { footerNodeName = 'Action.Drawer.Footer', zIndex: _zIndex, ...others } = props;
|
const { footerNodeName = 'Action.Drawer.Footer', zIndex: _zIndex, onClose: onCloseFromProps, ...others } = props;
|
||||||
const { visible, setVisible, openSize = 'middle', drawerProps } = useActionContext();
|
const { visible, setVisible, openSize = 'middle', drawerProps } = useActionContext();
|
||||||
const schema = useFieldSchema();
|
const schema = useFieldSchema();
|
||||||
const field = useField();
|
const field = useField();
|
||||||
@ -105,7 +106,13 @@ export const InternalActionDrawer: React.FC<ActionDrawerProps> = observer(
|
|||||||
|
|
||||||
const zIndex = _zIndex || parentZIndex + (props.level || 0);
|
const zIndex = _zIndex || parentZIndex + (props.level || 0);
|
||||||
|
|
||||||
const onClose = useCallback(() => setVisible(false, true), [setVisible]);
|
const onClose = useCallback(
|
||||||
|
(e) => {
|
||||||
|
setVisible(false, true);
|
||||||
|
onCloseFromProps?.(e);
|
||||||
|
},
|
||||||
|
[setVisible, onCloseFromProps],
|
||||||
|
);
|
||||||
const keepFooterNode = useCallback(
|
const keepFooterNode = useCallback(
|
||||||
(s) => {
|
(s) => {
|
||||||
return s['x-component'] === footerNodeName;
|
return s['x-component'] === footerNodeName;
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { cx } from '@emotion/css';
|
import { cx } from '@emotion/css';
|
||||||
import { observer, useFieldSchema } from '@formily/react';
|
import { useFieldSchema } from '@formily/react';
|
||||||
import { Space, SpaceProps } from 'antd';
|
import { Space, SpaceProps } from 'antd';
|
||||||
import React, { CSSProperties, useContext } from 'react';
|
import React, { CSSProperties, FC, useContext } from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import { useSchemaInitializerRender } from '../../../application';
|
import { useSchemaInitializerRender } from '../../../application';
|
||||||
import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField';
|
import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField';
|
||||||
@ -58,8 +58,7 @@ const Portal: React.FC = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ActionBar = withDynamicSchemaProps(
|
const InternalActionBar: FC = (props: any) => {
|
||||||
observer((props: any) => {
|
|
||||||
const { forceProps = {} } = useActionBarContext();
|
const { forceProps = {} } = useActionBarContext();
|
||||||
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||||
const { layout = 'two-columns', style, spaceProps, ...others } = { ...useProps(props), ...forceProps } as any;
|
const { layout = 'two-columns', style, spaceProps, ...others } = { ...useProps(props), ...forceProps } as any;
|
||||||
@ -77,7 +76,7 @@ export const ActionBar = withDynamicSchemaProps(
|
|||||||
{...others}
|
{...others}
|
||||||
className={cx(others.className, 'nb-action-bar')}
|
className={cx(others.className, 'nb-action-bar')}
|
||||||
>
|
>
|
||||||
{props.children && (
|
{
|
||||||
<div>
|
<div>
|
||||||
<Space {...spaceProps} style={{ flexWrap: 'wrap', ...(spaceProps?.style || {}) }}>
|
<Space {...spaceProps} style={{ flexWrap: 'wrap', ...(spaceProps?.style || {}) }}>
|
||||||
{fieldSchema.mapProperties((schema, key) => {
|
{fieldSchema.mapProperties((schema, key) => {
|
||||||
@ -85,7 +84,7 @@ export const ActionBar = withDynamicSchemaProps(
|
|||||||
})}
|
})}
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
)}
|
}
|
||||||
{render({ style: { margin: '0 !important' } })}
|
{render({ style: { margin: '0 !important' } })}
|
||||||
</div>
|
</div>
|
||||||
</DndContext>
|
</DndContext>
|
||||||
@ -144,6 +143,11 @@ export const ActionBar = withDynamicSchemaProps(
|
|||||||
{render()}
|
{render()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}),
|
};
|
||||||
|
|
||||||
|
export const ActionBar = withDynamicSchemaProps(
|
||||||
|
(props: any) => {
|
||||||
|
return <InternalActionBar {...props} />;
|
||||||
|
},
|
||||||
{ displayName: 'ActionBar' },
|
{ displayName: 'ActionBar' },
|
||||||
);
|
);
|
||||||
|
@ -54,9 +54,11 @@ export const InternalNester = observer(
|
|||||||
labelWidth = 120,
|
labelWidth = 120,
|
||||||
labelWrap = true,
|
labelWrap = true,
|
||||||
} = fieldSchema?.['x-component-props'] || {};
|
} = fieldSchema?.['x-component-props'] || {};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
insertNester(schema.Nester);
|
insertNester(schema.Nester);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CollectionProvider_deprecated name={collectionField.target}>
|
<CollectionProvider_deprecated name={collectionField.target}>
|
||||||
<ACLCollectionProvider actionPath={`${collectionField.target}:${actionName || 'view'}`}>
|
<ACLCollectionProvider actionPath={`${collectionField.target}:${actionName || 'view'}`}>
|
||||||
|
@ -155,7 +155,7 @@ const RenderRecord = React.memo(
|
|||||||
ellipsisWithTooltipRef,
|
ellipsisWithTooltipRef,
|
||||||
enableLink,
|
enableLink,
|
||||||
fieldNames?.label,
|
fieldNames?.label,
|
||||||
fieldSchema?.properties,
|
fieldSchema,
|
||||||
getLabelUiSchema,
|
getLabelUiSchema,
|
||||||
insertViewer,
|
insertViewer,
|
||||||
isTreeCollection,
|
isTreeCollection,
|
||||||
|
@ -37,7 +37,7 @@ export const useInsertSchema = (component) => {
|
|||||||
insertAfterBegin(cloneDeep(ss));
|
insertAfterBegin(cloneDeep(ss));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[component],
|
[component, fieldSchema, insertAfterBegin],
|
||||||
);
|
);
|
||||||
return insert;
|
return insert;
|
||||||
};
|
};
|
||||||
|
@ -8,12 +8,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Schema, useFieldSchema } from '@formily/react';
|
import { Schema, useFieldSchema } from '@formily/react';
|
||||||
import React, { useContext } from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { SchemaComponentContext, createDesignable } from '../..';
|
import { createDesignable } from '../..';
|
||||||
import { useAPIClient } from '../../../api-client';
|
import { useAPIClient } from '../../../api-client';
|
||||||
import { useBlockRequestContext } from '../../../block-provider';
|
import { useBlockRequestContext } from '../../../block-provider';
|
||||||
import { mergeFilter } from '../../../filter-provider/utils';
|
import { mergeFilter } from '../../../filter-provider/utils';
|
||||||
|
import { useRefreshFieldSchema } from '../../../formily/NocoBaseRecursionField';
|
||||||
import { ActionInitializerItem } from '../../../schema-initializer/items/ActionInitializerItem';
|
import { ActionInitializerItem } from '../../../schema-initializer/items/ActionInitializerItem';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -22,11 +23,11 @@ import { ActionInitializerItem } from '../../../schema-initializer/items/ActionI
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const ActionBarAssociationFilterAction = (props) => {
|
export const ActionBarAssociationFilterAction = (props) => {
|
||||||
const { refresh } = useContext(SchemaComponentContext);
|
const refreshFieldSchema = useRefreshFieldSchema();
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dn = createDesignable({ t, api, refresh, current: fieldSchema });
|
const dn = createDesignable({ t, api, refresh: refreshFieldSchema, current: fieldSchema });
|
||||||
const { service, props: blockProps } = useBlockRequestContext();
|
const { service, props: blockProps } = useBlockRequestContext();
|
||||||
|
|
||||||
dn.loadAPIClientEvents();
|
dn.loadAPIClientEvents();
|
||||||
|
@ -9,13 +9,14 @@
|
|||||||
|
|
||||||
import { FormLayout } from '@formily/antd-v5';
|
import { FormLayout } from '@formily/antd-v5';
|
||||||
import { createForm } from '@formily/core';
|
import { createForm } from '@formily/core';
|
||||||
import { FieldContext, FormContext, observer, RecursionField, useField, useFieldSchema } from '@formily/react';
|
import { FieldContext, FormContext, observer, useField, useFieldSchema } from '@formily/react';
|
||||||
import { Options, Result } from 'ahooks/es/useRequest/src/types';
|
import { Options, Result } from 'ahooks/es/useRequest/src/types';
|
||||||
import { ConfigProvider, Spin } from 'antd';
|
import { ConfigProvider, Spin } from 'antd';
|
||||||
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
||||||
import { useAttach, useComponent } from '../..';
|
import { useAttach, useComponent } from '../..';
|
||||||
import { useRequest } from '../../../api-client';
|
import { useRequest } from '../../../api-client';
|
||||||
import { useCollection_deprecated } from '../../../collection-manager';
|
import { useCollection_deprecated } from '../../../collection-manager';
|
||||||
|
import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField';
|
||||||
import { GeneralSchemaDesigner, SchemaSettingsDivider, SchemaSettingsRemove } from '../../../schema-settings';
|
import { GeneralSchemaDesigner, SchemaSettingsDivider, SchemaSettingsRemove } from '../../../schema-settings';
|
||||||
import { SchemaSettingsTemplate } from '../../../schema-settings/SchemaSettingsTemplate';
|
import { SchemaSettingsTemplate } from '../../../schema-settings/SchemaSettingsTemplate';
|
||||||
import { useSchemaTemplate } from '../../../schema-templates';
|
import { useSchemaTemplate } from '../../../schema-templates';
|
||||||
@ -39,7 +40,7 @@ const FormComponent: React.FC<FormProps> = (props) => {
|
|||||||
<FieldContext.Provider value={undefined}>
|
<FieldContext.Provider value={undefined}>
|
||||||
<FormContext.Provider value={form}>
|
<FormContext.Provider value={form}>
|
||||||
<FormLayout layout={'vertical'} {...others}>
|
<FormLayout layout={'vertical'} {...others}>
|
||||||
<RecursionField basePath={f.address} schema={fieldSchema} onlyRenderProperties />
|
<NocoBaseRecursionField basePath={f.address} schema={fieldSchema} onlyRenderProperties />
|
||||||
</FormLayout>
|
</FormLayout>
|
||||||
</FormContext.Provider>
|
</FormContext.Provider>
|
||||||
</FieldContext.Provider>
|
</FieldContext.Provider>
|
||||||
@ -65,7 +66,7 @@ const FormDecorator: React.FC<FormProps> = (props) => {
|
|||||||
<FormLayout layout={'vertical'} {...others}>
|
<FormLayout layout={'vertical'} {...others}>
|
||||||
<FieldContext.Provider value={f}>
|
<FieldContext.Provider value={f}>
|
||||||
<Component {...field.componentProps}>
|
<Component {...field.componentProps}>
|
||||||
<RecursionField basePath={f.address} schema={fieldSchema} onlyRenderProperties />
|
<NocoBaseRecursionField basePath={f.address} schema={fieldSchema} onlyRenderProperties />
|
||||||
</Component>
|
</Component>
|
||||||
</FieldContext.Provider>
|
</FieldContext.Provider>
|
||||||
{/* <FieldContext.Provider value={f}>{children}</FieldContext.Provider> */}
|
{/* <FieldContext.Provider value={f}>{children}</FieldContext.Provider> */}
|
||||||
|
@ -52,33 +52,6 @@ export const GridCardDesigner = () => {
|
|||||||
const defaultResource =
|
const defaultResource =
|
||||||
fieldSchema?.['x-decorator-props']?.resource || fieldSchema?.['x-decorator-props']?.association;
|
fieldSchema?.['x-decorator-props']?.resource || fieldSchema?.['x-decorator-props']?.association;
|
||||||
|
|
||||||
const columnCountSchema = useMemo(() => {
|
|
||||||
return {
|
|
||||||
'x-component': 'Slider',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component-props': {
|
|
||||||
min: 1,
|
|
||||||
max: 24,
|
|
||||||
marks: columnCountMarks,
|
|
||||||
tooltip: {
|
|
||||||
formatter: (value) => `${value}${t('Column')}`,
|
|
||||||
},
|
|
||||||
step: null,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
const columnCountProperties = useMemo(() => {
|
|
||||||
return gridSizes.reduce((o, k) => {
|
|
||||||
o[k] = {
|
|
||||||
...columnCountSchema,
|
|
||||||
title: t(screenSizeTitleMaps[k]),
|
|
||||||
description: `${t('Screen size')} ${screenSizeMaps[k]} ${t('pixels')}`,
|
|
||||||
};
|
|
||||||
return o;
|
|
||||||
}, {});
|
|
||||||
}, [columnCountSchema, t]);
|
|
||||||
|
|
||||||
const sort = defaultSort?.map((item: string) => {
|
const sort = defaultSort?.map((item: string) => {
|
||||||
return item.startsWith('-')
|
return item.startsWith('-')
|
||||||
? {
|
? {
|
||||||
|
@ -10,10 +10,11 @@
|
|||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import { FormLayout } from '@formily/antd-v5';
|
import { FormLayout } from '@formily/antd-v5';
|
||||||
import { ArrayField } from '@formily/core';
|
import { ArrayField } from '@formily/core';
|
||||||
import { RecursionField, Schema, useField, useFieldSchema } from '@formily/react';
|
import { Schema, useField, useFieldSchema } from '@formily/react';
|
||||||
import { List as AntdList, Col, PaginationProps } from 'antd';
|
import { List as AntdList, Col, PaginationProps } from 'antd';
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { getCardItemSchema } from '../../../block-provider';
|
import { getCardItemSchema } from '../../../block-provider';
|
||||||
|
import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField';
|
||||||
import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps';
|
import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps';
|
||||||
import { withSkeletonComponent } from '../../../hoc/withSkeletonComponent';
|
import { withSkeletonComponent } from '../../../hoc/withSkeletonComponent';
|
||||||
import { SortableItem } from '../../common';
|
import { SortableItem } from '../../common';
|
||||||
@ -127,25 +128,18 @@ const InternalGridCard = withSkeletonComponent(
|
|||||||
const field = useField<ArrayField>();
|
const field = useField<ArrayField>();
|
||||||
const Designer = useDesigner();
|
const Designer = useDesigner();
|
||||||
const height = useGridCardBodyHeight();
|
const height = useGridCardBodyHeight();
|
||||||
const [schemaMap] = useState(new Map());
|
|
||||||
const getSchema = useCallback(
|
const getSchema = useCallback(
|
||||||
(key) => {
|
(key) => {
|
||||||
if (!schemaMap.has(key)) {
|
return new Schema({
|
||||||
schemaMap.set(
|
|
||||||
key,
|
|
||||||
new Schema({
|
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
[key]: {
|
[key]: {
|
||||||
...fieldSchema.properties['item'],
|
...fieldSchema.properties['item'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
}
|
|
||||||
return schemaMap.get(key);
|
|
||||||
},
|
},
|
||||||
[fieldSchema.properties, schemaMap],
|
[fieldSchema.properties],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onPaginationChange: PaginationProps['onChange'] = useCallback(
|
const onPaginationChange: PaginationProps['onChange'] = useCallback(
|
||||||
@ -218,13 +212,13 @@ const InternalGridCard = withSkeletonComponent(
|
|||||||
renderItem={(item, index) => {
|
renderItem={(item, index) => {
|
||||||
return (
|
return (
|
||||||
<Col style={{ height: '100%' }} className="nb-card-item-warper">
|
<Col style={{ height: '100%' }} className="nb-card-item-warper">
|
||||||
<RecursionField
|
<NocoBaseRecursionField
|
||||||
key={index}
|
key={index}
|
||||||
basePath={field.address}
|
basePath={field.address}
|
||||||
name={index}
|
name={index}
|
||||||
onlyRenderProperties
|
onlyRenderProperties
|
||||||
schema={getSchema(index)}
|
schema={getSchema(index)}
|
||||||
></RecursionField>
|
></NocoBaseRecursionField>
|
||||||
</Col>
|
</Col>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
@ -297,7 +297,7 @@ const useRowProperties = () => {
|
|||||||
}
|
}
|
||||||
return buf;
|
return buf;
|
||||||
}, []);
|
}, []);
|
||||||
}, [Object.keys(fieldSchema.properties || {}).join(',')]);
|
}, [fieldSchema]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const useColProperties = () => {
|
const useColProperties = () => {
|
||||||
@ -309,7 +309,7 @@ const useColProperties = () => {
|
|||||||
}
|
}
|
||||||
return buf;
|
return buf;
|
||||||
}, []);
|
}, []);
|
||||||
}, [Object.keys(fieldSchema.properties || {}).join(',')]);
|
}, [fieldSchema]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const DndWrapper = (props) => {
|
const DndWrapper = (props) => {
|
||||||
@ -361,7 +361,7 @@ export const Grid: any = observer(
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
gridRef.current && setPrintContent?.(gridRef.current);
|
gridRef.current && setPrintContent?.(gridRef.current);
|
||||||
}, [gridRef.current]);
|
}, [setPrintContent]);
|
||||||
|
|
||||||
const gridContextValue = useMemo(() => {
|
const gridContextValue = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
@ -498,7 +498,7 @@ Grid.Col = observer(
|
|||||||
width = `calc(${w}% - ${token.marginBlock}px * ${(showDivider ? cols.length + 1 : 0) / cols.length})`;
|
width = `calc(${w}% - ${token.marginBlock}px * ${(showDivider ? cols.length + 1 : 0) / cols.length})`;
|
||||||
}
|
}
|
||||||
return { width };
|
return { width };
|
||||||
}, [cols?.length, schema?.['x-component-props']?.['width'], token.marginBlock]);
|
}, [cols.length, schema, showDivider, token.marginBlock]);
|
||||||
|
|
||||||
const { isOver, setNodeRef } = useDroppable({
|
const { isOver, setNodeRef } = useDroppable({
|
||||||
id: field.address.toString(),
|
id: field.address.toString(),
|
||||||
|
@ -10,10 +10,11 @@
|
|||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import { FormLayout } from '@formily/antd-v5';
|
import { FormLayout } from '@formily/antd-v5';
|
||||||
import { ArrayField } from '@formily/core';
|
import { ArrayField } from '@formily/core';
|
||||||
import { RecursionField, Schema, useField, useFieldSchema } from '@formily/react';
|
import { Schema, useField, useFieldSchema } from '@formily/react';
|
||||||
import { List as AntdList, PaginationProps, theme } from 'antd';
|
import { List as AntdList, PaginationProps, theme } from 'antd';
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { getCardItemSchema } from '../../../block-provider';
|
import { getCardItemSchema } from '../../../block-provider';
|
||||||
|
import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField';
|
||||||
import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps';
|
import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps';
|
||||||
import { withSkeletonComponent } from '../../../hoc/withSkeletonComponent';
|
import { withSkeletonComponent } from '../../../hoc/withSkeletonComponent';
|
||||||
import { SortableItem } from '../../common';
|
import { SortableItem } from '../../common';
|
||||||
@ -159,13 +160,13 @@ const InternalList = withSkeletonComponent(
|
|||||||
{field.value?.length
|
{field.value?.length
|
||||||
? field.value.map((item, index) => {
|
? field.value.map((item, index) => {
|
||||||
return (
|
return (
|
||||||
<RecursionField
|
<NocoBaseRecursionField
|
||||||
basePath={field.address}
|
basePath={field.address}
|
||||||
key={index}
|
key={index}
|
||||||
name={index}
|
name={index}
|
||||||
onlyRenderProperties
|
onlyRenderProperties
|
||||||
schema={getSchema(index)}
|
schema={getSchema(index)}
|
||||||
></RecursionField>
|
></NocoBaseRecursionField>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
: null}
|
: null}
|
||||||
|
@ -11,7 +11,6 @@ import { css } from '@emotion/css';
|
|||||||
import {
|
import {
|
||||||
FieldContext,
|
FieldContext,
|
||||||
observer,
|
observer,
|
||||||
RecursionField,
|
|
||||||
SchemaContext,
|
SchemaContext,
|
||||||
SchemaExpressionScopeContext,
|
SchemaExpressionScopeContext,
|
||||||
useField,
|
useField,
|
||||||
@ -23,14 +22,22 @@ import { Menu as AntdMenu, MenuProps } from 'antd';
|
|||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { createDesignable, DndContext, SchemaComponentContext, SortableItem, useDesignable, useDesigner } from '../..';
|
import { createDesignable, DndContext, SchemaComponentContext, SortableItem, useDesignable, useDesigner } from '../..';
|
||||||
import { Icon, useAPIClient, useParseURLAndParams, useSchemaInitializerRender } from '../../../';
|
import {
|
||||||
|
Icon,
|
||||||
|
NocoBaseRecursionField,
|
||||||
|
useAPIClient,
|
||||||
|
useParseURLAndParams,
|
||||||
|
useSchemaInitializerRender,
|
||||||
|
} from '../../../';
|
||||||
import { useCollectMenuItems, useMenuItem } from '../../../hooks/useMenuItem';
|
import { useCollectMenuItems, useMenuItem } from '../../../hooks/useMenuItem';
|
||||||
import { useProps } from '../../hooks/useProps';
|
import { useProps } from '../../hooks/useProps';
|
||||||
import { useMenuTranslation } from './locale';
|
import { useMenuTranslation } from './locale';
|
||||||
import { MenuDesigner } from './Menu.Designer';
|
import { MenuDesigner } from './Menu.Designer';
|
||||||
import { findKeysByUid, findMenuItem } from './util';
|
import { findKeysByUid, findMenuItem } from './util';
|
||||||
|
|
||||||
|
import { useUpdate } from 'ahooks';
|
||||||
import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
import { useRefreshComponent, useRefreshFieldSchema } from '../../../formily/NocoBaseRecursionField';
|
||||||
|
|
||||||
const subMenuDesignerCss = css`
|
const subMenuDesignerCss = css`
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -210,7 +217,6 @@ const HeaderMenu = React.memo<{
|
|||||||
onChange: any;
|
onChange: any;
|
||||||
onFocus: any;
|
onFocus: any;
|
||||||
theme: any;
|
theme: any;
|
||||||
refreshId: number;
|
|
||||||
}>(
|
}>(
|
||||||
({
|
({
|
||||||
schema,
|
schema,
|
||||||
@ -228,10 +234,6 @@ const HeaderMenu = React.memo<{
|
|||||||
onChange,
|
onChange,
|
||||||
onFocus,
|
onFocus,
|
||||||
theme,
|
theme,
|
||||||
/**
|
|
||||||
* Used to refresh the component
|
|
||||||
*/
|
|
||||||
refreshId,
|
|
||||||
}) => {
|
}) => {
|
||||||
const { Component, getMenuItems } = useMenuItem();
|
const { Component, getMenuItems } = useMenuItem();
|
||||||
const items = useMemo(() => {
|
const items = useMemo(() => {
|
||||||
@ -255,7 +257,7 @@ const HeaderMenu = React.memo<{
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [children, designable, refreshId]);
|
}, [children, designable]);
|
||||||
|
|
||||||
const handleSelect = useCallback(
|
const handleSelect = useCallback(
|
||||||
(info: { item; key; keyPath; domEvent }) => {
|
(info: { item; key; keyPath; domEvent }) => {
|
||||||
@ -327,17 +329,24 @@ const SideMenu = React.memo<any>(
|
|||||||
t,
|
t,
|
||||||
api,
|
api,
|
||||||
designable,
|
designable,
|
||||||
refreshId,
|
|
||||||
refresh,
|
|
||||||
}) => {
|
}) => {
|
||||||
// Used to refresh component
|
|
||||||
refreshId;
|
|
||||||
|
|
||||||
const { Component, getMenuItems } = useMenuItem();
|
const { Component, getMenuItems } = useMenuItem();
|
||||||
|
|
||||||
// 使用 ref 用来防止闭包问题
|
const update = useUpdate();
|
||||||
const sideMenuSchemaRef = useRef(sideMenuSchema);
|
const refreshFieldSchema = useRefreshFieldSchema();
|
||||||
sideMenuSchemaRef.current = sideMenuSchema;
|
const refreshComponent = useRefreshComponent();
|
||||||
|
const refresh = useCallback(
|
||||||
|
(options?: { refreshParentSchema?: boolean }) => {
|
||||||
|
console.log('refresh');
|
||||||
|
// refresh current component
|
||||||
|
update();
|
||||||
|
// refresh fieldSchema context value
|
||||||
|
refreshFieldSchema?.(options);
|
||||||
|
// refresh component context value
|
||||||
|
refreshComponent?.();
|
||||||
|
},
|
||||||
|
[update, refreshFieldSchema, refreshComponent],
|
||||||
|
);
|
||||||
|
|
||||||
const handleSelect = useCallback(
|
const handleSelect = useCallback(
|
||||||
(info) => {
|
(info) => {
|
||||||
@ -348,7 +357,7 @@ const SideMenu = React.memo<any>(
|
|||||||
|
|
||||||
const items = useMemo(() => {
|
const items = useMemo(() => {
|
||||||
const result = getMenuItems(() => {
|
const result = getMenuItems(() => {
|
||||||
return <RecursionField key={uid()} schema={sideMenuSchema} onlyRenderProperties />;
|
return <NocoBaseRecursionField key={uid()} schema={sideMenuSchema} onlyRenderProperties />;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (designable) {
|
if (designable) {
|
||||||
@ -362,7 +371,7 @@ const SideMenu = React.memo<any>(
|
|||||||
t,
|
t,
|
||||||
api,
|
api,
|
||||||
refresh: refresh,
|
refresh: refresh,
|
||||||
current: sideMenuSchemaRef.current,
|
current: sideMenuSchema,
|
||||||
});
|
});
|
||||||
dn.loadAPIClientEvents();
|
dn.loadAPIClientEvents();
|
||||||
dn.insertAdjacent('beforeEnd', s);
|
dn.insertAdjacent('beforeEnd', s);
|
||||||
@ -374,8 +383,7 @@ const SideMenu = React.memo<any>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [api, designable, getMenuItems, refresh, render, sideMenuSchema, t]);
|
||||||
}, [api, designable, getMenuItems, refresh, render, sideMenuSchema, t, refreshId]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
mode === 'mix' &&
|
mode === 'mix' &&
|
||||||
@ -497,18 +505,9 @@ export const Menu: ComposedMenu = React.memo((props) => {
|
|||||||
}, [defaultSelectedKeys]);
|
}, [defaultSelectedKeys]);
|
||||||
|
|
||||||
const ctx = useContext(SchemaComponentContext);
|
const ctx = useContext(SchemaComponentContext);
|
||||||
const refreshIdRef = useRef(0);
|
|
||||||
const ctxRefresh = ctx.refresh;
|
|
||||||
const refresh = useCallback(() => {
|
|
||||||
refreshIdRef.current += 1;
|
|
||||||
ctxRefresh?.();
|
|
||||||
}, [ctxRefresh]);
|
|
||||||
|
|
||||||
const newCtx = useMemo(() => ({ ...ctx, refresh }), [ctx, refresh]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DndContext>
|
<DndContext>
|
||||||
<SchemaComponentContext.Provider value={newCtx}>
|
|
||||||
<MenuItemDesignerContext.Provider value={Designer}>
|
<MenuItemDesignerContext.Provider value={Designer}>
|
||||||
<MenuModeContext.Provider value={mode}>
|
<MenuModeContext.Provider value={mode}>
|
||||||
<HeaderMenu
|
<HeaderMenu
|
||||||
@ -526,7 +525,6 @@ export const Menu: ComposedMenu = React.memo((props) => {
|
|||||||
selectedKeys={selectedKeys}
|
selectedKeys={selectedKeys}
|
||||||
designable={ctx.designable}
|
designable={ctx.designable}
|
||||||
render={render}
|
render={render}
|
||||||
refreshId={refreshIdRef.current}
|
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</HeaderMenu>
|
</HeaderMenu>
|
||||||
@ -542,12 +540,9 @@ export const Menu: ComposedMenu = React.memo((props) => {
|
|||||||
t={t}
|
t={t}
|
||||||
api={api}
|
api={api}
|
||||||
designable={ctx.designable}
|
designable={ctx.designable}
|
||||||
refreshId={refreshIdRef.current}
|
|
||||||
refresh={refresh}
|
|
||||||
/>
|
/>
|
||||||
</MenuModeContext.Provider>
|
</MenuModeContext.Provider>
|
||||||
</MenuItemDesignerContext.Provider>
|
</MenuItemDesignerContext.Provider>
|
||||||
</SchemaComponentContext.Provider>
|
|
||||||
</DndContext>
|
</DndContext>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -728,7 +723,7 @@ Menu.SubMenu = observer(
|
|||||||
</SchemaContext.Provider>
|
</SchemaContext.Provider>
|
||||||
),
|
),
|
||||||
children: getMenuItems(() => {
|
children: getMenuItems(() => {
|
||||||
return <RecursionField schema={schema} onlyRenderProperties />;
|
return <NocoBaseRecursionField schema={schema} onlyRenderProperties />;
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}, [field.title, icon, schema, children, Designer]);
|
}, [field.title, icon, schema, children, Designer]);
|
||||||
|
@ -35,7 +35,6 @@ import { KeepAliveProvider, useKeepAlive } from '../../../route-switch/antd/admi
|
|||||||
import { useGetAriaLabelOfSchemaInitializer } from '../../../schema-initializer/hooks/useGetAriaLabelOfSchemaInitializer';
|
import { useGetAriaLabelOfSchemaInitializer } from '../../../schema-initializer/hooks/useGetAriaLabelOfSchemaInitializer';
|
||||||
import { DndContext } from '../../common';
|
import { DndContext } from '../../common';
|
||||||
import { SortableItem } from '../../common/sortable-item';
|
import { SortableItem } from '../../common/sortable-item';
|
||||||
import { SchemaComponentContext, useNewRefreshContext } from '../../context';
|
|
||||||
import { SchemaComponent, SchemaComponentOptions } from '../../core';
|
import { SchemaComponent, SchemaComponentOptions } from '../../core';
|
||||||
import { useDesignable } from '../../hooks';
|
import { useDesignable } from '../../hooks';
|
||||||
import { useToken } from '../__builtins__';
|
import { useToken } from '../__builtins__';
|
||||||
@ -348,7 +347,6 @@ const NocoBasePageHeader = React.memo(({ activeKey, className }: { activeKey: st
|
|||||||
const { setTitle: setDocumentTitle } = useDocumentTitle();
|
const { setTitle: setDocumentTitle } = useDocumentTitle();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [pageTitle, setPageTitle] = useState(() => t(fieldSchema.title));
|
const [pageTitle, setPageTitle] = useState(() => t(fieldSchema.title));
|
||||||
const newRefreshCtx = useNewRefreshContext();
|
|
||||||
|
|
||||||
const disablePageHeader = fieldSchema['x-component-props']?.disablePageHeader;
|
const disablePageHeader = fieldSchema['x-component-props']?.disablePageHeader;
|
||||||
const enablePageTabs = fieldSchema['x-component-props']?.enablePageTabs;
|
const enablePageTabs = fieldSchema['x-component-props']?.enablePageTabs;
|
||||||
@ -376,7 +374,7 @@ const NocoBasePageHeader = React.memo(({ activeKey, className }: { activeKey: st
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SchemaComponentContext.Provider value={newRefreshCtx}>
|
<>
|
||||||
<PageDesigner title={pageTitle} />
|
<PageDesigner title={pageTitle} />
|
||||||
{!disablePageHeader && (
|
{!disablePageHeader && (
|
||||||
<AntdPageHeader
|
<AntdPageHeader
|
||||||
@ -387,7 +385,7 @@ const NocoBasePageHeader = React.memo(({ activeKey, className }: { activeKey: st
|
|||||||
footer={<NocoBasePageHeaderTabs className={className} activeKey={activeKey} />}
|
footer={<NocoBasePageHeaderTabs className={className} activeKey={activeKey} />}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</SchemaComponentContext.Provider>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ArrayField } from '@formily/core';
|
import { ArrayField } from '@formily/core';
|
||||||
import { RecursionField, useField, useFieldSchema } from '@formily/react';
|
import { useField, useFieldSchema } from '@formily/react';
|
||||||
import { toArr } from '@formily/shared';
|
import { toArr } from '@formily/shared';
|
||||||
import { Select } from 'antd';
|
import { Select } from 'antd';
|
||||||
import { differenceBy, unionBy } from 'lodash';
|
import { differenceBy, unionBy } from 'lodash';
|
||||||
@ -18,6 +18,7 @@ import {
|
|||||||
useTableSelectorProps as useTsp,
|
useTableSelectorProps as useTsp,
|
||||||
} from '../../../block-provider/TableSelectorProvider';
|
} from '../../../block-provider/TableSelectorProvider';
|
||||||
import { CollectionProvider_deprecated, useCollection_deprecated } from '../../../collection-manager';
|
import { CollectionProvider_deprecated, useCollection_deprecated } from '../../../collection-manager';
|
||||||
|
import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField';
|
||||||
import { FormProvider, SchemaComponentOptions } from '../../core';
|
import { FormProvider, SchemaComponentOptions } from '../../core';
|
||||||
import { useCompile } from '../../hooks';
|
import { useCompile } from '../../hooks';
|
||||||
import { ActionContextProvider, useActionContext } from '../action';
|
import { ActionContextProvider, useActionContext } from '../action';
|
||||||
@ -277,7 +278,7 @@ const Drawer: React.FunctionComponent<{
|
|||||||
<FormProvider>
|
<FormProvider>
|
||||||
<TableSelectorParamsProvider params={{ filter: getFilter() }}>
|
<TableSelectorParamsProvider params={{ filter: getFilter() }}>
|
||||||
<SchemaComponentOptions scope={{ useTableSelectorProps, usePickActionProps }}>
|
<SchemaComponentOptions scope={{ useTableSelectorProps, usePickActionProps }}>
|
||||||
<RecursionField
|
<NocoBaseRecursionField
|
||||||
schema={fieldSchema}
|
schema={fieldSchema}
|
||||||
onlyRenderProperties
|
onlyRenderProperties
|
||||||
filterProperties={(s) => {
|
filterProperties={(s) => {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { observer, RecursionField, useFieldSchema } from '@formily/react';
|
import { observer, useFieldSchema } from '@formily/react';
|
||||||
import { toArr } from '@formily/shared';
|
import { toArr } from '@formily/shared';
|
||||||
import React, { Fragment, useRef, useState } from 'react';
|
import React, { Fragment, useRef, useState } from 'react';
|
||||||
import { WithoutTableFieldResource } from '../../../block-provider';
|
import { WithoutTableFieldResource } from '../../../block-provider';
|
||||||
@ -16,6 +16,7 @@ import { BlockAssociationContext } from '../../../block-provider/BlockProvider';
|
|||||||
import { CollectionProvider_deprecated } from '../../../collection-manager';
|
import { CollectionProvider_deprecated } from '../../../collection-manager';
|
||||||
import { useCollectionManager } from '../../../data-source/collection/CollectionManagerProvider';
|
import { useCollectionManager } from '../../../data-source/collection/CollectionManagerProvider';
|
||||||
import { useCollection } from '../../../data-source/collection/CollectionProvider';
|
import { useCollection } from '../../../data-source/collection/CollectionProvider';
|
||||||
|
import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField';
|
||||||
import { RecordProvider, useRecord } from '../../../record-provider';
|
import { RecordProvider, useRecord } from '../../../record-provider';
|
||||||
import { FormProvider } from '../../core';
|
import { FormProvider } from '../../core';
|
||||||
import { useCompile } from '../../hooks';
|
import { useCompile } from '../../hooks';
|
||||||
@ -94,7 +95,7 @@ export const ReadPrettyRecordPicker: React.FC = observer(
|
|||||||
const renderWithoutTableFieldResourceProvider = () => (
|
const renderWithoutTableFieldResourceProvider = () => (
|
||||||
<WithoutTableFieldResource.Provider value={true}>
|
<WithoutTableFieldResource.Provider value={true}>
|
||||||
<FormProvider>
|
<FormProvider>
|
||||||
<RecursionField
|
<NocoBaseRecursionField
|
||||||
schema={fieldSchema}
|
schema={fieldSchema}
|
||||||
onlyRenderProperties
|
onlyRenderProperties
|
||||||
filterProperties={(s) => {
|
filterProperties={(s) => {
|
||||||
|
@ -13,11 +13,11 @@ import { SortableContext, SortableContextProps, useSortable } from '@dnd-kit/sor
|
|||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import { ArrayField } from '@formily/core';
|
import { ArrayField } from '@formily/core';
|
||||||
import { spliceArrayState } from '@formily/core/esm/shared/internals';
|
import { spliceArrayState } from '@formily/core/esm/shared/internals';
|
||||||
import { Schema, SchemaOptionsContext, observer, useField, useFieldSchema } from '@formily/react';
|
import { observer, Schema, SchemaOptionsContext, useField, useFieldSchema } from '@formily/react';
|
||||||
import { action } from '@formily/reactive';
|
import { action } from '@formily/reactive';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { isPortalInBody } from '@nocobase/utils/client';
|
import { isPortalInBody } from '@nocobase/utils/client';
|
||||||
import { useCreation, useDeepCompareEffect, useMemoizedFn } from 'ahooks';
|
import { useDeepCompareEffect, useMemoizedFn } from 'ahooks';
|
||||||
import { Table as AntdTable, TableColumnProps } from 'antd';
|
import { Table as AntdTable, TableColumnProps } from 'antd';
|
||||||
import { default as classNames, default as cls } from 'classnames';
|
import { default as classNames, default as cls } from 'classnames';
|
||||||
import _, { omit } from 'lodash';
|
import _, { omit } from 'lodash';
|
||||||
@ -29,11 +29,13 @@ import {
|
|||||||
BlockRequestLoadingContext,
|
BlockRequestLoadingContext,
|
||||||
RecordIndexProvider,
|
RecordIndexProvider,
|
||||||
RecordProvider,
|
RecordProvider,
|
||||||
|
useAssociationNames,
|
||||||
useCollection,
|
useCollection,
|
||||||
useCollectionParentRecordData,
|
useCollectionParentRecordData,
|
||||||
useDataBlockProps,
|
useDataBlockProps,
|
||||||
useDataBlockRequest,
|
useDataBlockRequest,
|
||||||
useDataBlockRequestData,
|
useDataBlockRequestData,
|
||||||
|
useDataBlockRequestGetter,
|
||||||
useFlag,
|
useFlag,
|
||||||
useSchemaInitializerRender,
|
useSchemaInitializerRender,
|
||||||
useTableSelectorContext,
|
useTableSelectorContext,
|
||||||
@ -41,10 +43,15 @@ import {
|
|||||||
import { useACLFieldWhitelist } from '../../../acl/ACLProvider';
|
import { useACLFieldWhitelist } from '../../../acl/ACLProvider';
|
||||||
import { useTableBlockContext } from '../../../block-provider/TableBlockProvider';
|
import { useTableBlockContext } from '../../../block-provider/TableBlockProvider';
|
||||||
import { isNewRecord } from '../../../data-source/collection-record/isNewRecord';
|
import { isNewRecord } from '../../../data-source/collection-record/isNewRecord';
|
||||||
import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField';
|
import {
|
||||||
|
NocoBaseRecursionField,
|
||||||
|
RefreshComponentProvider,
|
||||||
|
useRefreshFieldSchema,
|
||||||
|
} from '../../../formily/NocoBaseRecursionField';
|
||||||
import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps';
|
import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps';
|
||||||
import { withSkeletonComponent } from '../../../hoc/withSkeletonComponent';
|
import { withSkeletonComponent } from '../../../hoc/withSkeletonComponent';
|
||||||
import { useSatisfiedActionValues } from '../../../schema-settings/LinkageRules/useActionValues';
|
import { LinkageRuleDataKeyMap } from '../../../schema-settings/LinkageRules/type';
|
||||||
|
import { GetStyleRules } from '../../../schema-settings/LinkageRules/useActionValues';
|
||||||
import { HighPerformanceSpin } from '../../common/high-performance-spin/HighPerformanceSpin';
|
import { HighPerformanceSpin } from '../../common/high-performance-spin/HighPerformanceSpin';
|
||||||
import { useToken } from '../__builtins__';
|
import { useToken } from '../__builtins__';
|
||||||
import { useAssociationFieldContext } from '../association-field/hooks';
|
import { useAssociationFieldContext } from '../association-field/hooks';
|
||||||
@ -99,17 +106,6 @@ function adjustColumnOrder(columns) {
|
|||||||
return [...leftFixedColumns, ...normalColumns, ...rightFixedColumns];
|
return [...leftFixedColumns, ...normalColumns, ...rightFixedColumns];
|
||||||
}
|
}
|
||||||
|
|
||||||
const useColumnsDeepMemoized = (columns: any[]) => {
|
|
||||||
const columnsJSON = getSchemaArrJSON(columns);
|
|
||||||
const oldObj = useCreation(() => ({ value: _.cloneDeep(columnsJSON) }), []);
|
|
||||||
|
|
||||||
if (!_.isEqual(columnsJSON, oldObj.value)) {
|
|
||||||
oldObj.value = _.cloneDeep(columnsJSON);
|
|
||||||
}
|
|
||||||
|
|
||||||
return oldObj.value;
|
|
||||||
};
|
|
||||||
|
|
||||||
const TableCellRender: FC<{
|
const TableCellRender: FC<{
|
||||||
record: any;
|
record: any;
|
||||||
columnSchema: Schema;
|
columnSchema: Schema;
|
||||||
@ -118,7 +114,7 @@ const TableCellRender: FC<{
|
|||||||
schemaToolbarBigger: string;
|
schemaToolbarBigger: string;
|
||||||
field: ArrayField;
|
field: ArrayField;
|
||||||
index: number;
|
index: number;
|
||||||
}> = React.memo(({ record, columnSchema, uiSchema, filterProperties, schemaToolbarBigger, field, index }) => {
|
}> = ({ record, columnSchema, uiSchema, filterProperties, schemaToolbarBigger, field, index }) => {
|
||||||
const basePath = field.address.concat(record.__index || index);
|
const basePath = field.address.concat(record.__index || index);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -135,9 +131,29 @@ const TableCellRender: FC<{
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
});
|
};
|
||||||
|
|
||||||
TableCellRender.displayName = 'TableCellRender';
|
const useRefreshTableColumns = () => {
|
||||||
|
const { params: blockParams, dataSource } = useDataBlockProps() || {};
|
||||||
|
const { getDataBlockRequest } = useDataBlockRequestGetter();
|
||||||
|
const { getAssociationAppends } = useAssociationNames(dataSource);
|
||||||
|
const prevParamsRef = useRef(blockParams);
|
||||||
|
const refreshFieldSchema = useRefreshFieldSchema();
|
||||||
|
|
||||||
|
const refresh = useCallback(() => {
|
||||||
|
const { appends } = getAssociationAppends();
|
||||||
|
const service = getDataBlockRequest();
|
||||||
|
|
||||||
|
if (!_.isEqual(prevParamsRef.current.appends, appends)) {
|
||||||
|
prevParamsRef.current = { ...blockParams, appends };
|
||||||
|
service.run(prevParamsRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshFieldSchema?.();
|
||||||
|
}, [blockParams, getAssociationAppends, getDataBlockRequest, refreshFieldSchema]);
|
||||||
|
|
||||||
|
return { refresh };
|
||||||
|
};
|
||||||
|
|
||||||
const useTableColumns = (props: { showDel?: any; isSubTable?: boolean }, paginationProps) => {
|
const useTableColumns = (props: { showDel?: any; isSubTable?: boolean }, paginationProps) => {
|
||||||
const { token } = useToken();
|
const { token } = useToken();
|
||||||
@ -146,15 +162,17 @@ const useTableColumns = (props: { showDel?: any; isSubTable?: boolean }, paginat
|
|||||||
const { schemaInWhitelist } = useACLFieldWhitelist();
|
const { schemaInWhitelist } = useACLFieldWhitelist();
|
||||||
const { designable } = useDesignable();
|
const { designable } = useDesignable();
|
||||||
const { exists, render } = useSchemaInitializerRender(schema['x-initializer'], schema['x-initializer-props']);
|
const { exists, render } = useSchemaInitializerRender(schema['x-initializer'], schema['x-initializer-props']);
|
||||||
const columnsSchemas = schema.reduceProperties((buf, s) => {
|
const columnsSchemas = useMemo(() => {
|
||||||
|
return schema.reduceProperties((buf, s) => {
|
||||||
if (isColumnComponent(s) && schemaInWhitelist(Object.values(s.properties || {}).pop())) {
|
if (isColumnComponent(s) && schemaInWhitelist(Object.values(s.properties || {}).pop())) {
|
||||||
return buf.concat([s]);
|
return buf.concat([s]);
|
||||||
}
|
}
|
||||||
return buf;
|
return buf;
|
||||||
}, []);
|
}, []);
|
||||||
|
}, [schema, schemaInWhitelist]);
|
||||||
const { current, pageSize } = paginationProps;
|
const { current, pageSize } = paginationProps;
|
||||||
const hasChangedColumns = useColumnsDeepMemoized(columnsSchemas);
|
|
||||||
const { isPopupVisibleControlledByURL } = usePopupSettings();
|
const { isPopupVisibleControlledByURL } = usePopupSettings();
|
||||||
|
const { refresh } = useRefreshTableColumns();
|
||||||
|
|
||||||
const filterProperties = useCallback(
|
const filterProperties = useCallback(
|
||||||
(schema) =>
|
(schema) =>
|
||||||
@ -191,12 +209,14 @@ const useTableColumns = (props: { showDel?: any; isSubTable?: boolean }, paginat
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
title: (
|
title: (
|
||||||
|
<RefreshComponentProvider refresh={refresh}>
|
||||||
<NocoBaseRecursionField
|
<NocoBaseRecursionField
|
||||||
name={columnSchema.name}
|
name={columnSchema.name}
|
||||||
schema={columnSchema}
|
schema={columnSchema}
|
||||||
onlyRenderSelf
|
onlyRenderSelf
|
||||||
isUseFormilyField={false}
|
isUseFormilyField={false}
|
||||||
/>
|
/>
|
||||||
|
</RefreshComponentProvider>
|
||||||
),
|
),
|
||||||
dataIndex,
|
dataIndex,
|
||||||
key: columnSchema.name,
|
key: columnSchema.name,
|
||||||
@ -222,7 +242,6 @@ const useTableColumns = (props: { showDel?: any; isSubTable?: boolean }, paginat
|
|||||||
record,
|
record,
|
||||||
schema: columnSchema,
|
schema: columnSchema,
|
||||||
rowIndex,
|
rowIndex,
|
||||||
isSubTable: props.isSubTable,
|
|
||||||
columnHidden,
|
columnHidden,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -234,9 +253,7 @@ const useTableColumns = (props: { showDel?: any; isSubTable?: boolean }, paginat
|
|||||||
} as TableColumnProps<any>;
|
} as TableColumnProps<any>;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// 这里不能把 columnsSchema 作为依赖,因为其每次都会变化,这里使用 hasChangedColumns 作为依赖
|
[columnsSchemas, collection, refresh, designable, filterProperties, schemaToolbarBigger, field],
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
[hasChangedColumns, field.address, collection, schemaToolbarBigger, designable, filterProperties],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const tableColumns = useMemo(() => {
|
const tableColumns = useMemo(() => {
|
||||||
@ -246,7 +263,7 @@ const useTableColumns = (props: { showDel?: any; isSubTable?: boolean }, paginat
|
|||||||
const res = [
|
const res = [
|
||||||
...columns,
|
...columns,
|
||||||
{
|
{
|
||||||
title: render(),
|
title: <RefreshComponentProvider refresh={refresh}>{render()}</RefreshComponentProvider>,
|
||||||
dataIndex: 'TABLE_COLUMN_INITIALIZER',
|
dataIndex: 'TABLE_COLUMN_INITIALIZER',
|
||||||
key: 'TABLE_COLUMN_INITIALIZER',
|
key: 'TABLE_COLUMN_INITIALIZER',
|
||||||
render: designable
|
render: designable
|
||||||
@ -602,19 +619,27 @@ const InternalBodyCellComponent = React.memo<BodyCellComponentProps>((props) =>
|
|||||||
const inView = useContext(InViewContext);
|
const inView = useContext(InViewContext);
|
||||||
const isIndex = props.className?.includes('selection-column');
|
const isIndex = props.className?.includes('selection-column');
|
||||||
const { record, schema, rowIndex, isSubTable, ...others } = props;
|
const { record, schema, rowIndex, isSubTable, ...others } = props;
|
||||||
const { valueMap } = useSatisfiedActionValues({ formValues: record, category: 'style', schema });
|
const styleRules = schema?.[LinkageRuleDataKeyMap['style']];
|
||||||
const style = useMemo(() => Object.assign({ ...props.style }, valueMap), [props.style, valueMap]);
|
const [dynamicStyle, setDynamicStyle] = useState({});
|
||||||
const skeletonStyle = {
|
const style = useMemo(() => ({ ...props.style, ...dynamicStyle }), [props.style, dynamicStyle]);
|
||||||
|
const skeletonStyle = useMemo(
|
||||||
|
() => ({
|
||||||
height: '1em',
|
height: '1em',
|
||||||
backgroundColor: token.colorFillSecondary,
|
backgroundColor: token.colorFillSecondary,
|
||||||
borderRadius: `${token.borderRadiusSM}px`,
|
borderRadius: `${token.borderRadiusSM}px`,
|
||||||
};
|
}),
|
||||||
|
[token.borderRadiusSM, token.colorFillSecondary],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
{/* To improve rendering performance, do not render GetStyleRules component when no style rules are set */}
|
||||||
|
{!_.isEmpty(styleRules) && <GetStyleRules record={record} schema={schema} onStyleChange={setDynamicStyle} />}
|
||||||
<td {...others} className={classNames(props.className, cellClass)} style={style}>
|
<td {...others} className={classNames(props.className, cellClass)} style={style}>
|
||||||
{/* Lazy rendering cannot be used in sub-tables. */}
|
{/* Lazy rendering cannot be used in sub-tables. */}
|
||||||
{isSubTable || inView || isIndex ? props.children : <div style={skeletonStyle} />}
|
{isSubTable || inView || isIndex ? props.children : <div style={skeletonStyle} />}
|
||||||
</td>
|
</td>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ const useDragEnd = (onDragEnd) => {
|
|||||||
const dn = createDesignable({
|
const dn = createDesignable({
|
||||||
t,
|
t,
|
||||||
api,
|
api,
|
||||||
refresh,
|
refresh: ({ refreshParentSchema = true } = {}) => refresh({ refreshParentSchema }),
|
||||||
current: overSchema,
|
current: overSchema,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ const useDragEnd = (onDragEnd) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onDragEnd],
|
[api, onDragEnd, refresh, t],
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ const useSortableItemId = (props) => {
|
|||||||
if (props.id) {
|
if (props.id) {
|
||||||
return props.id;
|
return props.id;
|
||||||
}
|
}
|
||||||
return field.address.toString();
|
return field.address?.toString();
|
||||||
};
|
};
|
||||||
|
|
||||||
interface SortableItemProps extends HTMLAttributes<HTMLDivElement> {
|
interface SortableItemProps extends HTMLAttributes<HTMLDivElement> {
|
||||||
|
@ -7,39 +7,8 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useUpdate } from 'ahooks';
|
import { createContext } from 'react';
|
||||||
import { createContext, useCallback, useContext, useMemo } from 'react';
|
|
||||||
import { useRefreshFieldSchema } from '../formily/NocoBaseRecursionField';
|
|
||||||
import { ISchemaComponentContext } from './types';
|
import { ISchemaComponentContext } from './types';
|
||||||
|
|
||||||
export const SchemaComponentContext = createContext<ISchemaComponentContext>({});
|
export const SchemaComponentContext = createContext<ISchemaComponentContext>({});
|
||||||
SchemaComponentContext.displayName = 'SchemaComponentContext.Provider';
|
SchemaComponentContext.displayName = 'SchemaComponentContext.Provider';
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a new refresh context, used to refresh the block that uses this hook
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const useNewRefreshContext = (refresh?: () => void) => {
|
|
||||||
const oldCtx = useContext(SchemaComponentContext);
|
|
||||||
const newCtx = useMemo(() => ({ ...oldCtx }), [oldCtx]);
|
|
||||||
const refreshFieldSchema = useRefreshFieldSchema();
|
|
||||||
const update = useUpdate();
|
|
||||||
|
|
||||||
const _refresh = useCallback(
|
|
||||||
(options?: { refreshParent?: boolean }) => {
|
|
||||||
// refresh fieldSchema
|
|
||||||
refreshFieldSchema(options);
|
|
||||||
// refresh current component
|
|
||||||
update();
|
|
||||||
refresh?.();
|
|
||||||
},
|
|
||||||
[refreshFieldSchema, update, refresh],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (oldCtx) {
|
|
||||||
Object.assign(newCtx, oldCtx);
|
|
||||||
newCtx.refresh = _refresh;
|
|
||||||
}
|
|
||||||
|
|
||||||
return newCtx;
|
|
||||||
};
|
|
||||||
|
@ -15,16 +15,19 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { useDesignable } from '..';
|
import { useDesignable } from '..';
|
||||||
import { useToken } from '../../style';
|
import { useToken } from '../../style';
|
||||||
|
|
||||||
|
const designableStyle = {
|
||||||
|
backgroundColor: 'var(--colorSettings) !important',
|
||||||
|
};
|
||||||
|
|
||||||
|
const unDesignableStyle = {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
};
|
||||||
|
|
||||||
export const DesignableSwitch = () => {
|
export const DesignableSwitch = () => {
|
||||||
const { designable, setDesignable } = useDesignable();
|
const { designable, setDesignable } = useDesignable();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { token } = useToken();
|
const { token } = useToken();
|
||||||
const style = {};
|
const style = designable ? designableStyle : unDesignableStyle;
|
||||||
if (designable) {
|
|
||||||
style['backgroundColor'] = 'var(--colorSettings)';
|
|
||||||
} else {
|
|
||||||
style['backgroundColor'] = 'transparent';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 快捷键切换编辑状态
|
// 快捷键切换编辑状态
|
||||||
useHotkeys('Ctrl+Shift+U', () => setDesignable(!designable), [designable]);
|
useHotkeys('Ctrl+Shift+U', () => setDesignable(!designable), [designable]);
|
||||||
|
@ -8,14 +8,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { IRecursionFieldProps, ISchemaFieldProps, Schema } from '@formily/react';
|
import { IRecursionFieldProps, ISchemaFieldProps, Schema } from '@formily/react';
|
||||||
import { useUpdate } from 'ahooks';
|
import _ from 'lodash';
|
||||||
import React, { memo, useContext, useMemo } from 'react';
|
import React, { createContext, memo, useContext, useMemo } from 'react';
|
||||||
import { NocoBaseRecursionField } from '../../formily/NocoBaseRecursionField';
|
import { NocoBaseRecursionField } from '../../formily/NocoBaseRecursionField';
|
||||||
import { SchemaComponentContext } from '../context';
|
import { SchemaComponentContext } from '../context';
|
||||||
import { SchemaComponentOptions } from './SchemaComponentOptions';
|
import { SchemaComponentOptions } from './SchemaComponentOptions';
|
||||||
|
|
||||||
type SchemaComponentOnChange = {
|
type SchemaComponentOnChange = {
|
||||||
onChange?: (s: Schema) => void;
|
onChange?: (s?: Schema) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
function toSchema(schema?: any) {
|
function toSchema(schema?: any) {
|
||||||
@ -45,32 +45,49 @@ interface DistributedProps {
|
|||||||
distributed?: boolean;
|
distributed?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to pass the onChange callback function.
|
||||||
|
*
|
||||||
|
* The onChange callback will be triggered whenever a descendant Schema changes.
|
||||||
|
*/
|
||||||
|
export const SchemaComponentOnChangeContext = createContext<SchemaComponentOnChange>({ onChange: _.noop });
|
||||||
|
|
||||||
const RecursionSchemaComponent = memo((props: ISchemaFieldProps & SchemaComponentOnChange & DistributedProps) => {
|
const RecursionSchemaComponent = memo((props: ISchemaFieldProps & SchemaComponentOnChange & DistributedProps) => {
|
||||||
const { components, scope, schema: _schema, distributed, onChange, ...others } = props;
|
const { components, scope, schema: _schema, distributed, onChange: _onChange, ...others } = props;
|
||||||
const ctx = useContext(SchemaComponentContext);
|
const ctx = useContext(SchemaComponentContext);
|
||||||
const schema = useMemo(() => toSchema(_schema), [_schema]);
|
const schema = useMemo(() => toSchema(_schema), [_schema]);
|
||||||
const refresh = useUpdate();
|
|
||||||
const value = useMemo(
|
const value = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
...ctx,
|
...ctx,
|
||||||
distributed: ctx.distributed == false ? false : distributed,
|
distributed: ctx.distributed == false ? false : distributed,
|
||||||
refresh: () => {
|
/**
|
||||||
refresh();
|
* @deprecated
|
||||||
if (ctx.distributed === false || distributed === false) {
|
*/
|
||||||
ctx.refresh?.();
|
refresh: _.noop,
|
||||||
}
|
}),
|
||||||
onChange?.(schema);
|
[ctx, distributed],
|
||||||
|
);
|
||||||
|
|
||||||
|
const { onChange: onChangeFromContext } = useContext(SchemaComponentOnChangeContext);
|
||||||
|
|
||||||
|
const onChangeValue = useMemo(
|
||||||
|
() => ({
|
||||||
|
onChange: () => {
|
||||||
|
_onChange?.(schema);
|
||||||
|
onChangeFromContext?.();
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
[ctx, distributed, onChange, refresh, schema],
|
[_onChange, onChangeFromContext, schema],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<SchemaComponentOnChangeContext.Provider value={onChangeValue}>
|
||||||
<SchemaComponentContext.Provider value={value}>
|
<SchemaComponentContext.Provider value={value}>
|
||||||
<SchemaComponentOptions inherit components={components} scope={scope}>
|
<SchemaComponentOptions inherit components={components} scope={scope}>
|
||||||
<NocoBaseRecursionField {...others} schema={schema} isUseFormilyField />
|
<NocoBaseRecursionField {...others} schema={schema} isUseFormilyField />
|
||||||
</SchemaComponentOptions>
|
</SchemaComponentOptions>
|
||||||
</SchemaComponentContext.Provider>
|
</SchemaComponentContext.Provider>
|
||||||
|
</SchemaComponentOnChangeContext.Provider>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
import { createForm } from '@formily/core';
|
import { createForm } from '@formily/core';
|
||||||
import { FormProvider, Schema } from '@formily/react';
|
import { FormProvider, Schema } from '@formily/react';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { useUpdate } from 'ahooks';
|
import _ from 'lodash';
|
||||||
import React, { useCallback, useContext, useMemo, useState } from 'react';
|
import React, { useCallback, useContext, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { SchemaComponentContext } from '../context';
|
import { SchemaComponentContext } from '../context';
|
||||||
@ -56,7 +56,6 @@ export const SchemaComponentProvider: React.FC<ISchemaComponentProvider> = (prop
|
|||||||
const { designable, onDesignableChange, components, children } = props;
|
const { designable, onDesignableChange, components, children } = props;
|
||||||
const ctx = useContext(SchemaComponentContext);
|
const ctx = useContext(SchemaComponentContext);
|
||||||
const ctxOptions = useSchemaOptionsContext();
|
const ctxOptions = useSchemaOptionsContext();
|
||||||
const refresh = useUpdate();
|
|
||||||
const [formId, setFormId] = useState(() => uid());
|
const [formId, setFormId] = useState(() => uid());
|
||||||
const form = useMemo(() => props.form || createForm(), [formId]);
|
const form = useMemo(() => props.form || createForm(), [formId]);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -89,11 +88,14 @@ export const SchemaComponentProvider: React.FC<ISchemaComponentProvider> = (prop
|
|||||||
scope,
|
scope,
|
||||||
components,
|
components,
|
||||||
reset,
|
reset,
|
||||||
refresh,
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
refresh: _.noop,
|
||||||
designable: designableValue,
|
designable: designableValue,
|
||||||
setDesignable,
|
setDesignable,
|
||||||
}),
|
}),
|
||||||
[components, designableValue, refresh, reset, scope, setDesignable],
|
[components, designableValue, reset, scope, setDesignable],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
import { GeneralField, Query } from '@formily/core';
|
import { GeneralField, Query } from '@formily/core';
|
||||||
import { ISchema, Schema, SchemaOptionsContext, useField, useFieldSchema } from '@formily/react';
|
import { ISchema, Schema, SchemaOptionsContext, useField, useFieldSchema } from '@formily/react';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
|
import { useUpdate } from 'ahooks';
|
||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
import cloneDeep from 'lodash/cloneDeep';
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
@ -17,6 +18,7 @@ import set from 'lodash/set';
|
|||||||
import React, { ComponentType, useCallback, useContext, useEffect, useMemo } from 'react';
|
import React, { ComponentType, useCallback, useContext, useEffect, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { APIClient, useAPIClient } from '../../api-client';
|
import { APIClient, useAPIClient } from '../../api-client';
|
||||||
|
import { useRefreshComponent, useRefreshFieldSchema } from '../../formily/NocoBaseRecursionField';
|
||||||
import { LAZY_COMPONENT_KEY } from '../../lazy-helper';
|
import { LAZY_COMPONENT_KEY } from '../../lazy-helper';
|
||||||
import { SchemaComponentContext } from '../context';
|
import { SchemaComponentContext } from '../context';
|
||||||
import { addAppVersion } from './addAppVersion';
|
import { addAppVersion } from './addAppVersion';
|
||||||
@ -29,7 +31,7 @@ interface CreateDesignableProps {
|
|||||||
model?: GeneralField;
|
model?: GeneralField;
|
||||||
query?: Query;
|
query?: Query;
|
||||||
api?: APIClient;
|
api?: APIClient;
|
||||||
refresh?: (options?: { refreshParent?: boolean }) => void;
|
refresh?: (options?: { refreshParentSchema?: boolean }) => void;
|
||||||
onSuccess?: any;
|
onSuccess?: any;
|
||||||
t?: any;
|
t?: any;
|
||||||
/**
|
/**
|
||||||
@ -219,7 +221,6 @@ export class Designable {
|
|||||||
message.success(t('Saved successfully'), 0.2);
|
message.success(t('Saved successfully'), 0.2);
|
||||||
});
|
});
|
||||||
this.on('initializeActionContext', async ({ schema }) => {
|
this.on('initializeActionContext', async ({ schema }) => {
|
||||||
this.refresh();
|
|
||||||
if (!schema?.['x-uid']) {
|
if (!schema?.['x-uid']) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -316,7 +317,7 @@ export class Designable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh(options?: { refreshParent?: boolean }) {
|
refresh(options?: { refreshParentSchema?: boolean }) {
|
||||||
const { refresh } = this.options;
|
const { refresh } = this.options;
|
||||||
return refresh?.(options);
|
return refresh?.(options);
|
||||||
}
|
}
|
||||||
@ -743,7 +744,7 @@ export function useFindComponent() {
|
|||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
export function useDesignable() {
|
export function useDesignable() {
|
||||||
const { designable, setDesignable, refresh, reset } = useContext(SchemaComponentContext);
|
const { designable, setDesignable, refresh: refreshFromContext, reset } = useContext(SchemaComponentContext);
|
||||||
const schemaOptions = useContext(SchemaOptionsContext);
|
const schemaOptions = useContext(SchemaOptionsContext);
|
||||||
const components = useMemo(() => schemaOptions?.components || {}, [schemaOptions]);
|
const components = useMemo(() => schemaOptions?.components || {}, [schemaOptions]);
|
||||||
const DesignableBar = useMemo(
|
const DesignableBar = useMemo(
|
||||||
@ -752,6 +753,21 @@ export function useDesignable() {
|
|||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
const update = useUpdate();
|
||||||
|
const refreshFieldSchema = useRefreshFieldSchema();
|
||||||
|
const refreshComponent = useRefreshComponent();
|
||||||
|
const refresh = useCallback(
|
||||||
|
(options?: { refreshParentSchema?: boolean }) => {
|
||||||
|
refreshFromContext?.();
|
||||||
|
// refresh current component
|
||||||
|
update();
|
||||||
|
// refresh fieldSchema context value
|
||||||
|
refreshFieldSchema?.(options);
|
||||||
|
// refresh component context value
|
||||||
|
refreshComponent?.();
|
||||||
|
},
|
||||||
|
[refreshFromContext, update, refreshFieldSchema, refreshComponent],
|
||||||
|
);
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
|
@ -14,7 +14,7 @@ import React from 'react';
|
|||||||
export interface ISchemaComponentContext {
|
export interface ISchemaComponentContext {
|
||||||
scope?: any;
|
scope?: any;
|
||||||
components?: SchemaReactComponents;
|
components?: SchemaReactComponents;
|
||||||
refresh?: (options?: { refreshParent?: boolean }) => void;
|
refresh?: (options?: { refreshParentSchema?: boolean }) => void;
|
||||||
reset?: () => void;
|
reset?: () => void;
|
||||||
designable?: boolean;
|
designable?: boolean;
|
||||||
setDesignable?: (value: boolean) => void;
|
setDesignable?: (value: boolean) => void;
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
import { merge } from '@formily/shared';
|
import { merge } from '@formily/shared';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import { useUpdate } from 'ahooks';
|
||||||
import { SchemaInitializerSwitch, useSchemaInitializer } from '../../application';
|
import { SchemaInitializerSwitch, useSchemaInitializer } from '../../application';
|
||||||
import { useCurrentSchema } from '../utils';
|
import { useCurrentSchema } from '../utils';
|
||||||
|
|
||||||
@ -23,6 +24,7 @@ export const InitializerWithSwitch = (props) => {
|
|||||||
schema?.name || item?.schema?.name,
|
schema?.name || item?.schema?.name,
|
||||||
);
|
);
|
||||||
const { insert } = useSchemaInitializer();
|
const { insert } = useSchemaInitializer();
|
||||||
|
const update = useUpdate();
|
||||||
return (
|
return (
|
||||||
<SchemaInitializerSwitch
|
<SchemaInitializerSwitch
|
||||||
checked={exists}
|
checked={exists}
|
||||||
@ -33,11 +35,13 @@ export const InitializerWithSwitch = (props) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (exists) {
|
if (exists) {
|
||||||
return remove();
|
remove();
|
||||||
|
return update();
|
||||||
}
|
}
|
||||||
const s = merge(schema || {}, item.schema || {});
|
const s = merge(schema || {}, item.schema || {});
|
||||||
item?.schemaInitialize?.(s);
|
item?.schemaInitialize?.(s);
|
||||||
insert(s);
|
insert(s);
|
||||||
|
update();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -7,14 +7,15 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
import { Form, onFormValuesChange } from '@formily/core';
|
import { Form, onFormValuesChange } from '@formily/core';
|
||||||
import { useVariables, useLocalVariables } from '../../variables';
|
import { Schema, useFieldSchema } from '@formily/react';
|
||||||
import { useFieldSchema } from '@formily/react';
|
import { uid } from '@formily/shared';
|
||||||
import { LinkageRuleCategory, LinkageRuleDataKeyMap } from './type';
|
|
||||||
import { getSatisfiedValueMap } from './compute-rules';
|
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { useLocalVariables, useVariables } from '../../variables';
|
||||||
|
import { getSatisfiedValueMap } from './compute-rules';
|
||||||
|
import { LinkageRuleCategory, LinkageRuleDataKeyMap } from './type';
|
||||||
|
|
||||||
export function useSatisfiedActionValues({
|
export function useSatisfiedActionValues({
|
||||||
formValues,
|
formValues,
|
||||||
category = 'default',
|
category = 'default',
|
||||||
@ -33,10 +34,11 @@ export function useSatisfiedActionValues({
|
|||||||
const variables = useVariables();
|
const variables = useVariables();
|
||||||
const localVariables = useLocalVariables({ currentForm: { values: formValues } as any });
|
const localVariables = useLocalVariables({ currentForm: { values: formValues } as any });
|
||||||
const localSchema = schema ?? fieldSchema;
|
const localSchema = schema ?? fieldSchema;
|
||||||
const linkageRules = rules ?? localSchema[LinkageRuleDataKeyMap[category]];
|
const styleRules = rules ?? localSchema[LinkageRuleDataKeyMap[category]];
|
||||||
|
|
||||||
const compute = useCallback(() => {
|
const compute = useCallback(() => {
|
||||||
if (linkageRules && formValues) {
|
if (styleRules && formValues) {
|
||||||
getSatisfiedValueMap({ rules: linkageRules, variables, localVariables })
|
getSatisfiedValueMap({ rules: styleRules, variables, localVariables })
|
||||||
.then((valueMap) => {
|
.then((valueMap) => {
|
||||||
if (!isEmpty(valueMap)) {
|
if (!isEmpty(valueMap)) {
|
||||||
setValueMap(valueMap);
|
setValueMap(valueMap);
|
||||||
@ -46,11 +48,11 @@ export function useSatisfiedActionValues({
|
|||||||
throw new Error(err.message);
|
throw new Error(err.message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [variables, localVariables, linkageRules, formValues]);
|
}, [variables, localVariables, styleRules, formValues]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
compute();
|
compute();
|
||||||
}, [compute]);
|
|
||||||
useEffect(() => {
|
|
||||||
if (form) {
|
if (form) {
|
||||||
const id = uid();
|
const id = uid();
|
||||||
form.addEffects(id, () => {
|
form.addEffects(id, () => {
|
||||||
@ -63,5 +65,22 @@ export function useSatisfiedActionValues({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [form, compute]);
|
}, [form, compute]);
|
||||||
|
|
||||||
return { valueMap };
|
return { valueMap };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const GetStyleRules: React.FC<{
|
||||||
|
record: Record<string, any>;
|
||||||
|
schema: Schema;
|
||||||
|
onStyleChange?: (value: Record<string, any>) => void;
|
||||||
|
}> = React.memo(({ record, schema, onStyleChange }) => {
|
||||||
|
const { valueMap } = useSatisfiedActionValues({ formValues: record, category: 'style', schema });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onStyleChange(valueMap);
|
||||||
|
}, [onStyleChange, valueMap]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
GetStyleRules.displayName = 'GetStyleRules';
|
||||||
|
@ -96,7 +96,7 @@ import { useRecord } from '../record-provider';
|
|||||||
import { ActionContextProvider } from '../schema-component/antd/action/context';
|
import { ActionContextProvider } from '../schema-component/antd/action/context';
|
||||||
import { SubFormProvider, useSubFormValue } from '../schema-component/antd/association-field/hooks';
|
import { SubFormProvider, useSubFormValue } from '../schema-component/antd/association-field/hooks';
|
||||||
import { FormDialog } from '../schema-component/antd/form-dialog';
|
import { FormDialog } from '../schema-component/antd/form-dialog';
|
||||||
import { SchemaComponentContext, useNewRefreshContext } from '../schema-component/context';
|
import { SchemaComponentContext } from '../schema-component/context';
|
||||||
import { FormProvider } from '../schema-component/core/FormProvider';
|
import { FormProvider } from '../schema-component/core/FormProvider';
|
||||||
import { RemoteSchemaComponent } from '../schema-component/core/RemoteSchemaComponent';
|
import { RemoteSchemaComponent } from '../schema-component/core/RemoteSchemaComponent';
|
||||||
import { SchemaComponent } from '../schema-component/core/SchemaComponent';
|
import { SchemaComponent } from '../schema-component/core/SchemaComponent';
|
||||||
@ -174,14 +174,6 @@ export const SchemaSettingsDropdown: React.FC<SchemaSettingsProps> = React.memo(
|
|||||||
// 单测中需要在首次就把菜单渲染出来,否则不会触发菜单的渲染进而报错。原因未知。
|
// 单测中需要在首次就把菜单渲染出来,否则不会触发菜单的渲染进而报错。原因未知。
|
||||||
const [openDropdown, setOpenDropdown] = useState(process.env.__TEST__ ? true : false);
|
const [openDropdown, setOpenDropdown] = useState(process.env.__TEST__ ? true : false);
|
||||||
const toolbarVisible = useContext(SchemaToolbarVisibleContext);
|
const toolbarVisible = useContext(SchemaToolbarVisibleContext);
|
||||||
const refreshCtx = useContext(SchemaComponentContext);
|
|
||||||
const newRefreshCtx = useNewRefreshContext(refreshCtx.refresh);
|
|
||||||
|
|
||||||
const newDn: any = useMemo(() => {
|
|
||||||
const result = Object.create(dn);
|
|
||||||
result.refresh = newRefreshCtx.refresh;
|
|
||||||
return result;
|
|
||||||
}, [dn, newRefreshCtx.refresh]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (toolbarVisible) {
|
if (toolbarVisible) {
|
||||||
@ -214,8 +206,7 @@ export const SchemaSettingsDropdown: React.FC<SchemaSettingsProps> = React.memo(
|
|||||||
const items = getMenuItems(() => props.children);
|
const items = getMenuItems(() => props.children);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SchemaComponentContext.Provider value={newRefreshCtx}>
|
<SchemaSettingsProvider visible={visible} setVisible={setVisible} dn={dn} {...others}>
|
||||||
<SchemaSettingsProvider visible={visible} setVisible={setVisible} dn={newDn} {...others}>
|
|
||||||
<Component />
|
<Component />
|
||||||
<Dropdown
|
<Dropdown
|
||||||
open={visible}
|
open={visible}
|
||||||
@ -237,7 +228,6 @@ export const SchemaSettingsDropdown: React.FC<SchemaSettingsProps> = React.memo(
|
|||||||
<div data-testid={props['data-testid']}>{typeof title === 'string' ? <span>{title}</span> : title}</div>
|
<div data-testid={props['data-testid']}>{typeof title === 'string' ? <span>{title}</span> : title}</div>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</SchemaSettingsProvider>
|
</SchemaSettingsProvider>
|
||||||
</SchemaComponentContext.Provider>
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -300,7 +290,6 @@ export const SchemaSettingsFormItemTemplate = function FormItemTemplate(props) {
|
|||||||
const sdn = createDesignable({
|
const sdn = createDesignable({
|
||||||
t,
|
t,
|
||||||
api,
|
api,
|
||||||
refresh: dn.refresh.bind(dn),
|
|
||||||
current: templateSchema.parent,
|
current: templateSchema.parent,
|
||||||
});
|
});
|
||||||
sdn.loadAPIClientEvents();
|
sdn.loadAPIClientEvents();
|
||||||
@ -371,7 +360,6 @@ export const SchemaSettingsFormItemTemplate = function FormItemTemplate(props) {
|
|||||||
const sdn = createDesignable({
|
const sdn = createDesignable({
|
||||||
t,
|
t,
|
||||||
api,
|
api,
|
||||||
refresh: dn.refresh.bind(dn),
|
|
||||||
current: gridSchema.parent,
|
current: gridSchema.parent,
|
||||||
});
|
});
|
||||||
sdn.loadAPIClientEvents();
|
sdn.loadAPIClientEvents();
|
||||||
@ -400,6 +388,7 @@ export const SchemaSettingsFormItemTemplate = function FormItemTemplate(props) {
|
|||||||
'x-template-key': key,
|
'x-template-key': key,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
dn.refresh();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('Save as block template')}
|
{t('Save as block template')}
|
||||||
@ -537,6 +526,7 @@ export const SchemaSettingsRemove: FC<SchemaSettingsRemoveProps> = (props) => {
|
|||||||
}
|
}
|
||||||
await confirm?.onOk?.();
|
await confirm?.onOk?.();
|
||||||
delete form.values[fieldSchema.name];
|
delete form.values[fieldSchema.name];
|
||||||
|
dn.refresh({ refreshParentSchema: true });
|
||||||
removeActiveFieldName?.(fieldSchema.name as string);
|
removeActiveFieldName?.(fieldSchema.name as string);
|
||||||
form?.query(new RegExp(`${fieldSchema.parent.name}.${fieldSchema.name}$`)).forEach((field: Field) => {
|
form?.query(new RegExp(`${fieldSchema.parent.name}.${fieldSchema.name}$`)).forEach((field: Field) => {
|
||||||
// 如果字段被删掉,那么在提交的时候不应该提交这个字段
|
// 如果字段被删掉,那么在提交的时候不应该提交这个字段
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
import { PageHeader as AntdPageHeader } from '@ant-design/pro-layout';
|
import { PageHeader as AntdPageHeader } from '@ant-design/pro-layout';
|
||||||
import { Input, Spin } from 'antd';
|
import { Input, Spin } from 'antd';
|
||||||
import React, { useContext, useState } from 'react';
|
import React, { useContext, useMemo, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { useAPIClient, useRequest, useSchemaTemplateManager } from '..';
|
import { useAPIClient, useRequest, useSchemaTemplateManager } from '..';
|
||||||
import { useNavigateNoUpdate } from '../application/CustomRouterContextProvider';
|
import { useNavigateNoUpdate } from '../application/CustomRouterContextProvider';
|
||||||
@ -73,6 +73,8 @@ export const BlockTemplateDetails = () => {
|
|||||||
const params = useParams<any>();
|
const params = useParams<any>();
|
||||||
const key = params?.key;
|
const key = params?.key;
|
||||||
const value = useContext(SchemaComponentContext);
|
const value = useContext(SchemaComponentContext);
|
||||||
|
const schemaComponentContext = useMemo(() => ({ ...value, designable: true }), [value]);
|
||||||
|
|
||||||
const { data, loading } = useRequest<{
|
const { data, loading } = useRequest<{
|
||||||
data: any;
|
data: any;
|
||||||
}>({
|
}>({
|
||||||
@ -82,9 +84,11 @@ export const BlockTemplateDetails = () => {
|
|||||||
filterByTk: key,
|
filterByTk: key,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <Spin />;
|
return <Spin />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<AntdPageHeader
|
<AntdPageHeader
|
||||||
@ -96,7 +100,7 @@ export const BlockTemplateDetails = () => {
|
|||||||
title={<EditableTitle filterByTk={key} title={data?.data?.name} />}
|
title={<EditableTitle filterByTk={key} title={data?.data?.name} />}
|
||||||
/>
|
/>
|
||||||
<div style={{ margin: 'var(--nb-spacing)' }}>
|
<div style={{ margin: 'var(--nb-spacing)' }}>
|
||||||
<SchemaComponentContext.Provider value={{ ...value, designable: true }}>
|
<SchemaComponentContext.Provider value={schemaComponentContext}>
|
||||||
<RemoteSchemaComponent uid={data?.data?.uid} />
|
<RemoteSchemaComponent uid={data?.data?.uid} />
|
||||||
</SchemaComponentContext.Provider>
|
</SchemaComponentContext.Provider>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,23 +7,22 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState } from 'react';
|
import { ISchema, Schema } from '@formily/react';
|
||||||
import { Card, Row, Col, Tabs, Divider } from 'antd';
|
|
||||||
import {
|
import {
|
||||||
CollectionProvider,
|
|
||||||
CollectionProvider_deprecated,
|
CollectionProvider_deprecated,
|
||||||
ResourceActionProvider,
|
ResourceActionProvider,
|
||||||
SchemaComponentContext,
|
SchemaComponentContext,
|
||||||
usePlugin,
|
usePlugin,
|
||||||
useSchemaComponentContext,
|
useSchemaComponentContext,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { ISchema, Schema } from '@formily/react';
|
import { Card, Col, Divider, Row, Tabs } from 'antd';
|
||||||
|
import React, { useMemo, useState } from 'react';
|
||||||
|
import ACLPlugin from '.';
|
||||||
|
import { NewRole } from './NewRole';
|
||||||
|
import { RolesManagerContext } from './RolesManagerProvider';
|
||||||
import { RolesMenu } from './RolesMenu';
|
import { RolesMenu } from './RolesMenu';
|
||||||
import { useACLTranslation } from './locale';
|
import { useACLTranslation } from './locale';
|
||||||
import ACLPlugin from '.';
|
|
||||||
import { RolesManagerContext } from './RolesManagerProvider';
|
|
||||||
import { Permissions } from './permissions/Permissions';
|
import { Permissions } from './permissions/Permissions';
|
||||||
import { NewRole } from './NewRole';
|
|
||||||
|
|
||||||
const collection = {
|
const collection = {
|
||||||
name: 'roles',
|
name: 'roles',
|
||||||
@ -75,9 +74,10 @@ export const RolesManagement: React.FC = () => {
|
|||||||
}));
|
}));
|
||||||
const [role, setRole] = useState(null);
|
const [role, setRole] = useState(null);
|
||||||
const scCtx = useSchemaComponentContext();
|
const scCtx = useSchemaComponentContext();
|
||||||
|
const schemaComponentContext = useMemo(() => ({ ...scCtx, designable: false }), [scCtx]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SchemaComponentContext.Provider value={{ ...scCtx, designable: false }}>
|
<SchemaComponentContext.Provider value={schemaComponentContext}>
|
||||||
<RolesManagerContext.Provider value={{ role, setRole }}>
|
<RolesManagerContext.Provider value={{ role, setRole }}>
|
||||||
<Card>
|
<Card>
|
||||||
<Row gutter={24} style={{ flexWrap: 'nowrap' }}>
|
<Row gutter={24} style={{ flexWrap: 'nowrap' }}>
|
||||||
|
@ -8,11 +8,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import { RecursionField, observer, useField, useFieldSchema } from '@formily/react';
|
import { observer, useField, useFieldSchema } from '@formily/react';
|
||||||
import {
|
import {
|
||||||
ActionContextProvider,
|
ActionContextProvider,
|
||||||
CollectionProvider_deprecated,
|
CollectionProvider_deprecated,
|
||||||
FormBlockContext,
|
FormBlockContext,
|
||||||
|
NocoBaseRecursionField,
|
||||||
PopupSettingsProvider,
|
PopupSettingsProvider,
|
||||||
RecordProvider,
|
RecordProvider,
|
||||||
TabsContextProvider,
|
TabsContextProvider,
|
||||||
@ -208,7 +209,7 @@ export const DuplicateAction = observer(
|
|||||||
<RecordProvider record={{ ...parentRecordData, __collection: duplicateCollection || __collection }}>
|
<RecordProvider record={{ ...parentRecordData, __collection: duplicateCollection || __collection }}>
|
||||||
<ActionContextProvider value={{ ...ctx, visible, setVisible }}>
|
<ActionContextProvider value={{ ...ctx, visible, setVisible }}>
|
||||||
<PopupSettingsProvider enableURL={false}>
|
<PopupSettingsProvider enableURL={false}>
|
||||||
<RecursionField schema={fieldSchema} basePath={field.address} onlyRenderProperties />
|
<NocoBaseRecursionField schema={fieldSchema} basePath={field.address} onlyRenderProperties />
|
||||||
</PopupSettingsProvider>
|
</PopupSettingsProvider>
|
||||||
</ActionContextProvider>
|
</ActionContextProvider>
|
||||||
</RecordProvider>
|
</RecordProvider>
|
||||||
|
@ -124,7 +124,7 @@ test.describe('direct duplicate & copy into the form and continue to fill in', (
|
|||||||
.getByTestId('drawer-Action.Container-general2-Duplicate')
|
.getByTestId('drawer-Action.Container-general2-Duplicate')
|
||||||
.getByLabel('schema-initializer-Grid-popup')
|
.getByLabel('schema-initializer-Grid-popup')
|
||||||
.hover();
|
.hover();
|
||||||
await page.getByRole('menuitem', { name: 'form Form right' }).hover();
|
await page.getByRole('menuitem', { name: 'Form right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Current collection' }).click();
|
await page.getByRole('menuitem', { name: 'Current collection' }).click();
|
||||||
await page
|
await page
|
||||||
.getByTestId('drawer-Action.Container-general2-Duplicate')
|
.getByTestId('drawer-Action.Container-general2-Duplicate')
|
||||||
|
@ -7,20 +7,21 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { RecursionField, observer, useFieldSchema } from '@formily/react';
|
import { css } from '@emotion/css';
|
||||||
|
import { observer, useFieldSchema } from '@formily/react';
|
||||||
import {
|
import {
|
||||||
CollectionContext,
|
CollectionContext,
|
||||||
DataSourceContext,
|
DataSourceContext,
|
||||||
DndContext,
|
DndContext,
|
||||||
|
Icon,
|
||||||
|
NocoBaseRecursionField,
|
||||||
|
useBlockHeight,
|
||||||
useDesignable,
|
useDesignable,
|
||||||
useSchemaInitializerRender,
|
useSchemaInitializerRender,
|
||||||
withDynamicSchemaProps,
|
withDynamicSchemaProps,
|
||||||
Icon,
|
|
||||||
useBlockHeight,
|
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { css } from '@emotion/css';
|
import { Avatar, List, Space, theme } from 'antd';
|
||||||
import { Space, List, Avatar, theme } from 'antd';
|
import React, { createContext, useEffect, useState } from 'react';
|
||||||
import React, { createContext, useState, useEffect } from 'react';
|
|
||||||
import { WorkbenchLayout } from './workbenchBlockSettings';
|
import { WorkbenchLayout } from './workbenchBlockSettings';
|
||||||
|
|
||||||
const ConfigureActionsButton = observer(
|
const ConfigureActionsButton = observer(
|
||||||
@ -72,7 +73,7 @@ const InternalIcons = () => {
|
|||||||
{layout === WorkbenchLayout.Grid ? (
|
{layout === WorkbenchLayout.Grid ? (
|
||||||
<Space wrap size={gap}>
|
<Space wrap size={gap}>
|
||||||
{fieldSchema.mapProperties((s, key) => (
|
{fieldSchema.mapProperties((s, key) => (
|
||||||
<RecursionField name={key} schema={s} key={key} />
|
<NocoBaseRecursionField name={key} schema={s} key={key} />
|
||||||
))}
|
))}
|
||||||
</Space>
|
</Space>
|
||||||
) : (
|
) : (
|
||||||
@ -103,7 +104,7 @@ const InternalIcons = () => {
|
|||||||
>
|
>
|
||||||
<List.Item.Meta
|
<List.Item.Meta
|
||||||
avatar={<Avatar style={{ backgroundColor }} icon={<Icon type={icon} />} />}
|
avatar={<Avatar style={{ backgroundColor }} icon={<Icon type={icon} />} />}
|
||||||
title={<RecursionField name={key} schema={s} key={key} />}
|
title={<NocoBaseRecursionField name={key} schema={s} key={key} />}
|
||||||
></List.Item.Meta>
|
></List.Item.Meta>
|
||||||
</List.Item>
|
</List.Item>
|
||||||
);
|
);
|
||||||
|
@ -8,44 +8,44 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { LeftOutlined, RightOutlined } from '@ant-design/icons';
|
import { LeftOutlined, RightOutlined } from '@ant-design/icons';
|
||||||
import { RecursionField, Schema, observer, useFieldSchema, useField } from '@formily/react';
|
import { Schema, useField, useFieldSchema } from '@formily/react';
|
||||||
import {
|
import {
|
||||||
|
ActionContextProvider,
|
||||||
|
CollectionProvider,
|
||||||
|
NocoBaseRecursionField,
|
||||||
PopupContextProvider,
|
PopupContextProvider,
|
||||||
RecordProvider,
|
RecordProvider,
|
||||||
|
SchemaComponentOptions,
|
||||||
getLabelFormatValue,
|
getLabelFormatValue,
|
||||||
|
handleDateChangeOnForm,
|
||||||
useACLRoleContext,
|
useACLRoleContext,
|
||||||
|
useActionContext,
|
||||||
useCollection,
|
useCollection,
|
||||||
useCollectionParentRecordData,
|
useCollectionParentRecordData,
|
||||||
|
useDesignable,
|
||||||
|
useFormBlockContext,
|
||||||
useLazy,
|
useLazy,
|
||||||
usePopupUtils,
|
usePopupUtils,
|
||||||
useProps,
|
useProps,
|
||||||
useToken,
|
useToken,
|
||||||
withDynamicSchemaProps,
|
withDynamicSchemaProps,
|
||||||
useDesignable,
|
|
||||||
ActionContextProvider,
|
|
||||||
useActionContext,
|
|
||||||
CollectionProvider,
|
|
||||||
SchemaComponentOptions,
|
|
||||||
useFormBlockContext,
|
|
||||||
handleDateChangeOnForm,
|
|
||||||
withSkeletonComponent,
|
withSkeletonComponent,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import type { Dayjs } from 'dayjs';
|
import type { Dayjs } from 'dayjs';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { get, cloneDeep } from 'lodash';
|
import { cloneDeep, get } from 'lodash';
|
||||||
import React, { useMemo, useState, useEffect, useCallback } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { Calendar as BigCalendar, View, dayjsLocalizer } from 'react-big-calendar';
|
import { View } from 'react-big-calendar';
|
||||||
import * as dates from 'react-big-calendar/lib/utils/dates';
|
|
||||||
import { i18nt, useTranslation } from '../../locale';
|
import { i18nt, useTranslation } from '../../locale';
|
||||||
import { CalendarRecordViewer, findEventSchema } from './CalendarRecordViewer';
|
import { CalendarRecordViewer, findEventSchema } from './CalendarRecordViewer';
|
||||||
import Header from './components/Header';
|
import Header from './components/Header';
|
||||||
import { CalendarToolbarContext } from './context';
|
import { CalendarToolbarContext } from './context';
|
||||||
import GlobalStyle from './global.style';
|
import GlobalStyle from './global.style';
|
||||||
import { useCalenderHeight } from './hook';
|
import { useCalenderHeight } from './hook';
|
||||||
|
import { addNew } from './schema';
|
||||||
import useStyle from './style';
|
import useStyle from './style';
|
||||||
import type { ToolbarProps } from './types';
|
import type { ToolbarProps } from './types';
|
||||||
import { formatDate } from './utils';
|
import { formatDate } from './utils';
|
||||||
import { addNew } from './schema';
|
|
||||||
interface Event {
|
interface Event {
|
||||||
id: string;
|
id: string;
|
||||||
colorFieldValue: string;
|
colorFieldValue: string;
|
||||||
@ -93,7 +93,7 @@ function Toolbar(props: ToolbarProps) {
|
|||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<CalendarToolbarContext.Provider value={props}>
|
<CalendarToolbarContext.Provider value={props}>
|
||||||
<RecursionField name={toolBarSchema.name} schema={toolBarSchema} />
|
<NocoBaseRecursionField name={toolBarSchema.name} schema={toolBarSchema} />
|
||||||
</CalendarToolbarContext.Provider>
|
</CalendarToolbarContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -464,7 +464,7 @@ export const Calendar: any = withDynamicSchemaProps(
|
|||||||
<ActionContextProvider value={{ ...ctx, visible: visibleAddNewer, setVisible: setVisibleAddNewer }}>
|
<ActionContextProvider value={{ ...ctx, visible: visibleAddNewer, setVisible: setVisibleAddNewer }}>
|
||||||
<CollectionProvider name={collection.name}>
|
<CollectionProvider name={collection.name}>
|
||||||
<SchemaComponentOptions scope={{ useCreateFormBlockProps }}>
|
<SchemaComponentOptions scope={{ useCreateFormBlockProps }}>
|
||||||
<RecursionField
|
<NocoBaseRecursionField
|
||||||
onlyRenderProperties
|
onlyRenderProperties
|
||||||
basePath={field?.address}
|
basePath={field?.address}
|
||||||
schema={fieldSchema}
|
schema={fieldSchema}
|
||||||
|
@ -7,7 +7,8 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { RecursionField, Schema, useFieldSchema } from '@formily/react';
|
import { Schema, useFieldSchema } from '@formily/react';
|
||||||
|
import { NocoBaseRecursionField } from '@nocobase/client';
|
||||||
import React, { FC, useMemo } from 'react';
|
import React, { FC, useMemo } from 'react';
|
||||||
|
|
||||||
export const CalendarRecordViewer: FC = (props) => {
|
export const CalendarRecordViewer: FC = (props) => {
|
||||||
@ -18,7 +19,7 @@ export const CalendarRecordViewer: FC = (props) => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <RecursionField schema={eventSchema} name={eventSchema.name} />;
|
return <NocoBaseRecursionField schema={eventSchema} name={eventSchema.name} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function findEventSchema(schema: Schema) {
|
export function findEventSchema(schema: Schema) {
|
||||||
|
@ -9,11 +9,12 @@
|
|||||||
|
|
||||||
import { FormLayout } from '@formily/antd-v5';
|
import { FormLayout } from '@formily/antd-v5';
|
||||||
import { Field } from '@formily/core';
|
import { Field } from '@formily/core';
|
||||||
import { RecursionField, Schema, SchemaOptionsContext, observer, useField, useForm } from '@formily/react';
|
import { Schema, SchemaOptionsContext, observer, useField, useForm } from '@formily/react';
|
||||||
import {
|
import {
|
||||||
APIClientProvider,
|
APIClientProvider,
|
||||||
FormDialog,
|
FormDialog,
|
||||||
FormProvider,
|
FormProvider,
|
||||||
|
NocoBaseRecursionField,
|
||||||
SchemaComponent,
|
SchemaComponent,
|
||||||
SchemaComponentOptions,
|
SchemaComponentOptions,
|
||||||
css,
|
css,
|
||||||
@ -49,7 +50,7 @@ export const Options = observer(
|
|||||||
setSchema(new Schema(template.configurableProperties || {}));
|
setSchema(new Schema(template.configurableProperties || {}));
|
||||||
}
|
}
|
||||||
}, [form.values.type]);
|
}, [form.values.type]);
|
||||||
return <RecursionField schema={s} />;
|
return <NocoBaseRecursionField schema={s} />;
|
||||||
},
|
},
|
||||||
{ displayName: 'Options' },
|
{ displayName: 'Options' },
|
||||||
);
|
);
|
||||||
|
@ -74,10 +74,26 @@ test.describe('form item & view form', () => {
|
|||||||
},
|
},
|
||||||
supportedOptions: ['oneToMany', 'manyToOne', 'manyToMany', 'oneToOneBelongsTo', 'oneToOneHasOne'],
|
supportedOptions: ['oneToMany', 'manyToOne', 'manyToMany', 'oneToOneBelongsTo', 'oneToOneHasOne'],
|
||||||
expectValue: async () => {
|
expectValue: async () => {
|
||||||
await expect(page.getByText(record.oneToMany.map((item: any) => item.id).join(','))).toBeVisible();
|
await expect(
|
||||||
await expect(page.getByText(record.manyToOne.id)).toBeVisible();
|
page
|
||||||
await expect(page.getByText(record.manyToMany.map((item: any) => item.id).join(','))).toBeVisible();
|
.getByLabel('block-item-CollectionField-general-form-general.oneToMany-oneToMany')
|
||||||
await expect(page.getByText(record.oneToOneBelongsTo.id)).toBeVisible();
|
.getByText(record.oneToMany.map((item: any) => item.id).join(',')),
|
||||||
|
).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
page
|
||||||
|
.getByLabel('block-item-CollectionField-general-form-general.manyToOne-manyToOne')
|
||||||
|
.getByText(record.manyToOne.id),
|
||||||
|
).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
page
|
||||||
|
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
|
||||||
|
.getByText(record.manyToMany.map((item: any) => item.id).join(',')),
|
||||||
|
).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
page
|
||||||
|
.getByLabel('block-item-CollectionField-general-form-general.oneToOneBelongsTo-')
|
||||||
|
.getByText(record.oneToOneBelongsTo.id),
|
||||||
|
).toBeVisible();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -15,7 +15,7 @@ test.describe('form item & filter form', () => {
|
|||||||
|
|
||||||
// 在页面上创建一个筛选表单,并在表单中添加一个字段
|
// 在页面上创建一个筛选表单,并在表单中添加一个字段
|
||||||
await page.getByLabel('schema-initializer-Grid-page:').hover();
|
await page.getByLabel('schema-initializer-Grid-page:').hover();
|
||||||
await page.getByRole('menuitem', { name: 'form Form right' }).nth(1).click();
|
await page.getByRole('menuitem', { name: 'Form right' }).nth(1).click();
|
||||||
await page.getByRole('menuitem', { name: 'Users' }).click();
|
await page.getByRole('menuitem', { name: 'Users' }).click();
|
||||||
await page.getByLabel('schema-initializer-Grid-filterForm:configureFields-users').hover();
|
await page.getByLabel('schema-initializer-Grid-filterForm:configureFields-users').hover();
|
||||||
await page.getByRole('menuitem', { name: 'Nickname' }).click();
|
await page.getByRole('menuitem', { name: 'Nickname' }).click();
|
||||||
|
@ -186,8 +186,10 @@ export const ConfigurationTable = () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const schemaComponentContext = useMemo(() => ({ ...ctx, designable: false, dataSourceData }), [ctx, dataSourceData]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SchemaComponentContext.Provider value={{ ...ctx, designable: false, dataSourceData }}>
|
<SchemaComponentContext.Provider value={schemaComponentContext}>
|
||||||
<SchemaComponent
|
<SchemaComponent
|
||||||
schema={collectionSchema}
|
schema={collectionSchema}
|
||||||
components={{
|
components={{
|
||||||
|
@ -28,7 +28,7 @@ import {
|
|||||||
useRecord,
|
useRecord,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { getPickerFormat } from '@nocobase/utils/client';
|
import { getPickerFormat } from '@nocobase/utils/client';
|
||||||
import React, { useContext, useState } from 'react';
|
import React, { useContext, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { CollectionFields } from './CollectionFields';
|
import { CollectionFields } from './CollectionFields';
|
||||||
import { collectionSchema } from './schemas/collections';
|
import { collectionSchema } from './schemas/collections';
|
||||||
@ -212,8 +212,10 @@ export const ConfigurationTable = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ctx = useContext(SchemaComponentContext);
|
const ctx = useContext(SchemaComponentContext);
|
||||||
|
const schemaComponentContext = useMemo(() => ({ ...ctx, designable: false }), [ctx]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SchemaComponentContext.Provider value={{ ...ctx, designable: false }}>
|
<SchemaComponentContext.Provider value={schemaComponentContext}>
|
||||||
<SchemaComponent
|
<SchemaComponent
|
||||||
schema={collectionSchema}
|
schema={collectionSchema}
|
||||||
components={{
|
components={{
|
||||||
|
@ -41,12 +41,13 @@ export const useAvailableActions = () => {
|
|||||||
return useContext(AvailableActionsContext);
|
return useContext(AvailableActionsContext);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const schemaComponentContext = { designable: false };
|
||||||
export const DataSourceTable = () => {
|
export const DataSourceTable = () => {
|
||||||
const record = useRecord();
|
const record = useRecord();
|
||||||
const plugin = usePlugin(PluginDataSourceManagerClient);
|
const plugin = usePlugin(PluginDataSourceManagerClient);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SchemaComponentContext.Provider value={{ designable: false }}>
|
<SchemaComponentContext.Provider value={schemaComponentContext}>
|
||||||
<AvailableActionsProver>
|
<AvailableActionsProver>
|
||||||
<SchemaComponent
|
<SchemaComponent
|
||||||
schema={dataSourceSchema(plugin.getExtendedTabs())}
|
schema={dataSourceSchema(plugin.getExtendedTabs())}
|
||||||
|
@ -7,7 +7,8 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { RecursionField, useFieldSchema } from '@formily/react';
|
import { useFieldSchema } from '@formily/react';
|
||||||
|
import { NocoBaseRecursionField } from '@nocobase/client';
|
||||||
import { Schema } from '@nocobase/utils';
|
import { Schema } from '@nocobase/utils';
|
||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
|
|
||||||
@ -19,5 +20,5 @@ export const GanttRecordViewer: FC = (props) => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <RecursionField schema={eventSchema} name={eventSchema.name} />;
|
return <NocoBaseRecursionField schema={eventSchema} name={eventSchema.name} />;
|
||||||
};
|
};
|
||||||
|
@ -8,8 +8,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import { RecursionField, useFieldSchema } from '@formily/react';
|
import { useFieldSchema } from '@formily/react';
|
||||||
import {
|
import {
|
||||||
|
NocoBaseRecursionField,
|
||||||
PopupContextProvider,
|
PopupContextProvider,
|
||||||
RecordProvider,
|
RecordProvider,
|
||||||
useAPIClient,
|
useAPIClient,
|
||||||
@ -520,8 +521,8 @@ export const Gantt: any = withDynamicSchemaProps((props: any) => {
|
|||||||
<RecordProvider record={record} parent={parentRecordData}>
|
<RecordProvider record={record} parent={parentRecordData}>
|
||||||
<GanttRecordViewer />
|
<GanttRecordViewer />
|
||||||
</RecordProvider>
|
</RecordProvider>
|
||||||
<RecursionField name={'anctionBar'} schema={fieldSchema.properties.toolBar} />
|
<NocoBaseRecursionField name={'anctionBar'} schema={fieldSchema.properties.toolBar} />
|
||||||
<RecursionField name={'table'} schema={fieldSchema.properties.table} />
|
<NocoBaseRecursionField name={'table'} schema={fieldSchema.properties.table} />
|
||||||
<div className={styles.wrapper} onKeyDown={handleKeyDown} tabIndex={0} ref={wrapperRef}>
|
<div className={styles.wrapper} onKeyDown={handleKeyDown} tabIndex={0} ref={wrapperRef}>
|
||||||
<TaskGantt
|
<TaskGantt
|
||||||
gridProps={gridProps}
|
gridProps={gridProps}
|
||||||
|
@ -18,7 +18,7 @@ test.describe('where map block can be added', () => {
|
|||||||
|
|
||||||
// 1. 在页面中添加地图区块,因为没有配置 Access key 等信息,所以会显示错误提示
|
// 1. 在页面中添加地图区块,因为没有配置 Access key 等信息,所以会显示错误提示
|
||||||
await page.getByLabel('schema-initializer-Grid-page:').hover();
|
await page.getByLabel('schema-initializer-Grid-page:').hover();
|
||||||
await page.getByRole('menuitem', { name: 'table Map right' }).hover();
|
await page.getByRole('menuitem', { name: 'Map right' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'map', exact: true }).click();
|
await page.getByRole('menuitem', { name: 'map', exact: true }).click();
|
||||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||||
await expect(
|
await expect(
|
||||||
|
@ -7,8 +7,13 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { RecursionField, useFieldSchema } from '@formily/react';
|
import { useFieldSchema } from '@formily/react';
|
||||||
import { useCollection, useCollectionRecordData, VariablePopupRecordProvider } from '@nocobase/client';
|
import {
|
||||||
|
NocoBaseRecursionField,
|
||||||
|
useCollection,
|
||||||
|
useCollectionRecordData,
|
||||||
|
VariablePopupRecordProvider,
|
||||||
|
} from '@nocobase/client';
|
||||||
import React, { FC, useMemo } from 'react';
|
import React, { FC, useMemo } from 'react';
|
||||||
|
|
||||||
export const MapBlockDrawer: FC = (props) => {
|
export const MapBlockDrawer: FC = (props) => {
|
||||||
@ -32,7 +37,7 @@ export const MapBlockDrawer: FC = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<VariablePopupRecordProvider recordData={recordData} collection={collection}>
|
<VariablePopupRecordProvider recordData={recordData} collection={collection}>
|
||||||
<RecursionField schema={schema} name={schema.name} />
|
<NocoBaseRecursionField schema={schema} name={schema.name} />
|
||||||
</VariablePopupRecordProvider>
|
</VariablePopupRecordProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -7,8 +7,16 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { RecursionField, useField, useFieldSchema } from '@formily/react';
|
import { useField, useFieldSchema } from '@formily/react';
|
||||||
import { ActionBarProvider, SortableItem, TabsContextProvider, css, cx, useDesigner } from '@nocobase/client';
|
import {
|
||||||
|
ActionBarProvider,
|
||||||
|
NocoBaseRecursionField,
|
||||||
|
SortableItem,
|
||||||
|
TabsContextProvider,
|
||||||
|
css,
|
||||||
|
cx,
|
||||||
|
useDesigner,
|
||||||
|
} from '@nocobase/client';
|
||||||
import { TabsProps } from 'antd';
|
import { TabsProps } from 'antd';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { useSearchParams } from 'react-router-dom';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
@ -106,34 +114,34 @@ const InternalPage: React.FC = (props) => {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{isHeaderEnabled && (
|
{isHeaderEnabled && (
|
||||||
<RecursionField
|
<NocoBaseRecursionField
|
||||||
schema={fieldSchema}
|
schema={fieldSchema}
|
||||||
filterProperties={(s) => {
|
filterProperties={(s) => {
|
||||||
return 'MHeader' === s['x-component'];
|
return 'MHeader' === s['x-component'];
|
||||||
}}
|
}}
|
||||||
></RecursionField>
|
></NocoBaseRecursionField>
|
||||||
)}
|
)}
|
||||||
<TabsContextProvider
|
<TabsContextProvider
|
||||||
PaneRoot={GlobalActionProvider}
|
PaneRoot={GlobalActionProvider}
|
||||||
activeKey={searchParams.get('tab')}
|
activeKey={searchParams.get('tab')}
|
||||||
onChange={onTabsChange}
|
onChange={onTabsChange}
|
||||||
>
|
>
|
||||||
<RecursionField
|
<NocoBaseRecursionField
|
||||||
schema={isTabsEnabled ? fieldSchema : pageSchema}
|
schema={isTabsEnabled ? fieldSchema : pageSchema}
|
||||||
filterProperties={(s) => {
|
filterProperties={(s) => {
|
||||||
return 'Tabs' === s['x-component'] || 'Grid' === s['x-component'] || 'Grid.Row' === s['x-component'];
|
return 'Tabs' === s['x-component'] || 'Grid' === s['x-component'] || 'Grid.Row' === s['x-component'];
|
||||||
}}
|
}}
|
||||||
></RecursionField>
|
></NocoBaseRecursionField>
|
||||||
</TabsContextProvider>
|
</TabsContextProvider>
|
||||||
</div>
|
</div>
|
||||||
<GlobalActionProvider>
|
<GlobalActionProvider>
|
||||||
{!tabsSchema && (
|
{!tabsSchema && (
|
||||||
<RecursionField
|
<NocoBaseRecursionField
|
||||||
schema={fieldSchema}
|
schema={fieldSchema}
|
||||||
filterProperties={(s) => {
|
filterProperties={(s) => {
|
||||||
return s['x-component'] !== 'MHeader';
|
return s['x-component'] !== 'MHeader';
|
||||||
}}
|
}}
|
||||||
></RecursionField>
|
></NocoBaseRecursionField>
|
||||||
)}
|
)}
|
||||||
</GlobalActionProvider>
|
</GlobalActionProvider>
|
||||||
</SortableItem>
|
</SortableItem>
|
||||||
|
@ -172,7 +172,7 @@ test.describe('PageHeader', () => {
|
|||||||
test('Item:add and remove', async ({ page }) => {
|
test('Item:add and remove', async ({ page }) => {
|
||||||
// 添加页面内容
|
// 添加页面内容
|
||||||
await page.getByLabel('schema-initializer-Grid-mobile:addBlock').hover();
|
await page.getByLabel('schema-initializer-Grid-mobile:addBlock').hover();
|
||||||
await page.getByRole('menuitem', { name: 'form Markdown' }).click();
|
await page.getByRole('menuitem', { name: 'Markdown' }).click();
|
||||||
await expect(page.getByLabel('block-item-Markdown.Void-')).toBeVisible();
|
await expect(page.getByLabel('block-item-Markdown.Void-')).toBeVisible();
|
||||||
|
|
||||||
await page.getByLabel('action-Action-undefined').click();
|
await page.getByLabel('action-Action-undefined').click();
|
||||||
|
@ -7,8 +7,15 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ISchema, observer, RecursionField, useField, useFieldSchema } from '@formily/react';
|
import { ISchema, observer, useField, useFieldSchema } from '@formily/react';
|
||||||
import { Action, SchemaComponent, useActionContext, useZIndexContext, zIndexContext } from '@nocobase/client';
|
import {
|
||||||
|
Action,
|
||||||
|
NocoBaseRecursionField,
|
||||||
|
SchemaComponent,
|
||||||
|
useActionContext,
|
||||||
|
useZIndexContext,
|
||||||
|
zIndexContext,
|
||||||
|
} from '@nocobase/client';
|
||||||
import { ConfigProvider } from 'antd';
|
import { ConfigProvider } from 'antd';
|
||||||
import { Popup } from 'antd-mobile';
|
import { Popup } from 'antd-mobile';
|
||||||
import { CloseOutline } from 'antd-mobile-icons';
|
import { CloseOutline } from 'antd-mobile-icons';
|
||||||
@ -109,7 +116,7 @@ export const ActionDrawerUsedInMobile: any = observer((props: { footerNodeName?:
|
|||||||
)}
|
)}
|
||||||
{footerSchema ? (
|
{footerSchema ? (
|
||||||
<div className="nb-mobile-action-drawer-footer" style={isSpecialSchema ? specialStyle : null}>
|
<div className="nb-mobile-action-drawer-footer" style={isSpecialSchema ? specialStyle : null}>
|
||||||
<RecursionField
|
<NocoBaseRecursionField
|
||||||
basePath={field.address}
|
basePath={field.address}
|
||||||
schema={fieldSchema}
|
schema={fieldSchema}
|
||||||
onlyRenderProperties
|
onlyRenderProperties
|
||||||
|
@ -7,9 +7,10 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { RecursionField, useField, useFieldSchema } from '@formily/react';
|
import { useField, useFieldSchema } from '@formily/react';
|
||||||
import {
|
import {
|
||||||
BackButtonUsedInSubPage,
|
BackButtonUsedInSubPage,
|
||||||
|
NocoBaseRecursionField,
|
||||||
SchemaComponent,
|
SchemaComponent,
|
||||||
TabsContextProvider,
|
TabsContextProvider,
|
||||||
useActionContext,
|
useActionContext,
|
||||||
@ -67,7 +68,7 @@ export const MobileActionPage = ({ level, footerNodeName }) => {
|
|||||||
</TabsContextProvider>
|
</TabsContextProvider>
|
||||||
{footerSchema && (
|
{footerSchema && (
|
||||||
<div className="nb-mobile-action-page-footer" style={zIndexStyle}>
|
<div className="nb-mobile-action-page-footer" style={zIndexStyle}>
|
||||||
<RecursionField
|
<NocoBaseRecursionField
|
||||||
basePath={field.address}
|
basePath={field.address}
|
||||||
schema={fieldSchema}
|
schema={fieldSchema}
|
||||||
onlyRenderProperties
|
onlyRenderProperties
|
||||||
|
@ -7,11 +7,12 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { observer, RecursionField, useField, useFieldSchema } from '@formily/react';
|
import { observer, useField, useFieldSchema } from '@formily/react';
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
DndContext,
|
DndContext,
|
||||||
Icon,
|
Icon,
|
||||||
|
NocoBaseRecursionField,
|
||||||
SchemaComponent,
|
SchemaComponent,
|
||||||
SortableItem,
|
SortableItem,
|
||||||
Tabs as TabsOfPC,
|
Tabs as TabsOfPC,
|
||||||
@ -54,7 +55,9 @@ export const MobileTabsForMobileActionPage: any = observer(
|
|||||||
|
|
||||||
const items = useMemo(() => {
|
const items = useMemo(() => {
|
||||||
const result = fieldSchema.mapProperties((schema, key) => {
|
const result = fieldSchema.mapProperties((schema, key) => {
|
||||||
return <Tabs.Tab title={<RecursionField name={key} schema={schema} onlyRenderSelf />} key={key}></Tabs.Tab>;
|
return (
|
||||||
|
<Tabs.Tab title={<NocoBaseRecursionField name={key} schema={schema} onlyRenderSelf />} key={key}></Tabs.Tab>
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { RecursionField, useFieldSchema } from '@formily/react';
|
import { useFieldSchema } from '@formily/react';
|
||||||
import { cx, SchemaToolbarProvider } from '@nocobase/client';
|
import { cx, NocoBaseRecursionField, SchemaToolbarProvider } from '@nocobase/client';
|
||||||
import { NavBar } from 'antd-mobile';
|
import { NavBar } from 'antd-mobile';
|
||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
|
|
||||||
@ -31,12 +31,12 @@ export const MobilePageNavigationBar: FC = () => {
|
|||||||
back={null}
|
back={null}
|
||||||
left={
|
left={
|
||||||
<SchemaToolbarProvider position="left">
|
<SchemaToolbarProvider position="left">
|
||||||
<RecursionField name="actionBarLeft" schema={fieldSchema} onlyRenderProperties />
|
<NocoBaseRecursionField name="actionBarLeft" schema={fieldSchema} onlyRenderProperties />
|
||||||
</SchemaToolbarProvider>
|
</SchemaToolbarProvider>
|
||||||
}
|
}
|
||||||
right={
|
right={
|
||||||
<SchemaToolbarProvider position="right">
|
<SchemaToolbarProvider position="right">
|
||||||
<RecursionField name="actionBarRight" schema={fieldSchema} onlyRenderProperties />
|
<NocoBaseRecursionField name="actionBarRight" schema={fieldSchema} onlyRenderProperties />
|
||||||
</SchemaToolbarProvider>
|
</SchemaToolbarProvider>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -44,7 +44,7 @@ export const MobilePageNavigationBar: FC = () => {
|
|||||||
</NavBar>
|
</NavBar>
|
||||||
|
|
||||||
<SchemaToolbarProvider position="bottom">
|
<SchemaToolbarProvider position="bottom">
|
||||||
<RecursionField name="actionBarBottom" schema={fieldSchema} onlyRenderProperties />
|
<NocoBaseRecursionField name="actionBarBottom" schema={fieldSchema} onlyRenderProperties />
|
||||||
</SchemaToolbarProvider>
|
</SchemaToolbarProvider>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -8,16 +8,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { cx } from '@emotion/css';
|
import { cx } from '@emotion/css';
|
||||||
import { SpaceProps } from 'antd';
|
import { ISchema, observer, useFieldSchema } from '@formily/react';
|
||||||
import React, { CSSProperties, useContext } from 'react';
|
|
||||||
import { ISchema, RecursionField, observer, useFieldSchema } from '@formily/react';
|
|
||||||
import {
|
import {
|
||||||
DndContext,
|
DndContext,
|
||||||
|
NocoBaseRecursionField,
|
||||||
useProps,
|
useProps,
|
||||||
useSchemaInitializerRender,
|
useSchemaInitializerRender,
|
||||||
useSchemaToolbar,
|
useSchemaToolbar,
|
||||||
withDynamicSchemaProps,
|
withDynamicSchemaProps,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
|
import { SpaceProps } from 'antd';
|
||||||
|
import React, { CSSProperties, useContext } from 'react';
|
||||||
|
|
||||||
export interface ActionBarProps {
|
export interface ActionBarProps {
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
@ -81,7 +82,7 @@ export const MobileNavigationActionBar = withDynamicSchemaProps(
|
|||||||
{position === 'left' && render({})}
|
{position === 'left' && render({})}
|
||||||
{props.children && (
|
{props.children && (
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||||
<RecursionField
|
<NocoBaseRecursionField
|
||||||
onlyRenderProperties
|
onlyRenderProperties
|
||||||
schema={fieldSchema}
|
schema={fieldSchema}
|
||||||
filterProperties={(schema) => schema['x-position'] === position}
|
filterProperties={(schema) => schema['x-position'] === position}
|
||||||
@ -91,7 +92,7 @@ export const MobileNavigationActionBar = withDynamicSchemaProps(
|
|||||||
{position === 'right' && render({})}
|
{position === 'right' && render({})}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<RecursionField
|
<NocoBaseRecursionField
|
||||||
onlyRenderProperties
|
onlyRenderProperties
|
||||||
schema={fieldSchema}
|
schema={fieldSchema}
|
||||||
filterProperties={(schema) => schema['x-position'] === position}
|
filterProperties={(schema) => schema['x-position'] === position}
|
||||||
|
@ -18,8 +18,8 @@ import {
|
|||||||
useAsyncData,
|
useAsyncData,
|
||||||
useSchemaComponentContext,
|
useSchemaComponentContext,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { Button, Dropdown, Empty, Card } from 'antd';
|
import { Button, Card, Dropdown, Empty } from 'antd';
|
||||||
import React, { useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import channelCollection from '../../../../collections/channel';
|
import channelCollection from '../../../../collections/channel';
|
||||||
import messageLogCollection from '../../../../collections/messageLog';
|
import messageLogCollection from '../../../../collections/messageLog';
|
||||||
import { useNotificationTranslation } from '../../../locale';
|
import { useNotificationTranslation } from '../../../locale';
|
||||||
@ -125,10 +125,11 @@ export const ChannelManager = () => {
|
|||||||
const { t } = useNotificationTranslation();
|
const { t } = useNotificationTranslation();
|
||||||
const notificationTypes = useNotificationTypes();
|
const notificationTypes = useNotificationTypes();
|
||||||
const scCtx = useSchemaComponentContext();
|
const scCtx = useSchemaComponentContext();
|
||||||
|
const schemaComponentContext = useMemo(() => ({ ...scCtx, designable: false }), [scCtx]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExtendCollectionsProvider collections={[channelCollection, messageLogCollection]}>
|
<ExtendCollectionsProvider collections={[channelCollection, messageLogCollection]}>
|
||||||
<SchemaComponentContext.Provider value={{ ...scCtx, designable: false }}>
|
<SchemaComponentContext.Provider value={schemaComponentContext}>
|
||||||
<NotificationTypesContext.Provider value={{ channelTypes: notificationTypes }}>
|
<NotificationTypesContext.Provider value={{ channelTypes: notificationTypes }}>
|
||||||
<Card bordered={false}>
|
<Card bordered={false}>
|
||||||
<SchemaComponent
|
<SchemaComponent
|
||||||
|
@ -7,23 +7,29 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import {
|
||||||
|
ExtendCollectionsProvider,
|
||||||
|
SchemaComponent,
|
||||||
|
SchemaComponentContext,
|
||||||
|
useSchemaComponentContext,
|
||||||
|
} from '@nocobase/client';
|
||||||
import { Card } from 'antd';
|
import { Card } from 'antd';
|
||||||
import { messageLogsManagerSchema } from '../schemas';
|
import React, { useMemo } from 'react';
|
||||||
import { SchemaComponent, SchemaComponentContext, useSchemaComponentContext } from '@nocobase/client';
|
|
||||||
import { ExtendCollectionsProvider } from '@nocobase/client';
|
|
||||||
import { useNotificationTranslation } from '../../../locale';
|
|
||||||
import messageLogCollection from '../../../../collections/messageLog';
|
|
||||||
import channelCollection from '../../../../collections/channel';
|
import channelCollection from '../../../../collections/channel';
|
||||||
|
import messageLogCollection from '../../../../collections/messageLog';
|
||||||
|
import { useNotificationTranslation } from '../../../locale';
|
||||||
import { useEditFormProps, useNotificationTypes } from '../../channel/hooks';
|
import { useEditFormProps, useNotificationTypes } from '../../channel/hooks';
|
||||||
|
import { messageLogsManagerSchema } from '../schemas';
|
||||||
|
|
||||||
export const LogManager = () => {
|
export const LogManager = () => {
|
||||||
const { t } = useNotificationTranslation();
|
const { t } = useNotificationTranslation();
|
||||||
const scCtx = useSchemaComponentContext();
|
const scCtx = useSchemaComponentContext();
|
||||||
const notificationTypes = useNotificationTypes();
|
const notificationTypes = useNotificationTypes();
|
||||||
|
const schemaComponentContext = useMemo(() => ({ ...scCtx, designable: false }), [scCtx]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExtendCollectionsProvider collections={[messageLogCollection, channelCollection]}>
|
<ExtendCollectionsProvider collections={[messageLogCollection, channelCollection]}>
|
||||||
<SchemaComponentContext.Provider value={{ ...scCtx, designable: false }}>
|
<SchemaComponentContext.Provider value={schemaComponentContext}>
|
||||||
<Card bordered={false}>
|
<Card bordered={false}>
|
||||||
<SchemaComponent
|
<SchemaComponent
|
||||||
schema={messageLogsManagerSchema}
|
schema={messageLogsManagerSchema}
|
||||||
|
@ -11,7 +11,7 @@ import { FormLayout } from '@formily/antd-v5';
|
|||||||
import { createForm } from '@formily/core';
|
import { createForm } from '@formily/core';
|
||||||
import { FormProvider, ISchema, Schema, useFieldSchema, useForm } from '@formily/react';
|
import { FormProvider, ISchema, Schema, useFieldSchema, useForm } from '@formily/react';
|
||||||
import { Alert, Button, Modal, Space, message } from 'antd';
|
import { Alert, Button, Modal, Space, message } from 'antd';
|
||||||
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -39,7 +39,6 @@ import {
|
|||||||
useSchemaInitializer,
|
useSchemaInitializer,
|
||||||
useSchemaInitializerItem,
|
useSchemaInitializerItem,
|
||||||
useSchemaOptionsContext,
|
useSchemaOptionsContext,
|
||||||
useVariableScope,
|
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import WorkflowPlugin, {
|
import WorkflowPlugin, {
|
||||||
DetailsBlockProvider,
|
DetailsBlockProvider,
|
||||||
@ -430,6 +429,7 @@ export function SchemaConfig({ value, onChange }) {
|
|||||||
const nodes = useAvailableUpstreams(node);
|
const nodes = useAvailableUpstreams(node);
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
const { workflow } = useFlowContext();
|
const { workflow } = useFlowContext();
|
||||||
|
const refreshRef = useRef(() => {});
|
||||||
|
|
||||||
const nodeComponents = {};
|
const nodeComponents = {};
|
||||||
nodes.forEach((item) => {
|
nodes.forEach((item) => {
|
||||||
@ -452,6 +452,8 @@ export function SchemaConfig({ value, onChange }) {
|
|||||||
background: var(--nb-box-bg);
|
background: var(--nb-box-bg);
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
// Using ref to call refresh ensures accessing the latest refresh function
|
||||||
|
onClose: () => refreshRef.current(),
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
tabs: {
|
tabs: {
|
||||||
@ -501,6 +503,8 @@ export function SchemaConfig({ value, onChange }) {
|
|||||||
[form, onChange, schema],
|
[form, onChange, schema],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
refreshRef.current = refresh;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SchemaComponentContext.Provider
|
<SchemaComponentContext.Provider
|
||||||
value={{
|
value={{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user