@tailor-platform/app-shell 0.26.3 → 0.27.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.
@@ -1,9 +1,9 @@
1
1
  import { AuthClient } from '@tailor-platform/auth-public-client';
2
2
  import { ClassProp } from 'class-variance-authority/types';
3
+ import { FC } from 'react';
3
4
  import { JSX } from 'react/jsx-runtime';
4
5
  import { Link } from 'react-router';
5
6
  import { LoaderFunctionArgs } from 'react-router';
6
- import { Params } from 'react-router';
7
7
  import * as React_2 from 'react';
8
8
  import { ReactNode } from 'react';
9
9
  import { toast } from 'sonner';
@@ -14,7 +14,10 @@ import { useRouteError } from 'react-router';
14
14
  import { useSearchParams } from 'react-router';
15
15
  import { VariantProps } from 'class-variance-authority';
16
16
 
17
- export declare const AppShell: (props: AppShellProps) => JSX.Element | null;
17
+ export declare const AppShell: {
18
+ (props: AppShellProps): JSX.Element | null;
19
+ /* Excluded from this release type: WithPages */
20
+ };
18
21
 
19
22
  /**
20
23
  * Context for static configuration (title, icon, configurations).
@@ -34,102 +37,93 @@ declare type AppShellDataContextType = {
34
37
  contextData: ContextData;
35
38
  };
36
39
 
37
- export declare type AppShellProps = React.PropsWithChildren<{
38
- /**
39
- * App shell title
40
- */
41
- title?: string;
42
- /**
43
- * App shell icon
44
- */
45
- icon?: React.ReactNode;
40
+ /**
41
+ * Props that can be attached to a page component via static field.
42
+ *
43
+ * @example
44
+ * ```tsx
45
+ * const DashboardPage = () => <div>Dashboard</div>;
46
+ *
47
+ * DashboardPage.appShellPageProps = {
48
+ * meta: { title: "Dashboard", icon: <DashboardIcon /> },
49
+ * guards: [authGuard],
50
+ * } satisfies AppShellPageProps;
51
+ *
52
+ * export default DashboardPage;
53
+ * ```
54
+ */
55
+ export declare type AppShellPageProps = {
46
56
  /**
47
- * Base path for the app shell
57
+ * Metadata for the page used in navigation and breadcrumbs.
48
58
  */
49
- basePath?: string;
59
+ meta?: {
60
+ /**
61
+ * Title of the page displayed in navigation and breadcrumbs.
62
+ * Supports localized strings with translations.
63
+ */
64
+ title?: LocalizedString;
65
+ /**
66
+ * Icon displayed alongside the title in navigation.
67
+ */
68
+ icon?: ReactNode;
69
+ };
50
70
  /**
51
- * A component to be rendered at the root level of AppShell.
52
- * Use guards with redirectTo() for redirects.
71
+ * Guards to control access to this page.
72
+ * Guards are executed in order. Each page must define its own guards explicitly.
53
73
  *
54
74
  * @example
55
75
  * ```tsx
56
- * rootComponent: () => <DashboardHome />
76
+ * guards: [authGuard, adminGuard]
57
77
  * ```
58
78
  */
59
- rootComponent?: () => React.ReactNode;
60
- /**
61
- * Navigation configuration
62
- */
63
- modules: Modules;
64
- /**
65
- * Settings resources to be included in the settings menu
66
- */
67
- settingsResources?: Array<Resource>;
79
+ guards?: Guard[];
68
80
  /**
69
- * Locale code used for built-in UI strings.
70
- *
71
- * If not provided, auto-detects from browser preferences.
72
- * No browser locale information avilable, "en" used as default.
81
+ * Loader function to fetch data before rendering the page.
73
82
  */
74
- locale?: string;
83
+ loader?: LoaderHandler;
84
+ };
85
+
86
+ /**
87
+ * Props for AppShell component.
88
+ *
89
+ * Routes can be configured in two ways:
90
+ * 1. **Automatic (recommended)**: Use the vite-plugin which automatically
91
+ * configures pages via `AppShell.WithPages()`.
92
+ * 2. **Explicit modules**: Pass the `modules` prop for manual configuration.
93
+ *
94
+ * @example
95
+ * ```tsx
96
+ * // Automatic mode (configured by vite-plugin)
97
+ * import { AppShell } from "@tailor-platform/app-shell";
98
+ *
99
+ * <AppShell title="My App">
100
+ * <SidebarLayout />
101
+ * </AppShell>
102
+ * ```
103
+ *
104
+ * @example
105
+ * ```tsx
106
+ * // Explicit modules mode
107
+ * import { AppShell, defineModule } from "@tailor-platform/app-shell";
108
+ *
109
+ * <AppShell title="My App" modules={[...]}>
110
+ * <SidebarLayout />
111
+ * </AppShell>
112
+ * ```
113
+ */
114
+ export declare type AppShellProps = SharedAppShellProps & {
75
115
  /**
76
- * Global error boundary component applied to all routes.
77
- *
78
- * When an error occurs in any route component, this component will render.
79
- * Module and resource-level error boundaries take precedence over this.
80
- * Use the `useRouteError` hook to access error details within the component.
81
- *
82
- * @example
83
- * ```tsx
84
- * import { useRouteError } from "@tailor-platform/app-shell";
85
- *
86
- * const GlobalErrorBoundary = () => {
87
- * const error = useRouteError() as Error;
88
- * return (
89
- * <div>
90
- * <h1>Something went wrong</h1>
91
- * <p>{error.message}</p>
92
- * </div>
93
- * );
94
- * };
116
+ * Navigation configuration.
95
117
  *
96
- * <AppShell
97
- * modules: [...],
98
- * errorBoundary: <GlobalErrorBoundary />,
99
- * />
100
- * ```
118
+ * When using vite-plugin, this is automatically set via `AppShell.WithPages()`.
119
+ * For manual configuration, pass modules directly.
101
120
  */
102
- errorBoundary?: ErrorBoundaryComponent;
103
- /**
104
- * Custom context data accessible from guards and components.
105
- *
106
- * Use module augmentation to define the type of context data:
107
- *
108
- * @example
109
- * ```typescript
110
- * // types.d.ts
111
- * declare module "@tailor-platform/app-shell" {
112
- * interface AppShellRegister {
113
- * contextData: {
114
- * apiClient: ApiClient;
115
- * currentUser: User;
116
- * };
117
- * }
118
- * }
119
- *
120
- * // App.tsx
121
- * <AppShell
122
- * modules={modules}
123
- * contextData={{ apiClient, currentUser }}
124
- * />
125
- * ```
126
- */
127
- contextData?: ContextData;
128
- }>;
121
+ modules?: Modules;
122
+ };
129
123
 
