@idealyst/mcp-server 1.2.114 → 1.2.116
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/{generated/types.json → chunk-TLA6ZBC5.js} +33547 -8687
- package/dist/chunk-TLA6ZBC5.js.map +1 -0
- package/dist/index.cjs +45497 -1681
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +299 -136
- package/dist/index.js.map +1 -1
- package/dist/tools/index.cjs +48570 -4913
- package/dist/tools/index.cjs.map +1 -1
- package/dist/tools/index.d.cts +15 -4
- package/dist/tools/index.d.ts +15 -4
- package/dist/tools/index.js +5 -1
- package/package.json +7 -7
- package/dist/chunk-7WPOVADU.js +0 -22319
- package/dist/chunk-7WPOVADU.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
storageGuides,
|
|
8
8
|
toolDefinitions,
|
|
9
9
|
translateGuides
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-TLA6ZBC5.js";
|
|
11
11
|
|
|
12
12
|
// src/index.ts
|
|
13
13
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -2174,11 +2174,13 @@ For tab navigators:
|
|
|
2174
2174
|
|
|
2175
2175
|
\`\`\`tsx
|
|
2176
2176
|
type TabBarScreenOptions = {
|
|
2177
|
-
|
|
2177
|
+
// String form (simplest): just the icon name
|
|
2178
|
+
// Function form: render function (see warning below about size param)
|
|
2179
|
+
tabBarIcon?: string | ((props: {
|
|
2178
2180
|
focused: boolean;
|
|
2179
2181
|
color: string;
|
|
2180
2182
|
size: string | number
|
|
2181
|
-
}) => React.ReactElement;
|
|
2183
|
+
}) => React.ReactElement);
|
|
2182
2184
|
|
|
2183
2185
|
tabBarLabel?: string; // Tab label
|
|
2184
2186
|
tabBarBadge?: string | number; // Badge count
|
|
@@ -2186,9 +2188,11 @@ type TabBarScreenOptions = {
|
|
|
2186
2188
|
} & ScreenOptions;
|
|
2187
2189
|
\`\`\`
|
|
2188
2190
|
|
|
2191
|
+
> **tabBarIcon WARNING:** The function form receives \`{ size: number }\` from native tab bars, but Idealyst's Icon component expects a Size token (\`'xs' | 'sm' | 'md' | 'lg' | 'xl'\`). **Do NOT pass the size param to Icon.** Use a fixed size token instead.
|
|
2192
|
+
|
|
2189
2193
|
> **Nested navigators:** When a Stack is nested inside a Tab, the **stack's root route** still uses \`TabBarScreenOptions\` for its \`options\` (it needs tabBarIcon/tabBarLabel for the tab bar). Type it as \`options: TabBarScreenOptions\` on the stack wrapper route.
|
|
2190
2194
|
|
|
2191
|
-
Example:
|
|
2195
|
+
Example (string form -- simplest):
|
|
2192
2196
|
\`\`\`tsx
|
|
2193
2197
|
{
|
|
2194
2198
|
path: "home",
|
|
@@ -2196,8 +2200,23 @@ Example:
|
|
|
2196
2200
|
component: HomeScreen,
|
|
2197
2201
|
options: {
|
|
2198
2202
|
tabBarLabel: "Home",
|
|
2199
|
-
tabBarIcon:
|
|
2200
|
-
|
|
2203
|
+
tabBarIcon: "home", // Just the icon name -- layout renders it automatically
|
|
2204
|
+
tabBarBadge: 5
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
\`\`\`
|
|
2208
|
+
|
|
2209
|
+
Example (function form -- for focused/unfocused variants):
|
|
2210
|
+
\`\`\`tsx
|
|
2211
|
+
{
|
|
2212
|
+
path: "home",
|
|
2213
|
+
type: 'screen',
|
|
2214
|
+
component: HomeScreen,
|
|
2215
|
+
options: {
|
|
2216
|
+
tabBarLabel: "Home",
|
|
2217
|
+
// Ignore the size param -- use a Size token ('sm', 'md', etc.) instead
|
|
2218
|
+
tabBarIcon: ({ focused }) => (
|
|
2219
|
+
<Icon name={focused ? 'home' : 'home-outline'} size="sm" />
|
|
2201
2220
|
),
|
|
2202
2221
|
tabBarBadge: 5
|
|
2203
2222
|
}
|
|
@@ -2409,7 +2428,7 @@ Section-based navigation with a tab bar.
|
|
|
2409
2428
|
component: HomeScreen,
|
|
2410
2429
|
options: {
|
|
2411
2430
|
tabBarLabel: "Home",
|
|
2412
|
-
tabBarIcon:
|
|
2431
|
+
tabBarIcon: "home", // String form: just the icon name
|
|
2413
2432
|
}
|
|
2414
2433
|
},
|
|
2415
2434
|
{
|
|
@@ -2418,7 +2437,10 @@ Section-based navigation with a tab bar.
|
|
|
2418
2437
|
component: SearchScreen,
|
|
2419
2438
|
options: {
|
|
2420
2439
|
tabBarLabel: "Search",
|
|
2421
|
-
|
|
2440
|
+
// Function form: use focused for icon variants. Ignore size param.
|
|
2441
|
+
tabBarIcon: ({ focused }) => (
|
|
2442
|
+
<Icon name={focused ? 'magnify' : 'magnify'} size="sm" />
|
|
2443
|
+
),
|
|
2422
2444
|
}
|
|
2423
2445
|
},
|
|
2424
2446
|
]
|
|
@@ -2446,34 +2468,110 @@ Section-based navigation with a tab bar.
|
|
|
2446
2468
|
|
|
2447
2469
|
## Drawer Navigator
|
|
2448
2470
|
|
|
2449
|
-
Side menu navigation
|
|
2471
|
+
Side menu navigation with a persistent sidebar. Ideal for dashboards and admin panels.
|
|
2450
2472
|
|
|
2451
2473
|
### Configuration
|
|
2452
2474
|
|
|
2453
2475
|
\`\`\`tsx
|
|
2454
|
-
{
|
|
2476
|
+
import { NavigatorParam } from '@idealyst/navigation';
|
|
2477
|
+
|
|
2478
|
+
const AppRouter: NavigatorParam = {
|
|
2455
2479
|
path: "/",
|
|
2456
2480
|
type: 'navigator',
|
|
2457
2481
|
layout: 'drawer',
|
|
2458
2482
|
routes: [
|
|
2459
|
-
{
|
|
2460
|
-
|
|
2461
|
-
|
|
2483
|
+
{
|
|
2484
|
+
path: "",
|
|
2485
|
+
type: 'screen',
|
|
2486
|
+
component: DashboardScreen,
|
|
2487
|
+
options: { title: 'Dashboard', tabBarLabel: 'Dashboard' }
|
|
2488
|
+
},
|
|
2489
|
+
{
|
|
2490
|
+
path: "users",
|
|
2491
|
+
type: 'screen',
|
|
2492
|
+
component: UsersScreen,
|
|
2493
|
+
options: { title: 'Users', tabBarLabel: 'Users' }
|
|
2494
|
+
},
|
|
2495
|
+
{
|
|
2496
|
+
path: "settings",
|
|
2497
|
+
type: 'screen',
|
|
2498
|
+
component: SettingsScreen,
|
|
2499
|
+
options: { title: 'Settings', tabBarLabel: 'Settings' }
|
|
2500
|
+
},
|
|
2462
2501
|
]
|
|
2502
|
+
};
|
|
2503
|
+
\`\`\`
|
|
2504
|
+
|
|
2505
|
+
> **Route options:** Drawer routes use \`TabBarScreenOptions\` \u2014 set \`tabBarLabel\` for the sidebar label and \`title\` for the screen title. Both are displayed in the default sidebar.
|
|
2506
|
+
|
|
2507
|
+
### Custom Sidebar Component
|
|
2508
|
+
|
|
2509
|
+
You can provide a custom sidebar via \`sidebarComponent\`. The component receives \`DrawerSidebarProps\` which includes:
|
|
2510
|
+
- \`currentPath\`: The current URL/route path \u2014 use this for active state highlighting
|
|
2511
|
+
- \`insets\`: Safe area insets (mobile only, for notch/status bar padding)
|
|
2512
|
+
|
|
2513
|
+
\`\`\`tsx
|
|
2514
|
+
import { DrawerSidebarProps, useNavigator } from '@idealyst/navigation';
|
|
2515
|
+
import { View, Text, Pressable, Icon } from '@idealyst/components';
|
|
2516
|
+
|
|
2517
|
+
// currentPath is available both as a prop AND from useNavigator()
|
|
2518
|
+
function CustomSidebar({ currentPath, insets }: DrawerSidebarProps) {
|
|
2519
|
+
const { navigate } = useNavigator();
|
|
2520
|
+
|
|
2521
|
+
const navItems = [
|
|
2522
|
+
{ path: '/', label: 'Dashboard', icon: 'view-dashboard' as const },
|
|
2523
|
+
{ path: '/projects', label: 'Projects', icon: 'folder' as const },
|
|
2524
|
+
{ path: '/settings', label: 'Settings', icon: 'cog' as const },
|
|
2525
|
+
];
|
|
2526
|
+
|
|
2527
|
+
return (
|
|
2528
|
+
<View style={{ flex: 1, paddingTop: insets?.top ?? 0 }} background="secondary">
|
|
2529
|
+
<Text typography="h6" weight="bold" padding="lg">My App</Text>
|
|
2530
|
+
{navItems.map((item) => {
|
|
2531
|
+
const isActive = currentPath === item.path
|
|
2532
|
+
|| (item.path !== '/' && currentPath.startsWith(item.path + '/'));
|
|
2533
|
+
return (
|
|
2534
|
+
<Pressable key={item.path} onPress={() => navigate({ path: item.path })}>
|
|
2535
|
+
<View
|
|
2536
|
+
style={{ flexDirection: 'row', alignItems: 'center' }}
|
|
2537
|
+
padding="md"
|
|
2538
|
+
gap="md"
|
|
2539
|
+
background={isActive ? 'primary' : 'transparent'}
|
|
2540
|
+
>
|
|
2541
|
+
<Icon name={item.icon} size="md" intent={isActive ? 'primary' : undefined} />
|
|
2542
|
+
<Text weight={isActive ? 'semibold' : 'normal'}>{item.label}</Text>
|
|
2543
|
+
</View>
|
|
2544
|
+
</Pressable>
|
|
2545
|
+
);
|
|
2546
|
+
})}
|
|
2547
|
+
</View>
|
|
2548
|
+
);
|
|
2463
2549
|
}
|
|
2550
|
+
|
|
2551
|
+
const AppRouter: NavigatorParam = {
|
|
2552
|
+
path: "/",
|
|
2553
|
+
type: 'navigator',
|
|
2554
|
+
layout: 'drawer',
|
|
2555
|
+
sidebarComponent: CustomSidebar,
|
|
2556
|
+
routes: [...]
|
|
2557
|
+
};
|
|
2464
2558
|
\`\`\`
|
|
2465
2559
|
|
|
2560
|
+
> **TIP:** \`currentPath\` is available both as a prop in \`DrawerSidebarProps\` AND from \`useNavigator()\`. Either approach works: \`const { navigate, currentPath } = useNavigator()\` or use the prop.
|
|
2561
|
+
|
|
2466
2562
|
### Platform Behavior
|
|
2467
2563
|
|
|
2468
2564
|
**Mobile:**
|
|
2469
|
-
- Slide-out drawer
|
|
2470
|
-
- Swipe gesture to open
|
|
2565
|
+
- Slide-out drawer with swipe gesture
|
|
2471
2566
|
- Overlay when open
|
|
2567
|
+
- Custom \`sidebarComponent\` receives \`DrawerSidebarProps\` with \`currentPath\` and safe area \`insets\`
|
|
2472
2568
|
|
|
2473
2569
|
**Web:**
|
|
2474
|
-
-
|
|
2475
|
-
-
|
|
2476
|
-
-
|
|
2570
|
+
- Fixed 240px left sidebar with navigation items
|
|
2571
|
+
- Items show \`tabBarLabel\` (or \`title\`) from route options
|
|
2572
|
+
- Active route is highlighted
|
|
2573
|
+
- If \`sidebarComponent\` is provided, it replaces the default sidebar
|
|
2574
|
+
- Custom sidebar receives \`DrawerSidebarProps\` with \`currentPath\`
|
|
2477
2575
|
|
|
2478
2576
|
### Use Cases
|
|
2479
2577
|
- Admin panels
|
|
@@ -2570,9 +2668,92 @@ const appRouter: RouteParam = {
|
|
|
2570
2668
|
|
|
2571
2669
|
On web, navigators can use custom layout components to add headers, sidebars, and other UI around route content.
|
|
2572
2670
|
|
|
2671
|
+
## IMPORTANT: Layout Component File Structure
|
|
2672
|
+
|
|
2673
|
+
Layout components are **web-only**. Native ignores \`layoutComponent\` entirely. You must create platform-specific files so the native build doesn't import web-only code:
|
|
2674
|
+
|
|
2675
|
+
\`\`\`
|
|
2676
|
+
layouts/
|
|
2677
|
+
AppLayout.web.tsx \u2190 Real layout with Outlet from @idealyst/navigation
|
|
2678
|
+
AppLayout.native.tsx \u2190 No-op mock (return null)
|
|
2679
|
+
index.web.ts \u2190 export { AppLayout } from './AppLayout.web'
|
|
2680
|
+
index.native.ts \u2190 export { AppLayout } from './AppLayout.native'
|
|
2681
|
+
\`\`\`
|
|
2682
|
+
|
|
2683
|
+
### Example Files
|
|
2684
|
+
|
|
2685
|
+
**\`AppLayout.web.tsx\`** \u2014 The real layout:
|
|
2686
|
+
\`\`\`tsx
|
|
2687
|
+
import React from 'react';
|
|
2688
|
+
import { Outlet, useNavigator } from '@idealyst/navigation';
|
|
2689
|
+
import type { StackLayoutProps } from '@idealyst/navigation';
|
|
2690
|
+
import { View, Text, Pressable, Icon } from '@idealyst/components';
|
|
2691
|
+
|
|
2692
|
+
export function AppLayout({ routes, currentPath, options }: StackLayoutProps) {
|
|
2693
|
+
const { navigate } = useNavigator();
|
|
2694
|
+
return (
|
|
2695
|
+
<View style={{ flex: 1, flexDirection: 'row' }}>
|
|
2696
|
+
<View style={{ width: 240, borderRightWidth: 1, borderRightColor: '#e0e0e0' }}>
|
|
2697
|
+
{routes.map((route) => (
|
|
2698
|
+
<Pressable key={route.fullPath} onPress={() => navigate({ path: route.fullPath })}>
|
|
2699
|
+
<Text style={{ padding: 12, fontWeight: currentPath === route.fullPath ? 'bold' : 'normal' }}>
|
|
2700
|
+
{route.options?.title || route.path}
|
|
2701
|
+
</Text>
|
|
2702
|
+
</Pressable>
|
|
2703
|
+
))}
|
|
2704
|
+
</View>
|
|
2705
|
+
<View style={{ flex: 1 }}>
|
|
2706
|
+
<Outlet />
|
|
2707
|
+
</View>
|
|
2708
|
+
</View>
|
|
2709
|
+
);
|
|
2710
|
+
}
|
|
2711
|
+
\`\`\`
|
|
2712
|
+
|
|
2713
|
+
**\`AppLayout.native.tsx\`** \u2014 No-op mock (native uses its own navigator UI):
|
|
2714
|
+
\`\`\`tsx
|
|
2715
|
+
export function AppLayout() {
|
|
2716
|
+
return null;
|
|
2717
|
+
}
|
|
2718
|
+
\`\`\`
|
|
2719
|
+
|
|
2720
|
+
**\`index.web.ts\`**:
|
|
2721
|
+
\`\`\`ts
|
|
2722
|
+
export { AppLayout } from './AppLayout.web';
|
|
2723
|
+
\`\`\`
|
|
2724
|
+
|
|
2725
|
+
**\`index.native.ts\`**:
|
|
2726
|
+
\`\`\`ts
|
|
2727
|
+
export { AppLayout } from './AppLayout.native';
|
|
2728
|
+
\`\`\`
|
|
2729
|
+
|
|
2730
|
+
**\`AppRouter.ts\`** \u2014 Wiring:
|
|
2731
|
+
\`\`\`tsx
|
|
2732
|
+
import { AppLayout } from './layouts';
|
|
2733
|
+
import type { NavigatorParam } from '@idealyst/navigation';
|
|
2734
|
+
|
|
2735
|
+
const appRouter: NavigatorParam = {
|
|
2736
|
+
path: '/',
|
|
2737
|
+
type: 'navigator',
|
|
2738
|
+
layout: 'stack',
|
|
2739
|
+
layoutComponent: AppLayout, // Web only \u2014 native ignores this
|
|
2740
|
+
routes: [
|
|
2741
|
+
{ path: '', type: 'screen', component: HomeScreen },
|
|
2742
|
+
{ path: 'settings', type: 'screen', component: SettingsScreen },
|
|
2743
|
+
],
|
|
2744
|
+
};
|
|
2745
|
+
\`\`\`
|
|
2746
|
+
|
|
2747
|
+
> **Key rules:**
|
|
2748
|
+
> - Import \`Outlet\` from \`@idealyst/navigation\` (NOT from \`react-router-dom\`)
|
|
2749
|
+
> - Layout props do NOT include \`children\` \u2014 content renders via \`<Outlet />\`
|
|
2750
|
+
> - Always create both \`.web.tsx\` and \`.native.tsx\` files with platform index files
|
|
2751
|
+
|
|
2573
2752
|
## GeneralLayout Component
|
|
2574
2753
|
|
|
2575
|
-
|
|
2754
|
+
> **Note:** \`GeneralLayout\` is a reference pattern shown in documentation. For production apps, build custom layouts using Idealyst components (\`View\`, \`Text\`, \`Pressable\`, \`Icon\`) with \`<Outlet />\` from \`@idealyst/navigation\`. The examples below illustrate the configuration API.
|
|
2755
|
+
|
|
2756
|
+
The \`GeneralLayout\` demonstrates header and sidebar configuration:
|
|
2576
2757
|
|
|
2577
2758
|
### Basic Usage
|
|
2578
2759
|
|
|
@@ -2589,7 +2770,7 @@ import { GeneralLayout } from '@idealyst/navigation';
|
|
|
2589
2770
|
content: <NavigationMenu />,
|
|
2590
2771
|
}}
|
|
2591
2772
|
>
|
|
2592
|
-
|
|
2773
|
+
<Outlet />
|
|
2593
2774
|
</GeneralLayout>
|
|
2594
2775
|
\`\`\`
|
|
2595
2776
|
|
|
@@ -2687,36 +2868,32 @@ const router: NavigatorParam = {
|
|
|
2687
2868
|
### Stack Layout Component
|
|
2688
2869
|
|
|
2689
2870
|
\`\`\`tsx
|
|
2690
|
-
import {
|
|
2871
|
+
import { Outlet } from '@idealyst/navigation';
|
|
2691
2872
|
import type { StackLayoutProps } from '@idealyst/navigation';
|
|
2692
2873
|
|
|
2693
2874
|
export const CustomStackLayout: React.FC<StackLayoutProps> = ({
|
|
2694
|
-
children,
|
|
2695
2875
|
options,
|
|
2696
2876
|
routes,
|
|
2697
2877
|
currentPath
|
|
2698
2878
|
}) => {
|
|
2699
2879
|
return (
|
|
2700
|
-
<
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
}}
|
|
2710
|
-
sidebar={{
|
|
2711
|
-
enabled: true,
|
|
2712
|
-
collapsible: true,
|
|
2713
|
-
content: (
|
|
2880
|
+
<View style={{ flex: 1 }}>
|
|
2881
|
+
{/* Header */}
|
|
2882
|
+
<View style={{ height: 56, flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16 }}>
|
|
2883
|
+
<Text>{options?.headerTitle || 'My App'}</Text>
|
|
2884
|
+
<View style={{ marginLeft: 'auto' }}>{options?.headerRight}</View>
|
|
2885
|
+
</View>
|
|
2886
|
+
<View style={{ flex: 1, flexDirection: 'row' }}>
|
|
2887
|
+
{/* Sidebar */}
|
|
2888
|
+
<View style={{ width: 240 }}>
|
|
2714
2889
|
<NavigationMenu routes={routes} currentPath={currentPath} />
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2890
|
+
</View>
|
|
2891
|
+
{/* Content \u2014 rendered via Outlet, NOT children */}
|
|
2892
|
+
<View style={{ flex: 1 }}>
|
|
2893
|
+
<Outlet />
|
|
2894
|
+
</View>
|
|
2895
|
+
</View>
|
|
2896
|
+
</View>
|
|
2720
2897
|
);
|
|
2721
2898
|
};
|
|
2722
2899
|
\`\`\`
|
|
@@ -2724,10 +2901,10 @@ export const CustomStackLayout: React.FC<StackLayoutProps> = ({
|
|
|
2724
2901
|
### Tab Layout Component
|
|
2725
2902
|
|
|
2726
2903
|
\`\`\`tsx
|
|
2904
|
+
import { Outlet } from '@idealyst/navigation';
|
|
2727
2905
|
import type { TabLayoutProps } from '@idealyst/navigation';
|
|
2728
2906
|
|
|
2729
2907
|
export const CustomTabLayout: React.FC<TabLayoutProps> = ({
|
|
2730
|
-
children,
|
|
2731
2908
|
routes,
|
|
2732
2909
|
currentPath
|
|
2733
2910
|
}) => {
|
|
@@ -2739,8 +2916,8 @@ export const CustomTabLayout: React.FC<TabLayoutProps> = ({
|
|
|
2739
2916
|
<View style={{ flexDirection: 'row', borderBottom: '1px solid #ccc' }}>
|
|
2740
2917
|
{routes.map(route => (
|
|
2741
2918
|
<Pressable
|
|
2742
|
-
key={route.
|
|
2743
|
-
onPress={() => navigator.navigate({ path: route.fullPath
|
|
2919
|
+
key={route.fullPath}
|
|
2920
|
+
onPress={() => navigator.navigate({ path: route.fullPath })}
|
|
2744
2921
|
style={{
|
|
2745
2922
|
padding: 16,
|
|
2746
2923
|
borderBottom: currentPath === route.fullPath ? '2px solid blue' : 'none'
|
|
@@ -2751,9 +2928,9 @@ export const CustomTabLayout: React.FC<TabLayoutProps> = ({
|
|
|
2751
2928
|
))}
|
|
2752
2929
|
</View>
|
|
2753
2930
|
|
|
2754
|
-
{/* Content */}
|
|
2931
|
+
{/* Content \u2014 rendered via Outlet, NOT children */}
|
|
2755
2932
|
<View style={{ flex: 1 }}>
|
|
2756
|
-
|
|
2933
|
+
<Outlet />
|
|
2757
2934
|
</View>
|
|
2758
2935
|
</View>
|
|
2759
2936
|
);
|
|
@@ -2762,6 +2939,8 @@ export const CustomTabLayout: React.FC<TabLayoutProps> = ({
|
|
|
2762
2939
|
|
|
2763
2940
|
## Layout Props Reference
|
|
2764
2941
|
|
|
2942
|
+
> **IMPORTANT:** Layout props do NOT include \`children\`. Route content is rendered via \`<Outlet />\` imported from \`@idealyst/navigation\`.
|
|
2943
|
+
|
|
2765
2944
|
### StackLayoutProps
|
|
2766
2945
|
|
|
2767
2946
|
\`\`\`tsx
|
|
@@ -2769,8 +2948,8 @@ type StackLayoutProps = {
|
|
|
2769
2948
|
options?: NavigatorOptions; // Navigator options
|
|
2770
2949
|
routes: RouteWithFullPath[]; // All routes with full paths
|
|
2771
2950
|
currentPath: string; // Current active path
|
|
2772
|
-
children?: React.ReactNode; // Route content
|
|
2773
2951
|
};
|
|
2952
|
+
// Content renders via <Outlet /> from @idealyst/navigation \u2014 NOT via children
|
|
2774
2953
|
\`\`\`
|
|
2775
2954
|
|
|
2776
2955
|
### TabLayoutProps
|
|
@@ -2780,8 +2959,8 @@ type TabLayoutProps = {
|
|
|
2780
2959
|
options?: NavigatorOptions; // Navigator options
|
|
2781
2960
|
routes: RouteWithFullPath<TabBarScreenOptions>[]; // Tab routes
|
|
2782
2961
|
currentPath: string; // Current active path
|
|
2783
|
-
children?: React.ReactNode; // Route content
|
|
2784
2962
|
};
|
|
2963
|
+
// Content renders via <Outlet /> from @idealyst/navigation \u2014 NOT via children
|
|
2785
2964
|
\`\`\`
|
|
2786
2965
|
|
|
2787
2966
|
## Real-World Examples
|
|
@@ -2789,41 +2968,35 @@ type TabLayoutProps = {
|
|
|
2789
2968
|
### Dashboard Layout
|
|
2790
2969
|
|
|
2791
2970
|
\`\`\`tsx
|
|
2971
|
+
import { Outlet } from '@idealyst/navigation';
|
|
2972
|
+
|
|
2792
2973
|
export const DashboardLayout: React.FC<StackLayoutProps> = ({
|
|
2793
|
-
children,
|
|
2794
2974
|
routes,
|
|
2795
|
-
currentPath
|
|
2975
|
+
currentPath,
|
|
2976
|
+
options
|
|
2796
2977
|
}) => {
|
|
2978
|
+
const { navigate } = useNavigator();
|
|
2797
2979
|
return (
|
|
2798
|
-
<
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
<
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
enabled: true,
|
|
2819
|
-
collapsible: true,
|
|
2820
|
-
position: 'left',
|
|
2821
|
-
expandedWidth: 260,
|
|
2822
|
-
content: <DashboardSidebar routes={routes} currentPath={currentPath} />
|
|
2823
|
-
}}
|
|
2824
|
-
>
|
|
2825
|
-
{children}
|
|
2826
|
-
</GeneralLayout>
|
|
2980
|
+
<View style={{ flex: 1 }}>
|
|
2981
|
+
{/* Header */}
|
|
2982
|
+
<View style={{ height: 72, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', padding: 16, borderBottomWidth: 1, borderBottomColor: '#e0e0e0' }}>
|
|
2983
|
+
<Text typography="h5" weight="bold">Dashboard</Text>
|
|
2984
|
+
<View style={{ flexDirection: 'row', gap: 16 }}>
|
|
2985
|
+
<NotificationBell />
|
|
2986
|
+
<UserAvatar />
|
|
2987
|
+
</View>
|
|
2988
|
+
</View>
|
|
2989
|
+
<View style={{ flex: 1, flexDirection: 'row' }}>
|
|
2990
|
+
{/* Sidebar */}
|
|
2991
|
+
<View style={{ width: 260 }}>
|
|
2992
|
+
<DashboardSidebar routes={routes} currentPath={currentPath} />
|
|
2993
|
+
</View>
|
|
2994
|
+
{/* Content via Outlet */}
|
|
2995
|
+
<View style={{ flex: 1 }}>
|
|
2996
|
+
<Outlet />
|
|
2997
|
+
</View>
|
|
2998
|
+
</View>
|
|
2999
|
+
</View>
|
|
2827
3000
|
);
|
|
2828
3001
|
};
|
|
2829
3002
|
\`\`\`
|
|
@@ -2831,27 +3004,21 @@ export const DashboardLayout: React.FC<StackLayoutProps> = ({
|
|
|
2831
3004
|
### Admin Panel Layout
|
|
2832
3005
|
|
|
2833
3006
|
\`\`\`tsx
|
|
2834
|
-
|
|
3007
|
+
import { Outlet } from '@idealyst/navigation';
|
|
3008
|
+
|
|
3009
|
+
export const AdminLayout: React.FC<StackLayoutProps> = ({ options }) => {
|
|
2835
3010
|
return (
|
|
2836
|
-
<
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
position: 'left',
|
|
2846
|
-
initiallyExpanded: true,
|
|
2847
|
-
content: <AdminNavigationMenu />,
|
|
2848
|
-
style: { backgroundColor: '#2a2a2a' }
|
|
2849
|
-
}}
|
|
2850
|
-
>
|
|
2851
|
-
<View style={{ padding: 24 }}>
|
|
2852
|
-
{children}
|
|
3011
|
+
<View style={{ flex: 1 }}>
|
|
3012
|
+
<AdminHeader />
|
|
3013
|
+
<View style={{ flex: 1, flexDirection: 'row' }}>
|
|
3014
|
+
<View style={{ width: 240, backgroundColor: '#2a2a2a' }}>
|
|
3015
|
+
<AdminNavigationMenu />
|
|
3016
|
+
</View>
|
|
3017
|
+
<View style={{ flex: 1, padding: 24 }}>
|
|
3018
|
+
<Outlet />
|
|
3019
|
+
</View>
|
|
2853
3020
|
</View>
|
|
2854
|
-
</
|
|
3021
|
+
</View>
|
|
2855
3022
|
);
|
|
2856
3023
|
};
|
|
2857
3024
|
\`\`\`
|
|
@@ -3759,18 +3926,18 @@ Every layout component receives these props:
|
|
|
3759
3926
|
|
|
3760
3927
|
\`\`\`tsx
|
|
3761
3928
|
type LayoutProps = {
|
|
3762
|
-
children: React.ReactNode; // The route content (renders via <Outlet />)
|
|
3763
3929
|
options?: NavigatorOptions; // headerTitle, headerLeft, headerRight, etc.
|
|
3764
3930
|
routes: RouteWithFullPath[]; // All child routes with their full paths
|
|
3765
3931
|
currentPath: string; // Currently active route path
|
|
3766
3932
|
};
|
|
3933
|
+
// Content renders via <Outlet /> from @idealyst/navigation \u2014 NOT via children
|
|
3767
3934
|
\`\`\`
|
|
3768
3935
|
|
|
3769
3936
|
**This gives you everything you need to build any navigation UI:**
|
|
3770
3937
|
- \`options\` - What to show in headers
|
|
3771
3938
|
- \`routes\` - What tabs/menu items to render
|
|
3772
3939
|
- \`currentPath\` - Which one is active
|
|
3773
|
-
-
|
|
3940
|
+
- \`<Outlet />\` - Where to render the screen content (import from @idealyst/navigation)
|
|
3774
3941
|
|
|
3775
3942
|
## Stack Navigator Parity
|
|
3776
3943
|
|
|
@@ -3783,10 +3950,9 @@ type LayoutProps = {
|
|
|
3783
3950
|
### Web Implementation
|
|
3784
3951
|
|
|
3785
3952
|
\`\`\`tsx
|
|
3786
|
-
import { Outlet } from '
|
|
3787
|
-
import { View, Text, IconButton, Pressable } from '@idealyst/components';
|
|
3788
|
-
import { useNavigator } from '@idealyst/navigation';
|
|
3953
|
+
import { Outlet, useNavigator } from '@idealyst/navigation';
|
|
3789
3954
|
import type { StackLayoutProps } from '@idealyst/navigation';
|
|
3955
|
+
import { View, Text, IconButton, Pressable } from '@idealyst/components';
|
|
3790
3956
|
|
|
3791
3957
|
export function StackLayout({ options, currentPath }: StackLayoutProps) {
|
|
3792
3958
|
const { canGoBack, goBack } = useNavigator();
|
|
@@ -3863,10 +4029,9 @@ export function StackLayout({ options, currentPath }: StackLayoutProps) {
|
|
|
3863
4029
|
### Web Implementation
|
|
3864
4030
|
|
|
3865
4031
|
\`\`\`tsx
|
|
3866
|
-
import { Outlet } from '
|
|
3867
|
-
import { View, Text, Pressable, Icon, Badge } from '@idealyst/components';
|
|
3868
|
-
import { useNavigator } from '@idealyst/navigation';
|
|
4032
|
+
import { Outlet, useNavigator } from '@idealyst/navigation';
|
|
3869
4033
|
import type { TabLayoutProps } from '@idealyst/navigation';
|
|
4034
|
+
import { View, Text, Pressable, Icon, Badge } from '@idealyst/components';
|
|
3870
4035
|
|
|
3871
4036
|
export function TabLayout({ routes, currentPath }: TabLayoutProps) {
|
|
3872
4037
|
const { navigate } = useNavigator();
|
|
@@ -3903,10 +4068,12 @@ export function TabLayout({ routes, currentPath }: TabLayoutProps) {
|
|
|
3903
4068
|
>
|
|
3904
4069
|
{/* Icon with optional badge */}
|
|
3905
4070
|
<View style={{ position: 'relative' }}>
|
|
3906
|
-
{options?.tabBarIcon
|
|
4071
|
+
{typeof options?.tabBarIcon === 'string' ? (
|
|
4072
|
+
<Icon name={options.tabBarIcon as any} size="sm" intent={isActive ? 'primary' : undefined} />
|
|
4073
|
+
) : options?.tabBarIcon?.({
|
|
3907
4074
|
focused: isActive,
|
|
3908
4075
|
color: isActive ? '#007AFF' : '#8E8E93',
|
|
3909
|
-
size:
|
|
4076
|
+
size: 'sm',
|
|
3910
4077
|
})}
|
|
3911
4078
|
{options?.tabBarBadge && (
|
|
3912
4079
|
<Badge
|
|
@@ -3951,10 +4118,9 @@ export function TabLayout({ routes, currentPath }: TabLayoutProps) {
|
|
|
3951
4118
|
On web, drawers are typically persistent sidebars. Here's how to build both:
|
|
3952
4119
|
|
|
3953
4120
|
\`\`\`tsx
|
|
3954
|
-
import { Outlet } from '
|
|
3955
|
-
import { View, Text, Pressable, Icon } from '@idealyst/components';
|
|
3956
|
-
import { useNavigator } from '@idealyst/navigation';
|
|
4121
|
+
import { Outlet, useNavigator } from '@idealyst/navigation';
|
|
3957
4122
|
import type { StackLayoutProps } from '@idealyst/navigation';
|
|
4123
|
+
import { View, Text, Pressable, Icon } from '@idealyst/components';
|
|
3958
4124
|
|
|
3959
4125
|
export function DrawerLayout({ routes, currentPath, options }: StackLayoutProps) {
|
|
3960
4126
|
const { navigate } = useNavigator();
|
|
@@ -4028,29 +4194,26 @@ The \`GeneralLayout\` component simplifies building layouts:
|
|
|
4028
4194
|
\`\`\`tsx
|
|
4029
4195
|
import { GeneralLayout } from '@idealyst/navigation';
|
|
4030
4196
|
|
|
4031
|
-
export function AppLayout({ options, routes, currentPath
|
|
4197
|
+
export function AppLayout({ options, routes, currentPath }: StackLayoutProps) {
|
|
4198
|
+
const { navigate } = useNavigator();
|
|
4032
4199
|
return (
|
|
4033
|
-
<
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
}}
|
|
4051
|
-
>
|
|
4052
|
-
{children}
|
|
4053
|
-
</GeneralLayout>
|
|
4200
|
+
<View style={{ flex: 1 }}>
|
|
4201
|
+
{/* Header */}
|
|
4202
|
+
<View style={{ height: 56, flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16, borderBottomWidth: 1, borderBottomColor: '#e0e0e0' }}>
|
|
4203
|
+
<Text typography="h6" weight="bold">{options?.headerTitle || 'App'}</Text>
|
|
4204
|
+
<View style={{ marginLeft: 'auto' }}>{options?.headerRight}</View>
|
|
4205
|
+
</View>
|
|
4206
|
+
<View style={{ flex: 1, flexDirection: 'row' }}>
|
|
4207
|
+
{/* Sidebar */}
|
|
4208
|
+
<View style={{ width: 240 }}>
|
|
4209
|
+
<SidebarMenu routes={routes} currentPath={currentPath} />
|
|
4210
|
+
</View>
|
|
4211
|
+
{/* Content via Outlet */}
|
|
4212
|
+
<View style={{ flex: 1 }}>
|
|
4213
|
+
<Outlet />
|
|
4214
|
+
</View>
|
|
4215
|
+
</View>
|
|
4216
|
+
</View>
|
|
4054
4217
|
);
|
|
4055
4218
|
}
|
|
4056
4219
|
\`\`\`
|
|
@@ -4086,7 +4249,7 @@ const appRouter: NavigatorParam = {
|
|
|
4086
4249
|
component: HomeScreen,
|
|
4087
4250
|
options: {
|
|
4088
4251
|
tabBarLabel: "Home",
|
|
4089
|
-
tabBarIcon: ({
|
|
4252
|
+
tabBarIcon: ({ focused }) => <Icon name={focused ? 'home' : 'home-outline'} size="sm" />,
|
|
4090
4253
|
},
|
|
4091
4254
|
},
|
|
4092
4255
|
{
|
|
@@ -4095,7 +4258,7 @@ const appRouter: NavigatorParam = {
|
|
|
4095
4258
|
component: SearchScreen,
|
|
4096
4259
|
options: {
|
|
4097
4260
|
tabBarLabel: "Search",
|
|
4098
|
-
tabBarIcon: (
|
|
4261
|
+
tabBarIcon: () => <Icon name="magnify" size="sm" />,
|
|
4099
4262
|
},
|
|
4100
4263
|
},
|
|
4101
4264
|
],
|
|
@@ -4116,7 +4279,7 @@ const appRouter: NavigatorParam = {
|
|
|
4116
4279
|
3. **Options are your data source** - headerTitle, tabBarIcon, etc. drive your layout
|
|
4117
4280
|
4. **routes array is navigation menu** - Use it to build sidebars, tab bars, menus
|
|
4118
4281
|
5. **currentPath enables active states** - Compare to highlight current item
|
|
4119
|
-
6. **Outlet renders
|
|
4282
|
+
6. **Outlet renders content** - Import from @idealyst/navigation, this is where screen content goes
|
|
4120
4283
|
|
|
4121
4284
|
## Common Patterns
|
|
4122
4285
|
|