@arcote.tech/platform 0.5.0 → 0.5.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@arcote.tech/platform",
3
3
  "type": "module",
4
- "version": "0.5.0",
4
+ "version": "0.5.2",
5
5
  "private": false,
6
6
  "author": "Przemysław Krasiński [arcote.tech]",
7
7
  "description": "Arc Platform — module system, router, layout, theme, i18n, platform app shell",
@@ -18,11 +18,11 @@
18
18
  "type-check": "tsc --noEmit"
19
19
  },
20
20
  "dependencies": {
21
- "@arcote.tech/arc-ds": "^0.5.0",
22
- "@arcote.tech/arc-react": "^0.5.0"
21
+ "@arcote.tech/arc-ds": "^0.5.2",
22
+ "@arcote.tech/arc-react": "^0.5.2"
23
23
  },
24
24
  "peerDependencies": {
25
- "@arcote.tech/arc": "^0.5.0",
25
+ "@arcote.tech/arc": "^0.5.2",
26
26
  "@lingui/core": "^5.0.0",
27
27
  "@lingui/react": "^5.0.0",
28
28
  "framer-motion": "^12.0.0",
@@ -9,6 +9,7 @@ import type { PageFragment } from "../types";
9
9
  type MatchResult = {
10
10
  page: PageFragment;
11
11
  child?: PageFragment;
12
+ grandchild?: PageFragment;
12
13
  params: Record<string, string>;
13
14
  };
14
15
 
@@ -23,6 +24,26 @@ function matchPage(path: string, pages: PageFragment[]): MatchResult | undefined
23
24
  if (p.children?.length) {
24
25
  if (path === p.path) return { page: p, params: {} };
25
26
 
27
+ // Check grandchildren first (deeper match wins)
28
+ for (const c of p.children) {
29
+ if (c.children?.length) {
30
+ if (path === c.path) return { page: p, child: c, params: {} };
31
+
32
+ const exactGrandchild = c.children.find((gc) => gc.path === path);
33
+ if (exactGrandchild) return { page: p, child: c, grandchild: exactGrandchild, params: {} };
34
+
35
+ for (const gc of c.children) {
36
+ if (hasRouteParams(gc.path)) {
37
+ const params = matchRoutePath(gc.path, path);
38
+ if (params) return { page: p, child: c, grandchild: gc, params };
39
+ }
40
+ }
41
+
42
+ const prefixGrandchild = c.children.find((gc) => path.startsWith(gc.path));
43
+ if (prefixGrandchild) return { page: p, child: c, grandchild: prefixGrandchild, params: {} };
44
+ }
45
+ }
46
+
26
47
  // Try exact child match first
27
48
  const exactChild = p.children.find((c) => c.path === path);
28
49
  if (exactChild) return { page: p, child: exactChild, params: {} };
@@ -94,7 +115,7 @@ export function PageRouter() {
94
115
  return null;
95
116
  }
96
117
 
97
- const { page, child } = match;
118
+ const { page, child, grandchild } = match;
98
119
  const Layout = page.layout ?? DefaultLayout;
99
120
 
100
121
  // Parent with children — redirect to first child if at parent path
@@ -103,6 +124,29 @@ export function PageRouter() {
103
124
  return null;
104
125
  }
105
126
 
127
+ // Grandchild — nested shell: OuterShell > InnerShell > GrandchildComponent
128
+ if (page.children?.length && child?.children?.length && grandchild) {
129
+ const OuterShell = page.component;
130
+ const InnerShell = child.component;
131
+ const GrandchildComponent = grandchild.component;
132
+ const content = (
133
+ <OuterShell tabs={page.children}>
134
+ <InnerShell tabs={child.children}>
135
+ <Suspense fallback={null}>
136
+ <GrandchildComponent />
137
+ </Suspense>
138
+ </InnerShell>
139
+ </OuterShell>
140
+ );
141
+ return Layout ? <Layout>{content}</Layout> : content;
142
+ }
143
+
144
+ // Child with its own children — redirect to first grandchild
145
+ if (page.children?.length && child?.children?.length && !grandchild) {
146
+ navigate(child.children[0].path);
147
+ return null;
148
+ }
149
+
106
150
  // Parent with active child — render shell with tabs
107
151
  if (page.children?.length && child) {
108
152
  const Shell = page.component;