130
124
  /**
131
125
  * Empty interface for module augmentation.
132
- * Users can extend this to define their own context data type.
126
+ * Users can extend this to define their own context data type and route parameters.
133
127
  *
134
128
  * @example
135
129
  * ```typescript
@@ -139,6 +133,11 @@ export declare type AppShellProps = React.PropsWithChildren<{
139
133
  * apiClient: ApiClient;
140
134
  * currentUser: User;
141
135
  * };
136
+ * routeParams: {
137
+ * "/": {};
138
+ * "/dashboard": {};
139
+ * "/orders/:id": { id: string };
140
+ * };
142
141
  * }
143
142
  * }
144
143
  * ```
@@ -292,9 +291,9 @@ declare type CommonProps = {
292
291
  */
293
292
  path: string;
294
293
  /**
295
- * Metadata for the resource.
294
+ * Metadata for the page.
296
295
  */
297
- meta?: ResourceMetaProps;
296
+ meta?: ResourceMeta;
298
297
  /**
299
298
  * Guards to control access to this module/resource.
300
299
  * Guards are executed in order. If any guard returns non-pass result,
@@ -321,16 +320,76 @@ export declare type ContextData = AppShellRegister extends {
321
320
 
322
321
  export declare function createAuthClient(config: AuthClientConfig): EnhancedAuthClient;
323
322
 
323
+ /**
324
+ * Create a type-safe path builder from route definitions.
325
+ *
326
+ * This factory is designed to work with auto-generated RouteParams types
327
+ * from the vite-plugin's `generateTypedRoutes` option.
328
+ *
329
+ * @typeParam TRoutes - Mapping of route paths to their parameter types
330
+ * @returns TypedPaths instance with `for()` method
331
+ *
332
+ * @example
333
+ * ```ts
334
+ * type RouteParams = {
335
+ * "/dashboard": {};
336
+ * "/orders/:id": { id: string };
337
+ * "/orders/:orderId/items/:itemId": { orderId: string; itemId: string };
338
+ * };
339
+ *
340
+ * const paths = createTypedPaths<RouteParams>();
341
+ *
342
+ * paths.for("/dashboard"); // ✅
343
+ * paths.for("/orders/:id", { id: "123" }); // ✅
344
+ * paths.for("/orders/:id"); // ❌ TypeScript error: params required
345
+ * paths.for("/invalid"); // ❌ TypeScript error: path not found
346
+ * ```
347
+ */
348
+ export declare function createTypedPaths<TRoutes extends Record<string, Record<string, string>>>(): TypedPaths<TRoutes>;
349
+
324
350
  /**
325
351
  * Date format options
326
352
  */
