Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/clickstack-hexagon-mark.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hyperdx/app': patch
---

fix(app): use the ClickStack hexagon and bolt mark in the logo, the collapsed nav, and the favicon.
5 changes: 5 additions & 0 deletions .changeset/compact-nav-overlay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hyperdx/app': patch
---

fix(app): open the full nav over the page on compact viewports. On narrow screens the sidebar toggle now opens the full nav as an overlay instead of doing nothing.
Binary file modified packages/app/public/favicons/clickstack/apple-touch-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/public/favicons/clickstack/favicon-16x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/public/favicons/clickstack/favicon-32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/public/favicons/clickstack/favicon.ico
Binary file not shown.
22 changes: 10 additions & 12 deletions packages/app/public/favicons/clickstack/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 17 additions & 42 deletions packages/app/src/brand/Logomark.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,25 @@
/**
* ClickStack Logomark
* A stylized database/stack icon with the brand yellow color
* ClickStack mark from the hexagon-and-bolt symbol.
* The bolt uses the page background so it stays two-tone: white on the dark
* surface, dark on the light one.
*/
export default function Logomark({ size = 16 }: { size?: number }) {
return (
<svg width={size} height={size} fill="none" viewBox="0 0 24 24">
<rect
width="2.25"
height="20.249"
x="1.875"
y="1.875"
<svg
width={size}
height={size}
viewBox="0 0 38.7979 44.7998"
fill="none"
aria-hidden
>
<path
fill="currentColor"
rx="0.237"
></rect>
<rect
width="2.25"
height="20.249"
x="6.375"
y="1.875"
fill="currentColor"
rx="0.237"
></rect>
<rect
width="2.25"
height="20.249"
x="10.875"
y="1.875"
fill="currentColor"
rx="0.237"
></rect>
<rect
width="2.25"
height="20.249"
x="15.374"
y="1.875"
fill="currentColor"
rx="0.237"
></rect>
<rect
width="2.25"
height="4.5"
x="19.875"
y="9.75"
fill="currentColor"
rx="0.237"
></rect>
d="M38.7979 11.2002V33.5996L19.3994 44.7998L0 33.5996V11.2002L19.3994 0L38.7979 11.2002Z"
/>
<path
fill="var(--color-bg-body)"
d="M24.2434 7.39081C24.5455 7.61204 24.6866 8.0663 24.5838 8.48686L21.8763 19.5618H26.8722C27.1721 19.5618 27.4433 19.7862 27.5622 20.1326C27.6812 20.4791 27.6251 20.8814 27.4196 21.1563L15.3924 37.2399C15.1527 37.5603 14.7692 37.6305 14.467 37.4093C14.1649 37.188 14.0237 36.7338 14.1266 36.3132L16.834 25.2383H11.8382C11.5383 25.2383 11.2671 25.0139 11.1481 24.6675C11.0291 24.321 11.0852 23.9187 11.2908 23.6438L23.318 7.56021C23.5576 7.23979 23.9411 7.16958 24.2434 7.39081Z"
/>
</svg>
);
}
50 changes: 13 additions & 37 deletions packages/app/src/brand/Wordmark.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,22 @@ export default function Wordmark() {
fill="none"
viewBox="0 0 121 25"
>
<rect
width="2.25"
height="20.249"
<svg
x="1.875"
y="1.875"
fill="currentColor"
rx="0.237"
></rect>
<rect
width="2.25"
height="20.249"
x="6.375"
y="1.875"
fill="currentColor"
rx="0.237"
></rect>
<rect
width="2.25"
width="17.536"
height="20.249"
x="10.875"
y="1.875"
fill="currentColor"
rx="0.237"
></rect>
<rect
width="2.25"
height="20.249"
x="15.374"
y="1.875"
fill="currentColor"
rx="0.237"
></rect>
<rect
width="2.25"
height="4.5"
x="19.875"
y="9.75"
fill="currentColor"
rx="0.237"
></rect>
viewBox="0 0 38.7979 44.7998"
>
<path
fill="currentColor"
d="M38.7979 11.2002V33.5996L19.3994 44.7998L0 33.5996V11.2002L19.3994 0L38.7979 11.2002Z"
/>
<path
fill="var(--color-bg-body)"
d="M24.2434 7.39081C24.5455 7.61204 24.6866 8.0663 24.5838 8.48686L21.8763 19.5618H26.8722C27.1721 19.5618 27.4433 19.7862 27.5622 20.1326C27.6812 20.4791 27.6251 20.8814 27.4196 21.1563L15.3924 37.2399C15.1527 37.5603 14.7692 37.6305 14.467 37.4093C14.1649 37.188 14.0237 36.7338 14.1266 36.3132L16.834 25.2383H11.8382C11.5383 25.2383 11.2671 25.0139 11.1481 24.6675C11.0291 24.321 11.0852 23.9187 11.2908 23.6438L23.318 7.56021C23.5576 7.23979 23.9411 7.16958 24.2434 7.39081Z"
/>
</svg>
<path
fill="currentColor"
d="M41.2 7.642q-.954 0-1.71.342-.756.324-1.296.972a4.7 4.7 0 0 0-.81 1.566q-.27.918-.27 2.07 0 1.512.45 2.628.45 1.098 1.35 1.692t2.268.594q.828 0 1.584-.144a16 16 0 0 0 1.566-.414v1.674q-.756.288-1.548.414-.792.144-1.836.144-1.962 0-3.276-.81-1.296-.81-1.944-2.304t-.648-3.492q0-1.458.396-2.664a6.1 6.1 0 0 1 1.188-2.106 5.1 5.1 0 0 1 1.908-1.35q1.152-.486 2.646-.486.972 0 1.908.216a7 7 0 0 1 1.692.576l-.72 1.62q-.63-.288-1.368-.504a5 5 0 0 0-1.53-.234M48.793 19h-1.908V5.32h1.908zm4.904-9.702V19h-1.908V9.298zm-.936-3.69q.432 0 .756.252t.324.846q0 .576-.324.846a1.2 1.2 0 0 1-.756.252 1.25 1.25 0 0 1-.792-.252q-.306-.27-.306-.846 0-.594.306-.846a1.25 1.25 0 0 1 .792-.252m7.838 13.572q-1.332 0-2.34-.522t-1.566-1.62q-.558-1.116-.558-2.826 0-1.8.594-2.916.612-1.116 1.638-1.638 1.044-.54 2.376-.54.81 0 1.512.18.72.162 1.188.378l-.576 1.548a9 9 0 0 0-1.08-.342 4.3 4.3 0 0 0-1.062-.144q-.9 0-1.494.396-.576.378-.864 1.152-.27.756-.27 1.908 0 1.098.288 1.854t.846 1.152q.576.378 1.422.378.81 0 1.422-.18t1.152-.468v1.656a4 4 0 0 1-1.134.45q-.612.144-1.494.144m6.808-7.02q0 .378-.036.864-.018.486-.054.9h.054l.342-.432.432-.54a10 10 0 0 1 .396-.468l2.97-3.186h2.214l-3.906 4.158L73.977 19h-2.25l-3.204-4.338-1.116.936V19h-1.89V5.32h1.89zm15.761 3.366q0 1.152-.558 1.962t-1.602 1.26q-1.044.432-2.484.432a10 10 0 0 1-1.35-.09 9 9 0 0 1-1.206-.216 5 5 0 0 1-.99-.36v-1.836q.72.324 1.692.594a7.9 7.9 0 0 0 1.98.252q.864 0 1.44-.234t.864-.648.288-.972q0-.594-.306-.99-.288-.414-.918-.756-.612-.36-1.656-.756-.72-.27-1.314-.594a5.2 5.2 0 0 1-1.026-.792q-.432-.45-.666-1.044t-.234-1.386q0-1.062.522-1.818.54-.756 1.476-1.152.936-.414 2.178-.414a8 8 0 0 1 1.926.216q.9.198 1.71.558l-.612 1.602q-.738-.306-1.494-.504a6.2 6.2 0 0 0-1.584-.198q-.72 0-1.206.216t-.738.594a1.64 1.64 0 0 0-.234.882q0 .594.27.99t.864.738q.594.324 1.566.72 1.08.414 1.836.9.774.486 1.17 1.17.396.666.396 1.674m5.793 2.106q.378 0 .774-.072t.684-.162v1.44q-.306.144-.828.234a5 5 0 0 1-1.044.108q-.792 0-1.458-.27-.648-.27-1.044-.936t-.396-1.854v-5.364h-1.35v-.864l1.422-.72.666-2.052h1.17v2.178h2.772v1.458h-2.772v5.328q0 .792.378 1.17.396.378 1.026.378m7.16-8.514q1.836 0 2.736.81.9.792.9 2.502V19h-1.35l-.378-1.35h-.072a5 5 0 0 1-.864.864 2.8 2.8 0 0 1-1.008.504q-.558.162-1.368.162a3.9 3.9 0 0 1-1.566-.306 2.5 2.5 0 0 1-1.08-.972q-.396-.666-.396-1.656 0-1.476 1.116-2.25 1.134-.774 3.438-.846l1.656-.054v-.54q0-1.08-.486-1.512t-1.368-.432q-.756 0-1.44.216a9 9 0 0 0-1.296.522l-.612-1.386a7.5 7.5 0 0 1 1.566-.594 7 7 0 0 1 1.872-.252m.45 5.256q-1.656.072-2.304.558-.63.468-.63 1.332 0 .756.45 1.098.468.342 1.17.342 1.134 0 1.872-.63t.738-1.89v-.846zm10.03 4.806q-1.332 0-2.34-.522t-1.566-1.62q-.558-1.116-.558-2.826 0-1.8.594-2.916.612-1.116 1.638-1.638 1.044-.54 2.376-.54.81 0 1.512.18.72.162 1.188.378l-.576 1.548a9 9 0 0 0-1.08-.342 4.3 4.3 0 0 0-1.062-.144q-.9 0-1.494.396-.576.378-.864 1.152-.27.756-.27 1.908 0 1.098.288 1.854t.846 1.152q.576.378 1.422.378.81 0 1.422-.18t1.152-.468v1.656a4 4 0 0 1-1.134.45q-.612.144-1.494.144m6.808-7.02q0 .378-.036.864-.018.486-.054.9h.054l.342-.432.432-.54a10 10 0 0 1 .396-.468l2.97-3.186h2.214l-3.906 4.158L119.979 19h-2.25l-3.204-4.338-1.116.936V19h-1.89V5.32h1.89z"
Expand Down
29 changes: 29 additions & 0 deletions packages/app/src/components/AppNav/AppNav.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ $transition-slow: 0.2s ease;
.navGhost {
transition: width 0.2s ease;
will-change: width;
flex-shrink: 0;
}

// Covers the page while the rail's full nav is open on a compact viewport.
// Dimming sits one layer below the nav, in the same layer as top-level drawers.
.navScrim {
position: fixed;
inset: 0;
z-index: 9;
border: 0;
padding: 0;
background: rgb(0 0 0 / 40%);
cursor: pointer;
}

.nav {
Expand All @@ -60,6 +73,22 @@ $transition-slow: 0.2s ease;
position: fixed;
}

// Same layer as a top-level drawer (`contextZIndex + 10` from
// ZIndexContext, see DBRowSidePanel). The scrim is one below. PageHeader
// itself is z-index 2, so this covers the page chrome.
&Overlay {
top: 0;
left: 0;
z-index: 10;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 major — The nav overlay shares z-index 10 with the row side panel, so the open side panel covers most of the nav

DBRowSidePanel (packages/app/src/components/DBRowSidePanel.tsx:1405, 1461, 1476) renders a portaled Drawer at contextZIndex + 10 = 10, with withOverlay={false} and a width of 80% (DrawerUtils.tsx:16). The portal comes later in the DOM, so at the same z-index it paints over the in-tree nav. On a 390px viewport with a row open, the toggle at x<50 is still visible and clickable. Clicking it opens a 250px nav whose area from x≈78 to x=250 is hidden under the drawer. The z-9 scrim also sits under the drawer, so the drawer stays undimmed and interactive. Fix: put the overlay above top-level drawers, for example by portaling the nav and scrim with a z-index above drawerZIndex, or close open side panels when the nav opens.

Nothing blocks merge automatically, but a maintainer will expect this fixed if it is a real defect in code this PR changes. If it is about surrounding code, reply and say so instead of patching. Do not widen the PR. How to respond

box-shadow: 4px 0 16px rgb(0 0 0 / 25%);
}

// Closing the overlay drops the nav back into the flow. Animating its width
// from the overlay width would push the page content sideways first.
&CompactRail {
transition: none;
}

&Collapsed {
.navLinks {
padding-inline: 0;
Expand Down
70 changes: 60 additions & 10 deletions packages/app/src/components/AppNav/AppNav.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo } from 'react';
import { useCallback, useEffect, useMemo, useRef } from 'react';
import Link from 'next/link';
import Router, { useRouter } from 'next/router';
import Script from 'next/script';
Expand All @@ -7,7 +7,7 @@ import { Badge } from '@clickhouse/click-ui';
import HyperDX from '@hyperdx/browser';
import { SavedSearchListApiResponse } from '@hyperdx/common-utils/dist/types';
import { Anchor, Collapse, Flex, Group, ScrollArea, Text } from '@mantine/core';
import { useDisclosure, useLocalStorage } from '@mantine/hooks';
import { useDisclosure, useFocusTrap, useLocalStorage } from '@mantine/hooks';
import {
IconBell,
IconChartDots,
Expand All @@ -23,6 +23,7 @@ import api from '@/api';
import { useLogomark, useWordmark } from '@/brand';
import { AlertStatusIcon } from '@/components/AlertStatusIcon';
import { InstanceLabel } from '@/components/AppNav/InstanceLabel';
import { useMobileNavOverlay } from '@/components/AppNav/useMobileNavOverlay';
import { useCompactViewport } from '@/components/CompactViewport/useCompactViewport';
import { APP_VERSION, IS_LOCAL_MODE } from '@/config';
import { Dashboard, useDashboards } from '@/dashboard';
Expand Down Expand Up @@ -71,6 +72,9 @@ type NavLinkConfig = {
cloudOnly?: boolean; // Only show when not in local mode
};

const RAIL_WIDTH = 50;
const FULL_NAV_WIDTH = 250;

const NAV_LINKS: NavLinkConfig[] = [
{
id: 'chart',
Expand Down Expand Up @@ -182,9 +186,37 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) {
defaultValue: false,
});

const isCollapsed = useCompactViewport() || isPreferCollapsed;

const navWidth = isCollapsed ? 50 : 250;
const isCompact = useCompactViewport();
const {
isOpen: isMobileNavOpen,
toggle: toggleMobileNav,
close: closeMobileNav,
} = useMobileNavOverlay(isCompact);
const collapseButtonRef = useRef<HTMLButtonElement>(null);
const wasMobileNavOpenRef = useRef(false);
// Traps Tab inside the open overlay so keys do not reach the page under the scrim.
const navFocusTrapRef = useFocusTrap(isMobileNavOpen);

// The toggle stays mounted when the overlay closes, so keyboard focus can
// return to it after Escape or the scrim removes itself.
useEffect(() => {
if (wasMobileNavOpenRef.current && !isMobileNavOpen) {
collapseButtonRef.current?.focus();
}
wasMobileNavOpenRef.current = isMobileNavOpen;
}, [isMobileNavOpen]);

// The page layout reserves the rail width even while the overlay is open,
// so opening the nav does not shift the content.
const isRail = isCompact || isPreferCollapsed;
const isCollapsed = isRail && !isMobileNavOpen;
const navWidth = isCollapsed ? RAIL_WIDTH : FULL_NAV_WIDTH;
const reservedWidth = isRail ? RAIL_WIDTH : FULL_NAV_WIDTH;
// The in-flow rail is border-box, so its 1px border is already inside
// reservedWidth. The fixed nav's ghost keeps the extra pixel it had
// before. The overlay ghost must not, or the page shifts when it opens.
const ghostWidth =
isMobileNavOpen && !fixed ? reservedWidth : reservedWidth + 1;

useEffect(() => {
HyperDX.addAction('user navigated', {
Expand Down Expand Up @@ -321,24 +353,35 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) {

return (
<AppNavContext value={{ isCollapsed, pathname }}>
{fixed && (
{(fixed || isMobileNavOpen) && (
<div
className={styles.navGhost}
style={{
width: navWidth + 1,
minWidth: navWidth + 1,
width: ghostWidth,
minWidth: ghostWidth,
}}
></div>
)}
{isMobileNavOpen && (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — The overlay acts like a modal drawer but does not manage focus

Opening the nav leaves focus on the toggle, and Tab moves past the nav into page controls hidden behind the scrim. Closing it with Escape or the scrim does not return focus anywhere. Trap focus inside the nav while it is open, for example with Mantine FocusTrap / useFocusTrap on the nav container, and return focus to the toggle on close. Mantine Drawer already provides the overlay, Escape handling and focus return.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

<button
type="button"
className={styles.navScrim}
aria-label="Close navigation"
onClick={closeMobileNav}
/>
)}
<InstallInstructionModal
show={showInstallInstructions}
onHide={closeInstallInstructions}
/>
<div
ref={navFocusTrapRef}
data-testid="app-nav"
className={cx(styles.nav, {
[styles.navFixed]: fixed,
[styles.navFixed]: fixed || isMobileNavOpen,
[styles.navOverlay]: isMobileNavOpen,
[styles.navCollapsed]: isCollapsed,
[styles.navCompactRail]: isCompact && !isMobileNavOpen,
})}
style={{ width: navWidth }}
>
Expand Down Expand Up @@ -380,13 +423,20 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) {
{/* click-ui 0.12 has no sidebar-left icon, so the glyph is
inlined. Chrome matches IconButton type="primary" size="sm". */}
<button
ref={collapseButtonRef}
type="button"
className={cx(styles.collapseButton, {
[styles.collapseButtonCollapsed]: isCollapsed,
})}
title="Collapse/expand navigation"
aria-label="Collapse/expand navigation"
onClick={() => setIsPreferCollapsed((v: boolean) => !v)}
aria-expanded={!isCollapsed}
data-autofocus={isMobileNavOpen ? true : undefined}
onClick={() =>
isCompact
? toggleMobileNav()
: setIsPreferCollapsed((v: boolean) => !v)
}
>
<svg
xmlns="http://www.w3.org/2000/svg"
Expand Down
Loading
Loading