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
118 changes: 118 additions & 0 deletions packages/ui/src/components/molecules/SliderDeck/SliderDeck.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import React, { FC, useState, useRef } from 'react';
import { styled } from 'styled-components';

import { SliderStop } from './SliderStop';

const SliderContainer = styled.div`
position: relative;
width: 628px;
padding: var(--Subscription-Years, 1px) 0px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
gap: 10px;
cursor: pointer;
`;

const LineSegment = styled.div<{ isActive: boolean }>`
position: absolute;
height: 4px;
background-color: ${({ isActive, theme }) =>
isActive
? theme.palette.background.golden
: theme.palette.background.separator};
`;

const StopContainer = styled.div<{ left: string }>`
position: absolute;
top: -25px;
left: ${({ left }) => left};
`;

const TextContainer = styled.div`
margin-top: 8px;
color: ${({ theme }) => theme.palette.background.golden};
font-family: 'Poppins', sans-serif;
font-size: 12px;
font-weight: 500;
line-height: 18px;
text-align: center;
width: 45px;
position: absolute;
top: 50px;
`;

interface SliderProps {
selectedYear: number;
}

export const SliderDeck: FC<SliderProps> = ({
selectedYear: initialSelectedYear,
}) => {
const [selectedYear, setSelectedYear] = useState(initialSelectedYear);
const sliderRef = useRef<HTMLDivElement>(null);

const handleMouseMove = (event: MouseEvent) => {
if (sliderRef.current) {
const sliderRect = sliderRef.current.getBoundingClientRect();
const mousePosition =
(event.clientX - sliderRect.left) / sliderRect.width;
const newSelectedYear = Math.round(mousePosition * 5);
setSelectedYear(newSelectedYear);
}
};

const stops = [
{ label: '1 Year', isActive: selectedYear >= 1 },
{ label: '2 Years', isActive: selectedYear >= 2 },
{ label: '3 Years', isActive: selectedYear >= 3 },
{ label: '4 Years', isActive: selectedYear >= 4 },
{ label: '5 Years', isActive: selectedYear >= 5 },
];

const stopPositions = ['0%', '25%', '50%', '75%', '100%'];

return (
<SliderContainer
ref={sliderRef}
onMouseDown={() => {
sliderRef.current?.addEventListener('mousemove', handleMouseMove);
}}
onMouseUp={() => {
sliderRef.current?.removeEventListener('mousemove', handleMouseMove);
}}
onMouseLeave={() => {
sliderRef.current?.removeEventListener('mousemove', handleMouseMove);
}}
>
{stopPositions.map((_, index) => (
<LineSegment
key={`segment-${index - 1}`}
isActive={selectedYear > index + 1}
style={{
left: stopPositions[index],
width:
index < stopPositions.length - 1
? `calc(${stopPositions[index + 1]} - ${stopPositions[index]})`
: '0%',
}}
/>
))}
{stops.map((stop, index) => (
<StopContainer
key={`${index + 1}`}
left={index === 0 ? 'calc(0% - 3px)' : stopPositions[index]}
>
<SliderStop
Label={stop.label}
isActive={stop.isActive}
onClick={() => setSelectedYear(index + 1)}
isSelected={selectedYear === index + 1}
/>{' '}
{stop.isActive && <TextContainer>{stop.label}</TextContainer>}
</StopContainer>
))}
</SliderContainer>
);
};
110 changes: 110 additions & 0 deletions packages/ui/src/components/molecules/SliderDeck/SliderStop.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import React, { CSSProperties, FC, useEffect, useState } from 'react';
import { styled } from 'styled-components';

import { colors } from '../../../themes/color.styled';

export interface SliderStopProps {
Label: string;
isActive: boolean;
isSelected: boolean;
onClick: () => void;
}

const ContainerStyle: CSSProperties = {
fontFamily: 'Poppins',
fontSize: '12px',
fontWeight: 500,
lineHeight: '18px',
textAlign: 'center',
width: '45px',
height: '66px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
alignItems: 'center',
position: 'absolute',
top: '0',
zIndex: 8,
};

