@kestack/kabuki 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +156 -33
- package/dist/core/actor/actor.d.ts +24 -13
- package/dist/core/actor/actor.d.ts.map +1 -1
- package/dist/core/actor/actor.js +37 -26
- package/dist/core/actor/index.d.ts +2 -2
- package/dist/core/actor/index.d.ts.map +1 -1
- package/dist/core/actor/types.d.ts +91 -47
- package/dist/core/actor/types.d.ts.map +1 -1
- package/dist/core/history/history.d.ts +11 -3
- package/dist/core/history/history.d.ts.map +1 -1
- package/dist/core/history/history.js +106 -27
- package/dist/core/history/replay.d.ts +2 -0
- package/dist/core/history/replay.d.ts.map +1 -1
- package/dist/core/history/replay.js +1 -0
- package/dist/core/history/saved.d.ts +1 -1
- package/dist/core/history/saved.d.ts.map +1 -1
- package/dist/core/history/saved.js +13 -9
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/snapshot/invoke.d.ts +2 -3
- package/dist/core/snapshot/invoke.d.ts.map +1 -1
- package/dist/core/snapshot/invoke.js +42 -49
- package/dist/core/snapshot/restore.js +8 -7
- package/dist/core/snapshot/tree.d.ts +1 -1
- package/dist/core/snapshot/tree.d.ts.map +1 -1
- package/dist/core/snapshot/tree.js +1 -1
- package/dist/react/app.d.ts +30 -3
- package/dist/react/app.d.ts.map +1 -1
- package/dist/react/app.js +119 -40
- package/dist/react/drawn.d.ts +1 -1
- package/dist/react/drawn.d.ts.map +1 -1
- package/dist/react/drawn.js +2 -1
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +2 -0
- package/dist/react/lazy.d.ts +30 -0
- package/dist/react/lazy.d.ts.map +1 -0
- package/dist/react/lazy.js +56 -0
- package/dist/react/live.d.ts +7 -0
- package/dist/react/live.d.ts.map +1 -0
- package/dist/react/live.js +13 -0
- package/dist/react/view.d.ts +26 -6
- package/dist/react/view.d.ts.map +1 -1
- package/dist/react/view.js +17 -5
- package/dist/routing/adapter/adapter.d.ts +38 -0
- package/dist/routing/adapter/adapter.d.ts.map +1 -0
- package/dist/routing/adapter/adapter.js +10 -0
- package/dist/routing/adapter/browser/browser.d.ts +8 -0
- package/dist/routing/adapter/browser/browser.d.ts.map +1 -0
- package/dist/routing/adapter/browser/browser.js +33 -0
- package/dist/routing/adapter/browser/index.d.ts +2 -0
- package/dist/routing/adapter/browser/index.d.ts.map +1 -0
- package/dist/routing/adapter/browser/index.js +1 -0
- package/dist/routing/adapter/index.d.ts +3 -0
- package/dist/routing/adapter/index.d.ts.map +1 -0
- package/dist/routing/adapter/index.js +1 -0
- package/dist/routing/adapter/tanstack/index.d.ts +3 -0
- package/dist/routing/adapter/tanstack/index.d.ts.map +1 -0
- package/dist/routing/adapter/tanstack/index.js +1 -0
- package/dist/routing/adapter/tanstack/tanstack.d.ts +27 -0
- package/dist/routing/adapter/tanstack/tanstack.d.ts.map +1 -0
- package/dist/routing/adapter/tanstack/tanstack.js +38 -0
- package/dist/routing/capability/cache.d.ts +12 -0
- package/dist/routing/capability/cache.d.ts.map +1 -0
- package/dist/routing/capability/cache.js +170 -0
- package/dist/routing/capability/index.d.ts +6 -0
- package/dist/routing/capability/index.d.ts.map +1 -0
- package/dist/routing/capability/index.js +3 -0
- package/dist/routing/capability/loading.d.ts +82 -0
- package/dist/routing/capability/loading.d.ts.map +1 -0
- package/dist/routing/capability/loading.js +53 -0
- package/dist/routing/capability/navigation.d.ts +66 -0
- package/dist/routing/capability/navigation.d.ts.map +1 -0
- package/dist/routing/capability/navigation.js +65 -0
- package/dist/routing/index.d.ts +10 -0
- package/dist/routing/index.d.ts.map +1 -0
- package/dist/routing/index.js +4 -0
- package/dist/routing/page/index.d.ts +5 -0
- package/dist/routing/page/index.d.ts.map +1 -0
- package/dist/routing/page/index.js +2 -0
- package/dist/routing/page/page.d.ts +151 -0
- package/dist/routing/page/page.d.ts.map +1 -0
- package/dist/routing/page/page.js +121 -0
- package/dist/routing/page/status.d.ts +121 -0
- package/dist/routing/page/status.d.ts.map +1 -0
- package/dist/routing/page/status.js +69 -0
- package/dist/routing/route/index.d.ts +11 -0
- package/dist/routing/route/index.d.ts.map +1 -0
- package/dist/routing/route/index.js +6 -0
- package/dist/routing/route/loader.d.ts +98 -0
- package/dist/routing/route/loader.d.ts.map +1 -0
- package/dist/routing/route/loader.js +17 -0
- package/dist/routing/route/metadata.d.ts +44 -0
- package/dist/routing/route/metadata.d.ts.map +1 -0
- package/dist/routing/route/metadata.js +53 -0
- package/dist/routing/route/node.d.ts +162 -0
- package/dist/routing/route/node.d.ts.map +1 -0
- package/dist/routing/route/node.js +3 -0
- package/dist/routing/route/places.d.ts +15 -0
- package/dist/routing/route/places.d.ts.map +1 -0
- package/dist/routing/route/places.js +87 -0
- package/dist/routing/route/route.d.ts +53 -0
- package/dist/routing/route/route.d.ts.map +1 -0
- package/dist/routing/route/route.js +148 -0
- package/dist/routing/route/url.d.ts +13 -0
- package/dist/routing/route/url.d.ts.map +1 -0
- package/dist/routing/route/url.js +79 -0
- package/dist/routing/route/values.d.ts +12 -0
- package/dist/routing/route/values.d.ts.map +1 -0
- package/dist/routing/route/values.js +47 -0
- package/dist/routing/router/context.d.ts +123 -0
- package/dist/routing/router/context.d.ts.map +1 -0
- package/dist/routing/router/context.js +156 -0
- package/dist/routing/router/data.d.ts +117 -0
- package/dist/routing/router/data.d.ts.map +1 -0
- package/dist/routing/router/data.js +247 -0
- package/dist/routing/router/define.d.ts +52 -0
- package/dist/routing/router/define.d.ts.map +1 -0
- package/dist/routing/router/define.js +48 -0
- package/dist/routing/router/frame.d.ts +10 -0
- package/dist/routing/router/frame.d.ts.map +1 -0
- package/dist/routing/router/frame.js +10 -0
- package/dist/routing/router/index.d.ts +7 -0
- package/dist/routing/router/index.d.ts.map +1 -0
- package/dist/routing/router/index.js +3 -0
- package/dist/routing/router/router.d.ts +165 -0
- package/dist/routing/router/router.d.ts.map +1 -0
- package/dist/routing/router/router.js +801 -0
- package/dist/routing/routing.d.ts +72 -0
- package/dist/routing/routing.d.ts.map +1 -0
- package/dist/routing/routing.js +53 -0
- package/dist/routing/scroll/index.d.ts +4 -0
- package/dist/routing/scroll/index.d.ts.map +1 -0
- package/dist/routing/scroll/index.js +2 -0
- package/dist/routing/scroll/script.d.ts +6 -0
- package/dist/routing/scroll/script.d.ts.map +1 -0
- package/dist/routing/scroll/script.js +52 -0
- package/dist/routing/scroll/scroll.d.ts +75 -0
- package/dist/routing/scroll/scroll.d.ts.map +1 -0
- package/dist/routing/scroll/scroll.js +201 -0
- package/dist/routing/scroll/selector.d.ts +14 -0
- package/dist/routing/scroll/selector.d.ts.map +1 -0
- package/dist/routing/scroll/selector.js +40 -0
- package/dist/routing/scroll/session.d.ts +6 -0
- package/dist/routing/scroll/session.d.ts.map +1 -0
- package/dist/routing/scroll/session.js +23 -0
- package/dist/start/client/client.d.ts +11 -0
- package/dist/start/client/client.d.ts.map +1 -0
- package/dist/start/client/client.js +24 -0
- package/dist/start/client/index.d.ts +3 -0
- package/dist/start/client/index.d.ts.map +1 -0
- package/dist/start/client/index.js +2 -0
- package/dist/start/client/rpc.d.ts +14 -0
- package/dist/start/client/rpc.d.ts.map +1 -0
- package/dist/start/client/rpc.js +70 -0
- package/dist/start/common/app.d.ts +66 -0
- package/dist/start/common/app.d.ts.map +1 -0
- package/dist/start/common/app.js +23 -0
- package/dist/start/common/common.d.ts +26 -0
- package/dist/start/common/common.d.ts.map +1 -0
- package/dist/start/common/common.js +19 -0
- package/dist/start/common/document.d.ts +44 -0
- package/dist/start/common/document.d.ts.map +1 -0
- package/dist/start/common/document.js +58 -0
- package/dist/start/common/index.d.ts +9 -0
- package/dist/start/common/index.d.ts.map +1 -0
- package/dist/start/common/index.js +5 -0
- package/dist/start/common/page.d.ts +67 -0
- package/dist/start/common/page.d.ts.map +1 -0
- package/dist/start/common/page.js +29 -0
- package/dist/start/common/wire.d.ts +37 -0
- package/dist/start/common/wire.d.ts.map +1 -0
- package/dist/start/common/wire.js +73 -0
- package/dist/start/platform/cloudflare/cloudflare.d.ts +8 -0
- package/dist/start/platform/cloudflare/cloudflare.d.ts.map +1 -0
- package/dist/start/platform/cloudflare/cloudflare.js +31 -0
- package/dist/start/platform/cloudflare/index.d.ts +2 -0
- package/dist/start/platform/cloudflare/index.d.ts.map +1 -0
- package/dist/start/platform/cloudflare/index.js +1 -0
- package/dist/start/platform/index.d.ts +2 -0
- package/dist/start/platform/index.d.ts.map +1 -0
- package/dist/start/platform/index.js +1 -0
- package/dist/start/platform/platform.d.ts +31 -0
- package/dist/start/platform/platform.d.ts.map +1 -0
- package/dist/start/platform/platform.js +13 -0
- package/dist/start/server/cache.d.ts +4 -0
- package/dist/start/server/cache.d.ts.map +1 -0
- package/dist/start/server/cache.js +26 -0
- package/dist/start/server/draw.d.ts +10 -0
- package/dist/start/server/draw.d.ts.map +1 -0
- package/dist/start/server/draw.js +75 -0
- package/dist/start/server/index.d.ts +3 -0
- package/dist/start/server/index.d.ts.map +1 -0
- package/dist/start/server/index.js +2 -0
- package/dist/start/server/layers.d.ts +97 -0
- package/dist/start/server/layers.d.ts.map +1 -0
- package/dist/start/server/layers.js +37 -0
- package/dist/start/server/respond.d.ts +15 -0
- package/dist/start/server/respond.d.ts.map +1 -0
- package/dist/start/server/respond.js +35 -0
- package/dist/start/server/rpc.d.ts +11 -0
- package/dist/start/server/rpc.d.ts.map +1 -0
- package/dist/start/server/rpc.js +51 -0
- package/dist/start/server/server.d.ts +13 -0
- package/dist/start/server/server.d.ts.map +1 -0
- package/dist/start/server/server.js +138 -0
- package/dist/start/server/tiers.d.ts +14 -0
- package/dist/start/server/tiers.d.ts.map +1 -0
- package/dist/start/server/tiers.js +31 -0
- package/dist/start/vite/dev.d.ts +5 -0
- package/dist/start/vite/dev.d.ts.map +1 -0
- package/dist/start/vite/dev.js +56 -0
- package/dist/start/vite/entries.d.ts +27 -0
- package/dist/start/vite/entries.d.ts.map +1 -0
- package/dist/start/vite/entries.js +66 -0
- package/dist/start/vite/files.d.ts +22 -0
- package/dist/start/vite/files.d.ts.map +1 -0
- package/dist/start/vite/files.js +23 -0
- package/dist/start/vite/index.d.ts +2 -0
- package/dist/start/vite/index.d.ts.map +1 -0
- package/dist/start/vite/index.js +1 -0
- package/dist/start/vite/lazy.d.ts +4 -0
- package/dist/start/vite/lazy.d.ts.map +1 -0
- package/dist/start/vite/lazy.js +30 -0
- package/dist/start/vite/manifest.d.ts +18 -0
- package/dist/start/vite/manifest.d.ts.map +1 -0
- package/dist/start/vite/manifest.js +42 -0
- package/dist/start/vite/styles.d.ts +7 -0
- package/dist/start/vite/styles.d.ts.map +1 -0
- package/dist/start/vite/styles.js +58 -0
- package/dist/start/vite/vite.d.ts +11 -0
- package/dist/start/vite/vite.d.ts.map +1 -0
- package/dist/start/vite/vite.js +217 -0
- package/package.json +53 -3
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { back, backTo, cached, forward, memory, preload, push, reload, replace } from './capability/index.ts';
|
|
2
|
+
import type { AnyRoute } from './capability/index.ts';
|
|
3
|
+
import { NotFoundError, RedirectError } from './route/index.ts';
|
|
4
|
+
import type { RedirectTo } from './route/index.ts';
|
|
5
|
+
import { Link, Retry, app, define } from './router/index.ts';
|
|
6
|
+
/** A Loader's failure: the place it was asked for is not found. */
|
|
7
|
+
declare const notFound: () => NotFoundError;
|
|
8
|
+
/** How a Redirect is sent: `permanent` is a `308` on the server rather than a `302`. */
|
|
9
|
+
interface RedirectOptions {
|
|
10
|
+
readonly permanent?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/** A URL outside the app, whole: `https://accounts.example.com/sign-in`. */
|
|
13
|
+
type Href = `http://${string}` | `https://${string}`;
|
|
14
|
+
/**
|
|
15
|
+
* A Loader's failure: the app goes to `route` instead, in place of the move
|
|
16
|
+
* that asked; or, given a whole URL, leaves for it. Either replaces the entry.
|
|
17
|
+
*/
|
|
18
|
+
declare function redirect<R extends AnyRoute>(route: R, options?: RedirectOptions): RedirectTo<R>;
|
|
19
|
+
declare function redirect(href: Href, options?: RedirectOptions): RedirectTo<never>;
|
|
20
|
+
/**
|
|
21
|
+
* Pages: `Page.define` says what one is, an Actor with a place in the URL,
|
|
22
|
+
* and the pages under it; `Page.build` says how it behaves, with the build
|
|
23
|
+
* of each page under it. A page is drawn with `View.make`, as any Actor is.
|
|
24
|
+
* A Loader fails with `Page.notFound()` and `Page.redirect(route or URL)`,
|
|
25
|
+
* whose Schemas are `Page.NotFound` and `Page.Redirect`.
|
|
26
|
+
*/
|
|
27
|
+
export declare const Page: {
|
|
28
|
+
define: <const P extends import("./route/node.ts").Fields = {}, const Q extends import("./route/node.ts").Fields = {}, D = never, M extends import("./index.ts").RenderMode | undefined = undefined, const C extends {
|
|
29
|
+
readonly [name: string]: import("./page/page.ts").AnyPageDefinition;
|
|
30
|
+
} = {}, Model = {}, State extends import("../core/index.ts").Tagged = {
|
|
31
|
+
readonly _tag: 'Single';
|
|
32
|
+
}, Msg extends import("../core/index.ts").Tagged = never, const R extends import("../core/index.ts").Requires = readonly []>(options: import("./page/page.ts").PageOptions<P, Q, D, M, C, Model, State, Msg, R>) => import("./index.ts").PageDefinition<P, Q, C, D, M, Model, State, Msg, R>;
|
|
33
|
+
build: {
|
|
34
|
+
<D extends import("./page/page.ts").AnyPageDefinition>(definition: D & ({} extends import("./page/page.ts").PageBehaviorOf<D, {}> ? unknown : never)): import("./page/page.ts").BuiltPage<D, {}>;
|
|
35
|
+
<D extends import("./page/page.ts").AnyPageDefinition, const B extends import("./page/page.ts").PageBehaviorOf<D, B>>(definition: D, behavior: B): import("./page/page.ts").BuiltPage<D, B>;
|
|
36
|
+
};
|
|
37
|
+
notFound: typeof notFound;
|
|
38
|
+
redirect: typeof redirect;
|
|
39
|
+
NotFound: typeof NotFoundError;
|
|
40
|
+
Redirect: typeof RedirectError;
|
|
41
|
+
};
|
|
42
|
+
export declare namespace Page {
|
|
43
|
+
/** A Loader's failure: the place is not found. */
|
|
44
|
+
type NotFound = NotFoundError;
|
|
45
|
+
/** A Loader's failure: go to another Route instead, of type `R` if said, or out of the app. */
|
|
46
|
+
type Redirect<R = any> = RedirectTo<R>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The router: `Router.define` reads an app's router down from its root
|
|
50
|
+
* page's definition; `Router.app` puts it above the root page's View, as
|
|
51
|
+
* `Start.app` does; `Link` and `Retry` draw inside it; `push`, `replace`,
|
|
52
|
+
* `back`, `forward`, `backTo`, `reload` and `preload` are Commands an
|
|
53
|
+
* Update returns; `memory` is a `Loading` that runs the Loaders in place,
|
|
54
|
+
* and `cached` puts the Data cache in front of one.
|
|
55
|
+
*/
|
|
56
|
+
export declare const Router: {
|
|
57
|
+
define: typeof define;
|
|
58
|
+
app: typeof app;
|
|
59
|
+
Link: typeof Link;
|
|
60
|
+
Retry: typeof Retry;
|
|
61
|
+
memory: typeof memory;
|
|
62
|
+
cached: typeof cached;
|
|
63
|
+
push: typeof push;
|
|
64
|
+
replace: typeof replace;
|
|
65
|
+
back: typeof back;
|
|
66
|
+
forward: typeof forward;
|
|
67
|
+
backTo: typeof backTo;
|
|
68
|
+
reload: typeof reload;
|
|
69
|
+
preload: typeof preload;
|
|
70
|
+
};
|
|
71
|
+
export {};
|
|
72
|
+
//# sourceMappingURL=routing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/routing/routing.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,MAAM,EACN,MAAM,EACN,OAAO,EACP,MAAM,EACN,OAAO,EACP,IAAI,EACJ,MAAM,EACN,OAAO,EACR,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE7D,mEAAmE;AACnE,QAAA,MAAM,QAAQ,qBAA4B,CAAC;AAE3C,wFAAwF;AACxF,UAAU,eAAe;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,4EAA4E;AAC5E,KAAK,IAAI,GAAG,UAAU,MAAM,EAAE,GAAG,WAAW,MAAM,EAAE,CAAC;AAErD;;;GAGG;AACH,iBAAS,QAAQ,CAAC,CAAC,SAAS,QAAQ,EAClC,KAAK,EAAE,CAAC,EACR,OAAO,CAAC,EAAE,eAAe,GACxB,UAAU,CAAC,CAAC,CAAC,CAAC;AACjB,iBAAS,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;AAa5E;;;;;;GAMG;AACH,eAAO,MAAM,IAAI;;;;;;;;;;;;;;CAOhB,CAAC;AAEF,MAAM,CAAC,OAAO,WAAW,IAAI,CAAC;IAC5B,kDAAkD;IAClD,KAAY,QAAQ,GAAG,aAAa,CAAC;IACrC,+FAA+F;IAC/F,KAAY,QAAQ,CAAC,CAAC,GAAG,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;CAC/C;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;CAclB,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { back, backTo, cached, forward, memory, preload, push, reload, replace, } from './capability/index.js';
|
|
2
|
+
import { Page as Pages } from './page/index.js';
|
|
3
|
+
import { NotFoundError, RedirectError } from './route/index.js';
|
|
4
|
+
import { Link, Retry, app, define } from './router/index.js';
|
|
5
|
+
/** A Loader's failure: the place it was asked for is not found. */
|
|
6
|
+
const notFound = () => new NotFoundError();
|
|
7
|
+
function redirect(to, options = {}) {
|
|
8
|
+
// A cast string could name a `javascript:` URL, which the browser would run.
|
|
9
|
+
if (typeof to === 'string' && !/^https?:\/\//i.test(to))
|
|
10
|
+
throw new Error(`[@kestack/kabuki/routing] a Redirect leaves only for an http or https URL, not ${to}`);
|
|
11
|
+
return new RedirectError({
|
|
12
|
+
...(typeof to === 'string' ? { href: to } : { route: to }),
|
|
13
|
+
...(options.permanent ? { permanent: true } : {}),
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Pages: `Page.define` says what one is, an Actor with a place in the URL,
|
|
18
|
+
* and the pages under it; `Page.build` says how it behaves, with the build
|
|
19
|
+
* of each page under it. A page is drawn with `View.make`, as any Actor is.
|
|
20
|
+
* A Loader fails with `Page.notFound()` and `Page.redirect(route or URL)`,
|
|
21
|
+
* whose Schemas are `Page.NotFound` and `Page.Redirect`.
|
|
22
|
+
*/
|
|
23
|
+
export const Page = {
|
|
24
|
+
define: Pages.define,
|
|
25
|
+
build: Pages.build,
|
|
26
|
+
notFound,
|
|
27
|
+
redirect,
|
|
28
|
+
NotFound: NotFoundError,
|
|
29
|
+
Redirect: RedirectError,
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* The router: `Router.define` reads an app's router down from its root
|
|
33
|
+
* page's definition; `Router.app` puts it above the root page's View, as
|
|
34
|
+
* `Start.app` does; `Link` and `Retry` draw inside it; `push`, `replace`,
|
|
35
|
+
* `back`, `forward`, `backTo`, `reload` and `preload` are Commands an
|
|
36
|
+
* Update returns; `memory` is a `Loading` that runs the Loaders in place,
|
|
37
|
+
* and `cached` puts the Data cache in front of one.
|
|
38
|
+
*/
|
|
39
|
+
export const Router = {
|
|
40
|
+
define,
|
|
41
|
+
app,
|
|
42
|
+
Link,
|
|
43
|
+
Retry,
|
|
44
|
+
memory,
|
|
45
|
+
cached,
|
|
46
|
+
push,
|
|
47
|
+
replace,
|
|
48
|
+
back,
|
|
49
|
+
forward,
|
|
50
|
+
backTo,
|
|
51
|
+
reload,
|
|
52
|
+
preload,
|
|
53
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { Scrolling, keyOf, restores, useElementScrollRestoration, } from './scroll.tsx';
|
|
2
|
+
export type { HashScroll, ScrollLocation, ScrollRestorationOptions, ScrollingProps, } from './scroll.tsx';
|
|
3
|
+
export { scriptFor } from './script.ts';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routing/scroll/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,KAAK,EACL,QAAQ,EACR,2BAA2B,GAC5B,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,UAAU,EACV,cAAc,EACd,wBAAwB,EACxB,cAAc,GACf,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"script.d.ts","sourceRoot":"","sources":["../../../src/routing/scroll/script.ts"],"names":[],"mappings":"AAoDA;;;GAGG;AACH,eAAO,MAAM,SAAS,UAAW,MAAM,QAAQ,MAAM,WACkD,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { STORAGE } from './session.js';
|
|
2
|
+
/*
|
|
3
|
+
* The script a server writes at the end of the page, run before the app
|
|
4
|
+
* hydrates, as TanStack Start's is: it puts the window and every scrolled
|
|
5
|
+
* element back where the entry left them, so a reload never paints at the
|
|
6
|
+
* top first. With nothing saved, it goes to the hash's element, or the top.
|
|
7
|
+
* It runs as a string in the page, so it uses nothing from outside itself.
|
|
8
|
+
*/
|
|
9
|
+
function restore(storage, state, key) {
|
|
10
|
+
let byKey;
|
|
11
|
+
try {
|
|
12
|
+
byKey = JSON.parse(sessionStorage.getItem(storage) || '{}');
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const entries = byKey?.[key || history.state?.[state] || ''];
|
|
18
|
+
let windowRestored = false;
|
|
19
|
+
for (const selector in entries) {
|
|
20
|
+
const { scrollX, scrollY } = entries[selector] ?? {};
|
|
21
|
+
if (!Number.isFinite(scrollX) || !Number.isFinite(scrollY))
|
|
22
|
+
continue;
|
|
23
|
+
if (selector === 'window') {
|
|
24
|
+
scrollTo(scrollX, scrollY);
|
|
25
|
+
windowRestored = true;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
try {
|
|
29
|
+
const element = document.querySelector(selector);
|
|
30
|
+
if (element) {
|
|
31
|
+
element.scrollLeft = scrollX;
|
|
32
|
+
element.scrollTop = scrollY;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch { }
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (windowRestored)
|
|
39
|
+
return;
|
|
40
|
+
const hash = location.hash.slice(1);
|
|
41
|
+
if (hash) {
|
|
42
|
+
document.getElementById(decodeURIComponent(hash))?.scrollIntoView();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
scrollTo(0, 0);
|
|
46
|
+
}
|
|
47
|
+
const safe = (value) => JSON.stringify(value).replace(/</g, '\\u003c');
|
|
48
|
+
/**
|
|
49
|
+
* The inline script for a page: it reads the key the Router stamped on the
|
|
50
|
+
* history entry, under `state`, unless `key` names another.
|
|
51
|
+
*/
|
|
52
|
+
export const scriptFor = (state, key) => `(${restore.toString()})(${safe(STORAGE)},${safe(state)}${key === undefined ? '' : `,${safe(key)}`})`;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { Position } from './selector.ts';
|
|
3
|
+
/** How an element is scrolled into view, as `Element.scrollIntoView` takes it. */
|
|
4
|
+
export type HashScroll = boolean | {
|
|
5
|
+
readonly behavior?: 'auto' | 'instant' | 'smooth';
|
|
6
|
+
readonly block?: 'start' | 'center' | 'end' | 'nearest';
|
|
7
|
+
readonly inline?: 'start' | 'center' | 'end' | 'nearest';
|
|
8
|
+
};
|
|
9
|
+
/** Where an app is, as scroll restoration reads it. */
|
|
10
|
+
export interface ScrollLocation<Route = {
|
|
11
|
+
readonly _tag: string;
|
|
12
|
+
}> {
|
|
13
|
+
readonly route: Route;
|
|
14
|
+
/** Its path and query, as the address writes it. */
|
|
15
|
+
readonly path: string;
|
|
16
|
+
/** The key of its history entry. */
|
|
17
|
+
readonly key: string;
|
|
18
|
+
}
|
|
19
|
+
/** Scroll restoration's options on `Router.app`, named as TanStack Router's. */
|
|
20
|
+
export interface ScrollRestorationOptions<Route = {
|
|
21
|
+
readonly _tag: string;
|
|
22
|
+
}> {
|
|
23
|
+
/** Whether it restores scroll at all, or where: off unless said. */
|
|
24
|
+
readonly scrollRestoration?: boolean | ((options: {
|
|
25
|
+
readonly route: Route;
|
|
26
|
+
}) => boolean);
|
|
27
|
+
/** What positions are kept under: the entry's key unless said, its `path` to keep one per URL. */
|
|
28
|
+
readonly getScrollRestorationKey?: (location: ScrollLocation<Route>) => string;
|
|
29
|
+
/** Elements, by selector or function, that go to the top on a move to a new entry rather than keep where they were. */
|
|
30
|
+
readonly scrollToTopSelectors?: ReadonlyArray<string | (() => Element | null | undefined)>;
|
|
31
|
+
/** How it scrolls: `instant`, `smooth` or `auto`. */
|
|
32
|
+
readonly scrollRestorationBehavior?: 'auto' | 'instant' | 'smooth';
|
|
33
|
+
/** How a hash's element is scrolled into view when a move does not say: `true` unless said. */
|
|
34
|
+
readonly defaultHashScrollIntoView?: HashScroll;
|
|
35
|
+
}
|
|
36
|
+
/** What the Router hands Scrolling: its options, and the move drawn. */
|
|
37
|
+
export interface ScrollingProps {
|
|
38
|
+
readonly options: ScrollRestorationOptions<never>;
|
|
39
|
+
/** Where the app is drawn. */
|
|
40
|
+
readonly at: ScrollLocation;
|
|
41
|
+
/** The element on the page its entry goes to. */
|
|
42
|
+
readonly hash: string | undefined;
|
|
43
|
+
readonly move: {
|
|
44
|
+
readonly how: 'load' | 'push' | 'replace' | 'back' | 'forward';
|
|
45
|
+
readonly resetScroll?: boolean | undefined;
|
|
46
|
+
readonly hashScrollIntoView?: HashScroll | undefined;
|
|
47
|
+
};
|
|
48
|
+
/** Whether a move is held: the entry may already be the one it goes to. */
|
|
49
|
+
readonly pending: boolean;
|
|
50
|
+
/** Whether an Adapter holds the address: positions are saved for a reload. */
|
|
51
|
+
readonly attached: boolean;
|
|
52
|
+
/** Whether the Adapter has said which entry the app opened at: before, its key is not known. */
|
|
53
|
+
readonly loaded: boolean;
|
|
54
|
+
readonly children: ReactNode;
|
|
55
|
+
}
|
|
56
|
+
/** Whether scroll restoration is on at a Route. */
|
|
57
|
+
export declare const restores: (options: ScrollRestorationOptions<never>, route: unknown) => boolean;
|
|
58
|
+
/** The restoration key of a location. */
|
|
59
|
+
export declare const keyOf: (options: ScrollRestorationOptions<never>, at: ScrollLocation) => string;
|
|
60
|
+
/**
|
|
61
|
+
* Around the app, inside the Router: tracks what scrolls, and puts it back
|
|
62
|
+
* as every move lands.
|
|
63
|
+
*/
|
|
64
|
+
export declare function Scrolling(props: ScrollingProps): import("react").JSX.Element;
|
|
65
|
+
/**
|
|
66
|
+
* Where an element, or the window, was scrolled on the entry drawn, as kept
|
|
67
|
+
* for it: for a list that restores itself, as a virtualized one does. Name
|
|
68
|
+
* it by its `data-scroll-restoration-id`, or hand it over.
|
|
69
|
+
*/
|
|
70
|
+
export declare const useElementScrollRestoration: (options: {
|
|
71
|
+
readonly id: string;
|
|
72
|
+
} | {
|
|
73
|
+
readonly getElement: () => Element | Window | null | undefined;
|
|
74
|
+
}) => Position | undefined;
|
|
75
|
+
//# sourceMappingURL=scroll.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scroll.d.ts","sourceRoot":"","sources":["../../../src/routing/scroll/scroll.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, use, useLayoutEffect, useRef, useState } from 'react';
|
|
3
|
+
import { useLive } from '../../react/index.js';
|
|
4
|
+
import { ID, WINDOW, elementOf, selectorOf } from './selector.js';
|
|
5
|
+
import * as session from './session.js';
|
|
6
|
+
/** Whether scroll restoration is on at a Route. */
|
|
7
|
+
export const restores = (options, route) => typeof options.scrollRestoration === 'function'
|
|
8
|
+
? options.scrollRestoration({ route })
|
|
9
|
+
: options.scrollRestoration === true;
|
|
10
|
+
/** The restoration key of a location. */
|
|
11
|
+
export const keyOf = (options, at) => options.getScrollRestorationKey?.(at) ?? at.key;
|
|
12
|
+
const FrameContext = createContext(undefined);
|
|
13
|
+
/**
|
|
14
|
+
* Around the app, inside the Router: tracks what scrolls, and puts it back
|
|
15
|
+
* as every move lands.
|
|
16
|
+
*/
|
|
17
|
+
export function Scrolling(props) {
|
|
18
|
+
const live = useLive();
|
|
19
|
+
const on = restores(props.options, props.at.route);
|
|
20
|
+
const key = keyOf(props.options, props.at);
|
|
21
|
+
const latest = useRef({ props, live, on });
|
|
22
|
+
latest.current = { props, live, on };
|
|
23
|
+
const [s] = useState(() => ({
|
|
24
|
+
positions: undefined,
|
|
25
|
+
/** The restoration key drawn: through a held move, still the one before it. */
|
|
26
|
+
key,
|
|
27
|
+
/** Our own scrolling, which is not the user's. */
|
|
28
|
+
ignore: false,
|
|
29
|
+
/** The move last restored for, as one string. */
|
|
30
|
+
last: undefined,
|
|
31
|
+
}));
|
|
32
|
+
const positions = () => (s.positions ??= latest.current.props.attached ? session.read() : {});
|
|
33
|
+
const save = () => {
|
|
34
|
+
if (latest.current.props.attached && s.positions)
|
|
35
|
+
session.write(s.positions);
|
|
36
|
+
};
|
|
37
|
+
const [frame] = useState(() => ({
|
|
38
|
+
entry: (name) => positions()[s.key]?.[name],
|
|
39
|
+
}));
|
|
40
|
+
// 1. Track, and 4. save.
|
|
41
|
+
useLayoutEffect(() => {
|
|
42
|
+
const scrolled = (event) => {
|
|
43
|
+
if (s.ignore || !latest.current.live || !latest.current.on)
|
|
44
|
+
return;
|
|
45
|
+
const target = event.target;
|
|
46
|
+
const kept = (positions()[s.key] ??= {});
|
|
47
|
+
if (target === document || target === document.documentElement)
|
|
48
|
+
kept[WINDOW] = { scrollX, scrollY };
|
|
49
|
+
else if (target instanceof Element && target.isConnected)
|
|
50
|
+
kept[selectorOf(target)] = {
|
|
51
|
+
scrollX: target.scrollLeft,
|
|
52
|
+
scrollY: target.scrollTop,
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
const hidden = () => {
|
|
56
|
+
// The browser may put a reload back too; ours runs from what is saved.
|
|
57
|
+
history.scrollRestoration = 'auto';
|
|
58
|
+
save();
|
|
59
|
+
};
|
|
60
|
+
const shown = (event) => {
|
|
61
|
+
if (event.persisted && latest.current.on)
|
|
62
|
+
history.scrollRestoration = 'manual';
|
|
63
|
+
};
|
|
64
|
+
document.addEventListener('scroll', scrolled, {
|
|
65
|
+
capture: true,
|
|
66
|
+
passive: true,
|
|
67
|
+
});
|
|
68
|
+
addEventListener('pagehide', hidden);
|
|
69
|
+
addEventListener('pageshow', shown);
|
|
70
|
+
return () => {
|
|
71
|
+
save();
|
|
72
|
+
document.removeEventListener('scroll', scrolled, { capture: true });
|
|
73
|
+
removeEventListener('pagehide', hidden);
|
|
74
|
+
removeEventListener('pageshow', shown);
|
|
75
|
+
};
|
|
76
|
+
// oxlint-disable-next-line react-hooks/exhaustive-deps -- set up once; it reads the latest through refs.
|
|
77
|
+
}, []);
|
|
78
|
+
// Kabuki puts scroll back, not the browser, while it is on.
|
|
79
|
+
const manual = on && props.attached;
|
|
80
|
+
useLayoutEffect(() => {
|
|
81
|
+
if (!manual || !('scrollRestoration' in history))
|
|
82
|
+
return;
|
|
83
|
+
const before = history.scrollRestoration;
|
|
84
|
+
history.scrollRestoration = 'manual';
|
|
85
|
+
return () => {
|
|
86
|
+
history.scrollRestoration = before;
|
|
87
|
+
};
|
|
88
|
+
}, [manual]);
|
|
89
|
+
// 2. Carry, and 3. restore, after every move.
|
|
90
|
+
const settled = props.move.how !== 'load' || props.loaded;
|
|
91
|
+
const move = `${key} ${props.move.how} ${props.hash ?? ''} ${props.at.path}`;
|
|
92
|
+
useLayoutEffect(() => {
|
|
93
|
+
if (!live || props.pending || !settled)
|
|
94
|
+
return;
|
|
95
|
+
const from = s.key;
|
|
96
|
+
s.key = key;
|
|
97
|
+
if (s.last === move)
|
|
98
|
+
return;
|
|
99
|
+
s.last = move;
|
|
100
|
+
if (!on)
|
|
101
|
+
return;
|
|
102
|
+
const options = latest.current.props.options;
|
|
103
|
+
const { how } = props.move;
|
|
104
|
+
const reset = props.move.resetScroll !== false;
|
|
105
|
+
const hashing = how === 'push' || how === 'replace';
|
|
106
|
+
const hashScroll = props.move.hashScrollIntoView ??
|
|
107
|
+
options.defaultHashScrollIntoView ??
|
|
108
|
+
true;
|
|
109
|
+
const kept = positions();
|
|
110
|
+
let toTop;
|
|
111
|
+
const toTopElements = () => (toTop ??= new Set((options.scrollToTopSelectors ?? [])
|
|
112
|
+
.filter((selector) => selector !== WINDOW)
|
|
113
|
+
.map(elementOf)
|
|
114
|
+
.filter((element) => element !== undefined)));
|
|
115
|
+
// 2. Carry
|
|
116
|
+
const before = kept[from];
|
|
117
|
+
if (before && from !== key) {
|
|
118
|
+
const after = (kept[key] ??= {});
|
|
119
|
+
for (const name in before) {
|
|
120
|
+
if (name === WINDOW) {
|
|
121
|
+
if (reset)
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
const element = elementOf(name);
|
|
126
|
+
if (!element)
|
|
127
|
+
continue;
|
|
128
|
+
if (reset && toTopElements().has(element))
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
after[name] ??= before[name];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// 3. Restore
|
|
135
|
+
{
|
|
136
|
+
const behavior = options.scrollRestorationBehavior;
|
|
137
|
+
const hash = props.hash;
|
|
138
|
+
s.ignore = true;
|
|
139
|
+
try {
|
|
140
|
+
let windowRestored = false;
|
|
141
|
+
if (reset) {
|
|
142
|
+
const skipWindow = Boolean(hash && hashScroll && hashing);
|
|
143
|
+
const tops = hash ? undefined : new Set(toTopElements());
|
|
144
|
+
for (const name in kept[key] ?? {}) {
|
|
145
|
+
const { scrollX: left, scrollY: top } = kept[key][name];
|
|
146
|
+
if (name === WINDOW) {
|
|
147
|
+
if (skipWindow)
|
|
148
|
+
continue;
|
|
149
|
+
scrollTo({ top, left, ...(behavior ? { behavior } : {}) });
|
|
150
|
+
windowRestored = true;
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
const element = elementOf(name);
|
|
154
|
+
if (!element)
|
|
155
|
+
continue;
|
|
156
|
+
element.scrollLeft = left;
|
|
157
|
+
element.scrollTop = top;
|
|
158
|
+
tops?.delete(element);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (!hash) {
|
|
162
|
+
const origin = {
|
|
163
|
+
top: 0,
|
|
164
|
+
left: 0,
|
|
165
|
+
...(behavior ? { behavior } : {}),
|
|
166
|
+
};
|
|
167
|
+
if (!windowRestored)
|
|
168
|
+
scrollTo(origin);
|
|
169
|
+
for (const element of tops ?? [])
|
|
170
|
+
element.scrollTo(origin);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (!windowRestored && hash && hashScroll)
|
|
174
|
+
document
|
|
175
|
+
.getElementById(hash)
|
|
176
|
+
?.scrollIntoView(hashScroll === true ? undefined : hashScroll);
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
s.ignore = false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// oxlint-disable-next-line react-hooks/exhaustive-deps -- `move` is the move, as one string.
|
|
183
|
+
}, [move, props.pending, live, settled, on]);
|
|
184
|
+
return _jsx(FrameContext, { value: frame, children: props.children });
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Where an element, or the window, was scrolled on the entry drawn, as kept
|
|
188
|
+
* for it: for a list that restores itself, as a virtualized one does. Name
|
|
189
|
+
* it by its `data-scroll-restoration-id`, or hand it over.
|
|
190
|
+
*/
|
|
191
|
+
export const useElementScrollRestoration = (options) => {
|
|
192
|
+
const frame = use(FrameContext);
|
|
193
|
+
if (!frame)
|
|
194
|
+
return undefined;
|
|
195
|
+
if ('id' in options)
|
|
196
|
+
return frame.entry(`[${ID}="${options.id}"]`);
|
|
197
|
+
const element = options.getElement();
|
|
198
|
+
if (!element)
|
|
199
|
+
return undefined;
|
|
200
|
+
return frame.entry(element === window ? WINDOW : selectorOf(element));
|
|
201
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const WINDOW = "window";
|
|
2
|
+
export declare const ID = "data-scroll-restoration-id";
|
|
3
|
+
/** Where an element or the window was scrolled to. */
|
|
4
|
+
export interface Position {
|
|
5
|
+
readonly scrollX: number;
|
|
6
|
+
readonly scrollY: number;
|
|
7
|
+
}
|
|
8
|
+
/** Every saved position: by restoration key, then by element. */
|
|
9
|
+
export type Positions = Record<string, Record<string, Position>>;
|
|
10
|
+
/** An element's name: `[data-scroll-restoration-id="list"]`, or `main:nth-child(2) > ul:nth-child(1)`. */
|
|
11
|
+
export declare const selectorOf: (element: Element) => string;
|
|
12
|
+
/** The element a selector, or a function, names now; none if it is gone. */
|
|
13
|
+
export declare const elementOf: (selector: string | (() => Element | null | undefined)) => Element | undefined;
|
|
14
|
+
//# sourceMappingURL=selector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selector.d.ts","sourceRoot":"","sources":["../../../src/routing/scroll/selector.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,MAAM,WAAW,CAAC;AAC/B,eAAO,MAAM,EAAE,+BAA+B,CAAC;AAE/C,sDAAsD;AACtD,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,iEAAiE;AACjE,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;AAEjE,0GAA0G;AAC1G,eAAO,MAAM,UAAU,YAAa,OAAO,KAAG,MAgB7C,CAAC;AAEF,4EAA4E;AAC5E,eAAO,MAAM,SAAS,aACV,MAAM,GAAG,CAAC,MAAM,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,KACpD,OAAO,GAAG,SAUZ,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* How a scrolled element is named in the saved positions, as TanStack
|
|
3
|
+
* Router names it, so its inline script and the Router agree: by its
|
|
4
|
+
* `data-scroll-restoration-id`, else by its path from the document, each
|
|
5
|
+
* step its tag and its place among its siblings. The window is `window`.
|
|
6
|
+
*/
|
|
7
|
+
export const WINDOW = 'window';
|
|
8
|
+
export const ID = 'data-scroll-restoration-id';
|
|
9
|
+
/** An element's name: `[data-scroll-restoration-id="list"]`, or `main:nth-child(2) > ul:nth-child(1)`. */
|
|
10
|
+
export const selectorOf = (element) => {
|
|
11
|
+
const id = element.getAttribute(ID);
|
|
12
|
+
if (id)
|
|
13
|
+
return `[${ID}="${id}"]`;
|
|
14
|
+
let selector = '';
|
|
15
|
+
let at = element;
|
|
16
|
+
let parent;
|
|
17
|
+
while ((parent = at.parentNode)) {
|
|
18
|
+
let index = 1;
|
|
19
|
+
let sibling = at;
|
|
20
|
+
while ((sibling = sibling.previousElementSibling))
|
|
21
|
+
index++;
|
|
22
|
+
const part = `${at.localName}:nth-child(${index})`;
|
|
23
|
+
selector = selector ? `${part} > ${selector}` : part;
|
|
24
|
+
if (!(parent instanceof Element))
|
|
25
|
+
break;
|
|
26
|
+
at = parent;
|
|
27
|
+
}
|
|
28
|
+
return selector;
|
|
29
|
+
};
|
|
30
|
+
/** The element a selector, or a function, names now; none if it is gone. */
|
|
31
|
+
export const elementOf = (selector) => {
|
|
32
|
+
try {
|
|
33
|
+
return ((typeof selector === 'function'
|
|
34
|
+
? selector()
|
|
35
|
+
: document.querySelector(selector)) ?? undefined);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Positions } from './selector.ts';
|
|
2
|
+
export declare const STORAGE = "@kestack/kabuki/routing:scroll";
|
|
3
|
+
/** What the last page load in this tab saved, or nothing. */
|
|
4
|
+
export declare const read: () => Positions;
|
|
5
|
+
export declare const write: (positions: Positions) => void;
|
|
6
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/routing/scroll/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAS/C,eAAO,MAAM,OAAO,mCAAmC,CAAC;AAExD,6DAA6D;AAC7D,eAAO,MAAM,IAAI,QAAO,SASvB,CAAC;AAEF,eAAO,MAAM,KAAK,cAAe,SAAS,SAIzC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The browser's session storage, for the positions to outlast a reload:
|
|
3
|
+
* one tab's own, copied when the tab is duplicated, empty in a new one.
|
|
4
|
+
* Read once, written when the page is hidden. Storage that is full, or
|
|
5
|
+
* missing, keeps nothing.
|
|
6
|
+
*/
|
|
7
|
+
export const STORAGE = '@kestack/kabuki/routing:scroll';
|
|
8
|
+
/** What the last page load in this tab saved, or nothing. */
|
|
9
|
+
export const read = () => {
|
|
10
|
+
try {
|
|
11
|
+
const saved = JSON.parse(globalThis.sessionStorage?.getItem(STORAGE) || '{}');
|
|
12
|
+
return saved && typeof saved === 'object' ? saved : {};
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
export const write = (positions) => {
|
|
19
|
+
try {
|
|
20
|
+
globalThis.sessionStorage?.setItem(STORAGE, JSON.stringify(positions));
|
|
21
|
+
}
|
|
22
|
+
catch { }
|
|
23
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Root } from 'react-dom/client';
|
|
2
|
+
import type { StartApp } from '../common/index.ts';
|
|
3
|
+
/** The page once hydrated: its React root, and how to draw it again with new code. */
|
|
4
|
+
export interface Hydrated {
|
|
5
|
+
readonly root: Root;
|
|
6
|
+
/** Draw the page again with new code, as after a hot reload: the app carries on from its History. */
|
|
7
|
+
readonly redraw: (app: StartApp) => void;
|
|
8
|
+
}
|
|
9
|
+
/** Hydrates the page a server drew. */
|
|
10
|
+
export declare const client: (exported: StartApp) => Promise<Hydrated>;
|
|
11
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/start/client/client.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAc7C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAmBnD,sFAAsF;AACtF,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,qGAAqG;IACrG,QAAQ,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,IAAI,CAAC;CAC1C;AAMD,uCAAuC;AACvC,eAAO,MAAM,MAAM,aAAoB,QAAQ,KAAG,OAAO,CAAC,QAAQ,CAkCjE,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { hydrateRoot } from 'react-dom/client';
|
|
3
|
+
import { Layer } from 'effect';
|
|
4
|
+
import { View, toReact } from '../../react/index.js';
|
|
5
|
+
import { Router } from '../../routing/index.js';
|
|
6
|
+
import { browser } from '../../routing/adapter/browser/index.js';
|
|
7
|
+
import { Document as Plain, PageContext, RuntimeContext, appOf, readPage, } from '../common/index.js';
|
|
8
|
+
import { loading } from './rpc.js';
|
|
9
|
+
/** Hydrates the page a server drew. */
|
|
10
|
+
export const client = async (exported) => {
|
|
11
|
+
// 1. Read
|
|
12
|
+
const drawn = readPage(document);
|
|
13
|
+
// 2. Preload
|
|
14
|
+
await View.preload(Array.from(document.querySelectorAll('link[data-kabuki-view]'), (link) => link.getAttribute('data-kabuki-view')));
|
|
15
|
+
const page = (next) => {
|
|
16
|
+
const { app, document: Document = Plain, layer } = appOf(next);
|
|
17
|
+
const App = toReact(app.actor, app.view, Layer.mergeAll(browser(), Router.cached(app.routes, loading(app.routes)), layer ?? Layer.empty));
|
|
18
|
+
return (_jsx(PageContext, { value: drawn, children: _jsx(RuntimeContext, { value: App.useRuntime, children: _jsx(Document, { children: _jsx(App, { saved: drawn.page.saved }) }) }) }));
|
|
19
|
+
};
|
|
20
|
+
// 3. Hydrate
|
|
21
|
+
const root = hydrateRoot(document, page(exported));
|
|
22
|
+
// 4. Redraw
|
|
23
|
+
return { root, redraw: (next) => root.render(page(next)) };
|
|
24
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/start/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC"}
|