327
353
  declare type DateFormat = "short" | "medium" | "long" | "relative";
328
354
 
355
+ /**
356
+ * Default sidebar component with auto-generated navigation items.
357
+ *
358
+ * It works in both auto-generation mode and composition mode.
359
+ * - Auto-generation mode: when no children are provided, it automatically generates sidebar items based on the application's resource definitions.
360
+ * - Composition mode: when children are provided, it allows developers to manually define the sidebar structure using SidebarItem, SidebarGroup, and other components.
361
+ *
362
+ * @example
363
+ * ```tsx
364
+ * // Auto-generation mode
365
+ * <DefaultSidebar />
366
+ *
367
+ * // Composition mode
368
+ * <DefaultSidebar>
369
+ * <SidebarItem to="/dashboard" />
370
+ * <SidebarGroup title="products">
371
+ * <SidebarItem to="/products/all" />
372
+ * </SidebarGroup>
373
+ * <SidebarSeparator />
374
+ * </DefaultSidebar>
375
+ * ```
376
+ */
329
377
  export declare const DefaultSidebar: (props: DefaultSidebarProps) => JSX.Element;
330
378
 
331
- declare type DefaultSidebarProps = {
379
+ export declare type DefaultSidebarProps = {
380
+ /**
381
+ * Header content.
382
+ */
332
383
  header?: React.ReactNode;
384
+ /**
385
+ * Footer content.
386
+ */
333
387
  footer?: React.ReactNode;
388
+ /**
389
+ * When provided, enables explicit sidebar composition using React components.
390
+ * Auto-generation is completely disabled when children is specified.
391
+ */
392
+ children?: React.ReactNode;
334
393
  };
335
394
 
336
395
  /**
@@ -451,7 +510,9 @@ export declare function defineModule(props: DefineModuleProps): Module;
451
510
  declare type DefineModuleProps = CommonProps & CommonModuleProps & {
452
511
  /**
453
512
  * React component to render.
454
- * If not provided, the module will redirect to the first resource.
513
+ *
514
+ * If not provided, a guard with `redirectTo()` must be used to redirect
515
+ * users to the appropriate resource.
455
516
  *
456
517
  * @example
457
518
  * ```tsx
@@ -609,6 +670,35 @@ export declare interface EnhancedAuthClient extends AuthClient {
609
670
  */
610
671
  export declare type ErrorBoundaryComponent = ReactNode;
611
672
 
673
+ /**
674
+ * Type-safe path builder for file-based routing.
675
+ *
676
+ * This module provides a factory function to create type-safe path builders
677
+ * that work with auto-generated route type definitions.
678
+ *
679
+ * @example
680
+ * ```ts
681
+ * // In routes.generated.ts (auto-generated by vite-plugin)
682
+ * import { createTypedPaths } from "@tailor-platform/app-shell";
683
+ *
684
+ * type RouteParams = {
685
+ * "/": {};
686
+ * "/dashboard": {};
687
+ * "/orders/:id": { id: string };
688
+ * };
689
+ *
690
+ * export const paths = createTypedPaths<RouteParams>();
691
+ *
692
+ * // Usage
693
+ * paths.for("/orders/:id", { id: "123" }); // → "/orders/123"
694
+ * paths.for("/orders/:id?tab=details", { id: "123" }); // → "/orders/123?tab=details"
695
+ * ```
696
+ */
697
+ /**
698
+ * Extract base path (before ?) from a path that may include query string.
699
+ */
700
+ declare type ExtractBasePath<T extends string> = T extends `${infer Base}?${string}` ? Base : T;
701
+
612
702
  /**
613
703
  * A field configuration - either a divider or a field definition
614
704
  */
@@ -678,9 +768,6 @@ export declare type Guard = (ctx: GuardContext) => Promise<GuardResult> | GuardR
678
768
  * Context provided to guard functions
679
769
  */
680
770
  export declare type GuardContext = {
681
- params: Params;
682
- searchParams: URLSearchParams;
683
- signal: AbortSignal;
684
771
  context: ContextData;
685
772
  };
686
773
 
@@ -724,6 +811,11 @@ export declare type I18nLabels<Def extends Record<string, LabelValue> = Record<s
724
811
  [K in L]: LabelDefinition<Def>;
725
812
  } & DynamicLocales<Def>;
