Integration
Custom UI and Cursors
Hide built-in UI sections, drive tools from custom controls, and override multiplayer cursor rendering.
Custom UI and Cursors
Telva lets you keep the rendering/editor core while replacing parts of the host UI.
Hide built-in panels and render your own toolbar
import * as React from 'react'
import { TVShapeType, TVToolType, Telva, TelvaApp } from 'telva'
const AppContext = React.createContext<TelvaApp>({} as TelvaApp)
function useApp() {
return React.useContext(AppContext)
}
function ToolButton({ type, children }: React.PropsWithChildren<{ type: TVToolType }>) {
const app = useApp()
const isActive = app.useStore((state) => state.appState.activeTool === type)
return (
<button onClick={() => app.selectTool(type)} aria-pressed={isActive}>
{children}
</button>
)
}
export default function CustomUI() {
const [app, setApp] = React.useState<TelvaApp>()
return (
<div style={{ position: 'relative', width: '100%', height: 620 }}>
<Telva
onMount={setApp}
showUI
showStyles={false}
showTools={false}
showPages={false}
showMenu={false}
/>
{app && (
<AppContext.Provider value={app}>
<div style={{ position: 'absolute', bottom: 16, left: 16, display: 'flex', gap: 8 }}>
<ToolButton type="select">Select</ToolButton>
<ToolButton type="erase">Erase</ToolButton>
<ToolButton type={TVShapeType.Sticky}>Sticky</ToolButton>
</div>
</AppContext.Provider>
)}
</div>
)
}Override multiplayer cursor rendering
import * as React from 'react'
import { TVUserStatus, Telva, TelvaApp } from 'telva'
import { CursorComponent } from 'telva-core'
const CustomCursor: CursorComponent<{ name: string }> = ({ color, metadata }) => (
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<div style={{ width: 12, height: 12, borderRadius: '100%', background: color }} />
<div style={{ background: 'white', padding: '4px 8px', borderRadius: 4 }}>
{metadata?.name}
</div>
</div>
)
export default function CustomCursors() {
function onMount(app: TelvaApp) {
app.updateUsers([
{
id: 'fakeuser1',
point: [100, 100],
color: 'orange',
status: TVUserStatus.Connected,
activeShapes: [],
selectedIds: [],
metadata: { name: 'Steve' },
},
])
}
return <Telva onMount={onMount} components={{ Cursor: CustomCursor }} />
}Register custom React components for canvas placement
Pass a ReactComponentEntry[] to reactComponents:
import type { ReactComponentEntry } from 'telva'
import Grainient from './Grainient'
const REACT_COMPONENTS: ReactComponentEntry[] = [
{
id: 'grainient',
name: 'Grainient',
description: 'Animated gradient shader background',
defaultSize: [400, 300],
sourcePath: 'apps/www/react/Grainient.tsx',
previewColor: '#7B2FFF',
component: Grainient,
},
]
<Telva reactComponents={REACT_COMPONENTS} />