@logickernel/frame 0.1.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.
Files changed (50) hide show
  1. package/README.md +461 -0
  2. package/dist/adapters/nextjs.js +31 -0
  3. package/dist/adapters/nextjs.js.map +1 -0
  4. package/dist/adapters/nextjs.mjs +25 -0
  5. package/dist/adapters/nextjs.mjs.map +1 -0
  6. package/dist/components/AppSidebar.js +467 -0
  7. package/dist/components/AppSidebar.js.map +1 -0
  8. package/dist/components/AppSidebar.mjs +446 -0
  9. package/dist/components/AppSidebar.mjs.map +1 -0
  10. package/dist/components/NavMain.js +133 -0
  11. package/dist/components/NavMain.js.map +1 -0
  12. package/dist/components/NavMain.mjs +112 -0
  13. package/dist/components/NavMain.mjs.map +1 -0
  14. package/dist/components/NavUser.js +88 -0
  15. package/dist/components/NavUser.js.map +1 -0
  16. package/dist/components/NavUser.mjs +86 -0
  17. package/dist/components/NavUser.mjs.map +1 -0
  18. package/dist/components/TeamSwitcher.js +164 -0
  19. package/dist/components/TeamSwitcher.js.map +1 -0
  20. package/dist/components/TeamSwitcher.mjs +162 -0
  21. package/dist/components/TeamSwitcher.mjs.map +1 -0
  22. package/dist/hooks/useNavigation.d.mts +24 -0
  23. package/dist/hooks/useNavigation.d.ts +24 -0
  24. package/dist/hooks/useNavigation.js +59 -0
  25. package/dist/hooks/useNavigation.js.map +1 -0
  26. package/dist/hooks/useNavigation.mjs +57 -0
  27. package/dist/hooks/useNavigation.mjs.map +1 -0
  28. package/dist/index.d.mts +58 -0
  29. package/dist/index.d.ts +58 -0
  30. package/dist/index.js +495 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/index.mjs +465 -0
  33. package/dist/index.mjs.map +1 -0
  34. package/dist/types/index.d.mts +85 -0
  35. package/dist/types/index.d.ts +85 -0
  36. package/dist/types/index.js +4 -0
  37. package/dist/types/index.js.map +1 -0
  38. package/dist/types/index.mjs +3 -0
  39. package/dist/types/index.mjs.map +1 -0
  40. package/dist/types/ui-components.d.js +4 -0
  41. package/dist/types/ui-components.d.js.map +1 -0
  42. package/dist/types/ui-components.d.mjs +3 -0
  43. package/dist/types/ui-components.d.mjs.map +1 -0
  44. package/dist/utils/iconMapper.d.mts +16 -0
  45. package/dist/utils/iconMapper.d.ts +16 -0
  46. package/dist/utils/iconMapper.js +52 -0
  47. package/dist/utils/iconMapper.js.map +1 -0
  48. package/dist/utils/iconMapper.mjs +30 -0
  49. package/dist/utils/iconMapper.mjs.map +1 -0
  50. package/package.json +76 -0
