@jfdevelops/react-layout 0.20.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.
@@ -0,0 +1,18 @@
1
+ //#region src/create-config/shell-context.d.ts
2
+ /**
3
+ * Callback a per-resource layout component calls to report which resource it is
4
+ * currently rendering. An ancestor `Shell` (from
5
+ * {@link defineResourceLayout.withLayout}) installs one of these so it can
6
+ * resolve `resources.current` for its own render without the consumer threading
7
+ * a route param down to it.
8
+ *
9
+ * @param resource - The mounted resource, or `undefined` on unmount.
10
+ * @param ownerId - Identity of the `defineResourceLayout` definition that
11
+ * created the reporting component. The shell only accepts reports from its
12
+ * own definition — a page or pane produced by a *different* definition shares
13
+ * this context but its resource is unrelated, so the shell ignores it.
14
+ */
15
+ type ResourceLayoutShellPublisher = (resource: string | undefined, ownerId?: symbol) => void;
16
+ //#endregion
17
+ export { ResourceLayoutShellPublisher };
18
+ //# sourceMappingURL=shell-context.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-context.d.mts","names":[],"sources":["../../src/create-config/shell-context.tsx"],"mappings":";;AAeA;;;;AAEkB;;;;;;;;KAFN,4BAAA,IACV,QAAA,sBACA,OAAgB"}
@@ -0,0 +1,21 @@
1
+ import { createContext, useContext } from "react";
2
+
3
+ //#region src/create-config/shell-context.tsx
4
+ /**
5
+ * Bridge between a `Shell` and the per-resource components rendered inside its
6
+ * `Outlet`. It is deliberately `null` by default: a per-resource component is
7
+ * still valid without a `Shell` ancestor (the pre-`withLayout` usage), so the
8
+ * publish must degrade to a no-op rather than assume a provider.
9
+ */
10
+ const ResourceLayoutShellPublisherContext = createContext(null);
11
+ /**
12
+ * Reads the {@link ResourceLayoutShellPublisher} installed by an ancestor
13
+ * `Shell`, or `null` when the component renders standalone.
14
+ */
15
+ function useResourceLayoutShellPublisher() {
16
+ return useContext(ResourceLayoutShellPublisherContext);
17
+ }
18
+
19
+ //#endregion
20
+ export { ResourceLayoutShellPublisherContext, useResourceLayoutShellPublisher };
21
+ //# sourceMappingURL=shell-context.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-context.mjs","names":[],"sources":["../../src/create-config/shell-context.tsx"],"sourcesContent":["import { createContext, useContext } from 'react';\n\n/**\n * Callback a per-resource layout component calls to report which resource it is\n * currently rendering. An ancestor `Shell` (from\n * {@link defineResourceLayout.withLayout}) installs one of these so it can\n * resolve `resources.current` for its own render without the consumer threading\n * a route param down to it.\n *\n * @param resource - The mounted resource, or `undefined` on unmount.\n * @param ownerId - Identity of the `defineResourceLayout` definition that\n * created the reporting component. The shell only accepts reports from its\n * own definition — a page or pane produced by a *different* definition shares\n * this context but its resource is unrelated, so the shell ignores it.\n */\nexport type ResourceLayoutShellPublisher = (\n resource: string | undefined,\n ownerId?: symbol,\n) => void;\n\n/**\n * Bridge between a `Shell` and the per-resource components rendered inside its\n * `Outlet`. It is deliberately `null` by default: a per-resource component is\n * still valid without a `Shell` ancestor (the pre-`withLayout` usage), so the\n * publish must degrade to a no-op rather than assume a provider.\n */\nexport const ResourceLayoutShellPublisherContext =\n createContext<ResourceLayoutShellPublisher | null>(null);\n\n/**\n * Reads the {@link ResourceLayoutShellPublisher} installed by an ancestor\n * `Shell`, or `null` when the component renders standalone.\n */\nexport function useResourceLayoutShellPublisher(): ResourceLayoutShellPublisher | null {\n return useContext(ResourceLayoutShellPublisherContext);\n}\n"],"mappings":";;;;;;;;;AA0BA,MAAa,sCACX,cAAmD,IAAI;;;;;AAMzD,SAAgB,kCAAuE;CACrF,OAAO,WAAW,mCAAmC;AACvD"}
package/dist/index.d.cts CHANGED
@@ -6,7 +6,8 @@ import { CreateGetComponentForPath, GetComponentForPath, ResolvedPathValue } fro
6
6
  import { ForPaths, ForPathsRenderContext, RenderedPathComponent, RenderedPathComponentHOF, TargetPathBuilder } from "./create-config/for-paths/builder.cjs";
