Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
atellmer committed Dec 29, 2022
1 parent 5d4753e commit f506e39
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 51 deletions.
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@dark-engine/core",
"version": "0.16.1",
"version": "0.17.0",
"description": "Dark is lightweight component-and-hook-based UI rendering engine for javascript apps without dependencies and written in TypeScript 💫",
"author": "AlexPlex",
"license": "MIT",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import type { MutableRef } from '../ref';
function useImperativeHandle<T>(ref: MutableRef<T>, createHandle: () => T, deps?: Array<any>) {
const current = useMemo(() => createHandle(), deps || [{}]);

ref.current = current;
if (ref) {
ref.current = current;
}
}

export { useImperativeHandle };
2 changes: 1 addition & 1 deletion packages/platform-browser/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@dark-engine/platform-browser",
"version": "0.16.1",
"version": "0.17.0",
"description": "Dark is lightweight component-and-hook-based UI rendering engine for javascript apps without dependencies and written in TypeScript 💫",
"author": "AlexPlex",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/platform-server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@dark-engine/platform-server",
"version": "0.16.1",
"version": "0.17.0",
"description": "Dark is lightweight component-and-hook-based UI rendering engine for javascript apps without dependencies and written in TypeScript 💫",
"author": "AlexPlex",
"license": "MIT",
Expand Down
22 changes: 22 additions & 0 deletions packages/web-router/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,3 +293,25 @@ const App = createComponent(({ url }) => {
```

Full example SSR routing you can see in examples.

## Imperative access to router

```tsx
const App = createComponent<AppProps>(({ url, routes }) => {
const ref = useRef<RouterRef>(null);

useEffect(() => {
setTimeout(() => {
ref.current.navigateTo('/about');
});
}, []);

return (
<Router ref={ref} routes={routes}>
{slot => slot}
</Router>
);
});
```


2 changes: 1 addition & 1 deletion packages/web-router/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@dark-engine/web-router",
"version": "0.16.1",
"version": "0.17.0",
"description": "Dark is lightweight component-and-hook-based UI rendering engine for javascript apps without dependencies and written in TypeScript 💫",
"author": "AlexPlex",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/web-router/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { type Routes } from './create-routes';
export { Router } from './router';
export { type RouterRef, Router } from './router';
export { RouterLink } from './router-link';
export { useLocation } from './use-location';
export { useHistory } from './use-history';
Expand Down
113 changes: 68 additions & 45 deletions packages/web-router/src/router/router.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { type DarkElement, h, createComponent, useMemo, useEffect, useLayoutEffect, useState } from '@dark-engine/core';
import {
type DarkElement,
type MutableRef,
h,
createComponent,
useMemo,
useEffect,
useLayoutEffect,
useState,
forwardRef,
useImperativeHandle,
} from '@dark-engine/core';

import { SLASH, PROTOCOL_MARK } from '../constants';
import { normalaizePathname } from '../utils';
import { createRouterHistory } from '../history';
import { createRouterLocation } from '../location';
import { type RouterLocation, createRouterLocation } from '../location';
import { type Routes, createRoutes, renderRoot, pathnameFromPath } from '../create-routes';
import {
type RouterHistoryContextValue,
Expand All @@ -20,56 +31,68 @@ export type RouterProps = {
slot: (slot: DarkElement) => DarkElement;
};

const Router = createComponent<RouterProps>(({ url, baseURL = SLASH, routes: sourceRoutes, slot }) => {
if (useActiveRouteContext()) {
throw new Error('[web-router]: Parent active route context detected!');
}
const sourceURL = url || window.location.href;
const [location, setLocation] = useState(() => createRouterLocation(sourceURL));
const history = useMemo(() => createRouterHistory(sourceURL), []);
const routes = useMemo(() => createRoutes(sourceRoutes, normalaizePathname(baseURL)), []);
const { protocol, host, pathname, search } = location;
const { matched, params, rendered } = renderRoot(pathname, routes);
const scope = useMemo(() => ({ location }), []);
const historyContext = useMemo<RouterHistoryContextValue>(() => ({ history }), []);
const routerContext = useMemo<ActiveRouteContextValue>(() => ({ location, matched, params }), [pathname, search]);

scope.location = location;
export type RouterRef = {
navigateTo: (pathname: string) => void;
location: RouterLocation;
};

useLayoutEffect(() => {
if (sourceURL !== scope.location.url) {
setLocation(createRouterLocation(sourceURL));
const Router = forwardRef<RouterProps, RouterRef>(
createComponent(({ url, baseURL = SLASH, routes: sourceRoutes, slot }, ref) => {
if (useActiveRouteContext()) {
throw new Error('[web-router]: Parent active route context detected!');
}
}, [sourceURL]);
const sourceURL = url || window.location.href;
const [location, setLocation] = useState(() => createRouterLocation(sourceURL));
const history = useMemo(() => createRouterHistory(sourceURL), []);
const routes = useMemo(() => createRoutes(sourceRoutes, normalaizePathname(baseURL)), []);
const { protocol, host, pathname, search } = location;
const { matched, params, rendered } = renderRoot(pathname, routes);
const scope = useMemo(() => ({ location }), []);
const historyContext = useMemo<RouterHistoryContextValue>(() => ({ history }), []);
const routerContext = useMemo<ActiveRouteContextValue>(() => ({ location, matched, params }), [pathname, search]);

useLayoutEffect(() => {
const unsubscribe = history.subscribe(spathname => {
const url = `${protocol}${PROTOCOL_MARK}${host}${spathname}`;
scope.location = location;

setLocation(createRouterLocation(url));
});
useLayoutEffect(() => {
if (sourceURL !== scope.location.url) {
setLocation(createRouterLocation(sourceURL));
}
}, [sourceURL]);

return () => {
unsubscribe();
history.dispose();
};
}, []);
useLayoutEffect(() => {
const unsubscribe = history.subscribe(spathname => {
const url = `${protocol}${PROTOCOL_MARK}${host}${spathname}`;

useEffect(() => {
if (!matched) return;
const spathname = pathname + search;
const newSpathname = pathnameFromPath(pathname, matched.cursor.fullPath) + search;
setLocation(createRouterLocation(url));
});

if (spathname !== newSpathname) {
history.replace(newSpathname);
}
}, [pathname, search]);
return () => {
unsubscribe();
history.dispose();
};
}, []);

useEffect(() => {
if (!matched) return;
const spathname = pathname + search;
const newSpathname = pathnameFromPath(pathname, matched.cursor.fullPath) + search;

if (spathname !== newSpathname) {
history.replace(newSpathname);
}
}, [pathname, search]);

useImperativeHandle(ref as MutableRef<RouterRef>, () => ({
navigateTo: (pathname: string) => history.push(pathname),
location,
}));

return (
<RouterHistoryContext.Provider value={historyContext}>
<ActiveRouteContext.Provider value={routerContext}>{slot(rendered)}</ActiveRouteContext.Provider>
</RouterHistoryContext.Provider>
);
});
return (
<RouterHistoryContext.Provider value={historyContext}>
<ActiveRouteContext.Provider value={routerContext}>{slot(rendered)}</ActiveRouteContext.Provider>
</RouterHistoryContext.Provider>
);
}),
);

export { Router };

0 comments on commit f506e39

Please sign in to comment.