@@ -2,7 +2,8 @@ import { useForm } from '@tanstack/react-form'
22import { Button , ButtonGroup , Form , Modal } from 'react-bootstrap'
33import type { Piece } from '~backend/background/interfaces'
44import { FieldInfo } from '../form'
5- import { useState } from 'react'
5+ import { useEffect , useLayoutEffect , useRef , useState } from 'react'
6+ import * as OGrafForm from 'ograf-form'
67import { useNavigate } from '@tanstack/react-router'
78import { useAppDispatch , useAppSelector } from '~/store/app'
89import { removePiece , updatePiece } from '~/store/pieces'
@@ -121,7 +122,7 @@ export function PiecePropertiesForm({ piece }: { piece: Piece }) {
121122 children = { ( field ) => (
122123 < >
123124 < Form . Group className = "mb-3" >
124- < Form . Label htmlFor = { field . name } > { fieldInfo . label } :</ Form . Label >
125+ < Form . Label htmlFor = { field . name } > { fieldInfo . label } : </ Form . Label >
125126
126127 { fieldInfo . type === 'string' && (
127128 < Form . Control
@@ -153,6 +154,36 @@ export function PiecePropertiesForm({ piece }: { piece: Piece }) {
153154 onChange = { ( e ) => field . handleChange ( e . target . checked ) }
154155 />
155156 ) }
157+ { fieldInfo . type === 'ograf-form' && (
158+ < ReactOGrafForm
159+ schema = { fieldInfo . ografManifest ?. schema as OGrafForm . GDDSchema }
160+ data = { field . state . value }
161+ onDataChangeCallback = { ( value : any ) => {
162+ field . handleChange ( value )
163+ } }
164+ />
165+ ) }
166+
167+ { fieldInfo . type === 'enum' && (
168+ < >
169+ < Form . Select
170+ name = { field . name }
171+ value = { String ( field . state . value ) }
172+ onBlur = { field . handleBlur }
173+ onChange = { ( e ) => {
174+ console . log ( e . target . value )
175+ field . handleChange ( e . target . value )
176+ } }
177+ >
178+ { fieldInfo . enumValues ?. map ( ( ev , i ) => (
179+ < option key = { i } value = { ev . value } >
180+ { ev . label }
181+ </ option >
182+ ) ) }
183+ </ Form . Select >
184+ </ >
185+ ) }
186+ { fieldInfo . type === 'const' && < span > { field . state . value } </ span > }
156187 </ Form . Group >
157188 < FieldInfo field = { field } />
158189 </ >
@@ -271,3 +302,56 @@ function DeletePieceButton({
271302 </ >
272303 )
273304}
305+
306+ declare global {
307+ namespace React {
308+ namespace JSX {
309+ interface IntrinsicElements {
310+ 'superflytv-ograf-form' : React . DetailedHTMLProps <
311+ React . HTMLAttributes < HTMLElement > & {
312+ schema : string // OGraf.GraphicsManifest['schema']
313+ value : any
314+ } ,
315+ HTMLElement
316+ >
317+ }
318+ }
319+ }
320+ }
321+
322+ function ReactOGrafForm ( props : {
323+ data : any
324+ schema : OGrafForm . GDDSchema
325+ onDataChangeCallback : ( value : any ) => void
326+ } ) {
327+ const containerRef = useRef < HTMLDivElement > ( null )
328+ const ografFormRef = useRef < OGrafForm . SuperFlyTvOgrafDataForm > ( null )
329+
330+ useLayoutEffect ( ( ) => {
331+ if ( ografFormRef . current ) return // Already initialized
332+ if ( ! containerRef . current ) {
333+ // Should really not happen
334+ console . error ( 'Div ref not set for OGraf form' )
335+ return
336+ }
337+
338+ // Initialize the form OGraf form element:
339+ ografFormRef . current = new OGrafForm . SuperFlyTvOgrafDataForm ( )
340+ ografFormRef . current . addEventListener ( 'change' , ( ) => {
341+ if ( ografFormRef . current ) props . onDataChangeCallback ( ografFormRef . current . value )
342+ } )
343+ if ( props . schema ) ografFormRef . current . schema = props . schema as any
344+ ografFormRef . current . value = props . data ?? { }
345+
346+ containerRef . current . appendChild ( ografFormRef . current )
347+ } , [ ] )
348+
349+ useEffect ( ( ) => {
350+ if ( ografFormRef . current && props . schema ) ografFormRef . current . schema = props . schema as any
351+ } , [ props . schema ] )
352+ useEffect ( ( ) => {
353+ if ( ografFormRef . current ) ografFormRef . current . value = props . data
354+ } , [ props . data ] )
355+
356+ return < div ref = { containerRef } />
357+ }
0 commit comments