726
813
 
814
+ /**
815
+ * Check if an object type has no keys.
816
+ */
817
+ declare type IsEmptyObject<T> = keyof T extends never ? true : false;
818
+
727
819
  /**
728
820
  * Ensures that other locales have the same label structure as the base definition.
729
821
  * For dynamic labels (functions), the props type must match.
@@ -812,6 +904,35 @@ declare type Module = Omit<CommonPageResource, "meta"> & {
812
904
 
813
905
  declare type Modules = Array<Module>;
814
906
 
907
+ /**
908
+ * A React component that can be used as a page in file-based routing.
909
+ *
910
+ * The component can optionally have an `appShellPageProps` static field
911
+ * for configuring navigation metadata, guards, and loaders.
912
+ */
913
+ export declare type PageComponent = FC & {
914
+ appShellPageProps?: AppShellPageProps;
915
+ };
916
+
917
+ /**
918
+ * A page entry generated by the Vite plugin from file system.
919
+ */
920
+ declare type PageEntry = {
921
+ /**
922
+ * The resolved path for this page (e.g., "/dashboard/orders/:id")
923
+ */
924
+ path: string;
925
+ /**
926
+ * The page component with optional appShellProps
927
+ */
928
+ component: PageComponent;
929
+ };
930
+
931
+ export declare type PageMeta = {
932
+ title: string;
933
+ icon?: ReactNode;
934
+ };
935
+
815
936
  /**
816
937
  * Allow access to the route. Continue to next guard or render component.
817
938
  *
@@ -871,16 +992,16 @@ export declare type ResourceComponentProps = {
871
992
  resources?: Array<Resource>;
872
993
  };
873
994
 
874
- declare type ResourceMetaProps = {
995
+ declare type ResourceMeta = {
875
996
  /**
876
- * Title of the resource used in navigation.
997
+ * Title of the page used in navigation.
877
998
  *
878
999
  * If not provided, the title will be generated from the path.
879
1000
  */
880
1001
  title?: LocalizedString;
881
1002
  icon?: ReactNode;
882
1003
  /**
883
- * Custom breadcrumb segment title for this resource. Can be a string or a function.
1004
+ * Custom breadcrumb segment title for this page. Can be a string or a function.
884
1005
  */
885
1006
  breadcrumbTitle?: string | ((segment: string) => string);
