Skip to content

Commit

Permalink
[site] Add theme toggle (#14497)
Browse files Browse the repository at this point in the history
Some weird jitter happening on theme change, will follow up on that.

https://github.com/user-attachments/assets/ec12f7c0-bc19-447c-a11a-fb9a7e6acdc0

GitOrigin-RevId: 3fd66bbd8227a7e9e79f0cdc1bf86710115f81a0
  • Loading branch information
coreymartin authored and Lightspark Eng committed Dec 20, 2024
1 parent 34b2926 commit 07efe04
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 5 deletions.
38 changes: 33 additions & 5 deletions packages/ui/src/components/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { CSSInterpolation } from "@emotion/css";
import { css, useTheme } from "@emotion/react";
import type { CSSObject } from "@emotion/styled";
import styled from "@emotion/styled";
import type { ReactNode, RefObject } from "react";
import type { ComponentProps, ReactNode, RefObject } from "react";
import {
useCallback,
useEffect,
Expand Down Expand Up @@ -30,6 +30,7 @@ import { z } from "../styles/z-index.js";
import { type NewRoutesType } from "../types/index.js";
import { Icon } from "./Icon/Icon.js";
import { type IconName } from "./Icon/types.js";
import { IconToggle } from "./IconToggle.js";
import { UnstyledButton } from "./UnstyledButton.js";

type DropdownItemGetProps = WithTheme<{
Expand All @@ -53,6 +54,7 @@ type DropdownItemType = {
externalLink?: ExternalLink | undefined;
params?: RouteParams | undefined;
selected?: boolean;
toggle?: ComponentProps<typeof IconToggle> | undefined;
hash?: RouteHash;
};

Expand Down Expand Up @@ -423,6 +425,18 @@ function DropdownItem({ dropdownItem, onClick }: DropdownItemProps) {
);
}

if (dropdownItem.toggle) {
return (
<DropdownItemDiv selected={false} css={cssProp} withHoverColor={false}>
{dropdownItemIconNode}
{dropdownItem.label}
<div css={{ marginLeft: "auto" }}>
<IconToggle {...dropdownItem.toggle} />
</div>
</DropdownItemDiv>
);
}

return (
<DropdownItemDiv
selected={false}
Expand All @@ -439,9 +453,16 @@ const DropdownButton = styled(UnstyledButton)`
${standardFocusOutline}
`;

type DropdownItemStyleProps = WithTheme<{ selected?: boolean }>;
type DropdownItemStyleProps = WithTheme<{
selected?: boolean;
withHoverColor?: boolean;
}>;

const dropdownItemStyle = ({ selected, theme }: DropdownItemStyleProps) => `
const dropdownItemStyle = ({
theme,
selected,
withHoverColor = true,
}: DropdownItemStyleProps) => `
background-color: ${selected ? theme.primary : "transparent"};
box-sizing: border-box;
cursor: pointer;
Expand All @@ -455,13 +476,20 @@ const dropdownItemStyle = ({ selected, theme }: DropdownItemStyleProps) => `
padding: 8px 16px;
${bp.sm(`padding-left: ${smHeaderLogoMarginLeft};`)}
&:hover {
color: ${theme.hcNeutral} !important;
${
withHoverColor
? `
&:hover {
color: ${theme.hcNeutral} !important;
}
`
: ""
}
`;

type StyledDropdownItemProps = {
selected?: boolean;
withHoverColor?: boolean;
};

const cssProp = ({
Expand Down
68 changes: 68 additions & 0 deletions packages/ui/src/components/IconToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import styled from "@emotion/styled";
import { type ComponentProps } from "react";
import { getColor, type ThemeOrColorKey } from "../styles/themes.js";
import { Icon } from "./Icon/Icon.js";

type IconToggleProps = {
options: ({ key: string } & ComponentProps<typeof Icon>)[];
selectedOption: string;
onChange: (key: string) => void;
selectedBgColor?: ThemeOrColorKey;
selectedColor?: ThemeOrColorKey;
bgColor?: ThemeOrColorKey;
color?: ThemeOrColorKey;
};

export function IconToggle({
options,
selectedOption,
onChange,
selectedBgColor = "c1Neutral",
selectedColor = "hcNeutral",
bgColor = "c4Neutral",
color = "mcNeutral",
}: IconToggleProps) {
return (
<StyledIconToggle bgColor={bgColor}>
{options.map(({ key, ...iconProps }) => (
<StyledIconToggleOption
key={key}
onClick={() => onChange(key)}
selected={key === selectedOption}
selectedBgColor={selectedBgColor}
selectedColor={selectedColor}
colorProp={color}
>
<Icon
{...iconProps}
key={key}
width={11}
color={key === selectedOption ? selectedColor : color}
/>
</StyledIconToggleOption>
))}
</StyledIconToggle>
);
}

const StyledIconToggle = styled.div<{ bgColor: ThemeOrColorKey }>`
border-radius: 999px;
background: ${({ theme, bgColor }) => getColor(theme, bgColor)};
padding: 2px;
gap: 2px;
display: flex;
`;

const StyledIconToggleOption = styled.div<{
selected: boolean;
selectedBgColor: ThemeOrColorKey;
selectedColor: ThemeOrColorKey;
colorProp: ThemeOrColorKey;
}>`
display: flex;
cursor: pointer;
border-radius: 999px;
padding: 4px;
background: ${({ theme, selected, selectedBgColor }) =>
selected ? getColor(theme, selectedBgColor) : "transparent"};
`;
42 changes: 42 additions & 0 deletions packages/ui/src/icons/Contrast.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright ©, 2022, Lightspark Group, Inc. - All Rights Reserved

import { type PathProps } from "./types.js";

export function Contrast({
strokeLinecap = "round",
strokeLinejoin = "round",
}: PathProps) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="100%"
viewBox="0 0 12 12"
fill="none"
>
<path
d="M10.5 6C10.5 8.4853 8.4853 10.5 6 10.5C3.51472 10.5 1.5 8.4853 1.5 6C1.5 3.51472 3.51472 1.5 6 1.5C8.4853 1.5 10.5 3.51472 10.5 6Z"
stroke="currentColor"
strokeLinecap={strokeLinecap}
strokeLinejoin={strokeLinejoin}
/>
<path
d="M6 1.75V10.25"
stroke="currentColor"
strokeLinecap={strokeLinecap}
strokeLinejoin={strokeLinejoin}
/>
<path
d="M7.5 2V10"
stroke="currentColor"
strokeLinecap={strokeLinecap}
strokeLinejoin={strokeLinejoin}
/>
<path
d="M9 2.75V9.25"
stroke="currentColor"
strokeLinecap={strokeLinecap}
strokeLinejoin={strokeLinejoin}
/>
</svg>
);
}
24 changes: 24 additions & 0 deletions packages/ui/src/icons/Moon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright ©, 2022, Lightspark Group, Inc. - All Rights Reserved

import { type PathProps } from "./types.js";

export function Moon({
strokeLinecap = "round",
strokeLinejoin = "round",
}: PathProps) {
return (
<svg
width="100%"
viewBox="0 0 10 10"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.4819 5.38385C8.91805 5.77245 8.23465 6 7.49805 6C5.56505 6 3.99804 4.433 3.99804 2.5C3.99804 1.76346 4.22555 1.08006 4.61415 0.516235C2.30956 0.71151 0.5 2.64396 0.5 4.99905C0.5 7.48385 2.5143 9.49815 4.99905 9.49815C7.3542 9.49815 9.2867 7.6885 9.4819 5.38385Z"
stroke="currentColor"
strokeLinecap={strokeLinecap}
strokeLinejoin={strokeLinejoin}
/>
</svg>
);
}
32 changes: 32 additions & 0 deletions packages/ui/src/icons/Sun.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright ©, 2022, Lightspark Group, Inc. - All Rights Reserved

import { type PathProps } from "./types.js";

export function Sun({
strokeLinecap = "round",
strokeLinejoin = "round",
}: PathProps) {
return (
<svg
width="100%"
viewBox="0 0 12 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M7.76775 4.23224C8.7441 5.20855 8.7441 6.79145 7.76775 7.76775C6.79145 8.7441 5.20855 8.7441 4.23224 7.76775C3.25592 6.79145 3.25592 5.20855 4.23224 4.23224C5.20855 3.25592 6.79145 3.25592 7.76775 4.23224Z"
stroke="currentColor"
strokeLinecap={strokeLinecap}
strokeLinejoin={strokeLinejoin}
/>
<path
d="M6 1.5V1M6 11V10.5M9.1799 2.82002L9.5349 2.46502M2.46508 9.535L2.82008 9.18M10.5 6H11M1 6H1.5M9.1799 9.18L9.5349 9.535M2.46508 2.46502L2.82008 2.82002"
stroke="currentColor"
strokeLinecap={strokeLinecap}
strokeLinejoin={strokeLinejoin}
/>
</svg>
);
}
3 changes: 3 additions & 0 deletions packages/ui/src/icons/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export { CirclePlus } from "./CirclePlus.js";
export { Clock } from "./Clock.js";
export { Close } from "./Close.js";
export { Code } from "./Code.js";
export { Contrast } from "./Contrast.js";
export { Copy } from "./Copy.js";
export { CreditCard } from "./CreditCard.js";
export { DeleteIcon } from "./DeleteIcon.js";
Expand Down Expand Up @@ -91,6 +92,7 @@ export { Messages } from "./Messages.js";
export { Messenger } from "./Messenger.js";
export { Minus } from "./Minus.js";
export { Monitor } from "./Monitor.js";
export { Moon } from "./Moon.js";
export { NodeAdd } from "./NodeAdd.js";
export { NonagonCheckmark } from "./NonagonCheckmark.js";
export { Notebook } from "./Notebook.js";
Expand Down Expand Up @@ -130,6 +132,7 @@ export { Sort } from "./Sort.js";
export { Spark } from "./Spark.js";
export { SparklesSoft } from "./SparklesSoft.js";
export { StackedLines } from "./StackedLines.js";
export { Sun } from "./Sun.js";
export { SwiftTwoTone } from "./SwiftTwoTone.js";
export { TapSingle } from "./TapSingle.js";
export { Team } from "./Team.js";
Expand Down
4 changes: 4 additions & 0 deletions packages/ui/src/styles/themes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export enum Themes {
BridgeDark = "bridgeDark",
}

export function isTheme(theme: unknown): theme is Themes {
return Object.values(Themes).includes(theme as Themes);
}

const baseThemeColors = {
bg: colors.white,
smBg: colors.white,
Expand Down

0 comments on commit 07efe04

Please sign in to comment.