@xenide-io/the-old-ui-theme 0.3.2 → 0.4.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/chunk-54ZR4VL5.mjs +4641 -0
- package/dist/chunk-G53YLHVE.mjs +1023 -0
- package/dist/{index-BgG8Homu.d.mts → index-B5NOyoKS.d.mts} +100 -100
- package/dist/{index-BgG8Homu.d.ts → index-B5NOyoKS.d.ts} +100 -100
- package/dist/index.d.mts +9 -145
- package/dist/index.d.ts +9 -145
- package/dist/index.js +653 -1111
- package/dist/index.mjs +86 -342
- package/dist/suite.d.mts +760 -0
- package/dist/suite.d.ts +760 -0
- package/dist/suite.js +2800 -0
- package/dist/suite.mjs +2441 -0
- package/dist/ui.d.mts +1 -2
- package/dist/ui.d.ts +1 -2
- package/dist/ui.js +14 -10
- package/dist/ui.mjs +13 -11
- package/package.json +43 -24
- package/src/components/icons/icons.tsx +0 -2
- package/src/components/icons/index.ts +0 -3
- package/src/components/sections/IconShowcase.tsx +1 -110
- package/src/components/sections/NewComponentsShowcase.tsx +4 -4
- package/src/components/sections/SidebarShowcase.tsx +3 -3
- package/src/components/sections/SuiteShowcase.tsx +669 -0
- package/src/components/ui/ComponentDocs.tsx +2 -2
- package/src/components/ui/ContextMenu.tsx +1 -1
- package/src/components/ui/Menubar.tsx +1 -1
- package/src/components/ui/Sidebar.tsx +10 -8
- package/src/styles/suite-skin.css +255 -0
- package/src/suite/components/ai-panel.tsx +202 -0
- package/src/suite/components/app-switcher.tsx +196 -0
- package/src/suite/components/command-palette-host.tsx +62 -0
- package/src/suite/components/deferred-chrome.tsx +12 -0
- package/src/suite/components/suite-app-layout.tsx +69 -0
- package/src/suite/components/suite-bottom-nav.tsx +113 -0
- package/src/suite/components/suite-layout.tsx +285 -0
- package/src/suite/components/suite-mobile-drawer.tsx +150 -0
- package/src/suite/components/suite-mobile-header.tsx +71 -0
- package/src/suite/components/suite-notification-bell.tsx +227 -0
- package/src/suite/components/suite-settings-mobile-nav.tsx +83 -0
- package/src/suite/components/suite-sidebar.tsx +198 -0
- package/src/suite/components/suite-skeleton.tsx +191 -0
- package/src/suite/components/suite-user-menu.tsx +109 -0
- package/src/suite/components/theme-provider.tsx +174 -0
- package/src/suite/components/today-calibrating.tsx +95 -0
- package/src/suite/components/today-page-frame.tsx +28 -0
- package/src/suite/components/today-ui.tsx +370 -0
- package/src/suite/icons/app-accents.ts +75 -0
- package/src/suite/icons/glyph-parts.tsx +67 -0
- package/src/suite/icons/glyphs.ts +203 -0
- package/src/suite/icons/index.ts +12 -0
- package/src/suite/icons/suite-app-icon.tsx +68 -0
- package/src/suite/icons/suite-icon.tsx +93 -0
- package/src/suite/index.ts +108 -0
- package/src/suite/lib/apps.ts +75 -0
- package/src/suite/lib/cn.ts +6 -0
- package/src/suite/lib/injected.ts +54 -0
- package/src/suite/lib/motion.tsx +48 -0
- package/src/suite/lib/use-idle-mount.ts +25 -0
- package/dist/chunk-5HSOBJ4F.mjs +0 -5968
- package/src/components/icons/lemon-brand-icons.tsx +0 -16
- package/src/components/icons/lemon-category-icons.tsx +0 -72
- package/src/components/icons/lemon-icons.tsx +0 -57
|
@@ -0,0 +1,669 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, type ComponentPropsWithoutRef } from "react";
|
|
4
|
+
import Image from "next/image";
|
|
5
|
+
import {
|
|
6
|
+
NavArrowDown as ChevronDown,
|
|
7
|
+
Home,
|
|
8
|
+
Calendar,
|
|
9
|
+
Search,
|
|
10
|
+
Bell,
|
|
11
|
+
User,
|
|
12
|
+
Settings,
|
|
13
|
+
LogOut,
|
|
14
|
+
Menu,
|
|
15
|
+
Xmark,
|
|
16
|
+
Plus,
|
|
17
|
+
} from "iconoir-react";
|
|
18
|
+
|
|
19
|
+
import { Button, DropdownMenu, DropdownItem, CommandPalette, ShowcaseWrapper, Spinner } from "@/components/ui";
|
|
20
|
+
import {
|
|
21
|
+
AppSwitcher,
|
|
22
|
+
AppSwitcherMark,
|
|
23
|
+
AppSwitcherChevron,
|
|
24
|
+
type SuiteAppEntry,
|
|
25
|
+
} from "@/suite/components/app-switcher";
|
|
26
|
+
import { CommandPaletteHost } from "@/suite/components/command-palette-host";
|
|
27
|
+
import type { SuiteCommandItem } from "@/suite/lib/injected";
|
|
28
|
+
import { SuiteMobileHeader } from "@/suite/components/suite-mobile-header";
|
|
29
|
+
import { SuiteBottomNav, type SuiteBottomNavItem } from "@/suite/components/suite-bottom-nav";
|
|
30
|
+
import { SuiteUserMenu } from "@/suite/components/suite-user-menu";
|
|
31
|
+
import {
|
|
32
|
+
SuiteNotificationBell,
|
|
33
|
+
type SuiteNotification,
|
|
34
|
+
type SuiteNotificationsResponse,
|
|
35
|
+
} from "@/suite/components/suite-notification-bell";
|
|
36
|
+
import { SuiteMobileDrawer } from "@/suite/components/suite-mobile-drawer";
|
|
37
|
+
import { SuiteSettingsMobileNav } from "@/suite/components/suite-settings-mobile-nav";
|
|
38
|
+
import { SuiteSkeleton, SuiteSkeletonCard, SuiteSkeletonList } from "@/suite/components/suite-skeleton";
|
|
39
|
+
import {
|
|
40
|
+
SuitePage,
|
|
41
|
+
SuitePageHeader,
|
|
42
|
+
SuiteToolbar,
|
|
43
|
+
SuiteTabList,
|
|
44
|
+
SuiteSectionHeader,
|
|
45
|
+
} from "@/suite/components/suite-layout";
|
|
46
|
+
import { TodayPageFrame } from "@/suite/components/today-page-frame";
|
|
47
|
+
import { TodayCalibrating } from "@/suite/components/today-calibrating";
|
|
48
|
+
import { SuiteSidebar, type SuiteSidebarNavItem } from "@/suite/components/suite-sidebar";
|
|
49
|
+
import { SuiteAppLayout } from "@/suite/components/suite-app-layout";
|
|
50
|
+
import {
|
|
51
|
+
SuiteIcon,
|
|
52
|
+
SUITE_ICON_NAMES,
|
|
53
|
+
APP_ACCENTS,
|
|
54
|
+
type SuiteAccentSlug,
|
|
55
|
+
} from "@/suite/icons";
|
|
56
|
+
|
|
57
|
+
const REAL_APPS = ["kraken", "shellstack", "tides", "turtletime"] as const;
|
|
58
|
+
type RealAppSlug = (typeof REAL_APPS)[number];
|
|
59
|
+
|
|
60
|
+
const APP_ICON_SRC: Record<RealAppSlug, string> = {
|
|
61
|
+
turtletime: "/turtletime-icon.svg",
|
|
62
|
+
tides: "/tides-icon.svg",
|
|
63
|
+
kraken: "/kraken-icon.svg",
|
|
64
|
+
shellstack: "/shellstack-icon.svg",
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const ICON_SIZE = 44;
|
|
68
|
+
|
|
69
|
+
const SUITE_APPS: SuiteAppEntry[] = REAL_APPS.map((app) => ({
|
|
70
|
+
slug: app,
|
|
71
|
+
name: APP_ACCENTS[app].label,
|
|
72
|
+
description: `${APP_ACCENTS[app].label} app`,
|
|
73
|
+
icon: APP_ICON_SRC[app],
|
|
74
|
+
}));
|
|
75
|
+
|
|
76
|
+
const BOTTOM_NAV_ITEMS: SuiteBottomNavItem[] = [
|
|
77
|
+
{ href: "#suite-today", label: "Today", icon: Home, active: true },
|
|
78
|
+
{ href: "#suite-track", label: "Track", icon: Calendar },
|
|
79
|
+
{ href: "#suite-reports", label: "Reports", icon: Search },
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
const SETTINGS_ITEMS = [
|
|
83
|
+
{ href: "#profile", label: "Profile", icon: User },
|
|
84
|
+
{ href: "#account", label: "Account", icon: Settings },
|
|
85
|
+
{ href: "#notifications", label: "Notifications", icon: Bell },
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
const SIDEBAR_NAV_ITEMS: SuiteSidebarNavItem[] = [
|
|
89
|
+
{ href: "#suite-today", label: "Today", icon: Home, active: true },
|
|
90
|
+
{ href: "#suite-track", label: "Tracker", icon: Calendar },
|
|
91
|
+
{ href: "#suite-reports", label: "Reports", icon: Search },
|
|
92
|
+
{ href: "#suite-settings", label: "Settings", icon: Settings },
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
const NOTIFICATIONS: SuiteNotification[] = [
|
|
96
|
+
{
|
|
97
|
+
id: "1",
|
|
98
|
+
title: "New document shared",
|
|
99
|
+
body: "Alex shared the Q3 roadmap with you.",
|
|
100
|
+
href: "#",
|
|
101
|
+
kind: "document",
|
|
102
|
+
source_app: "kraken",
|
|
103
|
+
read_at: null,
|
|
104
|
+
created_at: new Date(Date.now() - 5 * 60 * 1000).toISOString(),
|
|
105
|
+
workspace: "ShellStack",
|
|
106
|
+
organisation: "Xenide",
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: "2",
|
|
110
|
+
title: "Task assigned",
|
|
111
|
+
body: "Review TurtleTime weekly report.",
|
|
112
|
+
href: "#",
|
|
113
|
+
kind: "task",
|
|
114
|
+
source_app: "tides",
|
|
115
|
+
read_at: new Date().toISOString(),
|
|
116
|
+
created_at: new Date(Date.now() - 60 * 60 * 1000).toISOString(),
|
|
117
|
+
workspace: "ShellStack",
|
|
118
|
+
organisation: "Xenide",
|
|
119
|
+
},
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
const PALETTE_ITEMS: SuiteCommandItem[] = [
|
|
123
|
+
{ id: "today", label: "Go to Today", icon: <Home className="h-4 w-4" />, onSelect: () => {} },
|
|
124
|
+
{ id: "search", label: "Search", icon: <Search className="h-4 w-4" />, onSelect: () => {} },
|
|
125
|
+
{ id: "settings", label: "Settings", icon: <Settings className="h-4 w-4" />, onSelect: () => {} },
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
function DemoAppIcon({ app }: { app: RealAppSlug }) {
|
|
129
|
+
return (
|
|
130
|
+
<div className="relative h-8 w-8 overflow-hidden rounded-lg">
|
|
131
|
+
<Image
|
|
132
|
+
src={APP_ICON_SRC[app]}
|
|
133
|
+
alt={APP_ACCENTS[app].label}
|
|
134
|
+
width={32}
|
|
135
|
+
height={32}
|
|
136
|
+
className="h-full w-full"
|
|
137
|
+
unoptimized
|
|
138
|
+
/>
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function InjectedDropdownItem(props: ComponentPropsWithoutRef<"button">) {
|
|
144
|
+
return <DropdownItem {...props} />;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export default function SuiteShowcase() {
|
|
148
|
+
const [currentApp, setCurrentApp] = useState<string>("turtletime");
|
|
149
|
+
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
150
|
+
const [settingsHref, setSettingsHref] = useState("#profile");
|
|
151
|
+
const [paletteOpen, setPaletteOpen] = useState(false);
|
|
152
|
+
|
|
153
|
+
return (
|
|
154
|
+
<ShowcaseWrapper
|
|
155
|
+
id="suite"
|
|
156
|
+
title="Suite"
|
|
157
|
+
description="The shared ShellStack chrome — app switcher, mobile header, bottom nav, account menu, notification bell, command palette, drawer, settings nav, skeletons, page layout, and icon system."
|
|
158
|
+
code={`import {
|
|
159
|
+
AppSwitcher, SuiteMobileHeader, SuiteBottomNav,
|
|
160
|
+
SuiteUserMenu, SuiteNotificationBell, CommandPaletteHost,
|
|
161
|
+
SuiteMobileDrawer, SuiteSettingsMobileNav,
|
|
162
|
+
SuiteSkeleton, SuitePage, SuitePageHeader,
|
|
163
|
+
} from "@xenide-io/the-old-ui-theme/suite";`}
|
|
164
|
+
filename="SuiteExample.tsx"
|
|
165
|
+
>
|
|
166
|
+
<div className="space-y-8">
|
|
167
|
+
{/* App icons */}
|
|
168
|
+
<section className="ph-panel space-y-4">
|
|
169
|
+
<div>
|
|
170
|
+
<h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Actual app icons</h3>
|
|
171
|
+
<p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
|
|
172
|
+
The real favicons shipped by each app — not generated suite glyphs.
|
|
173
|
+
</p>
|
|
174
|
+
</div>
|
|
175
|
+
<div className="flex flex-wrap items-center gap-6 rounded-lg border border-ph-border-subtle bg-ph-raised p-6">
|
|
176
|
+
{REAL_APPS.map((app) => (
|
|
177
|
+
<div key={app} className="flex flex-col items-center gap-2">
|
|
178
|
+
<div className="relative h-11 w-11 overflow-hidden rounded-xl">
|
|
179
|
+
<Image
|
|
180
|
+
src={APP_ICON_SRC[app]}
|
|
181
|
+
alt={APP_ACCENTS[app].label}
|
|
182
|
+
width={ICON_SIZE}
|
|
183
|
+
height={ICON_SIZE}
|
|
184
|
+
className="h-full w-full"
|
|
185
|
+
unoptimized
|
|
186
|
+
/>
|
|
187
|
+
</div>
|
|
188
|
+
<span className="text-xs font-medium text-ph-subtle">{APP_ACCENTS[app].label}</span>
|
|
189
|
+
</div>
|
|
190
|
+
))}
|
|
191
|
+
</div>
|
|
192
|
+
</section>
|
|
193
|
+
|
|
194
|
+
{/* App switcher */}
|
|
195
|
+
<section className="ph-panel space-y-4">
|
|
196
|
+
<div>
|
|
197
|
+
<h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">App switcher</h3>
|
|
198
|
+
<p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
|
|
199
|
+
Dropdown app launcher with app mark, title, and collapsible rail mode.
|
|
200
|
+
</p>
|
|
201
|
+
</div>
|
|
202
|
+
<div className="flex flex-wrap items-start gap-6 rounded-lg border border-ph-border-subtle bg-ph-raised p-6">
|
|
203
|
+
<div className="w-64">
|
|
204
|
+
<AppSwitcher
|
|
205
|
+
apps={SUITE_APPS}
|
|
206
|
+
currentApp={currentApp}
|
|
207
|
+
onSelect={(entry) => setCurrentApp(entry.slug)}
|
|
208
|
+
mark={<DemoAppIcon app={currentApp as RealAppSlug} />}
|
|
209
|
+
title={
|
|
210
|
+
<span className="flex min-w-0 flex-1 flex-col">
|
|
211
|
+
<span className="truncate text-sm font-semibold text-ph-ink">{APP_ACCENTS[currentApp as RealAppSlug].label}</span>
|
|
212
|
+
<span className="truncate text-xs text-ph-mutedtext">ShellStack workspace</span>
|
|
213
|
+
</span>
|
|
214
|
+
}
|
|
215
|
+
dropdownMenu={DropdownMenu}
|
|
216
|
+
dropdownItem={InjectedDropdownItem}
|
|
217
|
+
/>
|
|
218
|
+
</div>
|
|
219
|
+
<div className="flex flex-col items-center gap-2">
|
|
220
|
+
<span className="text-xs text-ph-mutedtext">Collapsed rail</span>
|
|
221
|
+
<AppSwitcher
|
|
222
|
+
apps={SUITE_APPS}
|
|
223
|
+
currentApp={currentApp}
|
|
224
|
+
onSelect={(entry) => setCurrentApp(entry.slug)}
|
|
225
|
+
mark={<DemoAppIcon app={currentApp as RealAppSlug} />}
|
|
226
|
+
title={null}
|
|
227
|
+
collapsed
|
|
228
|
+
dropdownMenu={DropdownMenu}
|
|
229
|
+
dropdownItem={InjectedDropdownItem}
|
|
230
|
+
/>
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
</section>
|
|
234
|
+
|
|
235
|
+
{/* Sidebar + app layout */}
|
|
236
|
+
<section className="ph-panel space-y-4">
|
|
237
|
+
<div>
|
|
238
|
+
<h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Standardised app shell</h3>
|
|
239
|
+
<p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
|
|
240
|
+
SuiteSidebar + SuiteAppLayout — the same sidebar and responsive shell every app should use. TurtleTime-style: primary nav + account footer.
|
|
241
|
+
</p>
|
|
242
|
+
</div>
|
|
243
|
+
<div className="overflow-hidden rounded-lg border border-ph-border-subtle bg-ph-raised h-[420px]">
|
|
244
|
+
<div className="hidden lg:block h-full">
|
|
245
|
+
<SuiteAppLayout
|
|
246
|
+
sidebarWidth={220}
|
|
247
|
+
sidebar={
|
|
248
|
+
<SuiteSidebar
|
|
249
|
+
appSwitcher={
|
|
250
|
+
<AppSwitcher
|
|
251
|
+
apps={SUITE_APPS}
|
|
252
|
+
currentApp={currentApp}
|
|
253
|
+
onSelect={(entry) => setCurrentApp(entry.slug)}
|
|
254
|
+
mark={<DemoAppIcon app={currentApp as RealAppSlug} />}
|
|
255
|
+
title={
|
|
256
|
+
<span className="flex min-w-0 flex-col">
|
|
257
|
+
<span className="truncate text-sm font-semibold text-ph-ink">{APP_ACCENTS[currentApp as RealAppSlug].label}</span>
|
|
258
|
+
<span className="truncate text-xs text-ph-mutedtext">ShellStack workspace</span>
|
|
259
|
+
</span>
|
|
260
|
+
}
|
|
261
|
+
dropdownMenu={DropdownMenu}
|
|
262
|
+
dropdownItem={InjectedDropdownItem}
|
|
263
|
+
/>
|
|
264
|
+
}
|
|
265
|
+
contextSwitcher={
|
|
266
|
+
<div className="flex items-center gap-2 rounded-lg border border-ph-border bg-ph-surface px-2 py-1.5 text-sm text-ph-ink">
|
|
267
|
+
<span className="h-5 w-5 rounded bg-ph-brand text-[10px] font-bold text-[var(--ph-on-accent)] flex items-center justify-center">S</span>
|
|
268
|
+
ShellStack
|
|
269
|
+
</div>
|
|
270
|
+
}
|
|
271
|
+
navItems={SIDEBAR_NAV_ITEMS}
|
|
272
|
+
notificationBell={
|
|
273
|
+
<Button variant="ghost" size="sm" icon={<Bell className="h-4 w-4" />} aria-label="Notifications" />
|
|
274
|
+
}
|
|
275
|
+
userMenu={
|
|
276
|
+
<SuiteUserMenu
|
|
277
|
+
name="Jane Doe"
|
|
278
|
+
email="jane@xenide.io"
|
|
279
|
+
settingsHref="#settings"
|
|
280
|
+
onSignOut={() => {}}
|
|
281
|
+
/>
|
|
282
|
+
}
|
|
283
|
+
/>
|
|
284
|
+
}
|
|
285
|
+
>
|
|
286
|
+
<div className="p-6 text-sm text-ph-subtle">
|
|
287
|
+
Main content area. On desktop the sidebar is fixed; on mobile it collapses to the suite mobile chrome.
|
|
288
|
+
</div>
|
|
289
|
+
</SuiteAppLayout>
|
|
290
|
+
</div>
|
|
291
|
+
<div className="flex flex-col lg:hidden h-full">
|
|
292
|
+
<SuiteMobileHeader
|
|
293
|
+
onMenuClick={() => setDrawerOpen((v) => !v)}
|
|
294
|
+
title={
|
|
295
|
+
<div className="flex items-center gap-2">
|
|
296
|
+
<DemoAppIcon app={currentApp as RealAppSlug} />
|
|
297
|
+
<span className="font-semibold text-ph-ink">{APP_ACCENTS[currentApp as RealAppSlug].label}</span>
|
|
298
|
+
</div>
|
|
299
|
+
}
|
|
300
|
+
actions={
|
|
301
|
+
<>
|
|
302
|
+
<Button variant="ghost" size="sm" icon={<Bell className="h-4 w-4" />} aria-label="Notifications" />
|
|
303
|
+
<div className="h-8 w-8 rounded-full bg-ph-brand text-xs font-bold text-[var(--ph-on-accent)] flex items-center justify-center">JD</div>
|
|
304
|
+
</>
|
|
305
|
+
}
|
|
306
|
+
/>
|
|
307
|
+
<main className="flex-1 p-4 text-sm text-ph-subtle">
|
|
308
|
+
Mobile content area.
|
|
309
|
+
</main>
|
|
310
|
+
<SuiteBottomNav items={BOTTOM_NAV_ITEMS} />
|
|
311
|
+
</div>
|
|
312
|
+
</div>
|
|
313
|
+
</section>
|
|
314
|
+
|
|
315
|
+
{/* Sidebar with secondary nav / project tree */}
|
|
316
|
+
<section className="ph-panel space-y-4">
|
|
317
|
+
<div>
|
|
318
|
+
<h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Sidebar with project tree</h3>
|
|
319
|
+
<p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
|
|
320
|
+
Tides / Kraken pattern: primary nav above, secondary project/document tree below, and a mobile browse button that opens the same tree in a drawer.
|
|
321
|
+
</p>
|
|
322
|
+
</div>
|
|
323
|
+
<div className="overflow-hidden rounded-lg border border-ph-border-subtle bg-ph-raised h-[420px]">
|
|
324
|
+
<div className="hidden lg:block h-full">
|
|
325
|
+
<SuiteAppLayout
|
|
326
|
+
sidebarWidth={240}
|
|
327
|
+
sidebar={
|
|
328
|
+
<SuiteSidebar
|
|
329
|
+
appSwitcher={
|
|
330
|
+
<AppSwitcher
|
|
331
|
+
apps={SUITE_APPS}
|
|
332
|
+
currentApp={currentApp}
|
|
333
|
+
onSelect={(entry) => setCurrentApp(entry.slug)}
|
|
334
|
+
mark={<DemoAppIcon app={currentApp as RealAppSlug} />}
|
|
335
|
+
title={
|
|
336
|
+
<span className="flex min-w-0 flex-col">
|
|
337
|
+
<span className="truncate text-sm font-semibold text-ph-ink">{APP_ACCENTS[currentApp as RealAppSlug].label}</span>
|
|
338
|
+
<span className="truncate text-xs text-ph-mutedtext">ShellStack workspace</span>
|
|
339
|
+
</span>
|
|
340
|
+
}
|
|
341
|
+
dropdownMenu={DropdownMenu}
|
|
342
|
+
dropdownItem={InjectedDropdownItem}
|
|
343
|
+
/>
|
|
344
|
+
}
|
|
345
|
+
contextSwitcher={
|
|
346
|
+
<div className="flex items-center gap-2 rounded-lg border border-ph-border bg-ph-surface px-2 py-1.5 text-sm text-ph-ink">
|
|
347
|
+
<span className="h-5 w-5 rounded bg-ph-brand text-[10px] font-bold text-[var(--ph-on-accent)] flex items-center justify-center">W</span>
|
|
348
|
+
Workspace
|
|
349
|
+
</div>
|
|
350
|
+
}
|
|
351
|
+
navItems={SIDEBAR_NAV_ITEMS}
|
|
352
|
+
secondaryNav={
|
|
353
|
+
<div className="space-y-1">
|
|
354
|
+
<p className="px-2 pb-1 text-[10px] font-semibold uppercase tracking-wider text-ph-mutedtext">Projects</p>
|
|
355
|
+
<button type="button" className="flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-sm text-ph-subtle hover:bg-ph-muted">
|
|
356
|
+
<span className="h-2 w-2 rounded-full bg-ph-brand" />
|
|
357
|
+
Acme Corp
|
|
358
|
+
</button>
|
|
359
|
+
<button type="button" className="flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-sm text-ph-subtle hover:bg-ph-muted">
|
|
360
|
+
<span className="h-2 w-2 rounded-full bg-ph-accent" />
|
|
361
|
+
Globex
|
|
362
|
+
</button>
|
|
363
|
+
</div>
|
|
364
|
+
}
|
|
365
|
+
notificationBell={
|
|
366
|
+
<Button variant="ghost" size="sm" icon={<Bell className="h-4 w-4" />} aria-label="Notifications" />
|
|
367
|
+
}
|
|
368
|
+
userMenu={
|
|
369
|
+
<SuiteUserMenu
|
|
370
|
+
name="Jane Doe"
|
|
371
|
+
email="jane@xenide.io"
|
|
372
|
+
settingsHref="#settings"
|
|
373
|
+
onSignOut={() => {}}
|
|
374
|
+
/>
|
|
375
|
+
}
|
|
376
|
+
/>
|
|
377
|
+
}
|
|
378
|
+
>
|
|
379
|
+
<div className="p-6 text-sm text-ph-subtle">
|
|
380
|
+
Desktop content area. The project tree lives inside the sidebar via the <code className="rounded bg-ph-muted px-1 py-0.5 text-xs">secondaryNav</code> prop.
|
|
381
|
+
</div>
|
|
382
|
+
</SuiteAppLayout>
|
|
383
|
+
</div>
|
|
384
|
+
<div className="flex flex-col lg:hidden h-full">
|
|
385
|
+
<SuiteMobileHeader
|
|
386
|
+
title={
|
|
387
|
+
<div className="flex items-center gap-2">
|
|
388
|
+
<DemoAppIcon app={currentApp as RealAppSlug} />
|
|
389
|
+
<span className="font-semibold text-ph-ink">{APP_ACCENTS[currentApp as RealAppSlug].label}</span>
|
|
390
|
+
<Button
|
|
391
|
+
variant="ghost"
|
|
392
|
+
size="sm"
|
|
393
|
+
onClick={() => setDrawerOpen(true)}
|
|
394
|
+
icon={<Search className="h-4 w-4" />}
|
|
395
|
+
aria-label="Browse projects"
|
|
396
|
+
/>
|
|
397
|
+
</div>
|
|
398
|
+
}
|
|
399
|
+
actions={
|
|
400
|
+
<>
|
|
401
|
+
<Button variant="ghost" size="sm" icon={<Bell className="h-4 w-4" />} aria-label="Notifications" />
|
|
402
|
+
<div className="h-8 w-8 rounded-full bg-ph-brand text-xs font-bold text-[var(--ph-on-accent)] flex items-center justify-center">JD</div>
|
|
403
|
+
</>
|
|
404
|
+
}
|
|
405
|
+
/>
|
|
406
|
+
<main className="flex-1 p-4 text-sm text-ph-subtle">
|
|
407
|
+
Mobile content area. Tap the search icon in the header to open the project tree drawer.
|
|
408
|
+
</main>
|
|
409
|
+
<SuiteBottomNav items={BOTTOM_NAV_ITEMS} />
|
|
410
|
+
<SuiteMobileDrawer open={drawerOpen} onClose={() => setDrawerOpen(false)} showCloseButton dialogLabel="Browse projects" side="right">
|
|
411
|
+
<div className="p-4 space-y-1">
|
|
412
|
+
<p className="px-2 pb-1 text-[10px] font-semibold uppercase tracking-wider text-ph-mutedtext">Projects</p>
|
|
413
|
+
<button type="button" className="flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-sm text-ph-subtle hover:bg-ph-muted">
|
|
414
|
+
<span className="h-2 w-2 rounded-full bg-ph-brand" />
|
|
415
|
+
Acme Corp
|
|
416
|
+
</button>
|
|
417
|
+
<button type="button" className="flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-sm text-ph-subtle hover:bg-ph-muted">
|
|
418
|
+
<span className="h-2 w-2 rounded-full bg-ph-accent" />
|
|
419
|
+
Globex
|
|
420
|
+
</button>
|
|
421
|
+
</div>
|
|
422
|
+
</SuiteMobileDrawer>
|
|
423
|
+
</div>
|
|
424
|
+
</div>
|
|
425
|
+
</section>
|
|
426
|
+
|
|
427
|
+
{/* Mobile header + bottom nav */}
|
|
428
|
+
<section className="ph-panel space-y-4">
|
|
429
|
+
<div>
|
|
430
|
+
<h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Mobile chrome</h3>
|
|
431
|
+
<p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
|
|
432
|
+
SuiteMobileHeader and SuiteBottomNav — the standard mobile shell used by every app.
|
|
433
|
+
</p>
|
|
434
|
+
</div>
|
|
435
|
+
<div className="space-y-4 rounded-lg border border-ph-border-subtle bg-ph-raised p-4">
|
|
436
|
+
<SuiteMobileHeader
|
|
437
|
+
onMenuClick={() => setDrawerOpen((v) => !v)}
|
|
438
|
+
title={
|
|
439
|
+
<div className="flex items-center gap-2">
|
|
440
|
+
<DemoAppIcon app="turtletime" />
|
|
441
|
+
<span className="font-semibold text-ph-ink">TurtleTime</span>
|
|
442
|
+
</div>
|
|
443
|
+
}
|
|
444
|
+
actions={
|
|
445
|
+
<>
|
|
446
|
+
<Button variant="ghost" size="sm" icon={<Bell className="h-4 w-4" />} aria-label="Notifications" />
|
|
447
|
+
<div className="h-8 w-8 rounded-full bg-ph-brand text-xs font-bold text-[var(--ph-on-accent)] flex items-center justify-center">JD</div>
|
|
448
|
+
</>
|
|
449
|
+
}
|
|
450
|
+
/>
|
|
451
|
+
<div className="rounded-lg border border-ph-border bg-ph-surface p-4 text-sm text-ph-subtle">
|
|
452
|
+
Page content area
|
|
453
|
+
</div>
|
|
454
|
+
<SuiteBottomNav items={BOTTOM_NAV_ITEMS} />
|
|
455
|
+
</div>
|
|
456
|
+
</section>
|
|
457
|
+
|
|
458
|
+
{/* User menu + notification bell */}
|
|
459
|
+
<section className="ph-panel space-y-4">
|
|
460
|
+
<div>
|
|
461
|
+
<h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Account & notifications</h3>
|
|
462
|
+
<p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
|
|
463
|
+
SuiteUserMenu and SuiteNotificationBell with mock data.
|
|
464
|
+
</p>
|
|
465
|
+
</div>
|
|
466
|
+
<div className="flex flex-wrap items-start gap-6 rounded-lg border border-ph-border-subtle bg-ph-raised p-6">
|
|
467
|
+
<SuiteUserMenu
|
|
468
|
+
name="Jane Doe"
|
|
469
|
+
email="jane@xenide.io"
|
|
470
|
+
settingsHref="#settings"
|
|
471
|
+
onSignOut={() => {}}
|
|
472
|
+
/>
|
|
473
|
+
<SuiteNotificationBell
|
|
474
|
+
fetchNotifications={async () => ({
|
|
475
|
+
notifications: NOTIFICATIONS,
|
|
476
|
+
unread_count: 1,
|
|
477
|
+
})}
|
|
478
|
+
markRead={async () => {}}
|
|
479
|
+
markAllRead={async () => {}}
|
|
480
|
+
dropdownMenu={DropdownMenu}
|
|
481
|
+
/>
|
|
482
|
+
</div>
|
|
483
|
+
</section>
|
|
484
|
+
|
|
485
|
+
{/* Command palette */}
|
|
486
|
+
<section className="ph-panel space-y-4">
|
|
487
|
+
<div>
|
|
488
|
+
<h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Command palette</h3>
|
|
489
|
+
<p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
|
|
490
|
+
Cmd/Ctrl+K host wired to the theme CommandPalette. Press the button below to open.
|
|
491
|
+
</p>
|
|
492
|
+
</div>
|
|
493
|
+
<div className="rounded-lg border border-ph-border-subtle bg-ph-raised p-6">
|
|
494
|
+
<CommandPaletteHost
|
|
495
|
+
items={PALETTE_ITEMS}
|
|
496
|
+
commandPalette={CommandPalette}
|
|
497
|
+
/>
|
|
498
|
+
<Button
|
|
499
|
+
variant="secondary"
|
|
500
|
+
size="sm"
|
|
501
|
+
onClick={() => setPaletteOpen(true)}
|
|
502
|
+
className="mt-4"
|
|
503
|
+
>
|
|
504
|
+
Open command palette
|
|
505
|
+
</Button>
|
|
506
|
+
<CommandPalette
|
|
507
|
+
items={PALETTE_ITEMS}
|
|
508
|
+
isOpen={paletteOpen}
|
|
509
|
+
onClose={() => setPaletteOpen(false)}
|
|
510
|
+
/>
|
|
511
|
+
</div>
|
|
512
|
+
</section>
|
|
513
|
+
|
|
514
|
+
{/* Mobile drawer */}
|
|
515
|
+
<section className="ph-panel space-y-4">
|
|
516
|
+
<div>
|
|
517
|
+
<h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Mobile drawer</h3>
|
|
518
|
+
<p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
|
|
519
|
+
Slide-in panel with backdrop, Escape close, and body scroll-lock.
|
|
520
|
+
</p>
|
|
521
|
+
</div>
|
|
522
|
+
<div className="rounded-lg border border-ph-border-subtle bg-ph-raised p-6">
|
|
523
|
+
<Button variant="secondary" size="sm" onClick={() => setDrawerOpen(true)}>
|
|
524
|
+
Open drawer
|
|
525
|
+
</Button>
|
|
526
|
+
<SuiteMobileDrawer open={drawerOpen} onClose={() => setDrawerOpen(false)} showCloseButton dialogLabel="Demo navigation">
|
|
527
|
+
<div className="p-4 space-y-2">
|
|
528
|
+
<a href="#suite" className="block rounded-lg px-3 py-2 text-sm text-ph-ink hover:bg-ph-muted">Today</a>
|
|
529
|
+
<a href="#suite" className="block rounded-lg px-3 py-2 text-sm text-ph-ink hover:bg-ph-muted">Track time</a>
|
|
530
|
+
<a href="#suite" className="block rounded-lg px-3 py-2 text-sm text-ph-ink hover:bg-ph-muted">Reports</a>
|
|
531
|
+
</div>
|
|
532
|
+
</SuiteMobileDrawer>
|
|
533
|
+
</div>
|
|
534
|
+
</section>
|
|
535
|
+
|
|
536
|
+
{/* Settings mobile nav */}
|
|
537
|
+
<section className="ph-panel space-y-4">
|
|
538
|
+
<div>
|
|
539
|
+
<h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Settings mobile nav</h3>
|
|
540
|
+
<p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
|
|
541
|
+
Horizontal scrollable icon+label pills for mobile settings.
|
|
542
|
+
</p>
|
|
543
|
+
</div>
|
|
544
|
+
<div className="rounded-lg border border-ph-border-subtle bg-ph-raised p-4">
|
|
545
|
+
<SuiteSettingsMobileNav
|
|
546
|
+
items={SETTINGS_ITEMS.map((item) => ({ ...item, icon: item.icon }))}
|
|
547
|
+
activeHref={settingsHref}
|
|
548
|
+
onSelect={(href) => setSettingsHref(href)}
|
|
549
|
+
/>
|
|
550
|
+
</div>
|
|
551
|
+
</section>
|
|
552
|
+
|
|
553
|
+
{/* Skeletons */}
|
|
554
|
+
<section className="ph-panel space-y-4">
|
|
555
|
+
<div>
|
|
556
|
+
<h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Skeletons</h3>
|
|
557
|
+
<p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
|
|
558
|
+
Shared loading placeholders using the theme shimmer.
|
|
559
|
+
</p>
|
|
560
|
+
</div>
|
|
561
|
+
<div className="grid gap-4 rounded-lg border border-ph-border-subtle bg-ph-raised p-6 sm:grid-cols-2 lg:grid-cols-3">
|
|
562
|
+
<SuiteSkeleton lines={3} />
|
|
563
|
+
<SuiteSkeletonCard rows={3} />
|
|
564
|
+
<SuiteSkeletonList items={3} />
|
|
565
|
+
</div>
|
|
566
|
+
</section>
|
|
567
|
+
|
|
568
|
+
{/* Page layout primitives */}
|
|
569
|
+
<section className="ph-panel space-y-4">
|
|
570
|
+
<div>
|
|
571
|
+
<h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Page layout primitives</h3>
|
|
572
|
+
<p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
|
|
573
|
+
SuitePage, SuitePageHeader, SuiteToolbar, SuiteTabList, and SuiteSectionHeader.
|
|
574
|
+
</p>
|
|
575
|
+
</div>
|
|
576
|
+
<div className="rounded-lg border border-ph-border-subtle bg-ph-raised p-6">
|
|
577
|
+
<SuitePage width="content" inset className="rounded-xl border border-ph-border bg-ph-surface shadow-sm">
|
|
578
|
+
<SuitePageHeader
|
|
579
|
+
eyebrow="Workspace"
|
|
580
|
+
title="Projects"
|
|
581
|
+
description="Manage client work across the suite."
|
|
582
|
+
actions={
|
|
583
|
+
<SuiteToolbar>
|
|
584
|
+
<Button variant="secondary" size="sm">Filter</Button>
|
|
585
|
+
<Button variant="primary" size="sm" icon={<Plus className="h-3.5 w-3.5" />}>New</Button>
|
|
586
|
+
</SuiteToolbar>
|
|
587
|
+
}
|
|
588
|
+
/>
|
|
589
|
+
<SuiteTabList label="Project views" className="mt-6">
|
|
590
|
+
<Button variant="ghost" size="sm">Board</Button>
|
|
591
|
+
<Button variant="ghost" size="sm">List</Button>
|
|
592
|
+
<Button variant="ghost" size="sm">Calendar</Button>
|
|
593
|
+
</SuiteTabList>
|
|
594
|
+
<SuiteSectionHeader
|
|
595
|
+
title="Active projects"
|
|
596
|
+
description="3 projects with work this week"
|
|
597
|
+
action={<Button variant="ghost" size="sm">View all</Button>}
|
|
598
|
+
className="mt-6"
|
|
599
|
+
/>
|
|
600
|
+
</SuitePage>
|
|
601
|
+
</div>
|
|
602
|
+
</section>
|
|
603
|
+
|
|
604
|
+
{/* Today frame */}
|
|
605
|
+
<section className="ph-panel space-y-4">
|
|
606
|
+
<div>
|
|
607
|
+
<h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Today page chrome</h3>
|
|
608
|
+
<p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
|
|
609
|
+
TodayPageFrame and TodayCalibrating loading state.
|
|
610
|
+
</p>
|
|
611
|
+
</div>
|
|
612
|
+
<div className="grid gap-4 rounded-lg border border-ph-border-subtle bg-ph-raised p-6 sm:grid-cols-2">
|
|
613
|
+
<div className="h-48 overflow-hidden rounded-xl">
|
|
614
|
+
<TodayPageFrame>
|
|
615
|
+
<div className="p-6 text-sm text-ph-ink">Today page content goes here.</div>
|
|
616
|
+
</TodayPageFrame>
|
|
617
|
+
</div>
|
|
618
|
+
<div className="flex h-48 items-center justify-center rounded-xl border border-ph-border bg-ph-surface">
|
|
619
|
+
<TodayCalibrating spinner={Spinner} />
|
|
620
|
+
</div>
|
|
621
|
+
</div>
|
|
622
|
+
</section>
|
|
623
|
+
|
|
624
|
+
{/* Suite glyphs */}
|
|
625
|
+
<section className="ph-panel space-y-4">
|
|
626
|
+
<div>
|
|
627
|
+
<h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Suite glyphs</h3>
|
|
628
|
+
<p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
|
|
629
|
+
24×24 strokes that inherit currentColor and accept a per-app accent tint.
|
|
630
|
+
</p>
|
|
631
|
+
</div>
|
|
632
|
+
<div className="grid grid-cols-3 gap-3 sm:grid-cols-4 md:grid-cols-6 lg:grid-cols-8">
|
|
633
|
+
{SUITE_ICON_NAMES.map((name) => (
|
|
634
|
+
<div
|
|
635
|
+
key={name}
|
|
636
|
+
className="flex min-w-0 flex-col items-center gap-2 rounded-lg border border-ph-border-subtle bg-ph-raised p-3 text-center"
|
|
637
|
+
>
|
|
638
|
+
<div className="flex h-8 items-center text-ph-ink">
|
|
639
|
+
<SuiteIcon name={name} size={24} />
|
|
640
|
+
</div>
|
|
641
|
+
<span className="max-w-full break-words text-[10px] font-medium tracking-wide text-ph-mutedtext">
|
|
642
|
+
{name}
|
|
643
|
+
</span>
|
|
644
|
+
</div>
|
|
645
|
+
))}
|
|
646
|
+
</div>
|
|
647
|
+
</section>
|
|
648
|
+
|
|
649
|
+
{/* Accent tints */}
|
|
650
|
+
<section className="ph-panel space-y-4">
|
|
651
|
+
<div>
|
|
652
|
+
<h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Accent tints</h3>
|
|
653
|
+
<p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
|
|
654
|
+
The same glyph with each app accent applied via CSS custom property.
|
|
655
|
+
</p>
|
|
656
|
+
</div>
|
|
657
|
+
<div className="flex flex-wrap items-center gap-6 rounded-lg border border-ph-border-subtle bg-ph-raised p-6">
|
|
658
|
+
{REAL_APPS.map((app) => (
|
|
659
|
+
<div key={app} className="flex flex-col items-center gap-2">
|
|
660
|
+
<SuiteIcon name="today-sun" accent={app} size={32} />
|
|
661
|
+
<span className="text-xs font-medium text-ph-subtle">{APP_ACCENTS[app].label}</span>
|
|
662
|
+
</div>
|
|
663
|
+
))}
|
|
664
|
+
</div>
|
|
665
|
+
</section>
|
|
666
|
+
</div>
|
|
667
|
+
</ShowcaseWrapper>
|
|
668
|
+
);
|
|
669
|
+
}
|