886
1007
  };
@@ -893,15 +1014,284 @@ declare type RootConfiguration = {
893
1014
  locale: string;
894
1015
  };
895
1016
 
1017
+ /**
1018
+ * Route parameters type inferred from AppShellRegister.
1019
+ * Falls back to Record<string, Record<string, string>> if not augmented.
1020
+ * This is typically set by the generated routes file from vite-plugin.
1021
+ */
1022
+ export declare type RouteParams = AppShellRegister extends {
1023
+ routeParams: infer T;
1024
+ } ? T : Record<string, Record<string, string>>;
1025
+
1026
+ declare type SharedAppShellProps = React.PropsWithChildren<{
1027
+ /**
1028
+ * App shell title
1029
+ */
1030
+ title?: string;
1031
+ /**
1032
+ * App shell icon
1033
+ */
1034
+ icon?: React.ReactNode;
1035
+ /**
1036
+ * Base path for the app shell
1037
+ */
1038
+ basePath?: string;
1039
+ /**
1040
+ * A component to be rendered at the root level of AppShell.
1041
+ * Use guards with redirectTo() for redirects.
1042
+ *
1043
+ * @example
1044
+ * ```tsx
1045
+ * rootComponent: () => <DashboardHome />
1046
+ * ```
1047
+ */
1048
+ rootComponent?: () => React.ReactNode;
1049
+ /**
1050
+ * Settings resources to be included in the settings menu
1051
+ */
1052
+ settingsResources?: Array<Resource>;
1053
+ /**
1054
+ * Locale code used for built-in UI strings.
1055
+ *
1056
+ * If not provided, auto-detects from browser preferences.
1057
+ * No browser locale information avilable, "en" used as default.
1058
+ */
1059
+ locale?: string;
1060
+ /**
1061
+ * Global error boundary component applied to all routes.
1062
+ *
1063
+ * When an error occurs in any route component, this component will render.
1064
+ * Module and resource-level error boundaries take precedence over this.
1065
+ * Use the `useRouteError` hook to access error details within the component.
1066
+ *
1067
+ * @example
1068
+ * ```tsx
1069
+ * import { useRouteError } from "@tailor-platform/app-shell";
1070
+ *
1071
+ * const GlobalErrorBoundary = () => {
1072
+ * const error = useRouteError() as Error;
1073
+ * return (
1074
+ * <div>
1075
+ * <h1>Something went wrong</h1>
1076
+ * <p>{error.message}</p>
1077
+ * </div>
1078
+ * );
1079
+ * };
1080
+ *
1081
+ * <AppShell
1082
+ * modules: [...],
1083
+ * errorBoundary: <GlobalErrorBoundary />,
1084
+ * />
1085
+ * ```
1086
+ */
1087
+ errorBoundary?: ErrorBoundaryComponent;
1088
+ /**
1089
+ * Custom context data accessible from guards and components.
1090
+ *
1091
+ * Use module augmentation to define the type of context data:
1092
+ *
1093
+ * @example
1094
+ * ```typescript
1095
+ * // types.d.ts
1096
+ * declare module "@tailor-platform/app-shell" {
1097
+ * interface AppShellRegister {
1098
+ * contextData: {
1099
+ * apiClient: ApiClient;
1100
+ * currentUser: User;
1101
+ * };
1102
+ * }
1103
+ * }
1104
+ *
1105
+ * // App.tsx
1106
+ * <AppShell
1107
+ * modules={modules}
1108
+ * contextData={{ apiClient, currentUser }}
1109
+ * />
1110
+ * ```
1111
+ */
1112
+ contextData?: ContextData;
1113
+ }>;
1114
+
1115
+ /**
1116
+ * A collapsible group for sidebar navigation.
1117
+ *
1118
+ * @example
1119
+ * ```tsx
1120
+ * // Basic group
1121
+ * <SidebarGroup title={labels.t("products")} icon={<Package />}>
1122
+ * <SidebarItem to="/products/all" />
1123
+ * <SidebarItem to="/products/categories" />
1124
+ * </SidebarGroup>
1125
+ *
1126
+ * // Clickable group header
1127
+ * <SidebarGroup title={labels.t("settings")} icon={<Settings />} to="/settings">
1128
+ * <SidebarItem to="/settings/profile" />
1129
+ * <SidebarItem to="/settings/security" />
1130
+ * </SidebarGroup>
1131
+ *
1132
+ * // Nested groups
1133
+ * <SidebarGroup title={labels.t("products")} icon={<Package />}>
1134
+ * <SidebarItem to="/products/all" />
1135
+ * <SidebarGroup title={labels.t("archives")} defaultOpen={false}>
1136
+ * <SidebarItem to="/products/archives/2024" />
1137
+ * <SidebarItem to="/products/archives/2023" />
1138
+ * </SidebarGroup>
1139
+ * </SidebarGroup>
1140
+ * ```
1141
+ */
1142
+ export declare const SidebarGroup: (props: SidebarGroupProps) => JSX.Element;
1143
+
1144
+ export declare type SidebarGroupProps = {
1145
+ /**
1146
+ * Group title (i18n supported).
1147
+ */
1148
+ title: LocalizedString;
1149
+ /**
1150
+ * Group icon.
1151
+ */
1152
+ icon?: ReactNode;
1153
+ /**
1154
+ * When specified, title becomes a clickable link.
1155
+ */
1156
+ to?: string;
1157
+ /**
1158
+ * Initial expanded state.
1159
+ * @default true
1160
+ */
1161
+ defaultOpen?: boolean;
1162
+ /**
1163
+ * Child items (SidebarItem, SidebarGroup, etc.)
1164
+ */
1165
+ children: ReactNode;
1166
+ };
1167
+
1168
+ /**
1169
+ * A navigation item for the sidebar.
1170
+ *
1171
+ * Automatically resolves title and icon from the resource definition's meta
1172
+ * corresponding to the URL specified in `to`.
1173
+ *
1174
+ * @example
1175
+ * ```tsx
1176
+ * // Auto-resolved from resource meta
1177
+ * <SidebarItem to="/dashboard" />
1178
+ *
1179
+ * // Override title and icon
1180
+ * <SidebarItem to="/" title="Home" icon={<Home />} />
1181
+ *
1182
+ * // Custom rendering
1183
+ * <SidebarItem
1184
+ * to="/tasks"
1185
+ * render={({ title, icon, isActive }) => (
1186
+ * <>
1187
+ * {icon}
1188
+ * {title}
1189
+ * <Badge>5</Badge>
1190
+ * </>
1191
+ * )}
1192
+ * />
1193
+ *
1194
+ * // External link
1195
+ * <SidebarItem to="https://docs.example.com" external />
1196
+ * ```
1197
+ */
1198
+ export declare const SidebarItem: (props: SidebarItemProps) => JSX.Element;
1199
+
1200
+ export declare type SidebarItemProps = {
1201
+ /**
1202
+ * Target URL (required).
1203
+ * External URLs (http://...) are rendered as external links.
1204
+ */
1205
+ to: string;
1206
+ /**
1207
+ * Override title.
1208
+ * When omitted, title is auto-resolved from resource meta.
1209
+ */
1210
+ title?: string;
1211
+ /**
1212
+ * Override icon.
1213
+ * When omitted, icon is auto-resolved from resource meta.
1214
+ */
1215
+ icon?: ReactNode;
1216
+ /**
1217
+ * Opens as external link with `target="_blank"`.
1218
+ * When true, adds an external link icon and opens in new tab.
1219
+ */
1220
+ external?: boolean;
1221
+ /**
1222
+ * How to match the current path for active state.
1223
+ * - "exact": Only highlight when the path matches exactly.
1224
+ * - "prefix": Highlight when the current path starts with `to` (with segment boundary check).
1225
+ * @default "prefix"
1226
+ */
1227
+ activeMatch?: "exact" | "prefix";
1228
+ /**
1229
+ * Custom rendering function.
1230
+ * When omitted, title/icon are auto-resolved from resource meta and default UI is rendered.
1231
+ * When specified, receives title/icon/isActive in render function for full customization.
1232
+ */
1233
+ render?: (props: SidebarItemRenderProps) => ReactNode;
1234
+ };
1235
+
1236
+ declare type SidebarItemRenderProps = {
1237
+ /** Title auto-resolved from resource meta */
1238
+ title: string;
1239
+ /** Target URL */
1240
+ url: string;
1241
+ /** Icon auto-resolved from resource meta */
1242
+ icon?: ReactNode;
1243
+ /** Whether this item is currently active */
1244
+ isActive: boolean;
1245
+ };
1246
+
896
1247
  export declare const SidebarLayout: (props: SidebarLayoutProps) => JSX.Element;
