@jfdevelops/react-layout 0.19.0 → 0.21.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/dist/create-config/define-layout.cjs +180 -3
- package/dist/create-config/define-layout.cjs.map +1 -1
- package/dist/create-config/define-layout.d.cts +214 -33
- package/dist/create-config/define-layout.d.cts.map +1 -1
- package/dist/create-config/define-layout.d.mts +214 -33
- package/dist/create-config/define-layout.d.mts.map +1 -1
- package/dist/create-config/define-layout.mjs +181 -4
- package/dist/create-config/define-layout.mjs.map +1 -1
- package/dist/create-config/for-resources/create-component.d.mts +1 -1
- package/dist/create-config/for-resources/create-for-resources.cjs +1 -1
- package/dist/create-config/for-resources/create-for-resources.mjs +1 -1
- package/dist/create-config/index.d.cts +2 -1
- package/dist/create-config/index.d.mts +2 -1
- package/dist/create-config/shell-context.cjs +22 -0
- package/dist/create-config/shell-context.cjs.map +1 -0
- package/dist/create-config/shell-context.d.cts +18 -0
- package/dist/create-config/shell-context.d.cts.map +1 -0
- package/dist/create-config/shell-context.d.mts +18 -0
- package/dist/create-config/shell-context.d.mts.map +1 -0
- package/dist/create-config/shell-context.mjs +21 -0
- package/dist/create-config/shell-context.mjs.map +1 -0
- package/dist/index.d.cts +4 -3
- package/dist/index.d.mts +4 -3
- package/dist/resource.cjs.map +1 -1
- package/dist/resource.d.cts +1 -1
- package/dist/resource.d.cts.map +1 -1
- package/dist/resource.d.mts +2 -2
- package/dist/resource.d.mts.map +1 -1
- package/dist/resource.mjs.map +1 -1
- package/package.json +1 -1
- package/skills/build-react-layouts/SKILL.md +4 -0
- package/skills/build-react-layouts/references/architecture.md +92 -0
package/dist/resource.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MergeIntersection, UnionToIntersection } from "./utils.mjs";
|
|
2
|
-
import { NonEmptyReadonlyArray } from "@jfdevelops/react-layout-validator";
|
|
3
2
|
import { ReactNode } from "react";
|
|
3
|
+
import { NonEmptyReadonlyArray } from "@jfdevelops/react-layout-validator";
|
|
4
4
|
|
|
5
5
|
//#region src/resource.d.ts
|
|
6
6
|
type ResourceDefinition = string | {
|
|
@@ -63,5 +63,5 @@ type SubResourceParamForResource<Resources extends ReadonlyArray<ResourceDefinit
|
|
|
63
63
|
type ResourceEnum<Resources extends ReadonlyArray<ResourceDefinition>> = NonEmptyReadonlyArray<LayoutResourceKey<Resources>>;
|
|
64
64
|
declare function toResourceEnum<Resources extends ReadonlyArray<ResourceDefinition>>(resources: NormalizeResources<Resources>): ResourceEnum<Resources>;
|
|
65
65
|
//#endregion
|
|
66
|
-
export { LayoutResourceKey, NormalizeResource, NormalizeResources, ResourceDefinition, ResourceDefinitionForKey, ResourceEnum, ResourceLayoutComponentProps, ResourceTree, SubResourceDefinitionsFor, SubResourceParamForResource, normalizeResource, normalizeResources, toResourceEnum };
|
|
66
|
+
export { LayoutResourceKey, NormalizeResource, NormalizeResources, ResourceDefinition, ResourceDefinitionForKey, ResourceDefinitionValue, ResourceEnum, ResourceLayoutComponentProps, ResourceTree, SubResourceDefinitionsFor, SubResourceParamForResource, normalizeResource, normalizeResources, toResourceEnum };
|
|
67
67
|
//# sourceMappingURL=resource.d.mts.map
|
package/dist/resource.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource.d.mts","names":[],"sources":["../src/resource.ts"],"mappings":";;;;;KAIY,kBAAA;EAGN,KAAA;EACA,YAAA,EAAc,aAAa,CACzB,kBAAA;IALsB;;;;;IAWpB,cAAA;EAAA;AAAA;AAAA,KAKE,YAAA,GAAe,MAAM;EAG7B,YAAA,EAAc,YAAA;AAAA;AAAA,KAIN,iBAAA,kBAAmC,kBAAA,IAC7C,QAAA,4BAEc,QAAA;EACN,YAAA;AAAA,MAGJ,QAAA;EACI,KAAA;EACA,YAAA,6BACE,aAAA,CAAc,kBAAA;AAAA,cAGR,KAAA;EACN,YAAA,EAAc,kBAAA,CAAmB,YAAA;AAAA;AAAA,KAKjC,kBAAA,mBACQ,aAAA,CAAc,kBAAA,KAC9B,iBAAA,CACF,mBAAA,CAAoB,iBAAA,CAAkB,SAAA;AAAA,KAE5B,4BAAA;EACV,QAAA,EAAU,SAAS;AAAA;AAAA,iBAiCL,iBAAA,CAAkB,QAAA,EAAU,kBAAA,GAAqB,YAAY;AAAA,iBAI7D,kBAAA,mBACI,aAAA,CAAc,kBAAA,GAChC,SAAA,EAAW,SAAA,GAAY,kBAAA,CAAmB,SAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"resource.d.mts","names":[],"sources":["../src/resource.ts"],"mappings":";;;;;KAIY,kBAAA;EAGN,KAAA;EACA,YAAA,EAAc,aAAa,CACzB,kBAAA;IALsB;;;;;IAWpB,cAAA;EAAA;AAAA;AAAA,KAKE,YAAA,GAAe,MAAM;EAG7B,YAAA,EAAc,YAAA;AAAA;AAAA,KAIN,iBAAA,kBAAmC,kBAAA,IAC7C,QAAA,4BAEc,QAAA;EACN,YAAA;AAAA,MAGJ,QAAA;EACI,KAAA;EACA,YAAA,6BACE,aAAA,CAAc,kBAAA;AAAA,cAGR,KAAA;EACN,YAAA,EAAc,kBAAA,CAAmB,YAAA;AAAA;AAAA,KAKjC,kBAAA,mBACQ,aAAA,CAAc,kBAAA,KAC9B,iBAAA,CACF,mBAAA,CAAoB,iBAAA,CAAkB,SAAA;AAAA,KAE5B,4BAAA;EACV,QAAA,EAAU,SAAS;AAAA;AAAA,iBAiCL,iBAAA,CAAkB,QAAA,EAAU,kBAAA,GAAqB,YAAY;AAAA,iBAI7D,kBAAA,mBACI,aAAA,CAAc,kBAAA,GAChC,SAAA,EAAW,SAAA,GAAY,kBAAA,CAAmB,SAAA;AAAA,KAehC,uBAAA,kBAAyC,kBAAA,IACnD,QAAA,kBACI,QAAA,GACA,QAAA;EAAmB,KAAA;AAAA,IACjB,KAAA;;;;;;KAQI,iBAAA,mBACQ,aAAA,CAAc,kBAAA,KAC9B,uBAAA,CAAwB,SAAA;AAAA,KAEhB,wBAAA,mBACQ,aAAA,CAAc,kBAAA,yBAE9B,OAAA,CAAQ,SAAA,UAAmB,GAAA;EAAQ,KAAA,EAAO,GAAA;AAAA;;KAGlC,yBAAA,mBACQ,aAAA,CAAc,kBAAA,oBACf,iBAAA,CAAkB,SAAA,KAEnC,wBAAA,CAAyB,SAAA,EAAW,QAAA;EAClC,YAAA,6BAAyC,aAAA,CAAc,kBAAA;AAAA,IAErD,YAAA;AAAA,KAGD,uBAAA,qEAGG,QAAA,6BAAqC,QAAQ;;;;;KAMzC,yBAAA,mBACQ,aAAA,CAAc,kBAAA,mBAChB,aAAA,CAAc,kBAAA,qFAI9B,iBAAA,CAAkB,OAAA,sBACd,GAAA,SAAY,iBAAA,CAAkB,OAAA,IAC5B,uBAAA,CAAwB,QAAA,EAAU,QAAA,iBAChC,wBAAA,CAAyB,OAAA,EAAS,GAAA;EAChC,YAAA,uBACE,aAAA,CAAc,kBAAA;AAAA,KAEf,iBAAA,CAAkB,MAAA,qBACjB,GAAA,GAEI,GAAA;EAEE,KAAA,EAAO,GAAA;EACP,WAAA,EAAa,yBAAA,CACX,SAAA,EACA,MAAA,EACA,QAAA,eACa,QAAA;AAAA,IAGvB,GAAA,GACF,GAAA;;KAKE,2BAAA,mBACQ,aAAA,CAAc,kBAAA,oBACf,iBAAA,CAAkB,SAAA,MAChC,yBAAA,CAA0B,SAAA,EAAW,QAAA,mCAEtC,yBAAA,CACE,SAAA,EACA,yBAAA,CAA0B,SAAA,EAAW,QAAA;AAAA,KAY/B,YAAA,mBAA+B,aAAA,CAAc,kBAAA,KACvD,qBAAA,CAAsB,iBAAA,CAAkB,SAAA;AAAA,iBAC1B,cAAA,mBACI,aAAA,CAAc,kBAAA,GAChC,SAAA,EAAW,kBAAA,CAAmB,SAAA,IAOJ,YAAA,CAAa,SAAA"}
|
package/dist/resource.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource.mjs","names":[],"sources":["../src/resource.ts"],"sourcesContent":["import { ReactNode } from 'react';\nimport { NonEmptyReadonlyArray } from '@jfdevelops/react-layout-validator';\nimport { MergeIntersection, UnionToIntersection } from './utils';\n\nexport type ResourceDefinition =\n | string\n | {\n value: string;\n subResources: ReadonlyArray<\n ResourceDefinition & {\n /**\n * Whether to allow any string as a sub resource.\n *\n * @default false\n */\n allowAnyString?: boolean;\n }\n >;\n };\n\nexport type ResourceTree = Record<\n string,\n {\n subResources: ResourceTree;\n }\n>;\n\nexport type NormalizeResource<Resource extends ResourceDefinition> =\n Resource extends string\n ? {\n [Key in Resource]: {\n subResources: {};\n };\n }\n : Resource extends {\n value: infer Value extends string;\n subResources: infer SubResources extends\n ReadonlyArray<ResourceDefinition>;\n }\n ? {\n [Key in Value]: {\n subResources: NormalizeResources<SubResources>;\n };\n }\n : never;\n\nexport type NormalizeResources<\n Resources extends ReadonlyArray<ResourceDefinition>,\n> = MergeIntersection<\n UnionToIntersection<NormalizeResource<Resources[number]>>\n>;\nexport type ResourceLayoutComponentProps = {\n children: ReactNode;\n};\n\nfunction normalizeResourceTree(resource: ResourceDefinition): ResourceTree {\n if (typeof resource === 'string') {\n return {\n [resource]: {\n subResources: {},\n },\n };\n }\n\n const { value, subResources } = resource;\n\n return {\n [value]: {\n subResources: normalizeResourcesTree(subResources),\n },\n };\n}\n\nfunction normalizeResourcesTree(\n resources: ReadonlyArray<ResourceDefinition>,\n): ResourceTree {\n const normalizedResources: ResourceTree = {};\n\n for (const resource of resources) {\n Object.assign(normalizedResources, normalizeResourceTree(resource));\n }\n\n return normalizedResources;\n}\n\nexport function normalizeResource(resource: ResourceDefinition): ResourceTree {\n return normalizeResourceTree(resource);\n}\n\nexport function normalizeResources<\n Resources extends ReadonlyArray<ResourceDefinition>,\n>(resources: Resources): NormalizeResources<Resources> {\n return normalizeResourcesTree(resources) as NormalizeResources<Resources>;\n}\n\nexport function createIsValidResourceFn<\n Resources extends ReadonlyArray<ResourceDefinition>,\n>(resources: Resources) {\n return (value: unknown): value is LayoutResourceKey<Resources> => {\n return (\n typeof value === 'string' &&\n Object.keys(normalizeResources(resources)).includes(value)\n );\n };\n}\n\
|
|
1
|
+
{"version":3,"file":"resource.mjs","names":[],"sources":["../src/resource.ts"],"sourcesContent":["import { ReactNode } from 'react';\nimport { NonEmptyReadonlyArray } from '@jfdevelops/react-layout-validator';\nimport { MergeIntersection, UnionToIntersection } from './utils';\n\nexport type ResourceDefinition =\n | string\n | {\n value: string;\n subResources: ReadonlyArray<\n ResourceDefinition & {\n /**\n * Whether to allow any string as a sub resource.\n *\n * @default false\n */\n allowAnyString?: boolean;\n }\n >;\n };\n\nexport type ResourceTree = Record<\n string,\n {\n subResources: ResourceTree;\n }\n>;\n\nexport type NormalizeResource<Resource extends ResourceDefinition> =\n Resource extends string\n ? {\n [Key in Resource]: {\n subResources: {};\n };\n }\n : Resource extends {\n value: infer Value extends string;\n subResources: infer SubResources extends\n ReadonlyArray<ResourceDefinition>;\n }\n ? {\n [Key in Value]: {\n subResources: NormalizeResources<SubResources>;\n };\n }\n : never;\n\nexport type NormalizeResources<\n Resources extends ReadonlyArray<ResourceDefinition>,\n> = MergeIntersection<\n UnionToIntersection<NormalizeResource<Resources[number]>>\n>;\nexport type ResourceLayoutComponentProps = {\n children: ReactNode;\n};\n\nfunction normalizeResourceTree(resource: ResourceDefinition): ResourceTree {\n if (typeof resource === 'string') {\n return {\n [resource]: {\n subResources: {},\n },\n };\n }\n\n const { value, subResources } = resource;\n\n return {\n [value]: {\n subResources: normalizeResourcesTree(subResources),\n },\n };\n}\n\nfunction normalizeResourcesTree(\n resources: ReadonlyArray<ResourceDefinition>,\n): ResourceTree {\n const normalizedResources: ResourceTree = {};\n\n for (const resource of resources) {\n Object.assign(normalizedResources, normalizeResourceTree(resource));\n }\n\n return normalizedResources;\n}\n\nexport function normalizeResource(resource: ResourceDefinition): ResourceTree {\n return normalizeResourceTree(resource);\n}\n\nexport function normalizeResources<\n Resources extends ReadonlyArray<ResourceDefinition>,\n>(resources: Resources): NormalizeResources<Resources> {\n return normalizeResourcesTree(resources) as NormalizeResources<Resources>;\n}\n\nexport function createIsValidResourceFn<\n Resources extends ReadonlyArray<ResourceDefinition>,\n>(resources: Resources) {\n return (value: unknown): value is LayoutResourceKey<Resources> => {\n return (\n typeof value === 'string' &&\n Object.keys(normalizeResources(resources)).includes(value)\n );\n };\n}\n\nexport type ResourceDefinitionValue<Resource extends ResourceDefinition> =\n Resource extends string\n ? Resource\n : Resource extends { value: infer Value extends string }\n ? Value\n : never;\n\n/**\n * Top-level resource names declared in a `resources` array. Preserves string\n * literal unions for const resource definitions (unlike `keyof` on the\n * normalized resource tree, which tends to widen to `string` in hovers).\n */\nexport type LayoutResourceKey<\n Resources extends ReadonlyArray<ResourceDefinition>,\n> = ResourceDefinitionValue<Resources[number]>;\n\nexport type ResourceDefinitionForKey<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Key extends string,\n> = Extract<Resources[number], Key | { value: Key }>;\n\n/** `subResources` array declared on a layout resource. */\nexport type SubResourceDefinitionsFor<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Resource extends LayoutResourceKey<Resources>,\n> =\n ResourceDefinitionForKey<Resources, Resource> extends {\n subResources: infer SubResources extends ReadonlyArray<ResourceDefinition>;\n }\n ? SubResources\n : readonly [];\n\ntype CanNestSubResourceParam<\n MaxDepth extends number,\n DepthAcc extends readonly unknown[],\n> = [...DepthAcc, unknown]['length'] extends MaxDepth ? false : true;\n\n/**\n * `subResource` route param: a slug at the current depth, or `{ value, subResource }`\n * when that slug has nested `subResources` in the layout tree and `MaxDepth` allows it.\n */\nexport type RecursiveSubResourceParam<\n Resources extends ReadonlyArray<ResourceDefinition>,\n SubDefs extends ReadonlyArray<ResourceDefinition>,\n MaxDepth extends number = 6,\n DepthAcc extends readonly unknown[] = readonly [],\n> =\n LayoutResourceKey<SubDefs> extends infer Key\n ? Key extends LayoutResourceKey<SubDefs>\n ? CanNestSubResourceParam<MaxDepth, DepthAcc> extends true\n ? ResourceDefinitionForKey<SubDefs, Key> extends {\n subResources: infer Nested extends\n ReadonlyArray<ResourceDefinition>;\n }\n ? [LayoutResourceKey<Nested>] extends [never]\n ? Key\n :\n | Key\n | {\n value: Key;\n subResource: RecursiveSubResourceParam<\n Resources,\n Nested,\n MaxDepth,\n readonly [...DepthAcc, unknown]\n >;\n }\n : Key\n : Key\n : never\n : never;\n\n/** Recursive `subResource` values for a layout resource (full tree depth). */\nexport type SubResourceParamForResource<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Resource extends LayoutResourceKey<Resources>,\n> = [SubResourceDefinitionsFor<Resources, Resource>] extends [readonly []]\n ? never\n : RecursiveSubResourceParam<\n Resources,\n SubResourceDefinitionsFor<Resources, Resource>\n >;\n\nexport type GetSubResourceKeys<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Resource extends LayoutResourceKey<Resources>,\n> =\n ResourceDefinitionForKey<Resources, Resource> extends {\n subResources: infer SubResources extends ReadonlyArray<ResourceDefinition>;\n }\n ? LayoutResourceKey<SubResources>\n : never;\nexport type ResourceEnum<Resources extends ReadonlyArray<ResourceDefinition>> =\n NonEmptyReadonlyArray<LayoutResourceKey<Resources>>;\nexport function toResourceEnum<\n Resources extends ReadonlyArray<ResourceDefinition>,\n>(resources: NormalizeResources<Resources>) {\n const keys = Object.keys(resources);\n\n if (keys.length === 0) {\n throw new Error('No resources provided');\n }\n\n return keys as unknown as ResourceEnum<Resources>;\n}\n"],"mappings":";AAuDA,SAAS,sBAAsB,UAA4C;CACzE,IAAI,OAAO,aAAa,UACtB,OAAO,GACJ,WAAW,EACV,cAAc,CAAC,EACjB,EACF;CAGF,MAAM,EAAE,OAAO,iBAAiB;CAEhC,OAAO,GACJ,QAAQ,EACP,cAAc,uBAAuB,YAAY,EACnD,EACF;AACF;AAEA,SAAS,uBACP,WACc;CACd,MAAM,sBAAoC,CAAC;CAE3C,KAAK,MAAM,YAAY,WACrB,OAAO,OAAO,qBAAqB,sBAAsB,QAAQ,CAAC;CAGpE,OAAO;AACT;AAEA,SAAgB,kBAAkB,UAA4C;CAC5E,OAAO,sBAAsB,QAAQ;AACvC;AAEA,SAAgB,mBAEd,WAAqD;CACrD,OAAO,uBAAuB,SAAS;AACzC;AAEA,SAAgB,wBAEd,WAAsB;CACtB,QAAQ,UAA0D;EAChE,OACE,OAAO,UAAU,YACjB,OAAO,KAAK,mBAAmB,SAAS,CAAC,EAAE,SAAS,KAAK;CAE7D;AACF;AAgGA,SAAgB,eAEd,WAA0C;CAC1C,MAAM,OAAO,OAAO,KAAK,SAAS;CAElC,IAAI,KAAK,WAAW,GAClB,MAAM,IAAI,MAAM,uBAAuB;CAGzC,OAAO;AACT"}
|
package/package.json
CHANGED
|
@@ -69,6 +69,10 @@ not-found states.
|
|
|
69
69
|
add a schema dependency solely to connect a route parameter to a resource.
|
|
70
70
|
- Create a separate `defineResourceLayout` definition for a surface with a
|
|
71
71
|
meaningfully different structural contract, such as a create or detail pane.
|
|
72
|
+
- Reach for `defineResourceLayout.withLayout` when the shared chrome (sidebar,
|
|
73
|
+
header, subscriptions) must survive navigation between resources. Mount its
|
|
74
|
+
`Shell` once around the router outlet; keep per-resource structure in
|
|
75
|
+
`layout.render`, which becomes optional there.
|
|
72
76
|
- Preserve inferred types. Do not use `any`, broad casts, or duplicated
|
|
73
77
|
hand-written resource types to work around a layout mismatch.
|
|
74
78
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
- Shared layout contract
|
|
7
7
|
- Semantic composables
|
|
8
8
|
- Preset composables
|
|
9
|
+
- Persistent application shell
|
|
9
10
|
- Secondary layout families
|
|
10
11
|
- Feature organization
|
|
11
12
|
|
|
@@ -125,6 +126,97 @@ Include the preset's required values in `layout.props.include`. Keep
|
|
|
125
126
|
presentation in the preset renderer and segment definitions in resource page
|
|
126
127
|
configuration.
|
|
127
128
|
|
|
129
|
+
## Persist the application shell across navigation
|
|
130
|
+
|
|
131
|
+
Plain `defineResourceLayout` wraps every page in its own `createResourceLayout`
|
|
132
|
+
component. Navigating between resources unmounts one page component and mounts
|
|
133
|
+
another, so the chrome that `render` produces — a sidebar, a header,
|
|
134
|
+
subscriptions, observers — is rebuilt on every navigation.
|
|
135
|
+
|
|
136
|
+
Use `defineResourceLayout.withLayout` when that rebuild is a problem. It returns
|
|
137
|
+
a `Shell` component in addition to the usual helpers:
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
export const {
|
|
141
|
+
createResourceConfig,
|
|
142
|
+
createResourceLayout,
|
|
143
|
+
createResourceLinks,
|
|
144
|
+
Shell,
|
|
145
|
+
} = defineResourceLayout.withLayout({
|
|
146
|
+
resources: ['appointments', 'locations', 'services', 'settings'],
|
|
147
|
+
options: {
|
|
148
|
+
title: createProp.component({ type: 'ReactNode' }).optional(),
|
|
149
|
+
},
|
|
150
|
+
shell: {
|
|
151
|
+
composables: (create) => ({
|
|
152
|
+
Layout: create({
|
|
153
|
+
name: ({ capitalize, resource }) =>
|
|
154
|
+
resource ? `${capitalize(resource)}Shell` : 'Shell',
|
|
155
|
+
wrapWith: AppLayout,
|
|
156
|
+
}),
|
|
157
|
+
}),
|
|
158
|
+
render: (_props, { composables, children }) => (
|
|
159
|
+
<composables.Layout>
|
|
160
|
+
<AppSidebar />
|
|
161
|
+
<main>{children}</main>
|
|
162
|
+
</composables.Layout>
|
|
163
|
+
),
|
|
164
|
+
},
|
|
165
|
+
layout: {
|
|
166
|
+
// Optional under `withLayout`. Give a resource its own header or actions
|
|
167
|
+
// here, or omit `render` entirely and the page renders its children into
|
|
168
|
+
// the shell's outlet.
|
|
169
|
+
props: {
|
|
170
|
+
custom: {
|
|
171
|
+
children: createProp.component({ type: 'ReactNode' }),
|
|
172
|
+
actions: createProp.component({ type: 'ReactNode' }).optional(),
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
render: ({ children, actions }, { composables }) => (
|
|
176
|
+
<composables.ResourceLayout>
|
|
177
|
+
{actions && (
|
|
178
|
+
<composables.ResourceHeaderActions>
|
|
179
|
+
{actions}
|
|
180
|
+
</composables.ResourceHeaderActions>
|
|
181
|
+
)}
|
|
182
|
+
{children}
|
|
183
|
+
</composables.ResourceLayout>
|
|
184
|
+
),
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Mount `Shell` once, in the parent route, wrapping the router outlet:
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
function AdminRoute() {
|
|
193
|
+
return (
|
|
194
|
+
<Shell>
|
|
195
|
+
<Outlet />
|
|
196
|
+
</Shell>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
The shell's `render` context matches a per-resource `render` context
|
|
202
|
+
(`composables`, `resources`, `name`) plus `children` — the outlet subtree, which
|
|
203
|
+
you place where page content belongs. `resource` and `resources.current` are
|
|
204
|
+
possibly `undefined`: the shell outlives any single page, so the current
|
|
205
|
+
resource is whatever the mounted page reports.
|
|
206
|
+
|
|
207
|
+
A page created by `createResourceLayout` reports its resource to the shell
|
|
208
|
+
automatically, so resource-scoped composable names still resolve. Pass the
|
|
209
|
+
always-optional `resource` prop to `<Shell>` for routes that render non-library
|
|
210
|
+
content, or in tests and stories:
|
|
211
|
+
|
|
212
|
+
```tsx
|
|
213
|
+
<Shell resource="appointments">{children}</Shell>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Keep genuinely shared, stateful chrome (sidebar, header, breadcrumb container)
|
|
217
|
+
in `shell`. Keep anything that varies per resource in `layout.render`. Anything
|
|
218
|
+
placed in `shell` cannot read per-page render props.
|
|
219
|
+
|
|
128
220
|
## Create secondary layout families
|
|
129
221
|
|
|
130
222
|
Create another `defineResourceLayout` when a surface has a distinct structural
|