feat: adds indentation controls to rich text
* feat: rich text indent PoC * fix: new slate version types * feat: ensures only lowest rich text list is shown as active * feat: adds icons for indentation * docs: adds indent to rich text
This commit is contained in:
@@ -57,6 +57,7 @@ The default `elements` available in Payload are:
|
|||||||
- `link`
|
- `link`
|
||||||
- `ol`
|
- `ol`
|
||||||
- `ul`
|
- `ul`
|
||||||
|
- `indent`
|
||||||
- [`relationship`](#relationship-element)
|
- [`relationship`](#relationship-element)
|
||||||
- [`upload`](#upload-element)
|
- [`upload`](#upload-element)
|
||||||
|
|
||||||
|
|||||||
@@ -166,10 +166,10 @@
|
|||||||
"sass": "^1.42.0",
|
"sass": "^1.42.0",
|
||||||
"sass-loader": "^10.1.0",
|
"sass-loader": "^10.1.0",
|
||||||
"sharp": "^0.29.3",
|
"sharp": "^0.29.3",
|
||||||
"slate": "^0.66.2",
|
"slate": "0.72.3",
|
||||||
"slate-history": "^0.66.0",
|
"slate-history": "^0.66.0",
|
||||||
"slate-hyperscript": "^0.66.0",
|
"slate-hyperscript": "^0.66.0",
|
||||||
"slate-react": "^0.66.4",
|
"slate-react": "^0.72.1",
|
||||||
"style-loader": "^2.0.0",
|
"style-loader": "^2.0.0",
|
||||||
"terser-webpack-plugin": "^5.0.3",
|
"terser-webpack-plugin": "^5.0.3",
|
||||||
"ts-essentials": "^7.0.1",
|
"ts-essentials": "^7.0.1",
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import defaultValue from '../../../../../fields/richText/defaultValue';
|
|||||||
import FieldTypeGutter from '../../FieldTypeGutter';
|
import FieldTypeGutter from '../../FieldTypeGutter';
|
||||||
import FieldDescription from '../../FieldDescription';
|
import FieldDescription from '../../FieldDescription';
|
||||||
import withHTML from './plugins/withHTML';
|
import withHTML from './plugins/withHTML';
|
||||||
import { Props } from './types';
|
import { Props, BlurSelectionEditor } from './types';
|
||||||
import { RichTextElement, RichTextLeaf } from '../../../../../fields/config/types';
|
import { RichTextElement, RichTextLeaf } from '../../../../../fields/config/types';
|
||||||
import listTypes from './elements/listTypes';
|
import listTypes from './elements/listTypes';
|
||||||
import mergeCustomFunctions from './mergeCustomFunctions';
|
import mergeCustomFunctions from './mergeCustomFunctions';
|
||||||
@@ -25,7 +25,7 @@ import withEnterBreakOut from './plugins/withEnterBreakOut';
|
|||||||
|
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
const defaultElements: RichTextElement[] = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'link', 'relationship', 'upload'];
|
const defaultElements: RichTextElement[] = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'indent', 'link', 'relationship', 'upload'];
|
||||||
const defaultLeaves: RichTextLeaf[] = ['bold', 'italic', 'underline', 'strikethrough', 'code'];
|
const defaultLeaves: RichTextLeaf[] = ['bold', 'italic', 'underline', 'strikethrough', 'code'];
|
||||||
|
|
||||||
const baseClass = 'rich-text';
|
const baseClass = 'rich-text';
|
||||||
@@ -35,7 +35,7 @@ type CustomElement = { type: string; children: CustomText[] }
|
|||||||
|
|
||||||
declare module 'slate' {
|
declare module 'slate' {
|
||||||
interface CustomTypes {
|
interface CustomTypes {
|
||||||
Editor: BaseEditor & ReactEditor & HistoryEditor
|
Editor: BaseEditor & ReactEditor & HistoryEditor & BlurSelectionEditor
|
||||||
Element: CustomElement
|
Element: CustomElement
|
||||||
Text: CustomText
|
Text: CustomText
|
||||||
}
|
}
|
||||||
@@ -280,12 +280,11 @@ const RichText: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
if (Text.isText(selectedLeaf) && String(selectedLeaf.text).length === editor.selection.anchor.offset) {
|
if (Text.isText(selectedLeaf) && String(selectedLeaf.text).length === editor.selection.anchor.offset) {
|
||||||
Transforms.insertNodes(editor, {
|
Transforms.insertNodes(editor, {
|
||||||
type: 'p',
|
text: '',
|
||||||
children: [{ text: '' }],
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Transforms.splitNodes(editor);
|
Transforms.splitNodes(editor);
|
||||||
Transforms.setNodes(editor, { type: 'p' });
|
Transforms.setNodes(editor, {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,7 +302,7 @@ const RichText: React.FC<Props> = (props) => {
|
|||||||
split: true,
|
split: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
Transforms.setNodes(editor, { type: 'p' });
|
Transforms.setNodes(editor, {});
|
||||||
}
|
}
|
||||||
} else if (editor.isVoid(selectedElement)) {
|
} else if (editor.isVoid(selectedElement)) {
|
||||||
Transforms.removeNodes(editor);
|
Transforms.removeNodes(editor);
|
||||||
|
|||||||
@@ -5,4 +5,8 @@
|
|||||||
width: base(.75);
|
width: base(.75);
|
||||||
height: base(.75);
|
height: base(.75);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--disabled {
|
||||||
|
opacity: .4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { ButtonProps } from './types';
|
|||||||
|
|
||||||
import '../buttons.scss';
|
import '../buttons.scss';
|
||||||
|
|
||||||
const baseClass = 'rich-text__button';
|
export const baseClass = 'rich-text__button';
|
||||||
|
|
||||||
const ElementButton: React.FC<ButtonProps> = ({ format, children, onClick, className }) => {
|
const ElementButton: React.FC<ButtonProps> = ({ format, children, onClick, className }) => {
|
||||||
const editor = useSlate();
|
const editor = useSlate();
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { useSlate } from 'slate-react';
|
||||||
|
import isListActive from './isListActive';
|
||||||
|
import toggleElement from './toggle';
|
||||||
|
import { ButtonProps } from './types';
|
||||||
|
|
||||||
|
import '../buttons.scss';
|
||||||
|
|
||||||
|
export const baseClass = 'rich-text__button';
|
||||||
|
|
||||||
|
const ListButton: React.FC<ButtonProps> = ({ format, children, onClick, className }) => {
|
||||||
|
const editor = useSlate();
|
||||||
|
|
||||||
|
const defaultOnClick = useCallback((event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
toggleElement(editor, format);
|
||||||
|
}, [editor, format]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={[
|
||||||
|
baseClass,
|
||||||
|
className,
|
||||||
|
isListActive(editor, format) && `${baseClass}__button--active`,
|
||||||
|
].filter(Boolean).join(' ')}
|
||||||
|
onClick={onClick || defaultOnClick}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ListButton;
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { useSlate, ReactEditor } from 'slate-react';
|
||||||
|
import { Editor, Element, Transforms } from 'slate';
|
||||||
|
import IndentLeft from '../../../../../icons/IndentLeft';
|
||||||
|
import IndentRight from '../../../../../icons/IndentRight';
|
||||||
|
import { baseClass } from '../Button';
|
||||||
|
import isElementActive from '../isActive';
|
||||||
|
import listTypes from '../listTypes';
|
||||||
|
|
||||||
|
const indentType = 'indent';
|
||||||
|
|
||||||
|
const IndentWithPadding = ({ attributes, children }) => (
|
||||||
|
<div
|
||||||
|
style={{ paddingLeft: 25 }}
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const indent = {
|
||||||
|
Button: () => {
|
||||||
|
const editor = useSlate();
|
||||||
|
const handleIndent = useCallback((e, dir) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (editor.blurSelection) {
|
||||||
|
Transforms.select(editor, editor.blurSelection);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir === 'left') {
|
||||||
|
Transforms.unwrapNodes(editor, {
|
||||||
|
match: (n) => Element.isElement(n) && [indentType, ...listTypes].includes(n.type),
|
||||||
|
split: true,
|
||||||
|
mode: 'lowest',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isElementActive(editor, 'li')) {
|
||||||
|
const [, parentLocation] = Editor.parent(editor, editor.selection);
|
||||||
|
const [, parentDepth] = parentLocation;
|
||||||
|
|
||||||
|
if (parentDepth > 0 || parentDepth === 0) {
|
||||||
|
Transforms.unwrapNodes(editor, {
|
||||||
|
match: (n) => Element.isElement(n) && n.type === 'li',
|
||||||
|
split: true,
|
||||||
|
mode: 'lowest',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Transforms.unsetNodes(editor, ['type']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir === 'right') {
|
||||||
|
const isCurrentlyOL = isElementActive(editor, 'ol');
|
||||||
|
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' });
|
||||||
|
} else {
|
||||||
|
Transforms.wrapNodes(editor, { type: indentType, children: [] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactEditor.focus(editor);
|
||||||
|
}, [editor]);
|
||||||
|
|
||||||
|
const canDeIndent = isElementActive(editor, 'li') || isElementActive(editor, indentType);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={[
|
||||||
|
baseClass,
|
||||||
|
!canDeIndent && `${baseClass}--disabled`,
|
||||||
|
].filter(Boolean).join(' ')}
|
||||||
|
onClick={canDeIndent ? (e) => handleIndent(e, 'left') : undefined}
|
||||||
|
>
|
||||||
|
<IndentLeft />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={baseClass}
|
||||||
|
onClick={(e) => handleIndent(e, 'right')}
|
||||||
|
>
|
||||||
|
<IndentRight />
|
||||||
|
</button>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
Element: IndentWithPadding,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default indent;
|
||||||
@@ -8,10 +8,11 @@ import link from './link';
|
|||||||
import ol from './ol';
|
import ol from './ol';
|
||||||
import ul from './ul';
|
import ul from './ul';
|
||||||
import li from './li';
|
import li from './li';
|
||||||
|
import indent from './indent';
|
||||||
import relationship from './relationship';
|
import relationship from './relationship';
|
||||||
import upload from './upload';
|
import upload from './upload';
|
||||||
|
|
||||||
export default {
|
const elements = {
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
@@ -22,6 +23,9 @@ export default {
|
|||||||
ol,
|
ol,
|
||||||
ul,
|
ul,
|
||||||
li,
|
li,
|
||||||
|
indent,
|
||||||
relationship,
|
relationship,
|
||||||
upload,
|
upload,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default elements;
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import { Editor, Element } from 'slate';
|
import { Editor, Element } from 'slate';
|
||||||
|
|
||||||
const isElementActive = (editor, format) => {
|
const isElementActive = (editor, format) => {
|
||||||
const [match] = Editor.nodes(editor, {
|
if (!editor.selection) return false;
|
||||||
match: (n) => Element.isElement(n) && n.type === format,
|
|
||||||
});
|
const [match] = Array.from(Editor.nodes(editor, {
|
||||||
|
at: Editor.unhangRange(editor, editor.selection),
|
||||||
|
match: (n) => !Editor.isEditor(n) && Element.isElement(n) && n.type === format,
|
||||||
|
}));
|
||||||
|
|
||||||
return !!match;
|
return !!match;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { Editor, Element } from 'slate';
|
||||||
|
|
||||||
|
const isListActive = (editor: Editor, format: string): boolean => {
|
||||||
|
if (!editor.selection
|
||||||
|
// If focus or anchor is at root of editor,
|
||||||
|
// Return false - as Editor.parent will fail
|
||||||
|
|| editor.selection.focus.path[1] === 0
|
||||||
|
|| editor.selection.anchor.path[1] === 0
|
||||||
|
) return false;
|
||||||
|
|
||||||
|
const parentLI = Editor.parent(editor, editor.selection);
|
||||||
|
|
||||||
|
if (parentLI[1].length > 0) {
|
||||||
|
const ancestor = Editor.above(editor, {
|
||||||
|
at: parentLI[1],
|
||||||
|
});
|
||||||
|
|
||||||
|
return Element.isElement(ancestor[0]) && ancestor[0].type === format;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default isListActive;
|
||||||
@@ -1,8 +1,19 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import listTypes from '../listTypes';
|
||||||
|
|
||||||
const LI = ({ attributes, children }) => (
|
const LI = (props) => {
|
||||||
<li {...attributes}>{children}</li>
|
const { attributes, element, children } = props;
|
||||||
);
|
const disableListStyle = element.children.length === 1 && listTypes.includes(element.children?.[0]?.type);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
style={{ listStyle: disableListStyle ? 'none' : undefined }}
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
Element: LI,
|
Element: LI,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ElementButton from '../Button';
|
import ListButton from '../ListButton';
|
||||||
import OLIcon from '../../../../../icons/OrderedList';
|
import OLIcon from '../../../../../icons/OrderedList';
|
||||||
|
|
||||||
const OL = ({ attributes, children }) => (
|
const OL = ({ attributes, children }) => (
|
||||||
@@ -8,9 +8,9 @@ const OL = ({ attributes, children }) => (
|
|||||||
|
|
||||||
const ol = {
|
const ol = {
|
||||||
Button: () => (
|
Button: () => (
|
||||||
<ElementButton format="ol">
|
<ListButton format="ol">
|
||||||
<OLIcon />
|
<OLIcon />
|
||||||
</ElementButton>
|
</ListButton>
|
||||||
),
|
),
|
||||||
Element: OL,
|
Element: OL,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const toggleElement = (editor, format) => {
|
|||||||
let type = format;
|
let type = format;
|
||||||
|
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
type = 'p';
|
type = undefined;
|
||||||
} else if (isList) {
|
} else if (isList) {
|
||||||
type = 'li';
|
type = 'li';
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,7 @@ const toggleElement = (editor, format) => {
|
|||||||
Transforms.unwrapNodes(editor, {
|
Transforms.unwrapNodes(editor, {
|
||||||
match: (n) => Element.isElement(n) && listTypes.includes(n.type as string),
|
match: (n) => Element.isElement(n) && listTypes.includes(n.type as string),
|
||||||
split: true,
|
split: true,
|
||||||
|
mode: 'lowest',
|
||||||
});
|
});
|
||||||
|
|
||||||
Transforms.setNodes(editor, { type });
|
Transforms.setNodes(editor, { type });
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ElementButton from '../Button';
|
|
||||||
import ULIcon from '../../../../../icons/UnorderedList';
|
import ULIcon from '../../../../../icons/UnorderedList';
|
||||||
|
import ListButton from '../ListButton';
|
||||||
|
|
||||||
const UL = ({ attributes, children }) => (
|
const UL = ({ attributes, children }) => (
|
||||||
<ul {...attributes}>{children}</ul>
|
<ul {...attributes}>{children}</ul>
|
||||||
@@ -8,9 +8,9 @@ const UL = ({ attributes, children }) => (
|
|||||||
|
|
||||||
const ul = {
|
const ul = {
|
||||||
Button: () => (
|
Button: () => (
|
||||||
<ElementButton format="ul">
|
<ListButton format="ul">
|
||||||
<ULIcon />
|
<ULIcon />
|
||||||
</ElementButton>
|
</ListButton>
|
||||||
),
|
),
|
||||||
Element: UL,
|
Element: UL,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
|
import { BaseEditor, Selection } from 'slate';
|
||||||
import { RichTextField } from '../../../../../fields/config/types';
|
import { RichTextField } from '../../../../../fields/config/types';
|
||||||
|
|
||||||
export type Props = Omit<RichTextField, 'type'> & {
|
export type Props = Omit<RichTextField, 'type'> & {
|
||||||
path?: string
|
path?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BlurSelectionEditor extends BaseEditor {
|
||||||
|
blurSelection?: Selection
|
||||||
|
}
|
||||||
|
|||||||
16
src/admin/components/icons/IndentLeft/index.scss
Normal file
16
src/admin/components/icons/IndentLeft/index.scss
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
@import '../../../scss/styles';
|
||||||
|
|
||||||
|
.icon--indent-left {
|
||||||
|
height: $baseline;
|
||||||
|
width: $baseline;
|
||||||
|
|
||||||
|
.stroke {
|
||||||
|
fill: none;
|
||||||
|
stroke: $color-dark-gray;
|
||||||
|
stroke-width: $style-stroke-width-m;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fill {
|
||||||
|
fill: $color-dark-gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/admin/components/icons/IndentLeft/index.tsx
Normal file
39
src/admin/components/icons/IndentLeft/index.tsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import './index.scss';
|
||||||
|
|
||||||
|
const IndentLeft: React.FC = () => (
|
||||||
|
<svg
|
||||||
|
className="icon icon--indent-left"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 25 25"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M16.005 9.61502L21.005 13.1864L21.005 6.04361L16.005 9.61502Z"
|
||||||
|
className="fill"
|
||||||
|
/>
|
||||||
|
<rect
|
||||||
|
x="5"
|
||||||
|
y="5.68199"
|
||||||
|
width="9.0675"
|
||||||
|
height="2.15625"
|
||||||
|
className="fill"
|
||||||
|
/>
|
||||||
|
<rect
|
||||||
|
x="5"
|
||||||
|
y="11.4738"
|
||||||
|
width="9.0675"
|
||||||
|
height="2.15625"
|
||||||
|
className="fill"
|
||||||
|
/>
|
||||||
|
<rect
|
||||||
|
x="5"
|
||||||
|
y="17.2656"
|
||||||
|
width="16.005"
|
||||||
|
height="2.15625"
|
||||||
|
className="fill"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default IndentLeft;
|
||||||
16
src/admin/components/icons/IndentRight/index.scss
Normal file
16
src/admin/components/icons/IndentRight/index.scss
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
@import '../../../scss/styles';
|
||||||
|
|
||||||
|
.icon--indent-right {
|
||||||
|
height: $baseline;
|
||||||
|
width: $baseline;
|
||||||
|
|
||||||
|
.stroke {
|
||||||
|
fill: none;
|
||||||
|
stroke: $color-dark-gray;
|
||||||
|
stroke-width: $style-stroke-width-m;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fill {
|
||||||
|
fill: $color-dark-gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/admin/components/icons/IndentRight/index.tsx
Normal file
39
src/admin/components/icons/IndentRight/index.tsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import './index.scss';
|
||||||
|
|
||||||
|
const IndentRight: React.FC = () => (
|
||||||
|
<svg
|
||||||
|
className="icon icon--indent-right"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 25 25"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M10 9.61502L5 6.04361L5 13.1864L10 9.61502Z"
|
||||||
|
fill="#333333"
|
||||||
|
/>
|
||||||
|
<rect
|
||||||
|
x="11.9375"
|
||||||
|
y="5.68199"
|
||||||
|
width="9.0675"
|
||||||
|
height="2.15625"
|
||||||
|
className="fill"
|
||||||
|
/>
|
||||||
|
<rect
|
||||||
|
x="11.9375"
|
||||||
|
y="11.4738"
|
||||||
|
width="9.0675"
|
||||||
|
height="2.15625"
|
||||||
|
className="fill"
|
||||||
|
/>
|
||||||
|
<rect
|
||||||
|
x="5"
|
||||||
|
y="17.2656"
|
||||||
|
width="16.005"
|
||||||
|
height="2.15625"
|
||||||
|
className="fill"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default IndentRight;
|
||||||
12
src/admin/components/icons/Swap/index.scss
Normal file
12
src/admin/components/icons/Swap/index.scss
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
@import '../../../scss/styles';
|
||||||
|
|
||||||
|
.icon--swap {
|
||||||
|
height: $baseline;
|
||||||
|
width: $baseline;
|
||||||
|
|
||||||
|
.stroke {
|
||||||
|
fill: none;
|
||||||
|
stroke: $color-dark-gray;
|
||||||
|
stroke-width: $style-stroke-width-m;
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/admin/components/icons/Swap/index.tsx
Normal file
37
src/admin/components/icons/Swap/index.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import './index.scss';
|
||||||
|
|
||||||
|
const Swap: React.FC = () => (
|
||||||
|
<svg
|
||||||
|
className="icon icon--swap"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 25 25"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M9.84631 4.78679L6.00004 8.63306L9.84631 12.4793"
|
||||||
|
className="stroke"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M15.1537 20.1059L19 16.2596L15.1537 12.4133"
|
||||||
|
className="stroke"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="7"
|
||||||
|
y1="8.7013"
|
||||||
|
x2="15"
|
||||||
|
y2="8.7013"
|
||||||
|
stroke="#333333"
|
||||||
|
className="stroke"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="18"
|
||||||
|
y1="16.1195"
|
||||||
|
x2="10"
|
||||||
|
y2="16.1195"
|
||||||
|
className="stroke"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Swap;
|
||||||
@@ -213,7 +213,7 @@ export type RichTextCustomLeaf = {
|
|||||||
plugins?: RichTextPlugin[]
|
plugins?: RichTextPlugin[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RichTextElement = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'blockquote' | 'ul' | 'ol' | 'link' | 'relationship' | 'upload' | RichTextCustomElement;
|
export type RichTextElement = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'blockquote' | 'ul' | 'ol' | 'link' | 'relationship' | 'upload' | 'indent' | RichTextCustomElement;
|
||||||
export type RichTextLeaf = 'bold' | 'italic' | 'underline' | 'strikethrough' | 'code' | RichTextCustomLeaf;
|
export type RichTextLeaf = 'bold' | 'italic' | 'underline' | 'strikethrough' | 'code' | RichTextCustomLeaf;
|
||||||
|
|
||||||
export type RichTextField = FieldBase & {
|
export type RichTextField = FieldBase & {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
export default [{
|
export default [{
|
||||||
|
type: 'p',
|
||||||
children: [{ text: '' }],
|
children: [{ text: '' }],
|
||||||
}];
|
}];
|
||||||
|
|||||||
16
yarn.lock
16
yarn.lock
@@ -11794,10 +11794,10 @@ slate-hyperscript@^0.66.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-plain-object "^5.0.0"
|
is-plain-object "^5.0.0"
|
||||||
|
|
||||||
slate-react@^0.66.4:
|
slate-react@^0.72.1:
|
||||||
version "0.66.7"
|
version "0.72.1"
|
||||||
resolved "https://registry.npmjs.org/slate-react/-/slate-react-0.66.7.tgz#033823dac8612e040094e7b8d942de6639b2e7de"
|
resolved "https://registry.npmjs.org/slate-react/-/slate-react-0.72.1.tgz#a18917625fa9ec87ae137b6f78bb36d04cdb732b"
|
||||||
integrity sha512-M0MGCnANdjRZCKGY9cvqHBR/WJ+/4SMIvBvEx4UJ5ycx9uNkDVPdpsyvwDKOpmsJuzZ1DH+34YrpxT7RnQyp1Q==
|
integrity sha512-H7bCem0xE0PHfaoWOcz18cQ0SZ/oTljAiEH4ygqaeYUjPOid6kAEJ8n28Psbp5g4njIgHTnHfVJGuPiTcKtKeA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/is-hotkey" "^0.1.1"
|
"@types/is-hotkey" "^0.1.1"
|
||||||
"@types/lodash" "^4.14.149"
|
"@types/lodash" "^4.14.149"
|
||||||
@@ -11808,10 +11808,10 @@ slate-react@^0.66.4:
|
|||||||
scroll-into-view-if-needed "^2.2.20"
|
scroll-into-view-if-needed "^2.2.20"
|
||||||
tiny-invariant "1.0.6"
|
tiny-invariant "1.0.6"
|
||||||
|
|
||||||
slate@^0.66.2:
|
slate@0.72.3:
|
||||||
version "0.66.5"
|
version "0.72.3"
|
||||||
resolved "https://registry.npmjs.org/slate/-/slate-0.66.5.tgz#bdd93a4422891341dd18a31ff7810f5cae60e40a"
|
resolved "https://registry.npmjs.org/slate/-/slate-0.72.3.tgz#c74eb85133b975b2d44d219462c127cdb992e76b"
|
||||||
integrity sha512-bG03uEKIm/gS6jQarKSNbHn2anemOON2vnSI3VGRd7MJJU5Yiwmutze0yHNO9uZwDLTB+LeDQYZeGu1ACWT0VA==
|
integrity sha512-ALsYQHKTN4rC+iHnOJzV+aC4AHdhoPkBWrfEK3W/LbXOzPrR+wL80a66OZiYg9Xb0QeGzlLSGdOOFQd2ix9Wmg==
|
||||||
dependencies:
|
dependencies:
|
||||||
immer "^9.0.6"
|
immer "^9.0.6"
|
||||||
is-plain-object "^5.0.0"
|
is-plain-object "^5.0.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user