diff --git a/src/components/SkillEdge.tsx b/src/components/SkillEdge.tsx index 493b2ef..38a88dc 100644 --- a/src/components/SkillEdge.tsx +++ b/src/components/SkillEdge.tsx @@ -1,14 +1,14 @@ import React from 'react'; -import { NodeState } from '../models'; +import { NodeState, Direction } from '../models'; import Line from './ui/Line'; import UpperAngledLine from './ui/UpperAngledLine'; import MiddleAngledLine from './ui/MiddleAngledLine'; import LowerAngledLine from './ui/LowerAngledLine'; -interface Props { +export interface Props { parentHasMultipleChildren: boolean; state: NodeState; - direction: 'left' | 'right'; + direction: Direction; parentCenterPosition: number; childCenterPosition: number; } @@ -25,7 +25,7 @@ function SkillEdge(props: Props) { if (!parentHasMultipleChildren) return ; return ( - <> +
- +
); } diff --git a/src/components/SkillNode.tsx b/src/components/SkillNode.tsx index f66b5cf..81b33c2 100644 --- a/src/components/SkillNode.tsx +++ b/src/components/SkillNode.tsx @@ -40,7 +40,6 @@ function SkillNode({ const { direction = 'top', content } = tooltip; const { isMobile } = React.useContext(MobileContext); const [parentPosition, setParentPosition] = React.useState({ - bottom: 0, center: 0, }); @@ -48,17 +47,11 @@ function SkillNode({ const childWidth: React.MutableRefObject = React.useRef(0); function calculatePosition() { - const { - bottom, - left, - right, - } = skillNodeRef.current!.getBoundingClientRect(); + const { left, right } = skillNodeRef.current!.getBoundingClientRect(); const scrollX = window.scrollX; - const scrollY = window.scrollY; setParentPosition({ - bottom: bottom + scrollY, center: (right - left) / 2 + left + scrollX, }); } diff --git a/src/components/SkillTree.tsx b/src/components/SkillTree.tsx index 64eba08..c3e3b59 100644 --- a/src/components/SkillTree.tsx +++ b/src/components/SkillTree.tsx @@ -21,7 +21,6 @@ interface Props { } const defaultParentPosition = { - bottom: 0, center: 0, }; diff --git a/src/components/__tests__/SkillEdge.test.tsx b/src/components/__tests__/SkillEdge.test.tsx index c4ca174..1a97640 100644 --- a/src/components/__tests__/SkillEdge.test.tsx +++ b/src/components/__tests__/SkillEdge.test.tsx @@ -1,23 +1,21 @@ import React from 'react'; import { render } from '@testing-library/react'; -import SkillEdge from '../SkillEdge'; -import { NodeState } from 'models'; +import SkillEdge, { Props } from '../SkillEdge'; import { ThemeProvider } from 'styled-components'; import defaultTheme from '../../theme'; -const defaultPosition = { - topX: 0, - topY: 0, - bottomX: 0, +const defaultProps: Props = { + parentHasMultipleChildren: false, + state: 'unlocked', + direction: 'right', + parentCenterPosition: 0, + childCenterPosition: 0, }; -function renderComponent(startingState: NodeState, position = defaultPosition) { - let state = startingState; - const { topX, topY, bottomX } = position; - +function renderComponent(props = defaultProps) { return render( - + ); } @@ -25,9 +23,7 @@ function renderComponent(startingState: NodeState, position = defaultPosition) { describe('SkillEdge', () => { describe('straight lines', () => { it('should be inactive if the next node is unlocked', async () => { - const startingState = 'unlocked'; - - const { getByTestId } = renderComponent(startingState); + const { getByTestId } = renderComponent(); const skillEdge = getByTestId('straight-line'); @@ -39,9 +35,7 @@ describe('SkillEdge', () => { }); it('should be inactive if the next node is locked', () => { - const startingState = 'unlocked'; - - const { getByTestId } = renderComponent(startingState); + const { getByTestId } = renderComponent(); const skillEdge = getByTestId('straight-line'); @@ -53,9 +47,9 @@ describe('SkillEdge', () => { }); it('should be active if the next node is selected', () => { - const startingState = 'selected'; + const props: Props = { ...defaultProps, state: 'selected' }; - const { getByTestId } = renderComponent(startingState); + const { getByTestId } = renderComponent(props); const skillEdge = getByTestId('straight-line'); @@ -66,28 +60,27 @@ describe('SkillEdge', () => { describe('angled lines', () => { const leftAngledLinePosition = { - topX: 100, - topY: 100, - bottomX: 50, + parentCenterPosition: 100, + childCenterPosition: 100, }; const rightAngledLinePosition = { - topX: 100, - topY: 100, - bottomX: 150, + parentCenterPosition: 100, + childCenterPosition: 150, }; it('should be inactive if the next node is unlocked', async () => { - const startingState = 'unlocked'; + const props = { + ...defaultProps, + ...leftAngledLinePosition, + parentHasMultipleChildren: true, + }; - const { getByTestId } = renderComponent( - startingState, - leftAngledLinePosition - ); + const { getByTestId } = renderComponent(props); - const skillEdgeOne = getByTestId('angled-line-one'); - const skillEdgeTwo = getByTestId('angled-line-two'); - const skillEdgeThree = getByTestId('angled-line-three'); + const skillEdgeOne = getByTestId('upper-angled-line'); + const skillEdgeTwo = getByTestId('middle-angled-line'); + const skillEdgeThree = getByTestId('lower-angled-line'); expect(skillEdgeOne).toHaveStyleRule('opacity', '1'); expect(skillEdgeOne).not.toHaveStyleRule( @@ -109,16 +102,17 @@ describe('SkillEdge', () => { }); it('should be inactive if the next node is locked', () => { - const startingState = 'unlocked'; + const props = { + ...defaultProps, + ...leftAngledLinePosition, + parentHasMultipleChildren: true, + }; - const { getByTestId } = renderComponent( - startingState, - leftAngledLinePosition - ); + const { getByTestId } = renderComponent(props); - const skillEdgeOne = getByTestId('angled-line-one'); - const skillEdgeTwo = getByTestId('angled-line-two'); - const skillEdgeThree = getByTestId('angled-line-three'); + const skillEdgeOne = getByTestId('upper-angled-line'); + const skillEdgeTwo = getByTestId('middle-angled-line'); + const skillEdgeThree = getByTestId('lower-angled-line'); expect(skillEdgeOne).not.toHaveStyleRule( 'background-position', @@ -135,28 +129,23 @@ describe('SkillEdge', () => { }); it('should be active if the next node is selected', () => { - const startingState = 'selected'; + const props = { + ...defaultProps, + ...rightAngledLinePosition, + parentHasMultipleChildren: true, + }; - const { getByTestId } = renderComponent( - startingState, - rightAngledLinePosition - ); + const { getByTestId } = renderComponent(props); - const skillEdgeOne = getByTestId('angled-line-one'); - const skillEdgeTwo = getByTestId('angled-line-two'); - const skillEdgeThree = getByTestId('angled-line-three'); + const skillEdgeOne = getByTestId('upper-angled-line'); + const skillEdgeTwo = getByTestId('middle-angled-line'); + const skillEdgeThree = getByTestId('lower-angled-line'); - expect(skillEdgeOne).toHaveStyleRule( - 'background-position', - 'left bottom' - ); - expect(skillEdgeTwo).toHaveStyleRule( - 'background-position', - 'left bottom' - ); + expect(skillEdgeOne).toHaveStyleRule('background-position', 'right top'); + expect(skillEdgeTwo).toHaveStyleRule('background-position', 'right top'); expect(skillEdgeThree).toHaveStyleRule( 'background-position', - 'left bottom' + 'right top' ); }); }); diff --git a/src/components/ui/AngledLine.tsx b/src/components/ui/AngledLine.tsx index 8b315e5..049027e 100644 --- a/src/components/ui/AngledLine.tsx +++ b/src/components/ui/AngledLine.tsx @@ -1,78 +1,21 @@ -import React from 'react'; -import styled, { BaseThemedCssFunction } from 'styled-components'; -import { NodeState } from '../../models'; -import { SELECTED_STATE, LOCKED_STATE } from '../../components/constants'; +import styled from 'styled-components'; +import { Direction } from '../../models'; -const keyframes = require('styled-components').keyframes; -const css: BaseThemedCssFunction = require('styled-components').css; - -type direction = 'left' | 'right'; - -interface Props { - topX: number; - topY: number; - bottomX: number; - direction: 'left' | 'right'; - state: NodeState; -} - -interface AngledLineProps { +export interface AngledLineProps { unlocked: boolean; selected: boolean; } -interface AngledLineVerticalProps { - top: number; - left: number; - direction: direction; +export interface AngledLineVerticalProps { + direction: Direction; } -interface AngledLineHoriztonalProps { - direction: direction; - top: number; - left: number; +export interface AngledLineHoriztonalProps { + direction: Direction; width: number; } -function AngledLine({ topX, topY, bottomX, direction, state }: Props) { - return ( - - - - - - ); -} - -export default AngledLine; - -const AngledLineContainer = styled.div` - height: 56px; -`; - -const StyledAngledLine = styled.div` +export const StyledAngledLine = styled.div` background: linear-gradient( to right, rgba(255, 255, 255, 1) 0%, @@ -94,119 +37,3 @@ const StyledAngledLine = styled.div` opacity: 1; `} `; - -const AngledLineVertical = styled(StyledAngledLine)` - transform: rotate(90deg); - transform-origin: 0 0; -`; - -const slideDownAngledLineTop = keyframes` - from, - 33% { - background-position: right top; - } - - to { - background-position: left bottom; - } -`; - -const AngledLineVerticalTop = styled(AngledLineVertical)< - AngledLineVerticalProps ->` - left: ${props => props.left}px; - top: ${props => props.top}px; - width: 29px; - - ${props => - props.direction === 'right' && - ` - border-bottom-right-radius: 8px; - `} - - ${props => - props.direction === 'left' && - ` - border-top-right-radius: 8px; - `} - - ${props => - props.selected && - css` - animation: ${slideDownAngledLineTop} 0.3s 1 ease-in; - background-position: left bottom; - `} -`; - -const slideDownAngledLineMiddle = keyframes` - from, - 30% { - background-position: right top; - } - - to { - background-position: left bottom; - } -`; - -const AngledLineHoriztonal = styled(StyledAngledLine)< - AngledLineHoriztonalProps ->` - border-left: none; - border-right: none; - left: ${props => props.left}px; - top: ${props => props.top}px; - width: ${props => props.width}px; - - ${props => - props.direction === 'left' && - ` - transform: scaleX(-1); - transform-origin: 0 0; - `} - - ${props => - props.selected && - css` - animation: ${slideDownAngledLineMiddle} 1s 1; - background-position: left bottom; - `} -`; - -const slideDownAngledLineBottom = keyframes` - from, - 70% { - background-position: right top; - } - - to { - background-position: left bottom; - } -`; - -const AngledLineVerticalBottom = styled(AngledLineVertical)< - AngledLineVerticalProps ->` - left: ${props => props.left}px; - top: ${props => props.top}px; - width: 31px; - - ${props => - props.direction === 'right' && - ` - border-top-left-radius: 8px; - `} - - ${props => - props.direction === 'left' && - ` - border-bottom-left-radius: 8px; - `} - - ${props => - props.selected && - css` - animation: ${slideDownAngledLineBottom} 1.2s 1 ease-out; - background-position: left bottom; - `} -`; diff --git a/src/components/ui/LowerAngledLine.tsx b/src/components/ui/LowerAngledLine.tsx index dba89f7..4e2df87 100644 --- a/src/components/ui/LowerAngledLine.tsx +++ b/src/components/ui/LowerAngledLine.tsx @@ -1,27 +1,17 @@ import React from 'react'; -import { NodeState } from '../../models'; +import { NodeState, Direction } from '../../models'; import { LOCKED_STATE, SELECTED_STATE } from '../../components/constants'; import styled, { BaseThemedCssFunction } from 'styled-components'; +import { StyledAngledLine, AngledLineVerticalProps } from './AngledLine'; const keyframes = require('styled-components').keyframes; const css: BaseThemedCssFunction = require('styled-components').css; -type Direction = 'left' | 'right'; - interface Props { direction: Direction; state: NodeState; } -interface AngledLineProps { - unlocked: boolean; - selected: boolean; -} - -interface AngledLineVerticalProps { - direction: Direction; -} - function LowerAngledLine(props: Props) { const { state, direction } = props; @@ -29,7 +19,7 @@ function LowerAngledLine(props: Props) { ); @@ -37,39 +27,13 @@ function LowerAngledLine(props: Props) { export default LowerAngledLine; -const StyledAngledLine = styled.div` - background: linear-gradient( - to right, - rgba(255, 255, 255, 1) 0%, - rgba(255, 255, 255, 1) 50%, - rgba(255, 255, 255, 0) 51%, - rgba(255, 255, 255, 0) 100% - ); - background-size: 210% 100%; - background-position: right top; - border: ${({ theme }) => theme.edgeBorder}; - height: 4px; - position: absolute; - opacity: 0.5; - transition: opacity 0.6s; - - ${props => - props.unlocked && - ` - opacity: 1; - `} -`; - -const AngledLineVertical = styled(StyledAngledLine)` - transform: rotate(90deg) translateY(-50%); - transform-origin: 0 0; -`; - -const AngledLineVerticalBottom = styled(AngledLineVertical)< +const AngledLineVerticalBottom = styled(StyledAngledLine)< AngledLineVerticalProps >` + transform: rotate(90deg) translateY(-50%); + transform-origin: 0 0; left: 50%; - top: -32px; + top: 24px; width: 31px; ${props => diff --git a/src/components/ui/MiddleAngledLine.tsx b/src/components/ui/MiddleAngledLine.tsx index baec538..641847c 100644 --- a/src/components/ui/MiddleAngledLine.tsx +++ b/src/components/ui/MiddleAngledLine.tsx @@ -1,13 +1,12 @@ import React from 'react'; import styled, { BaseThemedCssFunction } from 'styled-components'; -import { NodeState } from '../../models'; +import { NodeState, Direction } from '../../models'; import { SELECTED_STATE, LOCKED_STATE } from '../../components/constants'; +import { StyledAngledLine, AngledLineHoriztonalProps } from './AngledLine'; const keyframes = require('styled-components').keyframes; const css: BaseThemedCssFunction = require('styled-components').css; -type Direction = 'left' | 'right'; - interface Props { parentCenterPosition: number; childCenterPosition: number; @@ -15,16 +14,6 @@ interface Props { state: NodeState; } -interface AngledLineProps { - unlocked: boolean; - selected: boolean; -} - -interface AngledLineHoriztonalProps { - direction: Direction; - width: number; -} - function MiddleAngledLine(props: Props) { const { direction, parentCenterPosition, childCenterPosition, state } = props; @@ -35,7 +24,7 @@ function MiddleAngledLine(props: Props) { return ( ` - background: linear-gradient( - to right, - rgba(255, 255, 255, 1) 0%, - rgba(255, 255, 255, 1) 50%, - rgba(255, 255, 255, 0) 51%, - rgba(255, 255, 255, 0) 100% - ); - background-size: 210% 100%; - background-position: right top; - border: ${({ theme }) => theme.edgeBorder}; - height: 4px; - position: absolute; - opacity: 0.5; - transition: opacity 0.6s; - - ${props => - props.unlocked && - ` - opacity: 1; - `} -`; - const AngledLineHoriztonal = styled(StyledAngledLine)< AngledLineHoriztonalProps >` border-left: none; border-right: none; - top: -32px; + top: 24px; left: 50%; width: ${props => props.width}px; diff --git a/src/components/ui/UpperAngledLine.tsx b/src/components/ui/UpperAngledLine.tsx index 39886a6..7c16546 100644 --- a/src/components/ui/UpperAngledLine.tsx +++ b/src/components/ui/UpperAngledLine.tsx @@ -1,75 +1,35 @@ import React from 'react'; import styled, { BaseThemedCssFunction } from 'styled-components'; -import { NodeState } from '../../models'; +import { NodeState, Direction } from '../../models'; import { SELECTED_STATE, LOCKED_STATE } from '../../components/constants'; +import { StyledAngledLine, AngledLineVerticalProps } from './AngledLine'; const keyframes = require('styled-components').keyframes; const css: BaseThemedCssFunction = require('styled-components').css; -type Direction = 'left' | 'right'; - interface Props { direction: Direction; state: NodeState; } -interface AngledLineProps { - unlocked: boolean; - selected: boolean; -} - -interface AngledLineVerticalProps { - direction: Direction; -} - function UpperAngledLine(props: Props) { const { direction, state } = props; return ( -
- -
+ ); } export default UpperAngledLine; -const StyledAngledLine = styled.div` - background: linear-gradient( - to right, - rgba(255, 255, 255, 1) 0%, - rgba(255, 255, 255, 1) 50%, - rgba(255, 255, 255, 0) 51%, - rgba(255, 255, 255, 0) 100% - ); - background-size: 210% 100%; - background-position: right top; - border: ${({ theme }) => theme.edgeBorder}; - height: 4px; - position: absolute; - opacity: 0.5; - transition: opacity 0.6s; - - ${props => - props.unlocked && - ` - opacity: 1; - `} -`; - -const AngledLineVertical = styled(StyledAngledLine)` +const AngledLineVerticalTop = styled(StyledAngledLine)` transform: rotate(90deg) translateY(-50%); transform-origin: 0 0; -`; - -const AngledLineVerticalTop = styled(AngledLineVertical)< - AngledLineVerticalProps ->` left: 50%; top: -1px; width: 29px; diff --git a/src/models/index.ts b/src/models/index.ts index b663651..7e19a09 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -52,8 +52,9 @@ interface MajorSkill extends BaseSkill { icon: string; } +export type Direction = 'left' | 'right'; + export type ParentPosition = { - bottom: number; center: number; };