@statsbygg/layout 0.1.12 → 0.1.13

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/README.md CHANGED
@@ -63,26 +63,97 @@ src/
63
63
 
64
64
  ## Usage
65
65
 
66
- ### Basic Setup
66
+ ### 1. Defining Your Route Tree
67
67
 
68
- In your Next.js zone's root layout file (`app/layout.tsx`):
68
+ The routing system uses **URL-Driven Logic**. You define a single `RouteNode` tree passed to `RootLayout`.
69
+
70
+ - **External Links**: Any path starting with `http` or `https` is treated as an external parent.
71
+ - **Internal Links**: Any relative path (e.g., `/`, `/about`) is treated as internal to your application (relative to your `basePath`).
72
+
73
+ #### Scenario A: Standard Application
74
+ *This app sits directly under the main Statsbygg site.*
69
75
 
70
76
  ```tsx
71
- import { RootLayout } from '@statsbygg/layout';
72
- import '@digdir/designsystemet-theme';
73
- import '@digdir/designsystemet-css';
77
+ // app/layout.tsx
78
+ import { RootLayout, RouteNode } from '@statsbygg/layout';
79
+
80
+ const ROUTES: RouteNode = {
81
+ label: 'Hjem',
82
+ path: 'https://statsbygg.no', // External Parent
83
+ children: [
84
+ {
85
+ label: 'My App',
86
+ path: '/', // App Root (relative to basePath)
87
+ children: [
88
+ { label: 'Page 1', path: '/page-1' },
89
+ { label: 'Page 2', path: '/page-2' },
90
+ ],
91
+ },
92
+ ],
93
+ };
74
94
 
75
95
  export default function Layout({ children }: { children: React.ReactNode }) {
76
96
  return (
77
97
  <html lang="no">
78
98
  <body>
79
- <RootLayout zoneName="your-app-zone">
80
- {children}
81
- </RootLayout>
99
+ <RootLayout routes={ROUTES}>{children}</RootLayout>
82
100
  </body>
83
101
  </html>
84
102
  );
85
103
  }
104
+
105
+ ```
106
+
107
+ #### Scenario B: Deeply Nested Microfrontend
108
+
109
+ *This app sits deep within a hierarchy of other applications.*
110
+
111
+ ```tsx
112
+ // app/layout.tsx
113
+ const ROUTES: RouteNode = {
114
+ label: 'Hjem',
115
+ path: 'https://statsbygg.no',
116
+ children: [
117
+ {
118
+ label: 'Parent Route',
119
+ path: 'https://statsbygg.no/parent-route', // External Parent 2
120
+ children: [
121
+ {
122
+ label: 'Your App Name',
123
+ path: '/', // Your App Root
124
+ children: [
125
+ { label: 'Your App Route', path: '/your-app-route' }
126
+ ]
127
+ }
128
+ ]
129
+ }
130
+ ]
131
+ };
132
+
133
+ ```
134
+
135
+ ### 2. Handling Dynamic Routes
136
+
137
+ For pages with dynamic IDs (e.g., `/properties/[id]`), use the `useBreadcrumbs` hook.
138
+
139
+ **Note:** You only need to define the *dynamic* portion of the path. The layout package automatically prepends the static parents (Home, App Root, etc.) defined in your `ROUTES` tree.
140
+
141
+ ```tsx
142
+ // app/properties/[id]/page.tsx
143
+ 'use client';
144
+ import { useBreadcrumbs } from '@statsbygg/layout';
145
+
146
+ export default function PropertyPage({ params, propertyName }) {
147
+
148
+ useBreadcrumbs([
149
+ { label: 'Properties', href: '/properties' },
150
+ { label: propertyName, href: `/properties/${params.id}` },
151
+ ]);
152
+
153
+ return <div>...</div>;
154
+ }
155
+ // Resulting Breadcrumbs: Home > My App > Properties > [Property Name]
156
+
86
157
  ```
87
158
 
88
159
  ### Using the Global Store
@@ -121,7 +192,7 @@ Main layout component that orchestrates the entire layout structure.
121
192
  ```tsx
122
193
  interface RootLayoutProps {
123
194
  children: React.ReactNode;
124
- zoneName: string;
195
+ routes: RouteNode;
125
196
  className?: string;
126
197
  }
127
198
  ```
@@ -69,6 +69,26 @@
69
69
  color: var(--ds-color-accent-text-default);
70
70
  }
71
71
 
