chore: icons
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2ZM17 13H13V17H11V13H7V11H11V7H13V11H17V13Z" fill="#667085"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 264 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M3 2H21C21.2652 2 21.5196 2.11706 21.7071 2.32544C21.8946 2.53381 22 2.81643 22 3.11111V20.8889C22 21.1836 21.8946 21.4662 21.7071 21.6746C21.5196 21.8829 21.2652 22 21 22H3C2.73478 22 2.48043 21.8829 2.29289 21.6746C2.10536 21.4662 2 21.1836 2 20.8889V3.11111C2 2.81643 2.10536 2.53381 2.29289 2.32544C2.48043 2.11706 2.73478 2 3 2ZM7 15.8889V11.4444L9 13.6667L11 11.4444V15.8889H13V8.11111H11L9 10.3333L7 8.11111H5V15.8889H7ZM18 12.5556V8.11111H16V12.5556H14L17 15.8889L20 12.5556H18Z" fill="#667085"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 617 B |
@@ -0,0 +1,33 @@
|
|||||||
|
const Element = ({ attributes, children, element }) => {
|
||||||
|
console.log("element type", element);
|
||||||
|
switch (element.type) {
|
||||||
|
case "del":
|
||||||
|
return <del {...attributes}>{children}</del>;
|
||||||
|
case "italic":
|
||||||
|
return <i {...attributes}>{children}</i>;
|
||||||
|
case "bold":
|
||||||
|
return <strong {...attributes}>{children}</strong>;
|
||||||
|
case "blockquote":
|
||||||
|
return <blockquote {...attributes}>{children}</blockquote>;
|
||||||
|
case "bulleted-list":
|
||||||
|
return <ul {...attributes}>{children}</ul>;
|
||||||
|
case "heading-one":
|
||||||
|
return <h1 {...attributes}>{children}</h1>;
|
||||||
|
case "heading-two":
|
||||||
|
return <h2 {...attributes}>{children}</h2>;
|
||||||
|
case "heading-three":
|
||||||
|
return <h3 {...attributes}>{children}</h3>;
|
||||||
|
case "heading-four":
|
||||||
|
return <h4 {...attributes}>{children}</h4>;
|
||||||
|
case "heading-five":
|
||||||
|
return <h5 {...attributes}>{children}</h5>;
|
||||||
|
case "heading-six":
|
||||||
|
return <h6 {...attributes}>{children}</h6>;
|
||||||
|
case "list-item":
|
||||||
|
return <li {...attributes}>{children}</li>;
|
||||||
|
default:
|
||||||
|
return <p {...attributes}>{children}</p>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Element;
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
// import React from "react";
|
||||||
|
// import styled from 'styled-components';
|
||||||
|
import AutoReplace from "slate-auto-replace";
|
||||||
|
import { useState, useRef, useCallback, useMemo, useEffect } from "react";
|
||||||
|
import { serialize } from "remark-slate";
|
||||||
|
import { Slate, Editable, withReact } from "slate-react";
|
||||||
|
import { createEditor, Transforms, Editor } from "slate";
|
||||||
|
import { withHistory } from "slate-history";
|
||||||
|
import { useKey } from "rooks";
|
||||||
|
import Styled from "./styled";
|
||||||
|
|
||||||
|
import initialValue, { emptyValue } from "./initialValue";
|
||||||
|
import Element from "./Element";
|
||||||
|
// import withShortcuts from "./withShortcuts";
|
||||||
|
|
||||||
|
// Add the plugin to your set of plugins...
|
||||||
|
const plugins = [
|
||||||
|
AutoReplace({
|
||||||
|
trigger: "space",
|
||||||
|
before: /^(>)$/,
|
||||||
|
change: (change, e, matches) => {
|
||||||
|
return change.setBlocks({ type: "blockquote" });
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function RichInput({
|
||||||
|
setEditor,
|
||||||
|
placeholder = "Write some markdown...",
|
||||||
|
updateMarkdown,
|
||||||
|
updatePureText,
|
||||||
|
sendMessage,
|
||||||
|
}) {
|
||||||
|
const editableRef = useRef(null);
|
||||||
|
const [shift, setShift] = useState(false);
|
||||||
|
const [value, setValue] = useState(emptyValue);
|
||||||
|
// const renderElement = useCallback((props) => <Element {...props} />, []);
|
||||||
|
const editor = useMemo(() => withReact(withHistory(createEditor())), []);
|
||||||
|
// const editor = useMemo(
|
||||||
|
// () => withShortcuts(withReact(withHistory(createEditor()))),
|
||||||
|
// []
|
||||||
|
// );
|
||||||
|
useEffect(() => {
|
||||||
|
setEditor(editor);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useKey(
|
||||||
|
"Enter",
|
||||||
|
(evt) => {
|
||||||
|
evt.preventDefault();
|
||||||
|
|
||||||
|
sendMessage();
|
||||||
|
Transforms.delete(editor, {
|
||||||
|
at: {
|
||||||
|
anchor: Editor.start(editor, []),
|
||||||
|
focus: Editor.end(editor, []),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: editableRef,
|
||||||
|
when: !shift,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
useKey(
|
||||||
|
"Shift",
|
||||||
|
(e) => {
|
||||||
|
console.log("shift", e.type);
|
||||||
|
setShift(e.type == "keydown");
|
||||||
|
},
|
||||||
|
{ eventTypes: ["keydown", "keyup"], target: editableRef }
|
||||||
|
);
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(nextValue) => {
|
||||||
|
setValue(nextValue);
|
||||||
|
// serialize slate state to a markdown string
|
||||||
|
const md = nextValue.map((v) => serialize(v)).join("\n");
|
||||||
|
updateMarkdown(md);
|
||||||
|
console.log("serialized", nextValue, md);
|
||||||
|
|
||||||
|
const nonParagraphs = nextValue.filter((v) => v.type != "paragraph");
|
||||||
|
if (nonParagraphs.length == 0) {
|
||||||
|
const nonEmptyPs = nextValue.filter((v) => !!v.children[0].text);
|
||||||
|
// 全部是P
|
||||||
|
const pureText = nonEmptyPs
|
||||||
|
.map((v) => {
|
||||||
|
return v.children.map(({ text }) => text).join("");
|
||||||
|
})
|
||||||
|
.join("\n");
|
||||||
|
console.log("pure text", nonEmptyPs, pureText);
|
||||||
|
updatePureText(pureText);
|
||||||
|
} else {
|
||||||
|
updatePureText("");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[updateMarkdown, updatePureText]
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("slate value", value);
|
||||||
|
return (
|
||||||
|
<Styled className="input" ref={editableRef}>
|
||||||
|
<Slate
|
||||||
|
editor={editor}
|
||||||
|
plugins={plugins}
|
||||||
|
value={value}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
|
<Editable
|
||||||
|
// plugins={plugins}
|
||||||
|
renderElement={Element}
|
||||||
|
placeholder={placeholder}
|
||||||
|
spellCheck
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
</Slate>
|
||||||
|
</Styled>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
const initialValue = [
|
||||||
|
{
|
||||||
|
type: "paragraph",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
text:
|
||||||
|
'The editor gives you full control over the logic you can add. For example, it\'s fairly common to want to add markdown-like shortcuts to editors. So that, when you start a line with "> " you get a blockquote that looks like this:',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "block-quote",
|
||||||
|
children: [{ text: "A wise quote." }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "paragraph",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
text:
|
||||||
|
'Order when you start a line with "## " you get a level-two heading, like this:',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "heading-two",
|
||||||
|
children: [{ text: "Try it out!" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "paragraph",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
text:
|
||||||
|
'Try it out for yourself! Try starting a new line with ">", "-", or "#"s.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
export const emptyValue = [
|
||||||
|
{
|
||||||
|
type: "paragraph",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
text: "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default initialValue;
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import styled from "styled-components";
|
||||||
|
const Styled = styled.div`
|
||||||
|
max-height: 50vh;
|
||||||
|
overflow: auto;
|
||||||
|
p {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
/* line-height: 20px; */
|
||||||
|
line-height: 22px;
|
||||||
|
color: #475467;
|
||||||
|
/* margin-bottom: 10px; */
|
||||||
|
}
|
||||||
|
i {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
/* del{
|
||||||
|
font-style: italic;
|
||||||
|
} */
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
color: #475467;
|
||||||
|
list-style-position: inside;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
list-style-type: disc;
|
||||||
|
}
|
||||||
|
ol {
|
||||||
|
/* list-style-type:; */
|
||||||
|
}
|
||||||
|
strong {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3 {
|
||||||
|
font-weight: 700;
|
||||||
|
color: #475467;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
blockquote {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: #98a2b3;
|
||||||
|
opacity: 0.8;
|
||||||
|
box-shadow: inset 2px 0px 0px #a8b0bd;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default Styled;
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
import {
|
||||||
|
Editor,
|
||||||
|
Transforms,
|
||||||
|
Range,
|
||||||
|
Point,
|
||||||
|
Element as SlateElement,
|
||||||
|
} from "slate";
|
||||||
|
const SHORTCUTS = {
|
||||||
|
"-": "list-item",
|
||||||
|
"+": "list-item",
|
||||||
|
"~~": "del",
|
||||||
|
_: "italic",
|
||||||
|
__: "bold",
|
||||||
|
">": "blockquote",
|
||||||
|
"#": "heading-one",
|
||||||
|
"##": "heading-two",
|
||||||
|
"###": "heading-three",
|
||||||
|
"####": "heading-four",
|
||||||
|
"#####": "heading-five",
|
||||||
|
"######": "heading-six",
|
||||||
|
};
|
||||||
|
const withShortcuts = (editor) => {
|
||||||
|
const { deleteBackward, insertText } = editor;
|
||||||
|
|
||||||
|
editor.insertText = (text) => {
|
||||||
|
const { selection } = editor;
|
||||||
|
console.log("insert text", text, selection, Range.isCollapsed(selection));
|
||||||
|
|
||||||
|
if (text === " " && selection && Range.isCollapsed(selection)) {
|
||||||
|
const { anchor } = selection;
|
||||||
|
const block = Editor.above(editor, {
|
||||||
|
match: (n) => Editor.isBlock(editor, n),
|
||||||
|
});
|
||||||
|
const path = block ? block[1] : [];
|
||||||
|
const start = Editor.start(editor, path);
|
||||||
|
const range = { anchor, focus: start };
|
||||||
|
const beforeText = Editor.string(editor, range);
|
||||||
|
const type = SHORTCUTS[beforeText];
|
||||||
|
console.log(
|
||||||
|
"insert text parsed",
|
||||||
|
block,
|
||||||
|
path,
|
||||||
|
start,
|
||||||
|
range,
|
||||||
|
beforeText,
|
||||||
|
type
|
||||||
|
);
|
||||||
|
if (type) {
|
||||||
|
Transforms.select(editor, range);
|
||||||
|
Transforms.delete(editor);
|
||||||
|
const newProperties = {
|
||||||
|
type,
|
||||||
|
};
|
||||||
|
Transforms.setNodes(editor, newProperties, {
|
||||||
|
match: (n) => Editor.isBlock(editor, n),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (type === "list-item") {
|
||||||
|
const list = {
|
||||||
|
type: "bulleted-list",
|
||||||
|
children: [],
|
||||||
|
};
|
||||||
|
Transforms.wrapNodes(editor, list, {
|
||||||
|
match: (n) =>
|
||||||
|
!Editor.isEditor(n) &&
|
||||||
|
SlateElement.isElement(n) &&
|
||||||
|
n.type === "list-item",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
insertText(text);
|
||||||
|
};
|
||||||
|
|
||||||
|
editor.deleteBackward = (...args) => {
|
||||||
|
const { selection } = editor;
|
||||||
|
if (selection && Range.isCollapsed(selection)) {
|
||||||
|
const match = Editor.above(editor, {
|
||||||
|
match: (n) => Editor.isBlock(editor, n),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
const [block, path] = match;
|
||||||
|
const start = Editor.start(editor, path);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!Editor.isEditor(block) &&
|
||||||
|
SlateElement.isElement(block) &&
|
||||||
|
block.type !== "paragraph" &&
|
||||||
|
Point.equals(selection.anchor, start)
|
||||||
|
) {
|
||||||
|
const newProperties = {
|
||||||
|
type: "paragraph",
|
||||||
|
};
|
||||||
|
Transforms.setNodes(editor, newProperties);
|
||||||
|
|
||||||
|
if (block.type === "list-item") {
|
||||||
|
Transforms.unwrapNodes(editor, {
|
||||||
|
match: (n) =>
|
||||||
|
!Editor.isEditor(n) &&
|
||||||
|
SlateElement.isElement(n) &&
|
||||||
|
n.type === "bulleted-list",
|
||||||
|
split: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteBackward(...args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return editor;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withShortcuts;
|
||||||
Reference in New Issue
Block a user