mirror of
https://github.com/smartxworks/sunmao-ui.git
synced 2024-11-21 03:15:49 +08:00
remove slotsMap & resolveAppComponents
This commit is contained in:
parent
7192c5cd7c
commit
edf4b2b9d9
@ -39,22 +39,23 @@ export default implementRuntimeComponent({
|
||||
mergeState,
|
||||
subscribeMethods,
|
||||
hideSubmit,
|
||||
slotsMap,
|
||||
callbackMap,
|
||||
services,
|
||||
customStyle,
|
||||
Slot,
|
||||
treeMap,
|
||||
component
|
||||
}) => {
|
||||
const [invalidArray, setInvalidArray] = useState<boolean[]>([]);
|
||||
const [isFormInvalid, setIsFormInvalid] = useState<boolean>(false);
|
||||
const formDataRef = useRef<Record<string, any>>({});
|
||||
const formControlIds = useMemo<string[]>(() => {
|
||||
return (
|
||||
slotsMap?.get('content')?.map(slot => {
|
||||
treeMap[component.id]?.content.map(slot => {
|
||||
return slot.id;
|
||||
}) || []
|
||||
);
|
||||
}, [slotsMap]);
|
||||
}, [component.id, treeMap]);
|
||||
|
||||
useEffect(() => {
|
||||
setInvalidArray(
|
||||
|
@ -60,16 +60,17 @@ export default implementRuntimeComponent({
|
||||
fieldName,
|
||||
isRequired,
|
||||
helperText,
|
||||
slotsMap,
|
||||
mergeState,
|
||||
services,
|
||||
customStyle,
|
||||
Slot,
|
||||
treeMap,
|
||||
component,
|
||||
}) => {
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
// don't show Invalid state on component mount
|
||||
const [hideInvalid, setHideInvalid] = useState(true);
|
||||
const inputId = useMemo(() => first(slotsMap?.get('content'))?.id || '', [slotsMap]);
|
||||
const inputId = useMemo(() => first(treeMap[component.id].content)?.id || '', [component.id, treeMap]);
|
||||
const [validResult, setValidResult] = useState({
|
||||
isInvalid: false,
|
||||
errorMsg: '',
|
||||
|
@ -82,7 +82,7 @@ export default implementRuntimeComponent({
|
||||
${customStyle?.tabContent}
|
||||
`}
|
||||
>
|
||||
{<Component /> || placeholder}
|
||||
{Component ? <Component /> : placeholder}
|
||||
</TabPanel>
|
||||
);
|
||||
})}
|
||||
|
@ -26,10 +26,10 @@ export const ComponentTree: React.FC<Props> = props => {
|
||||
if (slots.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const slotsMap = childrenMap.get(component.id);
|
||||
const children = childrenMap.get(component.id);
|
||||
return slots.map(slot => {
|
||||
let slotContent;
|
||||
const slotChildren = slotsMap?.get(slot);
|
||||
const slotChildren = children?.get(slot);
|
||||
if (slotChildren && slotChildren.length > 0) {
|
||||
slotContent = slotChildren.map(c => {
|
||||
return (
|
||||
|
@ -3,7 +3,6 @@ import { ApplicationComponent } from '@sunmao-ui/core';
|
||||
export type ChildrenMap = Map<string, SlotsMap>;
|
||||
type SlotsMap = Map<string, ApplicationComponent[]>;
|
||||
|
||||
// similar to resolveAppComponents
|
||||
export function resolveApplicationComponents(components: ApplicationComponent[]): {
|
||||
topLevelComponents: ApplicationComponent[];
|
||||
childrenMap: ChildrenMap;
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import { initStateAndMethod } from './utils/initStateAndMethod';
|
||||
import { ImplWrapper } from './components/_internal/ImplWrapper';
|
||||
import { resolveAppComponents } from './services/resolveAppComponents';
|
||||
import { AppProps, UIServices } from './types/RuntimeSchema';
|
||||
import { DebugEvent, DebugStore } from './services/DebugComponents';
|
||||
import { RuntimeAppSchemaManager } from './services/RuntimeAppSchemaManager';
|
||||
@ -28,37 +27,16 @@ export const App: React.FC<AppProps> = props => {
|
||||
|
||||
initStateAndMethod(services.registry, services.stateManager, app.spec.components);
|
||||
|
||||
const { topLevelComponents, slotComponentsMap } = useMemo(
|
||||
() =>
|
||||
resolveAppComponents(app.spec.components, {
|
||||
services,
|
||||
app,
|
||||
componentWrapper,
|
||||
gridCallbacks,
|
||||
}),
|
||||
[app, componentWrapper, gridCallbacks, services]
|
||||
);
|
||||
|
||||
const { treeMap } = resolveTreeMap(app.spec.components);
|
||||
const { treeMap, topLevelComponents } = resolveTreeMap(app.spec.components);
|
||||
return (
|
||||
<div className="App" style={{ height: '100vh', overflow: 'auto' }}>
|
||||
{topLevelComponents.map(c => {
|
||||
const slotsMap = slotComponentsMap.get(c.id);
|
||||
// const Slot = genSlot({
|
||||
// component: c,
|
||||
// slotsMap,
|
||||
// treeMap,
|
||||
// ...props,
|
||||
// });
|
||||
return (
|
||||
<ImplWrapper
|
||||
key={c.id}
|
||||
component={c}
|
||||
services={services}
|
||||
slotsMap={slotsMap}
|
||||
Slot={() => null}
|
||||
treeMap={treeMap}
|
||||
targetSlot={null}
|
||||
app={app}
|
||||
componentWrapper={componentWrapper}
|
||||
gridCallbacks={gridCallbacks}
|
||||
|
@ -16,7 +16,6 @@ type ApplicationTrait = ArrayElement<RuntimeApplicationComponent['traits']>;
|
||||
const _ImplWrapper = React.forwardRef<HTMLDivElement, ImplWrapperProps>((props, ref) => {
|
||||
const {
|
||||
component: c,
|
||||
targetSlot,
|
||||
app,
|
||||
children,
|
||||
componentWrapper: ComponentWrapper,
|
||||
@ -153,15 +152,13 @@ const _ImplWrapper = React.forwardRef<HTMLDivElement, ImplWrapperProps>((props,
|
||||
}, [c.properties, stateManager]);
|
||||
|
||||
const mergedProps = { ...evaledComponentProperties, ...propsFromTraits };
|
||||
const { slotsMap, ...restProps } = props;
|
||||
const Slot = genSlots(props);
|
||||
const C = unmount ? null : (
|
||||
<Impl
|
||||
key={c.id}
|
||||
{...props}
|
||||
{...mergedProps}
|
||||
{...restProps}
|
||||
Slot={Slot}
|
||||
slotsMap={slotsMap}
|
||||
mergeState={mergeState}
|
||||
subscribeMethods={subscribeMethods}
|
||||
/>
|
||||
@ -175,8 +172,11 @@ const _ImplWrapper = React.forwardRef<HTMLDivElement, ImplWrapperProps>((props,
|
||||
);
|
||||
|
||||
let parentComponent;
|
||||
if (targetSlot && app) {
|
||||
parentComponent = app.spec.components.find(c => c.id === targetSlot.id);
|
||||
|
||||
const slotTrait = c.traits.find(t => t.type === 'core/v1/slot')
|
||||
|
||||
if (slotTrait && app) {
|
||||
parentComponent = app.spec.components.find(c => c.id === (slotTrait.properties.container as any).id);
|
||||
}
|
||||
// wrap component, but grid_layout is root component and cannot be chosen, so don't wrap it
|
||||
if (
|
||||
@ -197,9 +197,7 @@ const _ImplWrapper = React.forwardRef<HTMLDivElement, ImplWrapperProps>((props,
|
||||
const {
|
||||
component,
|
||||
services,
|
||||
targetSlot,
|
||||
app,
|
||||
slotsMap,
|
||||
componentWrapper,
|
||||
gridCallbacks,
|
||||
...restProps
|
||||
|
@ -9,7 +9,6 @@ import {
|
||||
RuntimeApplicationComponent,
|
||||
} from '../../types/RuntimeSchema';
|
||||
import { EventHandlerSchema } from '../../types/TraitPropertiesSchema';
|
||||
import { resolveAppComponents } from '../../services/resolveAppComponents';
|
||||
import { ImplWrapper } from './ImplWrapper';
|
||||
import { watch } from '../../utils/watchReactivity';
|
||||
import { ImplementedRuntimeModule } from '../../services/registry';
|
||||
@ -149,23 +148,12 @@ const ModuleRendererContent: React.FC<Props & { moduleSpec: ImplementedRuntimeMo
|
||||
}, [evaledHanlders, moduleId, services.apiService]);
|
||||
|
||||
const result = useMemo(() => {
|
||||
const { topLevelComponents, slotComponentsMap } = resolveAppComponents(
|
||||
evaledModuleTemplate,
|
||||
{
|
||||
services,
|
||||
app,
|
||||
}
|
||||
);
|
||||
const {treeMap} = resolveTreeMap(evaledModuleTemplate)
|
||||
const { treeMap, topLevelComponents } = resolveTreeMap(evaledModuleTemplate);
|
||||
return topLevelComponents.map(c => {
|
||||
const slotsMap = slotComponentsMap.get(c.id);
|
||||
return (
|
||||
<ImplWrapper
|
||||
key={c.id}
|
||||
component={c}
|
||||
slotsMap={slotsMap}
|
||||
Slot={() => null}
|
||||
targetSlot={null}
|
||||
services={services}
|
||||
app={app}
|
||||
treeMap={treeMap}
|
||||
@ -176,7 +164,6 @@ const ModuleRendererContent: React.FC<Props & { moduleSpec: ImplementedRuntimeMo
|
||||
|
||||
return <>{result}</>;
|
||||
};
|
||||
|
||||
|
||||
function parseTypeComponents(
|
||||
c: Application['spec']['components'][0]
|
||||
|
@ -17,15 +17,7 @@ export function genSlots<K extends string>(
|
||||
return (
|
||||
<>
|
||||
{childrenSchema.map(c => {
|
||||
return (
|
||||
<ImplWrapper
|
||||
Slot={() => null}
|
||||
targetSlot={null}
|
||||
key={c.id}
|
||||
{...props}
|
||||
component={c}
|
||||
/>
|
||||
);
|
||||
return <ImplWrapper key={c.id} {...props} component={c} />;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
@ -41,14 +33,6 @@ export function genSlotsAsArray<K extends string>(
|
||||
return [];
|
||||
}
|
||||
return childrenSchema.map(c => {
|
||||
return () => (
|
||||
<ImplWrapper
|
||||
Slot={() => null}
|
||||
targetSlot={null}
|
||||
key={c.id}
|
||||
{...props}
|
||||
component={c}
|
||||
/>
|
||||
);
|
||||
return () => <ImplWrapper key={c.id} {...props} component={c} />;
|
||||
});
|
||||
}
|
||||
|
@ -21,7 +21,12 @@ import {
|
||||
RouterCtx,
|
||||
useNavigate,
|
||||
} from './hooks';
|
||||
import { SlotsMap } from '../../../types/RuntimeSchema';
|
||||
import {
|
||||
RuntimeApplicationComponent,
|
||||
UIServices,
|
||||
TreeMap,
|
||||
} from '../../../types/RuntimeSchema';
|
||||
import { genSlotsAsArray } from '../../../components/_internal/Slot';
|
||||
|
||||
export type RouteLikeElement = PropsWithChildren<{
|
||||
path?: string;
|
||||
@ -51,7 +56,7 @@ export const Route: React.FC<RouteProps> = ({ match, children, mergeState }) =>
|
||||
}
|
||||
mergeState(destroyObj);
|
||||
};
|
||||
}, [params]);
|
||||
}, [matches, mergeState, params]);
|
||||
if (!matches) return null;
|
||||
return typeof children === 'function' ? children(params) : children;
|
||||
};
|
||||
@ -59,18 +64,15 @@ export const Route: React.FC<RouteProps> = ({ match, children, mergeState }) =>
|
||||
type SwitchProps = {
|
||||
location?: string;
|
||||
switchPolicy: SwitchPolicy;
|
||||
slotMap?: SlotsMap<string>;
|
||||
component: RuntimeApplicationComponent;
|
||||
treeMap: TreeMap<string>;
|
||||
services: UIServices;
|
||||
mergeState: (partialState: any) => void;
|
||||
subscribeMethods: (map: { [key: string]: (parameters: any) => void }) => void;
|
||||
};
|
||||
|
||||
export const Switch: React.FC<SwitchProps> = ({
|
||||
switchPolicy,
|
||||
location,
|
||||
slotMap,
|
||||
mergeState,
|
||||
subscribeMethods,
|
||||
}) => {
|
||||
export const Switch: React.FC<SwitchProps> = props => {
|
||||
const { switchPolicy, location, mergeState, subscribeMethods } = props;
|
||||
const [originalLocation] = useLocation();
|
||||
const matcher = useMemo(() => makeMatcher(), []);
|
||||
|
||||
@ -80,7 +82,7 @@ export const Switch: React.FC<SwitchProps> = ({
|
||||
let defaultPath: string | undefined = undefined;
|
||||
const result = switchPolicy.map(
|
||||
({ type, path, slotId, href, default: _default, exact, strict, sensitive }) => {
|
||||
const componentsArr = slotMap && slotMap.get(slotId);
|
||||
const componentsArr = genSlotsAsArray(props, slotId);
|
||||
if (defaultPath === undefined && _default) {
|
||||
defaultPath = path;
|
||||
}
|
||||
@ -106,7 +108,7 @@ export const Switch: React.FC<SwitchProps> = ({
|
||||
if (componentsArr.length !== 1) {
|
||||
console.warn('router slot can only have one component');
|
||||
}
|
||||
const { component: C } = componentsArr[0];
|
||||
const C = componentsArr[0];
|
||||
if (C.displayName === 'router') {
|
||||
return (
|
||||
// it should match both itself and its children path
|
||||
@ -141,7 +143,7 @@ export const Switch: React.FC<SwitchProps> = ({
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}, [switchPolicy]);
|
||||
}, [mergeState, props, switchPolicy]);
|
||||
|
||||
useEffect(() => {
|
||||
subscribeMethods({
|
||||
@ -149,7 +151,7 @@ export const Switch: React.FC<SwitchProps> = ({
|
||||
naviagte(path);
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
}, [naviagte, subscribeMethods]);
|
||||
|
||||
useEffect(() => {
|
||||
// to assign location as a state
|
||||
@ -161,7 +163,7 @@ export const Switch: React.FC<SwitchProps> = ({
|
||||
route: undefined,
|
||||
});
|
||||
};
|
||||
}, [loc]);
|
||||
}, [loc, mergeState]);
|
||||
|
||||
for (const element of flattenChildren(routes)) {
|
||||
const match: Match<DefaultParams> = element.props.path
|
||||
@ -228,7 +230,7 @@ export const Redirect: React.FC<RedirectProps> = props => {
|
||||
// empty array means running the effect once, navRef is a ref so it never changes
|
||||
useLayoutEffect(() => {
|
||||
navRef.current!();
|
||||
}, []);
|
||||
}, [navRef]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
@ -59,13 +59,10 @@ export default implementRuntimeComponent({
|
||||
styleSlots: [],
|
||||
events: [],
|
||||
},
|
||||
})(({ slotsMap, switchPolicy, subscribeMethods, mergeState }) => {
|
||||
})((props) => {
|
||||
return (
|
||||
<Switch
|
||||
slotMap={slotsMap}
|
||||
switchPolicy={switchPolicy}
|
||||
subscribeMethods={subscribeMethods}
|
||||
mergeState={mergeState}
|
||||
{...props}
|
||||
></Switch>
|
||||
);
|
||||
});
|
||||
|
@ -1,65 +0,0 @@
|
||||
import React from 'react';
|
||||
import { RuntimeApplication } from '@sunmao-ui/core';
|
||||
import { ContainerPropertySchema } from '../traits/core/slot';
|
||||
import { Static } from '@sinclair/typebox';
|
||||
import {
|
||||
ComponentParamsFromApp,
|
||||
UIServices,
|
||||
SlotComponentMap,
|
||||
} from '../types/RuntimeSchema';
|
||||
import { ImplWrapper } from '../components/_internal/ImplWrapper';
|
||||
|
||||
export function resolveAppComponents(
|
||||
components: RuntimeApplication['spec']['components'],
|
||||
params: {
|
||||
services: UIServices;
|
||||
app?: RuntimeApplication;
|
||||
} & ComponentParamsFromApp
|
||||
): {
|
||||
topLevelComponents: RuntimeApplication['spec']['components'];
|
||||
slotComponentsMap: SlotComponentMap;
|
||||
} {
|
||||
const topLevelComponents: RuntimeApplication['spec']['components'] = [];
|
||||
const slotComponentsMap: SlotComponentMap = new Map();
|
||||
|
||||
for (const c of components) {
|
||||
// handle component with slot trait
|
||||
const slotTrait = c.traits.find(t => t.parsedType.name === 'slot');
|
||||
if (slotTrait) {
|
||||
const { id, slot } = (
|
||||
slotTrait.properties as {
|
||||
container: Static<typeof ContainerPropertySchema>;
|
||||
}
|
||||
).container;
|
||||
if (!slotComponentsMap.has(id)) {
|
||||
slotComponentsMap.set(id, new Map());
|
||||
}
|
||||
if (!slotComponentsMap.get(id)?.has(slot)) {
|
||||
slotComponentsMap.get(id)?.set(slot, []);
|
||||
}
|
||||
const component = React.forwardRef<HTMLDivElement, any>((props, ref) => (
|
||||
<ImplWrapper
|
||||
component={c}
|
||||
slotsMap={slotComponentsMap.get(c.id)}
|
||||
targetSlot={{ id, slot }}
|
||||
{...params}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
));
|
||||
component.displayName = c.parsedType.name;
|
||||
slotComponentsMap.get(id)?.get(slot)?.push({
|
||||
component,
|
||||
id: c.id,
|
||||
});
|
||||
}
|
||||
|
||||
// if the component is neither assigned with slot trait nor route trait, consider it as a top level component
|
||||
!slotTrait && topLevelComponents.push(c);
|
||||
}
|
||||
|
||||
return {
|
||||
topLevelComponents,
|
||||
slotComponentsMap,
|
||||
};
|
||||
}
|
@ -44,11 +44,7 @@ export type AppProps = {
|
||||
// TODO: (type-safe), remove fallback type
|
||||
export type ImplWrapperProps<KSlot extends string = string> = {
|
||||
component: RuntimeApplicationComponent;
|
||||
// TODO: (type-safe), remove slotsMap from props
|
||||
slotsMap: SlotsMap<KSlot> | undefined;
|
||||
treeMap: TreeMap<KSlot>;
|
||||
Slot: SlotType<KSlot>;
|
||||
targetSlot: { id: string; slot: string } | null;
|
||||
services: UIServices;
|
||||
app?: RuntimeApplication;
|
||||
} & ComponentParamsFromApp;
|
||||
@ -58,15 +54,6 @@ export type TreeMap<KSlot extends string> = Record<
|
||||
Record<KSlot, RuntimeApplicationComponent[]>
|
||||
>;
|
||||
|
||||
export type SlotComponentMap = Map<string, SlotsMap<string>>;
|
||||
export type SlotsMap<K extends string> = Map<
|
||||
K,
|
||||
Array<{
|
||||
component: React.FC;
|
||||
id: string;
|
||||
}>
|
||||
>;
|
||||
|
||||
export type CallbackMap<K extends string> = Record<K, () => void>;
|
||||
|
||||
export type SubscribeMethods<U> = (map: {
|
||||
@ -87,7 +74,9 @@ export type ComponentImplementationProps<
|
||||
KEvent extends string
|
||||
> = ImplWrapperProps<KSlot> &
|
||||
TraitResult<KStyleSlot, KEvent>['props'] &
|
||||
RuntimeFunctions<TState, TMethods>;
|
||||
RuntimeFunctions<TState, TMethods> & {
|
||||
Slot: SlotType<KSlot>;
|
||||
};
|
||||
|
||||
export type TraitResult<KStyleSlot extends string, KEvent extends string> = {
|
||||
props: {
|
||||
|
Loading…
Reference in New Issue
Block a user