1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-09-03 12:12:39 +02:00

split format_text into two commands (#3340)

* split format_text into two commands

* fix lint
This commit is contained in:
Ian Storm Taylor
2019-12-17 22:17:33 -05:00
committed by GitHub
parent 235a578967
commit 28b5217370
3 changed files with 103 additions and 79 deletions

View File

@@ -95,6 +95,30 @@ export const createEditor = (): Editor => {
const { selection } = editor
switch (command.type) {
case 'add_mark': {
if (selection) {
const { key, value } = command
if (Range.isExpanded(selection)) {
Editor.setNodes(
editor,
{ [key]: value },
{ match: Text.isText, split: true }
)
} else {
const marks = {
...(Editor.marks(editor) || {}),
[key]: value,
}
editor.marks = marks
editor.onChange()
}
}
break
}
case 'delete_backward': {
if (selection && Range.isCollapsed(selection)) {
Editor.delete(editor, { unit: command.unit, reverse: true })
@@ -119,55 +143,6 @@ export const createEditor = (): Editor => {
break
}
case 'format_text': {
if (selection) {
const { properties } = command
if (Range.isExpanded(selection)) {
const [match] = Editor.nodes(editor, {
match: n => Text.isText(n) && Text.matches(n, properties),
universal: true,
})
if (match) {
const keys = Object.keys(properties)
Editor.unsetNodes(editor, keys, {
match: Text.isText,
split: true,
})
} else {
Editor.setNodes(editor, properties, {
match: Text.isText,
split: true,
})
}
} else {
const marks = { ...(Editor.marks(editor) || {}) }
let match = true
for (const key in properties) {
if (marks[key] !== properties[key]) {
match = false
break
}
}
if (match) {
for (const key in properties) {
delete marks[key]
}
} else {
Object.assign(marks, properties)
}
editor.marks = marks
editor.onChange()
}
}
break
}
case 'insert_break': {
Editor.splitNodes(editor, { always: true })
break
@@ -217,6 +192,26 @@ export const createEditor = (): Editor => {
}
break
}
case 'remove_mark': {
if (selection) {
const { key } = command
if (Range.isExpanded(selection)) {
Editor.unsetNodes(editor, key, {
match: Text.isText,
split: true,
})
} else {
const marks = { ...(Editor.marks(editor) || {}) }
delete marks[key]
editor.marks = marks
editor.onChange()
}
}
break
}
}
}
},

View File

@@ -21,6 +21,16 @@ export const Command = {
},
}
/**
* The `AddMarkCommand` adds properties to the text nodes in the selection.
*/
export interface AddMarkCommand {
type: 'add_mark'
key: string
value: any
}
/**
* The `DeleteBackwardCommand` delete's content backward, meaning before the
* current selection, by a specific `unit` of distance.
@@ -49,15 +59,6 @@ export interface DeleteFragmentCommand {
type: 'delete_fragment'
}
/**
* The `FormatTextCommand` adds properties to the text nodes in the selection.
*/
export interface FormatTextCommand {
type: 'format_text'
properties: Record<string, any>
}
/**
* The `InsertBreakCommand` breaks a block in two at the current selection.
*/
@@ -93,20 +94,30 @@ export interface InsertTextCommand {
text: string
}
/**
* The `RemoveMarkCommand` removes properties from text nodes in the selection.
*/
export interface RemoveMarkCommand {
type: 'remove_mark'
key: string
}
/**
* The `CoreCommand` union is a set of all of the commands that are recognized
* by Slate's "core" out of the box.
*/
export type CoreCommand =
| AddMarkCommand
| DeleteBackwardCommand
| DeleteForwardCommand
| DeleteFragmentCommand
| FormatTextCommand
| InsertBreakCommand
| InsertFragmentCommand
| InsertNodeCommand
| InsertTextCommand
| RemoveMarkCommand
export const CoreCommand = {
/**
@@ -116,14 +127,14 @@ export const CoreCommand = {
isCoreCommand(value: any): value is CoreCommand {
if (Command.isCommand(value)) {
switch (value.type) {
case 'add_mark':
return typeof value.key === 'string' && value.value != null
case 'delete_backward':
return typeof value.unit === 'string'
case 'delete_forward':
return typeof value.unit === 'string'
case 'delete_fragment':
return true
case 'format_text':
return isPlainObject(value.properties)
case 'insert_break':
return true
case 'insert_fragment':
@@ -132,6 +143,8 @@ export const CoreCommand = {
return Node.isNode(value.node)
case 'insert_text':
return typeof value.text === 'string'
case 'remove_mark':
return typeof value.key === 'string'
}
}

View File

@@ -49,8 +49,8 @@ const RichTextExample = () => {
event.preventDefault()
const mark = HOTKEYS[hotkey]
editor.exec({
type: 'format_text',
properties: { [mark]: true },
type: 'toggle_mark',
format: mark,
})
}
}
@@ -64,24 +64,41 @@ const withRichText = editor => {
const { exec } = editor
editor.exec = command => {
if (command.type === 'format_block') {
const { format } = command
const isActive = isBlockActive(editor, format)
const isList = LIST_TYPES.includes(format)
switch (command.type) {
case 'toggle_block': {
const { format } = command
const isActive = isBlockActive(editor, format)
const isList = LIST_TYPES.includes(format)
for (const f of LIST_TYPES) {
Editor.unwrapNodes(editor, { match: n => n.type === f, split: true })
Editor.unwrapNodes(editor, {
match: n => LIST_ITEMS.includes(n.type),
split: true,
})
Editor.setNodes(editor, {
type: isActive ? 'paragraph' : isList ? 'list-item' : format,
})
if (!isActive && isList) {
const block = { type: format, children: [] }
Editor.wrapNodes(editor, block)
}
}
Editor.setNodes(editor, {
type: isActive ? 'paragraph' : isList ? 'list-item' : format,
})
case 'toggle_mark': {
const { format } = command
const isActive = isMarkActive(editor, format)
if (!isActive && isList) {
Editor.wrapNodes(editor, { type: format, children: [] })
if (isActive) {
editor.exec({ type: 'remove_mark', key: format })
} else {
editor.exec({ type: 'add_mark', key: format, value: true })
}
}
default: {
exec(command)
}
} else {
exec(command)
}
}
@@ -91,7 +108,6 @@ const withRichText = editor => {
const isBlockActive = (editor, format) => {
const [match] = Editor.nodes(editor, {
match: n => n.type === format,
mode: 'all',
})
return !!match
@@ -148,7 +164,7 @@ const BlockButton = ({ format, icon }) => {
active={isBlockActive(editor, format)}
onMouseDown={event => {
event.preventDefault()
editor.exec({ type: 'format_block', format })
editor.exec({ type: 'toggle_block', format })
}}
>
<Icon>{icon}</Icon>
@@ -163,7 +179,7 @@ const MarkButton = ({ format, icon }) => {
active={isMarkActive(editor, format)}
onMouseDown={event => {
event.preventDefault()
editor.exec({ type: 'format_text', properties: { [format]: true } })
editor.exec({ type: 'toggle_mark', format })
}}
>
<Icon>{icon}</Icon>