Skip to content
Open
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
1 change: 1 addition & 0 deletions packages/widget/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const SKApp = (props: SKAppProps) => {
return (
<SettingsContextProvider {...variantProps} {...props}>
<Box
data-sk-layout={props.dashboardVariant ? "dashboard" : "widget"}
className={appContainer({
variant: props.dashboardVariant ? "dashboard" : "widget",
})}
Expand Down
29 changes: 29 additions & 0 deletions packages/widget/src/hooks/use-media-query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useEffect, useState } from "react";

const getMatches = (query: string) => {
if (typeof window === "undefined" || !window.matchMedia) {
return false;
}

return window.matchMedia(query).matches;
};

export const useMediaQuery = (query: string) => {
const [matches, setMatches] = useState(() => getMatches(query));

useEffect(() => {
if (typeof window === "undefined" || !window.matchMedia) {
return;
}

const mediaQueryList = window.matchMedia(query);
const onChange = () => setMatches(mediaQueryList.matches);

onChange();
mediaQueryList.addEventListener("change", onChange);

return () => mediaQueryList.removeEventListener("change", onChange);
}, [query]);

return matches;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import clsx from "clsx";
import { type ReactNode, useState } from "react";
import { Box } from "../../../../components/atoms/box";
import { CaretLeftIcon } from "../../../../components/atoms/icons/caret-left";
import { useMediaQuery } from "../../../../hooks/use-media-query";
import { splitCollapsedMediaQuery } from "../../../../styles/tokens/breakpoints";
import { VerticalDivider } from "../divider";
import * as styles from "./styles.css";

type Side = "primary" | "secondary";

type SplitViewProps = {
primary: ReactNode;
secondary: ReactNode;
primaryBarLabel: string;
secondaryBarLabel: string;
};

export const SplitView = ({
primary,
secondary,
primaryBarLabel,
secondaryBarLabel,
}: SplitViewProps) => {
const isCollapsed = useMediaQuery(splitCollapsedMediaQuery);
const [activeSide, setActiveSide] = useState<Side>("primary");

if (!primary || !secondary) {
return <Box className={styles.container}>{primary || secondary}</Box>;
}
Comment on lines +28 to +30

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use null checks instead of falsy checks for pane presence.

!primary || !secondary can misclassify valid ReactNode values like 0 or "" as missing.

Suggested fix
-  if (!primary || !secondary) {
+  if (primary == null || secondary == null) {
     return <Box className={styles.container}>{primary || secondary}</Box>;
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/widget/src/pages-dashboard/common/components/split-view/index.tsx`
around lines 28 - 30, The pane-presence check in split-view is using a falsy
test that can treat valid ReactNode values like 0 or "" as missing. Update the
conditional in the SplitView render logic to use explicit null/undefined checks
for primary and secondary instead of !primary or !secondary, and keep the
fallback Box rendering behavior the same when one pane is actually absent.


const showPrimary = !isCollapsed || activeSide === "primary";
const showSecondary = !isCollapsed || activeSide === "secondary";
const revealLabel =
activeSide === "primary" ? secondaryBarLabel : primaryBarLabel;

const primaryClass = !isCollapsed
? styles.panelWrapContents
: showPrimary
? styles.panelWrapActiveFromLeft
: styles.panelWrapHidden;

const secondaryClass = !isCollapsed
? styles.panelWrapContents
: showSecondary
? styles.panelWrapActiveFromRight
: styles.panelWrapHidden;

return (
<Box className={styles.container}>
<Box className={primaryClass}>{primary}</Box>

{isCollapsed ? (
<Box
as="button"
type="button"
aria-label={revealLabel}
className={clsx(
styles.bar,
activeSide === "primary"
? styles.barBleedRight
: styles.barBleedLeft
)}
onClick={() =>
setActiveSide((side) =>
side === "primary" ? "secondary" : "primary"
)
}
>
<Box
as="span"
className={clsx(
styles.barIcon,
activeSide === "secondary" && styles.barIconFlipped
)}
>
<CaretLeftIcon />
</Box>

<Box as="span" className={styles.barLabel}>
{revealLabel}
</Box>
</Box>
) : (
<VerticalDivider />
)}

<Box className={secondaryClass}>{secondary}</Box>
</Box>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { keyframes, style } from "@vanilla-extract/css";
import { atoms } from "../../../../styles/theme/atoms.css";
import { vars } from "../../../../styles/theme/contract.css";
import { OUTLET_PADDING } from "../styles.css";

export const container = style({
alignItems: "stretch",
display: "flex",
flexDirection: "row",
gap: "24px",
justifyContent: "center",
minWidth: 0,
width: "100%",
});

export const panelWrapContents = style({
display: "contents",
});

export const panelWrapHidden = style({
display: "none",
});

const reduceMotion = "(prefers-reduced-motion: reduce)";

const slideInFromLeft = keyframes({
from: { opacity: 0, transform: "translateX(-16px)" },
to: { opacity: 1, transform: "translateX(0)" },
});

const slideInFromRight = keyframes({
from: { opacity: 0, transform: "translateX(16px)" },
to: { opacity: 1, transform: "translateX(0)" },
});

const panelWrapActiveBase = style({
display: "flex",
flex: 1,
flexDirection: "row",
minWidth: 0,
});

export const panelWrapActiveFromLeft = style([
panelWrapActiveBase,
{
animation: `${slideInFromLeft} 260ms ease`,
"@media": {
[reduceMotion]: { animation: "none" },
},
},
]);

export const panelWrapActiveFromRight = style([
panelWrapActiveBase,
{
animation: `${slideInFromRight} 260ms ease`,
"@media": {
[reduceMotion]: { animation: "none" },
},
},
]);

export const bar = style([
atoms({
background: "stakeSectionBackground",
}),
{
alignItems: "center",
alignSelf: "stretch",
border: 0,
borderRadius: "10px",
cursor: "pointer",
display: "flex",
flexDirection: "column",
flexShrink: 0,
font: "inherit",
gap: "8px",
justifyContent: "center",
padding: "12px 0",
transition: "background 150ms ease",
width: "30px",
selectors: {
"&:hover": {
background: vars.color.backgroundMuted,
},
},
},
]);

/**
* Bleed the bar outward into the dashboard outlet padding, leaving an 8px gap
* to the card edge on whichever side it currently lives.
*/
export const barBleedRight = style({
marginRight: `calc(8px - ${OUTLET_PADDING})`,
});

export const barBleedLeft = style({
marginLeft: `calc(8px - ${OUTLET_PADDING})`,
});

export const barIcon = style({
alignItems: "center",
display: "flex",
justifyContent: "center",
transition: "transform 200ms ease",
"@media": {
[reduceMotion]: { transition: "none" },
},
});

export const barIconFlipped = style({
transform: "rotate(180deg)",
});

export const barLabel = style({
fontSize: "13px",
fontWeight: 600,
letterSpacing: "0.02em",
whiteSpace: "nowrap",
writingMode: "vertical-rl",
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ export const wrapper = recipe({
borderWidth: "1px",
borderStyle: "solid",
boxShadow: "0px 15px 40px 0px #0000000D",
width: "1000px",
maxWidth: "1000px",
width: "100%",
minWidth: "100%",
Comment on lines +14 to +16

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP '\bwrapper\s*\(' packages/widget/src -C3
rg -nP 'minWidth' packages/widget/src/pages-dashboard/common/components/styles.css.ts -C2

Repository: stakekit/widget

Length of output: 251


🏁 Script executed:

#!/bin/bash
set -euo pipefail

git ls-files 'packages/widget/src/pages-dashboard/common/components/*' 'packages/widget/src/pages-dashboard/**/styles.css.ts' 'packages/widget/src/**/styles.css.ts' | sed -n '1,200p'

echo '--- styles.css.ts outline ---'
ast-grep outline packages/widget/src/pages-dashboard/common/components/styles.css.ts --view expanded || true

echo '--- surrounding file ---'
cat -n packages/widget/src/pages-dashboard/common/components/styles.css.ts | sed -n '1,120p'

echo '--- search for wrapper/appContainer/dashboard variant ---'
rg -n 'appContainer|wrapper|dashboard variant|minWidth: "100%"|maxWidth: "1000px"' packages/widget/src -C 3

Repository: stakekit/widget

Length of output: 29018


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- packages/widget/src/style.css.ts ---'
cat -n packages/widget/src/style.css.ts | sed -n '1,140p'

echo '--- packages/widget/src/pages-dashboard/common/components/wrapper.tsx ---'
cat -n packages/widget/src/pages-dashboard/common/components/wrapper.tsx | sed -n '1,120p'

Repository: stakekit/widget

Length of output: 3150


Remove minWidth: "100%"

minWidth: "100%" prevents the new maxWidth: "1000px" cap from taking effect on wider dashboard layouts. width: "100%" is enough here.

🐛 Proposed fix
       maxWidth: "1000px",
       width: "100%",
-      minWidth: "100%",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
maxWidth: "1000px",
width: "100%",
minWidth: "100%",
maxWidth: "1000px",
width: "100%",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/widget/src/pages-dashboard/common/components/styles.css.ts` around
lines 14 - 16, Remove the conflicting minWidth rule from the dashboard styles so
the maxWidth cap can work correctly. Update the style object in the shared
dashboard component styles to keep width on 100% and maxWidth at 1000px, but
drop minWidth from the same declaration. Use the existing styles definition in
the component stylesheet to locate and adjust this layout rule.

},
],
variants: {
Expand Down Expand Up @@ -59,16 +61,6 @@ export const outletWrapper = recipe({
},
});

export const tabPageContainer = recipe({
base: {
display: "flex",
flexDirection: "row",
gap: "24px",
alignItems: "stretch",
justifyContent: "center",
},
});

export const tabPageDivider = recipe({
base: {
alignSelf: "stretch",
Expand Down

This file was deleted.

51 changes: 28 additions & 23 deletions packages/widget/src/pages-dashboard/overview/index.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,43 @@
import { useTranslation } from "react-i18next";
import { Outlet } from "react-router";
import { Box } from "../../components/atoms/box";
import { AnimationPage } from "../../navigation/containers/animation-page";
import { BackButtonProvider } from "../common/components/back-button";
import { VerticalDivider } from "../common/components/divider";
import { TabPageContainer } from "../common/components/tab-page-container";
import { SplitView } from "../common/components/split-view";
import { EarnDetails } from "./earn-details";
import { earnDetailsWrapper } from "./earn-details/styles.css";
import { overviewPageContainer } from "./styles.css";

export const OverviewPage = () => {
const { t } = useTranslation();

return (
<AnimationPage>
<Box display="flex" flexDirection="column" gap="4">
<TabPageContainer>
<Box
display="flex"
flex={1}
flexDirection="column"
gap="8"
width="0"
className={overviewPageContainer}
justifyContent="space-between"
>
<BackButtonProvider>
<Outlet />
</BackButtonProvider>
</Box>

<VerticalDivider />

<Box className={earnDetailsWrapper} flex={1} width="0">
<EarnDetails />
</Box>
</TabPageContainer>
<SplitView
primaryBarLabel={t("dashboard.split_view.earn")}
secondaryBarLabel={t("dashboard.split_view.details")}
primary={
<Box
display="flex"
flex={1}
flexDirection="column"
gap="8"
width="0"
className={overviewPageContainer}
justifyContent="space-between"
>
<BackButtonProvider>
<Outlet />
</BackButtonProvider>
</Box>
}
secondary={
<Box className={earnDetailsWrapper} flex={1} width="0">
<EarnDetails />
</Box>
}
/>
</Box>
</AnimationPage>
);
Expand Down
7 changes: 6 additions & 1 deletion packages/widget/src/pages-dashboard/overview/styles.css.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { style } from "@vanilla-extract/css";
import { splitExpandedMediaQuery } from "../../styles/tokens/breakpoints";

export const overviewPageContainer = style({
maxWidth: "380px",
"@media": {
[splitExpandedMediaQuery]: {
maxWidth: "380px",
},
},
});
Loading