7
7
  import { IsSubResourceKey } from "./create-config/is-sub-resource-key.cjs";
8
8
  import { ComponentTypes, CreatedResourceConfig } from "./create-config/get-component.cjs";
9
- import { DefineResourcesFn, LayoutRenderResourcesAccessor, LayoutResourcesAccessor, ResourceSelection, defineResourceLayout } from "./create-config/define-layout.cjs";
9
+ import { CreateResourceLayoutWithLayoutOptions, DefineResourceLayoutWithLayoutFn, DefineResourcesFn, DefinedResourceLayoutWithShell, LayoutRenderResourcesAccessor, LayoutResourcesAccessor, ResourceLayoutShellComponent, ResourceLayoutShellProps, ResourceSelection, ShellLayoutDefinition, ShellRenderContext, defineResourceLayout } from "./create-config/define-layout.cjs";
10
+ import { ResourceLayoutShellPublisher } from "./create-config/shell-context.cjs";
10
11
  import { CreateReactLayoutErrorOptions, CreatedReactLayoutError, ErrorMessageRenderer, ReactLayoutError, ReactLayoutErrorContext, ReactLayoutErrorRendererFactory, createReactLayoutError } from "./errors/react-layout-error.cjs";
11
12
  import { InvalidConfigContext, InvalidConfigError } from "./errors/invalid-config.cjs";
12
13
  import { InvalidPathContext, InvalidPathError } from "./errors/invalid-path.cjs";
@@ -16,4 +17,4 @@ import { InvalidComponentContext, InvalidComponentError } from "./errors/invalid
16
17
  import { MissingPathVariableContext, MissingPathVariableError } from "./errors/missing-path-variable.cjs";
17
18
  import { AnyComposableComponent, ComposableComponent, ComposableComponentCallable, ComposableComponents, ComposableNameContext, ComposablePresetComponent, ComposablePresetComponentCallProps, ComposablePresetMeta, ComposablePresetProps, ComposableResourceLayout, CreateLayoutComposable, DefinedComposableComponentRecord, LayoutComposablePresetProvider, LayoutComposablesFactory, MakeComposable, MakeComposableOptions, MergePresetProps, PresetPropsFromComposable, RequiredPresetLayoutProps, RequiredPresetRenderProps, ResolveLayoutComposables, collectComposablePresetEntries, createComposableComponent, createLayoutComposableFactory, defineComposableComponent, makeComposable, resolveComposablePresetProps, resolveLayoutComposables } from "@jfdevelops/react-layout-composables";
18
19
  import { AnyBuiltPropDefinition, ExtractDefinitionValue, PropError, PropErrorOptions, ResolveLayoutProps, ResolveLayoutPropsAsDefined, ResolveProps, ResolvedBuiltPropShape, createPrimitivePropBuilder, createProp, isPropDefinitionShape, resolvePropDefinitionValues, validateProps } from "@jfdevelops/react-layout-validator";
