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:
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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>
|
||||
|
Reference in New Issue
Block a user