@portabletext/editor
Advanced tools
Comparing version 1.0.11 to 1.0.12
{ | ||
"name": "@portabletext/editor", | ||
"version": "1.0.11", | ||
"version": "1.0.12", | ||
"description": "Portable Text Editor made in React", | ||
@@ -49,20 +49,21 @@ "keywords": [ | ||
"lodash": "^4.17.21", | ||
"slate": "0.100.0", | ||
"slate-react": "0.101.0" | ||
"slate": "0.103.0", | ||
"slate-react": "0.107.1" | ||
}, | ||
"devDependencies": { | ||
"@jest/globals": "^29.7.0", | ||
"@playwright/test": "1.45.3", | ||
"@playwright/test": "1.46.0", | ||
"@portabletext/toolkit": "^2.0.15", | ||
"@sanity/block-tools": "^3.52.4", | ||
"@sanity/block-tools": "^3.53.0", | ||
"@sanity/diff-match-patch": "^3.1.1", | ||
"@sanity/eslint-config-i18n": "^1.1.0", | ||
"@sanity/eslint-config-studio": "^4.0.0", | ||
"@sanity/pkg-utils": "^6.10.8", | ||
"@sanity/schema": "^3.52.4", | ||
"@sanity/pkg-utils": "^6.10.9", | ||
"@sanity/schema": "^3.53.0", | ||
"@sanity/test": "0.0.1-alpha.1", | ||
"@sanity/types": "^3.52.4", | ||
"@sanity/types": "^3.53.0", | ||
"@sanity/ui": "^2.8.8", | ||
"@sanity/util": "^3.52.4", | ||
"@testing-library/react": "^13.4.0", | ||
"@sanity/util": "^3.53.0", | ||
"@testing-library/dom": "^10.4.0", | ||
"@testing-library/react": "^16.0.0", | ||
"@types/debug": "^4.1.5", | ||
@@ -76,5 +77,5 @@ "@types/express": "^4.17.21", | ||
"@types/react-dom": "^18.3.0", | ||
"@types/ws": "~8.5.11", | ||
"@typescript-eslint/eslint-plugin": "^7.17.0", | ||
"@typescript-eslint/parser": "^7.17.0", | ||
"@types/ws": "~8.5.12", | ||
"@typescript-eslint/eslint-plugin": "^8.0.1", | ||
"@typescript-eslint/parser": "^8.0.1", | ||
"@vitejs/plugin-react": "^4.3.1", | ||
@@ -90,3 +91,3 @@ "dotenv": "^16.4.5", | ||
"eslint-plugin-tsdoc": "^0.3.0", | ||
"eslint-plugin-unicorn": "^54.0.0", | ||
"eslint-plugin-unicorn": "^55.0.0", | ||
"eslint-plugin-unused-imports": "^4.0.1", | ||
@@ -96,3 +97,3 @@ "express": "^4.19.2", | ||
"jest": "^29.7.0", | ||
"jest-dev-server": "^9.0.2", | ||
"jest-dev-server": "^10.0.0", | ||
"jest-environment-jsdom": "^29.7.0", | ||
@@ -105,5 +106,5 @@ "jest-environment-node": "^29.7.0", | ||
"styled-components": "^6.1.12", | ||
"tsx": "^4.16.2", | ||
"tsx": "^4.16.5", | ||
"typescript": "5.5.4", | ||
"vite": "^4.5.3" | ||
"vite": "^5.3.5" | ||
}, | ||
@@ -135,4 +136,5 @@ "peerDependencies": { | ||
"test:e2e": "jest --config=e2e-tests/e2e.config.cjs", | ||
"test:e2e:watch": "jest --config=e2e-tests/e2e.config.cjs --watch", | ||
"test:watch": "jest --watch" | ||
} | ||
} |
// This utils are inspired from https://github.dev/mwood23/slate-test-utils/blob/master/src/buildTestHarness.tsx | ||
import {act, fireEvent, type render} from '@testing-library/react' | ||
import {fireEvent, type render} from '@testing-library/react' | ||
import {parseHotkey} from 'is-hotkey-esm' | ||
import {act} from 'react' | ||
export async function triggerKeyboardEvent(hotkey: string, element: Element): Promise<void> { | ||
return act(async () => { | ||
const eventProps = parseHotkey(hotkey) | ||
const values = hotkey.split('+') | ||
const eventProps = parseHotkey(hotkey) | ||
const values = hotkey.split('+') | ||
fireEvent( | ||
element, | ||
new window.KeyboardEvent('keydown', { | ||
key: values[values.length - 1], | ||
code: `${eventProps.which}`, | ||
keyCode: eventProps.which, | ||
bubbles: true, | ||
...eventProps, | ||
}), | ||
) | ||
}) | ||
fireEvent( | ||
element, | ||
new window.KeyboardEvent('keydown', { | ||
key: values[values.length - 1], | ||
code: `${eventProps.which}`, | ||
keyCode: eventProps.which, | ||
bubbles: true, | ||
...eventProps, | ||
}), | ||
) | ||
} | ||
@@ -22,0 +21,0 @@ |
@@ -26,19 +26,34 @@ import {Editor, Element, Node, Transforms} from 'slate' | ||
const withNewKey = !isPreservingKeys(editor) || !('_key' in operation.properties) | ||
operation.properties = { | ||
...operation.properties, | ||
...(withNewKey ? {_key: keyGenerator()} : {}), | ||
} | ||
apply({ | ||
...operation, | ||
properties: { | ||
...operation.properties, | ||
...(withNewKey ? {_key: keyGenerator()} : {}), | ||
}, | ||
}) | ||
return | ||
} | ||
if (operation.type === 'insert_node') { | ||
// Must be given a new key or adding/removing marks while typing gets in trouble (duped keys)! | ||
const withNewKey = !isPreservingKeys(editor) || !('_key' in operation.node) | ||
if (!Editor.isEditor(operation.node)) { | ||
operation.node = { | ||
...operation.node, | ||
...(withNewKey ? {_key: keyGenerator()} : {}), | ||
} | ||
apply({ | ||
...operation, | ||
node: { | ||
...operation.node, | ||
...(withNewKey ? {_key: keyGenerator()} : {}), | ||
}, | ||
}) | ||
return | ||
} | ||
} | ||
apply(operation) | ||
} | ||
editor.normalizeNode = (entry) => { | ||
@@ -45,0 +60,0 @@ const [node, path] = entry |
@@ -9,2 +9,3 @@ /* eslint-disable max-statements */ | ||
import {isPortableTextBlock, isPortableTextSpan} from '@portabletext/toolkit' | ||
import {isEqual, uniq} from 'lodash' | ||
@@ -22,2 +23,3 @@ import {type Subject} from 'rxjs' | ||
import {EMPTY_MARKS} from '../../utils/values' | ||
import {withoutPreserveKeys} from '../../utils/withPreserveKeys' | ||
@@ -235,4 +237,4 @@ const debug = debugWithName('plugin:withPortableTextMarkModel') | ||
// Special hook before inserting text at the end of an annotation. | ||
editor.apply = (op) => { | ||
// Special hook before inserting text at the end of an annotation. | ||
if (op.type === 'insert_text') { | ||
@@ -279,2 +281,50 @@ const {selection} = editor | ||
} | ||
if (op.type === 'remove_text') { | ||
const nodeEntry = Array.from( | ||
Editor.nodes(editor, { | ||
mode: 'lowest', | ||
at: {path: op.path, offset: op.offset}, | ||
match: (n) => n._type === types.span.name, | ||
voids: false, | ||
}), | ||
)[0] | ||
const node = nodeEntry[0] | ||
const blockEntry = Editor.node(editor, Path.parent(op.path)) | ||
const block = blockEntry[0] | ||
if (node && isPortableTextSpan(node) && block && isPortableTextBlock(block)) { | ||
const markDefs = block.markDefs ?? [] | ||
const nodeHasAnnotations = (node.marks ?? []).some((mark) => | ||
markDefs.find((markDef) => markDef._key === mark), | ||
) | ||
const deletingPartOfTheNode = op.offset !== 0 | ||
const deletingFromTheEnd = op.offset + op.text.length === node.text.length | ||
if (nodeHasAnnotations && deletingPartOfTheNode && deletingFromTheEnd) { | ||
/** | ||
* If all of these conditions match then override the ordinary | ||
* `remove_text` operation and turn it into `split_nodes` followed | ||
* by `remove_nodes`. This is so if the operation can be properly | ||
* undone. Undoing a `remove_text` results in an `insert_text` and | ||
* we want to bail out of that in this exact scenario to make sure | ||
* the inserted text is annotated. (See custom logic regarding | ||
* `insert_text`) | ||
*/ | ||
Editor.withoutNormalizing(editor, () => { | ||
withoutPreserveKeys(editor, () => { | ||
Transforms.splitNodes(editor, { | ||
match: Text.isText, | ||
at: {path: op.path, offset: op.offset}, | ||
}) | ||
}) | ||
Transforms.removeNodes(editor, {at: Path.next(op.path)}) | ||
}) | ||
editor.onChange() | ||
return | ||
} | ||
} | ||
} | ||
apply(op) | ||
@@ -281,0 +331,0 @@ } |
@@ -149,2 +149,4 @@ /** | ||
}) | ||
const reversedOperations = transformedOperations.map(Operation.inverse).reverse() | ||
try { | ||
@@ -154,9 +156,5 @@ Editor.withoutNormalizing(editor, () => { | ||
withoutSaving(editor, () => { | ||
transformedOperations | ||
.map(Operation.inverse) | ||
.reverse() | ||
// eslint-disable-next-line max-nested-callbacks | ||
.forEach((op) => { | ||
editor.apply(op) | ||
}) | ||
reversedOperations.forEach((op) => { | ||
editor.apply(op) | ||
}) | ||
}) | ||
@@ -163,0 +161,0 @@ }) |
@@ -104,2 +104,3 @@ import {describe, expect, it} from '@jest/globals' | ||
) | ||
expect(result).toMatchInlineSnapshot(` | ||
@@ -106,0 +107,0 @@ Array [ |
@@ -12,4 +12,11 @@ import {type Editor} from 'slate' | ||
export function withoutPreserveKeys(editor: Editor, fn: () => void): void { | ||
const prev = isPreservingKeys(editor) | ||
PRESERVE_KEYS.set(editor, false) | ||
fn() | ||
PRESERVE_KEYS.set(editor, prev) | ||
} | ||
export function isPreservingKeys(editor: Editor): boolean | undefined { | ||
return PRESERVE_KEYS.get(editor) | ||
} |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
2687360
28852
52
+ Addedslate@0.103.0(transitive)
+ Addedslate-react@0.107.1(transitive)
- Removedslate@0.100.0(transitive)
- Removedslate-react@0.101.0(transitive)
Updatedslate@0.103.0
Updatedslate-react@0.107.1