diff --git a/.storybook/preview-body.html b/.storybook/preview-body.html index 013c9defe..bbfeaafd3 100644 --- a/.storybook/preview-body.html +++ b/.storybook/preview-body.html @@ -1,12 +1,13 @@ diff --git a/src/components/RecentActivity/EnvironmentPanel/EnvironmentTab/styles.ts b/src/components/RecentActivity/EnvironmentPanel/EnvironmentTab/styles.ts index 38b0d232f..74890a0b3 100644 --- a/src/components/RecentActivity/EnvironmentPanel/EnvironmentTab/styles.ts +++ b/src/components/RecentActivity/EnvironmentPanel/EnvironmentTab/styles.ts @@ -1,15 +1,13 @@ import styled from "styled-components"; import { ContainerProps } from "./types"; -export const Container = styled.button` +export const Container = styled.li` + display: flex; position: relative; cursor: pointer; - border: none; - font-family: inherit; font-weight: ${({ isSelected }) => (isSelected ? 700 : 500)}; font-size: 14px; padding: 4px 12px; - white-space: nowrap; color: ${({ isSelected, theme }) => { if (isSelected) { @@ -48,6 +46,9 @@ export const Container = styled.button` } }}; + border-bottom: ${({ isSelected }) => + isSelected ? "1px solid #5154ec" : "none"}; + &:hover { font-weight: 700; color: ${({ theme }) => { @@ -62,11 +63,9 @@ export const Container = styled.button` }}; } - ${({ isSelected }) => { - if (isSelected) { - return `border-bottom: 1px solid #5154ec`; - } - }} + transition-property: color, font-weight, border; + transition-duration: 300ms; + transition-timing-function: ease-out; `; export const BadgeContainer = styled.div` diff --git a/src/components/RecentActivity/EnvironmentPanel/index.tsx b/src/components/RecentActivity/EnvironmentPanel/index.tsx index e89d3f293..a56ecca97 100644 --- a/src/components/RecentActivity/EnvironmentPanel/index.tsx +++ b/src/components/RecentActivity/EnvironmentPanel/index.tsx @@ -1,12 +1,31 @@ +import { useEffect, useState } from "react"; +import useDimensions from "react-cool-dimensions"; +import { DefaultTheme, useTheme } from "styled-components"; import { IconButton } from "../../common/IconButton"; +import { ChevronIcon } from "../../common/icons/ChevronIcon"; import { DigmaLogoFlatIcon } from "../../common/icons/DigmaLogoFlatIcon"; import { ListIcon } from "../../common/icons/ListIcon"; import { TableIcon } from "../../common/icons/TableIcon"; +import { DIRECTION } from "../../common/icons/types"; import { EnvironmentTab } from "./EnvironmentTab"; import * as s from "./styles"; -import { EnvironmentPanelProps } from "./types"; +import { EnvironmentPanelProps, ScrollDirection } from "./types"; + +const getCarouselIconColor = (theme: DefaultTheme, isDisabled: boolean) => { + switch (theme.mode) { + case "light": + return isDisabled ? "#b9c0d4" : "#4d668a"; + case "dark": + case "dark-jetbrains": + return isDisabled ? "#7c7c94" : "#e2e7ff"; + } +}; export const EnvironmentPanel = (props: EnvironmentPanelProps) => { + const theme = useTheme(); + const [scrollLeft, setScrollLeft] = useState(0); + const { observe, width, entry } = useDimensions(); + const handleEnvironmentTabClick = (name: string) => { props.onEnvironmentSelect(name); }; @@ -16,11 +35,55 @@ export const EnvironmentPanel = (props: EnvironmentPanelProps) => { table: TableIcon }; + useEffect(() => { + if (entry) { + entry.target.scrollLeft = scrollLeft; + } + }, [entry, scrollLeft]); + const handleViewModeButtonClick = () => { const mode = props.viewMode === "table" ? "list" : "table"; props.onViewModeChange(mode); }; + const handleCarouselButtonClick = (direction: ScrollDirection) => { + if (entry) { + let delta = width; + if (direction === "left") { + delta *= -1; + } + + let newScrollLeft = entry.target.scrollLeft + delta; + + if (newScrollLeft >= entry.target.scrollWidth - width) { + newScrollLeft = entry.target.scrollWidth - width; + } + + if (newScrollLeft < 0) { + newScrollLeft = 0; + } + + setScrollLeft(newScrollLeft); + } + }; + + const isCarouselButtonDisabled = (direction: ScrollDirection) => { + if (entry) { + if (direction === "left") { + return scrollLeft === 0; + } + + if (direction === "right") { + return scrollLeft === entry.target.scrollWidth - width; + } + } + + return false; + }; + + const isLeftCarouselButtonDisabled = isCarouselButtonDisabled("left"); + const isRightCarouselButtonDisabled = isCarouselButtonDisabled("right"); + return ( @@ -29,15 +92,37 @@ export const EnvironmentPanel = (props: EnvironmentPanelProps) => { - {props.environments.map((environment) => ( - handleCarouselButtonClick("left")} + disabled={isLeftCarouselButtonDisabled} + > + + + + {props.environments.map((environment) => ( + + ))} + + handleCarouselButtonClick("right")} + disabled={isRightCarouselButtonDisabled} + > + - ))} + { @@ -42,7 +41,7 @@ export const Container = styled.div` align-items: center; width: 100%; height: 100%; - gap: 12px; + gap: 4px; background: ${({ theme }) => { switch (theme.mode) { case "light": @@ -57,8 +56,28 @@ export const Container = styled.div` border-radius: 8px; position: relative; box-sizing: border-box; +`; + +export const EnvironmentList = styled.ul` + display: flex; + gap: 12px; + margin: 0; + padding: 0; + overflow: hidden; + + scroll-behavior: smooth; +`; + +export const CarouselButton = styled.button` + display: flex; + background: none; + padding: 0; + border: none; + cursor: pointer; - flex-wrap: wrap; + &:disabled { + cursor: initial; + } `; const rotateAnimation = keyframes` diff --git a/src/components/RecentActivity/EnvironmentPanel/types.ts b/src/components/RecentActivity/EnvironmentPanel/types.ts index 3fdfd374a..08d9d5ac0 100644 --- a/src/components/RecentActivity/EnvironmentPanel/types.ts +++ b/src/components/RecentActivity/EnvironmentPanel/types.ts @@ -7,3 +7,5 @@ export interface EnvironmentPanelProps { } export type ViewMode = "table" | "list"; + +export type ScrollDirection = "left" | "right"; diff --git a/src/components/common/ToggleSwitch/styles.ts b/src/components/common/ToggleSwitch/styles.ts index 3683a8cbc..6fe33a2bf 100644 --- a/src/components/common/ToggleSwitch/styles.ts +++ b/src/components/common/ToggleSwitch/styles.ts @@ -27,8 +27,7 @@ export const SwitchContainer = styled.div` border-radius: 8px; width: 28px; height: 16px; - transition-property: background; - transition-duration: 0.3s; + transition: background 300ms; display: flex; align-items: center; @@ -48,7 +47,7 @@ export const Circle = styled.div` height: 8px; border-radius: 50%; transition-property: background, margin-left; - transition-duration: 0.3s; + transition-duration: 300ms; margin-left: ${({ isChecked }) => (isChecked ? "16px" : "4px")}; background: ${({ isChecked, theme }) => {