249 lines
6 KiB
TypeScript
249 lines
6 KiB
TypeScript
import type {Keyframe} from '@theatre/core/projects/store/types/SheetState_Historic'
|
|
import type {StudioSheetItemKey} from '@theatre/shared/utils/ids'
|
|
import type {VoidFn} from '@theatre/shared/utils/types'
|
|
import {pointerEventsAutoInNormalMode} from '@theatre/studio/css'
|
|
import React from 'react'
|
|
import styled, {css} from 'styled-components'
|
|
import {PresenceFlag} from '@theatre/studio/uiComponents/usePresence'
|
|
import usePresence from '@theatre/studio/uiComponents/usePresence'
|
|
|
|
export type NearbyKeyframesControls = {
|
|
prev?: Pick<Keyframe, 'position'> & {
|
|
jump: VoidFn
|
|
itemKey: StudioSheetItemKey
|
|
}
|
|
cur:
|
|
| {type: 'on'; toggle: VoidFn; itemKey: StudioSheetItemKey}
|
|
| {type: 'off'; toggle: VoidFn}
|
|
next?: Pick<Keyframe, 'position'> & {
|
|
jump: VoidFn
|
|
itemKey: StudioSheetItemKey
|
|
}
|
|
}
|
|
|
|
const Container = styled.div`
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
width: 16px;
|
|
margin: 0 0px 0 2px;
|
|
position: relative;
|
|
z-index: 0;
|
|
opacity: 1;
|
|
|
|
&:after {
|
|
position: absolute;
|
|
left: -14px;
|
|
right: -14px;
|
|
top: -2px;
|
|
bottom: -2px;
|
|
content: ' ';
|
|
display: none;
|
|
z-index: -1;
|
|
background: transparent;
|
|
}
|
|
|
|
&:hover {
|
|
opacity: 1;
|
|
&:after {
|
|
display: block;
|
|
}
|
|
}
|
|
`
|
|
|
|
const Button = styled.div`
|
|
background: none;
|
|
position: relative;
|
|
border: 0;
|
|
transition: transform 0.1s ease-out;
|
|
z-index: 0;
|
|
outline: none;
|
|
cursor: pointer;
|
|
|
|
&:after {
|
|
display: none;
|
|
${Container}:hover & {
|
|
display: block;
|
|
}
|
|
position: absolute;
|
|
left: -4px;
|
|
right: -4px;
|
|
top: -4px;
|
|
bottom: -4px;
|
|
content: ' ';
|
|
z-index: -1;
|
|
}
|
|
`
|
|
|
|
export const nextPrevCursorsTheme = {
|
|
offColor: '#555',
|
|
onColor: '#000000',
|
|
}
|
|
|
|
const CurButton = styled(Button)<{
|
|
isOn: boolean
|
|
presence: PresenceFlag | undefined
|
|
}>`
|
|
&:hover {
|
|
color: #000000;
|
|
}
|
|
|
|
color: ${(props) =>
|
|
props.presence === PresenceFlag.Primary
|
|
? 'white'
|
|
: props.isOn
|
|
? nextPrevCursorsTheme.onColor
|
|
: nextPrevCursorsTheme.offColor};
|
|
`
|
|
|
|
const pointerEventsNone = css`
|
|
pointer-events: none !important;
|
|
`
|
|
|
|
const PrevOrNextButton = styled(Button)<{
|
|
available: boolean
|
|
flag: PresenceFlag | undefined
|
|
}>`
|
|
color: ${(props) =>
|
|
props.flag === PresenceFlag.Primary
|
|
? 'white'
|
|
: props.available
|
|
? nextPrevCursorsTheme.onColor
|
|
: nextPrevCursorsTheme.offColor};
|
|
|
|
${(props) =>
|
|
props.available ? pointerEventsAutoInNormalMode : pointerEventsNone};
|
|
`
|
|
|
|
const Prev = styled(PrevOrNextButton)<{
|
|
available: boolean
|
|
flag: PresenceFlag | undefined
|
|
}>`
|
|
transform: translateX(2px);
|
|
${Container}:hover & {
|
|
transform: translateX(-2px);
|
|
}
|
|
`
|
|
const Next = styled(PrevOrNextButton)<{
|
|
available: boolean
|
|
flag: PresenceFlag | undefined
|
|
}>`
|
|
transform: translateX(-2px);
|
|
${Container}:hover & {
|
|
transform: translateX(2px);
|
|
}
|
|
`
|
|
|
|
namespace Icons {
|
|
const Chevron_Group = styled.g`
|
|
stroke-width: 1;
|
|
${PrevOrNextButton}:hover & path {
|
|
stroke-width: 3;
|
|
}
|
|
`
|
|
|
|
export const Prev = () => (
|
|
<svg
|
|
width="6.5"
|
|
height="9.3"
|
|
viewBox="0 0 6.5 9.3"
|
|
fill="none"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<Chevron_Group transform={`translate(0 0)`}>
|
|
<path
|
|
fill="#ea2333"
|
|
d="M3.8,0c0.4,0,0.8,0.2,0.8,0.6c0,0.5-2.8,3-2.8,4c0,1,2.8,3.4,2.8,4c0,0.4-0.3,0.6-0.8,0.6C2.4,9.3,0,5.8,0,4.6
|
|
C0,3.5,2.4,0,3.8,0z M11,0.4c1.1,0,4.2,3,4.2,4.2c0,1.1-3,4.2-4.2,4.2c-1.1,0-4.2-3-4.2-4.2C6.8,3.4,9.9,0.4,11,0.4z M17.5,0.6
|
|
c0-0.4,0.3-0.6,0.8-0.6C19.6,0,22,3.5,22,4.6c0,1.2-2.4,4.6-3.8,4.6c-0.4,0-0.8-0.2-0.8-0.6c0-0.7,2.8-3.1,2.8-4
|
|
C20.3,3.6,17.5,1.1,17.5,0.6z"
|
|
/>
|
|
</Chevron_Group>
|
|
</svg>
|
|
)
|
|
|
|
export const Next = () => (
|
|
<svg
|
|
width="6.5"
|
|
height="9.3"
|
|
viewBox="0 0 6.5 9.3"
|
|
fill="none"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<Chevron_Group transform={`translate(2 0)`}>
|
|
<path
|
|
fill="#ea2333"
|
|
d="M-13.7,0c0.4,0,0.8,0.2,0.8,0.6c0,0.5-2.8,3-2.8,4c0,1,2.8,3.4,2.8,4c0,0.4-0.3,0.6-0.8,0.6
|
|
c-1.3,0-3.8-3.4-3.8-4.6C-17.5,3.5-15.1,0-13.7,0z M-6.5,0.4c1.1,0,4.2,3,4.2,4.2c0,1.1-3,4.2-4.2,4.2c-1.1,0-4.2-3-4.2-4.2
|
|
C-10.7,3.4-7.6,0.4-6.5,0.4z M0,0.6C0,0.2,0.3,0,0.8,0c1.3,0,3.8,3.5,3.8,4.6c0,1.2-2.4,4.6-3.8,4.6C0.3,9.3,0,9,0,8.7
|
|
c0-0.7,2.8-3.1,2.8-4C2.8,3.6,0,1.1,0,0.6z"
|
|
/>
|
|
</Chevron_Group>
|
|
</svg>
|
|
)
|
|
|
|
const Cur_Group = styled.g`
|
|
stroke-width: 0;
|
|
${CurButton}:hover & path {
|
|
}
|
|
`
|
|
|
|
export const Cur = () => (
|
|
<svg
|
|
width="8.3"
|
|
height="9.3"
|
|
viewBox="0 0 8.3 9.3"
|
|
fill="none"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<Cur_Group transform="translate(0 0)">
|
|
<path
|
|
fill="#ea2333"
|
|
d="M-3.1,0c0.4,0,0.8,0.2,0.8,0.6c0,0.5-2.8,3-2.8,4c0,1,2.8,3.4,2.8,4c0,0.4-0.3,0.6-0.8,0.6
|
|
c-1.3,0-3.8-3.4-3.8-4.6C-6.8,3.5-4.4,0-3.1,0z M4.2,0.4c1.1,0,4.2,3,4.2,4.2c0,1.1-3,4.2-4.2,4.2C3,8.7,0,5.7,0,4.6
|
|
C0,3.4,3,0.4,4.2,0.4z M10.7,0.6C10.7,0.2,11,0,11.4,0c1.3,0,3.8,3.5,3.8,4.6c0,1.2-2.4,4.6-3.8,4.6c-0.4,0-0.8-0.2-0.8-0.6
|
|
c0-0.7,2.8-3.1,2.8-4C13.5,3.6,10.7,1.1,10.7,0.6z"
|
|
/>
|
|
</Cur_Group>
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
const NextPrevKeyframeCursors: React.VFC<NearbyKeyframesControls> = (props) => {
|
|
const prevPresence = usePresence(props.prev?.itemKey)
|
|
const curPresence = usePresence(
|
|
props.cur?.type === 'on' ? props.cur.itemKey : undefined,
|
|
)
|
|
const nextPresence = usePresence(props.next?.itemKey)
|
|
|
|
return (
|
|
<Container>
|
|
<Prev
|
|
available={!!props.prev}
|
|
onClick={props.prev?.jump}
|
|
flag={prevPresence.flag}
|
|
{...prevPresence.attrs}
|
|
>
|
|
<Icons.Prev />
|
|
</Prev>
|
|
<CurButton
|
|
isOn={props.cur.type === 'on'}
|
|
onClick={props.cur.toggle}
|
|
presence={curPresence.flag}
|
|
{...curPresence.attrs}
|
|
>
|
|
<Icons.Cur />
|
|
</CurButton>
|
|
<Next
|
|
available={!!props.next}
|
|
onClick={props.next?.jump}
|
|
flag={nextPresence.flag}
|
|
{...nextPresence.attrs}
|
|
>
|
|
<Icons.Next />
|
|
</Next>
|
|
</Container>
|
|
)
|
|
}
|
|
|
|
export default NextPrevKeyframeCursors
|