@glamcor/dna-shared-nav 1.0.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/README.md +210 -0
- package/dist/AppSubnav.d.ts +2 -0
- package/dist/AppSubnav.js +6 -0
- package/dist/AvatarMenu.d.ts +2 -0
- package/dist/AvatarMenu.js +33 -0
- package/dist/DNAHeader.d.ts +2 -0
- package/dist/DNAHeader.js +40 -0
- package/dist/DNANavigation.d.ts +28 -0
- package/dist/DNANavigation.js +33 -0
- package/dist/PinnedJobs.d.ts +2 -0
- package/dist/PinnedJobs.js +48 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +9 -0
- package/dist/styles.css +425 -0
- package/dist/types.d.ts +60 -0
- package/dist/types.js +1 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# DNA Shared Navigation
|
|
2
|
+
|
|
3
|
+
Universal navigation component for all DNA microsites. Provides a consistent two-row navigation header across Dashboard, SPOT, Water, Pulse, Hydrogen, and Oxygen apps.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
The navigation consists of two rows (80px total height):
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Row 1 - Header (44px): [DNA Logo] [Inter-app Nav: Dashboard | SPOT | Water | Pulse | Hydrogen | Oxygen] [Avatar]
|
|
11
|
+
Row 2 - App Subnav (36px): [App Name] [App-specific pages] ......................... [Pinned Jobs →]
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
- **Header**: Logo links to Dashboard, inter-app navigation for switching between DNA apps, avatar menu with user info and logout
|
|
15
|
+
- **App Subnav**: Current app name, app-specific page links, pinned jobs carousel (right-aligned)
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
In a DNA monorepo workspace, add to your app's `package.json`:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"dna-shared-nav": "*"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Then run `npm install` from the monorepo root.
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
### Basic Setup
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { DNANavigation } from 'dna-shared-nav';
|
|
37
|
+
import 'dna-shared-nav/dist/styles.css';
|
|
38
|
+
|
|
39
|
+
export function Layout({ children }) {
|
|
40
|
+
const user = {
|
|
41
|
+
id: '1',
|
|
42
|
+
email: 'user@example.com',
|
|
43
|
+
name: 'John Doe',
|
|
44
|
+
role: 'admin',
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<>
|
|
49
|
+
<DNANavigation
|
|
50
|
+
user={user}
|
|
51
|
+
currentApp="Water"
|
|
52
|
+
appName="Water"
|
|
53
|
+
subnavItems={[
|
|
54
|
+
{ name: 'Dashboard', href: '/water', isActive: true },
|
|
55
|
+
{ name: 'Customers', href: '/water/customers' },
|
|
56
|
+
{ name: 'Jobs', href: '/water/jobs' },
|
|
57
|
+
{ name: 'Analytics', href: '/water/analytics' },
|
|
58
|
+
]}
|
|
59
|
+
onLogout={() => signOut()}
|
|
60
|
+
/>
|
|
61
|
+
<main>{children}</main>
|
|
62
|
+
</>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### With Pinned Jobs
|
|
68
|
+
|
|
69
|
+
Pinned jobs appear on the right side of the app subnav. They show progress rings and support a carousel when there are more than 2 jobs.
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
<DNANavigation
|
|
73
|
+
user={user}
|
|
74
|
+
currentApp="Water"
|
|
75
|
+
appName="Water"
|
|
76
|
+
subnavItems={subnavItems}
|
|
77
|
+
pinnedJobs={[
|
|
78
|
+
{
|
|
79
|
+
id: '1',
|
|
80
|
+
appName: 'Water',
|
|
81
|
+
appColor: '#38bdf8',
|
|
82
|
+
jobName: 'Q4 Data Enrichment',
|
|
83
|
+
progress: 59,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: '2',
|
|
87
|
+
appName: 'SPOT',
|
|
88
|
+
appColor: '#a78bfa',
|
|
89
|
+
jobName: 'Lead Scoring Batch',
|
|
90
|
+
progress: 23,
|
|
91
|
+
},
|
|
92
|
+
]}
|
|
93
|
+
onUnpinJob={(jobId) => handleUnpin(jobId)}
|
|
94
|
+
onLogout={() => signOut()}
|
|
95
|
+
/>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Props
|
|
99
|
+
|
|
100
|
+
### DNANavigationProps
|
|
101
|
+
|
|
102
|
+
| Prop | Type | Required | Description |
|
|
103
|
+
|------|------|----------|-------------|
|
|
104
|
+
| `user` | `DNAUser \| null` | Yes | Current user object, or null if not logged in |
|
|
105
|
+
| `currentApp` | `string` | No | Name of active app in header nav (e.g., "Water", "SPOT") |
|
|
106
|
+
| `appName` | `string` | Yes | Display name shown in app subnav |
|
|
107
|
+
| `headerItems` | `DNANavItem[]` | No | Override default inter-app navigation items |
|
|
108
|
+
| `subnavItems` | `AppSubnavItem[]` | Yes | App-specific page links |
|
|
109
|
+
| `pinnedJobs` | `PinnedJob[]` | No | Jobs to show in pinned carousel |
|
|
110
|
+
| `logoSrc` | `string` | No | Custom logo image URL (defaults to "DNA" text) |
|
|
111
|
+
| `onLogout` | `() => void` | No | Callback when user clicks logout |
|
|
112
|
+
| `onUnpinJob` | `(jobId: string) => void` | No | Callback when user unpins a job |
|
|
113
|
+
|
|
114
|
+
### DNAUser
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
interface DNAUser {
|
|
118
|
+
id: string;
|
|
119
|
+
email: string;
|
|
120
|
+
name: string;
|
|
121
|
+
role?: 'superadmin' | 'admin' | 'editor' | 'viewer';
|
|
122
|
+
avatar?: string;
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### AppSubnavItem
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
interface AppSubnavItem {
|
|
130
|
+
name: string; // Display text
|
|
131
|
+
href: string; // Link URL
|
|
132
|
+
isActive?: boolean; // Highlight as current page
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### PinnedJob
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
interface PinnedJob {
|
|
140
|
+
id: string;
|
|
141
|
+
appName: string; // Short name (e.g., "Water", "SPOT")
|
|
142
|
+
appColor?: string; // Hex color for progress ring (e.g., '#38bdf8')
|
|
143
|
+
jobName: string; // Job display name
|
|
144
|
+
progress: number; // 0-100 percentage
|
|
145
|
+
meta?: string; // Optional metadata (e.g., "142 / 240 records")
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Environment Variables
|
|
150
|
+
|
|
151
|
+
The default inter-app navigation uses these environment variables for URLs:
|
|
152
|
+
|
|
153
|
+
```env
|
|
154
|
+
NEXT_PUBLIC_DNA_DASHBOARD_URL=https://dashboard.dna.example.com
|
|
155
|
+
NEXT_PUBLIC_SPOT_URL=https://spot.dna.example.com
|
|
156
|
+
NEXT_PUBLIC_DNA_WATER_URL=https://water.dna.example.com
|
|
157
|
+
NEXT_PUBLIC_DNA_PULSE_URL=https://pulse.dna.example.com
|
|
158
|
+
NEXT_PUBLIC_DNA_HYDROGEN_URL=https://hydrogen.dna.example.com
|
|
159
|
+
NEXT_PUBLIC_DNA_OXYGEN_URL=https://oxygen.dna.example.com
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
If not set, links fall back to relative paths (`/`, `/spot`, `/water`, etc.).
|
|
163
|
+
|
|
164
|
+
## App Colors
|
|
165
|
+
|
|
166
|
+
Recommended colors for each app's pinned jobs:
|
|
167
|
+
|
|
168
|
+
| App | Color | Hex |
|
|
169
|
+
|-----|-------|-----|
|
|
170
|
+
| Water | Sky Blue | `#38bdf8` |
|
|
171
|
+
| SPOT | Purple | `#a78bfa` |
|
|
172
|
+
| Pulse | Rose | `#fb7185` |
|
|
173
|
+
| Hydrogen | Emerald | `#34d399` |
|
|
174
|
+
| Oxygen | Amber | `#fbbf24` |
|
|
175
|
+
|
|
176
|
+
## Individual Components
|
|
177
|
+
|
|
178
|
+
For advanced use cases, you can import components individually:
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
import { DNAHeader, AppSubnav, PinnedJobs, AvatarMenu } from 'dna-shared-nav';
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Styling
|
|
185
|
+
|
|
186
|
+
The navigation uses CSS classes prefixed with `dna-`, `app-subnav-`, and `pinned-`. Styles require these fonts:
|
|
187
|
+
|
|
188
|
+
- **DM Sans** - Primary text
|
|
189
|
+
- **JetBrains Mono** - Logo, progress values
|
|
190
|
+
|
|
191
|
+
Add to your app's `<head>`:
|
|
192
|
+
|
|
193
|
+
```html
|
|
194
|
+
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600&family=JetBrains+Mono:wght@500;600&display=swap" rel="stylesheet">
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Role-Based Navigation
|
|
198
|
+
|
|
199
|
+
Navigation items can be restricted by role. The role hierarchy is:
|
|
200
|
+
|
|
201
|
+
`viewer` < `editor` < `admin` < `superadmin`
|
|
202
|
+
|
|
203
|
+
```tsx
|
|
204
|
+
const headerItems = [
|
|
205
|
+
{ name: 'Dashboard', href: '/' },
|
|
206
|
+
{ name: 'Admin', href: '/admin', requiredRole: 'admin' }, // Only admin+ see this
|
|
207
|
+
];
|
|
208
|
+
|
|
209
|
+
<DNANavigation headerItems={headerItems} ... />
|
|
210
|
+
```
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { PinnedJobs } from './PinnedJobs';
|
|
4
|
+
export function AppSubnav({ appName, items, pinnedJobs = [], onUnpinJob, }) {
|
|
5
|
+
return (_jsxs("nav", { className: "app-subnav", children: [_jsxs("div", { className: "app-subnav-left", children: [_jsx("span", { className: "app-subnav-name", children: appName }), _jsx("div", { className: "app-subnav-links", children: items.map((item) => (_jsx("a", { href: item.href, className: `app-subnav-item ${item.isActive ? 'active' : ''}`, children: item.name }, item.name))) })] }), _jsx(PinnedJobs, { jobs: pinnedJobs, onUnpin: onUnpinJob })] }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useRef, useEffect } from 'react';
|
|
4
|
+
export function AvatarMenu({ userName, userEmail, initials, onLogout }) {
|
|
5
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
6
|
+
const menuRef = useRef(null);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const handleClickOutside = (event) => {
|
|
9
|
+
if (menuRef.current && !menuRef.current.contains(event.target)) {
|
|
10
|
+
setIsOpen(false);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
if (isOpen) {
|
|
14
|
+
document.addEventListener('click', handleClickOutside);
|
|
15
|
+
}
|
|
16
|
+
return () => {
|
|
17
|
+
document.removeEventListener('click', handleClickOutside);
|
|
18
|
+
};
|
|
19
|
+
}, [isOpen]);
|
|
20
|
+
const handleLogout = () => {
|
|
21
|
+
setIsOpen(false);
|
|
22
|
+
if (onLogout) {
|
|
23
|
+
onLogout();
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
window.location.href = '/api/auth/logout';
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
return (_jsxs("div", { className: "dna-avatar-menu", ref: menuRef, children: [_jsx("div", { className: "dna-avatar-button", onClick: (e) => {
|
|
30
|
+
e.stopPropagation();
|
|
31
|
+
setIsOpen(!isOpen);
|
|
32
|
+
}, children: initials }), _jsxs("div", { className: `dna-dropdown-menu ${isOpen ? 'active' : ''}`, children: [_jsxs("div", { className: "dna-dropdown-header", children: [_jsx("div", { className: "dna-dropdown-user-name", children: userName }), _jsx("div", { className: "dna-dropdown-user-email", children: userEmail })] }), _jsx("a", { href: "/profile", className: "dna-dropdown-item", children: "Profile" }), _jsx("a", { href: "/settings", className: "dna-dropdown-item", children: "Settings" }), _jsx("a", { href: "/team", className: "dna-dropdown-item", children: "Team" }), _jsx("div", { className: "dna-dropdown-divider" }), _jsx("a", { href: "/docs", className: "dna-dropdown-item", children: "Documentation" }), _jsx("a", { href: "/support", className: "dna-dropdown-item", children: "Support" }), _jsx("div", { className: "dna-dropdown-divider" }), _jsx("button", { onClick: handleLogout, className: "dna-dropdown-item danger", children: "Sign Out" })] })] }));
|
|
33
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { AvatarMenu } from './AvatarMenu';
|
|
4
|
+
const DEFAULT_NAV_ITEMS = [
|
|
5
|
+
{ name: 'Dashboard', href: process.env.NEXT_PUBLIC_DNA_DASHBOARD_URL || '/' },
|
|
6
|
+
{ name: 'SPOT', href: process.env.NEXT_PUBLIC_SPOT_URL || '/spot' },
|
|
7
|
+
{ name: 'Water', href: process.env.NEXT_PUBLIC_DNA_WATER_URL || '/water' },
|
|
8
|
+
{ name: 'Pulse', href: process.env.NEXT_PUBLIC_DNA_PULSE_URL || '/pulse' },
|
|
9
|
+
{ name: 'Hydrogen', href: process.env.NEXT_PUBLIC_DNA_HYDROGEN_URL || '/hydrogen' },
|
|
10
|
+
{ name: 'Oxygen', href: process.env.NEXT_PUBLIC_DNA_OXYGEN_URL || '/oxygen' },
|
|
11
|
+
];
|
|
12
|
+
const ROLE_HIERARCHY = ['viewer', 'editor', 'admin', 'superadmin'];
|
|
13
|
+
function hasPermission(userRole, requiredRole) {
|
|
14
|
+
if (!userRole)
|
|
15
|
+
return false;
|
|
16
|
+
const userRoleIndex = ROLE_HIERARCHY.indexOf(userRole);
|
|
17
|
+
const requiredRoleIndex = ROLE_HIERARCHY.indexOf(requiredRole);
|
|
18
|
+
return userRoleIndex >= requiredRoleIndex;
|
|
19
|
+
}
|
|
20
|
+
function getUserInitials(name) {
|
|
21
|
+
return name
|
|
22
|
+
.split(' ')
|
|
23
|
+
.map((part) => part[0])
|
|
24
|
+
.join('')
|
|
25
|
+
.toUpperCase()
|
|
26
|
+
.slice(0, 2);
|
|
27
|
+
}
|
|
28
|
+
export function DNAHeader({ user, currentApp = 'Dashboard', items = DEFAULT_NAV_ITEMS, logoSrc = '/DNA_LOGO.png', onLogout, }) {
|
|
29
|
+
const visibleItems = items.filter((item) => {
|
|
30
|
+
if (!item.requiredRole)
|
|
31
|
+
return true;
|
|
32
|
+
if (!user)
|
|
33
|
+
return false;
|
|
34
|
+
return hasPermission(user.role, item.requiredRole);
|
|
35
|
+
});
|
|
36
|
+
return (_jsxs("header", { className: "dna-header", children: [_jsx("a", { href: process.env.NEXT_PUBLIC_DNA_DASHBOARD_URL || '/', className: "dna-logo-container", children: _jsx("img", { src: logoSrc, alt: "DNA", className: "dna-logo-img" }) }), _jsx("nav", { className: "dna-nav-links", children: visibleItems.map((item) => {
|
|
37
|
+
const isActive = item.name === currentApp;
|
|
38
|
+
return (_jsxs("a", { href: item.href, className: `dna-nav-item ${isActive ? 'active' : ''}`, children: [item.hasNotification && _jsx("span", { className: "dna-notification-dot" }), item.name] }, item.name));
|
|
39
|
+
}) }), _jsx("div", { className: "dna-header-right", children: user ? (_jsx(AvatarMenu, { userName: user.name, userEmail: user.email, initials: getUserInitials(user.name), onLogout: onLogout })) : (_jsx("a", { href: "/login", className: "dna-login-btn", children: "Sign In" })) })] }));
|
|
40
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { DNANavigationProps } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* DNANavigation - Complete navigation component for DNA microsites
|
|
4
|
+
*
|
|
5
|
+
* Renders:
|
|
6
|
+
* 1. Header bar: Logo | Inter-app navigation | Avatar
|
|
7
|
+
* 2. App subnav: App name | App-specific pages | Pinned jobs (right)
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* <DNANavigation
|
|
12
|
+
* user={user}
|
|
13
|
+
* currentApp="Water"
|
|
14
|
+
* appName="Water"
|
|
15
|
+
* subnavItems={[
|
|
16
|
+
* { name: 'Dashboard', href: '/admin', isActive: true },
|
|
17
|
+
* { name: 'Customers', href: '/admin/customers' },
|
|
18
|
+
* { name: 'Jobs', href: '/admin/jobs' },
|
|
19
|
+
* ]}
|
|
20
|
+
* pinnedJobs={[
|
|
21
|
+
* { id: '1', appName: 'Water', jobName: 'Q4 Enrichment', progress: 59, appColor: '#38bdf8' },
|
|
22
|
+
* ]}
|
|
23
|
+
* onUnpinJob={(id) => console.log('Unpin:', id)}
|
|
24
|
+
* onLogout={() => signOut()}
|
|
25
|
+
* />
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function DNANavigation({ user, currentApp, appName, headerItems, subnavItems, pinnedJobs, logoSrc, onLogout, onUnpinJob, }: DNANavigationProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { DNAHeader } from './DNAHeader';
|
|
4
|
+
import { AppSubnav } from './AppSubnav';
|
|
5
|
+
/**
|
|
6
|
+
* DNANavigation - Complete navigation component for DNA microsites
|
|
7
|
+
*
|
|
8
|
+
* Renders:
|
|
9
|
+
* 1. Header bar: Logo | Inter-app navigation | Avatar
|
|
10
|
+
* 2. App subnav: App name | App-specific pages | Pinned jobs (right)
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* <DNANavigation
|
|
15
|
+
* user={user}
|
|
16
|
+
* currentApp="Water"
|
|
17
|
+
* appName="Water"
|
|
18
|
+
* subnavItems={[
|
|
19
|
+
* { name: 'Dashboard', href: '/admin', isActive: true },
|
|
20
|
+
* { name: 'Customers', href: '/admin/customers' },
|
|
21
|
+
* { name: 'Jobs', href: '/admin/jobs' },
|
|
22
|
+
* ]}
|
|
23
|
+
* pinnedJobs={[
|
|
24
|
+
* { id: '1', appName: 'Water', jobName: 'Q4 Enrichment', progress: 59, appColor: '#38bdf8' },
|
|
25
|
+
* ]}
|
|
26
|
+
* onUnpinJob={(id) => console.log('Unpin:', id)}
|
|
27
|
+
* onLogout={() => signOut()}
|
|
28
|
+
* />
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export function DNANavigation({ user, currentApp, appName, headerItems, subnavItems, pinnedJobs = [], logoSrc, onLogout, onUnpinJob, }) {
|
|
32
|
+
return (_jsxs("div", { className: "dna-navigation", children: [_jsx(DNAHeader, { user: user, currentApp: currentApp, items: headerItems, logoSrc: logoSrc, onLogout: onLogout }), _jsx(AppSubnav, { appName: appName, items: subnavItems, pinnedJobs: pinnedJobs, onUnpinJob: onUnpinJob })] }));
|
|
33
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useRef, useEffect } from 'react';
|
|
4
|
+
// Progress ring component
|
|
5
|
+
function ProgressRing({ progress, color = '#facc15' }) {
|
|
6
|
+
const radius = 9;
|
|
7
|
+
const circumference = 2 * Math.PI * radius;
|
|
8
|
+
const offset = circumference - (progress / 100) * circumference;
|
|
9
|
+
return (_jsxs("div", { className: "pinned-progress-ring", children: [_jsxs("svg", { viewBox: "0 0 24 24", children: [_jsx("circle", { className: "pinned-ring-bg", cx: "12", cy: "12", r: radius }), _jsx("circle", { className: "pinned-ring-progress", cx: "12", cy: "12", r: radius, style: {
|
|
10
|
+
strokeDasharray: circumference,
|
|
11
|
+
strokeDashoffset: offset,
|
|
12
|
+
stroke: color,
|
|
13
|
+
} })] }), _jsxs("span", { className: "pinned-progress-value", children: [progress, "%"] })] }));
|
|
14
|
+
}
|
|
15
|
+
// Single pinned job pill
|
|
16
|
+
function PinnedJobPill({ job, onUnpin, }) {
|
|
17
|
+
const handleUnpin = (e) => {
|
|
18
|
+
e.stopPropagation();
|
|
19
|
+
onUnpin?.(job.id);
|
|
20
|
+
};
|
|
21
|
+
return (_jsxs("div", { className: "pinned-job", children: [_jsx(ProgressRing, { progress: job.progress, color: job.appColor }), _jsxs("div", { className: "pinned-job-info", children: [_jsx("span", { className: "pinned-job-app", style: { color: job.appColor }, children: job.appName }), _jsx("span", { className: "pinned-job-name", children: job.jobName })] }), _jsx("button", { className: "pinned-unpin-btn", onClick: handleUnpin, title: "Unpin", "aria-label": `Unpin ${job.jobName}`, children: _jsx("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: _jsx("path", { d: "M18 6L6 18M6 6l12 12" }) }) })] }));
|
|
22
|
+
}
|
|
23
|
+
export function PinnedJobs({ jobs, onUnpin }) {
|
|
24
|
+
const [scrollIndex, setScrollIndex] = useState(0);
|
|
25
|
+
const [showArrows, setShowArrows] = useState(false);
|
|
26
|
+
const containerRef = useRef(null);
|
|
27
|
+
// Check if we need carousel arrows
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
setShowArrows(jobs.length > 2);
|
|
30
|
+
// Reset scroll if jobs are removed
|
|
31
|
+
if (scrollIndex > 0 && scrollIndex >= jobs.length - 1) {
|
|
32
|
+
setScrollIndex(Math.max(0, jobs.length - 2));
|
|
33
|
+
}
|
|
34
|
+
}, [jobs.length, scrollIndex]);
|
|
35
|
+
const maxIndex = Math.max(0, jobs.length - 2);
|
|
36
|
+
const scrollPrev = () => {
|
|
37
|
+
setScrollIndex((prev) => Math.max(0, prev - 1));
|
|
38
|
+
};
|
|
39
|
+
const scrollNext = () => {
|
|
40
|
+
setScrollIndex((prev) => Math.min(maxIndex, prev + 1));
|
|
41
|
+
};
|
|
42
|
+
if (jobs.length === 0) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return (_jsxs("div", { className: "pinned-jobs", children: [showArrows && scrollIndex > 0 && (_jsx("button", { className: "pinned-nav-btn", onClick: scrollPrev, "aria-label": "Previous pinned jobs", children: _jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M15 18l-6-6 6-6" }) }) })), _jsx("div", { className: "pinned-jobs-container", ref: containerRef, style: {
|
|
46
|
+
transform: `translateX(-${scrollIndex * 200}px)`,
|
|
47
|
+
}, children: jobs.map((job) => (_jsx(PinnedJobPill, { job: job, onUnpin: onUnpin }, job.id))) }), showArrows && scrollIndex < maxIndex && (_jsx("button", { className: "pinned-nav-btn", onClick: scrollNext, "aria-label": "Next pinned jobs", children: _jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M9 18l6-6-6-6" }) }) }))] }));
|
|
48
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Main combined component
|
|
2
|
+
export { DNANavigation } from './DNANavigation';
|
|
3
|
+
// Individual components (for advanced usage)
|
|
4
|
+
export { DNAHeader } from './DNAHeader';
|
|
5
|
+
export { AppSubnav } from './AppSubnav';
|
|
6
|
+
export { PinnedJobs } from './PinnedJobs';
|
|
7
|
+
export { AvatarMenu } from './AvatarMenu';
|
|
8
|
+
// Types
|
|
9
|
+
export * from './types';
|
package/dist/styles.css
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
/* DNA Shared Navigation Styles */
|
|
2
|
+
/* Two-row navigation: Header + App Subnav */
|
|
3
|
+
|
|
4
|
+
/* ============================================
|
|
5
|
+
NAVIGATION WRAPPER
|
|
6
|
+
============================================ */
|
|
7
|
+
.dna-navigation {
|
|
8
|
+
position: sticky;
|
|
9
|
+
top: 0;
|
|
10
|
+
z-index: 100;
|
|
11
|
+
font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/* ============================================
|
|
15
|
+
HEADER (Logo + Inter-app Nav + Avatar)
|
|
16
|
+
============================================ */
|
|
17
|
+
.dna-header {
|
|
18
|
+
background: rgba(23, 23, 23, 0.8);
|
|
19
|
+
border-bottom: 1px solid #1a1a1a;
|
|
20
|
+
padding: 0 1.5rem;
|
|
21
|
+
display: flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
justify-content: space-between;
|
|
24
|
+
backdrop-filter: blur(12px);
|
|
25
|
+
height: 44px;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.dna-logo-container {
|
|
29
|
+
height: 28px;
|
|
30
|
+
display: flex;
|
|
31
|
+
align-items: center;
|
|
32
|
+
text-decoration: none;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.dna-logo-img {
|
|
36
|
+
height: 24px;
|
|
37
|
+
width: auto;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.dna-logo-text {
|
|
41
|
+
font-family: 'JetBrains Mono', monospace;
|
|
42
|
+
font-size: 16px;
|
|
43
|
+
font-weight: 600;
|
|
44
|
+
letter-spacing: 0.05em;
|
|
45
|
+
color: #facc15;
|
|
46
|
+
text-transform: uppercase;
|
|
47
|
+
text-shadow: 0 0 20px rgba(250, 204, 21, 0.3);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* Inter-app navigation links (centered) */
|
|
51
|
+
.dna-nav-links {
|
|
52
|
+
display: flex;
|
|
53
|
+
align-items: center;
|
|
54
|
+
gap: 0.25rem;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.dna-nav-item {
|
|
58
|
+
padding: 0.25rem 0.75rem;
|
|
59
|
+
border-radius: 5px;
|
|
60
|
+
background: transparent;
|
|
61
|
+
border: 1px solid transparent;
|
|
62
|
+
color: #737373;
|
|
63
|
+
font-size: 11px;
|
|
64
|
+
font-weight: 500;
|
|
65
|
+
cursor: pointer;
|
|
66
|
+
transition: all 0.15s ease;
|
|
67
|
+
text-decoration: none;
|
|
68
|
+
display: flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
gap: 0.375rem;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.dna-nav-item:hover {
|
|
74
|
+
color: #a3a3a3;
|
|
75
|
+
background: #1f1f1f;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.dna-nav-item.active {
|
|
79
|
+
color: #facc15;
|
|
80
|
+
background: rgba(250, 204, 21, 0.08);
|
|
81
|
+
border-color: rgba(250, 204, 21, 0.2);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.dna-header-right {
|
|
85
|
+
display: flex;
|
|
86
|
+
align-items: center;
|
|
87
|
+
gap: 0.75rem;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.dna-login-btn {
|
|
91
|
+
padding: 0.375rem 0.875rem;
|
|
92
|
+
border-radius: 5px;
|
|
93
|
+
background: transparent;
|
|
94
|
+
border: 1px solid #262626;
|
|
95
|
+
color: #a3a3a3;
|
|
96
|
+
font-size: 11px;
|
|
97
|
+
font-weight: 500;
|
|
98
|
+
cursor: pointer;
|
|
99
|
+
transition: all 0.15s ease;
|
|
100
|
+
text-decoration: none;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.dna-login-btn:hover {
|
|
104
|
+
color: #fafafa;
|
|
105
|
+
border-color: #404040;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.dna-notification-dot {
|
|
109
|
+
width: 6px;
|
|
110
|
+
height: 6px;
|
|
111
|
+
background: #facc15;
|
|
112
|
+
border-radius: 50%;
|
|
113
|
+
animation: dna-pulse 2s infinite;
|
|
114
|
+
box-shadow: 0 0 8px rgba(250, 204, 21, 0.6);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@keyframes dna-pulse {
|
|
118
|
+
0%, 100% {
|
|
119
|
+
opacity: 1;
|
|
120
|
+
transform: scale(1);
|
|
121
|
+
}
|
|
122
|
+
50% {
|
|
123
|
+
opacity: 0.6;
|
|
124
|
+
transform: scale(1.1);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* ============================================
|
|
129
|
+
APP SUBNAV (App name + pages + pinned jobs)
|
|
130
|
+
============================================ */
|
|
131
|
+
.app-subnav {
|
|
132
|
+
background: rgba(14, 14, 14, 0.95);
|
|
133
|
+
border-bottom: 1px solid #1a1a1a;
|
|
134
|
+
padding: 0 1.5rem;
|
|
135
|
+
display: flex;
|
|
136
|
+
align-items: center;
|
|
137
|
+
justify-content: space-between;
|
|
138
|
+
height: 36px;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.app-subnav-left {
|
|
142
|
+
display: flex;
|
|
143
|
+
align-items: center;
|
|
144
|
+
gap: 0.5rem;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.app-subnav-name {
|
|
148
|
+
font-size: 14px;
|
|
149
|
+
font-weight: 600;
|
|
150
|
+
color: #fafafa;
|
|
151
|
+
margin-right: 1rem;
|
|
152
|
+
padding-right: 1rem;
|
|
153
|
+
border-right: 1px solid #262626;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.app-subnav-links {
|
|
157
|
+
display: flex;
|
|
158
|
+
align-items: center;
|
|
159
|
+
gap: 0.25rem;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.app-subnav-item {
|
|
163
|
+
padding: 0.375rem 0.75rem;
|
|
164
|
+
color: #525252;
|
|
165
|
+
font-size: 11px;
|
|
166
|
+
font-weight: 500;
|
|
167
|
+
text-decoration: none;
|
|
168
|
+
transition: all 0.15s ease;
|
|
169
|
+
border-radius: 5px;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.app-subnav-item:hover {
|
|
173
|
+
color: #a3a3a3;
|
|
174
|
+
background: rgba(255, 255, 255, 0.03);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.app-subnav-item.active {
|
|
178
|
+
color: #fafafa;
|
|
179
|
+
background: rgba(255, 255, 255, 0.05);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* ============================================
|
|
183
|
+
PINNED JOBS (right side of app subnav)
|
|
184
|
+
============================================ */
|
|
185
|
+
.pinned-jobs {
|
|
186
|
+
display: flex;
|
|
187
|
+
align-items: center;
|
|
188
|
+
gap: 0.5rem;
|
|
189
|
+
overflow: hidden;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.pinned-jobs-container {
|
|
193
|
+
display: flex;
|
|
194
|
+
align-items: center;
|
|
195
|
+
gap: 0.5rem;
|
|
196
|
+
transition: transform 0.3s ease;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.pinned-job {
|
|
200
|
+
display: flex;
|
|
201
|
+
align-items: center;
|
|
202
|
+
gap: 0.5rem;
|
|
203
|
+
padding: 0.25rem 0.5rem 0.25rem 0.25rem;
|
|
204
|
+
background: rgba(255, 255, 255, 0.03);
|
|
205
|
+
border: 1px solid #1f1f1f;
|
|
206
|
+
border-radius: 6px;
|
|
207
|
+
transition: all 0.15s ease;
|
|
208
|
+
cursor: pointer;
|
|
209
|
+
white-space: nowrap;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.pinned-job:hover {
|
|
213
|
+
border-color: #2a2a2a;
|
|
214
|
+
background: rgba(255, 255, 255, 0.05);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.pinned-progress-ring {
|
|
218
|
+
width: 24px;
|
|
219
|
+
height: 24px;
|
|
220
|
+
position: relative;
|
|
221
|
+
flex-shrink: 0;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.pinned-progress-ring svg {
|
|
225
|
+
transform: rotate(-90deg);
|
|
226
|
+
width: 24px;
|
|
227
|
+
height: 24px;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.pinned-ring-bg {
|
|
231
|
+
fill: none;
|
|
232
|
+
stroke: #262626;
|
|
233
|
+
stroke-width: 2.5;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.pinned-ring-progress {
|
|
237
|
+
fill: none;
|
|
238
|
+
stroke-width: 2.5;
|
|
239
|
+
stroke-linecap: round;
|
|
240
|
+
filter: drop-shadow(0 0 3px rgba(250, 204, 21, 0.4));
|
|
241
|
+
transition: stroke-dashoffset 0.3s ease;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.pinned-progress-value {
|
|
245
|
+
position: absolute;
|
|
246
|
+
top: 50%;
|
|
247
|
+
left: 50%;
|
|
248
|
+
transform: translate(-50%, -50%);
|
|
249
|
+
font-size: 7px;
|
|
250
|
+
font-weight: 600;
|
|
251
|
+
font-family: 'JetBrains Mono', monospace;
|
|
252
|
+
color: #fafafa;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.pinned-job-info {
|
|
256
|
+
display: flex;
|
|
257
|
+
align-items: center;
|
|
258
|
+
gap: 0.375rem;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.pinned-job-app {
|
|
262
|
+
font-size: 9px;
|
|
263
|
+
font-weight: 600;
|
|
264
|
+
text-transform: uppercase;
|
|
265
|
+
letter-spacing: 0.03em;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.pinned-job-name {
|
|
269
|
+
font-size: 11px;
|
|
270
|
+
font-weight: 500;
|
|
271
|
+
color: #a3a3a3;
|
|
272
|
+
max-width: 120px;
|
|
273
|
+
overflow: hidden;
|
|
274
|
+
text-overflow: ellipsis;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.pinned-unpin-btn {
|
|
278
|
+
background: none;
|
|
279
|
+
border: none;
|
|
280
|
+
color: #404040;
|
|
281
|
+
cursor: pointer;
|
|
282
|
+
padding: 2px;
|
|
283
|
+
border-radius: 3px;
|
|
284
|
+
display: flex;
|
|
285
|
+
align-items: center;
|
|
286
|
+
justify-content: center;
|
|
287
|
+
transition: all 0.15s ease;
|
|
288
|
+
margin-left: 0.25rem;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.pinned-unpin-btn:hover {
|
|
292
|
+
color: #f87171;
|
|
293
|
+
background: rgba(248, 113, 113, 0.1);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* Carousel arrows */
|
|
297
|
+
.pinned-nav-btn {
|
|
298
|
+
background: rgba(255, 255, 255, 0.05);
|
|
299
|
+
border: 1px solid #262626;
|
|
300
|
+
color: #525252;
|
|
301
|
+
width: 20px;
|
|
302
|
+
height: 20px;
|
|
303
|
+
border-radius: 4px;
|
|
304
|
+
display: flex;
|
|
305
|
+
align-items: center;
|
|
306
|
+
justify-content: center;
|
|
307
|
+
cursor: pointer;
|
|
308
|
+
transition: all 0.15s ease;
|
|
309
|
+
flex-shrink: 0;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.pinned-nav-btn:hover {
|
|
313
|
+
color: #fafafa;
|
|
314
|
+
border-color: #404040;
|
|
315
|
+
background: rgba(255, 255, 255, 0.08);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/* ============================================
|
|
319
|
+
AVATAR MENU
|
|
320
|
+
============================================ */
|
|
321
|
+
.dna-avatar-menu {
|
|
322
|
+
position: relative;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.dna-avatar-button {
|
|
326
|
+
width: 28px;
|
|
327
|
+
height: 28px;
|
|
328
|
+
border-radius: 50%;
|
|
329
|
+
background: linear-gradient(135deg, #fde047 0%, #facc15 100%);
|
|
330
|
+
border: 2px solid #0a0a0a;
|
|
331
|
+
box-shadow:
|
|
332
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.2),
|
|
333
|
+
0 0 0 1px #262626;
|
|
334
|
+
cursor: pointer;
|
|
335
|
+
transition: all 0.15s ease;
|
|
336
|
+
display: flex;
|
|
337
|
+
align-items: center;
|
|
338
|
+
justify-content: center;
|
|
339
|
+
font-weight: 600;
|
|
340
|
+
font-size: 10px;
|
|
341
|
+
color: #0a0a0a;
|
|
342
|
+
text-transform: uppercase;
|
|
343
|
+
font-family: 'JetBrains Mono', monospace;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.dna-avatar-button:hover {
|
|
347
|
+
transform: translateY(-1px);
|
|
348
|
+
box-shadow:
|
|
349
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.3),
|
|
350
|
+
0 0 0 1px rgba(250, 204, 21, 0.3),
|
|
351
|
+
0 0 12px rgba(250, 204, 21, 0.2);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/* Dropdown Menu */
|
|
355
|
+
.dna-dropdown-menu {
|
|
356
|
+
position: absolute;
|
|
357
|
+
top: calc(100% + 0.5rem);
|
|
358
|
+
right: 0;
|
|
359
|
+
min-width: 180px;
|
|
360
|
+
background: linear-gradient(135deg, #171717 0%, #141414 100%);
|
|
361
|
+
border: 1px solid #262626;
|
|
362
|
+
border-radius: 8px;
|
|
363
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
|
|
364
|
+
opacity: 0;
|
|
365
|
+
visibility: hidden;
|
|
366
|
+
transform: translateY(-8px);
|
|
367
|
+
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
368
|
+
z-index: 1000;
|
|
369
|
+
overflow: hidden;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.dna-dropdown-menu.active {
|
|
373
|
+
opacity: 1;
|
|
374
|
+
visibility: visible;
|
|
375
|
+
transform: translateY(0);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.dna-dropdown-header {
|
|
379
|
+
padding: 0.625rem 0.875rem;
|
|
380
|
+
border-bottom: 1px solid #1a1a1a;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.dna-dropdown-user-name {
|
|
384
|
+
font-size: 12px;
|
|
385
|
+
font-weight: 600;
|
|
386
|
+
color: #fafafa;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.dna-dropdown-user-email {
|
|
390
|
+
font-size: 10px;
|
|
391
|
+
color: #737373;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.dna-dropdown-divider {
|
|
395
|
+
height: 1px;
|
|
396
|
+
background: #1a1a1a;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.dna-dropdown-item {
|
|
400
|
+
padding: 0.5rem 0.875rem;
|
|
401
|
+
color: #a3a3a3;
|
|
402
|
+
font-size: 11px;
|
|
403
|
+
font-weight: 500;
|
|
404
|
+
cursor: pointer;
|
|
405
|
+
transition: all 0.15s ease;
|
|
406
|
+
display: block;
|
|
407
|
+
text-decoration: none;
|
|
408
|
+
background: none;
|
|
409
|
+
border: none;
|
|
410
|
+
width: 100%;
|
|
411
|
+
text-align: left;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.dna-dropdown-item:hover {
|
|
415
|
+
background: #1f1f1f;
|
|
416
|
+
color: #fafafa;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.dna-dropdown-item.danger {
|
|
420
|
+
color: #f87171;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.dna-dropdown-item.danger:hover {
|
|
424
|
+
background: rgba(248, 113, 113, 0.1);
|
|
425
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export interface DNAUser {
|
|
2
|
+
id: string;
|
|
3
|
+
email: string;
|
|
4
|
+
name: string;
|
|
5
|
+
role?: 'superadmin' | 'admin' | 'editor' | 'viewer';
|
|
6
|
+
avatar?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface DNANavItem {
|
|
9
|
+
name: string;
|
|
10
|
+
href: string;
|
|
11
|
+
requiredRole?: 'viewer' | 'editor' | 'admin' | 'superadmin';
|
|
12
|
+
hasNotification?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface AppSubnavItem {
|
|
15
|
+
name: string;
|
|
16
|
+
href: string;
|
|
17
|
+
isActive?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface PinnedJob {
|
|
20
|
+
id: string;
|
|
21
|
+
appName: string;
|
|
22
|
+
appColor?: string;
|
|
23
|
+
jobName: string;
|
|
24
|
+
progress: number;
|
|
25
|
+
meta?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface DNAHeaderProps {
|
|
28
|
+
user: DNAUser | null;
|
|
29
|
+
currentApp?: string;
|
|
30
|
+
items?: DNANavItem[];
|
|
31
|
+
logoSrc?: string;
|
|
32
|
+
onLogout?: () => void;
|
|
33
|
+
}
|
|
34
|
+
export interface AppSubnavProps {
|
|
35
|
+
appName: string;
|
|
36
|
+
items: AppSubnavItem[];
|
|
37
|
+
pinnedJobs?: PinnedJob[];
|
|
38
|
+
onUnpinJob?: (jobId: string) => void;
|
|
39
|
+
}
|
|
40
|
+
export interface DNANavigationProps {
|
|
41
|
+
user: DNAUser | null;
|
|
42
|
+
currentApp?: string;
|
|
43
|
+
appName: string;
|
|
44
|
+
headerItems?: DNANavItem[];
|
|
45
|
+
subnavItems: AppSubnavItem[];
|
|
46
|
+
pinnedJobs?: PinnedJob[];
|
|
47
|
+
logoSrc?: string;
|
|
48
|
+
onLogout?: () => void;
|
|
49
|
+
onUnpinJob?: (jobId: string) => void;
|
|
50
|
+
}
|
|
51
|
+
export interface AvatarMenuProps {
|
|
52
|
+
userName: string;
|
|
53
|
+
userEmail: string;
|
|
54
|
+
initials: string;
|
|
55
|
+
onLogout?: () => void;
|
|
56
|
+
}
|
|
57
|
+
export interface PinnedJobsProps {
|
|
58
|
+
jobs: PinnedJob[];
|
|
59
|
+
onUnpin?: (jobId: string) => void;
|
|
60
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@glamcor/dna-shared-nav",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Shared navigation component for DNA microsites",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"require": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./dist/styles.css": "./dist/styles.css"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc && cp src/styles.css dist/styles.css",
|
|
20
|
+
"prepublishOnly": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/glamcor/dna-shared-nav.git"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"dna",
|
|
28
|
+
"navigation",
|
|
29
|
+
"react",
|
|
30
|
+
"component"
|
|
31
|
+
],
|
|
32
|
+
"author": "GlamCor",
|
|
33
|
+
"license": "UNLICENSED",
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"react": "^18.0.0",
|
|
36
|
+
"react-dom": "^18.0.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^20.0.0",
|
|
40
|
+
"@types/react": "^18.2.0",
|
|
41
|
+
"@types/react-dom": "^18.2.0",
|
|
42
|
+
"typescript": "^5.0.0"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "restricted"
|
|
46
|
+
}
|
|
47
|
+
}
|