diff --git a/components/TranscendenceFragment/index.scss b/components/TranscendenceFragment/index.scss new file mode 100644 index 00000000..bab27223 --- /dev/null +++ b/components/TranscendenceFragment/index.scss @@ -0,0 +1,83 @@ +.Fragment { + $degrees: 72deg; + + $origWidth: 29px; + $origHeight: 54px; + + $scaledWidth: 12px; + $scaledHeight: calc(($scaledWidth / $origWidth) * $origHeight); + + $scale: 1.2; + + @include hidpiImage( + '/icons/transcendence/interactive/interactive-piece', + png, + $scaledWidth, + $scaledHeight + ); + + position: absolute; + z-index: 32; + + aspect-ratio: 29 / 54; + height: $scaledHeight; + width: $scaledWidth; + opacity: 0; + + &:hover { + cursor: pointer; + } + + &.Visible { + opacity: 1; + } + + &.Stage1 { + top: 3px; + left: 18px; + + // &:hover { + // transform: scale($scale, $scale) translateY(-2px); + // } + } + + &.Stage2 { + top: 10px; + left: 27px; + transform: rotate($degrees); + + // &:hover { + // transform: rotate($degrees) scale($scale, $scale) translateY(-2px); + // } + } + + &.Stage3 { + top: 21px; + left: 24px; + transform: rotate($degrees * 2); + + // &:hover { + // transform: rotate($degrees * 2) scale($scale, $scale) translateY(-1px); + // } + } + + &.Stage4 { + top: 21px; + left: 12px; + transform: rotate($degrees * 3); + + // &:hover { + // transform: rotate($degrees * 3) scale($scale, $scale) translateY(-1px); + // } + } + + &.Stage5 { + top: 10px; + left: 8px; + transform: rotate($degrees * 4); + + // &:hover { + // transform: rotate($degrees * 4) scale($scale, $scale) translateY(-1px); + // } + } +} diff --git a/components/TranscendenceFragment/index.tsx b/components/TranscendenceFragment/index.tsx new file mode 100644 index 00000000..0daaef18 --- /dev/null +++ b/components/TranscendenceFragment/index.tsx @@ -0,0 +1,49 @@ +import React from 'react' +import classnames from 'classnames' + +import './index.scss' + +interface Props { + stage: number + interactive: boolean + visible: boolean + onClick?: (index: number) => void + onHover?: (index: number) => void +} + +const TranscendenceFragment = ({ + interactive, + stage, + visible, + onClick, + onHover, +}: Props) => { + const classes = classnames({ + Fragment: true, + Visible: visible, + Stage1: stage === 1, + Stage2: stage === 2, + Stage3: stage === 3, + Stage4: stage === 4, + Stage5: stage === 5, + }) + + function handleClick() { + if (interactive && onClick) onClick(stage) + } + + function handleHover() { + if (interactive && onHover) onHover(stage) + } + + return ( + + ) +} + +TranscendenceFragment.defaultProps = { + interactive: false, + visible: false, +} + +export default TranscendenceFragment diff --git a/components/TranscendenceStar/index.scss b/components/TranscendenceStar/index.scss new file mode 100644 index 00000000..4d73a120 --- /dev/null +++ b/components/TranscendenceStar/index.scss @@ -0,0 +1,79 @@ +.TranscendenceStar { + position: relative; + + &.Immutable { + pointer-events: none; + } + + .Figure { + $size: 18px; + background-repeat: no-repeat; + background-size: 54px 54px; + display: block; + height: $size; + width: $size; + + &.Interactive.Base { + $size: $unit-6x; + + @include hidpiImage( + '/icons/transcendence/interactive/interactive-base', + png, + $size, + $size + ); + + height: $size; + width: $size; + + &:hover { + cursor: pointer; + transform: none; + } + } + + &:hover { + transform: scale(1.2); + } + + &.Stage1 { + background-image: url('/icons/transcendence/1/step-1@3x.png'); + + &:hover { + background-image: url('/icons/transcendence/1/step-1-hover@3x.png'); + } + } + + &.Stage2 { + background-image: url('/icons/transcendence/2/step-2@3x.png'); + + &:hover { + background-image: url('/icons/transcendence/2/step-2-hover@3x.png'); + } + } + + &.Stage3 { + background-image: url('/icons/transcendence/3/step-3@3x.png'); + + &:hover { + background-image: url('/icons/transcendence/3/step-3-hover@3x.png'); + } + } + + &.Stage4 { + background-image: url('/icons/transcendence/4/step-4@3x.png'); + + &:hover { + background-image: url('/icons/transcendence/4/step-4-hover@3x.png'); + } + } + + &.Stage5 { + background-image: url('/icons/transcendence/5/step-5@3x.png'); + + &:hover { + background-image: url('/icons/transcendence/5/step-5-hover@3x.png'); + } + } + } +} diff --git a/components/TranscendenceStar/index.tsx b/components/TranscendenceStar/index.tsx new file mode 100644 index 00000000..0e798483 --- /dev/null +++ b/components/TranscendenceStar/index.tsx @@ -0,0 +1,103 @@ +import React, { useEffect, useState } from 'react' +import classnames from 'classnames' + +import './index.scss' +import TranscendenceFragment from '~components/TranscendenceFragment' + +interface Props { + className?: string + stage: number + editable: boolean + interactive: boolean + onClick?: () => void + onFragmentClick?: (newStage: number) => void + onFragmentHover?: (newStage: number) => void +} + +const NUM_FRAGMENTS = 5 + +const TranscendenceStar = ({ + className, + interactive, + stage, + editable, + onClick, + onFragmentClick, + onFragmentHover, +}: Props) => { + const [visibleStage, setVisibleStage] = useState(0) + const [currentStage, setCurrentStage] = useState(0) + + // Classes + const starClasses = classnames({ + TranscendenceStar: true, + Immutable: !editable, + }) + + const baseImageClasses = classnames(className, { + Figure: true, + }) + + useEffect(() => { + setVisibleStage(stage) + setCurrentStage(stage) + }, [stage]) + + function handleClick() { + if (onClick) { + onClick() + } + } + + function handleFragmentClick(index: number) { + let newStage = index + if (index === currentStage) newStage = 0 + + setVisibleStage(newStage) + setCurrentStage(newStage) + if (onFragmentClick) onFragmentClick(newStage) + } + + function handleFragmentHover(index: number) { + setVisibleStage(index) + if (onFragmentHover) onFragmentHover(index) + } + + function handleMouseLeave() { + setVisibleStage(currentStage) + if (onFragmentHover) onFragmentHover(currentStage) + } + + return ( +
  • {}} + onMouseLeave={interactive ? handleMouseLeave : () => {}} + > +
    + {[...Array(NUM_FRAGMENTS)].map((e, i) => { + const loopStage = i + 1 + return ( + {}} + onHover={interactive ? handleFragmentHover : () => {}} + /> + ) + })} +
    + +
  • + ) +} + +TranscendenceStar.defaultProps = { + stage: 0, + editable: false, + interactive: false, +} + +export default TranscendenceStar