19
- export { type AnyBuiltPropDefinition, type AnyComposableComponent, type BaseComponent, type ComponentTypes, type ComposableComponent, type ComposableComponentCallable, type ComposableComponents, type ComposableNameContext, type ComposablePresetComponent, type ComposablePresetComponentCallProps, type ComposablePresetMeta, type ComposablePresetProps, type ComposableResourceLayout, type CreateGetComponentForPath, type CreateLayoutComposable, type CreateReactLayoutErrorOptions, type CreatedReactLayoutError, type CreatedResourceConfig, type DefineResourcesFn, type DefinedComposableComponentRecord, type ErrorMessageRenderer, type ExtractDefinitionValue, type ForPaths, type ForPathsRenderContext, type GetComponentForPath, type InPropsDefinition, type InPropsFunction, type InPropsObject, type InPropsOptions, type IncludedPropBehavior, type IncludedProps, type InferredInProps, type InvalidComponentContext, InvalidComponentError, type InvalidConfigContext, InvalidConfigError, type InvalidPathContext, InvalidPathError, type InvalidResourceContext, InvalidResourceError, type InvalidSubResourceContext, InvalidSubResourceError, type IsSubResourceKey, LayoutComposablePresetProvider, type LayoutComposablesFactory, type LayoutRenderProps, type LayoutRenderResourcesAccessor, type LayoutResourceKey, type LayoutResourcesAccessor, type MakeComposable, type MakeComposableOptions, type MergeIntersection, type MergePresetProps, type MergedLayoutInProps, type MissingPathVariableContext, MissingPathVariableError, type NormalizeResource, type NormalizeResources, type ParameterizedResourcePath, type PathVariables, type PresetPropsFromComposable, PropError, type PropErrorOptions, type PropsContextRender, type PropsRenderDefinition, ReactLayoutError, type ReactLayoutErrorContext, type ReactLayoutErrorRendererFactory, type RenderedPathComponent, type RenderedPathComponentHOF, type RequiredPresetLayoutProps, type RequiredPresetRenderProps, type ResolveLayoutComposables, type ResolveLayoutProps, type ResolveLayoutPropsAsDefined, type ResolveProps, type ResolvedBuiltPropShape, type ResolvedIncludedComponentProps, type ResolvedIncludedConfigProps, type ResolvedIncludedProps, type ResolvedIncludedPropsAsDefined, type ResolvedPathValue, type ResourceDefinition, type ResourceDefinitionValue, type ResourceEnum, type ResourceLayoutComponentProps, type ResourceSelection, type ResourceTree, type Show, type TargetPathBuilder, type UnionToIntersection, collectComposablePresetEntries, createComposableComponent, createLayoutComposableFactory, createPrimitivePropBuilder, createProp, createReactLayoutError, defineComposableComponent, defineResourceLayout, isPropDefinitionShape, makeComposable, normalizeResource, normalizeResources, pick, resolveComposablePresetProps, resolveLayoutComposables, resolvePropDefinitionValues, toResourceEnum, validateProps };
20
+ export { type AnyBuiltPropDefinition, type AnyComposableComponent, type BaseComponent, type ComponentTypes, type ComposableComponent, type ComposableComponentCallable, type ComposableComponents, type ComposableNameContext, type ComposablePresetComponent, type ComposablePresetComponentCallProps, type ComposablePresetMeta, type ComposablePresetProps, type ComposableResourceLayout, type CreateGetComponentForPath, type CreateLayoutComposable, type CreateReactLayoutErrorOptions, type CreateResourceLayoutWithLayoutOptions, type CreatedReactLayoutError, type CreatedResourceConfig, type DefineResourceLayoutWithLayoutFn, type DefineResourcesFn, type DefinedComposableComponentRecord, type DefinedResourceLayoutWithShell, type ErrorMessageRenderer, type ExtractDefinitionValue, type ForPaths, type ForPathsRenderContext, type GetComponentForPath, type InPropsDefinition, type InPropsFunction, type InPropsObject, type InPropsOptions, type IncludedPropBehavior, type IncludedProps, type InferredInProps, type InvalidComponentContext, InvalidComponentError, type InvalidConfigContext, InvalidConfigError, type InvalidPathContext, InvalidPathError, type InvalidResourceContext, InvalidResourceError, type InvalidSubResourceContext, InvalidSubResourceError, type IsSubResourceKey, LayoutComposablePresetProvider, type LayoutComposablesFactory, type LayoutRenderProps, type LayoutRenderResourcesAccessor, type LayoutResourceKey, type LayoutResourcesAccessor, type MakeComposable, type MakeComposableOptions, type MergeIntersection, type MergePresetProps, type MergedLayoutInProps, type MissingPathVariableContext, MissingPathVariableError, type NormalizeResource, type NormalizeResources, type ParameterizedResourcePath, type PathVariables, type PresetPropsFromComposable, PropError, type PropErrorOptions, type PropsContextRender, type PropsRenderDefinition, ReactLayoutError, type ReactLayoutErrorContext, type ReactLayoutErrorRendererFactory, type RenderedPathComponent, type RenderedPathComponentHOF, type RequiredPresetLayoutProps, type RequiredPresetRenderProps, type ResolveLayoutComposables, type ResolveLayoutProps, type ResolveLayoutPropsAsDefined, type ResolveProps, type ResolvedBuiltPropShape, type ResolvedIncludedComponentProps, type ResolvedIncludedConfigProps, type ResolvedIncludedProps, type ResolvedIncludedPropsAsDefined, type ResolvedPathValue, type ResourceDefinition, type ResourceDefinitionValue, type ResourceEnum, type ResourceLayoutComponentProps, type ResourceLayoutShellComponent, type ResourceLayoutShellProps, type ResourceLayoutShellPublisher, type ResourceSelection, type ResourceTree, type ShellLayoutDefinition, type ShellRenderContext, type Show, type TargetPathBuilder, type UnionToIntersection, collectComposablePresetEntries, createComposableComponent, createLayoutComposableFactory, createPrimitivePropBuilder, createProp, createReactLayoutError, defineComposableComponent, defineResourceLayout, isPropDefinitionShape, makeComposable, normalizeResource, normalizeResources, pick, resolveComposablePresetProps, resolveLayoutComposables, resolvePropDefinitionValues, toResourceEnum, validateProps };
package/dist/index.d.mts CHANGED
@@ -6,7 +6,8 @@ import { CreateGetComponentForPath, GetComponentForPath, ResolvedPathValue } fro
6
6
  import { ForPaths, ForPathsRenderContext, RenderedPathComponent, RenderedPathComponentHOF, TargetPathBuilder } from "./create-config/for-paths/builder.mjs";
