Open davisg123 opened 2 years ago
Facing the exact same issue.
–– EDIT ––
Adding an if
clause to the onKeyDown
handler of <Editable />
helps with the behaviour:
<Editable
renderElement={renderElement}
renderLeaf={renderLeaf}
onKeyDown={(event) => {
// disable key down events if max signs is reached
// (except nav keys such as Tab/Arrows)
if (
config.maxSigns &&
getCharCount(editor.children) >= config.maxSigns &&
!NAV_KEYS.concat('Backspace').includes(event.key)
) {
event.preventDefault()
return false
}
}}
/>
Same issue. Only number input is limited.
Facing the exact same issue.
–– EDIT ––
Adding an
if
clause to theonKeyDown
handler of<Editable />
helps with the behaviour:<Editable renderElement={renderElement} renderLeaf={renderLeaf} onKeyDown={(event) => { // disable key down events if max signs is reached // (except nav keys such as Tab/Arrows) if ( config.maxSigns && getCharCount(editor.children) >= config.maxSigns && !NAV_KEYS.concat('Backspace').includes(event.key) ) { event.preventDefault() return false } }} />
this worked perfectly, thanks a lot @annatraussnig! 🙌
Facing the exact same issue.
–– EDIT ––
Adding an
if
clause to theonKeyDown
handler of<Editable />
helps with the behaviour:<Editable renderElement={renderElement} renderLeaf={renderLeaf} onKeyDown={(event) => { // disable key down events if max signs is reached // (except nav keys such as Tab/Arrows) if ( config.maxSigns && getCharCount(editor.children) >= config.maxSigns && !NAV_KEYS.concat('Backspace').includes(event.key) ) { event.preventDefault() return false } }} />
If it is a Chinese input method, it will make an error.
I have two more different approaches.
The first one is based on predicting the result:
<Editable
renderLeaf={renderLeaf}
renderElement={renderElement}
renderPlaceholder={renderPlaceholder}
placeholder={placeholder}
onDOMBeforeInput={(e) => {
if (
e.inputType !== 'insertText' &&
e.inputType !== 'insertFromPaste'
)
return
const input = e.data || e.dataTransfer?.getData('text/plain')
if (!input) return
const sel = [
editor.selection?.anchor?.offset || 0,
editor.selection?.focus?.offset || 0
].sort()
const text = serializeString(editor.children)
const newText =
text.substring(0, sel[0]) + input + text.substring(sel[1])
if (newText.length > config.maxSigns) e.preventDefault()
}}
/>
It also works with pasting some (not every 🤷♂️) clipboard data.
The second one I made for formatting on the go, so it may be overkill for just limiting the characters length.
In the editor:
const editor = useMemo(
() =>
withFormatting(
withHistory(withReact(createEditor())),
(s) =>
s.trimStart().replace(/\s+/gi, ' ') // some formatting
.substring(0, config.maxSigns) // limit char length
),
[]
)
the plugin:
function withFormatting<T extends Editor>(editor: T, format?: FormatCb) {
const { insertText, deleteFragment, deleteBackward, deleteForward } = editor
const canFormat = typeof format === 'function'
if (!canFormat) return editor
editor.insertText = (textPart) => {
insertText(textPart)
const text = serializeString(currentNode(editor))
const sel = editor.selection?.anchor?.offset || text.length
const formattedText = format(text)
// replacing with formatted text
Transforms.insertText(editor, formattedText, {
at: changeSelectionOffset(editor.selection, [0, text.length])
})
const textDiff = formattedText.length - text.length
const newOffset = Math.min(sel + textDiff, formattedText.length)
editor.selection = changeSelectionOffset(editor.selection, newOffset)
}
editor.deleteFragment = (d) => {
deleteFragment(d)
editor.insertText('')
}
editor.deleteBackward = (d) => {
deleteBackward(d)
editor.insertText('')
}
editor.deleteForward = (d) => {
deleteForward(d)
editor.insertText('')
}
return editor
}
function currentNode<T extends Editor>({
children,
selection
}: T): Descendant[] {
if (!selection) return children
const path = selection?.anchor.path || [0, 0]
const url = path
.slice(0, path.length - 1)
.map((i) => `[${i}]`)
.join('.children')
const node = _.get(children, url)
return node?.children || children
}
function changeSelectionOffset(
selection: BaseSelection,
offsetOrArray: [number, number] | number
) {
const offset =
typeof offsetOrArray === 'number' ? [offsetOrArray] : offsetOrArray
return {
anchor: {
...selection.anchor,
offset: offset[0]
},
focus: {
...selection.focus,
offset: offset[1] || offset[0]
}
}
}
function serializeString (value?: Descendant[]){
return (value || []).map((n) => Node.string(n)).join()
}
not work while useing Chinese.....😮💨
Thx for the workarounds, but shouldn't this work correctly out of the box?
how can i get the NAV_KEYS variable?
Description We're attempting to limit the content to a maximum length by overriding
insertText
, but as of version 0.67 Slate will continue to display characters that are typed even though they don't exist in the node representation. This can lead to data lossRecording
https://user-images.githubusercontent.com/2341305/170587569-57485465-c84e-4f64-8a78-2d3946e80eb2.mov
Sandbox https://codesandbox.io/s/slate-character-limit-bug-rdursq?file=/index.js
Change slate-react to < 0.67 to see the desired behavior
Steps To reproduce the behavior:
insertText
Expectation The editor should not allow any characters to be typed
Environment
Context