misc update on router

1. update example page's title
2. pass params to ImplWrapper(currently not pass it to the component, need discussion on it)
This commit is contained in:
Sczlog 2021-08-16 10:34:27 +08:00
parent 619e86e526
commit 5bfc886b31
6 changed files with 151 additions and 133 deletions

View File

@ -6,7 +6,7 @@
<script> <script>
delete window.history; delete window.history;
</script> </script>
<title>meta-ui runtime example: nested components</title> <title>meta-ui runtime example: hash router</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>meta-ui runtime example: nested components</title> <title>meta-ui runtime example: nested hash router</title>
<script> <script>
delete window.history; delete window.history;
</script> </script>

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>meta-ui runtime example: nested components</title> <title>meta-ui runtime example: history router</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>meta-ui runtime example: nested components</title> <title>meta-ui runtime example: nested router</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@ -30,146 +30,155 @@ const ImplWrapper = React.forwardRef<
routerMap: RouterComponentMap | undefined; routerMap: RouterComponentMap | undefined;
targetSlot: { id: string; slot: string } | null; targetSlot: { id: string; slot: string } | null;
app: RuntimeApplication; app: RuntimeApplication;
[key: string]: any;
} }
>(({ component: c, slotsMap, routerMap, targetSlot, app, ...props }, ref) => { >(
// TODO: find better way to add barrier (
if (!stateStore[c.id]) { { component: c, slotsMap, routerMap, targetSlot, app, children, ...props },
stateStore[c.id] = {}; ref
} ) => {
// TODO: find better way to add barrier
if (!stateStore[c.id]) {
stateStore[c.id] = {};
}
const Impl = registry.getComponent(c.parsedType.version, c.parsedType.name) const Impl = registry.getComponent(c.parsedType.version, c.parsedType.name)
.impl; .impl;
const handlerMap = useRef<Record<string, (parameters?: any) => void>>({}); const handlerMap = useRef<Record<string, (parameters?: any) => void>>({});
useEffect(() => { useEffect(() => {
const handler = (s: { const handler = (s: {
componentId: string; componentId: string;
name: string; name: string;
parameters?: any; parameters?: any;
}) => { }) => {
if (s.componentId !== c.id) { if (s.componentId !== c.id) {
return; return;
} }
if (!handlerMap.current[s.name]) { if (!handlerMap.current[s.name]) {
// maybe log? // maybe log?
return; return;
} }
handlerMap.current[s.name](s.parameters); handlerMap.current[s.name](s.parameters);
}; };
apiService.on("uiMethod", handler); apiService.on("uiMethod", handler);
return () => { return () => {
apiService.off("uiMethod", handler); apiService.off("uiMethod", handler);
}; };
}, []); }, []);
const mergeState = useCallback( const mergeState = useCallback(
(partial: any) => { (partial: any) => {
stateStore[c.id] = { ...stateStore[c.id], ...partial }; stateStore[c.id] = { ...stateStore[c.id], ...partial };
}, },
[c.id] [c.id]
); );
const subscribeMethods = useCallback( const subscribeMethods = useCallback(
(map: any) => { (map: any) => {
handlerMap.current = merge(handlerMap.current, map); handlerMap.current = merge(handlerMap.current, map);
}, },
[handlerMap.current] [handlerMap.current]
); );
// traits // traits
const [traitPropertiesMap, setTraitPropertiesMap] = useState< const [traitPropertiesMap, setTraitPropertiesMap] = useState<
Map<typeof c["traits"][0], object> Map<typeof c["traits"][0], object>
>( >(
c.traits.reduce((prev, cur) => { c.traits.reduce((prev, cur) => {
prev.set(cur, deepEval(cur.properties).result); prev.set(cur, deepEval(cur.properties).result);
return prev; return prev;
}, new Map()) }, new Map())
); );
useEffect(() => { useEffect(() => {
const stops: ReturnType<typeof watch>[] = []; const stops: ReturnType<typeof watch>[] = [];
for (const t of c.traits) { for (const t of c.traits) {
const { stop, result } = deepEval(t.properties, ({ result }) => { const { stop, result } = deepEval(t.properties, ({ result }) => {
setTraitPropertiesMap(
new Map(traitPropertiesMap.set(t, { ...result }))
);
});
setTraitPropertiesMap( setTraitPropertiesMap(
new Map(traitPropertiesMap.set(t, { ...result })) new Map(traitPropertiesMap.set(t, { ...result }))
); );
stops.push(stop);
}
return () => stops.forEach((s) => s());
}, []);
const traitsProps = {};
const wrappers: React.FC[] = [];
for (const t of c.traits) {
const tImpl = registry.getTrait(t.parsedType.version, t.parsedType.name)
.impl;
const { props: tProps, component: Wrapper } = tImpl({
...traitPropertiesMap.get(t),
mergeState,
subscribeMethods,
}); });
setTraitPropertiesMap(new Map(traitPropertiesMap.set(t, { ...result }))); merge(traitsProps, tProps);
stops.push(stop); if (Wrapper) {
wrappers.push(Wrapper);
}
} }
return () => stops.forEach((s) => s());
}, []);
const traitsProps = {}; const [mergedProps, setMergedProps] = useState(
const wrappers: React.FC[] = []; deepEval({
for (const t of c.traits) { ...c.properties,
const tImpl = registry.getTrait(t.parsedType.version, t.parsedType.name) ...traitsProps,
.impl; }).result
const { props: tProps, component: Wrapper } = tImpl({ );
...traitPropertiesMap.get(t), useEffect(() => {
mergeState, const rawProps: Record<string, unknown> = {
subscribeMethods, ...c.properties,
}); ...traitsProps,
merge(traitsProps, tProps); };
if (Wrapper) {
wrappers.push(Wrapper);
}
}
const [mergedProps, setMergedProps] = useState( const { stop, result } = deepEval(rawProps, ({ result }) => {
deepEval({ setMergedProps({ ...result });
...c.properties, });
...traitsProps,
}).result
);
useEffect(() => {
const rawProps: Record<string, unknown> = {
...c.properties,
...traitsProps,
};
const { stop, result } = deepEval(rawProps, ({ result }) => {
setMergedProps({ ...result }); setMergedProps({ ...result });
}); return stop;
}, []);
setMergedProps({ ...result }); let C = (
return stop; <Impl
}, []); key={c.id}
{...mergedProps}
{...props}
mergeState={mergeState}
subscribeMethods={subscribeMethods}
slotsMap={slotsMap}
routerMap={routerMap}
/>
);
let C = ( while (wrappers.length) {
<Impl const W = wrappers.pop()!;
key={c.id} C = <W>{C}</W>;
{...mergedProps}
mergeState={mergeState}
subscribeMethods={subscribeMethods}
slotsMap={slotsMap}
routerMap={routerMap}
/>
);
while (wrappers.length) {
const W = wrappers.pop()!;
C = <W>{C}</W>;
}
if (targetSlot) {
const targetC = app.spec.components.find((c) => c.id === targetSlot.id);
if (targetC?.parsedType.name === "grid_layout") {
return (
<div key={c.id} data-meta-ui-id={c.id} ref={ref} {...props}>
{C}
{props.children}
</div>
);
} }
}
return ( if (targetSlot) {
<React.Fragment key={c.id}> const targetC = app.spec.components.find((c) => c.id === targetSlot.id);
{C} if (targetC?.parsedType.name === "grid_layout") {
{props.children} return (
</React.Fragment> <div key={c.id} data-meta-ui-id={c.id} ref={ref} {...props}>
); {C}
}); {children}
</div>
);
}
}
return (
<React.Fragment key={c.id}>
{C}
{children}
</React.Fragment>
);
}
);
const DebugStore: React.FC = () => { const DebugStore: React.FC = () => {
const [store, setStore] = useState(stateStore); const [store, setStore] = useState(stateStore);

View File

@ -34,7 +34,6 @@ const useHashLocation = ({ base = "" } = {}): [
const [loc, setLoc] = useState(currentLocation(base)); const [loc, setLoc] = useState(currentLocation(base));
const navigate = useCallback( const navigate = useCallback(
(to: string) => { (to: string) => {
console.log(base);
window.location.hash = base + to; window.location.hash = base + to;
}, },
[base] [base]
@ -73,7 +72,7 @@ export const RouterProvider: React.FC<{
); );
}; };
// all route-like component must have path property, router use path to determined if a component match the route // all route-like component must have path property, wouter use path to determined if a component match the route
// but we need path to match both nested router itself and its child route, so we need to have an alternative property called base as the real path of the nested router // but we need path to match both nested router itself and its child route, so we need to have an alternative property called base as the real path of the nested router
// and used by its children // and used by its children
const NestedWouter: React.FC<{ path: string; base: string }> = ({ const NestedWouter: React.FC<{ path: string; base: string }> = ({
@ -108,9 +107,8 @@ const NestedWouter: React.FC<{ path: string; base: string }> = ({
const Router: ComponentImplementation<{ const Router: ComponentImplementation<{
routerPolicy: Static<typeof RouterPolicyPropertySchema>; routerPolicy: Static<typeof RouterPolicyPropertySchema>;
}> = ({ routerMap, routerPolicy, subscribeMethods }) => { }> = ({ routerMap, routerPolicy, subscribeMethods, mergeState }) => {
const [, naviagte] = useLocation(); const [location, naviagte] = useLocation();
const router = useRouter();
const routes = useMemo(() => { const routes = useMemo(() => {
let defaultPath: string | undefined = undefined; let defaultPath: string | undefined = undefined;
@ -134,7 +132,7 @@ const Router: ComponentImplementation<{
} }
if (C.displayName === "router") { if (C.displayName === "router") {
return ( return (
// it should match both itself and its children path, so we need to match its path itself // it should match both itself and its children path
<NestedWouter <NestedWouter
path={`(${path}|${path}/.*)`} path={`(${path}|${path}/.*)`}
base={path} base={path}
@ -146,7 +144,9 @@ const Router: ComponentImplementation<{
} }
return ( return (
<Woute key={path} path={path}> <Woute key={path} path={path}>
<C key={cid}></C> {(params) => {
return <C {...params} key={cid}></C>;
}}
</Woute> </Woute>
); );
default: default:
@ -163,7 +163,8 @@ const Router: ComponentImplementation<{
); );
} }
return result; return result;
}, []); }, [routerPolicy]);
useEffect(() => { useEffect(() => {
subscribeMethods({ subscribeMethods({
navigate: (path: string) => { navigate: (path: string) => {
@ -171,6 +172,14 @@ const Router: ComponentImplementation<{
}, },
}); });
}, []); }, []);
useEffect(() => {
// to assign location as a state
mergeState({
route: location,
});
}, [location]);
return <Switch children={routes}></Switch>; return <Switch children={routes}></Switch>;
}; };