Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | /**
* ComponentWidget — dynamically loads and renders a portal component.
*
* Used as a form field widget with:
* `widget: {type: 'component', component: 'portal.page.key', params: {...}}`
*
* Dispatches `component/${page}` with the configured page key to obtain a
* React component, then renders it. Shows a centred spinner while loading.
*
* Both `component` and `params` values support `${field}` template expressions
* resolved against the current form values at mount time. Uses the
* blong-template browser engine (Function-constructor based, same syntax as the
* server-side trusted engine).
*/
import type {IWidgetProps} from '@feasibleone/blong';
import {renderAll} from '@feasibleone/blong-template';
import React from 'react';
import {useBlongForm} from '../components/Form/FormContext.js';
import {useBlong} from '../context/BlongContext.js';
import {ProgressSpinner} from '../primereact/index.js';
export function ComponentWidget(widgetProps: IWidgetProps) {
const {schema, loading} = widgetProps;
const {handler} = useBlong();
const formCtx = useBlongForm();
const page = schema.widget?.component ?? '';
const params = schema.widget?.params;
const [errorRender, setErrorRender] = React.useState<Error | null>(null);
const [LoadedComponent, setLoadedComponent] = React.useState<React.ComponentType<
IWidgetProps
> | null>(null);
const vars = formCtx?.getValues();
React.useEffect(() => {
if (!page || loading) return;
let resolvedPage: string;
let resolvedParams: Record<string, unknown> = {};
try {
resolvedPage = renderAll(page, vars ?? {});
if (!resolvedPage) return;
resolvedParams = params
? (renderAll(params, vars ?? {}) as Record<string, unknown>)
: {};
} catch (error) {
// eslint-disable-next-line @eslint-react/set-state-in-effect
setErrorRender(error as Error);
return;
}
void (handler[`component/${resolvedPage}`]({
...resolvedParams,
}, {}) as Promise<React.ComponentType | null>)?.then(component => {
if (component) {
setErrorRender(null);
setLoadedComponent(() => component as React.ComponentType<IWidgetProps>);
}
});
}, [page, params, formCtx, handler, loading, vars]);
if (!LoadedComponent) {
return (
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '100%',
}}
>
<ProgressSpinner title={errorRender ? errorRender.message : 'Loading...'} />
</div>
);
}
return <LoadedComponent {...widgetProps} />;
}
|