897
1248
 
898
- declare type SidebarLayoutProps = {
1249
+ export declare type SidebarLayoutProps = {
1250
+ /**
1251
+ * Custom content renderer.
1252
+ *
1253
+ * @example
1254
+ * ```tsx
1255
+ * <SidebarLayout>
1256
+ * {({ Outlet }) => (
1257
+ * <>
1258
+ * <CustomHeader />
1259
+ * <Outlet />
1260
+ * <CustomFooter />
1261
+ * </>
1262
+ * )}
1263
+ * </SidebarLayout>
1264
+ * ```
1265
+ */
899
1266
  children?: (props: {
900
1267
  Outlet: () => React.ReactNode;
901
1268
  }) => React.ReactNode;
1269
+ /**
1270
+ * Custom sidebar content.
1271
+ *
1272
+ * @default DefaultSidebar
1273
+ * @example
1274
+ * ```tsx
1275
+ * <SidebarLayout sidebar={<MyCustomSidebar />} />
1276
+ * ```
1277
+ */
902
1278
  sidebar?: React.ReactNode;
903
1279
  };
904
1280
 
1281
+ /**
1282
+ * A visual divider for sidebar navigation.
1283
+ *
1284
+ * @example
1285
+ * ```tsx
1286
+ * <DefaultSidebar>
1287
+ * <SidebarItem to="/dashboard" />
1288
+ * <SidebarSeparator />
1289
+ * <SidebarItem to="/settings" />
1290
+ * </DefaultSidebar>
1291
+ * ```
1292
+ */
1293
+ export declare const SidebarSeparator: () => JSX.Element;
1294
+
905
1295
  declare type Theme = "dark" | "light" | "system";
906
1296
 
907
1297
  declare type ThemeProviderState = {
@@ -910,6 +1300,34 @@ declare type ThemeProviderState = {
910
1300
  setTheme: (theme: Theme) => void;
911
1301
  };
912
1302
 
1303
+ /**
1304
+ * Type-safe path builder interface.
1305
+ *
1306
+ * @typeParam TRoutes - Mapping of route paths to their parameter types
1307
+ */
1308
+ declare type TypedPaths<TRoutes extends Record<string, Record<string, string>>> = {
1309
+ /**
1310
+ * Build a URL path with type-checked parameters.
1311
+ *
1312
+ * @param path - Route path template (can include ?query suffix)
1313
+ * @param params - Path parameters (required if route has dynamic segments)
1314
+ * @returns Resolved URL path string
1315
+ *
1316
+ * @example
1317
+ * ```ts
1318
+ * // Static route
1319
+ * paths.for("/dashboard"); // → "/dashboard"
1320
+ *
1321
+ * // Dynamic route
1322
+ * paths.for("/orders/:id", { id: "123" }); // → "/orders/123"
1323
+ *
1324
+ * // With query string
1325
+ * paths.for("/items/:id?tab=details", { id: "456" }); // → "/items/456?tab=details"
1326
+ * ```
1327
+ */
1328
+ for<T extends (keyof TRoutes & string) | `${keyof TRoutes & string}?${string}`>(path: T, ...params: ExtractBasePath<T> extends keyof TRoutes ? IsEmptyObject<TRoutes[ExtractBasePath<T>]> extends true ? [] : [TRoutes[ExtractBasePath<T>]] : never): string;
1329
+ };
1330
+
913
1331
  /**
914
1332
  * Hook to access the full AppShell context (both config and data).
915
1333
  * For better performance, prefer useAppShellConfig() or useAppShellData()
@@ -1025,6 +1443,15 @@ export { useLocation }
1025
1443
 
1026
1444
  export { useNavigate }
1027
1445
 
1446
+ /**
1447
+ * Find page meta (title, icon) for a given URL path.
1448
+ * Searches through modules and their resources recursively.
1449
+ *
1450
+ * @param path - URL path to find meta for (e.g., "/products/all")
1451
+ * @returns PageMeta if found, null for external links or not found
1452
+ */
1453
+ export declare const usePageMeta: (path: string) => PageMeta | null;
1454
+
1028
1455
  export { useParams }
1029
1456
 
1030
1457
  export { useRouteError }
@@ -1035,4 +1462,72 @@ export declare const useTheme: () => ThemeProviderState;
1035
1462
 
1036
1463
  export declare const useToast: () => typeof toast;
1037
1464
 
1465
+ /**
1466
+ * Conditionally render children based on guard evaluation.
1467
+ *
1468
+ * Guards are evaluated in order. If any guard returns `hidden()`,
1469
+ * the fallback is rendered instead of children. Supports async guards.
1470
+ *
1471
+ * Note: Unlike route guards, `redirectTo()` is not supported in WithGuard.
1472
+ * Use `hidden()` with a fallback that handles navigation if needed.
1473
+ *
1474
+ * @example
1475
+ * ```tsx
1476
+ * // Simple role-based guard
1477
+ * const isAdmin = ({ context }) =>
1478
+ * context.currentUser.role === "admin" ? pass() : hidden();
1479
+ *
1480
+ * <WithGuard guards={[isAdmin]}>
1481
+ * <AdminPanel />
1482
+ * </WithGuard>
1483
+ *
1484
+ * // With fallback
1485
+ * <WithGuard guards={[isAdmin]} fallback={<UpgradePrompt />}>
1486
+ * <AdminPanel />
1487
+ * </WithGuard>
1488
+ *
1489
+ * // Parameterized guard (curried)
1490
+ * const isOwner = (resourceId: string) => ({ context }) =>
1491
+ * resourceId === context.currentUser.id ? pass() : hidden();
1492
+ *
1493
+ * const { id } = useParams();
1494
+ * <WithGuard guards={[isOwner(id)]}>
1495
+ * <EditButton />
1496
+ * </WithGuard>
1497
+ *
1498
+ * // In sidebar
1499
+ * <DefaultSidebar>
1500
+ * <SidebarItem to="/dashboard" />
1501
+ * <WithGuard guards={[isAdmin]}>
1502
+ * <SidebarItem to="/admin" />
1503
+ * </WithGuard>
1504
+ * </DefaultSidebar>
1505
+ * ```
1506
+ */
1507
+ export declare const WithGuard: (props: WithGuardProps) => JSX.Element;
1508
+
1509
+ export declare type WithGuardProps = {
1510
+ /**
1511
+ * Guards to evaluate. All guards must pass for children to render.
1512
+ * Uses the same Guard type as route guards for full reusability.
1513
+ */
1514
+ guards: Guard[];
1515
+ /**
1516
+ * Content to render when all guards pass.
1517
+ */
1518
+ children: ReactNode;
1519
+ /**
1520
+ * Content to render when any guard returns hidden.
1521
+ * @default null (renders nothing)
1522
+ */
1523
+ fallback?: ReactNode;
1524
+ /**
1525
+ * Content to render while guards are being evaluated (for async guards).
1526
+ * @default null (renders nothing)
1527
+ */
1528
+ loading?: ReactNode;
1529
+ };
1530
+
1531
+ declare function withPages(pages: PageEntry[]): FC<AppShellProps>;
1532
+
1038
1533
  export { }