@ssgoi/solid 7.4.1 → 7.5.0-beta.0

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 CHANGED
@@ -12,15 +12,52 @@ npm install @ssgoi/solid
12
12
 
13
13
  Agent setup guide: https://ssgoi.dev/llms/solid.txt
14
14
 
15
- ## Router helpers
15
+ ## Layout boundary
16
16
 
17
- Install this framework package once; router helpers are optional subpaths.
18
- These router helpers are experimental APIs and may change.
17
+ Pass a reactive route getter to `<Ssgoi>` once (inside `<Router>`, see [Root](#root)),
18
+ then wrap routed content in `SsgoiLayoutBoundary`.
19
19
 
20
- | Router | Import | API |
21
- | ------------ | --------------------------- | -------------------- |
22
- | Solid Router | `@ssgoi/solid/solid-router` | `SsgoiRouteBoundary` |
23
- | SolidStart | `@ssgoi/solid/solidstart` | `SsgoiRouteBoundary` |
20
+ ```tsx
21
+ import { SsgoiLayoutBoundary } from "@ssgoi/solid";
22
+
23
+ export default function ProductsLayout(props) {
24
+ return (
25
+ <SsgoiLayoutBoundary as="section">
26
+ <div>{props.children}</div>
27
+ </SsgoiLayoutBoundary>
28
+ );
29
+ }
30
+ ```
31
+
32
+ Each boundary marks the current path with `data-ssgoi-transition`. Nest them
33
+ freely: depth is automatic. Declare which paths share a layout in
34
+ `config.layouts`; the scope matched at a boundary's depth keeps its DOM, and
35
+ past the declared scopes a boundary remounts on every path change (the default
36
+ without `layouts`).
37
+
38
+ ```ts
39
+ const TOP_LEVEL = ["/home", "/search", "/profile"];
40
+ const config = {
41
+ layouts: [
42
+ { paths: TOP_LEVEL }, // tabs share one depth-0 layout
43
+ {
44
+ paths: "/**",
45
+ except: TOP_LEVEL,
46
+ layouts: [{ paths: "/products/**" }], // depth-1 scope inside details
47
+ },
48
+ ],
49
+ transitions: [/* ... */],
50
+ };
51
+ ```
52
+
53
+ The first matching scope at each level wins. Single-segment captures (`*`,
54
+ `:id`, `[id]`) split instances, so `/projects/:id/**` keeps one layout per
55
+ project; `**` tails do not. `SsgoiLayoutBoundary` takes no SSGOI props (`as`
56
+ sets the element) and throws without `framework` on `<Ssgoi>`.
57
+
58
+ `SsgoiRouteBoundary` from `@ssgoi/solid/solid-router` and
59
+ `@ssgoi/solid/solidstart` is deprecated; use `SsgoiLayoutBoundary` with
60
+ `framework` instead.
24
61
 
25
62
  See the [framework guide](https://ssgoi.dev/docs/frameworks/solid) for wiring and limits.
26
63
 
@@ -29,7 +66,7 @@ See the [framework guide](https://ssgoi.dev/docs/frameworks/solid) for wiring an
29
66
  Use one `<Ssgoi>` around file routes.
30
67
 
31
68
  ```tsx
32
- import { Router } from "@solidjs/router";
69
+ import { Router, useLocation, type RouteSectionProps } from "@solidjs/router";
33
70
  import { FileRoutes } from "@solidjs/start/router";
34
71
  import { Ssgoi } from "@ssgoi/solid";
35
72
  import { drill } from "@ssgoi/solid/view-transitions";
@@ -38,15 +75,24 @@ const config = {
38
75
  transitions: [{ on: "/posts/**", except: "/posts", transition: drill() }],
39
76
  };
40
77
 
78
+ function Root(props: RouteSectionProps) {
79
+ const location = useLocation();
80
+
81
+ return (
82
+ <main class="relative z-0 min-h-dvh overflow-x-clip">
83
+ <Ssgoi
84
+ config={config}
85
+ framework={{ getRoute: () => ({ pathname: location.pathname }) }}
86
+ >
87
+ {props.children}
88
+ </Ssgoi>
89
+ </main>
90
+ );
91
+ }
92
+
41
93
  export default function App() {
42
94
  return (
43
- <Router
44
- root={(props) => (
45
- <main class="relative z-0 min-h-dvh overflow-x-clip">
46
- <Ssgoi config={config}>{props.children}</Ssgoi>
47
- </main>
48
- )}
49
- >
95
+ <Router root={Root}>
50
96
  <FileRoutes />
51
97
  </Router>
52
98
  );
package/dist/context.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("solid-js"),s=e.createContext(null),r=s.Provider,u=()=>{const t=e.useContext(s);if(!t)throw new Error("useSsgoi must be used within SsgoiProvider");return t},c=()=>{const t=e.useContext(s);if(!t)throw new Error("useSsgoiTransition must be used within <Ssgoi>");const[o,i]=e.createSignal(t.getTransitionState());return e.onCleanup(t.subscribe(n=>i(()=>n))),o};exports.SsgoiProvider=r;exports.useSsgoi=u;exports.useSsgoiTransition=c;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("solid-js"),e=t.createContext(null),s=t.createContext(void 0),i=t.createContext(0),a=e.Provider,c=s.Provider,S=i.Provider,g=()=>t.useContext(s),d=()=>t.useContext(i),v=()=>{const o=t.useContext(e);if(!o)throw new Error("useSsgoi must be used within SsgoiProvider");return o},x=()=>{const o=t.useContext(e);if(!o)throw new Error("useSsgoiTransition must be used within <Ssgoi>");const[n,r]=t.createSignal(o.getTransitionState());return t.onCleanup(o.subscribe(u=>r(()=>u))),n};exports.SsgoiLayoutDepthProvider=S;exports.SsgoiLayoutProvider=c;exports.SsgoiProvider=a;exports.useLayoutDepth=d;exports.useSsgoi=v;exports.useSsgoiLayout=g;exports.useSsgoiTransition=x;
package/dist/context.d.ts CHANGED
@@ -1,6 +1,18 @@
1
1
  import { Accessor } from 'solid-js';
2
2
  import { SsgoiContext, SsgoiTransitionState } from './types';
3
+ /** Route state `<Ssgoi>` resolves once for every layout boundary below it. */
4
+ export interface SsgoiLayoutState {
5
+ routePath: string;
6
+ chain: string[];
7
+ }
3
8
  export declare const SsgoiProvider: import('solid-js').ContextProviderComponent<SsgoiContext | null>;
9
+ export declare const SsgoiLayoutProvider: import('solid-js').ContextProviderComponent<Accessor<SsgoiLayoutState | undefined> | undefined>;
10
+ /** Gives descendants of a layout boundary their depth. */
11
+ export declare const SsgoiLayoutDepthProvider: import('solid-js').ContextProviderComponent<number>;
12
+ /** Layout state of the nearest `<Ssgoi>`; undefined without a framework. */
13
+ export declare const useSsgoiLayout: () => Accessor<SsgoiLayoutState | undefined> | undefined;
14
+ /** Depth of the calling layout boundary: 0 unless inside another. */
15
+ export declare const useLayoutDepth: () => number;
4
16
  export declare const useSsgoi: () => SsgoiContext;
5
17
  /**
6
18
  * Signal of the provider's transition lifecycle: whether a page transition is
package/dist/context.js CHANGED
@@ -1,18 +1,22 @@
1
- import { createContext as i, useContext as o, createSignal as u, onCleanup as c } from "solid-js";
2
- const e = i(null), S = e.Provider, g = () => {
3
- const t = o(e);
1
+ import { createContext as e, useContext as o, createSignal as a, onCleanup as S } from "solid-js";
2
+ const s = e(null), n = e(void 0), i = e(0), d = s.Provider, v = n.Provider, x = i.Provider, h = () => o(n), P = () => o(i), p = () => {
3
+ const t = o(s);
4
4
  if (!t)
5
5
  throw new Error("useSsgoi must be used within SsgoiProvider");
6
6
  return t;
7
7
  }, w = () => {
8
- const t = o(e);
8
+ const t = o(s);
9
9
  if (!t)
10
10
  throw new Error("useSsgoiTransition must be used within <Ssgoi>");
11
- const [s, n] = u(t.getTransitionState());
12
- return c(t.subscribe((r) => n(() => r))), s;
11
+ const [r, u] = a(t.getTransitionState());
12
+ return S(t.subscribe((c) => u(() => c))), r;
13
13
  };
14
14
  export {
15
- S as SsgoiProvider,
16
- g as useSsgoi,
15
+ x as SsgoiLayoutDepthProvider,
16
+ v as SsgoiLayoutProvider,
17
+ d as SsgoiProvider,
18
+ P as useLayoutDepth,
19
+ p as useSsgoi,
20
+ h as useSsgoiLayout,
17
21
  w as useSsgoiTransition
18
22
  };
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("@ssgoi/core/types"),o=require("./ssgoi.cjs"),i=require("./context.cjs"),s=require("./ssgoi-transition.cjs"),r=require("@ssgoi/core");exports.Ssgoi=o.Ssgoi;exports.useSsgoiTransition=i.useSsgoiTransition;exports.SsgoiTransition=s.SsgoiTransition;Object.keys(t).forEach(e=>{e!=="default"&&!Object.prototype.hasOwnProperty.call(exports,e)&&Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})});Object.keys(r).forEach(e=>{e!=="default"&&!Object.prototype.hasOwnProperty.call(exports,e)&&Object.defineProperty(exports,e,{enumerable:!0,get:()=>r[e]})});
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("@ssgoi/core/types"),r=require("./ssgoi.cjs"),i=require("./context.cjs"),s=require("./layout-boundary.cjs"),n=require("./legacy/ssgoi-transition.cjs"),t=require("@ssgoi/core");exports.Ssgoi=r.Ssgoi;exports.useSsgoiTransition=i.useSsgoiTransition;exports.SsgoiLayoutBoundary=s.SsgoiLayoutBoundary;exports.SsgoiTransition=n.SsgoiTransition;Object.keys(o).forEach(e=>{e!=="default"&&!Object.prototype.hasOwnProperty.call(exports,e)&&Object.defineProperty(exports,e,{enumerable:!0,get:()=>o[e]})});Object.keys(t).forEach(e=>{e!=="default"&&!Object.prototype.hasOwnProperty.call(exports,e)&&Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})});
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export * from './types';
2
2
  export { Ssgoi } from './ssgoi';
3
3
  export { useSsgoiTransition } from './context';
4
- /** @deprecated Set `data-ssgoi-transition` directly on an element inside `<Ssgoi>` instead. */
5
- export { SsgoiTransition } from './ssgoi-transition';
4
+ export { SsgoiLayoutBoundary } from './layout-boundary';
5
+ export type { SsgoiLayoutBoundaryProps } from './layout-boundary';
6
+ /** @deprecated Will be removed in 8.0.0. */
7
+ export { SsgoiTransition } from './routers/ssgoi-transition';
6
8
  export * from '@ssgoi/core';
package/dist/index.js CHANGED
@@ -1,10 +1,12 @@
1
1
  export * from "@ssgoi/core/types";
2
- import { Ssgoi as t } from "./ssgoi.js";
2
+ import { Ssgoi as i } from "./ssgoi.js";
3
3
  import { useSsgoiTransition as s } from "./context.js";
4
- import { SsgoiTransition as m } from "./ssgoi-transition.js";
4
+ import { SsgoiLayoutBoundary as m } from "./layout-boundary.js";
5
+ import { SsgoiTransition as x } from "./legacy/ssgoi-transition.js";
5
6
  export * from "@ssgoi/core";
6
7
  export {
7
- t as Ssgoi,
8
- m as SsgoiTransition,
8
+ i as Ssgoi,
9
+ m as SsgoiLayoutBoundary,
10
+ x as SsgoiTransition,
9
11
  s as useSsgoiTransition
10
12
  };
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("solid-js/web"),o=require("solid-js"),u=require("./context.cjs");function l(i){const[e,n]=o.splitProps(i,["children","as","state"]),a=o.createMemo(r=>{const s=e.state().key;return r?.key===s?r:{key:s}});return t.createComponent(o.Show,{get when(){return a()},keyed:!0,children:r=>t.createComponent(t.Dynamic,t.mergeProps({get component(){return e.as??"div"}},n,{get"data-ssgoi-transition"(){return e.state().id},get children(){return e.children}}))})}function d(){throw new Error("SSGOI: SsgoiLayoutBoundary requires a framework. Pass it to <Ssgoi framework={{ getRoute }}>.")}function g(i){const e=u.useSsgoiLayout();e?.()||d();const n=u.useLayoutDepth(),[a,r]=o.splitProps(i,["children"]),s=o.createMemo(()=>{const{routePath:c,chain:y}=e()??d();return{id:c,key:y[n]??c}});return t.createComponent(l,t.mergeProps(r,{state:s,get children(){return t.createComponent(u.SsgoiLayoutDepthProvider,{value:n+1,get children(){return a.children}})}}))}exports.RouteBoundary=l;exports.SsgoiLayoutBoundary=g;
@@ -0,0 +1,12 @@
1
+ import { Accessor, JSX } from 'solid-js';
2
+ export type SsgoiLayoutBoundaryProps = JSX.HTMLAttributes<HTMLElement> & {
3
+ as?: keyof JSX.IntrinsicElements;
4
+ };
5
+ /** Shared rendering; remounts its element whenever `state().key` changes. */
6
+ export declare function RouteBoundary(props: SsgoiLayoutBoundaryProps & {
7
+ state: Accessor<{
8
+ id: string;
9
+ key: string | number;
10
+ }>;
11
+ }): JSX.Element;
12
+ export declare function SsgoiLayoutBoundary(props: SsgoiLayoutBoundaryProps): JSX.Element;
@@ -0,0 +1,61 @@
1
+ import { createComponent as n, mergeProps as c, Dynamic as m } from "solid-js/web";
2
+ import { splitProps as d, createMemo as h, Show as g } from "solid-js";
3
+ import { useSsgoiLayout as y, useLayoutDepth as f, SsgoiLayoutDepthProvider as p } from "./context.js";
4
+ function k(i) {
5
+ const [t, r] = d(i, ["children", "as", "state"]), s = h((e) => {
6
+ const o = t.state().key;
7
+ return e?.key === o ? e : {
8
+ key: o
9
+ };
10
+ });
11
+ return n(g, {
12
+ get when() {
13
+ return s();
14
+ },
15
+ keyed: !0,
16
+ children: (e) => n(m, c({
17
+ get component() {
18
+ return t.as ?? "div";
19
+ }
20
+ }, r, {
21
+ get "data-ssgoi-transition"() {
22
+ return t.state().id;
23
+ },
24
+ get children() {
25
+ return t.children;
26
+ }
27
+ }))
28
+ });
29
+ }
30
+ function u() {
31
+ throw new Error("SSGOI: SsgoiLayoutBoundary requires a framework. Pass it to <Ssgoi framework={{ getRoute }}>.");
32
+ }
33
+ function P(i) {
34
+ const t = y();
35
+ t?.() || u();
36
+ const r = f(), [s, e] = d(i, ["children"]), o = h(() => {
37
+ const {
38
+ routePath: a,
39
+ chain: l
40
+ } = t() ?? u();
41
+ return {
42
+ id: a,
43
+ key: l[r] ?? a
44
+ };
45
+ });
46
+ return n(k, c(e, {
47
+ state: o,
48
+ get children() {
49
+ return n(p, {
50
+ value: r + 1,
51
+ get children() {
52
+ return s.children;
53
+ }
54
+ });
55
+ }
56
+ }));
57
+ }
58
+ export {
59
+ k as RouteBoundary,
60
+ P as SsgoiLayoutBoundary
61
+ };
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("solid-js/web"),a=require("solid-js"),l=require("../context.cjs"),u=t=>{const[e,o]=a.splitProps(t,["children","id","as","class"]),n=l.useSsgoi(),i=e.as||"div";return r.createComponent(r.Dynamic,r.mergeProps({component:i,ref(c){var s=n.refFor(e.id);typeof s=="function"&&s(c)},get"data-ssgoi-transition"(){return e.id},get class(){return e.class}},o,{get children(){return e.children}}))};exports.SsgoiTransition=u;
@@ -1,6 +1,6 @@
1
1
  import { createComponent as c, Dynamic as a, mergeProps as m } from "solid-js/web";
2
2
  import { splitProps as p } from "solid-js";
3
- import { useSsgoi as l } from "./context.js";
3
+ import { useSsgoi as l } from "../context.js";
4
4
  const u = (t) => {
5
5
  const [r, s] = p(t, ["children", "id", "as", "class"]), e = l(), n = r.as || "div";
6
6
  return c(a, m({
@@ -1,8 +1,10 @@
1
1
  import { JSX } from 'solid-js';
2
+ /** @deprecated Will be removed in 8.0.0. Declare layout scopes in `config.layouts` and use `SsgoiLayoutBoundary` from "@ssgoi/solid" instead. */
2
3
  export interface RouteBoundaryState {
3
4
  id: string;
4
5
  key?: string | number;
5
6
  }
7
+ /** @deprecated Will be removed in 8.0.0. Declare layout scopes in `config.layouts` and use `SsgoiLayoutBoundary` from "@ssgoi/solid" instead. */
6
8
  export type SsgoiRouteBoundaryProps = JSX.HTMLAttributes<HTMLElement> & {
7
9
  as?: keyof JSX.IntrinsicElements;
8
10
  routeKey?: string | number;
@@ -10,5 +12,8 @@ export type SsgoiRouteBoundaryProps = JSX.HTMLAttributes<HTMLElement> & {
10
12
  pathname: string;
11
13
  }) => RouteBoundaryState;
12
14
  };
13
- /** @experimental Solid Router integration, including SolidStart. The API may change. */
15
+ /**
16
+ * @experimental Solid Router integration, including SolidStart. The API may change.
17
+ * @deprecated Will be removed in 8.0.0. Use `SsgoiLayoutBoundary` from "@ssgoi/solid" with `<Ssgoi framework={{ getRoute: () => ({ pathname: location.pathname }) }}>` (`useLocation` from "@solidjs/router") instead.
18
+ */
14
19
  export declare function SsgoiRouteBoundary(props: SsgoiRouteBoundaryProps): JSX.Element;
@@ -1,3 +1,10 @@
1
- /** @experimental SolidStart integration through Solid Router. The API may change. */
1
+ /**
2
+ * @experimental SolidStart integration through Solid Router. The API may change.
3
+ * @deprecated Will be removed in 8.0.0. Use `SsgoiLayoutBoundary` from "@ssgoi/solid" with `<Ssgoi framework={{ getRoute: () => ({ pathname: location.pathname }) }}>` (`useLocation` from "@solidjs/router") instead.
4
+ */
2
5
  export { SsgoiRouteBoundary } from './solid-router';
3
- export type { SsgoiRouteBoundaryProps, RouteBoundaryState, } from './solid-router';
6
+ export type {
7
+ /** @deprecated Will be removed in 8.0.0. Declare layout scopes in `config.layouts` and use `SsgoiLayoutBoundary` from "@ssgoi/solid" instead. */
8
+ SsgoiRouteBoundaryProps,
9
+ /** @deprecated Will be removed in 8.0.0. Declare layout scopes in `config.layouts` and use `SsgoiLayoutBoundary` from "@ssgoi/solid" instead. */
10
+ RouteBoundaryState, } from './solid-router';
@@ -1,13 +1,11 @@
1
1
  import { JSX, ValidComponent } from 'solid-js';
2
+ /** @deprecated Will be removed in 8.0.0. */
2
3
  type SsgoiTransitionProps<T extends ValidComponent = "div"> = {
3
4
  children: JSX.Element;
4
5
  id: string;
5
6
  as?: T;
6
7
  class?: string;
7
8
  };
8
- /**
9
- * @deprecated Set `data-ssgoi-transition` directly on the page boundary
10
- * element inside `<Ssgoi>` instead.
11
- */
9
+ /** @deprecated Will be removed in 8.0.0. */
12
10
  export declare const SsgoiTransition: <T extends ValidComponent = "div">(props: SsgoiTransitionProps<T>) => JSX.Element;
13
11
  export {};
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("solid-js/web"),l=require("@solidjs/router"),r=require("solid-js");function m(a){const n=l.useLocation(),[e,u]=r.splitProps(a,["children","as","resolve","routeKey"]),i=r.createMemo(()=>e.resolve?.({pathname:n.pathname})??{id:n.pathname}),d=r.createMemo(o=>{const c=i(),s=e.routeKey??c.key??c.id;return o?.key===s?o:{key:s}});return t.createComponent(r.Show,{get when(){return d()},keyed:!0,children:o=>t.createComponent(t.Dynamic,t.mergeProps({get component(){return e.as??"div"}},u,{get"data-ssgoi-transition"(){return i().id},get children(){return e.children}}))})}exports.SsgoiRouteBoundary=m;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("solid-js/web"),i=require("@solidjs/router"),n=require("solid-js"),c=require("./layout-boundary.cjs");function d(u){const o=i.useLocation(),[t,s]=n.splitProps(u,["resolve","routeKey"]),a=n.createMemo(()=>{const e=t.resolve?.({pathname:o.pathname})??{id:o.pathname};return{id:e.id,key:t.routeKey??e.key??e.id}});return r.createComponent(c.RouteBoundary,r.mergeProps(s,{state:a}))}exports.SsgoiRouteBoundary=d;
@@ -1,2 +1 @@
1
- export * from './routers/solid-router'
2
1
  export {}
@@ -1,36 +1,23 @@
1
- import { createComponent as a, Dynamic as d, mergeProps as l } from "solid-js/web";
2
- import { useLocation as p } from "@solidjs/router";
3
- import { splitProps as h, createMemo as c, Show as y } from "solid-js";
4
- function v(s) {
5
- const r = p(), [e, m] = h(s, ["children", "as", "resolve", "routeKey"]), o = c(() => e.resolve?.({
6
- pathname: r.pathname
7
- }) ?? {
8
- id: r.pathname
9
- }), u = c((t) => {
10
- const n = o(), i = e.routeKey ?? n.key ?? n.id;
11
- return t?.key === i ? t : {
12
- key: i
1
+ import { createComponent as m, mergeProps as i } from "solid-js/web";
2
+ import { useLocation as s } from "@solidjs/router";
3
+ import { splitProps as p, createMemo as c } from "solid-js";
4
+ import { RouteBoundary as u } from "./layout-boundary.js";
5
+ function h(r) {
6
+ const e = s(), [t, n] = p(r, ["resolve", "routeKey"]), a = c(() => {
7
+ const o = t.resolve?.({
8
+ pathname: e.pathname
9
+ }) ?? {
10
+ id: e.pathname
11
+ };
12
+ return {
13
+ id: o.id,
14
+ key: t.routeKey ?? o.key ?? o.id
13
15
  };
14
16
  });
15
- return a(y, {
16
- get when() {
17
- return u();
18
- },
19
- keyed: !0,
20
- children: (t) => a(d, l({
21
- get component() {
22
- return e.as ?? "div";
23
- }
24
- }, m, {
25
- get "data-ssgoi-transition"() {
26
- return o().id;
27
- },
28
- get children() {
29
- return e.children;
30
- }
31
- }))
32
- });
17
+ return m(u, i(n, {
18
+ state: a
19
+ }));
33
20
  }
34
21
  export {
35
- v as SsgoiRouteBoundary
22
+ h as SsgoiRouteBoundary
36
23
  };
@@ -1,2 +1 @@
1
- export * from './routers/solidstart'
2
1
  export {}
package/dist/ssgoi.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("solid-js/web"),n=require("solid-js"),a=require("./context.cjs"),c=require("@ssgoi/core/internal");var l=o.template("<div data-ssgoi-root style=display:contents>");const u=r=>{let t;const i=n.createMemo(()=>c.createSggoiTransitionContext(r.config,{host:r.host}));return n.createEffect(()=>{if(!t)return;const e=c.observeSsgoiTransitions(t,i());n.onCleanup(e)}),o.createComponent(a.SsgoiProvider,{get value(){return i()},get children(){var e=l(),s=t;return typeof s=="function"?o.use(s,e):t=e,o.insert(e,()=>r.children),e}})};exports.Ssgoi=u;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("solid-js/web"),i=require("solid-js"),s=require("./context.cjs"),a=require("@ssgoi/core/internal");var l=r.template("<div data-ssgoi-root style=display:contents>");const g=t=>{let n;const c=i.createMemo(()=>a.createSggoiTransitionContext(t.config,{host:t.host})),u=i.createMemo(()=>{const e=t.framework?.getRoute();if(!e)return;const o=e.renderedPath??e.pathname;return{routePath:o,chain:a.resolveLayoutChain(t.config.layouts,o)}});return i.createEffect(()=>{if(!n)return;const e=a.observeSsgoiTransitions(n,c());i.onCleanup(e)}),r.createComponent(s.SsgoiProvider,{get value(){return c()},get children(){return r.createComponent(s.SsgoiLayoutProvider,{value:u,get children(){var e=l(),o=n;return typeof o=="function"?r.use(o,e):n=e,r.insert(e,()=>t.children),e}})}})};exports.Ssgoi=g;
package/dist/ssgoi.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { JSX } from 'solid-js';
2
- import { SsgoiConfig } from './types';
2
+ import { SsgoiConfig, SsgoiFramework } from './types';
3
3
  import { HostAnimation } from '@ssgoi/core/internal';
4
4
  interface SsgoiProps {
5
5
  config: SsgoiConfig;
6
6
  host?: HostAnimation;
7
+ framework?: SsgoiFramework;
7
8
  children: JSX.Element;
8
9
  }
9
10
  export declare const Ssgoi: (props: SsgoiProps) => JSX.Element;
package/dist/ssgoi.js CHANGED
@@ -1,27 +1,40 @@
1
- import { createComponent as i, insert as a, template as s, use as c } from "solid-js/web";
2
- import { createMemo as l, createEffect as m, onCleanup as f } from "solid-js";
3
- import { SsgoiProvider as u } from "./context.js";
4
- import { createSggoiTransitionContext as g, observeSsgoiTransitions as p } from "@ssgoi/core/internal";
5
- var v = /* @__PURE__ */ s("<div data-ssgoi-root style=display:contents>");
6
- const y = (o) => {
7
- let e;
8
- const r = l(() => g(o.config, {
9
- host: o.host
10
- }));
11
- return m(() => {
12
- if (!e) return;
13
- const t = p(e, r());
14
- f(t);
15
- }), i(u, {
1
+ import { createComponent as i, insert as s, template as u, use as l } from "solid-js/web";
2
+ import { createMemo as a, createEffect as f, onCleanup as m } from "solid-js";
3
+ import { SsgoiProvider as g, SsgoiLayoutProvider as d } from "./context.js";
4
+ import { createSggoiTransitionContext as v, resolveLayoutChain as h, observeSsgoiTransitions as y } from "@ssgoi/core/internal";
5
+ var p = /* @__PURE__ */ u("<div data-ssgoi-root style=display:contents>");
6
+ const _ = (e) => {
7
+ let r;
8
+ const n = a(() => v(e.config, {
9
+ host: e.host
10
+ })), c = a(() => {
11
+ const t = e.framework?.getRoute();
12
+ if (!t) return;
13
+ const o = t.renderedPath ?? t.pathname;
14
+ return {
15
+ routePath: o,
16
+ chain: h(e.config.layouts, o)
17
+ };
18
+ });
19
+ return f(() => {
20
+ if (!r) return;
21
+ const t = y(r, n());
22
+ m(t);
23
+ }), i(g, {
16
24
  get value() {
17
- return r();
25
+ return n();
18
26
  },
19
27
  get children() {
20
- var t = v(), n = e;
21
- return typeof n == "function" ? c(n, t) : e = t, a(t, () => o.children), t;
28
+ return i(d, {
29
+ value: c,
30
+ get children() {
31
+ var t = p(), o = r;
32
+ return typeof o == "function" ? l(o, t) : r = t, s(t, () => e.children), t;
33
+ }
34
+ });
22
35
  }
23
36
  });
24
37
  };
25
38
  export {
26
- y as Ssgoi
39
+ _ as Ssgoi
27
40
  };
package/dist/types.d.ts CHANGED
@@ -1 +1,12 @@
1
1
  export * from '@ssgoi/core/types';
2
+ /**
3
+ * Router bridge for `<Ssgoi framework>`. `getRoute` must be reactive, e.g.
4
+ * `() => ({ pathname: location.pathname })` with `useLocation()` from Solid
5
+ * Router. `renderedPath`, when given, is the path the page content shows.
6
+ */
7
+ export interface SsgoiFramework {
8
+ getRoute: () => {
9
+ pathname: string;
10
+ renderedPath?: string;
11
+ };
12
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ssgoi/solid",
3
- "version": "7.4.1",
3
+ "version": "7.5.0-beta.0",
4
4
  "description": "Native app-like page transitions for Solid and SolidStart. Make a web app, PWA or WebView app feel native.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -38,14 +38,14 @@
38
38
  "default": "./dist/view-transitions.js"
39
39
  },
40
40
  "./solidstart": {
41
- "solid": "./src/lib/routers/solidstart.ts",
41
+ "solid": "./src/lib/legacy/solidstart.ts",
42
42
  "types": "./dist/routers/solidstart.d.ts",
43
43
  "import": "./dist/solidstart.js",
44
44
  "require": "./dist/solidstart.cjs",
45
45
  "default": "./dist/solidstart.js"
46
46
  },
47
47
  "./solid-router": {
48
- "solid": "./src/lib/routers/solid-router.tsx",
48
+ "solid": "./src/lib/legacy/solid-router.tsx",
49
49
  "types": "./dist/routers/solid-router.d.ts",
50
50
  "import": "./dist/solid-router.js",
51
51
  "require": "./dist/solid-router.cjs",
@@ -53,13 +53,15 @@
53
53
  }
54
54
  },
55
55
  "dependencies": {
56
- "@ssgoi/core": "^7.4.1"
56
+ "@ssgoi/core": "^7.5.0-beta.0"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@eslint/js": "^9.30.1",
60
+ "@solidjs/router": "^0.16.1",
60
61
  "@types/node": "^24.10.1",
61
62
  "eslint": "^9.30.1",
62
63
  "globals": "^16.3.0",
64
+ "jsdom": "^26.1.0",
63
65
  "rollup-plugin-preserve-directives": "^0.4.0",
64
66
  "solid-js": "^1.9.13",
65
67
  "typescript": "~5.9.3",
@@ -67,11 +69,11 @@
67
69
  "vite": "^7.2.4",
68
70
  "vite-plugin-dts": "^4.5.4",
69
71
  "vite-plugin-solid": "^2.11.10",
70
- "@solidjs/router": "^0.16.1"
72
+ "vitest": "^3.2.4"
71
73
  },
72
74
  "peerDependencies": {
73
- "solid-js": "^1.0.0",
74
- "@solidjs/router": "*"
75
+ "@solidjs/router": "*",
76
+ "solid-js": "^1.0.0"
75
77
  },
76
78
  "keywords": [
77
79
  "solid",
@@ -127,6 +129,7 @@
127
129
  "watch": "vite build --watch",
128
130
  "lint": "eslint .",
129
131
  "preview": "vite preview",
130
- "package": "vite build"
132
+ "package": "vite build",
133
+ "test:run": "vitest run"
131
134
  }
132
135
  }
@@ -7,9 +7,28 @@ import {
7
7
  } from "solid-js";
8
8
  import type { SsgoiContext, SsgoiTransitionState } from "./types";
9
9
 
10
+ /** Route state `<Ssgoi>` resolves once for every layout boundary below it. */
11
+ export interface SsgoiLayoutState {
12
+ routePath: string;
13
+ chain: string[];
14
+ }
15
+
10
16
  const SsgoiContextInstance = createContext<SsgoiContext | null>(null);
17
+ const SsgoiLayoutContext = createContext<
18
+ Accessor<SsgoiLayoutState | undefined> | undefined
19
+ >(undefined);
20
+ const SsgoiLayoutDepthContext = createContext(0);
11
21
 
12
22
  export const SsgoiProvider = SsgoiContextInstance.Provider;
23
+ export const SsgoiLayoutProvider = SsgoiLayoutContext.Provider;
24
+ /** Gives descendants of a layout boundary their depth. */
25
+ export const SsgoiLayoutDepthProvider = SsgoiLayoutDepthContext.Provider;
26
+
27
+ /** Layout state of the nearest `<Ssgoi>`; undefined without a framework. */
28
+ export const useSsgoiLayout = () => useContext(SsgoiLayoutContext);
29
+
30
+ /** Depth of the calling layout boundary: 0 unless inside another. */
31
+ export const useLayoutDepth = () => useContext(SsgoiLayoutDepthContext);
13
32
 
14
33
  export const useSsgoi = () => {
15
34
  const context = useContext(SsgoiContextInstance);
package/src/lib/index.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export * from "./types";
2
2
  export { Ssgoi } from "./ssgoi";
3
3
  export { useSsgoiTransition } from "./context";
4
- /** @deprecated Set `data-ssgoi-transition` directly on an element inside `<Ssgoi>` instead. */
5
- export { SsgoiTransition } from "./ssgoi-transition";
4
+ export { SsgoiLayoutBoundary } from "./layout-boundary";
5
+ export type { SsgoiLayoutBoundaryProps } from "./layout-boundary";
6
+ /** @deprecated Will be removed in 8.0.0. */
7
+ export { SsgoiTransition } from "./legacy/ssgoi-transition";
6
8
  export * from "@ssgoi/core";
@@ -0,0 +1,72 @@
1
+ import {
2
+ createMemo,
3
+ Show,
4
+ splitProps,
5
+ type Accessor,
6
+ type JSX,
7
+ } from "solid-js";
8
+ import { Dynamic } from "solid-js/web";
9
+ import {
10
+ SsgoiLayoutDepthProvider,
11
+ useLayoutDepth,
12
+ useSsgoiLayout,
13
+ } from "./context";
14
+
15
+ export type SsgoiLayoutBoundaryProps = JSX.HTMLAttributes<HTMLElement> & {
16
+ as?: keyof JSX.IntrinsicElements;
17
+ };
18
+
19
+ /** Shared rendering; remounts its element whenever `state().key` changes. */
20
+ export function RouteBoundary(
21
+ props: SsgoiLayoutBoundaryProps & {
22
+ state: Accessor<{ id: string; key: string | number }>;
23
+ },
24
+ ) {
25
+ const [local, rest] = splitProps(props, ["children", "as", "state"]);
26
+ // Show uses truthiness. A token also supports valid keys such as 0 and "".
27
+ const lifetime = createMemo<{ key: string | number }>((previous) => {
28
+ const key = local.state().key;
29
+ return previous?.key === key ? previous : { key };
30
+ });
31
+
32
+ return (
33
+ <Show when={lifetime()} keyed>
34
+ {(_lifetime) => (
35
+ <Dynamic
36
+ component={local.as ?? "div"}
37
+ {...rest}
38
+ data-ssgoi-transition={local.state().id}
39
+ >
40
+ {local.children}
41
+ </Dynamic>
42
+ )}
43
+ </Show>
44
+ );
45
+ }
46
+
47
+ function throwMissingFramework(): never {
48
+ throw new Error(
49
+ "SSGOI: SsgoiLayoutBoundary requires a framework. Pass it to <Ssgoi framework={{ getRoute }}>.",
50
+ );
51
+ }
52
+
53
+ export function SsgoiLayoutBoundary(props: SsgoiLayoutBoundaryProps) {
54
+ const layout = useSsgoiLayout();
55
+ if (!layout?.()) throwMissingFramework();
56
+ const depth = useLayoutDepth();
57
+ const [local, rest] = splitProps(props, ["children"]);
58
+ // The config's layout scope at this depth keeps the DOM; past the declared
59
+ // scopes every route path remounts.
60
+ const state = createMemo(() => {
61
+ const { routePath, chain } = layout() ?? throwMissingFramework();
62
+ return { id: routePath, key: chain[depth] ?? routePath };
63
+ });
64
+
65
+ return (
66
+ <RouteBoundary {...rest} state={state}>
67
+ <SsgoiLayoutDepthProvider value={depth + 1}>
68
+ {local.children}
69
+ </SsgoiLayoutDepthProvider>
70
+ </RouteBoundary>
71
+ );
72
+ }
@@ -0,0 +1,33 @@
1
+ // TODO(8.0.0): remove this entry (deprecated in 7.5.0).
2
+ import { useLocation } from "@solidjs/router";
3
+ import { createMemo, splitProps, type JSX } from "solid-js";
4
+ import { RouteBoundary } from "../layout-boundary";
5
+
6
+ /** @deprecated Will be removed in 8.0.0. Declare layout scopes in `config.layouts` and use `SsgoiLayoutBoundary` from "@ssgoi/solid" instead. */
7
+ export interface RouteBoundaryState {
8
+ id: string;
9
+ key?: string | number;
10
+ }
11
+
12
+ /** @deprecated Will be removed in 8.0.0. Declare layout scopes in `config.layouts` and use `SsgoiLayoutBoundary` from "@ssgoi/solid" instead. */
13
+ export type SsgoiRouteBoundaryProps = JSX.HTMLAttributes<HTMLElement> & {
14
+ as?: keyof JSX.IntrinsicElements;
15
+ routeKey?: string | number;
16
+ resolve?: (location: { pathname: string }) => RouteBoundaryState;
17
+ };
18
+
19
+ /**
20
+ * @experimental Solid Router integration, including SolidStart. The API may change.
21
+ * @deprecated Will be removed in 8.0.0. Use `SsgoiLayoutBoundary` from "@ssgoi/solid" with `<Ssgoi framework={{ getRoute: () => ({ pathname: location.pathname }) }}>` (`useLocation` from "@solidjs/router") instead.
22
+ */
23
+ export function SsgoiRouteBoundary(props: SsgoiRouteBoundaryProps) {
24
+ const location = useLocation();
25
+ const [local, rest] = splitProps(props, ["resolve", "routeKey"]);
26
+ const state = createMemo(() => {
27
+ const current = local.resolve?.({ pathname: location.pathname }) ?? {
28
+ id: location.pathname,
29
+ };
30
+ return { id: current.id, key: local.routeKey ?? current.key ?? current.id };
31
+ });
32
+ return <RouteBoundary {...rest} state={state} />;
33
+ }
@@ -0,0 +1,12 @@
1
+ // TODO(8.0.0): remove this entry (deprecated in 7.5.0).
2
+ /**
3
+ * @experimental SolidStart integration through Solid Router. The API may change.
4
+ * @deprecated Will be removed in 8.0.0. Use `SsgoiLayoutBoundary` from "@ssgoi/solid" with `<Ssgoi framework={{ getRoute: () => ({ pathname: location.pathname }) }}>` (`useLocation` from "@solidjs/router") instead.
5
+ */
6
+ export { SsgoiRouteBoundary } from "./solid-router";
7
+ export type {
8
+ /** @deprecated Will be removed in 8.0.0. Declare layout scopes in `config.layouts` and use `SsgoiLayoutBoundary` from "@ssgoi/solid" instead. */
9
+ SsgoiRouteBoundaryProps,
10
+ /** @deprecated Will be removed in 8.0.0. Declare layout scopes in `config.layouts` and use `SsgoiLayoutBoundary` from "@ssgoi/solid" instead. */
11
+ RouteBoundaryState,
12
+ } from "./solid-router";
@@ -1,7 +1,9 @@
1
+ // TODO(8.0.0): remove this entry (deprecated in 7.5.0).
1
2
  import { splitProps, type JSX, type ValidComponent } from "solid-js";
2
3
  import { Dynamic } from "solid-js/web";
3
- import { useSsgoi } from "./context";
4
+ import { useSsgoi } from "../context";
4
5
 
6
+ /** @deprecated Will be removed in 8.0.0. */
5
7
  type SsgoiTransitionProps<T extends ValidComponent = "div"> = {
6
8
  children: JSX.Element;
7
9
  id: string;
@@ -9,10 +11,7 @@ type SsgoiTransitionProps<T extends ValidComponent = "div"> = {
9
11
  class?: string;
10
12
  };
11
13
 
12
- /**
13
- * @deprecated Set `data-ssgoi-transition` directly on the page boundary
14
- * element inside `<Ssgoi>` instead.
15
- */
14
+ /** @deprecated Will be removed in 8.0.0. */
16
15
  export const SsgoiTransition = <T extends ValidComponent = "div">(
17
16
  props: SsgoiTransitionProps<T>,
18
17
  ) => {
package/src/lib/ssgoi.tsx CHANGED
@@ -1,15 +1,17 @@
1
1
  import { createEffect, createMemo, onCleanup, type JSX } from "solid-js";
2
- import type { SsgoiConfig, SsgoiContext } from "./types";
3
- import { SsgoiProvider } from "./context";
2
+ import type { SsgoiConfig, SsgoiContext, SsgoiFramework } from "./types";
3
+ import { SsgoiLayoutProvider, SsgoiProvider } from "./context";
4
4
  import {
5
5
  createSggoiTransitionContext,
6
6
  observeSsgoiTransitions,
7
+ resolveLayoutChain,
7
8
  } from "@ssgoi/core/internal";
8
9
  import type { HostAnimation } from "@ssgoi/core/internal";
9
10
 
10
11
  interface SsgoiProps {
11
12
  config: SsgoiConfig;
12
13
  host?: HostAnimation;
14
+ framework?: SsgoiFramework;
13
15
  children: JSX.Element;
14
16
  }
15
17
 
@@ -19,6 +21,17 @@ export const Ssgoi = (props: SsgoiProps) => {
19
21
  createSggoiTransitionContext(props.config, { host: props.host }),
20
22
  );
21
23
 
24
+ // Resolve the route once; every SsgoiLayoutBoundary keys off this chain.
25
+ const layout = createMemo(() => {
26
+ const route = props.framework?.getRoute();
27
+ if (!route) return undefined;
28
+ const routePath = route.renderedPath ?? route.pathname;
29
+ return {
30
+ routePath,
31
+ chain: resolveLayoutChain(props.config.layouts, routePath),
32
+ };
33
+ });
34
+
22
35
  createEffect(() => {
23
36
  if (!root) return;
24
37
  const cleanup = observeSsgoiTransitions(root, contextValue());
@@ -27,9 +40,11 @@ export const Ssgoi = (props: SsgoiProps) => {
27
40
 
28
41
  return (
29
42
  <SsgoiProvider value={contextValue()}>
30
- <div ref={root} data-ssgoi-root="" style={{ display: "contents" }}>
31
- {props.children}
32
- </div>
43
+ <SsgoiLayoutProvider value={layout}>
44
+ <div ref={root} data-ssgoi-root="" style={{ display: "contents" }}>
45
+ {props.children}
46
+ </div>
47
+ </SsgoiLayoutProvider>
33
48
  </SsgoiProvider>
34
49
  );
35
50
  };
package/src/lib/types.ts CHANGED
@@ -1 +1,10 @@
1
1
  export * from "@ssgoi/core/types";
2
+
3
+ /**
4
+ * Router bridge for `<Ssgoi framework>`. `getRoute` must be reactive, e.g.
5
+ * `() => ({ pathname: location.pathname })` with `useLocation()` from Solid
6
+ * Router. `renderedPath`, when given, is the path the page content shows.
7
+ */
8
+ export interface SsgoiFramework {
9
+ getRoute: () => { pathname: string; renderedPath?: string };
10
+ }
@@ -1 +0,0 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("solid-js/web"),a=require("solid-js"),l=require("./context.cjs"),u=t=>{const[e,o]=a.splitProps(t,["children","id","as","class"]),n=l.useSsgoi(),i=e.as||"div";return r.createComponent(r.Dynamic,r.mergeProps({component:i,ref(c){var s=n.refFor(e.id);typeof s=="function"&&s(c)},get"data-ssgoi-transition"(){return e.id},get class(){return e.class}},o,{get children(){return e.children}}))};exports.SsgoiTransition=u;
@@ -1,51 +0,0 @@
1
- import { useLocation } from "@solidjs/router";
2
- import { createMemo, Show, splitProps, type JSX } from "solid-js";
3
- import { Dynamic } from "solid-js/web";
4
-
5
- export interface RouteBoundaryState {
6
- id: string;
7
- key?: string | number;
8
- }
9
-
10
- export type SsgoiRouteBoundaryProps = JSX.HTMLAttributes<HTMLElement> & {
11
- as?: keyof JSX.IntrinsicElements;
12
- routeKey?: string | number;
13
- resolve?: (location: { pathname: string }) => RouteBoundaryState;
14
- };
15
-
16
- /** @experimental Solid Router integration, including SolidStart. The API may change. */
17
- export function SsgoiRouteBoundary(props: SsgoiRouteBoundaryProps) {
18
- const location = useLocation();
19
- const [local, rest] = splitProps(props, [
20
- "children",
21
- "as",
22
- "resolve",
23
- "routeKey",
24
- ]);
25
- const boundary = createMemo(
26
- () =>
27
- local.resolve?.({ pathname: location.pathname }) ?? {
28
- id: location.pathname,
29
- },
30
- );
31
- // Show uses truthiness. A token also supports valid keys such as 0 and "".
32
- const lifetime = createMemo<{ key: string | number }>((previous) => {
33
- const current = boundary();
34
- const key = local.routeKey ?? current.key ?? current.id;
35
- return previous?.key === key ? previous : { key };
36
- });
37
-
38
- return (
39
- <Show when={lifetime()} keyed>
40
- {(_lifetime) => (
41
- <Dynamic
42
- component={local.as ?? "div"}
43
- {...rest}
44
- data-ssgoi-transition={boundary().id}
45
- >
46
- {local.children}
47
- </Dynamic>
48
- )}
49
- </Show>
50
- );
51
- }
@@ -1,6 +0,0 @@
1
- /** @experimental SolidStart integration through Solid Router. The API may change. */
2
- export { SsgoiRouteBoundary } from "./solid-router";
3
- export type {
4
- SsgoiRouteBoundaryProps,
5
- RouteBoundaryState,
6
- } from "./solid-router";