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