fix: #551 - rich text nested list structure

This commit is contained in:
James
2022-12-19 22:51:24 -05:00
parent 60bb2652f0
commit 542ea8eb81
7 changed files with 182 additions and 29 deletions

View File

@@ -32,7 +32,7 @@ const defaultLeaves: RichTextLeaf[] = ['bold', 'italic', 'underline', 'strikethr
const baseClass = 'rich-text';
type CustomText = { text: string;[x: string]: unknown }
type CustomElement = { type?: string; children: CustomText[] }
type CustomElement = { type?: string; children: (CustomText | CustomElement)[] }
declare module 'slate' {
interface CustomTypes {
@@ -349,13 +349,13 @@ const RichText: React.FC<Props> = (props) => {
if (SlateElement.isElement(selectedElement) && selectedElement.type === 'li') {
const selectedLeaf = Node.descendant(editor, editor.selection.anchor.path);
if (Text.isText(selectedLeaf) && String(selectedLeaf.text).length === 1) {
if (Text.isText(selectedLeaf) && String(selectedLeaf.text).length === 0) {
Transforms.unwrapNodes(editor, {
match: (n) => SlateElement.isElement(n) && listTypes.includes(n.type),
split: true,
});
Transforms.setNodes(editor, {});
Transforms.setNodes(editor, { type: undefined });
}
} else if (editor.isVoid(selectedElement)) {
Transforms.removeNodes(editor);
@@ -374,6 +374,12 @@ const RichText: React.FC<Props> = (props) => {
</div>
</div>
</Slate>
<pre>
{JSON.stringify(editor.selection)}
</pre>
<pre>
{JSON.stringify(value, null, 2)}
</pre>
<FieldDescription
value={value}
description={description}

View File

@@ -1,7 +1,7 @@
import React, { useCallback } from 'react';
import { useSlate } from 'slate-react';
import isListActive from './isListActive';
import toggleElement from './toggle';
import toggleList from './toggleList';
import { ButtonProps } from './types';
import '../buttons.scss';
@@ -13,7 +13,7 @@ const ListButton: React.FC<ButtonProps> = ({ format, children, onClick, classNam
const defaultOnClick = useCallback((event) => {
event.preventDefault();
toggleElement(editor, format);
toggleList(editor, format);
}, [editor, format]);
return (

View File

@@ -52,12 +52,57 @@ const indent = {
const isCurrentlyUL = isElementActive(editor, 'ul');
if (isCurrentlyOL || isCurrentlyUL) {
Transforms.wrapNodes(editor, {
type: 'li',
children: [],
});
Transforms.wrapNodes(editor, { type: isCurrentlyOL ? 'ol' : 'ul', children: [{ text: ' ' }] });
Transforms.setNodes(editor, { type: 'li' });
let hasText = false;
if (editor.selection) {
const leafNode = Editor.leaf(editor, editor.selection.focus);
if (leafNode) {
const [leaf] = leafNode;
hasText = leaf.text.length > 0;
}
}
if (hasText) {
Transforms.wrapNodes(editor, {
type: 'li',
children: [],
});
Transforms.wrapNodes(editor, { type: isCurrentlyOL ? 'ol' : 'ul', children: [{ text: ' ' }] });
Transforms.setNodes(editor, { type: 'li' });
} else {
const [previousNode, previousNodePath] = Editor.previous(editor, {
at: editor.selection.focus,
});
Transforms.removeNodes(editor);
Transforms.insertNodes(
editor,
[
{
children: [
previousNode,
],
},
{
type: isCurrentlyOL ? 'ol' : 'ul',
children: [
{
type: 'li',
children: [
{
text: '',
},
],
},
],
},
],
{
at: previousNodePath,
},
);
}
} else {
Transforms.wrapNodes(editor, { type: indentType, children: [] });
}

View File

@@ -1,33 +1,17 @@
import { Element, Transforms } from 'slate';
import { Transforms } from 'slate';
import { ReactEditor } from 'slate-react';
import isElementActive from './isActive';
import listTypes from './listTypes';
const toggleElement = (editor, format) => {
const isActive = isElementActive(editor, format);
const isList = listTypes.includes(format);
let type = format;
if (isActive) {
type = undefined;
} else if (isList) {
type = 'li';
}
Transforms.unwrapNodes(editor, {
match: (n) => Element.isElement(n) && listTypes.includes(n.type as string),
split: true,
mode: 'lowest',
});
Transforms.setNodes(editor, { type });
if (!isActive && isList) {
const block = { type: format, children: [] };
Transforms.wrapNodes(editor, block);
}
ReactEditor.focus(editor);
};

View File

@@ -0,0 +1,34 @@
import { Element, Transforms } from 'slate';
import { ReactEditor } from 'slate-react';
import isElementActive from './isActive';
import listTypes from './listTypes';
const toggleList = (editor, format) => {
const isActive = isElementActive(editor, format);
const isList = listTypes.includes(format);
let type = format;
if (isActive) {
type = undefined;
} else if (isList) {
type = 'li';
}
Transforms.unwrapNodes(editor, {
match: (n) => Element.isElement(n) && listTypes.includes(n.type as string),
split: true,
mode: 'lowest',
});
Transforms.setNodes(editor, { type });
if (!isActive && isList) {
const block = { type: format, children: [] };
Transforms.wrapNodes(editor, block);
}
ReactEditor.focus(editor);
};
export default toggleList;

View File

@@ -3,7 +3,15 @@ import { loremIpsum } from './loremIpsum';
const RichTextFields: CollectionConfig = {
slug: 'rich-text-fields',
admin: {
useAsTitle: 'title',
},
fields: [
{
name: 'title',
type: 'text',
required: true,
},
{
name: 'selectHasMany',
hasMany: true,
@@ -257,7 +265,82 @@ function generateRichText() {
];
}
export const richTextBulletsDoc = {
title: 'Bullets and Indentation',
richText: [
{
type: 'ul',
children: [
{
children: [
{
text: 'Normal bullet',
},
],
type: 'li',
},
{
type: 'li',
children: [
{
type: 'ul',
children: [
{
type: 'li',
children: [
{
text: 'I am the old style of sub-bullet',
},
],
},
],
},
],
},
{
type: 'li',
children: [
{
text: 'Another normal bullet',
},
],
},
{
type: 'li',
children: [
{
text: 'This text precedes a nested list',
},
{
type: 'ul',
children: [
{
type: 'li',
children: [
{
text: 'I am a sub-bullet',
},
],
},
{
type: 'li',
children: [
{
text: 'And I am another sub-bullet',
},
],
},
],
},
],
},
],
},
],
};
export const richTextDoc = {
title: 'Rich Text',
selectHasMany: ['one', 'five'],
richText: generateRichText(),
richTextReadOnly: generateRichText(),

View File

@@ -8,7 +8,7 @@ import BlockFields, { blocksDoc } from './collections/Blocks';
import CollapsibleFields, { collapsibleDoc } from './collections/Collapsible';
import ConditionalLogic, { conditionalLogicDoc } from './collections/ConditionalLogic';
import DateFields, { dateDoc } from './collections/Date';
import RichTextFields, { richTextDoc } from './collections/RichText';
import RichTextFields, { richTextBulletsDoc, richTextDoc } from './collections/RichText';
import SelectFields, { selectsDoc } from './collections/Select';
import TabsFields, { tabsDoc } from './collections/Tabs';
import TextFields, { textDoc, textFieldsSlug } from './collections/Text';
@@ -100,6 +100,7 @@ export default buildConfig({
richTextDocWithRelationship.richTextReadOnly[richTextUploadIndex].value = { id: createdUploadDoc.id };
await payload.create({ collection: 'rich-text-fields', data: richTextDocWithRelationship });
await payload.create({ collection: 'rich-text-fields', data: richTextBulletsDoc });
await payload.create({ collection: 'number-fields', data: numberDoc });