cleans up the code and file structure
This commit is contained in:
@@ -1,40 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useSlate } from 'slate-react';
|
||||
import {
|
||||
ToolbarButton,
|
||||
isNodeInSelection,
|
||||
insertLink,
|
||||
} from '@udecode/slate-plugins';
|
||||
|
||||
import { nodeTypes } from '../../types';
|
||||
|
||||
const ToolbarLink = (props) => {
|
||||
const { typeLink, ...rest } = props;
|
||||
const editor = useSlate();
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
active={isNodeInSelection(editor, typeLink)}
|
||||
onMouseDown={(event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const url = window.prompt('Enter the URL of the link:');
|
||||
if (!url) return;
|
||||
|
||||
insertLink(editor, url, { typeLink });
|
||||
}}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
ToolbarLink.defaultProps = {
|
||||
typeLink: nodeTypes.UL,
|
||||
};
|
||||
|
||||
ToolbarLink.propTypes = {
|
||||
typeLink: PropTypes.string,
|
||||
};
|
||||
|
||||
export default ToolbarLink;
|
||||
@@ -10,17 +10,19 @@ import {
|
||||
import { nodeTypes } from '../../types';
|
||||
|
||||
const ToolbarList = (props) => {
|
||||
const { typeList, ...rest } = props;
|
||||
const editor = useSlate();
|
||||
const { typeList } = props;
|
||||
// const editor = useSlate();
|
||||
console.log('render');
|
||||
|
||||
return null;
|
||||
return (
|
||||
<ToolbarElement
|
||||
type={typeList}
|
||||
onMouseDown={getPreventDefaultHandler(toggleList, editor, {
|
||||
...rest,
|
||||
...props,
|
||||
typeList,
|
||||
})}
|
||||
{...rest}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,53 +1,29 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Transforms } from 'slate';
|
||||
import { useSlate, useFocused } from 'slate-react';
|
||||
import { ToolbarElement, ToolbarMark, ToolbarList } from '@udecode/slate-plugins';
|
||||
|
||||
import { nodeTypes, headingTypes } from '../types';
|
||||
import Icons from '../icons';
|
||||
import { nodeTypes } from '../types';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const baseClass = 'command-toolbar';
|
||||
|
||||
const enabledHeadings = [{ label: 'Normal Text', value: 'p' }];
|
||||
|
||||
const CommandToolbar = (props) => {
|
||||
const { enabledPluginList, maxHeadingLevel, disabledMarks } = props;
|
||||
const CommandToolbar = () => {
|
||||
const editor = useSlate();
|
||||
const editorFocus = useFocused();
|
||||
const [editorFocusedSelection, setEditorFocusedSelection] = useState(editorFocus);
|
||||
const headingsAreEnabled = enabledPluginList?.heading;
|
||||
const blockquoteIsEnabled = enabledPluginList?.blockquote;
|
||||
|
||||
const onHeadingSelectChange = (headingType) => {
|
||||
if (editorFocusedSelection) {
|
||||
Transforms.select(editor, editorFocusedSelection);
|
||||
editor.toggleType(headingType);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (editorFocus && editor.selection) setEditorFocusedSelection(editor.selection);
|
||||
if (editorFocus && editor.selection) {
|
||||
setEditorFocusedSelection(editor.selection);
|
||||
}
|
||||
}, [editorFocus, editorFocusedSelection, editor.selection]);
|
||||
|
||||
useEffect(() => {
|
||||
if (headingsAreEnabled) {
|
||||
Array.from(Array(maxHeadingLevel)).forEach((_, index) => {
|
||||
enabledHeadings.push({
|
||||
label: `Heading ${index + 1}`,
|
||||
value: headingTypes[index],
|
||||
});
|
||||
});
|
||||
}
|
||||
}, [headingsAreEnabled, maxHeadingLevel]);
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
|
||||
<div className={`${baseClass}__toggles`}>
|
||||
|
||||
<div className={`${baseClass}__elements toggle-section`}>
|
||||
<ToolbarElement
|
||||
type={nodeTypes.typeH1}
|
||||
icon={<Icons.H1 />}
|
||||
@@ -78,10 +54,22 @@ const CommandToolbar = (props) => {
|
||||
icon={<Icons.H6 />}
|
||||
/>
|
||||
|
||||
<ToolbarElement
|
||||
type={nodeTypes.typeCodeBlock}
|
||||
icon={<Icons.CodeBlock />}
|
||||
/>
|
||||
|
||||
<ToolbarElement
|
||||
type={nodeTypes.typeBlockquote}
|
||||
icon={<Icons.Blockquote />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={`${baseClass}__marks toggle-section`}>
|
||||
<ToolbarMark
|
||||
type={nodeTypes.typeCode}
|
||||
icon={<Icons.Code />}
|
||||
/>
|
||||
|
||||
<ToolbarMark
|
||||
type={nodeTypes.typeBold}
|
||||
@@ -102,7 +90,9 @@ const CommandToolbar = (props) => {
|
||||
type={nodeTypes.typeStrikethrough}
|
||||
icon={<Icons.Strikethrough />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={`${baseClass}__lists toggle-section`}>
|
||||
<ToolbarList
|
||||
{...nodeTypes}
|
||||
typeList={nodeTypes.typeUl}
|
||||
|
||||
@@ -4,23 +4,28 @@
|
||||
border-bottom: 1px solid $color-light-gray;
|
||||
padding: base(.5);
|
||||
background: $color-background-gray;
|
||||
display: flex;
|
||||
|
||||
&__marks,
|
||||
&__blocks,
|
||||
.react-select {
|
||||
display: inline-block;
|
||||
}
|
||||
.toggle-section {
|
||||
display: inline-flex;
|
||||
padding: 0 base(.5);
|
||||
|
||||
.react-select {
|
||||
margin-right: base(.25);
|
||||
}
|
||||
&:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
border-bottom: 1px solid $color-light-gray;
|
||||
+ .toggle-section {
|
||||
border-left: $style-stroke-width-m solid $color-light-gray;
|
||||
}
|
||||
}
|
||||
|
||||
.slate-ToolbarButton {
|
||||
display: inline-block;
|
||||
display: inline-flex;
|
||||
@include color-svg($color-gray);
|
||||
|
||||
&-active {
|
||||
border-radius: $style-radius-s;
|
||||
@include color-svg($color-dark-gray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
|
||||
const CodeBlockIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="graphic code-block-icon"
|
||||
>
|
||||
<path
|
||||
className="fill"
|
||||
d="M20 3H4c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2h16c1.103 0 2-.897 2-2V5c0-1.103-.897-2-2-2zM4 19V7h16l.002 12H4z"
|
||||
/>
|
||||
<path
|
||||
className="fill"
|
||||
d="M9.293 9.293L5.586 13l3.707 3.707 1.414-1.414L8.414 13l2.293-2.293zm5.414 0l-1.414 1.414L15.586 13l-2.293 2.293 1.414 1.414L18.414 13z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default CodeBlockIcon;
|
||||
@@ -10,7 +10,10 @@ const OrderedListIcon = () => {
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="graphic ordered-list-icon"
|
||||
>
|
||||
<path d="M2 17h2v.5H3v1h1v.5H2v1h3v-4H2v1zm1-9h1V4H2v1h1v3zm-1 3h1.8L2 13.1v.9h3v-1H3.2L5 10.9V10H2v1zm5-6v2h14V5H7zm0 14h14v-2H7v2zm0-6h14v-2H7v2z" />
|
||||
<path
|
||||
className="fill"
|
||||
d="M2 17h2v.5H3v1h1v.5H2v1h3v-4H2v1zm1-9h1V4H2v1h1v3zm-1 3h1.8L2 13.1v.9h3v-1H3.2L5 10.9V10H2v1zm5-6v2h14V5H7zm0 14h14v-2H7v2zm0-6h14v-2H7v2z"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
d="M0 0h24v24H0z"
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import Blockquote from './Blockquote';
|
||||
import Bold from './Bold';
|
||||
import Strikethrough from './Strikethrough';
|
||||
import OrderedList from './OrderedList';
|
||||
import UnorderedList from './UnorderedList';
|
||||
import Code from './Code';
|
||||
import Underline from './Underline';
|
||||
import Italic from './Italic';
|
||||
import CodeBlock from './CodeBlock';
|
||||
import Headings from './Headings';
|
||||
import Italic from './Italic';
|
||||
import OrderedList from './OrderedList';
|
||||
import Strikethrough from './Strikethrough';
|
||||
import Underline from './Underline';
|
||||
import UnorderedList from './UnorderedList';
|
||||
|
||||
const Icons = {
|
||||
Blockquote,
|
||||
Bold,
|
||||
Code,
|
||||
CodeBlock,
|
||||
H1: Headings.H1,
|
||||
H2: Headings.H2,
|
||||
H3: Headings.H3,
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
BlockquotePlugin,
|
||||
BoldPlugin,
|
||||
CodePlugin,
|
||||
CodeBlockPlugin,
|
||||
EditablePlugins,
|
||||
ExitBreakPlugin,
|
||||
HeadingPlugin,
|
||||
ImagePlugin,
|
||||
ItalicPlugin,
|
||||
@@ -19,13 +19,10 @@ import {
|
||||
StrikethroughPlugin,
|
||||
UnderlinePlugin,
|
||||
withList,
|
||||
withResetBlockType,
|
||||
withToggleType,
|
||||
} from '@udecode/slate-plugins';
|
||||
|
||||
// import BlockquotePlugin from './plugins/Elements/Blockquote/BlockquotePlugin';
|
||||
|
||||
import { nodeTypes, headingTypes } from './types';
|
||||
import { nodeTypes } from './types';
|
||||
import { richText } from '../../../../../fields/validations';
|
||||
import useFieldType from '../../useFieldType';
|
||||
import Label from '../../Label';
|
||||
@@ -38,29 +35,36 @@ const emptyRichTextNode = [{
|
||||
children: [{ text: '' }],
|
||||
}];
|
||||
|
||||
const enabledPluginList = {
|
||||
blockquote: options => BlockquotePlugin(options),
|
||||
bold: options => BoldPlugin(options),
|
||||
code: options => CodePlugin(options),
|
||||
heading: options => HeadingPlugin(options),
|
||||
image: options => ImagePlugin(options),
|
||||
italic: options => ItalicPlugin(options),
|
||||
list: options => ListPlugin(options),
|
||||
paragraph: options => ParagraphPlugin(options),
|
||||
strikethrough: options => StrikethroughPlugin(options),
|
||||
underline: options => UnderlinePlugin(options),
|
||||
};
|
||||
const plugins = [
|
||||
BlockquotePlugin(),
|
||||
BoldPlugin(),
|
||||
CodePlugin({ hotkey: 'mod+shift+c' }),
|
||||
CodeBlockPlugin(),
|
||||
HeadingPlugin(),
|
||||
ImagePlugin(),
|
||||
ItalicPlugin(),
|
||||
ListPlugin(),
|
||||
ParagraphPlugin(),
|
||||
StrikethroughPlugin(),
|
||||
UnderlinePlugin(),
|
||||
SoftBreakPlugin({
|
||||
rules: [
|
||||
{ hotkey: 'shift+enter' },
|
||||
{
|
||||
hotkey: 'enter',
|
||||
query: {
|
||||
allow: [nodeTypes.typeCodeBlock, nodeTypes.typeBlockquote],
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
];
|
||||
|
||||
const enabledPluginFunctions = [];
|
||||
const withPlugins = [
|
||||
withReact,
|
||||
withHistory,
|
||||
withList(nodeTypes),
|
||||
withToggleType({ defaultType: nodeTypes.typeP }),
|
||||
withResetBlockType({
|
||||
types: [nodeTypes.typeActionItem],
|
||||
defaultType: nodeTypes.typeP,
|
||||
}),
|
||||
];
|
||||
|
||||
const baseClass = 'rich-text';
|
||||
@@ -78,9 +82,6 @@ const RichText = (props) => {
|
||||
label,
|
||||
placeholder,
|
||||
readOnly,
|
||||
disabledPlugins,
|
||||
disabledMarks,
|
||||
maxHeadingLevel,
|
||||
} = props;
|
||||
|
||||
const editor = useMemo(() => pipe(createEditor(), ...withPlugins), []);
|
||||
@@ -108,61 +109,13 @@ const RichText = (props) => {
|
||||
useEffect(() => { setValue(internalState); }, [setValue, internalState]);
|
||||
|
||||
useEffect(() => {
|
||||
// ! could use review
|
||||
if (value !== undefined && !valueHasLoaded) {
|
||||
setInternalState(value);
|
||||
setValueHasLoaded(true);
|
||||
}
|
||||
}, [value, valueHasLoaded]);
|
||||
|
||||
useEffect(() => {
|
||||
// remove plugins disabled in config
|
||||
if (disabledPlugins.length > 0) {
|
||||
disabledPlugins.forEach((pluginKey) => {
|
||||
delete enabledPluginList[pluginKey];
|
||||
});
|
||||
}
|
||||
|
||||
// push the rest to enabledPlugins
|
||||
Object.keys(enabledPluginList).forEach((plugin) => {
|
||||
const options = { editor };
|
||||
if (plugin === 'heading' && maxHeadingLevel < 6) {
|
||||
options.levels = maxHeadingLevel;
|
||||
}
|
||||
|
||||
enabledPluginFunctions.push(enabledPluginList[plugin](options));
|
||||
enabledPluginFunctions.push(SoftBreakPlugin({
|
||||
rules: [
|
||||
{ hotkey: 'shift+enter' },
|
||||
{
|
||||
hotkey: 'enter',
|
||||
query: {
|
||||
allow: [nodeTypes.typeCodeBlock, nodeTypes.typeBlockquote],
|
||||
},
|
||||
},
|
||||
],
|
||||
}));
|
||||
});
|
||||
enabledPluginFunctions.push(ExitBreakPlugin({
|
||||
rules: [
|
||||
{
|
||||
hotkey: 'mod+enter',
|
||||
},
|
||||
{
|
||||
hotkey: 'mod+shift+enter',
|
||||
before: true,
|
||||
},
|
||||
// {
|
||||
// hotkey: 'enter',
|
||||
// query: {
|
||||
// start: true,
|
||||
// end: true,
|
||||
// allow: headingTypes,
|
||||
// },
|
||||
// },
|
||||
],
|
||||
}));
|
||||
}, [disabledPlugins, maxHeadingLevel]);
|
||||
|
||||
const classes = [
|
||||
baseClass,
|
||||
'field-type',
|
||||
@@ -193,14 +146,10 @@ const RichText = (props) => {
|
||||
value={internalState ?? emptyRichTextNode}
|
||||
onChange={val => setInternalState(val)}
|
||||
>
|
||||
<CommandToolbar
|
||||
enabledPluginList={enabledPluginList}
|
||||
disabledMarks={disabledMarks}
|
||||
maxHeadingLevel={maxHeadingLevel}
|
||||
/>
|
||||
<CommandToolbar enabledPluginList={plugins} />
|
||||
|
||||
<EditablePlugins
|
||||
plugins={enabledPluginFunctions}
|
||||
plugins={plugins}
|
||||
placeholder={placeholder}
|
||||
className={`${baseClass}__editor`}
|
||||
/>
|
||||
@@ -221,9 +170,6 @@ RichText.defaultProps = {
|
||||
style: {},
|
||||
validate: richText,
|
||||
path: '',
|
||||
disabledPlugins: [],
|
||||
disabledMarks: [],
|
||||
maxHeadingLevel: 6,
|
||||
};
|
||||
|
||||
RichText.propTypes = {
|
||||
@@ -238,9 +184,6 @@ RichText.propTypes = {
|
||||
width: PropTypes.string,
|
||||
style: PropTypes.shape({}),
|
||||
label: PropTypes.string,
|
||||
disabledPlugins: PropTypes.arrayOf(PropTypes.string),
|
||||
disabledMarks: PropTypes.arrayOf(PropTypes.string),
|
||||
maxHeadingLevel: PropTypes.number,
|
||||
};
|
||||
|
||||
export default RichText;
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import renderElementBlockquote from './renderElementBlockquote';
|
||||
import onKeyDown from './KeyDownHandler';
|
||||
|
||||
const BlockquotePlugin = (options = {}) => ({
|
||||
renderElement: renderElementBlockquote(options),
|
||||
onKeyDown: e => onKeyDown(e, options),
|
||||
});
|
||||
|
||||
export default BlockquotePlugin;
|
||||
@@ -1,3 +0,0 @@
|
||||
# Components
|
||||
|
||||
These are the components that make up the element to render, strictly visual.
|
||||
@@ -1,15 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import './blockquote.scss';
|
||||
|
||||
const BlockquoteElement = ({ attributes, children }) => {
|
||||
return <blockquote {...attributes}>{children}</blockquote>;
|
||||
};
|
||||
|
||||
BlockquoteElement.propTypes = {
|
||||
attributes: PropTypes.shape({}).isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
export default BlockquoteElement;
|
||||
@@ -1,6 +0,0 @@
|
||||
@import '../../../../../../../../scss/styles';
|
||||
|
||||
blockquote {
|
||||
padding: 10px 20px 10px 16px;
|
||||
border-left: 2px solid $color-light-gray;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// need to determine if current selection is within a blockquote
|
||||
// if it is, is it empty?
|
||||
// if its empty, is the key the delete key?
|
||||
// if the key === delete, toggle the element type off
|
||||
// if the key combo === 'mod+alt' insert soft break
|
||||
|
||||
// use editor, to get current selection and type
|
||||
|
||||
import { Editor } from 'slate';
|
||||
import {
|
||||
isNodeInSelection, isBlockTextEmpty, getTextFromBlockStartToAnchor,
|
||||
} from '@udecode/slate-plugins';
|
||||
|
||||
const onKeyDown = (e, options) => {
|
||||
const { editor } = options;
|
||||
const isInsideBlockquote = isNodeInSelection(editor, 'blockquote');
|
||||
const currentKey = e.key;
|
||||
|
||||
// check to see if we are in a blockquote
|
||||
if (!isInsideBlockquote) return null;
|
||||
const selectionText = getTextFromBlockStartToAnchor(editor);
|
||||
console.log(selectionText);
|
||||
// check to see if there is text inside the blockquote
|
||||
// const blockTextIsEmpty = isBlockTextEmpty(match);
|
||||
// console.log(blockTextIsEmpty);
|
||||
// if (blockTextIsEmpty) return null;
|
||||
// return 'test';
|
||||
};
|
||||
|
||||
export default onKeyDown;
|
||||
@@ -1,14 +0,0 @@
|
||||
import { getRenderElement } from '@udecode/slate-plugins';
|
||||
|
||||
import { nodeTypes } from '../../../types';
|
||||
import BlockquoteElement from './Components/blockquote';
|
||||
|
||||
const renderElementBlockquote = ({
|
||||
typeBlockquote = nodeTypes.typeBlockquote,
|
||||
component = BlockquoteElement,
|
||||
}) => getRenderElement({
|
||||
type: typeBlockquote,
|
||||
component,
|
||||
});
|
||||
|
||||
export default renderElementBlockquote;
|
||||
Reference in New Issue
Block a user