feat: introduce VariableProvider

This commit is contained in:
sheldon66 2025-03-09 14:47:42 +08:00
parent bde404b540
commit 3f6cae33ec
8 changed files with 133 additions and 26 deletions

View File

@ -35,6 +35,7 @@ import { XButton } from './XButton';
import { useStyles } from './style';
import { Json } from '../input';
import { Filters, Addition, FilterContext } from './VariableFilters';
import { VariableProvider } from './VariableProvider';
const { Text } = Typography;
const JT_VALUE_RE = /^\s*{{\s*([^{}]+)\s*}}\s*$/;
@ -484,11 +485,13 @@ export function Input(props: VariableInputProps) {
</React.Fragment>
);
})}
<FilterContext.Provider value={{ updateFilterParams, deleteFilter }}>
<Filters filters={filters} onFilterChange={onFilterAdd} />
<VariableProvider variableName={fullVariable}>
<FilterContext.Provider value={{ updateFilterParams, deleteFilter, variableName: fullVariable }}>
<Filters filters={filters} onFilterChange={onFilterAdd} />
{variableText.length > 0 && <Addition variable={fullVariable} onFilterAdd={onFilterAdd} />}
</FilterContext.Provider>
{variableText.length > 0 && <Addition variable={fullVariable} onFilterAdd={onFilterAdd} />}
</FilterContext.Provider>
</VariableProvider>
</Tag>
</div>
{!disabled ? (

View File

@ -17,6 +17,7 @@ import type { MenuProps } from 'antd';
import { SchemaComponent } from '../../core/SchemaComponent';
import { useApp } from '../../../application';
import { useCompile } from '../../hooks';
import { useVariable } from './VariableProvider';
export function Addition({ variable, onFilterAdd }) {
const app = useApp();
@ -59,15 +60,30 @@ export function Filters({ filters, onFilterChange }) {
if (!filterConfig) {
return null;
}
return <Filter key={index} filterId={index} config={filterConfig} filter={filter} />;
const previousFilters = filters.slice(0, index);
return (
<Filter
key={index}
filterId={index}
config={filterConfig}
filter={filter}
previousFilters={previousFilters}
/>
);
})}
</>
);
}
export function Filter({ config, filter, filterId }) {
export function Filter({ config, filter, filterId, previousFilters }) {
const [open, setOpen] = useState(false);
const { value } = useVariable();
const inputValue = [...previousFilters].reduce((value, filter) => {
return filter.handler(value, ...filter.args);
}, value);
const outputValue = [...previousFilters, filter].reduce((value, filter) => {
return filter.handler(value, ...filter.args);
}, value);
const handleOpenChange = (newOpen: boolean) => {
setOpen(newOpen);
};
@ -108,16 +124,28 @@ export function Filter({ config, filter, filterId }) {
};
const useFormBlockProps = () => {
return { form };
return { form, layout: 'vertical' };
};
const WithPropOver = ({ children }) => {
const { value } = useVariable();
const schema = {
'x-uid': uid(),
type: 'void',
// 'x-component': 'FormV2',
// 'x-use-component-props': 'useFormBlockProps',
'x-component': 'FormV2',
'x-use-component-props': 'useFormBlockProps',
properties: {
'$input-value': {
type: 'void',
'x-component': 'Input',
'x-component-props': {
defaultValue: '{{ inputValue }}',
disabled: true,
},
'x-decorator': 'FormItem',
title: tval('Input Value'),
'x-read-pretty': true,
},
...Object.fromEntries(
config.uiSchema.map((param) => [
param.name,
@ -127,6 +155,17 @@ export function Filter({ config, filter, filterId }) {
},
]),
),
'$return-value': {
type: 'void',
'x-component': 'Input',
'x-component-props': {
defaultValue: '{{ outputValue }}',
disabled: true,
},
'x-decorator': 'FormItem',
title: tval('Return Value'),
'x-read-pretty': true,
},
actions: {
type: 'void',
title: tval('Save'),
@ -153,13 +192,11 @@ export function Filter({ config, filter, filterId }) {
open={open}
onOpenChange={handleOpenChange}
content={
<FormContext.Provider value={form}>
<SchemaComponent
schema={schema}
scope={{ useSaveActionProps, useFormBlockProps, useDeleteActionProps }}
basePath={['']}
/>
</FormContext.Provider>
<SchemaComponent
schema={schema}
scope={{ useSaveActionProps, useFormBlockProps, useDeleteActionProps, outputValue, inputValue }}
basePath={['']}
/>
}
trigger={'hover'}
>
@ -171,19 +208,19 @@ export function Filter({ config, filter, filterId }) {
return (
<>
<span style={{ color: '#bfbfbf', margin: '0 5px' }}>|</span>
<FormContext.Provider value={form}>
{config.uiSchema ? <WithPropOver>{Label}</WithPropOver> : Label}
</FormContext.Provider>
{config.uiSchema ? <WithPropOver>{Label}</WithPropOver> : Label}
</>
);
}
type FilterContextType = {
variableName: string;
updateFilterParams: (args: { filterId: number; params: any[] }) => any;
deleteFilter: (args: { filterId: number }) => any;
};
export const FilterContext = React.createContext<FilterContextType>({
variableName: '',
updateFilterParams: (params: any) => {},
deleteFilter: (params: any) => {},
});

View File

@ -0,0 +1,47 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { useVariables, useLocalVariables } from '../../../variables';
import { getPath } from '../../../variables/utils/getPath';
import { isArray } from 'lodash';
interface VariableContextValue {
value: any;
}
interface VariableProviderProps {
variableName: string;
children: React.ReactNode;
}
const VariableContext = createContext<VariableContextValue>({ value: null });
export const useVariable = () => {
const context = useContext(VariableContext);
if (!context) {
throw new Error('useVariable must be used within a VariableProvider');
}
return context;
};
export const VariableProvider: React.FC<VariableProviderProps> = ({ variableName, children }) => {
const [value, setValue] = useState();
const variables = useVariables();
const localVariables = useLocalVariables();
isArray(localVariables) ? localVariables : [localVariables];
useEffect(() => {
async function fetchValue() {
const result = await variables.getVariableValue(variableName, localVariables);
setValue(result.value);
}
fetchValue();
}, [localVariables, variableName, variables]);
return <VariableContext.Provider value={{ value }}>{children}</VariableContext.Provider>;
};

View File

@ -347,11 +347,12 @@ const VariablesProvider = ({ children, filterVariables }: any) => {
parseVariable,
registerVariable,
getVariable,
getVariableValue: getResult,
getCollectionField,
removeVariable,
filterVariables,
}) as VariablesContextType,
[getCollectionField, getVariable, parseVariable, registerVariable, removeVariable, setCtx],
[getCollectionField, getVariable, parseVariable, registerVariable, removeVariable, setCtx, getResult],
);
return <VariablesContext.Provider value={value}>{children}</VariablesContext.Provider>;

View File

@ -82,6 +82,26 @@ export interface VariablesContextType {
* @returns
*/
getVariable: (variableName: string) => VariableOption;
/**
*
* @param variableName `$user`
* @returns
*/
getVariableValue: (
variablePath: string,
localVariables?: VariableOption[],
options?: {
/** Related fields that need to be included in the first request */
appends?: string[];
/** Do not request when the association field is empty */
doNotRequest?: boolean;
/**
* The operator related to the current field, provided when parsing the default value of the field
*/
fieldOperator?: string | void;
},
) => Promise<any>;
getCollectionField: (
variableString: string,
localVariables?: VariableOption | VariableOption[],

View File

@ -7,4 +7,4 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
export { createJSONTemplateParser, JSONTemplateParser } from './json-template-parser';
export { createJSONTemplateParser, JSONTemplateParser, parse } from './json-template-parser';

View File

@ -256,3 +256,5 @@ const parser = new JSONTemplateParser();
export function createJSONTemplateParser() {
return parser;
}
export const parse = parser.parse;

View File

@ -9,11 +9,8 @@
import lodash from 'lodash';
import { dayjs } from './dayjs';
import { createJSONTemplateParser } from '@nocobase/json-template-parser';
const parser = createJSONTemplateParser();
const parse = parser.parse;
export { parse };
export { parse } from '@nocobase/json-template-parser';
export * from './assign';
export * from './collections-graph';
export * from './common';