Skip to content

Instantly share code, notes, and snippets.

@steveruizok
Created August 17, 2023 17:24
Show Gist options
  • Save steveruizok/232e9bf621e3d2dfaebd4f198c7e69fc to your computer and use it in GitHub Desktop.
Save steveruizok/232e9bf621e3d2dfaebd4f198c7e69fc to your computer and use it in GitHub Desktop.
history scrubbing in tldraw
import '@tldraw/tldraw/tldraw.css'
import { RecordsDiff, TLRecord, Tldraw, useEditor } from '@tldraw/tldraw'
import { useEffect, useRef } from 'react'
export default function App() {
return (
<div className="tldraw__editor">
<Tldraw autoFocus>
<Slider />
</Tldraw>
</div>
)
}
function Slider() {
const diffs = useRef<RecordsDiff<TLRecord>[]>([])
const pointer = useRef(0)
const editor = useEditor()
const handleSliderChange = (e) => {
const events = diffs.current
const curr = pointer.current
const prevPct = curr / 10000
const next = e.currentTarget.value
const nextPct = next / 10000
const prevIndex = Math.ceil(prevPct * diffs.current.length)
const nextIndex = Math.ceil(nextPct * diffs.current.length)
if (nextPct === 1 && editor.instanceState.isReadonly) {
editor.updateInstanceState({ isReadonly: false })
} else if (nextPct < 1 && !editor.instanceState.isReadonly) {
editor.updateInstanceState({ isReadonly: true })
}
pointer.current = next
editor.store.mergeRemoteChanges(() => {
if (nextIndex > prevIndex) {
// console.log('redoing', prevIndex, nextIndex)
for (let i = prevIndex; i <= nextIndex; i++) {
const changes = events[i]
if (!changes) continue
Object.values(changes.added).forEach((record) => {
editor.store.put([record])
})
Object.values(changes.updated).forEach(([prev, next]) => {
editor.store.put([next])
})
Object.values(changes.removed).forEach((record) => {
editor.store.remove([record.id])
})
}
} else if (nextIndex < prevIndex) {
// console.log('undoing', prevIndex, nextIndex)
for (let i = prevIndex; i >= nextIndex; i--) {
const changes = events[i]
if (!changes) continue
Object.values(changes.added).forEach((record) => {
editor.store.remove([record.id])
})
Object.values(changes.updated).forEach(([prev, next]) => {
editor.store.put([prev])
})
Object.values(changes.removed).forEach((record) => {
editor.store.put([record])
})
}
}
})
}
useEffect(() => {
return editor.store.listen(({ changes }) => diffs.current.push(changes), {
source: 'user',
scope: 'document',
})
}, [editor])
return (
<input
type="range"
defaultValue="10000"
onChange={handleSliderChange}
style={{
position: 'absolute',
top: 64,
left: 8,
width: 300,
zIndex: 999,
}}
min="0"
max="10000"
/>
)
}
@KesavarajaRK
Copy link

Codepen available for this ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment