@portabletext/editor 3.2.6 → 3.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/_chunks-es/use-editor.js +8 -4
- package/lib/_chunks-es/use-editor.js.map +1 -1
- package/lib/index.js +130 -67
- package/lib/index.js.map +1 -1
- package/package.json +2 -1
- package/src/behaviors/behavior.abstract.deserialize.ts +68 -84
- package/src/converters/converter.text-markdown.ts +67 -0
- package/src/converters/converters.core.ts +2 -0
- package/src/editor/PortableTextEditor.tsx +3 -1
- package/src/editor/plugins/createWithPatches.ts +4 -2
- package/src/editor/sync-machine.ts +8 -3
- package/src/internal-utils/apply-operation-to-portable-text.ts +3 -1
- package/src/internal-utils/event-position.ts +43 -4
- package/src/internal-utils/global-scope.ts +12 -4
- package/src/priority/priority.sort.ts +3 -1
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import React, { createContext } from "react";
|
|
2
2
|
function getGlobalScope() {
|
|
3
|
-
if (typeof globalThis < "u")
|
|
4
|
-
|
|
5
|
-
if (typeof
|
|
6
|
-
|
|
3
|
+
if (typeof globalThis < "u")
|
|
4
|
+
return globalThis;
|
|
5
|
+
if (typeof window < "u")
|
|
6
|
+
return window;
|
|
7
|
+
if (typeof self < "u")
|
|
8
|
+
return self;
|
|
9
|
+
if (typeof global < "u")
|
|
10
|
+
return global;
|
|
7
11
|
throw new Error("@portabletext/editor: could not locate global scope");
|
|
8
12
|
}
|
|
9
13
|
const globalScope = getGlobalScope();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-editor.js","sources":["../../src/internal-utils/global-scope.ts","../../src/internal-utils/globally-scoped-context.ts","../../src/editor/editor-context.tsx","../../src/editor/use-editor.ts"],"sourcesContent":["/**\n * Gets the global scope instance in a given environment.\n *\n * The strategy is to return the most modern, and if not, the most common:\n * - The `globalThis` variable is the modern approach to accessing the global scope\n * - The `window` variable is the global scope in a web browser\n * - The `self` variable is the global scope in workers and others\n * - The `global` variable is the global scope in Node.js\n */\nfunction getGlobalScope() {\n if (typeof globalThis !== 'undefined') return globalThis\n if (typeof window !== 'undefined') return window\n if (typeof self !== 'undefined') return self\n if (typeof global !== 'undefined') return global\n\n throw new Error('@portabletext/editor: could not locate global scope')\n}\n\nexport const globalScope = getGlobalScope() as any\n","import {createContext, type Context} from 'react'\nimport {globalScope} from './global-scope'\n\n/**\n * As `@portabletext/editor` is declared as a dependency, and may be\n * duplicated, sometimes across major versions it's critical that vital\n * React Contexts are shared even when there is a duplicate.\n *\n * We have to support a Sanity Plugin being able to call hooks like\n * `useEditor`, and then read the context setup by `sanity`, which calls\n * `EditorProvider`, even if the provider and hook are different instances in\n * memory.\n *\n * For this reason it's vital that all changes to globally scoped providers\n * remain fully backwards compatible.\n */\nexport function createGloballyScopedContext<\n ContextType,\n const T extends ContextType = ContextType,\n>(\n /**\n * Enforce that all Symbol.for keys used for globally scoped contexts have a predictable prefix\n */\n key: `@portabletext/editor/context/${string}`,\n defaultValue: T,\n): Context<ContextType> {\n const symbol = Symbol.for(key)\n\n /**\n * Prevent errors about re-renders on React SSR on Next.js App Router\n */\n if (typeof document === 'undefined') {\n return createContext<ContextType>(defaultValue)\n }\n\n globalScope[symbol] = globalScope[symbol] ?? createContext<T>(defaultValue)\n\n return globalScope[symbol]\n}\n","import type {Editor} from '../editor'\nimport {createGloballyScopedContext} from '../internal-utils/globally-scoped-context'\n\nexport const EditorContext = createGloballyScopedContext<Editor | null>(\n '@portabletext/editor/context/editor',\n null,\n)\n","import React from 'react'\nimport {EditorContext} from './editor-context'\n\n/**\n * @public\n * Get the current editor context from the `EditorProvider`.\n * Must be used inside the `EditorProvider` component.\n * @returns The current editor object.\n * @example\n * ```tsx\n * import { useEditor } from '@portabletext/editor'\n *\n * function MyComponent() {\n * const editor = useEditor()\n * }\n * ```\n * @group Hooks\n */\nexport function useEditor() {\n const editor = React.useContext(EditorContext)\n\n if (!editor) {\n throw new Error('No Editor set. Use EditorProvider to set one.')\n }\n\n return editor\n}\n"],"names":["getGlobalScope","globalThis","window","self","global","Error","globalScope","createGloballyScopedContext","key","defaultValue","symbol","Symbol","for","document","createContext","EditorContext","useEditor","editor","React","useContext"],"mappings":";AASA,SAASA,iBAAiB;AACxB,MAAI,OAAOC,aAAe,
|
|
1
|
+
{"version":3,"file":"use-editor.js","sources":["../../src/internal-utils/global-scope.ts","../../src/internal-utils/globally-scoped-context.ts","../../src/editor/editor-context.tsx","../../src/editor/use-editor.ts"],"sourcesContent":["/**\n * Gets the global scope instance in a given environment.\n *\n * The strategy is to return the most modern, and if not, the most common:\n * - The `globalThis` variable is the modern approach to accessing the global scope\n * - The `window` variable is the global scope in a web browser\n * - The `self` variable is the global scope in workers and others\n * - The `global` variable is the global scope in Node.js\n */\nfunction getGlobalScope() {\n if (typeof globalThis !== 'undefined') {\n return globalThis\n }\n if (typeof window !== 'undefined') {\n return window\n }\n if (typeof self !== 'undefined') {\n return self\n }\n if (typeof global !== 'undefined') {\n return global\n }\n\n throw new Error('@portabletext/editor: could not locate global scope')\n}\n\nexport const globalScope = getGlobalScope() as any\n","import {createContext, type Context} from 'react'\nimport {globalScope} from './global-scope'\n\n/**\n * As `@portabletext/editor` is declared as a dependency, and may be\n * duplicated, sometimes across major versions it's critical that vital\n * React Contexts are shared even when there is a duplicate.\n *\n * We have to support a Sanity Plugin being able to call hooks like\n * `useEditor`, and then read the context setup by `sanity`, which calls\n * `EditorProvider`, even if the provider and hook are different instances in\n * memory.\n *\n * For this reason it's vital that all changes to globally scoped providers\n * remain fully backwards compatible.\n */\nexport function createGloballyScopedContext<\n ContextType,\n const T extends ContextType = ContextType,\n>(\n /**\n * Enforce that all Symbol.for keys used for globally scoped contexts have a predictable prefix\n */\n key: `@portabletext/editor/context/${string}`,\n defaultValue: T,\n): Context<ContextType> {\n const symbol = Symbol.for(key)\n\n /**\n * Prevent errors about re-renders on React SSR on Next.js App Router\n */\n if (typeof document === 'undefined') {\n return createContext<ContextType>(defaultValue)\n }\n\n globalScope[symbol] = globalScope[symbol] ?? createContext<T>(defaultValue)\n\n return globalScope[symbol]\n}\n","import type {Editor} from '../editor'\nimport {createGloballyScopedContext} from '../internal-utils/globally-scoped-context'\n\nexport const EditorContext = createGloballyScopedContext<Editor | null>(\n '@portabletext/editor/context/editor',\n null,\n)\n","import React from 'react'\nimport {EditorContext} from './editor-context'\n\n/**\n * @public\n * Get the current editor context from the `EditorProvider`.\n * Must be used inside the `EditorProvider` component.\n * @returns The current editor object.\n * @example\n * ```tsx\n * import { useEditor } from '@portabletext/editor'\n *\n * function MyComponent() {\n * const editor = useEditor()\n * }\n * ```\n * @group Hooks\n */\nexport function useEditor() {\n const editor = React.useContext(EditorContext)\n\n if (!editor) {\n throw new Error('No Editor set. Use EditorProvider to set one.')\n }\n\n return editor\n}\n"],"names":["getGlobalScope","globalThis","window","self","global","Error","globalScope","createGloballyScopedContext","key","defaultValue","symbol","Symbol","for","document","createContext","EditorContext","useEditor","editor","React","useContext"],"mappings":";AASA,SAASA,iBAAiB;AACxB,MAAI,OAAOC,aAAe;AACxB,WAAOA;AAET,MAAI,OAAOC,SAAW;AACpB,WAAOA;AAET,MAAI,OAAOC,OAAS;AAClB,WAAOA;AAET,MAAI,OAAOC,SAAW;AACpB,WAAOA;AAGT,QAAM,IAAIC,MAAM,qDAAqD;AACvE;AAEO,MAAMC,cAAcN,eAAAA;ACVpB,SAASO,4BAOdC,KACAC,cACsB;AACtB,QAAMC,SAASC,OAAOC,IAAIJ,GAAG;AAK7B,SAAI,OAAOK,WAAa,MACfC,cAA2BL,YAAY,KAGhDH,YAAYI,MAAM,IAAIJ,YAAYI,MAAM,KAAKI,cAAiBL,YAAY,GAEnEH,YAAYI,MAAM;AAC3B;ACnCO,MAAMK,gBAAgBR,4BAC3B,uCACA,IACF;ACYO,SAAAS,YAAA;AACL,QAAAC,SAAeC,MAAKC,WAAYJ,aAAa;AAE7C,MAAI,CAACE;AACH,UAAM,IAAIZ,MAAM,+CAA+C;AAChE,SAEMY;AAAM;"}
|
package/lib/index.js
CHANGED
|
@@ -19,6 +19,7 @@ import { setup, fromCallback, assign, and, enqueueActions, emit, assertEvent, ra
|
|
|
19
19
|
import { compileSchemaDefinitionToPortableTextMemberSchemaTypes, createPortableTextMemberSchemaTypes, portableTextMemberSchemaTypesToSchema } from "@portabletext/sanity-bridge";
|
|
20
20
|
import { htmlToBlocks } from "@portabletext/block-tools";
|
|
21
21
|
import { toHTML } from "@portabletext/to-html";
|
|
22
|
+
import { markdownToPortableText, portableTextToMarkdown } from "@portabletext/markdown";
|
|
22
23
|
import { Schema } from "@sanity/schema";
|
|
23
24
|
import flatten from "lodash/flatten.js";
|
|
24
25
|
import { set, applyAll, unset, insert, setIfMissing, diffMatchPatch as diffMatchPatch$1 } from "@portabletext/patches";
|
|
@@ -442,7 +443,15 @@ function getEventNode({
|
|
|
442
443
|
slateEditor,
|
|
443
444
|
event
|
|
444
445
|
}) {
|
|
445
|
-
|
|
446
|
+
if (!DOMEditor.hasTarget(slateEditor, event.target))
|
|
447
|
+
return;
|
|
448
|
+
let node;
|
|
449
|
+
try {
|
|
450
|
+
node = DOMEditor.toSlateNode(slateEditor, event.target);
|
|
451
|
+
} catch (error) {
|
|
452
|
+
console.error(error);
|
|
453
|
+
}
|
|
454
|
+
return node;
|
|
446
455
|
}
|
|
447
456
|
function getEventPositionBlock({
|
|
448
457
|
node,
|
|
@@ -454,7 +463,15 @@ function getEventPositionBlock({
|
|
|
454
463
|
});
|
|
455
464
|
if (!firstBlock)
|
|
456
465
|
return;
|
|
457
|
-
|
|
466
|
+
let firstBlockElement;
|
|
467
|
+
try {
|
|
468
|
+
firstBlockElement = DOMEditor.toDOMNode(slateEditor, firstBlock);
|
|
469
|
+
} catch (error) {
|
|
470
|
+
console.error(error);
|
|
471
|
+
}
|
|
472
|
+
if (!firstBlockElement)
|
|
473
|
+
return;
|
|
474
|
+
const firstBlockRect = firstBlockElement.getBoundingClientRect();
|
|
458
475
|
if (event.pageY < firstBlockRect.top)
|
|
459
476
|
return "start";
|
|
460
477
|
const [lastBlock] = getLastBlock({
|
|
@@ -462,10 +479,26 @@ function getEventPositionBlock({
|
|
|
462
479
|
});
|
|
463
480
|
if (!lastBlock)
|
|
464
481
|
return;
|
|
465
|
-
|
|
482
|
+
let lastBlockElement;
|
|
483
|
+
try {
|
|
484
|
+
lastBlockElement = DOMEditor.toDOMNode(slateEditor, lastBlock);
|
|
485
|
+
} catch (error) {
|
|
486
|
+
console.error(error);
|
|
487
|
+
}
|
|
488
|
+
if (!lastBlockElement)
|
|
489
|
+
return;
|
|
490
|
+
const lastBlockRef = lastBlockElement.getBoundingClientRect();
|
|
466
491
|
if (event.pageY > lastBlockRef.bottom)
|
|
467
492
|
return "end";
|
|
468
|
-
|
|
493
|
+
let element;
|
|
494
|
+
try {
|
|
495
|
+
element = DOMEditor.toDOMNode(slateEditor, node);
|
|
496
|
+
} catch (error) {
|
|
497
|
+
console.error(error);
|
|
498
|
+
}
|
|
499
|
+
if (!element)
|
|
500
|
+
return;
|
|
501
|
+
const elementRect = element.getBoundingClientRect(), top = elementRect.top, height = elementRect.height;
|
|
469
502
|
return Math.abs(top - event.pageY) < height / 2 ? "start" : "end";
|
|
470
503
|
}
|
|
471
504
|
function getEventSelection({
|
|
@@ -2733,6 +2766,57 @@ function createConverterTextHtml(legacySchema) {
|
|
|
2733
2766
|
}
|
|
2734
2767
|
};
|
|
2735
2768
|
}
|
|
2769
|
+
const converterTextMarkdown = {
|
|
2770
|
+
mimeType: "text/markdown",
|
|
2771
|
+
serialize: ({
|
|
2772
|
+
snapshot,
|
|
2773
|
+
event
|
|
2774
|
+
}) => {
|
|
2775
|
+
if (!snapshot.context.selection)
|
|
2776
|
+
return {
|
|
2777
|
+
type: "serialization.failure",
|
|
2778
|
+
mimeType: "text/markdown",
|
|
2779
|
+
reason: "No selection",
|
|
2780
|
+
originEvent: event.originEvent
|
|
2781
|
+
};
|
|
2782
|
+
const blocks = getSelectedValue(snapshot);
|
|
2783
|
+
return {
|
|
2784
|
+
type: "serialization.success",
|
|
2785
|
+
data: portableTextToMarkdown(blocks),
|
|
2786
|
+
mimeType: "text/markdown",
|
|
2787
|
+
originEvent: event.originEvent
|
|
2788
|
+
};
|
|
2789
|
+
},
|
|
2790
|
+
deserialize: ({
|
|
2791
|
+
snapshot,
|
|
2792
|
+
event
|
|
2793
|
+
}) => {
|
|
2794
|
+
const parsedBlocks = markdownToPortableText(event.data, {
|
|
2795
|
+
keyGenerator: snapshot.context.keyGenerator,
|
|
2796
|
+
schema: snapshot.context.schema
|
|
2797
|
+
}).flatMap((block) => {
|
|
2798
|
+
const parsedBlock = parseBlock({
|
|
2799
|
+
context: snapshot.context,
|
|
2800
|
+
block,
|
|
2801
|
+
options: {
|
|
2802
|
+
normalize: !1,
|
|
2803
|
+
removeUnusedMarkDefs: !0,
|
|
2804
|
+
validateFields: !1
|
|
2805
|
+
}
|
|
2806
|
+
});
|
|
2807
|
+
return parsedBlock ? [parsedBlock] : [];
|
|
2808
|
+
});
|
|
2809
|
+
return parsedBlocks.length === 0 ? {
|
|
2810
|
+
type: "deserialization.failure",
|
|
2811
|
+
mimeType: "text/markdown",
|
|
2812
|
+
reason: "No blocks deserialized"
|
|
2813
|
+
} : {
|
|
2814
|
+
type: "deserialization.success",
|
|
2815
|
+
data: parsedBlocks,
|
|
2816
|
+
mimeType: "text/markdown"
|
|
2817
|
+
};
|
|
2818
|
+
}
|
|
2819
|
+
};
|
|
2736
2820
|
function createConverterTextPlain(legacySchema) {
|
|
2737
2821
|
return {
|
|
2738
2822
|
mimeType: "text/plain",
|
|
@@ -2796,7 +2880,7 @@ function escapeHtml(str) {
|
|
|
2796
2880
|
return String(str).replace(/[&<>"'`=/]/g, (s) => entityMap[s]);
|
|
2797
2881
|
}
|
|
2798
2882
|
function createCoreConverters(legacySchema) {
|
|
2799
|
-
return [converterJson, converterPortableText, createConverterTextHtml(legacySchema), createConverterTextPlain(legacySchema)];
|
|
2883
|
+
return [converterJson, converterPortableText, converterTextMarkdown, createConverterTextHtml(legacySchema), createConverterTextPlain(legacySchema)];
|
|
2800
2884
|
}
|
|
2801
2885
|
function compileType(rawType) {
|
|
2802
2886
|
return Schema.compile({
|
|
@@ -6581,7 +6665,8 @@ function applyOperationToPortableTextImmutable(context, root, operation) {
|
|
|
6581
6665
|
offset,
|
|
6582
6666
|
text
|
|
6583
6667
|
} = operation;
|
|
6584
|
-
if (text.length === 0)
|
|
6668
|
+
if (text.length === 0)
|
|
6669
|
+
return root;
|
|
6585
6670
|
const span = getSpan(context, root, path);
|
|
6586
6671
|
if (!span)
|
|
6587
6672
|
return root;
|
|
@@ -8606,41 +8691,37 @@ const abstractAnnotationBehaviors = [defineBehavior({
|
|
|
8606
8691
|
...event,
|
|
8607
8692
|
type: "delete"
|
|
8608
8693
|
})]]
|
|
8609
|
-
})],
|
|
8694
|
+
})], mimeTypePriority = ["application/x-portable-text", "application/json", "text/markdown", "text/html", "text/plain"];
|
|
8695
|
+
function getFirstAvailableData({
|
|
8696
|
+
dataTransfer,
|
|
8697
|
+
startAfter
|
|
8698
|
+
}) {
|
|
8699
|
+
const startIndex = startAfter ? mimeTypePriority.indexOf(startAfter) + 1 : 0;
|
|
8700
|
+
for (let index = startIndex; index < mimeTypePriority.length; index++) {
|
|
8701
|
+
const mimeType = mimeTypePriority.at(index);
|
|
8702
|
+
if (!mimeType)
|
|
8703
|
+
continue;
|
|
8704
|
+
const data = dataTransfer.getData(mimeType);
|
|
8705
|
+
if (data)
|
|
8706
|
+
return {
|
|
8707
|
+
mimeType,
|
|
8708
|
+
data
|
|
8709
|
+
};
|
|
8710
|
+
}
|
|
8711
|
+
}
|
|
8712
|
+
const abstractDeserializeBehaviors = [
|
|
8610
8713
|
defineBehavior({
|
|
8611
8714
|
on: "deserialize",
|
|
8612
8715
|
guard: ({
|
|
8613
8716
|
event
|
|
8614
8717
|
}) => {
|
|
8615
|
-
const
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
mimeType: "application/x-portable-text",
|
|
8620
|
-
data: portableText,
|
|
8621
|
-
originEvent: event.originEvent
|
|
8622
|
-
};
|
|
8623
|
-
const json = event.originEvent.originEvent.dataTransfer.getData("application/json");
|
|
8624
|
-
if (json)
|
|
8625
|
-
return {
|
|
8626
|
-
type: "deserialize.data",
|
|
8627
|
-
mimeType: "application/json",
|
|
8628
|
-
data: json,
|
|
8629
|
-
originEvent: event.originEvent
|
|
8630
|
-
};
|
|
8631
|
-
const html = event.originEvent.originEvent.dataTransfer.getData("text/html");
|
|
8632
|
-
if (html)
|
|
8633
|
-
return {
|
|
8634
|
-
type: "deserialize.data",
|
|
8635
|
-
mimeType: "text/html",
|
|
8636
|
-
data: html,
|
|
8637
|
-
originEvent: event.originEvent
|
|
8638
|
-
};
|
|
8639
|
-
const text = event.originEvent.originEvent.dataTransfer.getData("text/plain");
|
|
8640
|
-
return text ? {
|
|
8718
|
+
const availableData = getFirstAvailableData({
|
|
8719
|
+
dataTransfer: event.originEvent.originEvent.dataTransfer
|
|
8720
|
+
});
|
|
8721
|
+
return availableData ? {
|
|
8641
8722
|
type: "deserialize.data",
|
|
8642
|
-
mimeType:
|
|
8643
|
-
data:
|
|
8723
|
+
mimeType: availableData.mimeType,
|
|
8724
|
+
data: availableData.data,
|
|
8644
8725
|
originEvent: event.originEvent
|
|
8645
8726
|
} : !1;
|
|
8646
8727
|
},
|
|
@@ -8747,37 +8828,18 @@ const abstractAnnotationBehaviors = [defineBehavior({
|
|
|
8747
8828
|
guard: ({
|
|
8748
8829
|
event
|
|
8749
8830
|
}) => {
|
|
8750
|
-
if (event.mimeType === "
|
|
8751
|
-
|
|
8752
|
-
|
|
8753
|
-
|
|
8754
|
-
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
|
|
8758
|
-
|
|
8759
|
-
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
if (html)
|
|
8763
|
-
return {
|
|
8764
|
-
type: "deserialize.data",
|
|
8765
|
-
mimeType: "text/html",
|
|
8766
|
-
data: html,
|
|
8767
|
-
originEvent: event.originEvent
|
|
8768
|
-
};
|
|
8769
|
-
}
|
|
8770
|
-
if (event.mimeType === "text/html") {
|
|
8771
|
-
const text = event.originEvent.originEvent.dataTransfer.getData("text/plain");
|
|
8772
|
-
if (text)
|
|
8773
|
-
return {
|
|
8774
|
-
type: "deserialize.data",
|
|
8775
|
-
mimeType: "text/plain",
|
|
8776
|
-
data: text,
|
|
8777
|
-
originEvent: event.originEvent
|
|
8778
|
-
};
|
|
8779
|
-
}
|
|
8780
|
-
return !1;
|
|
8831
|
+
if (event.mimeType === "*/*")
|
|
8832
|
+
return !1;
|
|
8833
|
+
const availableData = getFirstAvailableData({
|
|
8834
|
+
dataTransfer: event.originEvent.originEvent.dataTransfer,
|
|
8835
|
+
startAfter: event.mimeType
|
|
8836
|
+
});
|
|
8837
|
+
return availableData ? {
|
|
8838
|
+
type: "deserialize.data",
|
|
8839
|
+
mimeType: availableData.mimeType,
|
|
8840
|
+
data: availableData.data,
|
|
8841
|
+
originEvent: event.originEvent
|
|
8842
|
+
} : !1;
|
|
8781
8843
|
},
|
|
8782
8844
|
actions: [(_, deserializeDataEvent) => [raise(deserializeDataEvent)]]
|
|
8783
8845
|
}),
|
|
@@ -12793,7 +12855,8 @@ class PortableTextEditor extends Component {
|
|
|
12793
12855
|
static insertBreak = (editor) => editor.editable?.insertBreak();
|
|
12794
12856
|
static isVoid = (editor, element) => editor.editable?.isVoid(element);
|
|
12795
12857
|
static isObjectPath = (_editor, path) => {
|
|
12796
|
-
if (!path || !Array.isArray(path))
|
|
12858
|
+
if (!path || !Array.isArray(path))
|
|
12859
|
+
return !1;
|
|
12797
12860
|
const isChildObjectEditPath = path.length > 3 && path[1] === "children";
|
|
12798
12861
|
return path.length > 1 && path[1] !== "children" || isChildObjectEditPath;
|
|
12799
12862
|
};
|