7
7
  import { IsSubResourceKey } from "./create-config/is-sub-resource-key.mjs";
8
8
  import { ComponentTypes, CreatedResourceConfig } from "./create-config/get-component.mjs";
9
- import { DefineResourcesFn, LayoutRenderResourcesAccessor, LayoutResourcesAccessor, ResourceSelection, defineResourceLayout } from "./create-config/define-layout.mjs";
9
+ import { CreateResourceLayoutWithLayoutOptions, DefineResourceLayoutWithLayoutFn, DefineResourcesFn, DefinedResourceLayoutWithShell, LayoutRenderResourcesAccessor, LayoutResourcesAccessor, ResourceLayoutShellComponent, ResourceLayoutShellProps, ResourceSelection, ShellLayoutDefinition, ShellRenderContext, defineResourceLayout } from "./create-config/define-layout.mjs";
10
+ import { ResourceLayoutShellPublisher } from "./create-config/shell-context.mjs";
10
11
  import { CreateReactLayoutErrorOptions, CreatedReactLayoutError, ErrorMessageRenderer, ReactLayoutError, ReactLayoutErrorContext, ReactLayoutErrorRendererFactory, createReactLayoutError } from "./errors/react-layout-error.mjs";
11
12
  import { InvalidConfigContext, InvalidConfigError } from "./errors/invalid-config.mjs";
12
13
  import { InvalidPathContext, InvalidPathError } from "./errors/invalid-path.mjs";
