@xenide-io/the-old-ui-theme 0.6.13 → 0.7.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/dist/{chunk-E5ZBQYEX.mjs → chunk-KDR6HI6F.mjs} +3 -1
- package/dist/{chunk-VOCHIR3W.mjs → chunk-VXISSXTB.mjs} +1 -1
- package/dist/index.js +3 -1
- package/dist/index.mjs +2 -2
- package/dist/suite.d.mts +6 -3
- package/dist/suite.d.ts +6 -3
- package/dist/suite.js +50 -40
- package/dist/suite.mjs +57 -44
- package/dist/ui.js +3 -1
- package/dist/ui.mjs +2 -2
- package/package.json +1 -1
- package/scripts/sync-posthog-icons.mjs +3 -4
- package/src/components/icons/icons.tsx +3 -1
- package/src/suite/components/app-switcher.test.tsx +96 -0
- package/src/suite/components/app-switcher.tsx +57 -39
- package/src/suite/components/suite-notification-bell.test.tsx +47 -0
- package/src/suite/components/suite-notification-bell.tsx +6 -4
- package/src/suite/index.ts +1 -0
|
@@ -170,7 +170,9 @@ var IconPlayCircle = createIconBase({
|
|
|
170
170
|
)
|
|
171
171
|
});
|
|
172
172
|
var IconGridView = createIconBase({
|
|
173
|
-
paths: fill(
|
|
173
|
+
paths: fill(
|
|
174
|
+
"M3,3v8h8V3H3z M9,9H5V5h4V9z M3,13v8h8v-8H3z M9,19H5v-4h4V19z M13,3v8h8V3H13z M19,9h-4V5h4V9z M13,13v8h8v-8H13z M19,19h-4v-4h4V19z"
|
|
175
|
+
)
|
|
174
176
|
});
|
|
175
177
|
var IconListView = createIconBase({
|
|
176
178
|
paths: fill(
|
package/dist/index.js
CHANGED
|
@@ -480,7 +480,9 @@ var IconPlayCircle = createIconBase({
|
|
|
480
480
|
)
|
|
481
481
|
});
|
|
482
482
|
var IconGridView = createIconBase({
|
|
483
|
-
paths: fill(
|
|
483
|
+
paths: fill(
|
|
484
|
+
"M3,3v8h8V3H3z M9,9H5V5h4V9z M3,13v8h8v-8H3z M9,19H5v-4h4V19z M13,3v8h8V3H13z M19,9h-4V5h4V9z M13,13v8h8v-8H13z M19,19h-4v-4h4V19z"
|
|
485
|
+
)
|
|
484
486
|
});
|
|
485
487
|
var IconListView = createIconBase({
|
|
486
488
|
paths: fill(
|
package/dist/index.mjs
CHANGED
|
@@ -63,7 +63,7 @@ import {
|
|
|
63
63
|
isThemeId,
|
|
64
64
|
persistTheme,
|
|
65
65
|
readStoredTheme
|
|
66
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-VXISSXTB.mjs";
|
|
67
67
|
import {
|
|
68
68
|
ButtonChrome,
|
|
69
69
|
CANONICAL_ICONS,
|
|
@@ -157,7 +157,7 @@ import {
|
|
|
157
157
|
TooltipProvider,
|
|
158
158
|
createIconBase,
|
|
159
159
|
createIconBasePath
|
|
160
|
-
} from "./chunk-
|
|
160
|
+
} from "./chunk-KDR6HI6F.mjs";
|
|
161
161
|
import "./chunk-FWCSY2DS.mjs";
|
|
162
162
|
export {
|
|
163
163
|
Accordion,
|
package/dist/suite.d.mts
CHANGED
|
@@ -170,7 +170,6 @@ type SuiteSpinnerComponent = ComponentType<{
|
|
|
170
170
|
|
|
171
171
|
declare function appSwitcherTriggerClass(open: boolean, collapsed?: boolean): string;
|
|
172
172
|
declare function appSwitcherMarkClass(): string;
|
|
173
|
-
/** No sticky Radix highlight while waiting for cross-app redirect. */
|
|
174
173
|
declare function appSwitcherMenuItemClass(): string;
|
|
175
174
|
declare function AppSwitcherMark({ children, collapsed, }: {
|
|
176
175
|
children: ReactNode;
|
|
@@ -186,6 +185,10 @@ interface SuiteAppEntry {
|
|
|
186
185
|
description: string;
|
|
187
186
|
icon: string;
|
|
188
187
|
}
|
|
188
|
+
interface SuiteAppSelectOptions {
|
|
189
|
+
/** Open the handoff in a separate browser tab when requested by a caller. */
|
|
190
|
+
newTab?: boolean;
|
|
191
|
+
}
|
|
189
192
|
/**
|
|
190
193
|
* Suite app switcher dropdown. Apps inject their brand mark/title, the
|
|
191
194
|
* visible app list, the cross-app navigation handler, and the theme
|
|
@@ -194,7 +197,7 @@ interface SuiteAppEntry {
|
|
|
194
197
|
declare function AppSwitcher({ apps, currentApp, onSelect, mark, title, collapsed, dropdownMenu: DropdownMenu, dropdownItem: DropdownItem, triggerId, triggerDataTest, }: {
|
|
195
198
|
apps: SuiteAppEntry[];
|
|
196
199
|
currentApp: string;
|
|
197
|
-
onSelect: (app: SuiteAppEntry) => void;
|
|
200
|
+
onSelect: (app: SuiteAppEntry, options?: SuiteAppSelectOptions) => void;
|
|
198
201
|
mark: ReactNode;
|
|
199
202
|
title: ReactNode;
|
|
200
203
|
collapsed?: boolean;
|
|
@@ -984,4 +987,4 @@ interface SuiteAppLayoutProps {
|
|
|
984
987
|
*/
|
|
985
988
|
declare function SuiteAppLayout({ sidebar, children, mobileHeader, bottomNav, sidebarWidth, collapsed, lockMainScroll, onStartResize, onResizeBy, onResetWidth, className, }: SuiteAppLayoutProps): react.JSX.Element;
|
|
986
989
|
|
|
987
|
-
export { APP_ACCENTS, APP_GLYPHS, AnimatedMoon, AnimatedSun, AppSwitcher, AppSwitcherChevron, AppSwitcherMark, CommandPaletteHost, DeferredChrome, SIDEBAR_COLLAPSE_THRESHOLD, SIDEBAR_DEFAULT_WIDTH, SIDEBAR_MAX_WIDTH, SIDEBAR_MIN_EXPANDED_WIDTH, SIDEBAR_RAIL_WIDTH, SUITE_APPS, SUITE_APP_MAP, SUITE_ASK_AI_OPEN_KEY, SUITE_ASK_AI_OPEN_QUERY, SUITE_GLYPHS, SUITE_ICON_NAMES, SUITE_MUTATED_EVENT, SUITE_OPEN_ASK_AI_EVENT, SUITE_SIDEBAR_WIDTH_COOKIE, SUITE_SIDEBAR_WIDTH_KEY, SUITE_SPRINGS, SourceAppGlyph, type SuiteAccentSlug, type SuiteAiAction, type SuiteAiActionStatus, type SuiteAiChatMessage, SuiteAiPanel, type SuiteAiPreset, type SuiteAppAccent, type SuiteAppDefinition, type SuiteAppEntry, SuiteAppIcon, type SuiteAppIconProps, SuiteAppLayout, type SuiteAppLayoutProps, type SuiteAppSlug, SuiteAppStrip, type SuiteAskAiOpenDetail, SuiteBottomNav, type SuiteBottomNavItem, SuiteBreadcrumbs, type SuiteBreadcrumbsProps, type SuiteCommandItem, type SuiteCommandPaletteComponent, type SuiteDropdownItemComponent, type SuiteDropdownMenuComponent, type SuiteDropdownMenuProps, SuiteEmptyState, type SuiteGlyph, type SuiteGlyphElement, type SuiteHrefTarget, SuiteIcon, type SuiteIconName, type SuiteIconProps, SuiteMobileDrawer, SuiteMobileHeader, SuiteMotionProvider, type SuiteNotification, SuiteNotificationBell, type SuiteNotificationsResponse, SuitePage, SuitePageHeader, type SuitePageWidth, type SuiteResolvedTheme, SuiteSectionHeader, SuiteSettingsLayout, type SuiteSettingsLayoutProps, SuiteSettingsMobileNav, type SuiteSettingsNavItem, SuiteSidebar, type SuiteSidebarNavItem, SuiteSkeleton, SuiteSkeletonCard, SuiteSkeletonList, type SuiteSpinnerComponent, type SuiteSpringName, SuiteTabList, type SuiteTheme, type SuiteThemeConfig, type SuiteThemeContextValue, SuiteThemeProvider, SuiteToolbar, SuiteUserMenu, type SuiteUserMenuProps, type SuiteWorkspaceEntry, type SuiteWorkspaceGroup, SuiteWorkspaceSwitcher, TURTLETIME_TIMER_MUTATED_EVENT, TodayAskAiCard, TodayCalibrating, TodayEmptyAction, TodayEmptyState, TodayHero, TodayLayout, TodayPageFrame, TodayPanel, type TodayPrimaryAccent, TodayPrimaryAction, TodaySection, TodayTimeIcon, TodayTopBar, appSwitcherMarkClass, appSwitcherMenuItemClass, appSwitcherTriggerClass, classifySuiteHref, cn, consumePendingAskAiPrompt, consumeSuiteAskAiOpenFromLocation, getTodayGreeting, openSuiteAskAi, pathWithSuiteAskAiOpen, persistSidebarWidth, persistSuiteAskAiOpen, readSidebarWidth, readSuiteAskAiOpen, resetSuiteSidebarWidth, resolveSuiteNotificationHref, sidebarColumnWidth, sourceToSuiteApp, suiteAppBaseUrl, suiteAppLabel, useIdleMount, useSidebarWidth, useSuiteTheme };
|
|
990
|
+
export { APP_ACCENTS, APP_GLYPHS, AnimatedMoon, AnimatedSun, AppSwitcher, AppSwitcherChevron, AppSwitcherMark, CommandPaletteHost, DeferredChrome, SIDEBAR_COLLAPSE_THRESHOLD, SIDEBAR_DEFAULT_WIDTH, SIDEBAR_MAX_WIDTH, SIDEBAR_MIN_EXPANDED_WIDTH, SIDEBAR_RAIL_WIDTH, SUITE_APPS, SUITE_APP_MAP, SUITE_ASK_AI_OPEN_KEY, SUITE_ASK_AI_OPEN_QUERY, SUITE_GLYPHS, SUITE_ICON_NAMES, SUITE_MUTATED_EVENT, SUITE_OPEN_ASK_AI_EVENT, SUITE_SIDEBAR_WIDTH_COOKIE, SUITE_SIDEBAR_WIDTH_KEY, SUITE_SPRINGS, SourceAppGlyph, type SuiteAccentSlug, type SuiteAiAction, type SuiteAiActionStatus, type SuiteAiChatMessage, SuiteAiPanel, type SuiteAiPreset, type SuiteAppAccent, type SuiteAppDefinition, type SuiteAppEntry, SuiteAppIcon, type SuiteAppIconProps, SuiteAppLayout, type SuiteAppLayoutProps, type SuiteAppSelectOptions, type SuiteAppSlug, SuiteAppStrip, type SuiteAskAiOpenDetail, SuiteBottomNav, type SuiteBottomNavItem, SuiteBreadcrumbs, type SuiteBreadcrumbsProps, type SuiteCommandItem, type SuiteCommandPaletteComponent, type SuiteDropdownItemComponent, type SuiteDropdownMenuComponent, type SuiteDropdownMenuProps, SuiteEmptyState, type SuiteGlyph, type SuiteGlyphElement, type SuiteHrefTarget, SuiteIcon, type SuiteIconName, type SuiteIconProps, SuiteMobileDrawer, SuiteMobileHeader, SuiteMotionProvider, type SuiteNotification, SuiteNotificationBell, type SuiteNotificationsResponse, SuitePage, SuitePageHeader, type SuitePageWidth, type SuiteResolvedTheme, SuiteSectionHeader, SuiteSettingsLayout, type SuiteSettingsLayoutProps, SuiteSettingsMobileNav, type SuiteSettingsNavItem, SuiteSidebar, type SuiteSidebarNavItem, SuiteSkeleton, SuiteSkeletonCard, SuiteSkeletonList, type SuiteSpinnerComponent, type SuiteSpringName, SuiteTabList, type SuiteTheme, type SuiteThemeConfig, type SuiteThemeContextValue, SuiteThemeProvider, SuiteToolbar, SuiteUserMenu, type SuiteUserMenuProps, type SuiteWorkspaceEntry, type SuiteWorkspaceGroup, SuiteWorkspaceSwitcher, TURTLETIME_TIMER_MUTATED_EVENT, TodayAskAiCard, TodayCalibrating, TodayEmptyAction, TodayEmptyState, TodayHero, TodayLayout, TodayPageFrame, TodayPanel, type TodayPrimaryAccent, TodayPrimaryAction, TodaySection, TodayTimeIcon, TodayTopBar, appSwitcherMarkClass, appSwitcherMenuItemClass, appSwitcherTriggerClass, classifySuiteHref, cn, consumePendingAskAiPrompt, consumeSuiteAskAiOpenFromLocation, getTodayGreeting, openSuiteAskAi, pathWithSuiteAskAiOpen, persistSidebarWidth, persistSuiteAskAiOpen, readSidebarWidth, readSuiteAskAiOpen, resetSuiteSidebarWidth, resolveSuiteNotificationHref, sidebarColumnWidth, sourceToSuiteApp, suiteAppBaseUrl, suiteAppLabel, useIdleMount, useSidebarWidth, useSuiteTheme };
|
package/dist/suite.d.ts
CHANGED
|
@@ -170,7 +170,6 @@ type SuiteSpinnerComponent = ComponentType<{
|
|
|
170
170
|
|
|
171
171
|
declare function appSwitcherTriggerClass(open: boolean, collapsed?: boolean): string;
|
|
172
172
|
declare function appSwitcherMarkClass(): string;
|
|
173
|
-
/** No sticky Radix highlight while waiting for cross-app redirect. */
|
|
174
173
|
declare function appSwitcherMenuItemClass(): string;
|
|
175
174
|
declare function AppSwitcherMark({ children, collapsed, }: {
|
|
176
175
|
children: ReactNode;
|
|
@@ -186,6 +185,10 @@ interface SuiteAppEntry {
|
|
|
186
185
|
description: string;
|
|
187
186
|
icon: string;
|
|
188
187
|
}
|
|
188
|
+
interface SuiteAppSelectOptions {
|
|
189
|
+
/** Open the handoff in a separate browser tab when requested by a caller. */
|
|
190
|
+
newTab?: boolean;
|
|
191
|
+
}
|
|
189
192
|
/**
|
|
190
193
|
* Suite app switcher dropdown. Apps inject their brand mark/title, the
|
|
191
194
|
* visible app list, the cross-app navigation handler, and the theme
|
|
@@ -194,7 +197,7 @@ interface SuiteAppEntry {
|
|
|
194
197
|
declare function AppSwitcher({ apps, currentApp, onSelect, mark, title, collapsed, dropdownMenu: DropdownMenu, dropdownItem: DropdownItem, triggerId, triggerDataTest, }: {
|
|
195
198
|
apps: SuiteAppEntry[];
|
|
196
199
|
currentApp: string;
|
|
197
|
-
onSelect: (app: SuiteAppEntry) => void;
|
|
200
|
+
onSelect: (app: SuiteAppEntry, options?: SuiteAppSelectOptions) => void;
|
|
198
201
|
mark: ReactNode;
|
|
199
202
|
title: ReactNode;
|
|
200
203
|
collapsed?: boolean;
|
|
@@ -984,4 +987,4 @@ interface SuiteAppLayoutProps {
|
|
|
984
987
|
*/
|
|
985
988
|
declare function SuiteAppLayout({ sidebar, children, mobileHeader, bottomNav, sidebarWidth, collapsed, lockMainScroll, onStartResize, onResizeBy, onResetWidth, className, }: SuiteAppLayoutProps): react.JSX.Element;
|
|
986
989
|
|
|
987
|
-
export { APP_ACCENTS, APP_GLYPHS, AnimatedMoon, AnimatedSun, AppSwitcher, AppSwitcherChevron, AppSwitcherMark, CommandPaletteHost, DeferredChrome, SIDEBAR_COLLAPSE_THRESHOLD, SIDEBAR_DEFAULT_WIDTH, SIDEBAR_MAX_WIDTH, SIDEBAR_MIN_EXPANDED_WIDTH, SIDEBAR_RAIL_WIDTH, SUITE_APPS, SUITE_APP_MAP, SUITE_ASK_AI_OPEN_KEY, SUITE_ASK_AI_OPEN_QUERY, SUITE_GLYPHS, SUITE_ICON_NAMES, SUITE_MUTATED_EVENT, SUITE_OPEN_ASK_AI_EVENT, SUITE_SIDEBAR_WIDTH_COOKIE, SUITE_SIDEBAR_WIDTH_KEY, SUITE_SPRINGS, SourceAppGlyph, type SuiteAccentSlug, type SuiteAiAction, type SuiteAiActionStatus, type SuiteAiChatMessage, SuiteAiPanel, type SuiteAiPreset, type SuiteAppAccent, type SuiteAppDefinition, type SuiteAppEntry, SuiteAppIcon, type SuiteAppIconProps, SuiteAppLayout, type SuiteAppLayoutProps, type SuiteAppSlug, SuiteAppStrip, type SuiteAskAiOpenDetail, SuiteBottomNav, type SuiteBottomNavItem, SuiteBreadcrumbs, type SuiteBreadcrumbsProps, type SuiteCommandItem, type SuiteCommandPaletteComponent, type SuiteDropdownItemComponent, type SuiteDropdownMenuComponent, type SuiteDropdownMenuProps, SuiteEmptyState, type SuiteGlyph, type SuiteGlyphElement, type SuiteHrefTarget, SuiteIcon, type SuiteIconName, type SuiteIconProps, SuiteMobileDrawer, SuiteMobileHeader, SuiteMotionProvider, type SuiteNotification, SuiteNotificationBell, type SuiteNotificationsResponse, SuitePage, SuitePageHeader, type SuitePageWidth, type SuiteResolvedTheme, SuiteSectionHeader, SuiteSettingsLayout, type SuiteSettingsLayoutProps, SuiteSettingsMobileNav, type SuiteSettingsNavItem, SuiteSidebar, type SuiteSidebarNavItem, SuiteSkeleton, SuiteSkeletonCard, SuiteSkeletonList, type SuiteSpinnerComponent, type SuiteSpringName, SuiteTabList, type SuiteTheme, type SuiteThemeConfig, type SuiteThemeContextValue, SuiteThemeProvider, SuiteToolbar, SuiteUserMenu, type SuiteUserMenuProps, type SuiteWorkspaceEntry, type SuiteWorkspaceGroup, SuiteWorkspaceSwitcher, TURTLETIME_TIMER_MUTATED_EVENT, TodayAskAiCard, TodayCalibrating, TodayEmptyAction, TodayEmptyState, TodayHero, TodayLayout, TodayPageFrame, TodayPanel, type TodayPrimaryAccent, TodayPrimaryAction, TodaySection, TodayTimeIcon, TodayTopBar, appSwitcherMarkClass, appSwitcherMenuItemClass, appSwitcherTriggerClass, classifySuiteHref, cn, consumePendingAskAiPrompt, consumeSuiteAskAiOpenFromLocation, getTodayGreeting, openSuiteAskAi, pathWithSuiteAskAiOpen, persistSidebarWidth, persistSuiteAskAiOpen, readSidebarWidth, readSuiteAskAiOpen, resetSuiteSidebarWidth, resolveSuiteNotificationHref, sidebarColumnWidth, sourceToSuiteApp, suiteAppBaseUrl, suiteAppLabel, useIdleMount, useSidebarWidth, useSuiteTheme };
|
|
990
|
+
export { APP_ACCENTS, APP_GLYPHS, AnimatedMoon, AnimatedSun, AppSwitcher, AppSwitcherChevron, AppSwitcherMark, CommandPaletteHost, DeferredChrome, SIDEBAR_COLLAPSE_THRESHOLD, SIDEBAR_DEFAULT_WIDTH, SIDEBAR_MAX_WIDTH, SIDEBAR_MIN_EXPANDED_WIDTH, SIDEBAR_RAIL_WIDTH, SUITE_APPS, SUITE_APP_MAP, SUITE_ASK_AI_OPEN_KEY, SUITE_ASK_AI_OPEN_QUERY, SUITE_GLYPHS, SUITE_ICON_NAMES, SUITE_MUTATED_EVENT, SUITE_OPEN_ASK_AI_EVENT, SUITE_SIDEBAR_WIDTH_COOKIE, SUITE_SIDEBAR_WIDTH_KEY, SUITE_SPRINGS, SourceAppGlyph, type SuiteAccentSlug, type SuiteAiAction, type SuiteAiActionStatus, type SuiteAiChatMessage, SuiteAiPanel, type SuiteAiPreset, type SuiteAppAccent, type SuiteAppDefinition, type SuiteAppEntry, SuiteAppIcon, type SuiteAppIconProps, SuiteAppLayout, type SuiteAppLayoutProps, type SuiteAppSelectOptions, type SuiteAppSlug, SuiteAppStrip, type SuiteAskAiOpenDetail, SuiteBottomNav, type SuiteBottomNavItem, SuiteBreadcrumbs, type SuiteBreadcrumbsProps, type SuiteCommandItem, type SuiteCommandPaletteComponent, type SuiteDropdownItemComponent, type SuiteDropdownMenuComponent, type SuiteDropdownMenuProps, SuiteEmptyState, type SuiteGlyph, type SuiteGlyphElement, type SuiteHrefTarget, SuiteIcon, type SuiteIconName, type SuiteIconProps, SuiteMobileDrawer, SuiteMobileHeader, SuiteMotionProvider, type SuiteNotification, SuiteNotificationBell, type SuiteNotificationsResponse, SuitePage, SuitePageHeader, type SuitePageWidth, type SuiteResolvedTheme, SuiteSectionHeader, SuiteSettingsLayout, type SuiteSettingsLayoutProps, SuiteSettingsMobileNav, type SuiteSettingsNavItem, SuiteSidebar, type SuiteSidebarNavItem, SuiteSkeleton, SuiteSkeletonCard, SuiteSkeletonList, type SuiteSpinnerComponent, type SuiteSpringName, SuiteTabList, type SuiteTheme, type SuiteThemeConfig, type SuiteThemeContextValue, SuiteThemeProvider, SuiteToolbar, SuiteUserMenu, type SuiteUserMenuProps, type SuiteWorkspaceEntry, type SuiteWorkspaceGroup, SuiteWorkspaceSwitcher, TURTLETIME_TIMER_MUTATED_EVENT, TodayAskAiCard, TodayCalibrating, TodayEmptyAction, TodayEmptyState, TodayHero, TodayLayout, TodayPageFrame, TodayPanel, type TodayPrimaryAccent, TodayPrimaryAction, TodaySection, TodayTimeIcon, TodayTopBar, appSwitcherMarkClass, appSwitcherMenuItemClass, appSwitcherTriggerClass, classifySuiteHref, cn, consumePendingAskAiPrompt, consumeSuiteAskAiOpenFromLocation, getTodayGreeting, openSuiteAskAi, pathWithSuiteAskAiOpen, persistSidebarWidth, persistSuiteAskAiOpen, readSidebarWidth, readSuiteAskAiOpen, resetSuiteSidebarWidth, resolveSuiteNotificationHref, sidebarColumnWidth, sourceToSuiteApp, suiteAppBaseUrl, suiteAppLabel, useIdleMount, useSidebarWidth, useSuiteTheme };
|
package/dist/suite.js
CHANGED
|
@@ -1370,43 +1370,53 @@ function AppSwitcher({
|
|
|
1370
1370
|
),
|
|
1371
1371
|
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { id: "app-switcher-menu", className: "p-1", "data-test": "app-switcher-menu", children: [
|
|
1372
1372
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "px-3 py-2 text-[10px] font-semibold uppercase tracking-[0.14em] text-ph-mutedtext", children: "Switch application" }),
|
|
1373
|
-
apps.map((app) => /* @__PURE__ */ (0, import_jsx_runtime6.
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1373
|
+
apps.filter((app) => app.slug !== currentApp).map((app) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "relative", children: [
|
|
1374
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1375
|
+
DropdownItem2,
|
|
1376
|
+
{
|
|
1377
|
+
id: `switch-app-${app.slug}`,
|
|
1378
|
+
"data-test": `switch-app-${app.slug}`,
|
|
1379
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1380
|
+
onClick: () => {
|
|
1381
|
+
setOpen(false);
|
|
1382
|
+
onSelect(app);
|
|
1383
|
+
},
|
|
1384
|
+
className: cn(appSwitcherMenuItemClass(), "pr-11"),
|
|
1385
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "flex min-w-0 items-center gap-3", children: [
|
|
1386
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1387
|
+
import_image.default,
|
|
1388
|
+
{
|
|
1389
|
+
src: app.icon,
|
|
1390
|
+
alt: "",
|
|
1391
|
+
width: 28,
|
|
1392
|
+
height: 28,
|
|
1393
|
+
className: "h-7 w-7 shrink-0 rounded-md object-contain"
|
|
1394
|
+
}
|
|
1395
|
+
),
|
|
1396
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "min-w-0 flex-1", children: [
|
|
1397
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "block truncate text-sm font-medium text-ph-ink", children: app.name }),
|
|
1398
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "block truncate text-xs text-ph-mutedtext", children: app.description })
|
|
1399
|
+
] })
|
|
1400
|
+
] })
|
|
1401
|
+
}
|
|
1402
|
+
),
|
|
1403
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1404
|
+
"button",
|
|
1405
|
+
{
|
|
1406
|
+
type: "button",
|
|
1407
|
+
id: `switch-app-${app.slug}-new-tab`,
|
|
1408
|
+
"data-test": `switch-app-${app.slug}-new-tab`,
|
|
1409
|
+
"aria-label": `Open ${app.name} in a new tab`,
|
|
1410
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1411
|
+
onClick: () => {
|
|
1412
|
+
setOpen(false);
|
|
1413
|
+
onSelect(app, { newTab: true });
|
|
1414
|
+
},
|
|
1415
|
+
className: "absolute right-1.5 top-1/2 z-[1] flex h-8 w-8 -translate-y-1/2 items-center justify-center rounded-md text-ph-mutedtext transition-colors hover:bg-ph-muted hover:text-ph-ink focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ph-brand/35",
|
|
1416
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_iconoir_react2.OpenNewWindow, { className: "h-4 w-4", "aria-hidden": "true" })
|
|
1417
|
+
}
|
|
1418
|
+
)
|
|
1419
|
+
] }, app.slug))
|
|
1410
1420
|
] })
|
|
1411
1421
|
}
|
|
1412
1422
|
);
|
|
@@ -1636,16 +1646,16 @@ function SuiteNotificationBell({
|
|
|
1636
1646
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1637
1647
|
DropdownMenu2,
|
|
1638
1648
|
{
|
|
1639
|
-
"aria-label": "Notifications",
|
|
1649
|
+
"aria-label": unread > 0 ? `Notifications, ${unread} unread` : "Notifications",
|
|
1640
1650
|
triggerId: triggerId != null ? triggerId : `${dataTest}-trigger`,
|
|
1641
1651
|
triggerDataTest: triggerDataTest != null ? triggerDataTest : `${dataTest}-trigger`,
|
|
1642
1652
|
align: "end",
|
|
1643
1653
|
panelClassName: "w-80 overflow-hidden p-0",
|
|
1644
1654
|
open,
|
|
1645
1655
|
onOpenChange,
|
|
1646
|
-
trigger: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "inline-flex h-11 w-11 items-center justify-center rounded-full text-ph-mutedtext transition hover:bg-ph-muted hover:text-ph-ink", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "relative inline-flex", children: [
|
|
1656
|
+
trigger: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "relative inline-flex h-11 w-11 items-center justify-center overflow-visible rounded-full text-ph-mutedtext transition hover:bg-ph-muted hover:text-ph-ink", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "relative inline-flex h-5 w-5", children: [
|
|
1647
1657
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_iconoir_react3.Bell, { className: "h-5 w-5", strokeWidth: 1.75, "aria-hidden": true }),
|
|
1648
|
-
unread > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "absolute right-
|
|
1658
|
+
unread > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "pointer-events-none absolute -right-2 -top-2 z-[1] flex h-4 min-w-4 items-center justify-center rounded-full bg-ph-brand px-1 text-[9px] font-semibold leading-none text-white ring-2 ring-ph-surface", children: unread > 9 ? "9+" : unread }) : null
|
|
1649
1659
|
] }) }),
|
|
1650
1660
|
children: [
|
|
1651
1661
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between border-b border-ph-border px-3 py-2", children: [
|
package/dist/suite.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
DropdownItem,
|
|
3
3
|
DropdownMenu,
|
|
4
4
|
Tooltip
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-KDR6HI6F.mjs";
|
|
6
6
|
import {
|
|
7
7
|
SuiteThemeProvider,
|
|
8
8
|
useSuiteTheme
|
|
@@ -896,7 +896,10 @@ function TodayEmptyState({
|
|
|
896
896
|
// src/suite/components/app-switcher.tsx
|
|
897
897
|
import Image from "next/image";
|
|
898
898
|
import { useState } from "react";
|
|
899
|
-
import {
|
|
899
|
+
import {
|
|
900
|
+
NavArrowDown as ChevronDown,
|
|
901
|
+
OpenNewWindow
|
|
902
|
+
} from "iconoir-react";
|
|
900
903
|
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
901
904
|
function appSwitcherTriggerClass(open, collapsed = false) {
|
|
902
905
|
return cn(
|
|
@@ -1013,43 +1016,53 @@ function AppSwitcher({
|
|
|
1013
1016
|
),
|
|
1014
1017
|
children: /* @__PURE__ */ jsxs6("div", { id: "app-switcher-menu", className: "p-1", "data-test": "app-switcher-menu", children: [
|
|
1015
1018
|
/* @__PURE__ */ jsx6("p", { className: "px-3 py-2 text-[10px] font-semibold uppercase tracking-[0.14em] text-ph-mutedtext", children: "Switch application" }),
|
|
1016
|
-
apps.map((app) => /* @__PURE__ */
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
/* @__PURE__ */
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
/* @__PURE__ */
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1019
|
+
apps.filter((app) => app.slug !== currentApp).map((app) => /* @__PURE__ */ jsxs6("div", { className: "relative", children: [
|
|
1020
|
+
/* @__PURE__ */ jsx6(
|
|
1021
|
+
DropdownItem2,
|
|
1022
|
+
{
|
|
1023
|
+
id: `switch-app-${app.slug}`,
|
|
1024
|
+
"data-test": `switch-app-${app.slug}`,
|
|
1025
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1026
|
+
onClick: () => {
|
|
1027
|
+
setOpen(false);
|
|
1028
|
+
onSelect(app);
|
|
1029
|
+
},
|
|
1030
|
+
className: cn(appSwitcherMenuItemClass(), "pr-11"),
|
|
1031
|
+
children: /* @__PURE__ */ jsxs6("span", { className: "flex min-w-0 items-center gap-3", children: [
|
|
1032
|
+
/* @__PURE__ */ jsx6(
|
|
1033
|
+
Image,
|
|
1034
|
+
{
|
|
1035
|
+
src: app.icon,
|
|
1036
|
+
alt: "",
|
|
1037
|
+
width: 28,
|
|
1038
|
+
height: 28,
|
|
1039
|
+
className: "h-7 w-7 shrink-0 rounded-md object-contain"
|
|
1040
|
+
}
|
|
1041
|
+
),
|
|
1042
|
+
/* @__PURE__ */ jsxs6("span", { className: "min-w-0 flex-1", children: [
|
|
1043
|
+
/* @__PURE__ */ jsx6("span", { className: "block truncate text-sm font-medium text-ph-ink", children: app.name }),
|
|
1044
|
+
/* @__PURE__ */ jsx6("span", { className: "block truncate text-xs text-ph-mutedtext", children: app.description })
|
|
1045
|
+
] })
|
|
1046
|
+
] })
|
|
1047
|
+
}
|
|
1048
|
+
),
|
|
1049
|
+
/* @__PURE__ */ jsx6(
|
|
1050
|
+
"button",
|
|
1051
|
+
{
|
|
1052
|
+
type: "button",
|
|
1053
|
+
id: `switch-app-${app.slug}-new-tab`,
|
|
1054
|
+
"data-test": `switch-app-${app.slug}-new-tab`,
|
|
1055
|
+
"aria-label": `Open ${app.name} in a new tab`,
|
|
1056
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1057
|
+
onClick: () => {
|
|
1058
|
+
setOpen(false);
|
|
1059
|
+
onSelect(app, { newTab: true });
|
|
1060
|
+
},
|
|
1061
|
+
className: "absolute right-1.5 top-1/2 z-[1] flex h-8 w-8 -translate-y-1/2 items-center justify-center rounded-md text-ph-mutedtext transition-colors hover:bg-ph-muted hover:text-ph-ink focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ph-brand/35",
|
|
1062
|
+
children: /* @__PURE__ */ jsx6(OpenNewWindow, { className: "h-4 w-4", "aria-hidden": "true" })
|
|
1063
|
+
}
|
|
1064
|
+
)
|
|
1065
|
+
] }, app.slug))
|
|
1053
1066
|
] })
|
|
1054
1067
|
}
|
|
1055
1068
|
);
|
|
@@ -1279,16 +1292,16 @@ function SuiteNotificationBell({
|
|
|
1279
1292
|
return /* @__PURE__ */ jsxs7(
|
|
1280
1293
|
DropdownMenu2,
|
|
1281
1294
|
{
|
|
1282
|
-
"aria-label": "Notifications",
|
|
1295
|
+
"aria-label": unread > 0 ? `Notifications, ${unread} unread` : "Notifications",
|
|
1283
1296
|
triggerId: triggerId != null ? triggerId : `${dataTest}-trigger`,
|
|
1284
1297
|
triggerDataTest: triggerDataTest != null ? triggerDataTest : `${dataTest}-trigger`,
|
|
1285
1298
|
align: "end",
|
|
1286
1299
|
panelClassName: "w-80 overflow-hidden p-0",
|
|
1287
1300
|
open,
|
|
1288
1301
|
onOpenChange,
|
|
1289
|
-
trigger: /* @__PURE__ */ jsx7("span", { className: "inline-flex h-11 w-11 items-center justify-center rounded-full text-ph-mutedtext transition hover:bg-ph-muted hover:text-ph-ink", children: /* @__PURE__ */ jsxs7("span", { className: "relative inline-flex", children: [
|
|
1302
|
+
trigger: /* @__PURE__ */ jsx7("span", { className: "relative inline-flex h-11 w-11 items-center justify-center overflow-visible rounded-full text-ph-mutedtext transition hover:bg-ph-muted hover:text-ph-ink", children: /* @__PURE__ */ jsxs7("span", { className: "relative inline-flex h-5 w-5", children: [
|
|
1290
1303
|
/* @__PURE__ */ jsx7(Bell, { className: "h-5 w-5", strokeWidth: 1.75, "aria-hidden": true }),
|
|
1291
|
-
unread > 0 ? /* @__PURE__ */ jsx7("span", { className: "absolute right-
|
|
1304
|
+
unread > 0 ? /* @__PURE__ */ jsx7("span", { className: "pointer-events-none absolute -right-2 -top-2 z-[1] flex h-4 min-w-4 items-center justify-center rounded-full bg-ph-brand px-1 text-[9px] font-semibold leading-none text-white ring-2 ring-ph-surface", children: unread > 9 ? "9+" : unread }) : null
|
|
1292
1305
|
] }) }),
|
|
1293
1306
|
children: [
|
|
1294
1307
|
/* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between border-b border-ph-border px-3 py-2", children: [
|
|
@@ -2727,7 +2740,7 @@ function SuiteAiPanel({
|
|
|
2727
2740
|
|
|
2728
2741
|
// src/suite/components/workspace-switcher.tsx
|
|
2729
2742
|
import { useState as useState8 } from "react";
|
|
2730
|
-
import { Building, Check
|
|
2743
|
+
import { Building, Check, NavArrowDown } from "iconoir-react";
|
|
2731
2744
|
import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2732
2745
|
function SuiteWorkspaceSwitcher({
|
|
2733
2746
|
workspaces = [],
|
|
@@ -2814,7 +2827,7 @@ function SuiteWorkspaceSwitcher({
|
|
|
2814
2827
|
workspace.description ? /* @__PURE__ */ jsx18("span", { className: "block truncate text-xs text-ph-mutedtext", children: workspace.description }) : null
|
|
2815
2828
|
] }),
|
|
2816
2829
|
workspace.id === currentId ? /* @__PURE__ */ jsx18(
|
|
2817
|
-
|
|
2830
|
+
Check,
|
|
2818
2831
|
{
|
|
2819
2832
|
className: "h-4 w-4 shrink-0 text-ph-brand",
|
|
2820
2833
|
"aria-hidden": "true"
|
package/dist/ui.js
CHANGED
|
@@ -399,7 +399,9 @@ var IconPlayCircle = createIconBase({
|
|
|
399
399
|
)
|
|
400
400
|
});
|
|
401
401
|
var IconGridView = createIconBase({
|
|
402
|
-
paths: fill(
|
|
402
|
+
paths: fill(
|
|
403
|
+
"M3,3v8h8V3H3z M9,9H5V5h4V9z M3,13v8h8v-8H3z M9,19H5v-4h4V19z M13,3v8h8V3H13z M19,9h-4V5h4V9z M13,13v8h8v-8H13z M19,19h-4v-4h4V19z"
|
|
404
|
+
)
|
|
403
405
|
});
|
|
404
406
|
var IconListView = createIconBase({
|
|
405
407
|
paths: fill(
|
package/dist/ui.mjs
CHANGED
|
@@ -65,7 +65,7 @@ import {
|
|
|
65
65
|
ThemeDomSync,
|
|
66
66
|
ThemeSwitcher,
|
|
67
67
|
Toggle
|
|
68
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-VXISSXTB.mjs";
|
|
69
69
|
import {
|
|
70
70
|
ButtonChrome,
|
|
71
71
|
DropdownButton,
|
|
@@ -77,7 +77,7 @@ import {
|
|
|
77
77
|
IconByName,
|
|
78
78
|
Tooltip,
|
|
79
79
|
TooltipProvider
|
|
80
|
-
} from "./chunk-
|
|
80
|
+
} from "./chunk-KDR6HI6F.mjs";
|
|
81
81
|
import "./chunk-FWCSY2DS.mjs";
|
|
82
82
|
export {
|
|
83
83
|
Accordion,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xenide-io/the-old-ui-theme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Props-driven React components and theme tokens inspired by classic interface design \u2014 optimized for Next.js apps and internal tools.",
|
|
5
5
|
"author": "Xenide",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,7 +18,7 @@ const CACHE = resolve("scripts/.posthog-icons.tsx");
|
|
|
18
18
|
function extractPhBlock(source, name) {
|
|
19
19
|
const start = source.indexOf(`export function ${name}(`);
|
|
20
20
|
if (start < 0) return null;
|
|
21
|
-
const next = source.indexOf("\nexport function
|
|
21
|
+
const next = source.indexOf("\nexport function ", start + 10);
|
|
22
22
|
return source.slice(start, next > 0 ? next : start + 4000);
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -34,9 +34,8 @@ function extractOurBlock(source, name) {
|
|
|
34
34
|
function normPaths(block) {
|
|
35
35
|
if (!block) return "";
|
|
36
36
|
const fills = [...block.matchAll(/fill\(\s*\n?\s*"([^"]+)"/g)].map((m) => m[1]);
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
return [...fills, ...ds, ...attrs].join("|").replace(/\s/g, "");
|
|
37
|
+
const paths = [...block.matchAll(/\sd="([^"]+)"/g)].map((m) => m[1]);
|
|
38
|
+
return [...fills, ...paths].join("|").replace(/\s/g, "");
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
async function main() {
|
|
@@ -98,7 +98,9 @@ export const IconPlayCircle = createIconBase({
|
|
|
98
98
|
});
|
|
99
99
|
|
|
100
100
|
export const IconGridView = createIconBase({
|
|
101
|
-
paths: fill(
|
|
101
|
+
paths: fill(
|
|
102
|
+
"M3,3v8h8V3H3z M9,9H5V5h4V9z M3,13v8h8v-8H3z M9,19H5v-4h4V19z M13,3v8h8V3H13z M19,9h-4V5h4V9z M13,13v8h8v-8H13z M19,19h-4v-4h4V19z",
|
|
103
|
+
),
|
|
102
104
|
});
|
|
103
105
|
|
|
104
106
|
export const IconListView = createIconBase({
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { ComponentPropsWithoutRef, ReactNode } from "react";
|
|
2
|
+
import { cleanup, render, screen } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
|
|
6
|
+
vi.mock("next/image", () => ({
|
|
7
|
+
default: (props: ComponentPropsWithoutRef<"img">) => <img {...props} />,
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
import { AppSwitcher, type SuiteAppEntry } from "./app-switcher";
|
|
11
|
+
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
cleanup();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const APPS: SuiteAppEntry[] = [
|
|
17
|
+
{ slug: "tides", name: "Tides", description: "Projects", icon: "/tides.svg" },
|
|
18
|
+
{
|
|
19
|
+
slug: "turtletime",
|
|
20
|
+
name: "TurtleTime",
|
|
21
|
+
description: "Time",
|
|
22
|
+
icon: "/time.svg",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
slug: "kraken",
|
|
26
|
+
name: "Kraken",
|
|
27
|
+
description: "Documents",
|
|
28
|
+
icon: "/kraken.svg",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
slug: "nakama",
|
|
32
|
+
name: "Nakama",
|
|
33
|
+
description: "AI teammates",
|
|
34
|
+
icon: "/nakama.svg",
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
function TestDropdownMenu({
|
|
39
|
+
trigger,
|
|
40
|
+
children,
|
|
41
|
+
"aria-label": ariaLabel,
|
|
42
|
+
}: {
|
|
43
|
+
trigger: ReactNode;
|
|
44
|
+
children: ReactNode;
|
|
45
|
+
"aria-label": string;
|
|
46
|
+
}) {
|
|
47
|
+
return (
|
|
48
|
+
<div aria-label={ariaLabel}>
|
|
49
|
+
{trigger}
|
|
50
|
+
{children}
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function TestDropdownItem({
|
|
56
|
+
children,
|
|
57
|
+
...props
|
|
58
|
+
}: ComponentPropsWithoutRef<"button">) {
|
|
59
|
+
return <button {...props}>{children}</button>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
describe("AppSwitcher", () => {
|
|
63
|
+
it("keeps the current app out of the simple application list", async () => {
|
|
64
|
+
const user = userEvent.setup();
|
|
65
|
+
const onSelect = vi.fn();
|
|
66
|
+
|
|
67
|
+
render(
|
|
68
|
+
<AppSwitcher
|
|
69
|
+
apps={APPS}
|
|
70
|
+
currentApp="tides"
|
|
71
|
+
onSelect={onSelect}
|
|
72
|
+
mark={<span />}
|
|
73
|
+
title={<span>Tides</span>}
|
|
74
|
+
dropdownMenu={TestDropdownMenu}
|
|
75
|
+
dropdownItem={TestDropdownItem}
|
|
76
|
+
/>,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const menu = document.querySelector("[data-test='app-switcher-menu']");
|
|
80
|
+
expect(menu).not.toHaveClass("grid-cols-2");
|
|
81
|
+
expect(menu).toHaveTextContent("Switch application");
|
|
82
|
+
expect(
|
|
83
|
+
document.querySelector("[data-test='switch-app-tides']"),
|
|
84
|
+
).not.toBeInTheDocument();
|
|
85
|
+
const newTabButton = screen.getByRole("button", {
|
|
86
|
+
name: "Open Kraken in a new tab",
|
|
87
|
+
});
|
|
88
|
+
expect(newTabButton).toHaveClass("absolute", "h-8", "w-8");
|
|
89
|
+
expect(
|
|
90
|
+
screen.queryByRole("button", { name: "Open Tides in a new tab" }),
|
|
91
|
+
).not.toBeInTheDocument();
|
|
92
|
+
|
|
93
|
+
await user.click(newTabButton);
|
|
94
|
+
expect(onSelect).toHaveBeenCalledWith(APPS[2], { newTab: true });
|
|
95
|
+
});
|
|
96
|
+
});
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import Image from "next/image";
|
|
4
4
|
import { useState, type ReactNode } from "react";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
NavArrowDown as ChevronDown,
|
|
7
|
+
OpenNewWindow,
|
|
8
|
+
} from "iconoir-react";
|
|
6
9
|
|
|
7
10
|
import { cn } from "../lib/cn";
|
|
8
11
|
import type {
|
|
@@ -24,7 +27,6 @@ export function appSwitcherMarkClass() {
|
|
|
24
27
|
return "h-8 w-8 shrink-0 rounded-lg shadow-[0_1px_2px_rgba(0,0,0,0.1),0_4px_12px_rgba(0,0,0,0.08)] ring-1 ring-black/[0.06]";
|
|
25
28
|
}
|
|
26
29
|
|
|
27
|
-
/** No sticky Radix highlight while waiting for cross-app redirect. */
|
|
28
30
|
export function appSwitcherMenuItemClass() {
|
|
29
31
|
return "rounded-lg px-2 py-2 data-[highlighted]:bg-transparent hover:bg-ph-muted/80";
|
|
30
32
|
}
|
|
@@ -101,6 +103,11 @@ export interface SuiteAppEntry {
|
|
|
101
103
|
icon: string;
|
|
102
104
|
}
|
|
103
105
|
|
|
106
|
+
export interface SuiteAppSelectOptions {
|
|
107
|
+
/** Open the handoff in a separate browser tab when requested by a caller. */
|
|
108
|
+
newTab?: boolean;
|
|
109
|
+
}
|
|
110
|
+
|
|
104
111
|
/**
|
|
105
112
|
* Suite app switcher dropdown. Apps inject their brand mark/title, the
|
|
106
113
|
* visible app list, the cross-app navigation handler, and the theme
|
|
@@ -120,7 +127,7 @@ export function AppSwitcher({
|
|
|
120
127
|
}: {
|
|
121
128
|
apps: SuiteAppEntry[];
|
|
122
129
|
currentApp: string;
|
|
123
|
-
onSelect: (app: SuiteAppEntry) => void;
|
|
130
|
+
onSelect: (app: SuiteAppEntry, options?: SuiteAppSelectOptions) => void;
|
|
124
131
|
mark: ReactNode;
|
|
125
132
|
title: ReactNode;
|
|
126
133
|
collapsed?: boolean;
|
|
@@ -160,43 +167,54 @@ export function AppSwitcher({
|
|
|
160
167
|
<p className="px-3 py-2 text-[10px] font-semibold uppercase tracking-[0.14em] text-ph-mutedtext">
|
|
161
168
|
Switch application
|
|
162
169
|
</p>
|
|
163
|
-
{apps
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
170
|
+
{apps
|
|
171
|
+
.filter((app) => app.slug !== currentApp)
|
|
172
|
+
.map((app) => (
|
|
173
|
+
<div key={app.slug} className="relative">
|
|
174
|
+
<DropdownItem
|
|
175
|
+
id={`switch-app-${app.slug}`}
|
|
176
|
+
data-test={`switch-app-${app.slug}`}
|
|
177
|
+
onMouseDown={(e) => e.preventDefault()}
|
|
178
|
+
onClick={() => {
|
|
179
|
+
setOpen(false);
|
|
180
|
+
onSelect(app);
|
|
181
|
+
}}
|
|
182
|
+
className={cn(appSwitcherMenuItemClass(), "pr-11")}
|
|
183
|
+
>
|
|
184
|
+
<span className="flex min-w-0 items-center gap-3">
|
|
185
|
+
<Image
|
|
186
|
+
src={app.icon}
|
|
187
|
+
alt=""
|
|
188
|
+
width={28}
|
|
189
|
+
height={28}
|
|
190
|
+
className="h-7 w-7 shrink-0 rounded-md object-contain"
|
|
191
|
+
/>
|
|
192
|
+
<span className="min-w-0 flex-1">
|
|
193
|
+
<span className="block truncate text-sm font-medium text-ph-ink">
|
|
194
|
+
{app.name}
|
|
195
|
+
</span>
|
|
196
|
+
<span className="block truncate text-xs text-ph-mutedtext">
|
|
197
|
+
{app.description}
|
|
198
|
+
</span>
|
|
199
|
+
</span>
|
|
189
200
|
</span>
|
|
190
|
-
</
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
201
|
+
</DropdownItem>
|
|
202
|
+
<button
|
|
203
|
+
type="button"
|
|
204
|
+
id={`switch-app-${app.slug}-new-tab`}
|
|
205
|
+
data-test={`switch-app-${app.slug}-new-tab`}
|
|
206
|
+
aria-label={`Open ${app.name} in a new tab`}
|
|
207
|
+
onMouseDown={(e) => e.preventDefault()}
|
|
208
|
+
onClick={() => {
|
|
209
|
+
setOpen(false);
|
|
210
|
+
onSelect(app, { newTab: true });
|
|
211
|
+
}}
|
|
212
|
+
className="absolute right-1.5 top-1/2 z-[1] flex h-8 w-8 -translate-y-1/2 items-center justify-center rounded-md text-ph-mutedtext transition-colors hover:bg-ph-muted hover:text-ph-ink focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ph-brand/35"
|
|
213
|
+
>
|
|
214
|
+
<OpenNewWindow className="h-4 w-4" aria-hidden="true" />
|
|
215
|
+
</button>
|
|
216
|
+
</div>
|
|
217
|
+
))}
|
|
200
218
|
</div>
|
|
201
219
|
</DropdownMenu>
|
|
202
220
|
);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { cleanup, render, waitFor } from "@testing-library/react";
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
|
|
4
|
+
vi.mock("next/navigation", () => ({
|
|
5
|
+
useRouter: () => ({ push: vi.fn() }),
|
|
6
|
+
}));
|
|
7
|
+
|
|
8
|
+
import { SuiteNotificationBell } from "./suite-notification-bell";
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
cleanup();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe("SuiteNotificationBell", () => {
|
|
15
|
+
it("anchors the unread badge close to the bell glyph", async () => {
|
|
16
|
+
render(
|
|
17
|
+
<SuiteNotificationBell
|
|
18
|
+
fetchNotifications={async () => ({
|
|
19
|
+
notifications: [],
|
|
20
|
+
unread_count: 12,
|
|
21
|
+
})}
|
|
22
|
+
markRead={async () => undefined}
|
|
23
|
+
markAllRead={async () => undefined}
|
|
24
|
+
dropdownMenu={({ trigger, "aria-label": label, children }) => (
|
|
25
|
+
<div aria-label={label}>
|
|
26
|
+
{trigger}
|
|
27
|
+
{children}
|
|
28
|
+
</div>
|
|
29
|
+
)}
|
|
30
|
+
cacheKey={null}
|
|
31
|
+
/>,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const badge = await waitFor(() => {
|
|
35
|
+
const el = document.querySelector("span.bg-ph-brand");
|
|
36
|
+
if (!el) throw new Error("missing badge");
|
|
37
|
+
return el;
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
expect(badge.textContent).toBe("9+");
|
|
41
|
+
expect(badge.className).toContain("-right-2");
|
|
42
|
+
expect(badge.className).toContain("-top-2");
|
|
43
|
+
expect(badge.parentElement?.className).toContain("relative");
|
|
44
|
+
expect(badge.parentElement?.className).toContain("h-5");
|
|
45
|
+
expect(badge.parentElement?.className).toContain("w-5");
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -170,7 +170,9 @@ export function SuiteNotificationBell({
|
|
|
170
170
|
|
|
171
171
|
return (
|
|
172
172
|
<DropdownMenu
|
|
173
|
-
aria-label=
|
|
173
|
+
aria-label={
|
|
174
|
+
unread > 0 ? `Notifications, ${unread} unread` : "Notifications"
|
|
175
|
+
}
|
|
174
176
|
triggerId={triggerId ?? `${dataTest}-trigger`}
|
|
175
177
|
triggerDataTest={triggerDataTest ?? `${dataTest}-trigger`}
|
|
176
178
|
align="end"
|
|
@@ -178,11 +180,11 @@ export function SuiteNotificationBell({
|
|
|
178
180
|
open={open}
|
|
179
181
|
onOpenChange={onOpenChange}
|
|
180
182
|
trigger={
|
|
181
|
-
<span className="inline-flex h-11 w-11 items-center justify-center rounded-full text-ph-mutedtext transition hover:bg-ph-muted hover:text-ph-ink">
|
|
182
|
-
<span className="relative inline-flex">
|
|
183
|
+
<span className="relative inline-flex h-11 w-11 items-center justify-center overflow-visible rounded-full text-ph-mutedtext transition hover:bg-ph-muted hover:text-ph-ink">
|
|
184
|
+
<span className="relative inline-flex h-5 w-5">
|
|
183
185
|
<Bell className="h-5 w-5" strokeWidth={1.75} aria-hidden />
|
|
184
186
|
{unread > 0 ? (
|
|
185
|
-
<span className="absolute right-
|
|
187
|
+
<span className="pointer-events-none absolute -right-2 -top-2 z-[1] flex h-4 min-w-4 items-center justify-center rounded-full bg-ph-brand px-1 text-[9px] font-semibold leading-none text-white ring-2 ring-ph-surface">
|
|
186
188
|
{unread > 9 ? "9+" : unread}
|
|
187
189
|
</span>
|
|
188
190
|
) : null}
|