@portabletext/plugin-character-pair-decorator 2.0.0 → 3.0.0

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/README.md CHANGED
@@ -23,8 +23,8 @@ function App() {
23
23
  >
24
24
  <PortableTextEditable />
25
25
  <CharacterPairDecoratorPlugin
26
- decorator={({schema}) =>
27
- schema.decorators.find((d) => d.name === 'italic')?.name
26
+ decorator={({context}) =>
27
+ context.schema.decorators.find((d) => d.name === 'italic')?.name
28
28
  }
29
29
  pair={{char: '#', amount: 1}}
30
30
  />
package/dist/index.d.ts CHANGED
@@ -1,10 +1,14 @@
1
- import type {EditorSchema} from '@portabletext/editor'
1
+ import type {EditorContext} from '@portabletext/editor'
2
2
 
3
3
  /**
4
- * @beta
4
+ * @public
5
5
  */
6
- export declare function CharacterPairDecoratorPlugin(config: {
7
- decorator: ({schema}: {schema: EditorSchema}) => string | undefined
6
+ export declare function CharacterPairDecoratorPlugin(props: {
7
+ decorator: ({
8
+ context,
9
+ }: {
10
+ context: Pick<EditorContext, 'schema'>
11
+ }) => string | undefined
8
12
  pair: {
9
13
  char: string
10
14
  amount: number
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { c } from "react/compiler-runtime";
2
2
  import { useEditor } from "@portabletext/editor";
3
- import { defineBehavior, execute, effect, forward } from "@portabletext/editor/behaviors";
3
+ import { defineBehavior, forward, raise, effect } from "@portabletext/editor/behaviors";
4
4
  import * as utils from "@portabletext/editor/utils";
5
5
  import { useActorRef } from "@xstate/react";
6
6
  import { isDeepEqual } from "remeda";
@@ -22,7 +22,9 @@ function createCharacterPairDecoratorBehavior(config) {
22
22
  if (config.pair.amount < 1)
23
23
  return !1;
24
24
  const decorator = config.decorator({
25
- schema: snapshot.context.schema
25
+ context: {
26
+ schema: snapshot.context.schema
27
+ }
26
28
  });
27
29
  if (decorator === void 0)
28
30
  return !1;
@@ -102,14 +104,14 @@ function createCharacterPairDecoratorBehavior(config) {
102
104
  // Insert the text as usual in its own undo step
103
105
  ({
104
106
  event
105
- }) => [execute(event)],
107
+ }) => [forward(event)],
106
108
  (_, {
107
109
  prefixOffsets,
108
110
  suffixOffsets,
109
111
  decorator
110
112
  }) => [
111
113
  // Decorate the text between the prefix and suffix
112
- execute({
114
+ raise({
113
115
  type: "decorator.add",
114
116
  decorator,
115
117
  at: {
@@ -118,17 +120,17 @@ function createCharacterPairDecoratorBehavior(config) {
118
120
  }
119
121
  }),
120
122
  // Delete the suffix
121
- execute({
123
+ raise({
122
124
  type: "delete.text",
123
125
  at: suffixOffsets
124
126
  }),
125
127
  // Delete the prefix
126
- execute({
128
+ raise({
127
129
  type: "delete.text",
128
130
  at: prefixOffsets
129
131
  }),
130
132
  // Toggle the decorator off so the next inserted text isn't emphasized
131
- execute({
133
+ raise({
132
134
  type: "decorator.remove",
133
135
  decorator
134
136
  }),
@@ -142,16 +144,16 @@ function createCharacterPairDecoratorBehavior(config) {
142
144
  ]
143
145
  });
144
146
  }
145
- function CharacterPairDecoratorPlugin(config) {
147
+ function CharacterPairDecoratorPlugin(props) {
146
148
  const $ = c(4), editor = useEditor();
147
149
  let t0;
148
- return $[0] !== config.decorator || $[1] !== config.pair || $[2] !== editor ? (t0 = {
150
+ return $[0] !== editor || $[1] !== props.decorator || $[2] !== props.pair ? (t0 = {
149
151
  input: {
150
152
  editor,
151
- decorator: config.decorator,
152
- pair: config.pair
153
+ decorator: props.decorator,
154
+ pair: props.pair
153
155
  }
154
- }, $[0] = config.decorator, $[1] = config.pair, $[2] = editor, $[3] = t0) : t0 = $[3], useActorRef(decoratorPairMachine, t0), null;
156
+ }, $[0] = editor, $[1] = props.decorator, $[2] = props.pair, $[3] = t0) : t0 = $[3], useActorRef(decoratorPairMachine, t0), null;
155
157
  }
156
158
  const decorateListener = ({
157
159
  sendBack,
@@ -217,7 +219,7 @@ const decorateListener = ({
217
219
  }) => input.editor.registerBehavior({
218
220
  behavior: defineBehavior({
219
221
  on: "delete.backward",
220
- actions: [() => [execute({
222
+ actions: [() => [raise({
221
223
  type: "history.undo"
222
224
  }), effect(() => {
223
225
  sendBack({
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/regex.character-pair.ts","../src/behavior.character-pair-decorator.ts","../src/plugin.character-pair-decorator.ts"],"sourcesContent":["export function createCharacterPairRegex(char: string, amount: number) {\n // Negative lookbehind: Ensures that the matched sequence is not preceded by the same character\n const prePrefix = `(?<!\\\\${char})`\n\n // Repeats the character `amount` times\n const prefix = `\\\\${char}`.repeat(Math.max(amount, 1))\n\n // Negative lookahead: Ensures that the opening pair (**, *, etc.) is not followed by a space\n const postPrefix = `(?!\\\\s)`\n\n // Captures the content inside the pair\n const content = `([^${char}\\\\n]+?)`\n\n // Negative lookbehind: Ensures that the content is not followed by a space\n const preSuffix = `(?<!\\\\s)`\n\n // Repeats the character `amount` times\n const suffix = `\\\\${char}`.repeat(Math.max(amount, 1))\n\n // Negative lookahead: Ensures that the matched sequence is not followed by the same character\n const postSuffix = `(?!\\\\${char})`\n\n return `${prePrefix}${prefix}${postPrefix}${content}${preSuffix}${suffix}${postSuffix}`\n}\n","import type {BlockOffset, EditorSchema} from '@portabletext/editor'\nimport {defineBehavior, effect, execute} from '@portabletext/editor/behaviors'\nimport * as selectors from '@portabletext/editor/selectors'\nimport * as utils from '@portabletext/editor/utils'\nimport {createCharacterPairRegex} from './regex.character-pair'\n\nexport function createCharacterPairDecoratorBehavior(config: {\n decorator: ({schema}: {schema: EditorSchema}) => string | undefined\n pair: {char: string; amount: number}\n onDecorate: (offset: BlockOffset) => void\n}) {\n if (config.pair.amount < 1) {\n console.warn(\n `The amount of characters in the pair should be greater than 0`,\n )\n }\n\n const pairRegex = createCharacterPairRegex(\n config.pair.char,\n config.pair.amount,\n )\n const regEx = new RegExp(`(${pairRegex})$`)\n\n return defineBehavior({\n on: 'insert.text',\n guard: ({snapshot, event}) => {\n if (config.pair.amount < 1) {\n return false\n }\n\n const decorator = config.decorator({schema: snapshot.context.schema})\n\n if (decorator === undefined) {\n return false\n }\n\n const focusTextBlock = selectors.getFocusTextBlock(snapshot)\n const selectionStartPoint = selectors.getSelectionStartPoint(snapshot)\n const selectionStartOffset = selectionStartPoint\n ? utils.spanSelectionPointToBlockOffset({\n context: snapshot.context,\n selectionPoint: selectionStartPoint,\n })\n : undefined\n\n if (!focusTextBlock || !selectionStartOffset) {\n return false\n }\n\n const textBefore = selectors.getBlockTextBefore(snapshot)\n const newText = `${textBefore}${event.text}`\n const textToDecorate = newText.match(regEx)?.at(0)\n\n if (textToDecorate === undefined) {\n return false\n }\n\n const prefixOffsets = {\n anchor: {\n path: focusTextBlock.path,\n // Example: \"foo **bar**\".length - \"**bar**\".length = 4\n offset: newText.length - textToDecorate.length,\n },\n focus: {\n path: focusTextBlock.path,\n // Example: \"foo **bar**\".length - \"**bar**\".length + \"*\".length * 2 = 6\n offset:\n newText.length -\n textToDecorate.length +\n config.pair.char.length * config.pair.amount,\n },\n }\n\n const suffixOffsets = {\n anchor: {\n path: focusTextBlock.path,\n // Example: \"foo **bar*|\" (10) + \"*\".length - 2 = 9\n offset:\n selectionStartOffset.offset +\n event.text.length -\n config.pair.char.length * config.pair.amount,\n },\n focus: {\n path: focusTextBlock.path,\n // Example: \"foo **bar*|\" (10) + \"*\".length = 11\n offset: selectionStartOffset.offset + event.text.length,\n },\n }\n\n // If the prefix is more than one character, then we need to check if\n // there is an inline object inside it\n if (prefixOffsets.focus.offset - prefixOffsets.anchor.offset > 1) {\n const prefixSelection = utils.blockOffsetsToSelection({\n context: snapshot.context,\n offsets: prefixOffsets,\n })\n const inlineObjectBeforePrefixFocus = selectors.getPreviousInlineObject(\n {\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: prefixSelection\n ? {\n anchor: prefixSelection.focus,\n focus: prefixSelection.focus,\n }\n : null,\n },\n },\n )\n const inlineObjectBeforePrefixFocusOffset =\n inlineObjectBeforePrefixFocus\n ? utils.childSelectionPointToBlockOffset({\n context: snapshot.context,\n selectionPoint: {\n path: inlineObjectBeforePrefixFocus.path,\n offset: 0,\n },\n })\n : undefined\n\n if (\n inlineObjectBeforePrefixFocusOffset &&\n inlineObjectBeforePrefixFocusOffset.offset >\n prefixOffsets.anchor.offset &&\n inlineObjectBeforePrefixFocusOffset.offset <\n prefixOffsets.focus.offset\n ) {\n return false\n }\n }\n\n // If the suffix is more than one character, then we need to check if\n // there is an inline object inside it\n if (suffixOffsets.focus.offset - suffixOffsets.anchor.offset > 1) {\n const previousInlineObject = selectors.getPreviousInlineObject(snapshot)\n const previousInlineObjectOffset = previousInlineObject\n ? utils.childSelectionPointToBlockOffset({\n context: snapshot.context,\n selectionPoint: {\n path: previousInlineObject.path,\n offset: 0,\n },\n })\n : undefined\n\n if (\n previousInlineObjectOffset &&\n previousInlineObjectOffset.offset > suffixOffsets.anchor.offset &&\n previousInlineObjectOffset.offset < suffixOffsets.focus.offset\n ) {\n return false\n }\n }\n\n return {\n prefixOffsets,\n suffixOffsets,\n decorator,\n }\n },\n actions: [\n // Insert the text as usual in its own undo step\n ({event}) => [execute(event)],\n (_, {prefixOffsets, suffixOffsets, decorator}) => [\n // Decorate the text between the prefix and suffix\n execute({\n type: 'decorator.add',\n decorator,\n at: {\n anchor: prefixOffsets.focus,\n focus: suffixOffsets.anchor,\n },\n }),\n // Delete the suffix\n execute({\n type: 'delete.text',\n at: suffixOffsets,\n }),\n // Delete the prefix\n execute({\n type: 'delete.text',\n at: prefixOffsets,\n }),\n // Toggle the decorator off so the next inserted text isn't emphasized\n execute({\n type: 'decorator.remove',\n decorator,\n }),\n effect(() => {\n config.onDecorate({\n ...suffixOffsets.anchor,\n offset:\n suffixOffsets.anchor.offset -\n (prefixOffsets.focus.offset - prefixOffsets.anchor.offset),\n })\n }),\n ],\n ],\n })\n}\n","import type {BlockOffset, Editor, EditorSchema} from '@portabletext/editor'\nimport {useEditor} from '@portabletext/editor'\nimport {\n defineBehavior,\n effect,\n execute,\n forward,\n} from '@portabletext/editor/behaviors'\nimport * as utils from '@portabletext/editor/utils'\nimport {useActorRef} from '@xstate/react'\nimport {isDeepEqual} from 'remeda'\nimport {\n assign,\n fromCallback,\n setup,\n type AnyEventObject,\n type CallbackLogicFunction,\n} from 'xstate'\nimport {createCharacterPairDecoratorBehavior} from './behavior.character-pair-decorator'\n\n/**\n * @beta\n */\nexport function CharacterPairDecoratorPlugin(config: {\n decorator: ({schema}: {schema: EditorSchema}) => string | undefined\n pair: {char: string; amount: number}\n}) {\n const editor = useEditor()\n\n useActorRef(decoratorPairMachine, {\n input: {\n editor,\n decorator: config.decorator,\n pair: config.pair,\n },\n })\n\n return null\n}\n\ntype DecoratorPairEvent =\n | {\n type: 'decorator.add'\n blockOffset: BlockOffset\n }\n | {\n type: 'selection'\n blockOffsets?: {\n anchor: BlockOffset\n focus: BlockOffset\n }\n }\n | {\n type: 'delete.backward'\n }\n\nconst decorateListener: CallbackLogicFunction<\n AnyEventObject,\n DecoratorPairEvent,\n {\n decorator: ({schema}: {schema: EditorSchema}) => string | undefined\n editor: Editor\n pair: {char: string; amount: number}\n }\n> = ({sendBack, input}) => {\n const unregister = input.editor.registerBehavior({\n behavior: createCharacterPairDecoratorBehavior({\n decorator: input.decorator,\n pair: input.pair,\n onDecorate: (offset) => {\n sendBack({type: 'decorator.add', blockOffset: offset})\n },\n }),\n })\n\n return unregister\n}\n\nconst selectionListenerCallback: CallbackLogicFunction<\n AnyEventObject,\n DecoratorPairEvent,\n {editor: Editor}\n> = ({sendBack, input}) => {\n const unregister = input.editor.registerBehavior({\n behavior: defineBehavior({\n on: 'select',\n guard: ({snapshot, event}) => {\n if (!event.at) {\n return {blockOffsets: undefined}\n }\n\n const anchor = utils.spanSelectionPointToBlockOffset({\n context: snapshot.context,\n selectionPoint: event.at.anchor,\n })\n const focus = utils.spanSelectionPointToBlockOffset({\n context: snapshot.context,\n selectionPoint: event.at.focus,\n })\n\n if (!anchor || !focus) {\n return {blockOffsets: undefined}\n }\n\n return {\n blockOffsets: {\n anchor,\n focus,\n },\n }\n },\n actions: [\n ({event}, {blockOffsets}) => [\n {\n type: 'effect',\n effect: () => {\n sendBack({type: 'selection', blockOffsets})\n },\n },\n forward(event),\n ],\n ],\n }),\n })\n\n return unregister\n}\n\nconst deleteBackwardListenerCallback: CallbackLogicFunction<\n AnyEventObject,\n DecoratorPairEvent,\n {editor: Editor}\n> = ({sendBack, input}) => {\n const unregister = input.editor.registerBehavior({\n behavior: defineBehavior({\n on: 'delete.backward',\n actions: [\n () => [\n execute({\n type: 'history.undo',\n }),\n effect(() => {\n sendBack({type: 'delete.backward'})\n }),\n ],\n ],\n }),\n })\n\n return unregister\n}\n\nconst decoratorPairMachine = setup({\n types: {\n context: {} as {\n decorator: ({schema}: {schema: EditorSchema}) => string | undefined\n editor: Editor\n offsetAfterDecorator?: BlockOffset\n pair: {char: string; amount: number}\n },\n input: {} as {\n decorator: ({schema}: {schema: EditorSchema}) => string | undefined\n editor: Editor\n pair: {char: string; amount: number}\n },\n events: {} as DecoratorPairEvent,\n },\n actors: {\n 'decorate listener': fromCallback(decorateListener),\n 'delete.backward listener': fromCallback(deleteBackwardListenerCallback),\n 'selection listener': fromCallback(selectionListenerCallback),\n },\n}).createMachine({\n id: 'decorator pair',\n context: ({input}) => ({\n decorator: input.decorator,\n editor: input.editor,\n pair: input.pair,\n }),\n initial: 'idle',\n states: {\n 'idle': {\n invoke: [\n {\n src: 'decorate listener',\n input: ({context}) => ({\n decorator: context.decorator,\n editor: context.editor,\n pair: context.pair,\n }),\n },\n ],\n on: {\n 'decorator.add': {\n target: 'decorator added',\n actions: assign({\n offsetAfterDecorator: ({event}) => event.blockOffset,\n }),\n },\n },\n },\n 'decorator added': {\n exit: [\n assign({\n offsetAfterDecorator: undefined,\n }),\n ],\n invoke: [\n {\n src: 'selection listener',\n input: ({context}) => ({editor: context.editor}),\n },\n {\n src: 'delete.backward listener',\n input: ({context}) => ({editor: context.editor}),\n },\n ],\n on: {\n 'selection': {\n target: 'idle',\n guard: ({context, event}) => {\n const selectionChanged = !isDeepEqual(\n {\n anchor: context.offsetAfterDecorator,\n focus: context.offsetAfterDecorator,\n },\n event.blockOffsets,\n )\n\n return selectionChanged\n },\n },\n 'delete.backward': {\n target: 'idle',\n },\n },\n },\n },\n})\n"],"names":["createCharacterPairRegex","char","amount","prePrefix","prefix","repeat","Math","max","postPrefix","content","preSuffix","suffix","postSuffix","createCharacterPairDecoratorBehavior","config","pair","console","warn","pairRegex","regEx","RegExp","defineBehavior","on","guard","snapshot","event","decorator","schema","context","undefined","focusTextBlock","selectors","getFocusTextBlock","selectionStartPoint","getSelectionStartPoint","selectionStartOffset","utils","spanSelectionPointToBlockOffset","selectionPoint","newText","getBlockTextBefore","text","textToDecorate","match","at","prefixOffsets","anchor","path","offset","length","focus","suffixOffsets","prefixSelection","blockOffsetsToSelection","offsets","inlineObjectBeforePrefixFocus","getPreviousInlineObject","selection","inlineObjectBeforePrefixFocusOffset","childSelectionPointToBlockOffset","previousInlineObject","previousInlineObjectOffset","actions","execute","_","type","effect","onDecorate","CharacterPairDecoratorPlugin","$","_c","editor","useEditor","t0","input","useActorRef","decoratorPairMachine","decorateListener","sendBack","registerBehavior","behavior","blockOffset","selectionListenerCallback","blockOffsets","forward","deleteBackwardListenerCallback","setup","types","events","actors","fromCallback","createMachine","id","initial","states","invoke","src","target","assign","offsetAfterDecorator","exit","isDeepEqual"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/regex.character-pair.ts","../src/behavior.character-pair-decorator.ts","../src/plugin.character-pair-decorator.ts"],"sourcesContent":["export function createCharacterPairRegex(char: string, amount: number) {\n // Negative lookbehind: Ensures that the matched sequence is not preceded by the same character\n const prePrefix = `(?<!\\\\${char})`\n\n // Repeats the character `amount` times\n const prefix = `\\\\${char}`.repeat(Math.max(amount, 1))\n\n // Negative lookahead: Ensures that the opening pair (**, *, etc.) is not followed by a space\n const postPrefix = `(?!\\\\s)`\n\n // Captures the content inside the pair\n const content = `([^${char}\\\\n]+?)`\n\n // Negative lookbehind: Ensures that the content is not followed by a space\n const preSuffix = `(?<!\\\\s)`\n\n // Repeats the character `amount` times\n const suffix = `\\\\${char}`.repeat(Math.max(amount, 1))\n\n // Negative lookahead: Ensures that the matched sequence is not followed by the same character\n const postSuffix = `(?!\\\\${char})`\n\n return `${prePrefix}${prefix}${postPrefix}${content}${preSuffix}${suffix}${postSuffix}`\n}\n","import type {BlockOffset, EditorContext} from '@portabletext/editor'\nimport {\n defineBehavior,\n effect,\n forward,\n raise,\n} from '@portabletext/editor/behaviors'\nimport * as selectors from '@portabletext/editor/selectors'\nimport * as utils from '@portabletext/editor/utils'\nimport {createCharacterPairRegex} from './regex.character-pair'\n\nexport function createCharacterPairDecoratorBehavior(config: {\n decorator: ({\n context,\n }: {\n context: Pick<EditorContext, 'schema'>\n }) => string | undefined\n pair: {char: string; amount: number}\n onDecorate: (offset: BlockOffset) => void\n}) {\n if (config.pair.amount < 1) {\n console.warn(\n `The amount of characters in the pair should be greater than 0`,\n )\n }\n\n const pairRegex = createCharacterPairRegex(\n config.pair.char,\n config.pair.amount,\n )\n const regEx = new RegExp(`(${pairRegex})$`)\n\n return defineBehavior({\n on: 'insert.text',\n guard: ({snapshot, event}) => {\n if (config.pair.amount < 1) {\n return false\n }\n\n const decorator = config.decorator({\n context: {schema: snapshot.context.schema},\n })\n\n if (decorator === undefined) {\n return false\n }\n\n const focusTextBlock = selectors.getFocusTextBlock(snapshot)\n const selectionStartPoint = selectors.getSelectionStartPoint(snapshot)\n const selectionStartOffset = selectionStartPoint\n ? utils.spanSelectionPointToBlockOffset({\n context: snapshot.context,\n selectionPoint: selectionStartPoint,\n })\n : undefined\n\n if (!focusTextBlock || !selectionStartOffset) {\n return false\n }\n\n const textBefore = selectors.getBlockTextBefore(snapshot)\n const newText = `${textBefore}${event.text}`\n const textToDecorate = newText.match(regEx)?.at(0)\n\n if (textToDecorate === undefined) {\n return false\n }\n\n const prefixOffsets = {\n anchor: {\n path: focusTextBlock.path,\n // Example: \"foo **bar**\".length - \"**bar**\".length = 4\n offset: newText.length - textToDecorate.length,\n },\n focus: {\n path: focusTextBlock.path,\n // Example: \"foo **bar**\".length - \"**bar**\".length + \"*\".length * 2 = 6\n offset:\n newText.length -\n textToDecorate.length +\n config.pair.char.length * config.pair.amount,\n },\n }\n\n const suffixOffsets = {\n anchor: {\n path: focusTextBlock.path,\n // Example: \"foo **bar*|\" (10) + \"*\".length - 2 = 9\n offset:\n selectionStartOffset.offset +\n event.text.length -\n config.pair.char.length * config.pair.amount,\n },\n focus: {\n path: focusTextBlock.path,\n // Example: \"foo **bar*|\" (10) + \"*\".length = 11\n offset: selectionStartOffset.offset + event.text.length,\n },\n }\n\n // If the prefix is more than one character, then we need to check if\n // there is an inline object inside it\n if (prefixOffsets.focus.offset - prefixOffsets.anchor.offset > 1) {\n const prefixSelection = utils.blockOffsetsToSelection({\n context: snapshot.context,\n offsets: prefixOffsets,\n })\n const inlineObjectBeforePrefixFocus = selectors.getPreviousInlineObject(\n {\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: prefixSelection\n ? {\n anchor: prefixSelection.focus,\n focus: prefixSelection.focus,\n }\n : null,\n },\n },\n )\n const inlineObjectBeforePrefixFocusOffset =\n inlineObjectBeforePrefixFocus\n ? utils.childSelectionPointToBlockOffset({\n context: snapshot.context,\n selectionPoint: {\n path: inlineObjectBeforePrefixFocus.path,\n offset: 0,\n },\n })\n : undefined\n\n if (\n inlineObjectBeforePrefixFocusOffset &&\n inlineObjectBeforePrefixFocusOffset.offset >\n prefixOffsets.anchor.offset &&\n inlineObjectBeforePrefixFocusOffset.offset <\n prefixOffsets.focus.offset\n ) {\n return false\n }\n }\n\n // If the suffix is more than one character, then we need to check if\n // there is an inline object inside it\n if (suffixOffsets.focus.offset - suffixOffsets.anchor.offset > 1) {\n const previousInlineObject = selectors.getPreviousInlineObject(snapshot)\n const previousInlineObjectOffset = previousInlineObject\n ? utils.childSelectionPointToBlockOffset({\n context: snapshot.context,\n selectionPoint: {\n path: previousInlineObject.path,\n offset: 0,\n },\n })\n : undefined\n\n if (\n previousInlineObjectOffset &&\n previousInlineObjectOffset.offset > suffixOffsets.anchor.offset &&\n previousInlineObjectOffset.offset < suffixOffsets.focus.offset\n ) {\n return false\n }\n }\n\n return {\n prefixOffsets,\n suffixOffsets,\n decorator,\n }\n },\n actions: [\n // Insert the text as usual in its own undo step\n ({event}) => [forward(event)],\n (_, {prefixOffsets, suffixOffsets, decorator}) => [\n // Decorate the text between the prefix and suffix\n raise({\n type: 'decorator.add',\n decorator,\n at: {\n anchor: prefixOffsets.focus,\n focus: suffixOffsets.anchor,\n },\n }),\n // Delete the suffix\n raise({\n type: 'delete.text',\n at: suffixOffsets,\n }),\n // Delete the prefix\n raise({\n type: 'delete.text',\n at: prefixOffsets,\n }),\n // Toggle the decorator off so the next inserted text isn't emphasized\n raise({\n type: 'decorator.remove',\n decorator,\n }),\n effect(() => {\n config.onDecorate({\n ...suffixOffsets.anchor,\n offset:\n suffixOffsets.anchor.offset -\n (prefixOffsets.focus.offset - prefixOffsets.anchor.offset),\n })\n }),\n ],\n ],\n })\n}\n","import type {BlockOffset, Editor, EditorContext} from '@portabletext/editor'\nimport {useEditor} from '@portabletext/editor'\nimport {\n defineBehavior,\n effect,\n forward,\n raise,\n} from '@portabletext/editor/behaviors'\nimport * as utils from '@portabletext/editor/utils'\nimport {useActorRef} from '@xstate/react'\nimport {isDeepEqual} from 'remeda'\nimport {\n assign,\n fromCallback,\n setup,\n type AnyEventObject,\n type CallbackLogicFunction,\n} from 'xstate'\nimport {createCharacterPairDecoratorBehavior} from './behavior.character-pair-decorator'\n\n/**\n * @public\n */\nexport function CharacterPairDecoratorPlugin(props: {\n decorator: ({\n context,\n }: {\n context: Pick<EditorContext, 'schema'>\n }) => string | undefined\n pair: {char: string; amount: number}\n}) {\n const editor = useEditor()\n\n useActorRef(decoratorPairMachine, {\n input: {\n editor,\n decorator: props.decorator,\n pair: props.pair,\n },\n })\n\n return null\n}\n\ntype DecoratorPairEvent =\n | {\n type: 'decorator.add'\n blockOffset: BlockOffset\n }\n | {\n type: 'selection'\n blockOffsets?: {\n anchor: BlockOffset\n focus: BlockOffset\n }\n }\n | {\n type: 'delete.backward'\n }\n\nconst decorateListener: CallbackLogicFunction<\n AnyEventObject,\n DecoratorPairEvent,\n {\n decorator: ({\n context,\n }: {\n context: Pick<EditorContext, 'schema'>\n }) => string | undefined\n editor: Editor\n pair: {char: string; amount: number}\n }\n> = ({sendBack, input}) => {\n const unregister = input.editor.registerBehavior({\n behavior: createCharacterPairDecoratorBehavior({\n decorator: input.decorator,\n pair: input.pair,\n onDecorate: (offset) => {\n sendBack({type: 'decorator.add', blockOffset: offset})\n },\n }),\n })\n\n return unregister\n}\n\nconst selectionListenerCallback: CallbackLogicFunction<\n AnyEventObject,\n DecoratorPairEvent,\n {editor: Editor}\n> = ({sendBack, input}) => {\n const unregister = input.editor.registerBehavior({\n behavior: defineBehavior({\n on: 'select',\n guard: ({snapshot, event}) => {\n if (!event.at) {\n return {blockOffsets: undefined}\n }\n\n const anchor = utils.spanSelectionPointToBlockOffset({\n context: snapshot.context,\n selectionPoint: event.at.anchor,\n })\n const focus = utils.spanSelectionPointToBlockOffset({\n context: snapshot.context,\n selectionPoint: event.at.focus,\n })\n\n if (!anchor || !focus) {\n return {blockOffsets: undefined}\n }\n\n return {\n blockOffsets: {\n anchor,\n focus,\n },\n }\n },\n actions: [\n ({event}, {blockOffsets}) => [\n {\n type: 'effect',\n effect: () => {\n sendBack({type: 'selection', blockOffsets})\n },\n },\n forward(event),\n ],\n ],\n }),\n })\n\n return unregister\n}\n\nconst deleteBackwardListenerCallback: CallbackLogicFunction<\n AnyEventObject,\n DecoratorPairEvent,\n {editor: Editor}\n> = ({sendBack, input}) => {\n const unregister = input.editor.registerBehavior({\n behavior: defineBehavior({\n on: 'delete.backward',\n actions: [\n () => [\n raise({\n type: 'history.undo',\n }),\n effect(() => {\n sendBack({type: 'delete.backward'})\n }),\n ],\n ],\n }),\n })\n\n return unregister\n}\n\nconst decoratorPairMachine = setup({\n types: {\n context: {} as {\n decorator: ({\n context,\n }: {\n context: Pick<EditorContext, 'schema'>\n }) => string | undefined\n editor: Editor\n offsetAfterDecorator?: BlockOffset\n pair: {char: string; amount: number}\n },\n input: {} as {\n decorator: ({\n context,\n }: {\n context: Pick<EditorContext, 'schema'>\n }) => string | undefined\n editor: Editor\n pair: {char: string; amount: number}\n },\n events: {} as DecoratorPairEvent,\n },\n actors: {\n 'decorate listener': fromCallback(decorateListener),\n 'delete.backward listener': fromCallback(deleteBackwardListenerCallback),\n 'selection listener': fromCallback(selectionListenerCallback),\n },\n}).createMachine({\n id: 'decorator pair',\n context: ({input}) => ({\n decorator: input.decorator,\n editor: input.editor,\n pair: input.pair,\n }),\n initial: 'idle',\n states: {\n 'idle': {\n invoke: [\n {\n src: 'decorate listener',\n input: ({context}) => ({\n decorator: context.decorator,\n editor: context.editor,\n pair: context.pair,\n }),\n },\n ],\n on: {\n 'decorator.add': {\n target: 'decorator added',\n actions: assign({\n offsetAfterDecorator: ({event}) => event.blockOffset,\n }),\n },\n },\n },\n 'decorator added': {\n exit: [\n assign({\n offsetAfterDecorator: undefined,\n }),\n ],\n invoke: [\n {\n src: 'selection listener',\n input: ({context}) => ({editor: context.editor}),\n },\n {\n src: 'delete.backward listener',\n input: ({context}) => ({editor: context.editor}),\n },\n ],\n on: {\n 'selection': {\n target: 'idle',\n guard: ({context, event}) => {\n const selectionChanged = !isDeepEqual(\n {\n anchor: context.offsetAfterDecorator,\n focus: context.offsetAfterDecorator,\n },\n event.blockOffsets,\n )\n\n return selectionChanged\n },\n },\n 'delete.backward': {\n target: 'idle',\n },\n },\n },\n },\n})\n"],"names":["createCharacterPairRegex","char","amount","prePrefix","prefix","repeat","Math","max","postPrefix","content","preSuffix","suffix","postSuffix","createCharacterPairDecoratorBehavior","config","pair","console","warn","pairRegex","regEx","RegExp","defineBehavior","on","guard","snapshot","event","decorator","context","schema","undefined","focusTextBlock","selectors","getFocusTextBlock","selectionStartPoint","getSelectionStartPoint","selectionStartOffset","utils","spanSelectionPointToBlockOffset","selectionPoint","newText","getBlockTextBefore","text","textToDecorate","match","at","prefixOffsets","anchor","path","offset","length","focus","suffixOffsets","prefixSelection","blockOffsetsToSelection","offsets","inlineObjectBeforePrefixFocus","getPreviousInlineObject","selection","inlineObjectBeforePrefixFocusOffset","childSelectionPointToBlockOffset","previousInlineObject","previousInlineObjectOffset","actions","forward","_","raise","type","effect","onDecorate","CharacterPairDecoratorPlugin","props","$","_c","editor","useEditor","t0","input","useActorRef","decoratorPairMachine","decorateListener","sendBack","registerBehavior","behavior","blockOffset","selectionListenerCallback","blockOffsets","deleteBackwardListenerCallback","setup","types","events","actors","fromCallback","createMachine","id","initial","states","invoke","src","target","assign","offsetAfterDecorator","exit","isDeepEqual"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/plugin-character-pair-decorator",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "Automatically match a pair of characters and decorate the text in between",
5
5
  "keywords": [
6
6
  "portabletext",
@@ -41,19 +41,19 @@
41
41
  "xstate": "^5.23.0"
42
42
  },
43
43
  "devDependencies": {
44
- "@types/react": "^19.1.12",
44
+ "@types/react": "^19.2.2",
45
45
  "babel-plugin-react-compiler": "1.0.0",
46
46
  "eslint": "^9.38.0",
47
47
  "eslint-plugin-react-hooks": "7.0.1",
48
- "react": "^19.1.1",
48
+ "react": "^19.2.0",
49
49
  "typescript": "5.9.3",
50
50
  "typescript-eslint": "^8.46.1",
51
- "vitest": "^4.0.5",
52
- "@portabletext/editor": "^2.18.0"
51
+ "vitest": "^4.0.6",
52
+ "@portabletext/editor": "^2.19.0"
53
53
  },
54
54
  "peerDependencies": {
55
55
  "react": "^19",
56
- "@portabletext/editor": "^2.18.0"
56
+ "@portabletext/editor": "^2.19.0"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">=20.19 <22 || >=22.12"
@@ -1,11 +1,20 @@
1
- import type {BlockOffset, EditorSchema} from '@portabletext/editor'
2
- import {defineBehavior, effect, execute} from '@portabletext/editor/behaviors'
1
+ import type {BlockOffset, EditorContext} from '@portabletext/editor'
2
+ import {
3
+ defineBehavior,
4
+ effect,
5
+ forward,
6
+ raise,
7
+ } from '@portabletext/editor/behaviors'
3
8
  import * as selectors from '@portabletext/editor/selectors'
4
9
  import * as utils from '@portabletext/editor/utils'
5
10
  import {createCharacterPairRegex} from './regex.character-pair'
6
11
 
7
12
  export function createCharacterPairDecoratorBehavior(config: {
8
- decorator: ({schema}: {schema: EditorSchema}) => string | undefined
13
+ decorator: ({
14
+ context,
15
+ }: {
16
+ context: Pick<EditorContext, 'schema'>
17
+ }) => string | undefined
9
18
  pair: {char: string; amount: number}
10
19
  onDecorate: (offset: BlockOffset) => void
11
20
  }) {
@@ -28,7 +37,9 @@ export function createCharacterPairDecoratorBehavior(config: {
28
37
  return false
29
38
  }
30
39
 
31
- const decorator = config.decorator({schema: snapshot.context.schema})
40
+ const decorator = config.decorator({
41
+ context: {schema: snapshot.context.schema},
42
+ })
32
43
 
33
44
  if (decorator === undefined) {
34
45
  return false
@@ -161,10 +172,10 @@ export function createCharacterPairDecoratorBehavior(config: {
161
172
  },
162
173
  actions: [
163
174
  // Insert the text as usual in its own undo step
164
- ({event}) => [execute(event)],
175
+ ({event}) => [forward(event)],
165
176
  (_, {prefixOffsets, suffixOffsets, decorator}) => [
166
177
  // Decorate the text between the prefix and suffix
167
- execute({
178
+ raise({
168
179
  type: 'decorator.add',
169
180
  decorator,
170
181
  at: {
@@ -173,17 +184,17 @@ export function createCharacterPairDecoratorBehavior(config: {
173
184
  },
174
185
  }),
175
186
  // Delete the suffix
176
- execute({
187
+ raise({
177
188
  type: 'delete.text',
178
189
  at: suffixOffsets,
179
190
  }),
180
191
  // Delete the prefix
181
- execute({
192
+ raise({
182
193
  type: 'delete.text',
183
194
  at: prefixOffsets,
184
195
  }),
185
196
  // Toggle the decorator off so the next inserted text isn't emphasized
186
- execute({
197
+ raise({
187
198
  type: 'decorator.remove',
188
199
  decorator,
189
200
  }),
@@ -1,10 +1,10 @@
1
- import type {BlockOffset, Editor, EditorSchema} from '@portabletext/editor'
1
+ import type {BlockOffset, Editor, EditorContext} from '@portabletext/editor'
2
2
  import {useEditor} from '@portabletext/editor'
3
3
  import {
4
4
  defineBehavior,
5
5
  effect,
6
- execute,
7
6
  forward,
7
+ raise,
8
8
  } from '@portabletext/editor/behaviors'
9
9
  import * as utils from '@portabletext/editor/utils'
10
10
  import {useActorRef} from '@xstate/react'
@@ -19,10 +19,14 @@ import {
19
19
  import {createCharacterPairDecoratorBehavior} from './behavior.character-pair-decorator'
20
20
 
21
21
  /**
22
- * @beta
22
+ * @public
23
23
  */
24
- export function CharacterPairDecoratorPlugin(config: {
25
- decorator: ({schema}: {schema: EditorSchema}) => string | undefined
24
+ export function CharacterPairDecoratorPlugin(props: {
25
+ decorator: ({
26
+ context,
27
+ }: {
28
+ context: Pick<EditorContext, 'schema'>
29
+ }) => string | undefined
26
30
  pair: {char: string; amount: number}
27
31
  }) {
28
32
  const editor = useEditor()
@@ -30,8 +34,8 @@ export function CharacterPairDecoratorPlugin(config: {
30
34
  useActorRef(decoratorPairMachine, {
31
35
  input: {
32
36
  editor,
33
- decorator: config.decorator,
34
- pair: config.pair,
37
+ decorator: props.decorator,
38
+ pair: props.pair,
35
39
  },
36
40
  })
37
41
 
@@ -58,7 +62,11 @@ const decorateListener: CallbackLogicFunction<
58
62
  AnyEventObject,
59
63
  DecoratorPairEvent,
60
64
  {
61
- decorator: ({schema}: {schema: EditorSchema}) => string | undefined
65
+ decorator: ({
66
+ context,
67
+ }: {
68
+ context: Pick<EditorContext, 'schema'>
69
+ }) => string | undefined
62
70
  editor: Editor
63
71
  pair: {char: string; amount: number}
64
72
  }
@@ -136,7 +144,7 @@ const deleteBackwardListenerCallback: CallbackLogicFunction<
136
144
  on: 'delete.backward',
137
145
  actions: [
138
146
  () => [
139
- execute({
147
+ raise({
140
148
  type: 'history.undo',
141
149
  }),
142
150
  effect(() => {
@@ -153,13 +161,21 @@ const deleteBackwardListenerCallback: CallbackLogicFunction<
153
161
  const decoratorPairMachine = setup({
154
162
  types: {
155
163
  context: {} as {
156
- decorator: ({schema}: {schema: EditorSchema}) => string | undefined
164
+ decorator: ({
165
+ context,
166
+ }: {
167
+ context: Pick<EditorContext, 'schema'>
168
+ }) => string | undefined
157
169
  editor: Editor
158
170
  offsetAfterDecorator?: BlockOffset
159
171
  pair: {char: string; amount: number}
160
172
  },
161
173
  input: {} as {
162
- decorator: ({schema}: {schema: EditorSchema}) => string | undefined
174
+ decorator: ({
175
+ context,
176
+ }: {
177
+ context: Pick<EditorContext, 'schema'>
178
+ }) => string | undefined
163
179
  editor: Editor
164
180
  pair: {char: string; amount: number}
165
181
  },