import styled from "@emotion/styled"; import * as React from "react"; import { IoIosPricetag } from "react-icons/io"; import { MdFormatBold, MdFormatItalic, MdFormatListBulleted, MdFormatQuote, MdFormatUnderlined, MdLooks3, MdLooksOne, MdLooksTwo, } from "react-icons/md"; import { Editor, Transforms } from "slate"; import { jsx } from "slate-hyperscript"; import { useSlate } from "slate-react"; import { DARK_TERTIARY_ONE, DARK_TERTIARY_THREE } from "../../../shared/colors"; import withPlatform, { PlatformProps } from "../../platform/withPlatform"; const LIST_TYPES = ["numbered-list", "bulleted-list"]; const ELEMENT_TAGS = { A: (el: any) => ({ type: "link", url: el.getAttribute("href") }), BLOCKQUOTE: () => ({ type: "quote" }), H1: () => ({ type: "heading-one" }), H2: () => ({ type: "heading-two" }), H3: () => ({ type: "heading-three" }), H4: () => ({ type: "heading-four" }), H5: () => ({ type: "heading-five" }), H6: () => ({ type: "heading-six" }), IMG: (el: any) => ({ type: "image", url: el.getAttribute("src") }), LI: () => ({ type: "list-item" }), OL: () => ({ type: "numbered-list" }), P: () => ({ type: "paragraph" }), PRE: () => ({ type: "code" }), UL: () => ({ type: "bulleted-list" }), }; // COMPAT: `B` is omitted here because Google Docs uses `` in weird ways. const TEXT_TAGS = { CODE: () => ({ code: true }), DEL: () => ({ strikethrough: true }), EM: () => ({ italic: true }), I: () => ({ italic: true }), S: () => ({ strikethrough: true }), STRONG: () => ({ bold: true }), U: () => ({ underline: true }), }; type Props = {} & PlatformProps; function EditorToolbar(props: Props) { return ( ); } export default withPlatform(EditorToolbar); const BlockButton = ({ format }: { format: string }) => { const editor = useSlate(); let iconComponent: any = null; switch (format) { case "heading-one": iconComponent = ; break; case "heading-two": iconComponent = ; break; case "heading-three": iconComponent = ; break; case "block-quote": iconComponent = ( ); break; case "bulleted-list": iconComponent = ( ); break; } return ( { toggleBlock(format, editor); e.preventDefault(); }} > {iconComponent} ); }; const MarkButton = ({ format }: { format: string }) => { const editor = useSlate(); let iconComponent: any = null; switch (format) { case "bold": iconComponent = ( ); break; case "italic": iconComponent = ( ); break; case "underline": iconComponent = ( ); break; case "highlight": iconComponent = ( ); break; } return ( { toggleMark(format, editor); e.preventDefault(); }} > {iconComponent} ); }; export const isMarkActive = (format: string, editor: Editor) => { const marks = Editor.marks(editor); return marks ? marks[format] === true : false; }; export const isBlockActive = (format: string, editor: Editor) => { const [match] = Editor.nodes(editor, { match: (n) => n.type === format, }) as any; return !!match; }; export const toggleBlock = (format: string, editor: Editor) => { const isActive = isBlockActive(format, editor); const isList = LIST_TYPES.includes(format); Transforms.unwrapNodes(editor, { match: (n) => LIST_TYPES.includes((n as any).type), split: true, }); Transforms.setNodes(editor, { type: isActive ? "paragraph" : isList ? "list-item" : format, }); if (!isActive && isList) { const block = { type: format, children: [] }; Transforms.wrapNodes(editor, block); } }; export const toggleMark = (format: string, editor: Editor) => { const isActive = isMarkActive(format, editor); isActive ? Editor.removeMark(editor, format) : Editor.addMark(editor, format, true); }; export const deserialize = (el: any) => { // Need the !== \n to block against Notion stuff for some reason if (el.nodeType === 3 && el.textContent !== "\n") { return el.textContent; } else if (el.nodeType !== 1) { return null; } else if (el.nodeName === "BR") { return "\n"; } const { nodeName } = el; let parent = el; if ( nodeName === "PRE" && el.childNodes[0] && el.childNodes[0].nodeName === "CODE" ) { parent = el.childNodes[0]; } const children: any = Array.from(parent.childNodes).map(deserialize).flat(); if (el.nodeName === "BODY") { return jsx("fragment", {}, children); } if ((ELEMENT_TAGS as any)[nodeName]) { const attrs = (ELEMENT_TAGS as any)[nodeName](el); return jsx("element", attrs, children); } if ((TEXT_TAGS as any)[nodeName]) { const attrs = (TEXT_TAGS as any)[nodeName](el); return children.map((child: any) => jsx("text", attrs, child)); } return children; }; const Toolbar = styled.div` position: relative; display: flex; justify-content: center; `; const StyleToggler = styled.div<{ active: boolean }>` cursor: pointer; height: 40px; width: 40px; color: ${(props) => (props.active ? DARK_TERTIARY_THREE : DARK_TERTIARY_ONE)}; `;