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

Diff #2900

Merged
merged 15 commits into from
Feb 7, 2024
Merged

Diff #2900

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
5 changes: 5 additions & 0 deletions .changeset/dry-poets-give.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@udecode/plate-suggestion": minor
---

slate-diff
6 changes: 6 additions & 0 deletions apps/www/content/docs/examples/version-history.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: Version History
description: Show a diff of two different points in a Plate document's history.
---

<ComponentPreview name="version-history-demo" />
7 changes: 7 additions & 0 deletions apps/www/src/__registry__/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,13 @@ export const Index: Record<string, any> = {
files: ['registry/default/example/multiple-editors-demo.tsx'],
component: React.lazy(() => import('@/registry/default/example/multiple-editors-demo')),
},
'version-history-demo': {
name: 'version-history-demo',
type: 'components:example',
registryDependencies: [],
files: ['registry/default/example/version-history-demo.tsx'],
component: React.lazy(() => import('@/registry/default/example/version-history-demo')),
},
'playground-demo': {
name: 'playground-demo',
type: 'components:example',
Expand Down
4 changes: 4 additions & 0 deletions apps/www/src/config/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,10 @@ export const docsConfig: DocsConfig = {
title: 'Preview Markdown',
href: '/docs/examples/preview-markdown',
},
{
title: 'Version History',
href: '/docs/examples/version-history',
},
],
},
{
Expand Down
195 changes: 195 additions & 0 deletions apps/www/src/registry/default/example/version-history-demo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import React from 'react';
import { Plate, PlateContent, PlateProps, Value, createPlugins, createPlateEditor, createPluginFactory, PlateLeafProps, PlateLeaf, PlateElementProps, PlateElement, isInline } from '@udecode/plate-common';
import { ELEMENT_PARAGRAPH, createParagraphPlugin } from '@udecode/plate-paragraph';
import {ParagraphElement} from '../plate-ui/paragraph-element';
import {Button} from '../plate-ui/button';
import {slateDiff, applyDiffToSuggestions} from '@udecode/plate-suggestion';
import { createBoldPlugin, MARK_BOLD } from '@udecode/plate-basic-marks';
import {cn, withProps} from '@udecode/cn';
import {useSelected} from 'slate-react';

const ELEMENT_INLINE_VOID = 'inlineVoid';

const createInlineVoidPlugin = createPluginFactory({
key: ELEMENT_INLINE_VOID,
isElement: true,
isInline: true,
isVoid: true,
});

const InlineVoidElement = ({ children, ...props }: PlateElementProps) => {
const selected = useSelected();
return (
<PlateElement {...props} as="span">
<span
contentEditable={false}
className={cn(
'p-1 bg-slate-200 rounded-sm',
selected && 'bg-blue-500 text-white'
)}
>
Inline void
</span>
{children}
</PlateElement>
);
};

const KEY_DIFF = 'diff';
const MARK_SUGGESTION = 'suggestion';

const createDiffPlugin = createPluginFactory({
key: KEY_DIFF,
plugins: [
{
key: MARK_SUGGESTION,
isLeaf: true,
},
],
inject: {
aboveComponent: () => ({ element, children, editor }) => {
if (!element.suggestion) return children;
const Component = isInline(editor, element) ? 'span' : 'div';
return (
<Component
className={element.suggestionDeletion ? 'bg-red-200' : 'bg-green-200'}
aria-label={element.suggestionDeletion ? 'deletion' : 'insertion'}
>
{children}
</Component>
);
},
},
});

function SuggestionLeaf({ children, ...props }: PlateLeafProps) {
const isDeletion = props.leaf.suggestionDeletion;
const isUpdate = !isDeletion && props.leaf.suggestionUpdate;
const Component = isDeletion ? 'del' : 'ins';

return (
<PlateLeaf {...props} asChild>
<Component className={isDeletion ? 'bg-red-200' : (isUpdate ? 'bg-blue-200' : 'bg-green-200')}>
{children}
</Component>
</PlateLeaf>
);
}

const plugins = createPlugins([
createParagraphPlugin(),
createInlineVoidPlugin(),
createBoldPlugin(),
createDiffPlugin(),
], {
components: {
[ELEMENT_PARAGRAPH]: ParagraphElement,
[ELEMENT_INLINE_VOID]: InlineVoidElement,
[MARK_BOLD]: withProps(PlateLeaf, { as: 'strong' }),
[MARK_SUGGESTION]: SuggestionLeaf,
},
});

const initialValue: Value = [
{
type: ELEMENT_PARAGRAPH,
children: [{ text: 'This is a version history demo.' }],
},
{
type: ELEMENT_PARAGRAPH,
children: [
{ text: 'Try editing the ' },
{ text: 'text and see what', bold: true },
{ text: ' happens.' }
],
},
{
type: ELEMENT_PARAGRAPH,
children: [
{ text: 'This is an ' },
{ type: ELEMENT_INLINE_VOID, children: [{ text: '' }] },
{ text: '. Try removing it.' },
],
},
];

function VersionHistoryPlate(props: Omit<PlateProps, 'children' | 'plugins'>) {
return (
<Plate {...props} plugins={plugins}>
<PlateContent className="border rounded-md p-3" />
</Plate>
);
}

interface DiffProps {
previous: Value;
current: Value;
}

function Diff({ previous, current }: DiffProps) {
const operations = React.useMemo(() => slateDiff(previous, current), [previous, current]);

const diffValue: Value = React.useMemo(() => {
const editor = createPlateEditor({ plugins });
editor.children = previous;
applyDiffToSuggestions(editor, operations);
return editor.children;
}, [previous, current]);

return (
<>
<VersionHistoryPlate key={JSON.stringify(diffValue)} value={diffValue} readOnly />

<pre>
{JSON.stringify(operations, null, 2)}
</pre>

<pre>
{JSON.stringify(diffValue, null, 2)}
</pre>
</>
);
}

export default function VersionHistoryDemo() {
const [revisions, setRevisions] = React.useState<Value[]>([initialValue]);
const [selectedRevisionIndex, setSelectedRevisionIndex] = React.useState<number>(0);
const [value, setValue] = React.useState<Value>(initialValue);

const selectedRevisionValue = React.useMemo(() => revisions[selectedRevisionIndex], [revisions, selectedRevisionIndex]);

const saveRevision = () => {
setRevisions([...revisions, value]);
};

return (
<div className="p-3 flex flex-col gap-3">
<Button onClick={saveRevision}>Save revision</Button>

<VersionHistoryPlate initialValue={initialValue} onChange={setValue} />

<label>
Revision to compare:
<select className="border rounded-md p-1" onChange={(e) => setSelectedRevisionIndex(Number(e.target.value))}>
{revisions.map((_, i) => (
<option key={i} value={i}>
Revision {i + 1}
</option>
))}
</select>
</label>

<div className="grid md:grid-cols-2 gap-3">
<div>
<h2>Revision {selectedRevisionIndex + 1}</h2>
<VersionHistoryPlate key={selectedRevisionIndex} initialValue={selectedRevisionValue} readOnly />
</div>

<div>
<h2>Diff</h2>
<Diff previous={selectedRevisionValue} current={value} />
</div>
</div>
</div>
);
}
6 changes: 6 additions & 0 deletions apps/www/src/registry/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,12 @@ const example: Registry = [
registryDependencies: [],
files: ['example/multiple-editors-demo.tsx'],
},
{
name: 'version-history-demo',
type: 'components:example',
registryDependencies: [],
files: ['example/version-history-demo.tsx'],
},
{
name: 'playground-demo',
type: 'components:example',
Expand Down
1 change: 1 addition & 0 deletions config/eslint/bases/unicorn.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module.exports = {
extends: ['plugin:unicorn/recommended'],
plugins: ['unicorn'],
rules: {
'unicorn/no-abusive-eslint-disable': 'off',
'unicorn/prefer-module': 'off',
'unicorn/consistent-destructuring': 'off',
'unicorn/consistent-function-scoping': [
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@
"g:typecheck:apps": "turbo --filter=www typecheck --no-daemon",
"install:playwright": "yarn playwright install --with-deps",
"nuke:node_modules": "rimraf '**/node_modules'",
"p:brl": "cd $INIT_CWD && barrelsby -d $INIT_CWD/src -D -l all -q -e '.*(fixture|template|spec|__tests__).*'",
"p:brl:below": "cd $INIT_CWD && barrelsby -d $INIT_CWD/src -D -l below -q -e '.*(fixture|template|spec|__tests__).*'",
"p:brl": "cd $INIT_CWD && barrelsby -d $INIT_CWD/src -D -l all -q -e '.*(fixture|template|spec|internal).*'",
"p:brl:below": "cd $INIT_CWD && barrelsby -d $INIT_CWD/src -D -l below -q -e '.*(fixture|template|spec|internal).*'",
"p:build": "cd $INIT_CWD && yarn p:tsup",
"p:build:watch": "cd $INIT_CWD && yarn p:tsup --watch",
"p:clean": "cd $INIT_CWD && rimraf dist && jest --clear-cache",
Expand Down
2 changes: 1 addition & 1 deletion packages/suggestion/.npmignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__tests__
src/diff/internal
__test-utils__
__mocks__
Loading
Loading