@tailor-platform/app-shell 0.26.2 → 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.
- package/LICENSE.md +21 -0
- package/dist/app-shell.css +1 -1
- package/dist/{index.d.ts → app-shell.d.ts} +588 -93
- package/dist/app-shell.js +1069 -747
- package/dist/vite-plugin.d.ts +5 -0
- package/dist/vite-plugin.js +4 -0
- package/package.json +28 -11
|
@@ -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:
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
*
|
|
57
|
+
* Metadata for the page used in navigation and breadcrumbs.
|
|
48
58
|
*/
|
|
49
|
-
|
|
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
|
-
*
|
|
52
|
-
*
|
|
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
|
-
*
|
|
76
|
+
* guards: [authGuard, adminGuard]
|
|
57
77
|
* ```
|
|
58
78
|
*/
|
|
59
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
97
|
-
*
|
|
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
|
-
|
|
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
|
|
294
|
+
* Metadata for the page.
|
|
296
295
|
*/
|
|
297
|
-
meta?:
|
|
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
|
-
*
|
|
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
|
|
995
|
+
declare type ResourceMeta = {
|
|
875
996
|
/**
|
|
876
|
-
* Title of the
|
|
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
|
|
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 { }
|