package/README.md ADDED
@@ -0,0 +1,461 @@
1
+ # @logickernel/frame
2
+
3
+ Shared navigation and layout components for microfrontends and core applications.
4
+
5
+ ## Overview
6
+
7
+ This library provides reusable navigation and layout components that can be shared across multiple microfrontends. The library supports both props-based and API-based navigation configuration with role-based filtering.
8
+
9
+ ## Features
10
+
11
+ - **Shared Layout Components**: AppSidebar, NavMain, NavUser, TeamSwitcher
12
+ - **Dual Navigation Modes**:
13
+ - Props-based: Pass navigation items as props
14
+ - API-based: Fetch navigation from API with role-based filtering
15
+ - **Role-Based Filtering**: Server-side filtering of navigation items based on user roles
16
+ - **Type-Safe**: Full TypeScript support with exported types
17
+ - **Framework Agnostic**: Works with any React framework (Next.js, Remix, etc.)
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install @logickernel/frame
23
+ # or
24
+ yarn add @logickernel/frame
25
+ # or
26
+ pnpm add @logickernel/frame
27
+ ```
28
+
29
+ ### Peer Dependencies
30
+
31
+ This library requires the following peer dependencies to be installed in your project:
32
+
33
+ ```bash
34
+ npm install react react-dom lucide-react
35
+ npm install @radix-ui/react-avatar @radix-ui/react-collapsible @radix-ui/react-dropdown-menu @radix-ui/react-slot @radix-ui/react-tooltip
36
+ ```
37
+
38
+ **For Next.js users**, you'll also need:
39
+ ```bash
40
+ npm install next next-auth
41
+ ```
42
+
43
+ ### UI Components Requirement
44
+
45
+ This library uses shadcn/ui components that must be available in your application. You need to have the following components installed and accessible via the `@/components/ui/*` path alias:
46
+
47
+ - `@/components/ui/sidebar`
48
+ - `@/components/ui/avatar`
49
+ - `@/components/ui/dropdown-menu`
50
+ - `@/components/ui/collapsible`
51
+
52
+ To install these components, use shadcn/ui:
53
+
54
+ ```bash
55
+ npx shadcn@latest add sidebar avatar dropdown-menu collapsible
56
+ ```
57
+
58
+ Make sure your `tsconfig.json` has the path alias configured:
59
+
60
+ ```json
61
+ {
62
+ "compilerOptions": {
63
+ "paths": {
64
+ "@/*": ["./src/*"]
65
+ }
66
+ }
67
+ }
68
+ ```
69
+
70
+ ## Usage
71
+
72
+ ### Framework Adapter
73
+
74
+ The library is framework-agnostic and requires a framework adapter to be provided. For Next.js applications, use the provided adapter:
75
+
76
+ ```typescript
77
+ import { AppSidebar, createNextJSAdapter } from "@logickernel/frame"
78
+ import type { User, Organization } from "@logickernel/frame"
79
+
80
+ // Create the adapter (do this once, can be reused)
81
+ const adapter = createNextJSAdapter()
82
+ ```
83
+
84
+ For other frameworks, you'll need to implement the `FrameworkAdapter` interface (see Types section).
85
+
86
+ ### Props Mode vs API Mode
87
+
88
+ The component automatically detects which mode to use:
89
+
90
+ - **Props Mode**: Provide `data` → organizations and navigation come from props
91
+ - **API Mode**: Omit `data` → organizations and navigation come from the API
92
+
93
+ ### Props Mode
94
+
95
+ Pass a `data` object containing organizations and navigation items. The `{organizationId}` placeholder in URLs is automatically replaced.
96
+
97
+ ```typescript
98
+ import { AppSidebar, createNextJSAdapter } from "@logickernel/frame"
99
+ import { Home, Users, GalleryVerticalEnd } from "lucide-react"
100
+ import type { SidebarData, User } from "@logickernel/frame"
101
+
102
+ const adapter = createNextJSAdapter()
103
+
104
+ const user: User = {
105
+ name: "John Doe",
106
+ email: "john@example.com",
107
+ image: null,
108
+ }
109
+
110
+ const data: SidebarData = {
111
+ organizations: [
112
+ { id: "org-1", name: "Acme Corp", plan: "Pro" },
113
+ { id: "org-2", name: "Another Org", plan: "Free" },
114
+ ],
115
+ navigationItems: [
116
+ { title: "Organization", icon: GalleryVerticalEnd },
117
+ { title: "Home", url: "/app/{organizationId}/home", icon: Home },
118
+ { title: "Members", url: "/app/{organizationId}/members", icon: Users },
119
+ ],
120
+ }
121
+
122
+ export default function Layout({ children }: { children: React.ReactNode }) {
123
+ return (
124
+ <>
125
+ <AppSidebar user={user} adapter={adapter} data={data} />
126
+ <main>{children}</main>
127
+ </>
128
+ )
129
+ }
130
+ ```
131
+
132
+ ### API Mode
133
+
134
+ Omit `data` — the component fetches organizations and navigation from the API.
135
+
136
+ ```typescript
137
+ import { AppSidebar, createNextJSAdapter } from "@logickernel/frame"
138
+
139
+ const adapter = createNextJSAdapter()
140
+
141
+ export default function Layout({ children }: { children: React.ReactNode }) {
142
+ return (
143
+ <>
144
+ <AppSidebar
145
+ user={user}
146
+ adapter={adapter}
147
+ // apiBaseUrl="/api" // Optional, defaults to "/api"
148
+ />
149
+ <main>{children}</main>
150
+ </>
151
+ )
152
+ }
153
+ ```
154
+
155
+ The API endpoint (`/api/navigation/[organization_id]`) should:
156
+ - Authenticate the user via shared cookies
157
+ - Fetch user roles in the organization
158
+ - Filter navigation items based on role requirements
159
+ - Return filtered navigation items and optionally organizations
160
+
161
+ **Expected API Response:**
162
+ ```json
163
+ {
164
+ "items": [
165
+ {
166
+ "title": "Organization",
167
+ "icon": "GalleryVerticalEnd"
168
+ },
169
+ {
170
+ "title": "Home",
171
+ "url": "/app/{organizationId}/home",
172
+ "icon": "Home"
173
+ }
174
+ ],
175
+ "organizationId": "org-1",
176
+ "organizations": [
177
+ {
178
+ "id": "org-1",
179
+ "name": "Acme Corp",
180
+ "plan": "Pro"
181
+ },
182
+ {
183
+ "id": "org-2",
184
+ "name": "Another Org",
185
+ "plan": "Free"
186
+ }
187
+ ]
188
+ }
189
+ ```
190
+
191
+ **Note:** In API mode, the `organizations` field is **required** in the API response. The API determines which organizations the user belongs to. The `logo` field in organizations is optional - if omitted, a default icon will be used.
192
+
193
+ ## Components
194
+
195
+ ### AppSidebar
196
+
197
+ Main sidebar component that combines TeamSwitcher, NavMain, and NavUser.
198
+
199
+ **Props:**
200
+ - `user: User` - User information (name, email, image)
201
+ - `adapter: FrameworkAdapter` - Framework adapter (use `createNextJSAdapter()` for Next.js)
202
+ - `data?: SidebarData` - Sidebar data with organizations and navigation items (props mode)
203
+ - `organizationId?: string` - Current organization ID (optional, extracted from pathname)
204
+ - `apiBaseUrl?: string` - Custom API base URL (API mode only, defaults to "/api")
205
+ - All other props from `Sidebar` component
206
+
207
+ **Mode Detection:**
208
+ - If `data` is provided → Props Mode
209
+ - If `data` is omitted → API Mode
210
+
211
+ ### NavMain
212
+
213
+ Renders navigation menu items with support for groups and nested items.
214
+
215
+ **Props:**
216
+ - `items: NavigationItem[]` - Array of navigation items
217
+ - `adapter: FrameworkAdapter` - Framework adapter
218
+
219
+ ### NavUser
220
+
221
+ User profile dropdown with sign-out functionality.
222
+
223
+ **Props:**
224
+ - `user: User` - User information
225
+ - `adapter: FrameworkAdapter` - Framework adapter
226
+
227
+ ### TeamSwitcher
228
+
229
+ Organization switcher dropdown.
230
+
231
+ **Props:**
232
+ - `teams: Organization[]` - List of organizations
233
+ - `organizationId?: string` - Current organization ID
234
+ - `adapter: FrameworkAdapter` - Framework adapter
235
+
236
+ ## Hooks
237
+
238
+ ### useNavigation
239
+
240
+ Hook to fetch navigation items and organizations from API.
241
+
242
+ ```typescript
243
+ import { useNavigation } from "@logickernel/frame"
244
+
245
+ const { items, organizations, loading, error } = useNavigation({
246
+ organizationId: "org-1",
247
+ apiBaseUrl: "/api",
248
+ enabled: true,
249
+ })
250
+ ```
251
+
252
+ **Options:**
253
+ - `organizationId?: string` - Organization ID to fetch navigation for
254
+ - `apiBaseUrl?: string` - API base URL (default: "/api")
255
+ - `enabled?: boolean` - Enable/disable fetching (default: true)
256
+
257
+ **Returns:**
258
+ - `items: NavigationItem[]` - Navigation items
259
+ - `organizations: Organization[]` - Organizations (if provided by API)
260
+ - `loading: boolean` - Loading state
261
+ - `error: Error | null` - Error state
262
+
263
+ ## Types
264
+
265
+ ### NavigationItem
266
+
267
+ ```typescript
268
+ interface NavigationItem {
269
+ title: string
270
+ url?: string
271
+ icon?: string | LucideIcon
272
+ isActive?: boolean
273
+ items?: {
274
+ title: string
275
+ url: string
276
+ }[]
277
+ }
278
+ ```
279
+
280
+ ### User
281
+
282
+ ```typescript
283
+ interface User {
284
+ name: string | null
285
+ email: string
286
+ image: string | null
287
+ }
288
+ ```
289
+
290
+ ### Organization
291
+
292
+ ```typescript
293
+ interface Organization {
294
+ id: string
295
+ name: string
296
+ logo?: LucideIcon | React.ComponentType<{ className?: string }>
297
+ plan?: string
298
+ }
299
+ ```
300
+
301
+ ### SidebarData
302
+
303
+ ```typescript
304
+ interface SidebarData {
305
+ organizations: Organization[]
306
+ navigationItems: NavigationItem[]
307
+ }
308
+ ```
309
+
310
+ ### NavigationConfig
311
+
312
+ ```typescript
313
+ interface NavigationConfig {
314
+ items: NavigationItem[]
315
+ organizationId?: string
316
+ }
317
+ ```
318
+
319
+ ### FrameworkAdapter
320
+
321
+ ```typescript
322
+ interface FrameworkAdapter {
323
+ usePathname: () => string
324
+ useRouter: () => Router
325
+ Link: ComponentType<LinkProps>
326
+ signOut?: (options?: { redirect?: boolean }) => Promise<void>
327
+ }
328
+ ```
329
+
330
+ ### Router
331
+
332
+ ```typescript
333
+ interface Router {
334
+ push: (path: string) => void
335
+ refresh?: () => void
336
+ }
337
+ ```
338
+
339
+ ### LinkProps
340
+
341
+ ```typescript
342
+ interface LinkProps {
343
+ href: string
344
+ children: ReactNode
345
+ className?: string
346
+ }
347
+ ```
348
+
349
+ ## Utilities
350
+
351
+ ### getIconComponent
352
+
353
+ Utility to convert icon name strings to Lucide React icon components.
354
+
355
+ ```typescript
356
+ import { getIconComponent } from "@logickernel/frame"
357
+
358
+ const Icon = getIconComponent("Home") // Returns Home icon component
359
+ ```
360
+
361
+ ## Adapters
362
+
363
+ ### createNextJSAdapter
364
+
365
+ Creates a Next.js adapter for the framework. Use this when using the library in a Next.js application.
366
+
367
+ ```typescript
368
+ import { createNextJSAdapter } from "@logickernel/frame"
369
+
370
+ const adapter = createNextJSAdapter()
371
+ ```
372
+
373
+ **Note**: This requires `next` and `next-auth` to be installed.
374
+
375
+ ### Custom Adapters
376
+
377
+ For other frameworks, implement the `FrameworkAdapter` interface:
378
+
379
+ ```typescript
380
+ import type { FrameworkAdapter } from "@logickernel/frame"
381
+
382
+ const myAdapter: FrameworkAdapter = {
383
+ usePathname: () => {
384
+ // Return current pathname
385
+ return window.location.pathname
386
+ },
387
+ useRouter: () => ({
388
+ push: (path: string) => {
389
+ // Navigate to path
390
+ window.history.pushState({}, "", path)
391
+ },
392
+ refresh: () => {
393
+ // Refresh the page
394
+ window.location.reload()
395
+ },
396
+ }),
397
+ Link: ({ href, children, className }) => (
398
+ <a href={href} className={className}>
399
+ {children}
400
+ </a>
401
+ ),
402
+ signOut: async () => {
403
+ // Handle sign out
404
+ },
405
+ }
406
+ ```
407
+
408
+ ## Role-Based Filtering
409
+
410
+ Navigation items can specify `requiredRoles` in the service layer configuration. Items are filtered server-side based on the user's roles in the organization.
411
+
412
+ Example API-side configuration:
413
+ ```typescript
414
+ {
415
+ title: "Settings",
416
+ url: "/app/{organizationId}/settings",
417
+ icon: "Settings",
418
+ requiredRoles: ["organization.owner", "organization.editor"]
419
+ }
420
+ ```
421
+
422
+ ## Cross-Origin Setup (Microfrontends)
423
+
424
+ If your microfrontends are on different domains, configure CORS in your navigation API:
425
+
426
+ ```typescript
427
+ // In your navigation API route
428
+ const response = NextResponse.json(data)
429
+ response.headers.set("Access-Control-Allow-Origin", "https://microfrontend.example.com")
430
+ response.headers.set("Access-Control-Allow-Credentials", "true")
431
+ return response
432
+ ```
433
+
434
+ For cross-origin requests, use the full API URL:
435
+
436
+ ```typescript
437
+ <AppSidebar
438
+ user={user}
439
+ organizations={organizations}
440
+ organizationId={orgId}
441
+ useApiNavigation={true}
442
+ apiBaseUrl="https://kernel.example.com/api"
443
+ />
444
+ ```
445
+
446
+ ## Development
447
+
448
+ ```bash
449
+ # Build the library
450
+ npm run build
451
+
452
+ # Watch mode for development
453
+ npm run dev
454
+
455
+ # Type check
456
+ npm run type-check
457
+ ```
458
+
459
+ ## License
460
+
461
+ MIT
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ var Link = require('next/link');
4
+ var navigation = require('next/navigation');
5
+ var react = require('next-auth/react');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var Link__default = /*#__PURE__*/_interopDefault(Link);
11
+
12
+ // src/adapters/nextjs.tsx
13
+ function createNextJSAdapter() {
14
+ const NextJSLink = ({ href, children, className }) => /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href, className, children });
15
+ return {
16
+ usePathname: navigation.usePathname,
17
+ useRouter: () => {
18
+ const router = navigation.useRouter();
19
+ return {
20
+ push: (path) => router.push(path),
21
+ refresh: () => router.refresh()
22
+ };
23
+ },
24
+ Link: NextJSLink,
25
+ signOut: react.signOut
26
+ };
27
+ }
28
+
29
+ exports.createNextJSAdapter = createNextJSAdapter;
30
+ //# sourceMappingURL=nextjs.js.map
31
+ //# sourceMappingURL=nextjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/nextjs.tsx"],"names":["jsx","Link","usePathname","useRouter","signOut"],"mappings":";;;;;;;;;;;;AAiBO,SAAS,mBAAA,GAAwC;AACtD,EAAA,MAAM,UAAA,GAA6C,CAAC,EAAE,IAAA,EAAM,QAAA,EAAU,SAAA,EAAU,qBAC9EA,cAAA,CAACC,qBAAA,EAAA,EAAK,IAAA,EAAY,SAAA,EACf,QAAA,EACH,CAAA;AAGF,EAAA,OAAO;AAAA,iBACLC,sBAAA;AAAA,IACA,WAAW,MAAM;AACf,MAAA,MAAM,SAASC,oBAAA,EAAU;AACzB,MAAA,OAAO;AAAA,QACL,IAAA,EAAM,CAAC,IAAA,KAAiB,MAAA,CAAO,KAAK,IAAI,CAAA;AAAA,QACxC,OAAA,EAAS,MAAM,MAAA,CAAO,OAAA;AAAQ,OAChC;AAAA,IACF,CAAA;AAAA,IACA,IAAA,EAAM,UAAA;AAAA,aACNC;AAAA,GACF;AACF","file":"nextjs.js","sourcesContent":["/**\n * Next.js adapter for @logickernel/frame\n * Provides Next.js-specific implementations of FrameworkAdapter\n */\n\nimport * as React from \"react\"\nimport Link from \"next/link\"\nimport { usePathname, useRouter } from \"next/navigation\"\nimport { signOut } from \"next-auth/react\"\nimport type { FrameworkAdapter, LinkProps } from \"../types\"\n\n/**\n * Creates a Next.js adapter for the framework\n * Use this when using @logickernel/frame in a Next.js application\n * \n * Note: This requires Next.js and next-auth to be installed\n */\nexport function createNextJSAdapter(): FrameworkAdapter {\n const NextJSLink: React.ComponentType<LinkProps> = ({ href, children, className }) => (\n <Link href={href} className={className}>\n {children}\n </Link>\n )\n\n return {\n usePathname,\n useRouter: () => {\n const router = useRouter()\n return {\n push: (path: string) => router.push(path),\n refresh: () => router.refresh(),\n }\n },\n Link: NextJSLink,\n signOut: signOut as FrameworkAdapter[\"signOut\"],\n }\n}\n\n"]}
@@ -0,0 +1,25 @@
1
+ import Link from 'next/link';
2
+ import { usePathname, useRouter } from 'next/navigation';
3
+ import { signOut } from 'next-auth/react';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ // src/adapters/nextjs.tsx
7
+ function createNextJSAdapter() {
8
+ const NextJSLink = ({ href, children, className }) => /* @__PURE__ */ jsx(Link, { href, className, children });
9
+ return {
10
+ usePathname,
11
+ useRouter: () => {
12
+ const router = useRouter();
13
+ return {
14
+ push: (path) => router.push(path),
15
+ refresh: () => router.refresh()
16
+ };
17
+ },
18
+ Link: NextJSLink,
19
+ signOut
20
+ };
21
+ }
22
+
23
+ export { createNextJSAdapter };
24
+ //# sourceMappingURL=nextjs.mjs.map
25
+ //# sourceMappingURL=nextjs.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/nextjs.tsx"],"names":[],"mappings":";;;;;;AAiBO,SAAS,mBAAA,GAAwC;AACtD,EAAA,MAAM,UAAA,GAA6C,CAAC,EAAE,IAAA,EAAM,QAAA,EAAU,SAAA,EAAU,qBAC9E,GAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAY,SAAA,EACf,QAAA,EACH,CAAA;AAGF,EAAA,OAAO;AAAA,IACL,WAAA;AAAA,IACA,WAAW,MAAM;AACf,MAAA,MAAM,SAAS,SAAA,EAAU;AACzB,MAAA,OAAO;AAAA,QACL,IAAA,EAAM,CAAC,IAAA,KAAiB,MAAA,CAAO,KAAK,IAAI,CAAA;AAAA,QACxC,OAAA,EAAS,MAAM,MAAA,CAAO,OAAA;AAAQ,OAChC;AAAA,IACF,CAAA;AAAA,IACA,IAAA,EAAM,UAAA;AAAA,IACN;AAAA,GACF;AACF","file":"nextjs.mjs","sourcesContent":["/**\n * Next.js adapter for @logickernel/frame\n * Provides Next.js-specific implementations of FrameworkAdapter\n */\n\nimport * as React from \"react\"\nimport Link from \"next/link\"\nimport { usePathname, useRouter } from \"next/navigation\"\nimport { signOut } from \"next-auth/react\"\nimport type { FrameworkAdapter, LinkProps } from \"../types\"\n\n/**\n * Creates a Next.js adapter for the framework\n * Use this when using @logickernel/frame in a Next.js application\n * \n * Note: This requires Next.js and next-auth to be installed\n */\nexport function createNextJSAdapter(): FrameworkAdapter {\n const NextJSLink: React.ComponentType<LinkProps> = ({ href, children, className }) => (\n <Link href={href} className={className}>\n {children}\n </Link>\n )\n\n return {\n usePathname,\n useRouter: () => {\n const router = useRouter()\n return {\n push: (path: string) => router.push(path),\n refresh: () => router.refresh(),\n }\n },\n Link: NextJSLink,\n signOut: signOut as FrameworkAdapter[\"signOut\"],\n }\n}\n\n"]}