Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create Operation Messages plugin #2755

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/op-messages/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
__tests__
__test-utils__
__mocks__
Empty file.
12 changes: 12 additions & 0 deletions packages/op-messages/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Plate operation messages plugin

TODO

## Documentation

Check out
[Operation Messages](https://platejs.org/docs/op-messages).

## License

[MIT](../../LICENSE)
60 changes: 60 additions & 0 deletions packages/op-messages/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"name": "@udecode/plate-op-messages",
"version": "25.0.1",
"description": "TODO",
"license": "MIT",
"homepage": "https://platejs.org",
"repository": {
"type": "git",
"url": "https://github.com/udecode/plate.git",
"directory": "packages/tabbable"
},
"bugs": {
"url": "https://github.com/udecode/plate/issues"
},
"sideEffects": false,
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"files": [
"dist/**/*"
],
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"module": "./dist/index.mjs",
"require": "./dist/index.js"
}
},
"scripts": {
"build": "yarn p:build",
"build:watch": "yarn p:build:watch",
"brl": "yarn p:brl",
"clean": "yarn p:clean",
"lint": "yarn p:lint",
"lint:fix": "yarn p:lint:fix",
"test": "yarn p:test",
"test:watch": "yarn p:test:watch",
"typecheck": "yarn p:typecheck"
},
"dependencies": {
"@udecode/plate-common": "25.0.1"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
"slate": ">=0.94.0",
"slate-history": ">=0.93.0",
"slate-hyperscript": ">=0.66.0",
"slate-react": ">=0.99.0"
},
"keywords": [
"plate",
"plugin",
"slate"
],
"publishConfig": {
"access": "public"
}
}
44 changes: 44 additions & 0 deletions packages/op-messages/src/createOpMessagesPlugin.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { createPlateEditor } from '@udecode/plate-common';

import { createOpMessagesPlugin } from './createOpMessagesPlugin';
import { dispatchOpMessage } from './dispatchOpMessage';

describe('createOpMessagesPlugin', () => {
it('dispatches and receives messages and inverse messages', () => {
const onMessage = jest.fn();

const editor = createPlateEditor({
plugins: [
createOpMessagesPlugin({
options: {
onMessage,
},
}) as any,
],
});

dispatchOpMessage(editor, 'create_user', { name: 'John' });

expect(onMessage).toHaveBeenLastCalledWith({
messageType: 'create_user',
data: { name: 'John' },
inverse: false,
});

editor.undo();

expect(onMessage).toHaveBeenLastCalledWith({
messageType: 'create_user',
data: { name: 'John' },
inverse: true,
});

editor.redo();

expect(onMessage).toHaveBeenLastCalledWith({
messageType: 'create_user',
data: { name: 'John' },
inverse: false,
});
});
});
16 changes: 16 additions & 0 deletions packages/op-messages/src/createOpMessagesPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createPluginFactory } from '@udecode/plate-common';

import { OpMessagesPlugin } from './types';
import { withOpMessages } from './withOpMessages';

export const KEY_OP_MESSAGES = 'opMessages';

export const createOpMessagesPlugin = createPluginFactory<OpMessagesPlugin>({
key: KEY_OP_MESSAGES,
withOverrides: withOpMessages,
options: {
operationPath: [],
catchErrors: true,
onMessage: () => {},
},
});
34 changes: 34 additions & 0 deletions packages/op-messages/src/dispatchOpMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { getPluginOptions, PlateEditor, Value } from '@udecode/plate-common';

import { KEY_OP_MESSAGES } from './createOpMessagesPlugin';
import { OpMessage, OpMessagesPlugin } from './types';

export const dispatchOpMessage = <
V extends Value = Value,
E extends PlateEditor<V> = PlateEditor<V>,
>(
editor: E,
messageType: string,
data: any
) => {
const { operationPath } = getPluginOptions<OpMessagesPlugin, V, E>(
editor,
KEY_OP_MESSAGES
);

const message: OpMessage = {
messageType,
data,
inverse: false,
};

editor.apply({
type: 'set_node',
path: operationPath!,
newProperties: message,
properties: {
...message,
inverse: true,
},
});
};
33 changes: 33 additions & 0 deletions packages/op-messages/src/handleOpMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { getPluginOptions, PlateEditor, Value } from '@udecode/plate-common';

import { KEY_OP_MESSAGES } from './createOpMessagesPlugin';
import { OpMessage, OpMessagesPlugin } from './types';

export const handleOpMessage = <
V extends Value = Value,
E extends PlateEditor<V> = PlateEditor<V>,
>(
editor: E,
message: OpMessage
) => {
const { onMessage, catchErrors } = getPluginOptions<OpMessagesPlugin, V, E>(
editor,
KEY_OP_MESSAGES
);

if (catchErrors) {
try {
onMessage(message);
} catch (error) {
// eslint-disable-next-line no-console
console.error(
'An error occurred while handling an operation message:',
error
);
// eslint-disable-next-line no-console
console.debug('Operation message:', message);
}
} else {
onMessage(message);
}
};
9 changes: 9 additions & 0 deletions packages/op-messages/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @file Automatically generated by barrelsby.
*/

export * from './createOpMessagesPlugin';
export * from './dispatchOpMessage';
export * from './handleOpMessage';
export * from './types';
export * from './withOpMessages';
28 changes: 28 additions & 0 deletions packages/op-messages/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Path } from 'slate';

export interface OpMessage {
messageType: string;
data: any;
inverse: boolean;
}

export interface OpMessagesPlugin {
/**
* Handle operaion messages.
*/
onMessage: (message: OpMessage) => void;

/**
* This plugin passes messages using Slate operations by triggering a
* set_node operation with a path not normally associated with that operation
* type. By default, this path is [], but any other invalid path can be used
* in case of conflicts.
*/
operationPath?: Path;

/**
* Automatically catch errors thrown by the onMessage handler. This helps to
* prevent malicious messages from crashing the editor. Default: true.
*/
catchErrors?: boolean;
}
36 changes: 36 additions & 0 deletions packages/op-messages/src/withOpMessages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { PlateEditor, Value, WithPlatePlugin } from '@udecode/plate-common';
import { Path } from 'slate';

import { handleOpMessage } from './handleOpMessage';
import { OpMessage, OpMessagesPlugin } from './types';

export const withOpMessages = <
V extends Value = Value,
E extends PlateEditor<V> = PlateEditor<V>,
>(
editor: E,
{ options: { operationPath } }: WithPlatePlugin<OpMessagesPlugin, V, E>
) => {
const { apply } = editor;

editor.apply = (op) => {
if (op.type === 'set_node' && Path.equals(op.path, operationPath!)) {
handleOpMessage<V, E>(editor, op.newProperties as OpMessage);

/**
* We need to allow the history editor to save the operation, while
* ignoring any errors caused by attempting to apply set_node to an
* invalid path.
*/
try {
apply(op);
} catch (error) {}

return;
}

apply(op);
};

return editor;
};
8 changes: 8 additions & 0 deletions packages/op-messages/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "../../config/tsconfig.build.json",
"compilerOptions": {
"declarationDir": "./dist",
"outDir": "./dist"
},
"include": ["src"]
}
Loading
Loading