Here was the best solution I could come up with. Feels pretty hacky and adds a small bit of latency. But, works well enough to create the desired effect reliably without issues.
As I create the Slate content based on the new text I am pasting, I inject a propert "inserted" as true. On the Slate render I pull properties and make them a class. There is a timeout that executes shortly after the insertion (based on the CSS transition that then quickly removes it. Finally, before I send the data to the DB, I make sure nothing with the "inserted" or "flash" properties are sent up by removing them from all words if found.
export const insertText = ({editor, text, inserted}) => {
const textArr = text.split('\n').filter(text => !!text.trim())
const texts = textArr.map(txt => ({inserted, type: 'paragraph', children: [{text: txt}]}))
Transforms.insertNodes(editor, texts)
setTimeout(() => {
const el = document.querySelector('.inserted')
if (el) {
const rect = el.getBoundingClientRect()
const isInViewport = rect.top >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight)
if (!isInViewport) {
el.style.scrollMargin = '100px'
document.querySelector('.inserted').scrollIntoView(true)
}
setTimeout(() => {
document.querySelectorAll('.inserted').forEach(el => el.classList.add('flash'))
setTimeout(() => {
document.querySelectorAll('.inserted').forEach(el => el.classList.remove('flash', 'inserted'))
},1500)
},300)
}
},300)
}
export const Element = props => {
// console.log('ELEMENT', {props})
const {attributes, children} = props
return <div className={`${attributes.inserted ? 'inserted' : ''}`}>{children}</div>
}
Elsewhere in the save to DB.
...
...
handleSaveContent(value ? value.map(({flash, inserted,...rest}) => rest) : value)
...
...
In the CSS
.flash {
animation: wordFlash 2s;
animation-timing-function: ease-in-out;
}