@@ -16,4 +17,4 @@ import { InvalidComponentContext, InvalidComponentError } from "./errors/invalid
16
17
  import { MissingPathVariableContext, MissingPathVariableError } from "./errors/missing-path-variable.mjs";
17
18
  import { AnyComposableComponent, ComposableComponent, ComposableComponentCallable, ComposableComponents, ComposableNameContext, ComposablePresetComponent, ComposablePresetComponentCallProps, ComposablePresetMeta, ComposablePresetProps, ComposableResourceLayout, CreateLayoutComposable, DefinedComposableComponentRecord, LayoutComposablePresetProvider, LayoutComposablesFactory, MakeComposable, MakeComposableOptions, MergePresetProps, PresetPropsFromComposable, RequiredPresetLayoutProps, RequiredPresetRenderProps, ResolveLayoutComposables, collectComposablePresetEntries, createComposableComponent, createLayoutComposableFactory, defineComposableComponent, makeComposable, resolveComposablePresetProps, resolveLayoutComposables } from "@jfdevelops/react-layout-composables";
18
19
  import { AnyBuiltPropDefinition, ExtractDefinitionValue, PropError, PropErrorOptions, ResolveLayoutProps, ResolveLayoutPropsAsDefined, ResolveProps, ResolvedBuiltPropShape, createPrimitivePropBuilder, createProp, isPropDefinitionShape, resolvePropDefinitionValues, validateProps } from "@jfdevelops/react-layout-validator";