72
+ .parentLink.active {
73
+ color: var(--ds-color-accent-text-default);
74
+ font-weight: 600;
75
+ text-decoration: none;
76
+ }
77
+
78
+ .childLink.active {
79
+ color: var(--ds-color-accent-text-default);
80
+ font-weight: 500;
81
+ text-decoration: none;
82
+ border-left: 2px solid var(--ds-color-accent-text-default);
83
+ padding-left: calc(var(--ds-size-2) - 2px); /* Adjust padding to prevent layout shift */
84
+ }
85
+
86
+ .compactLink.active {
87
+ color: var(--ds-color-accent-text-default);
88
+ font-weight: 600;
89
+ text-decoration: none;
90
+ }
91
+
72
92
  @keyframes fadeIn {
73
93
  from {
74
94
  opacity: 0;
@@ -0,0 +1,3 @@
1
+ .container {
2
+ /* Add your styles here */
3
+ }
package/dist/index.d.ts CHANGED
@@ -1,20 +1,34 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as zustand_middleware from 'zustand/middleware';
3
3
  import * as zustand from 'zustand';
4
+ import { ReactNode } from 'react';
5
+
6
+ type RouteNode = {
7
+ path: string;
8
+ label: string;
9
+ children?: RouteNode[];
10
+ };
11
+ type BreadcrumbItem = {
12
+ label: string;
13
+ href: string;
14
+ };
15
+ type AncestorChain = {
16
+ externalAncestors: RouteNode[];
17
+ appRoot: RouteNode | null;
18
+ };
19
+ declare function findAppRootWithAncestors(node: RouteNode, ancestors?: RouteNode[]): AncestorChain;
20
+ declare function getBreadcrumbs(routes: RouteNode, pathname: string): BreadcrumbItem[];
4
21
 
5
22
  type RootLayoutProps = {
6
23
  children: React.ReactNode;
7
- zone: string;
24
+ routes: RouteNode;
25
+ appBasePath?: string;
8
26
  className?: string;
9
27
  };
10
28
 
11
- declare function RootLayout({ children, zone, className, }: RootLayoutProps): react_jsx_runtime.JSX.Element;
29
+ declare function RootLayout({ children, routes, appBasePath, className }: RootLayoutProps): react_jsx_runtime.JSX.Element;
12
30
 
13
31
  type Theme = 'light' | 'dark' | 'system';
14
- type BreadcrumbItem = {
15
- label: string;
16
- href: string;
17
- };
18
32
  type GlobalState = {
19
33
  user?: {
20
34
  id: string;
@@ -23,16 +37,14 @@ type GlobalState = {
23
37
  theme: Theme;
24
38
  locale: string;
25
39
  isMenuOpen: boolean;
26
- zone: string | null;
27
40
  breadcrumbs: BreadcrumbItem[] | null;
28
41
  setUser: (user: GlobalState['user']) => void;
29
42
  setTheme: (theme: Theme) => void;
30
43
  setLocale: (locale: string) => void;
31
44
  setIsMenuOpen: (isOpen: boolean) => void;
32
45
  toggleMenu: () => void;
33
- setZone: (zone: string) => void;
34
46
  setBreadcrumbs: (breadcrumbs: BreadcrumbItem[] | null) => void;
35
- initialize: () => void | Promise<void>;
47
+ initialize: () => void;
36
48
  };
37
49
  declare const useGlobalStore: zustand.UseBoundStore<Omit<zustand.StoreApi<GlobalState>, "setState" | "persist"> & {
38
50
  setState(partial: GlobalState | Partial<GlobalState> | ((state: GlobalState) => GlobalState | Partial<GlobalState>), replace?: false | undefined): unknown;
@@ -48,6 +60,42 @@ declare const useGlobalStore: zustand.UseBoundStore<Omit<zustand.StoreApi<Global
48
60
  };
49
61
  }>;
50
62
 
63
+ /**
64
+ * Hook for manually setting breadcrumbs on dynamic routes.
65
+ * Overrides automatic breadcrumb generation, preserving external ancestors.
66
+ *
67
+ * @param breadcrumbs - Array of breadcrumb items for the current dynamic route
68
+ */
51
69
  declare function useBreadcrumbs(breadcrumbs: BreadcrumbItem[]): void;
52
70
 
53
- export { type GlobalState, RootLayout, type RootLayoutProps, useBreadcrumbs, useGlobalStore };
71
+ type SmartLinkProps = {
72
+ href: string;
73
+ appBasePath?: string;
74
+ external?: boolean;
75
+ className?: string;
76
+ children: ReactNode;
77
+ };
78
+
79
+ /**
80
+ * Determines if a given href belongs to the current zone, another zone or should be external
81
+ */
82
+ declare function SmartLink({ href, appBasePath, external, className, children, }: SmartLinkProps): react_jsx_runtime.JSX.Element;
83
+
84
+ type MenuItem = {
85
+ label: string;
86
+ href: string;
87
+ external?: boolean;
88
+ children?: MenuItem[];
89
+ };
90
+ type MenuSubsection = {
91
+ title: string;
92
+ items: MenuItem[];
93
+ };
94
+ type MenuSection = {
95
+ title: string;
96
+ layout?: 'columns' | 'subsections';
97
+ items?: MenuItem[];
98
+ subsections?: MenuSubsection[];
99
+ };
100
+
101
+ export { type BreadcrumbItem, type GlobalState, type MenuItem, type MenuSection, type MenuSubsection, RootLayout, type RootLayoutProps, type RouteNode, SmartLink, type SmartLinkProps, findAppRootWithAncestors, getBreadcrumbs, useBreadcrumbs, useGlobalStore };