@campfire-interactive/shell-header 0.1.1 → 0.1.3
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/index.d.ts +2 -0
- package/dist/index.js +69 -55
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -24,20 +24,17 @@ function styleInject(css, { insertAt } = {}) {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// src/styles.css
|
|
27
|
-
styleInject(".cfi-sh-header {\n display: flex;\n align-items: center;\n height: var(--cfi-shell-header-height, 48px);\n padding: 0 var(--cfi-spacing-md, 1rem);\n background: var(--cfi-color-gray-900, #111827);\n
|
|
28
|
-
|
|
29
|
-
// src/AppSwitcher.tsx
|
|
30
|
-
import { useState, useRef, useEffect } from "react";
|
|
27
|
+
styleInject(".cfi-sh-header {\n display: flex;\n align-items: center;\n height: var(--cfi-shell-header-height, 48px);\n padding: 0 var(--cfi-spacing-md, 1rem);\n background: white;\n color: var(--cfi-color-gray-900, #111827);\n font-family: var(--cfi-font-family, system-ui, sans-serif);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n position: relative;\n z-index: 1000;\n}\n.cfi-sh-left {\n display: flex;\n align-items: center;\n flex: 1;\n gap: var(--cfi-spacing-md, 1rem);\n overflow: hidden;\n}\n.cfi-sh-app-brand {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n flex-shrink: 0;\n}\n.cfi-sh-app-badge {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n background: var(--cfi-color-primary-500, #f97316);\n color: white;\n font-size: 14px;\n font-weight: 700;\n font-family: inherit;\n border-radius: var(--cfi-radius-md, 0.375rem);\n flex-shrink: 0;\n}\n.cfi-sh-app-title {\n font-weight: 600;\n font-size: var(--cfi-font-size-base, 1rem);\n color: var(--cfi-color-gray-900, #111827);\n white-space: nowrap;\n}\n.cfi-sh-right {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-xs, 0.25rem);\n flex-shrink: 0;\n}\n.cfi-sh-icon-btn {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n background: transparent;\n border: none;\n border-radius: 50%;\n color: var(--cfi-color-gray-600, #4b5563);\n cursor: pointer;\n transition: background 150ms;\n}\n.cfi-sh-icon-btn:hover {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-badge {\n position: absolute;\n top: 2px;\n right: 2px;\n min-width: 16px;\n height: 16px;\n padding: 0 4px;\n background: var(--cfi-color-error, #ef4444);\n color: white;\n font-size: 10px;\n font-weight: 600;\n line-height: 16px;\n text-align: center;\n border-radius: 99px;\n}\n.cfi-sh-app-switcher {\n position: relative;\n}\n.cfi-sh-app-grid {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: var(--cfi-spacing-xs, 0.25rem);\n padding: var(--cfi-spacing-sm, 0.5rem);\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: var(--cfi-shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n z-index: 1001;\n min-width: 280px;\n}\n.cfi-sh-app-grid-item {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--cfi-spacing-xs, 0.25rem);\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-xs, 0.25rem);\n border-radius: var(--cfi-radius-md, 0.375rem);\n color: var(--cfi-color-gray-700, #374151);\n text-decoration: none;\n cursor: pointer;\n transition: background 100ms;\n font-family: inherit;\n}\n.cfi-sh-app-grid-item:hover {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-app-grid-item-active {\n background: var(--cfi-color-primary-50, #fff7ed);\n color: var(--cfi-color-primary-700, #c2410c);\n}\n.cfi-sh-app-grid-item-active:hover {\n background: var(--cfi-color-primary-100, #ffedd5);\n}\n.cfi-sh-app-grid-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border-radius: 50%;\n background: var(--cfi-color-gray-100, #f3f4f6);\n color: var(--cfi-color-gray-600, #4b5563);\n}\n.cfi-sh-app-grid-item-active .cfi-sh-app-grid-icon {\n background: var(--cfi-color-primary-100, #ffedd5);\n color: var(--cfi-color-primary-700, #c2410c);\n}\n.cfi-sh-app-grid-label {\n font-size: var(--cfi-font-size-xs, 0.75rem);\n text-align: center;\n line-height: 1.2;\n max-width: 80px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cfi-sh-user-menu {\n position: relative;\n}\n.cfi-sh-avatar-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n padding: 0;\n background: var(--cfi-color-primary-600, #ea580c);\n border: none;\n border-radius: 50%;\n cursor: pointer;\n transition: opacity 150ms;\n}\n.cfi-sh-avatar-btn:hover {\n opacity: 0.85;\n}\n.cfi-sh-avatar-img {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n object-fit: cover;\n}\n.cfi-sh-avatar-initials {\n color: white;\n font-size: var(--cfi-font-size-xs, 0.75rem);\n font-weight: 600;\n font-family: inherit;\n}\n.cfi-sh-user-dropdown {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n min-width: 200px;\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: var(--cfi-shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n z-index: 1001;\n}\n.cfi-sh-user-header {\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-md, 1rem);\n display: flex;\n flex-direction: column;\n}\n.cfi-sh-user-name {\n font-weight: 500;\n color: var(--cfi-color-gray-900, #111827);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n}\n.cfi-sh-user-email {\n color: var(--cfi-color-gray-500, #6b7280);\n font-size: var(--cfi-font-size-xs, 0.75rem);\n}\n.cfi-sh-divider {\n height: 1px;\n background: var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-sh-menu-item {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n width: 100%;\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-md, 1rem);\n background: none;\n border: none;\n color: var(--cfi-color-gray-700, #374151);\n cursor: pointer;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n font-family: inherit;\n transition: background 100ms;\n}\n.cfi-sh-menu-item:hover {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n");
|
|
31
28
|
|
|
32
29
|
// src/appCatalog.ts
|
|
33
30
|
var appCatalog = [
|
|
34
|
-
{ id: "si", name: "Supplier Intelligence", icon: "Building2", localPort: 3e3 },
|
|
35
|
-
{ id: "masterdata", name: "Master Data", icon: "Database", localPort: 3100 },
|
|
36
|
-
{ id: "forecast", name: "Forecasting", icon: "TrendingUp", localPort: 3200 },
|
|
37
|
-
{ id: "pim", name: "Price Index", icon: "BarChart3", localPort: 3300 },
|
|
38
|
-
{ id: "cpq", name: "CPQ", icon: "Calculator", localPort: 3400 },
|
|
39
|
-
{ id: "omsf", name: "OMSF", icon: "FileText", localPort: 3500 },
|
|
40
|
-
{ id: "identity", name: "Identity", icon: "Shield", localPort: 3600 }
|
|
31
|
+
{ id: "si", name: "Supplier Intelligence", icon: "Building2", letter: "S", localPort: 3e3 },
|
|
32
|
+
{ id: "masterdata", name: "Master Data", icon: "Database", letter: "M", localPort: 3100 },
|
|
33
|
+
{ id: "forecast", name: "Forecasting", icon: "TrendingUp", letter: "F", localPort: 3200 },
|
|
34
|
+
{ id: "pim", name: "Price Index", icon: "BarChart3", letter: "P", localPort: 3300 },
|
|
35
|
+
{ id: "cpq", name: "CPQ", icon: "Calculator", letter: "C", localPort: 3400 },
|
|
36
|
+
{ id: "omsf", name: "OMSF", icon: "FileText", letter: "O", localPort: 3500 },
|
|
37
|
+
{ id: "identity", name: "Identity", icon: "Shield", letter: "I", localPort: 3600 }
|
|
41
38
|
];
|
|
42
39
|
function getAppUrl(app) {
|
|
43
40
|
const host = typeof window !== "undefined" ? window.location.hostname : "";
|
|
@@ -51,6 +48,9 @@ function getAppUrl(app) {
|
|
|
51
48
|
return `${protocol}//${app.id}.campfiresuite.com`;
|
|
52
49
|
}
|
|
53
50
|
|
|
51
|
+
// src/AppSwitcher.tsx
|
|
52
|
+
import { useState, useRef, useEffect } from "react";
|
|
53
|
+
|
|
54
54
|
// src/icons.tsx
|
|
55
55
|
import {
|
|
56
56
|
Building2,
|
|
@@ -60,12 +60,11 @@ import {
|
|
|
60
60
|
Calculator,
|
|
61
61
|
FileText,
|
|
62
62
|
Shield,
|
|
63
|
-
|
|
63
|
+
GripHorizontal,
|
|
64
64
|
Bell,
|
|
65
|
-
|
|
66
|
-
LogOut,
|
|
67
|
-
User
|
|
65
|
+
LogOut
|
|
68
66
|
} from "lucide-react";
|
|
67
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
69
68
|
var iconMap = {
|
|
70
69
|
Building2,
|
|
71
70
|
Database,
|
|
@@ -76,15 +75,27 @@ var iconMap = {
|
|
|
76
75
|
Shield
|
|
77
76
|
};
|
|
78
77
|
function getIcon(name) {
|
|
79
|
-
return iconMap[name] ||
|
|
78
|
+
return iconMap[name] || GripHorizontal;
|
|
79
|
+
}
|
|
80
|
+
function GridIcon({ size = 20 }) {
|
|
81
|
+
return /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 20 20", fill: "currentColor", children: [
|
|
82
|
+
/* @__PURE__ */ jsx("circle", { cx: "4", cy: "4", r: "1.5" }),
|
|
83
|
+
/* @__PURE__ */ jsx("circle", { cx: "10", cy: "4", r: "1.5" }),
|
|
84
|
+
/* @__PURE__ */ jsx("circle", { cx: "16", cy: "4", r: "1.5" }),
|
|
85
|
+
/* @__PURE__ */ jsx("circle", { cx: "4", cy: "10", r: "1.5" }),
|
|
86
|
+
/* @__PURE__ */ jsx("circle", { cx: "10", cy: "10", r: "1.5" }),
|
|
87
|
+
/* @__PURE__ */ jsx("circle", { cx: "16", cy: "10", r: "1.5" }),
|
|
88
|
+
/* @__PURE__ */ jsx("circle", { cx: "4", cy: "16", r: "1.5" }),
|
|
89
|
+
/* @__PURE__ */ jsx("circle", { cx: "10", cy: "16", r: "1.5" }),
|
|
90
|
+
/* @__PURE__ */ jsx("circle", { cx: "16", cy: "16", r: "1.5" })
|
|
91
|
+
] });
|
|
80
92
|
}
|
|
81
93
|
|
|
82
94
|
// src/AppSwitcher.tsx
|
|
83
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
95
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
84
96
|
function AppSwitcher({ currentAppId, authorizedApps }) {
|
|
85
97
|
const [open, setOpen] = useState(false);
|
|
86
98
|
const ref = useRef(null);
|
|
87
|
-
const currentApp = appCatalog.find((a) => a.id === currentAppId);
|
|
88
99
|
const visibleApps = appCatalog.filter((a) => authorizedApps.includes(a.id));
|
|
89
100
|
useEffect(() => {
|
|
90
101
|
function handleClickOutside(e) {
|
|
@@ -95,37 +106,33 @@ function AppSwitcher({ currentAppId, authorizedApps }) {
|
|
|
95
106
|
if (open) document.addEventListener("mousedown", handleClickOutside);
|
|
96
107
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
97
108
|
}, [open]);
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
/* @__PURE__ */ jsxs(
|
|
109
|
+
return /* @__PURE__ */ jsxs2("div", { className: "cfi-sh-app-switcher", ref, children: [
|
|
110
|
+
/* @__PURE__ */ jsx2(
|
|
101
111
|
"button",
|
|
102
112
|
{
|
|
103
|
-
className: "cfi-sh-
|
|
113
|
+
className: "cfi-sh-icon-btn",
|
|
104
114
|
onClick: () => setOpen(!open),
|
|
105
115
|
"aria-expanded": open,
|
|
106
116
|
"aria-haspopup": "true",
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
/* @__PURE__ */ jsx("span", { className: "cfi-sh-app-name", children: currentApp?.name || "Apps" }),
|
|
110
|
-
/* @__PURE__ */ jsx(ChevronDown, { size: 14, className: open ? "cfi-sh-chevron-open" : void 0 })
|
|
111
|
-
]
|
|
117
|
+
"aria-label": "App switcher",
|
|
118
|
+
children: /* @__PURE__ */ jsx2(GridIcon, { size: 20 })
|
|
112
119
|
}
|
|
113
120
|
),
|
|
114
|
-
open && /* @__PURE__ */
|
|
121
|
+
open && /* @__PURE__ */ jsx2("div", { className: "cfi-sh-app-grid", children: visibleApps.map((app) => {
|
|
115
122
|
const Icon = getIcon(app.icon);
|
|
116
123
|
const isCurrent = app.id === currentAppId;
|
|
117
|
-
return /* @__PURE__ */
|
|
124
|
+
return /* @__PURE__ */ jsxs2(
|
|
118
125
|
"a",
|
|
119
126
|
{
|
|
120
127
|
href: isCurrent ? void 0 : getAppUrl(app),
|
|
121
|
-
className: `cfi-sh-app-item ${isCurrent ? "cfi-sh-app-item-active" : ""}`,
|
|
128
|
+
className: `cfi-sh-app-grid-item ${isCurrent ? "cfi-sh-app-grid-item-active" : ""}`,
|
|
122
129
|
onClick: isCurrent ? (e) => {
|
|
123
130
|
e.preventDefault();
|
|
124
131
|
setOpen(false);
|
|
125
132
|
} : void 0,
|
|
126
133
|
children: [
|
|
127
|
-
/* @__PURE__ */
|
|
128
|
-
/* @__PURE__ */
|
|
134
|
+
/* @__PURE__ */ jsx2("div", { className: "cfi-sh-app-grid-icon", children: /* @__PURE__ */ jsx2(Icon, { size: 22 }) }),
|
|
135
|
+
/* @__PURE__ */ jsx2("span", { className: "cfi-sh-app-grid-label", children: app.name })
|
|
129
136
|
]
|
|
130
137
|
},
|
|
131
138
|
app.id
|
|
@@ -135,17 +142,17 @@ function AppSwitcher({ currentAppId, authorizedApps }) {
|
|
|
135
142
|
}
|
|
136
143
|
|
|
137
144
|
// src/NotificationBell.tsx
|
|
138
|
-
import { jsx as
|
|
145
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
139
146
|
function NotificationBell({ count = 0, onClick }) {
|
|
140
|
-
return /* @__PURE__ */
|
|
141
|
-
/* @__PURE__ */
|
|
142
|
-
count > 0 && /* @__PURE__ */
|
|
147
|
+
return /* @__PURE__ */ jsxs3("button", { className: "cfi-sh-icon-btn", onClick, "aria-label": "Notifications", children: [
|
|
148
|
+
/* @__PURE__ */ jsx3(Bell, { size: 18 }),
|
|
149
|
+
count > 0 && /* @__PURE__ */ jsx3("span", { className: "cfi-sh-badge", children: count > 99 ? "99+" : count })
|
|
143
150
|
] });
|
|
144
151
|
}
|
|
145
152
|
|
|
146
153
|
// src/UserMenu.tsx
|
|
147
154
|
import { useState as useState2, useRef as useRef2, useEffect as useEffect2 } from "react";
|
|
148
|
-
import { jsx as
|
|
155
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
149
156
|
function UserMenu({ user, onLogout }) {
|
|
150
157
|
const [open, setOpen] = useState2(false);
|
|
151
158
|
const ref = useRef2(null);
|
|
@@ -159,24 +166,24 @@ function UserMenu({ user, onLogout }) {
|
|
|
159
166
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
160
167
|
}, [open]);
|
|
161
168
|
const initials = user.name.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2);
|
|
162
|
-
return /* @__PURE__ */
|
|
163
|
-
/* @__PURE__ */
|
|
169
|
+
return /* @__PURE__ */ jsxs4("div", { className: "cfi-sh-user-menu", ref, children: [
|
|
170
|
+
/* @__PURE__ */ jsx4(
|
|
164
171
|
"button",
|
|
165
172
|
{
|
|
166
173
|
className: "cfi-sh-avatar-btn",
|
|
167
174
|
onClick: () => setOpen(!open),
|
|
168
175
|
"aria-expanded": open,
|
|
169
176
|
"aria-haspopup": "true",
|
|
170
|
-
children: user.avatarUrl ? /* @__PURE__ */
|
|
177
|
+
children: user.avatarUrl ? /* @__PURE__ */ jsx4("img", { src: user.avatarUrl, alt: user.name, className: "cfi-sh-avatar-img" }) : /* @__PURE__ */ jsx4("span", { className: "cfi-sh-avatar-initials", children: initials })
|
|
171
178
|
}
|
|
172
179
|
),
|
|
173
|
-
open && /* @__PURE__ */
|
|
174
|
-
/* @__PURE__ */
|
|
175
|
-
/* @__PURE__ */
|
|
176
|
-
/* @__PURE__ */
|
|
180
|
+
open && /* @__PURE__ */ jsxs4("div", { className: "cfi-sh-user-dropdown", children: [
|
|
181
|
+
/* @__PURE__ */ jsxs4("div", { className: "cfi-sh-user-header", children: [
|
|
182
|
+
/* @__PURE__ */ jsx4("span", { className: "cfi-sh-user-name", children: user.name }),
|
|
183
|
+
/* @__PURE__ */ jsx4("span", { className: "cfi-sh-user-email", children: user.email })
|
|
177
184
|
] }),
|
|
178
|
-
/* @__PURE__ */
|
|
179
|
-
/* @__PURE__ */
|
|
185
|
+
/* @__PURE__ */ jsx4("div", { className: "cfi-sh-divider" }),
|
|
186
|
+
/* @__PURE__ */ jsxs4(
|
|
180
187
|
"button",
|
|
181
188
|
{
|
|
182
189
|
className: "cfi-sh-menu-item",
|
|
@@ -185,8 +192,8 @@ function UserMenu({ user, onLogout }) {
|
|
|
185
192
|
onLogout?.();
|
|
186
193
|
},
|
|
187
194
|
children: [
|
|
188
|
-
/* @__PURE__ */
|
|
189
|
-
/* @__PURE__ */
|
|
195
|
+
/* @__PURE__ */ jsx4(LogOut, { size: 14 }),
|
|
196
|
+
/* @__PURE__ */ jsx4("span", { children: "Sign out" })
|
|
190
197
|
]
|
|
191
198
|
}
|
|
192
199
|
)
|
|
@@ -195,7 +202,7 @@ function UserMenu({ user, onLogout }) {
|
|
|
195
202
|
}
|
|
196
203
|
|
|
197
204
|
// src/ShellHeader.tsx
|
|
198
|
-
import { jsx as
|
|
205
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
199
206
|
function ShellHeader({
|
|
200
207
|
appId,
|
|
201
208
|
user,
|
|
@@ -205,12 +212,19 @@ function ShellHeader({
|
|
|
205
212
|
onLogout,
|
|
206
213
|
children
|
|
207
214
|
}) {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
/* @__PURE__ */
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
215
|
+
const currentApp = appCatalog.find((a) => a.id === appId);
|
|
216
|
+
return /* @__PURE__ */ jsxs5("header", { className: "cfi-sh-header", children: [
|
|
217
|
+
/* @__PURE__ */ jsxs5("div", { className: "cfi-sh-left", children: [
|
|
218
|
+
/* @__PURE__ */ jsxs5("div", { className: "cfi-sh-app-brand", children: [
|
|
219
|
+
currentApp && /* @__PURE__ */ jsx5("span", { className: "cfi-sh-app-badge", children: currentApp.letter }),
|
|
220
|
+
/* @__PURE__ */ jsx5("span", { className: "cfi-sh-app-title", children: currentApp?.name || appId })
|
|
221
|
+
] }),
|
|
222
|
+
children
|
|
223
|
+
] }),
|
|
224
|
+
/* @__PURE__ */ jsxs5("div", { className: "cfi-sh-right", children: [
|
|
225
|
+
/* @__PURE__ */ jsx5(NotificationBell, { count: notificationCount, onClick: onNotificationClick }),
|
|
226
|
+
/* @__PURE__ */ jsx5(AppSwitcher, { currentAppId: appId, authorizedApps }),
|
|
227
|
+
/* @__PURE__ */ jsx5(UserMenu, { user, onLogout })
|
|
214
228
|
] })
|
|
215
229
|
] });
|
|
216
230
|
}
|