diff --git a/package.json b/package.json index f92c528..f60ca52 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@fortawesome/free-solid-svg-icons": "^6.7.2", "@fortawesome/react-fontawesome": "^0.2.2", "@pixi/react": "8.0.0-beta.21", + "@reduxjs/toolkit": "^2.5.1", "@types/node": "^22.10.5", "audio-decode": "^2.2.2", "eventemitter3": "^5.0.1", @@ -26,7 +27,9 @@ "rc-dock": "^3.3.0", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-redux": "^9.2.0", "react-split-pane": "^0.1.92", + "redux": "^5.0.1", "uuid": "^11.0.4" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c3e4a19..6a31ca8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,6 +25,9 @@ importers: '@pixi/react': specifier: 8.0.0-beta.21 version: 8.0.0-beta.21(@types/react@19.0.7)(pixi.js@8.6.6)(react@19.0.0) + '@reduxjs/toolkit': + specifier: ^2.5.1 + version: 2.5.1(react-redux@9.2.0(@types/react@19.0.7)(react@19.0.0)(redux@5.0.1))(react@19.0.0) '@types/node': specifier: ^22.10.5 version: 22.10.5 @@ -49,9 +52,15 @@ importers: react-dom: specifier: ^19.0.0 version: 19.0.0(react@19.0.0) + react-redux: + specifier: ^9.2.0 + version: 9.2.0(@types/react@19.0.7)(react@19.0.0)(redux@5.0.1) react-split-pane: specifier: ^0.1.92 version: 0.1.92(patch_hash=we2kgptptkdh5cuho4fck6fmnu)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + redux: + specifier: ^5.0.1 + version: 5.0.1 uuid: specifier: ^11.0.4 version: 11.0.4 @@ -345,6 +354,17 @@ packages: pixi.js: ^8.2.6 react: '>=19.0.0' + '@reduxjs/toolkit@2.5.1': + resolution: {integrity: sha512-UHhy3p0oUpdhnSxyDjaRDYaw8Xra75UiLbCiRozVPHjfDwNYkh0TsVm/1OmTW8Md+iDAJmYPWUKMvsMc2GtpNg==} + peerDependencies: + react: ^16.9.0 || ^17.0.0 || ^18 || ^19 + react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 + peerDependenciesMeta: + react: + optional: true + react-redux: + optional: true + '@rollup/rollup-android-arm-eabi@4.29.1': resolution: {integrity: sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==} cpu: [arm] @@ -551,6 +571,9 @@ packages: '@types/react@19.0.7': resolution: {integrity: sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==} + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + '@typescript-eslint/eslint-plugin@8.18.2': resolution: {integrity: sha512-adig4SzPLjeQ0Tm+jvsozSGiCliI2ajeURDGHjZ2llnA+A67HihCQ+a3amtPhUakd1GlwHxSRvzOZktbEvhPPg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1014,6 +1037,9 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} + immer@10.1.1: + resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==} + import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -1415,6 +1441,18 @@ packages: peerDependencies: react: ^19.0.0 + react-redux@9.2.0: + resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==} + peerDependencies: + '@types/react': ^18.2.25 || ^19 + react: ^18.0 || ^19 + redux: ^5.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + redux: + optional: true + react-split-pane@0.1.92: resolution: {integrity: sha512-GfXP1xSzLMcLJI5BM36Vh7GgZBpy+U/X0no+VM3fxayv+p1Jly5HpMofZJraeaMl73b3hvlr+N9zJKvLB/uz9w==} peerDependencies: @@ -1428,6 +1466,14 @@ packages: resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} engines: {node: '>=0.10.0'} + redux-thunk@3.1.0: + resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==} + peerDependencies: + redux: ^5.0.0 + + redux@5.0.1: + resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} + reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -1439,6 +1485,9 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + resize-observer-polyfill@1.5.1: resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} @@ -1615,6 +1664,11 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + use-sync-external-store@1.4.0: + resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + uuid@11.0.4: resolution: {integrity: sha512-IzL6VtTTYcAhA/oghbFJ1Dkmqev+FpQWnCBaKq/gUluLxliWvO8DPFWfIviRmYbtaavtSQe4WBL++rFjdcGWEg==} hasBin: true @@ -1864,6 +1918,16 @@ snapshots: transitivePeerDependencies: - '@types/react' + '@reduxjs/toolkit@2.5.1(react-redux@9.2.0(@types/react@19.0.7)(react@19.0.0)(redux@5.0.1))(react@19.0.0)': + dependencies: + immer: 10.1.1 + redux: 5.0.1 + redux-thunk: 3.1.0(redux@5.0.1) + reselect: 5.1.1 + optionalDependencies: + react: 19.0.0 + react-redux: 9.2.0(@types/react@19.0.7)(react@19.0.0)(redux@5.0.1) + '@rollup/rollup-android-arm-eabi@4.29.1': optional: true @@ -2003,6 +2067,8 @@ snapshots: dependencies: csstype: 3.1.3 + '@types/use-sync-external-store@0.0.6': {} + '@typescript-eslint/eslint-plugin@8.18.2(@typescript-eslint/parser@8.18.2(eslint@9.17.0)(typescript@5.6.3))(eslint@9.17.0)(typescript@5.6.3)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -2665,6 +2731,8 @@ snapshots: ignore@5.3.2: {} + immer@10.1.1: {} + import-fresh@3.3.0: dependencies: parent-module: 1.0.1 @@ -3125,6 +3193,15 @@ snapshots: react: 19.0.0 scheduler: 0.25.0 + react-redux@9.2.0(@types/react@19.0.7)(react@19.0.0)(redux@5.0.1): + dependencies: + '@types/use-sync-external-store': 0.0.6 + react: 19.0.0 + use-sync-external-store: 1.4.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.7 + redux: 5.0.1 + react-split-pane@0.1.92(patch_hash=we2kgptptkdh5cuho4fck6fmnu)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: prop-types: 15.8.1 @@ -3139,6 +3216,12 @@ snapshots: react@19.0.0: {} + redux-thunk@3.1.0(redux@5.0.1): + dependencies: + redux: 5.0.1 + + redux@5.0.1: {} + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -3161,6 +3244,8 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 + reselect@5.1.1: {} + resize-observer-polyfill@1.5.1: {} resolve-from@4.0.0: {} @@ -3411,6 +3496,10 @@ snapshots: dependencies: punycode: 2.3.1 + use-sync-external-store@1.4.0(react@19.0.0): + dependencies: + react: 19.0.0 + uuid@11.0.4: {} vite@6.0.6(@types/node@22.10.5): diff --git a/src/Chart/types.ts b/src/Chart/types.ts index e27b41a..cc9d059 100644 --- a/src/Chart/types.ts +++ b/src/Chart/types.ts @@ -1,4 +1,4 @@ -import { Nullable } from '@/utils/types'; +import { BeatArray, Nullable } from '@/utils/types'; export type ChartInfo = { name: string, @@ -27,3 +27,48 @@ export type FloorPosition = { endTime: number, value: number, }; + +export type ChartNote = { + id: string, + lineID: string, + type: NoteType, + time: BeatArray, + speed: number, + isAbove: boolean, + holdEndTime: BeatArray, +}; + +export type ChartKeyframe = { + id: string, + lineID: string, + time: BeatArray, + value: number, + continuous: boolean, + easing: number, +}; + +export type ChartJudglineProps = { + speed: ChartKeyframe[], + positionX: ChartKeyframe[], + positionY: ChartKeyframe[], + rotate: ChartKeyframe[], + alpha: ChartKeyframe[], +}; + +export type ChartJudgeline = { + id: string, + props: ChartJudglineProps, + notes: ChartNote[], +}; + +export type ChartBPM = { + id: string, + time: BeatArray, + bpm: number, +}; + +export type Chart = { + bpms: ChartBPM[], + lines: ChartJudgeline[], + notes: ChartNote[], +}; diff --git a/src/ui/Timeline/LeftPanel/LeftPanel.tsx b/src/ui/Timeline/LeftPanel/LeftPanel.tsx index 5044b7d..63e7c93 100644 --- a/src/ui/Timeline/LeftPanel/LeftPanel.tsx +++ b/src/ui/Timeline/LeftPanel/LeftPanel.tsx @@ -1,28 +1,29 @@ import React from "react"; import TimelineList from "../List/List"; -import ChartJudgeline from "@/Chart/Judgeline"; import LeftPanelHead from "./Head"; import LeftPanelLine from './Line'; import './styles.css'; +import { useAppSelector } from "@/ui/store/hooks"; +import { selectAllLinesState } from "@/ui/store/selectors/chart"; export type TimelineLeftPanelProps = { - lines: ChartJudgeline[], expandedLines: number[], onLineExpanded: (lineIndex: number, isExpanded: boolean) => void, }; const TimelineLeftPanel: React.FC = ({ - lines, expandedLines, onLineExpanded, }) => { + const lines = useAppSelector((state) => selectAllLinesState(state)); + return (
{lines.map((line, index) => { // TODO: Render line props & add right click menu return onLineExpanded(index, e)} diff --git a/src/ui/Timeline/LeftPanel/Line.tsx b/src/ui/Timeline/LeftPanel/Line.tsx index eb4ce81..e47b9a3 100644 --- a/src/ui/Timeline/LeftPanel/Line.tsx +++ b/src/ui/Timeline/LeftPanel/Line.tsx @@ -2,31 +2,28 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import React, { useCallback } from 'react'; import TimelineList from '../List/List'; import TimelineListItem from '../List/Item'; -import { useSelectedItem } from '@/ui/contexts/SelectedItem'; import { faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons'; -import ChartJudgeline from '@/Chart/Judgeline'; +import { selectLine } from "@/ui/store/slices/chart"; +import { useAppDispatch } from '@/ui/store/hooks'; export type TimelineLeftPanelLineProps = { - line: ChartJudgeline, + lineID: string, name: string, isExpanded: boolean, onExpandClick: (isExpanded: boolean) => void }; const TimelineLeftPanelLine: React.FC = ({ - line, + lineID, name, isExpanded, onExpandClick }: TimelineLeftPanelLineProps) => { - const [ , setSelectedItem ] = useSelectedItem()!; + const dispatch = useAppDispatch(); const handleLineClicked = useCallback(() => { - setSelectedItem((oldItem) => { - if (oldItem === null) return { line, keyframe: null, note: null }; - else return { ...oldItem, line }; - }); - }, [line, setSelectedItem]); + dispatch(selectLine(lineID)); + }, [dispatch, lineID]); return
diff --git a/src/ui/Timeline/RightPanel/Keyframes.tsx b/src/ui/Timeline/RightPanel/Keyframes.tsx index 65da069..2a935fd 100644 --- a/src/ui/Timeline/RightPanel/Keyframes.tsx +++ b/src/ui/Timeline/RightPanel/Keyframes.tsx @@ -5,28 +5,34 @@ import { useScale } from '../ScaleContext'; import { useSelectedItem } from '@/ui/contexts/SelectedItem'; import useDrag from '@/ui/hooks/useDrag'; import { setCSSProperties } from '@/utils/ui'; -import { BeatNumberToArray, GridValue, parseDoublePrecist } from '@/utils/math'; -import ChartKeyframe from '@/Chart/Keyframe'; +import { BeatArrayToNumber, BeatNumberToArray, GridValue, parseDoublePrecist } from '@/utils/math'; +import { ChartJudglineProps, ChartKeyframe } from '@/Chart/types'; import { TChartJudgelineProps } from '@/Chart/JudgelineProps'; import { BeatArray, Nullable } from '@/utils/types'; +import { useAppDispatch, useAppSelector } from '@/ui/store/hooks'; +import { selectKeyframeState, selectLinePropState } from '@/ui/store/selectors/chart'; +import { addKeyframe, editKeyframe, removeKeyframe } from '@/ui/store/slices/chart'; type KeyframeProps = { - keyframe: ChartKeyframe, - time: number, + lineID: string, + type: keyof ChartJudglineProps, + id: string, nextTime: Nullable, onSelected: (id: string) => void, - onKeyframeMove: (id: string, newBeat: BeatArray) => void, - onRightClicked: (id: string) => void, }; const Keyframe: React.FC = ({ - keyframe, - time, + lineID, + type, + id, nextTime, onSelected, - onKeyframeMove, - onRightClicked, }) => { + const dispatch = useAppDispatch(); + const keyframe = useAppSelector((state) => selectKeyframeState(state, lineID, type, id)); + if (!keyframe) return; + + const time = BeatArrayToNumber(keyframe.time); const tempo = useTempo(); const scale = useScale(); const tempoGrid = useMemo(() => parseDoublePrecist(1 / tempo, 6, -1), [tempo]); @@ -45,19 +51,25 @@ const Keyframe: React.FC = ({ }; const calculateNewTime = useCallback((x: number) => { - return GridValue(keyframe.beatNum + (x / beatGrid / tempo), tempoGrid); - }, [keyframe, beatGrid, tempo, tempoGrid]); + return GridValue(time + (x / beatGrid / tempo), tempoGrid); + }, [time, beatGrid, tempo, tempoGrid]); - const handleDragging = useCallback(({ x }: { x: number }) => { + const handleDragging = ({ x }: { x: number }) => { setCurrentTime(calculateNewTime(x)); - }, [calculateNewTime]); + }; - const handleDragEnd = useCallback(({ x }: { x: number }) => { + const handleDragEnd = ({ x }: { x: number }) => { const newTime = calculateNewTime(x); + const newBeat = BeatNumberToArray(newTime, tempo); setCurrentTime(newTime); - onKeyframeMove(keyframe.id, BeatNumberToArray(newTime, tempo)); - }, [calculateNewTime, keyframe.id, onKeyframeMove, tempo]); + dispatch(editKeyframe({ + lineID, + type, + id, + time: newBeat, + })); + }; const handleSelected = useCallback(() => { onSelected(keyframe.id); @@ -71,10 +83,15 @@ const Keyframe: React.FC = ({ onClick: handleSelected, }); - const handleMouseDown = useCallback((e: React.MouseEvent) => { - if (e.button === 2) return onRightClicked(keyframe.id); - else return onMouseDown(e); - }, [keyframe.id, onRightClicked, onMouseDown]); + const handleMouseDown = (e: React.MouseEvent) => { + if (e.button === 2) { + dispatch(removeKeyframe({ + lineID, + type, + id, + })); + } else return onMouseDown(e); + }; useEffect(() => { setCurrentTime(time); @@ -106,65 +123,83 @@ const Keyframe: React.FC = ({ }; export type TimelineRightPanelKeyframesProps = { + lineID: string, type: keyof TChartJudgelineProps, keyframes: ChartKeyframe[], timeRange: [number, number], onKeyframeSelected: (type: keyof TChartJudgelineProps, id: string) => void, - onDoubleClick: (type: keyof TChartJudgelineProps, clickedPosX: number) => void, - onKeyframeMove: (type: keyof TChartJudgelineProps, id: string, newBeat: BeatArray) => void, - onKeyframeDeleted: (type: keyof TChartJudgelineProps, id: string) => void, }; const TimelineRightPanelKeyframes: React.FC = ({ + lineID, type, - keyframes, timeRange, onKeyframeSelected, - onDoubleClick, - onKeyframeMove, - onKeyframeDeleted, }: TimelineRightPanelKeyframesProps) => { + const dispatch = useAppDispatch(); + const keyframes = useAppSelector((state) => selectLinePropState(state, lineID, type)); + const tempo = useTempo(); + const scale = useScale(); + const handleKeyframeSelected = useCallback((id: string) => { onKeyframeSelected(type, id); }, [type, onKeyframeSelected]); - const onRowDoubleClick = useCallback((e: React.MouseEvent) => { + // const onRowDoubleClick = useCallback((e: React.MouseEvent) => { + // const target = e.target as HTMLDivElement; + // const rect = target.getBoundingClientRect(); + // console.log(e.clientX - rect.x); + // addKeyframe({ + // lineID, + // type, + // time: [ 1, 0, 1 ], + // value: 1, + // continuous: false, + // easing: 0, + // }); + // // onDoubleClick(type, e.clientX - rect.x); + // }, [type, addKeyframe, onDoubleClick]); + + const onRowDoubleClick = (e: React.MouseEvent) => { const target = e.target as HTMLDivElement; const rect = target.getBoundingClientRect(); - onDoubleClick(type, e.clientX - rect.x); - }, [type, onDoubleClick]); - - const handleKeyframeMove = useCallback((id: string, newBeat: BeatArray) => { - onKeyframeMove(type, id, newBeat); - }, [type, onKeyframeMove]); - - const handleKeyframeRightClick = useCallback((id: string) => { - onKeyframeDeleted(type, id); - }, [type, onKeyframeDeleted]); + const time = BeatNumberToArray((e.clientX - rect.x) / scale, tempo) + + dispatch(addKeyframe({ + lineID: lineID, + type: type, + time: time, + value: 1, + continuous: false, + easing: 0, + })); + }; const keyframesDom = useMemo(() => { const result: React.ReactNode[] = []; for (let i = 0; i < keyframes.length; i++) { const keyframe = keyframes[i]; - if (keyframe.beatNum < timeRange[0]) continue; - if (keyframe.beatNum > timeRange[1]) break; + const time = BeatArrayToNumber(keyframe.time); + + if (time < timeRange[0]) continue; + if (time > timeRange[1]) break; result.push( ); } return result; - }, [keyframes, timeRange, handleKeyframeMove, handleKeyframeSelected, handleKeyframeRightClick]); + }, [keyframes, timeRange, handleKeyframeSelected]); return ( diff --git a/src/ui/Timeline/RightPanel/KeyframesRow.tsx b/src/ui/Timeline/RightPanel/KeyframesRow.tsx index c44bfc3..fc712b8 100644 --- a/src/ui/Timeline/RightPanel/KeyframesRow.tsx +++ b/src/ui/Timeline/RightPanel/KeyframesRow.tsx @@ -9,6 +9,8 @@ import ChartKeyframe from '@/Chart/Keyframe'; import { BeatArray, Nullable } from '@/utils/types'; import { useSelectedItem } from '@/ui/contexts/SelectedItem'; import { BeatArrayToNumber } from '@/utils/math'; +import { selectLineState, selectLinePropsState } from '@/ui/store/selectors/chart'; +import { useAppSelector } from '@/ui/store/hooks'; const getLastKeyframe = (beat: BeatArray, keyframes: ChartKeyframe[]): Nullable => { const time = BeatArrayToNumber(beat); @@ -26,19 +28,20 @@ const getLastKeyframe = (beat: BeatArray, keyframes: ChartKeyframe[]): Nullable< } type KeyframesRowProps = { - line: ChartJudgeline, + lineID: string, isExpanded: boolean, timeRange: [number, number], }; const KeyframesRow: React.FC = ({ - line, + lineID, isExpanded, timeRange, }) => { + const lineProps = useAppSelector((state) => selectLinePropsState(state, lineID))!; + const line = useAppSelector((state) => selectLineState(state, lineID))!; const tempo = useTempo(); const scale = useScale(); - const [ lineProp, setLineProp ] = useState({ ...line.props }); const [ , setSelectedItem ] = useSelectedItem()!; const onAddKeyframe = useCallback((type: keyof TChartJudgelineProps, clickedPosX: number) => { @@ -52,14 +55,14 @@ const KeyframesRow: React.FC = ({ } const beatArr: BeatArray = [ beatFloor, beatSub, tempo ]; - const lastKeyframe = getLastKeyframe(beatArr, line.props[type]); - line.addKeyframe( - type, - beatArr, - lastKeyframe ? lastKeyframe.value : 0, - lastKeyframe ? lastKeyframe.continuous : false, - lastKeyframe ? lastKeyframe.easing : 0 - ); + // const lastKeyframe = getLastKeyframe(beatArr, line.props[type]); + // line.addKeyframe( + // type, + // beatArr, + // lastKeyframe ? lastKeyframe.value : 0, + // lastKeyframe ? lastKeyframe.continuous : false, + // lastKeyframe ? lastKeyframe.easing : 0 + // ); }, [scale, tempo, line]); const onKeyframeMove = useCallback((type: keyof TChartJudgelineProps, id: string, newBeat: BeatArray) => { @@ -67,7 +70,7 @@ const KeyframesRow: React.FC = ({ if (oldItem !== null) return { ...oldItem, keyframe: null }; else return null; }); - line.editKeyframe(type, id, { beat: newBeat }); + // line.editKeyframe(type, id, { beat: newBeat }); }, [line, setSelectedItem]); const onKeyframeDeleted = useCallback((type: keyof TChartJudgelineProps, id: string) => { @@ -75,75 +78,70 @@ const KeyframesRow: React.FC = ({ if (oldItem !== null) return { ...oldItem, keyframe: null }; else return null; }); - line.deleteKeyframe(type, id); + // line.deleteKeyframe(type, id); }, [line, setSelectedItem]); - const handlePropsUpdate = useCallback(({ - type, - keyframes, - }: { - type: keyof TChartJudgelineProps, - keyframes: ChartKeyframe[], - }) => { - const newProp: Partial = {}; - newProp[type] = keyframes; - setLineProp({ ...lineProp, ...newProp }); - }, [lineProp]); + // const handlePropsUpdate = useCallback(({ + // type, + // keyframes, + // }: { + // type: keyof TChartJudgelineProps, + // keyframes: ChartKeyframe[], + // }) => { + // const newProp: Partial = {}; + // newProp[type] = keyframes; + // setLineProp({ ...lineProp, ...newProp }); + // }, [lineProp]); const handleKeyframeSelected = useCallback((type: keyof TChartJudgelineProps, id: string) => { - const keyframe = line.findKeyframeById(type, id); - if (!keyframe) return; - - setSelectedItem({ - line, - keyframe: { - type, id - }, - note: null, - }); + // const keyframe = line.findKeyframeById(type, id); + // if (!keyframe) return; + + // setSelectedItem({ + // line, + // keyframe: { + // type, id + // }, + // note: null, + // }); }, [line, setSelectedItem]); - useEffect(() => { - line.events.on('props.updated', handlePropsUpdate); - return (() => { - line.events.off('props.updated', handlePropsUpdate); - }); - }, [line, handlePropsUpdate]); - const keyframesProps = { timeRange, onKeyframeSelected: handleKeyframeSelected, - onDoubleClick: onAddKeyframe, - onKeyframeMove, - onKeyframeDeleted, }; return <> {isExpanded && <> } diff --git a/src/ui/Timeline/RightPanel/RightPanel.tsx b/src/ui/Timeline/RightPanel/RightPanel.tsx index dc19cf0..7d2db44 100644 --- a/src/ui/Timeline/RightPanel/RightPanel.tsx +++ b/src/ui/Timeline/RightPanel/RightPanel.tsx @@ -8,6 +8,8 @@ import RightPanelHead from './Head'; import KeyframesRow from './KeyframesRow'; import { useScale } from '../ScaleContext'; import './styles.css'; +import { useAppSelector } from '@/ui/store/hooks'; +import { selectAllLinesState } from '@/ui/store/selectors/chart'; export type TimelineRightPanelProps = { timeLength: number, @@ -17,10 +19,10 @@ export type TimelineRightPanelProps = { const TimelineRightPanel: React.FC = ({ timeLength, - lines, expandedLines, }) => { const scale = useScale(); + const lines = useAppSelector((state) => selectAllLinesState(state)); const [ timeRange, setTimeRange ] = useState<[number, number]>([ 0, 0 ]); const handleRangeChanged = useCallback((newRange: [number, number]) => { @@ -43,7 +45,7 @@ const TimelineRightPanel: React.FC = ({ const keyframeRowMemoed = useMemo(() => { return lines.map((line, index) => { // TODO: Render keyframes return { + const dispatch = useAppDispatch(); const [ timeLength, setTimeLength ] = useState(0); const [ lineList, setLineList ] = useState([]); const [ expandedLines, setExpandedLines ] = useState([]); @@ -62,7 +65,6 @@ const Timeline: React.FC = () => { }} > setLineExpand(id, e)} /> @@ -83,6 +85,11 @@ const Timeline: React.FC = () => { > Add line + |