cleans up the code and file structure

This commit is contained in:
Jarrod Flesch
2020-06-15 17:10:01 -04:00
parent 58f6ff3aa6
commit 8feeb41a9b
17 changed files with 113 additions and 260 deletions

View File

@@ -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;

View File

@@ -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}
/>
);
};

View File

@@ -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}

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -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"

View File

@@ -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,

View File

@@ -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;

View File

@@ -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;

View File

@@ -1,3 +0,0 @@
# Components
These are the components that make up the element to render, strictly visual.

View File

@@ -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;

View File

@@ -1,6 +0,0 @@
@import '../../../../../../../../scss/styles';
blockquote {
padding: 10px 20px 10px 16px;
border-left: 2px solid $color-light-gray;
}

View File

@@ -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;

View File

@@ -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;