19
- export { type AnyBuiltPropDefinition, type AnyComposableComponent, type BaseComponent, type ComponentTypes, type ComposableComponent, type ComposableComponentCallable, type ComposableComponents, type ComposableNameContext, type ComposablePresetComponent, type ComposablePresetComponentCallProps, type ComposablePresetMeta, type ComposablePresetProps, type ComposableResourceLayout, type CreateGetComponentForPath, type CreateLayoutComposable, type CreateReactLayoutErrorOptions, type CreatedReactLayoutError, type CreatedResourceConfig, type DefineResourcesFn, type DefinedComposableComponentRecord, type ErrorMessageRenderer, type ExtractDefinitionValue, type ForPaths, type ForPathsRenderContext, type GetComponentForPath, type InPropsDefinition, type InPropsFunction, type InPropsObject, type InPropsOptions, type IncludedPropBehavior, type IncludedProps, type InferredInProps, type InvalidComponentContext, InvalidComponentError, type InvalidConfigContext, InvalidConfigError, type InvalidPathContext, InvalidPathError, type InvalidResourceContext, InvalidResourceError, type InvalidSubResourceContext, InvalidSubResourceError, type IsSubResourceKey, LayoutComposablePresetProvider, type LayoutComposablesFactory, type LayoutRenderProps, type LayoutRenderResourcesAccessor, type LayoutResourceKey, type LayoutResourcesAccessor, type MakeComposable, type MakeComposableOptions, type MergeIntersection, type MergePresetProps, type MergedLayoutInProps, type MissingPathVariableContext, MissingPathVariableError, type NormalizeResource, type NormalizeResources, type ParameterizedResourcePath, type PathVariables, type PresetPropsFromComposable, PropError, type PropErrorOptions, type PropsContextRender, type PropsRenderDefinition, ReactLayoutError, type ReactLayoutErrorContext, type ReactLayoutErrorRendererFactory, type RenderedPathComponent, type RenderedPathComponentHOF, type RequiredPresetLayoutProps, type RequiredPresetRenderProps, type ResolveLayoutComposables, type ResolveLayoutProps, type ResolveLayoutPropsAsDefined, type ResolveProps, type ResolvedBuiltPropShape, type ResolvedIncludedComponentProps, type ResolvedIncludedConfigProps, type ResolvedIncludedProps, type ResolvedIncludedPropsAsDefined, type ResolvedPathValue, type ResourceDefinition, type ResourceDefinitionValue, type ResourceEnum, type ResourceLayoutComponentProps, type ResourceSelection, type ResourceTree, type Show, type TargetPathBuilder, type UnionToIntersection, collectComposablePresetEntries, createComposableComponent, createLayoutComposableFactory, createPrimitivePropBuilder, createProp, createReactLayoutError, defineComposableComponent, defineResourceLayout, isPropDefinitionShape, makeComposable, normalizeResource, normalizeResources, pick, resolveComposablePresetProps, resolveLayoutComposables, resolvePropDefinitionValues, toResourceEnum, validateProps };
20
+ export { type AnyBuiltPropDefinition, type AnyComposableComponent, type BaseComponent, type ComponentTypes, type ComposableComponent, type ComposableComponentCallable, type ComposableComponents, type ComposableNameContext, type ComposablePresetComponent, type ComposablePresetComponentCallProps, type ComposablePresetMeta, type ComposablePresetProps, type ComposableResourceLayout, type CreateGetComponentForPath, type CreateLayoutComposable, type CreateReactLayoutErrorOptions, type CreateResourceLayoutWithLayoutOptions, type CreatedReactLayoutError, type CreatedResourceConfig, type DefineResourceLayoutWithLayoutFn, type DefineResourcesFn, type DefinedComposableComponentRecord, type DefinedResourceLayoutWithShell, type ErrorMessageRenderer, type ExtractDefinitionValue, type ForPaths, type ForPathsRenderContext, type GetComponentForPath, type InPropsDefinition, type InPropsFunction, type InPropsObject, type InPropsOptions, type IncludedPropBehavior, type IncludedProps, type InferredInProps, type InvalidComponentContext, InvalidComponentError, type InvalidConfigContext, InvalidConfigError, type InvalidPathContext, InvalidPathError, type InvalidResourceContext, InvalidResourceError, type InvalidSubResourceContext, InvalidSubResourceError, type IsSubResourceKey, LayoutComposablePresetProvider, type LayoutComposablesFactory, type LayoutRenderProps, type LayoutRenderResourcesAccessor, type LayoutResourceKey, type LayoutResourcesAccessor, type MakeComposable, type MakeComposableOptions, type MergeIntersection, type MergePresetProps, type MergedLayoutInProps, type MissingPathVariableContext, MissingPathVariableError, type NormalizeResource, type NormalizeResources, type ParameterizedResourcePath, type PathVariables, type PresetPropsFromComposable, PropError, type PropErrorOptions, type PropsContextRender, type PropsRenderDefinition, ReactLayoutError, type ReactLayoutErrorContext, type ReactLayoutErrorRendererFactory, type RenderedPathComponent, type RenderedPathComponentHOF, type RequiredPresetLayoutProps, type RequiredPresetRenderProps, type ResolveLayoutComposables, type ResolveLayoutProps, type ResolveLayoutPropsAsDefined, type ResolveProps, type ResolvedBuiltPropShape, type ResolvedIncludedComponentProps, type ResolvedIncludedConfigProps, type ResolvedIncludedProps, type ResolvedIncludedPropsAsDefined, type ResolvedPathValue, type ResourceDefinition, type ResourceDefinitionValue, type ResourceEnum, type ResourceLayoutComponentProps, type ResourceLayoutShellComponent, type ResourceLayoutShellProps, type ResourceLayoutShellPublisher, type ResourceSelection, type ResourceTree, type ShellLayoutDefinition, type ShellRenderContext, type Show, type TargetPathBuilder, type UnionToIntersection, collectComposablePresetEntries, createComposableComponent, createLayoutComposableFactory, createPrimitivePropBuilder, createProp, createReactLayoutError, defineComposableComponent, defineResourceLayout, isPropDefinitionShape, makeComposable, normalizeResource, normalizeResources, pick, resolveComposablePresetProps, resolveLayoutComposables, resolvePropDefinitionValues, toResourceEnum, validateProps };
@@ -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 | {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jfdevelops/react-layout",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "A React library for creating layout components.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -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