11import styled from "@emotion/styled"
2- import { FC } from "react"
2+ import type { CheckedState } from "@radix-ui/react-checkbox"
3+ import type { TrackEventOf , TrackId } from "@signal-app/core"
4+ import type { ProgramChangeEvent } from "midifile-ts"
5+ import { type FC , useCallback , useEffect , useMemo , useState } from "react"
36import { useInstrumentBrowser } from "../../hooks/useInstrumentBrowser"
7+ import { useTrack } from "../../hooks/useTrack"
48import { Localized } from "../../localize/useLocalization"
59import { Dialog , DialogActions , DialogContent } from "../Dialog/Dialog"
610import { InstrumentName } from "../TrackList/InstrumentName"
711import { Button , PrimaryButton } from "../ui/Button"
812import { Checkbox } from "../ui/Checkbox"
13+ import { DropdownButton } from "../ui/DropdownButton"
914import { Label } from "../ui/Label"
1015import { DrumKitCategoryName , FancyCategoryName } from "./CategoryName"
1116import { SelectBox } from "./SelectBox"
@@ -36,18 +41,84 @@ const Footer = styled.div`
3641 margin-top: 1rem;
3742`
3843
39- export const InstrumentBrowser : FC = ( ) => {
44+ export interface InstrumentBrowserProps {
45+ isOpen : boolean
46+ onOpenChange : ( open : boolean ) => void
47+ trackId : TrackId
48+ targetEventId ?: number
49+ showInsertButton ?: boolean
50+ }
51+
52+ export const InstrumentBrowser : FC < InstrumentBrowserProps > = ( {
53+ isOpen,
54+ onOpenChange,
55+ trackId,
56+ targetEventId,
57+ showInsertButton = false ,
58+ } ) => {
59+ const {
60+ programNumber : initialProgramNumber ,
61+ isRhythmTrack : initialIsRhythmTrack ,
62+ getEventById,
63+ removeEvent,
64+ } = useTrack ( trackId )
65+ const [ setting , setSetting ] = useState ( {
66+ programNumber : initialProgramNumber ?? 0 ,
67+ isRhythmTrack : initialIsRhythmTrack ?? false ,
68+ } )
69+ const { programNumber, isRhythmTrack } = setting
4070 const {
41- isOpen,
42- setOpen,
43- setting : { programNumber, isRhythmTrack } ,
4471 selectedCategoryIndex,
4572 categoryFirstProgramEvents,
4673 categoryInstruments,
47- onChangeInstrument : onChange ,
74+ insertInstrumentChangeAtCurrentPosition,
75+ changeInstrument,
4876 onClickOK,
49- onChangeRhythmTrack,
50- } = useInstrumentBrowser ( )
77+ changeRhythmTrack,
78+ } = useInstrumentBrowser ( setting , targetEventId )
79+
80+ const targetEvent = useMemo ( ( ) => {
81+ if ( targetEventId !== undefined ) {
82+ return getEventById ( targetEventId ) as
83+ | TrackEventOf < ProgramChangeEvent >
84+ | undefined
85+ }
86+ return undefined
87+ } , [ targetEventId , getEventById ] )
88+
89+ useEffect ( ( ) => {
90+ if ( isOpen ) {
91+ if ( targetEvent ) {
92+ setSetting ( {
93+ programNumber : targetEvent . value ,
94+ isRhythmTrack : initialIsRhythmTrack ?? false ,
95+ } )
96+ }
97+ }
98+ } , [ isOpen , targetEvent , initialIsRhythmTrack ] )
99+
100+ const onChange = useCallback (
101+ ( programNumber : number ) => {
102+ setSetting ( {
103+ programNumber,
104+ isRhythmTrack,
105+ } )
106+ changeInstrument ( programNumber )
107+ } ,
108+ [ isRhythmTrack , changeInstrument ] ,
109+ )
110+
111+ const handleChangeRhythmTrack = useCallback (
112+ ( state : CheckedState ) => {
113+ const isRhythmTrack = state === true
114+ setSetting ( {
115+ programNumber : 0 , // reset program number when changing rhythm track
116+ isRhythmTrack,
117+ } )
118+ changeRhythmTrack ( isRhythmTrack )
119+ } ,
120+ [ changeRhythmTrack ] ,
121+ )
51122
52123 const categoryOptions = categoryFirstProgramEvents . map ( ( preset , i ) => ( {
53124 value : i ,
@@ -63,8 +134,25 @@ export const InstrumentBrowser: FC = () => {
63134 label : < InstrumentName programNumber = { p } isRhythmTrack = { isRhythmTrack } /> ,
64135 } ) )
65136
137+ const handleClickOK = useCallback ( ( ) => {
138+ onClickOK ( )
139+ onOpenChange ( false )
140+ } , [ onClickOK , onOpenChange ] )
141+
142+ const handleClickInsert = useCallback ( ( ) => {
143+ insertInstrumentChangeAtCurrentPosition ( programNumber )
144+ onOpenChange ( false )
145+ } , [ onOpenChange , insertInstrumentChangeAtCurrentPosition , programNumber ] )
146+
147+ const handleClickDelete = useCallback ( ( ) => {
148+ if ( targetEventId !== undefined ) {
149+ removeEvent ( targetEventId )
150+ }
151+ onOpenChange ( false )
152+ } , [ targetEventId , removeEvent , onOpenChange ] )
153+
66154 return (
67- < Dialog open = { isOpen } onOpenChange = { setOpen } >
155+ < Dialog open = { isOpen } onOpenChange = { onOpenChange } >
68156 < DialogContent className = "InstrumentBrowser" >
69157 < Finder >
70158 < Left >
@@ -91,18 +179,41 @@ export const InstrumentBrowser: FC = () => {
91179 < Footer >
92180 < Checkbox
93181 checked = { isRhythmTrack }
94- onCheckedChange = { ( state ) => onChangeRhythmTrack ( state === true ) }
182+ onCheckedChange = { handleChangeRhythmTrack }
95183 label = { < Localized name = "rhythm-track" /> }
96184 />
97185 </ Footer >
98186 </ DialogContent >
99187 < DialogActions >
100- < Button onClick = { ( ) => setOpen ( false ) } >
188+ { targetEventId !== undefined && (
189+ < Button
190+ onClick = { handleClickDelete }
191+ style = { { marginRight : "auto" } }
192+ >
193+ < Localized name = "delete" />
194+ </ Button >
195+ ) }
196+ < Button onClick = { ( ) => onOpenChange ( false ) } >
101197 < Localized name = "cancel" />
102198 </ Button >
103- < PrimaryButton onClick = { onClickOK } >
104- < Localized name = "ok" />
105- </ PrimaryButton >
199+ { ! showInsertButton && (
200+ < PrimaryButton onClick = { handleClickOK } >
201+ < Localized name = "ok" />
202+ </ PrimaryButton >
203+ ) }
204+ { showInsertButton && (
205+ < DropdownButton
206+ onClick = { handleClickOK }
207+ actions = { [
208+ {
209+ label : "Insert" ,
210+ onClick : handleClickInsert ,
211+ } ,
212+ ] }
213+ >
214+ < Localized name = "ok" />
215+ </ DropdownButton >
216+ ) }
106217 </ DialogActions >
107218 </ Dialog >
108219 )
0 commit comments