UNPKG

@zapstra/core-router

Version:

Query-preserving router for Zapstra platform apps

219 lines (164 loc) 5.09 kB
# @zapstra/core-router Query-preserving router and navigation utilities for Zapstra platform apps. ## Features - Query parameter preservation during navigation (essential for Shopify apps) - Built-in loading states and progress indicators - React context for navigation state management - TypeScript support with full type safety - Customizable loading spinner with gradient design - Path matching utilities for active state detection ## Installation ```bash npm install @zapstra/core-router ``` ## Usage ### Basic Setup ```typescript import { AppRouterProvider } from '@zapstra/core-router'; export default function App() { return ( <AppRouterProvider> {/* Your app content */} </AppRouterProvider> ); } ``` ### Navigation with Query Preservation ```typescript import { useAppRouter } from '@zapstra/core-router'; function NavigationComponent() { const { route, isNavigating } = useAppRouter(); const handleNavigate = () => { // Automatically preserves ?host, ?shop, and other query params route('/app/settings'); }; return ( <button onClick={handleNavigate} disabled={isNavigating} > Go to Settings </button> ); } ``` ### Direct Navigation Utilities ```typescript import { useNavigateWithQuery, useQueryParams, useIsPathActive } from '@zapstra/core-router'; function MyComponent() { const navigate = useNavigateWithQuery(); const queryParams = useQueryParams(); const isActive = useIsPathActive(); // Navigate preserving query params const goToHome = () => navigate('/app'); // Access current query parameters const shopDomain = queryParams.shop; const host = queryParams.host; // Check if path is active const isSettingsActive = isActive('/app/settings'); const isExactHome = isActive('/app', true); // exact match return ( <div> <button onClick={goToHome}>Home</button> <p>Current shop: {shopDomain}</p> <p>Settings active: {isSettingsActive}</p> </div> ); } ``` ### Custom Loading Spinner ```typescript import { AppRouterProvider } from '@zapstra/core-router'; export default function App() { return ( <AppRouterProvider showLoadingSpinner={true} spinnerProps={{ size: 100, strokeWidth: 6 }} > {/* Your app */} </AppRouterProvider> ); } ``` ### Without Built-in Spinner ```typescript import { AppRouterProvider, useAppRouter, GradientProgressSpinner } from '@zapstra/core-router'; function CustomLoadingWrapper({ children }) { const { isNavigating } = useAppRouter(); return ( <> {isNavigating && <div>Custom loading...</div>} {children} </> ); } export default function App() { return ( <AppRouterProvider showLoadingSpinner={false}> <CustomLoadingWrapper> {/* Your app */} </CustomLoadingWrapper> </AppRouterProvider> ); } ``` ### Higher-Order Component ```typescript import { withAppRouter } from '@zapstra/core-router'; function MyApp() { return <div>My App Content</div>; } export default withAppRouter(MyApp, { showLoadingSpinner: true, spinnerProps: { size: 80 } }); ``` ## Why Query Preservation? Shopify apps run in embedded iframes and require specific query parameters like `?host` and `?shop` to function correctly. Standard navigation can lose these parameters, breaking the app. This router automatically preserves all query parameters during navigation. ```typescript // Without @zapstra/core-router - BREAKS SHOPIFY APPS navigate('/app/settings'); // Loses ?host=... and ?shop=... // With @zapstra/core-router - WORKS CORRECTLY const { route } = useAppRouter(); route('/app/settings'); // Preserves ?host=... and ?shop=... ``` ## API Reference ### Components #### `AppRouterProvider` Main provider component that wraps your app. **Props:** - `children: ReactNode` - Your app content - `showLoadingSpinner?: boolean` - Show built-in loading spinner (default: true) - `spinnerProps?: object` - Customize spinner appearance #### `GradientProgressSpinner` Standalone loading spinner component. **Props:** - `isLoading: boolean` - Whether to show spinner - `size?: number` - Spinner size in pixels (default: 80) - `strokeWidth?: number` - Stroke width (default: 4) ### Hooks #### `useAppRouter()` Main navigation hook. **Returns:** - `route: (path: string) => void` - Navigate function with query preservation - `isNavigating: boolean` - Current navigation state #### `useNavigateWithQuery()` Low-level navigation hook. **Returns:** - `(path: string) => void` - Navigate function with query preservation #### `useQueryParams()` Get current query parameters as object. **Returns:** - `Record<string, string>` - Current query parameters #### `useIsPathActive()` Check if paths are active. **Returns:** - `(path: string, exact?: boolean) => boolean` - Path matching function ### Utilities #### `navigateWithQuery(navigate, path, search)` Navigate while preserving query parameters. #### `withAppRouter(Component, options?)` Higher-order component to wrap apps with router provider.