const StyledContainer = styled.div<{
isActive: boolean;
isSelected: boolean;
isHover: boolean;
}>`
width: ${({ isSelected }) => (isSelected ? '48px' : '40px')};
height: ${({ isSelected }) => (isSelected ? '48px' : '40px')};
border-radius: 50%;
margin-top: ${({ isActive, isSelected, isHover }) => {
if (isSelected) {
return '-1px';
}
if (!isActive && isHover) {
return '0px';
}
return '8px';
}};
border: ${({ isSelected }) => (isSelected ? '8px solid' : 'none')};
border-color: ${({ isActive, isSelected, isHover }) =>
isSelected || (!isActive && isHover) ? colors.active.main : 'inherit'};
background: ${({ isActive, isSelected, isHover }) =>
isActive || isSelected || isHover
? colors.text.gold
: colors.background.separator};
`;

const TextStyle = (isSelected: boolean, isHover: boolean): CSSProperties => ({
background:
isSelected && !isHover ? colors.text.gold : colors.background.muted,
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
marginTop: '8px',
textAlign: 'center',
fontFamily: 'Poppins',
fontSize: '12px',
fontStyle: ' normal',
fontWeight: '500',
lineHeight: ' normal',
});

export const SliderStop: FC<SliderStopProps> = ({
Label,
isActive,
isSelected,
onClick,
}) => {
const [isHover, setIsHover] = useState(false);

useEffect(() => {
if (!isActive) {
setIsHover(false);
}
}, [isActive]);

const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === 'Enter' || event.key === ' ') {
onClick();
}
};

return (
<div
style={ContainerStyle}
onMouseEnter={() => setIsHover(true)}
onClick={onClick}
onMouseLeave={() => setIsHover(false)}
onKeyDown={handleKeyDown}
role="button"
tabIndex={0}
>
<StyledContainer
isActive={isActive}
isSelected={isSelected}
isHover={isHover}
/>
<div style={TextStyle(isSelected, isHover)}>
{!isActive && (isHover || isSelected) ? Label : ''}
</div>
</div>
);
};
1 change: 1 addition & 0 deletions packages/ui/src/components/molecules/SliderDeck/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './SliderDeck';
2 changes: 2 additions & 0 deletions packages/ui/src/components/molecules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ export * from './Topbar';
export * from './UpdateBar';
export * from './WallectConnect';
export * from './WalletDialogMainContainer';
export * from './SliderDeck/SliderStop';
export * from './SimpleJsonView';
export * from './SliderDeck';
42 changes: 42 additions & 0 deletions packages/ui/src/stories/molecules/SliderDeck.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { Meta, StoryObj } from '@storybook/react';

import { SliderDeck } from '../../components';

const meta: Meta<typeof SliderDeck> = {
component: SliderDeck,
tags: ['autodocs'],
};

export default meta;

type Story = StoryObj<typeof SliderDeck>;

export const Default: Story = {
args: {
selectedYear: 1,
},
};

export const TwoYears: Story = {
args: {
selectedYear: 2,
},
};

export const ThreeYears: Story = {
args: {
selectedYear: 3,
},
};

export const FourYears: Story = {
args: {
selectedYear: 4,
},
};

export const FiveYears: Story = {
args: {
selectedYear: 5,
},
};
26 changes: 26 additions & 0 deletions packages/ui/src/stories/molecules/SliderStop.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { Meta, StoryObj } from '@storybook/react';

import { SliderStop } from '../../components';

const meta: Meta<typeof SliderStop> = {
component: SliderStop,
tags: ['autodocs'],
};

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
Label: '5 Years',
isActive: false,
},
};

export const Active: Story = {
args: {
Label: '5 Years',
isActive: true,
},
};
3 changes: 3 additions & 0 deletions packages/ui/src/themes/color.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export const colors = {
info: {
main: '#F1AE4A',
},
active: {
main: '#3c3329',
},
warning: {
main: '#FF624C',
},
Expand Down