From d232979eda01d7ecf81de1399d2e3944594a7523 Mon Sep 17 00:00:00 2001 From: wildlifehexagon Date: Wed, 21 Jul 2021 15:40:10 -0500 Subject: [PATCH] fix: use sticky appbar --- dist/dicty-components-page-editor.cjs.development.js | 4 +--- dist/dicty-components-page-editor.cjs.development.js.map | 2 +- dist/dicty-components-page-editor.cjs.production.min.js | 2 +- dist/dicty-components-page-editor.cjs.production.min.js.map | 2 +- dist/dicty-components-page-editor.esm.js | 4 +--- dist/dicty-components-page-editor.esm.js.map | 2 +- src/components/Toolbar.tsx | 4 +--- 7 files changed, 7 insertions(+), 13 deletions(-) diff --git a/dist/dicty-components-page-editor.cjs.development.js b/dist/dicty-components-page-editor.cjs.development.js index e92046a9..5fdeca97 100644 --- a/dist/dicty-components-page-editor.cjs.development.js +++ b/dist/dicty-components-page-editor.cjs.development.js @@ -2096,8 +2096,6 @@ var useStyles$7 = /*#__PURE__*/styles.makeStyles(function (theme) { marginBottom: theme.spacing(1) }, toolbar: { - position: "sticky", - top: 0, cursor: "default" }, divider: { @@ -2144,7 +2142,7 @@ var EditorToolbar = function EditorToolbar() { var classes = useStyles$7(); return React__default.createElement(AppBar, { color: "default", - position: "static", + position: "sticky", className: classes.container }, React__default.createElement(Toolbar, { disableGutters: true, diff --git a/dist/dicty-components-page-editor.cjs.development.js.map b/dist/dicty-components-page-editor.cjs.development.js.map index a5193253..7c098daf 100644 --- a/dist/dicty-components-page-editor.cjs.development.js.map +++ b/dist/dicty-components-page-editor.cjs.development.js.map @@ -1 +1 @@ -{"version":3,"file":"dicty-components-page-editor.cjs.development.js","sources":["../src/styles/buttons.ts","../src/components/buttons/MarkButton.tsx","../src/constants/types.ts","../src/utils/blocks.ts","../src/components/buttons/BlockButton.tsx","../src/components/buttons/AlignButton.tsx","../src/styles/dialog.ts","../src/components/dialogs/LinkDialog.tsx","../src/utils/links.ts","../src/hooks/useLinks.ts","../src/components/buttons/LinkButton.tsx","../src/hooks/useAnchorElement.ts","../src/components/buttons/AutolinkIDsButton.tsx","../src/components/icons/FontColorIcon.tsx","../src/components/dropdowns/ColorPicker.tsx","../src/utils/getCurrentMark.ts","../src/components/buttons/FontColorButton.tsx","../src/components/icons/CheckIcon.tsx","../src/utils/dropdownValues.ts","../src/utils/getParentNode.ts","../src/components/buttons/LineSpacingButton.tsx","../src/components/dialogs/ImageDialog.tsx","../src/components/buttons/ImageButton.tsx","../src/components/dialogs/VideoDialog.tsx","../src/utils/getVideoID.ts","../src/components/buttons/VideoButton.tsx","../src/components/icons/ScientificSymbolIcon.tsx","../src/components/buttons/ScientificSymbolsButton.tsx","../src/components/icons/BoldIcon.tsx","../src/components/icons/ItalicIcon.tsx","../src/components/icons/StrikethroughIcon.tsx","../src/components/icons/SubscriptIcon.tsx","../src/components/icons/SuperscriptIcon.tsx","../src/components/icons/UnderlinedIcon.tsx","../src/components/icons/H1Icon.tsx","../src/components/icons/H2Icon.tsx","../src/components/icons/H3Icon.tsx","../src/components/icons/LinkIcon.tsx","../src/components/icons/DividerIcon.tsx","../src/components/icons/LineSpacingIcon.tsx","../src/components/icons/ImageIcon.tsx","../src/components/icons/VideoIcon.tsx","../src/components/icons/UnorderedListIcon.tsx","../src/components/icons/OrderedListIcon.tsx","../src/components/icons/IndentIncreaseIcon.tsx","../src/components/icons/IndentDecreaseIcon.tsx","../src/components/icons/AlignLeftIcon.tsx","../src/components/icons/AlignCenterIcon.tsx","../src/components/icons/AlignRightIcon.tsx","../src/components/icons/AlignJustifyIcon.tsx","../src/components/dropdowns/Dropdown.tsx","../src/utils/lists.ts","../src/plugins/withLists.ts","../src/components/icons/TableIcon.tsx","../src/components/icons/TableInsertColumnIcon.tsx","../src/components/icons/TableInsertRowIcon.tsx","../src/components/icons/TableDeleteColumnIcon.tsx","../src/components/icons/TableDeleteRowIcon.tsx","../src/components/icons/DeleteIcon.tsx","../src/utils/tables.ts","../src/components/buttons/TableButtons.tsx","../src/components/Separator.tsx","../src/components/Toolbar.tsx","../src/components/InlineToolbar.tsx","../src/styles/media.ts","../src/components/Image.tsx","../src/components/Video.tsx","../src/components/Element.tsx","../src/utils/getFontSize.ts","../src/components/Leaf.tsx","../src/components/buttons/ActionButton.tsx","../src/components/ActionButtons.tsx","../src/utils/deserialize.ts","../src/plugins/withHTML.ts","../src/plugins/withLinks.ts","../src/plugins/withMedia.ts","../src/plugins/withNormalize.ts","../src/utils/onKeyDown.ts","../src/styles/theme.ts","../src/components/PageEditor.tsx"],"sourcesContent":["import { makeStyles } from \"@material-ui/core/styles\"\n\ntype StyleProps = {\n active: boolean | unknown\n}\n\nconst useStyles = makeStyles(() => ({\n button: (props?: StyleProps) => ({\n color: props?.active ? \"#000\" : \"rgba(0, 0, 0, 0.54)\",\n }),\n}))\n\nexport default useStyles\n","import React, { MouseEvent } from \"react\"\nimport { Editor } from \"slate\"\nimport { useSlate } from \"slate-react\"\nimport IconButton from \"@material-ui/core/IconButton\"\nimport Tooltip from \"@material-ui/core/Tooltip\"\nimport { CustomEditor } from \"../../types/editor\"\nimport useStyles from \"../../styles/buttons\"\n\n/**\n * isMarkActive determines if the current text selection contains an\n * active mark\n */\nconst isMarkActive = (editor: CustomEditor, format: string) => {\n // get a list of marks from the selected text\n const marks = Editor.marks(editor)\n\n // if there are marks for specified format then the mark is active\n if (marks && marks[format]) {\n return true\n } else {\n return false\n }\n}\n\n/**\n * toggleMark will either remove or add a mark to the given text selection\n */\nconst toggleMark = (editor: CustomEditor, format: string) => {\n // first find if the selection's mark is currently active\n const isActive = isMarkActive(editor, format)\n\n // we either want to add or remove a mark based on whether it is currently active\n if (isActive) {\n Editor.removeMark(editor, format)\n } else {\n Editor.addMark(editor, format, true)\n }\n}\n\ntype Props = {\n /** Type of mark (i.e. \"bold\") */\n format: string\n /** Icon to display in button */\n icon: JSX.Element\n}\n\n/**\n * MarkButton displays a button with associated click logic for toggling a mark.\n */\nconst MarkButton = ({ format, icon }: Props) => {\n const editor = useSlate()\n const props = {\n active: isMarkActive(editor, format),\n }\n const classes = useStyles(props)\n\n // when button is clicked, toggle the mark within the editor\n const handleMouseDown = (event: MouseEvent) => {\n event.preventDefault()\n toggleMark(editor, format)\n }\n\n return (\n \n \n {icon}\n \n \n )\n}\n\nexport default MarkButton\n","const types = {\n // marks\n bold: \"bold\",\n italic: \"italic\",\n underline: \"underline\",\n strikethrough: \"strikethrough\",\n subscript: \"subscript\",\n superscript: \"superscript\",\n // inline\n link: \"link\",\n // blocks\n paragraph: \"paragraph\",\n h1: \"h1\",\n h2: \"h2\",\n h3: \"h3\",\n divider: \"divider\",\n lineSpacing: \"lineSpacing\",\n image: \"image\",\n video: \"video\",\n orderedList: \"orderedList\",\n unorderedList: \"unorderedList\",\n listItem: \"listItem\",\n indentDecrease: \"indentDecrease\",\n indentIncrease: \"indentIncrease\",\n tableWrap: \"tableWrap\",\n table: \"table\",\n tableRow: \"tableRow\",\n tableColumn: \"tableColumn\",\n tableCell: \"tableCell\",\n tableDelete: \"tableDelete\",\n tableRowDelete: \"tableRowDelete\",\n tableColumnDelete: \"tableColumnDelete\",\n}\n\nconst alignments = {\n left: \"left\",\n center: \"center\",\n right: \"right\",\n justify: \"justify\",\n}\n\nconst attributes = {\n borderColor: \"borderColor\",\n fontColor: \"fontColor\",\n}\n\nexport { types, alignments, attributes }\n","import { Editor, Transforms, Element as SlateElement } from \"slate\"\nimport { types } from \"../constants/types\"\n\n/**\n * isBlockActive determines if the current text selection contains an active block\n */\nconst isBlockActive = (editor: Editor, property: string, value: string) => {\n // convert nodes iterator to array and get first result\n const [match] = Array.from(\n Editor.nodes(editor, {\n match: (n) =>\n !Editor.isEditor(n) &&\n SlateElement.isElement(n) &&\n n[property] === value,\n }),\n )\n // return boolean to indicate if match was found\n return !!match\n}\n\n/**\n * toggleBlock will set the appropriate nodes for the given selection\n */\nconst toggleBlock = (editor: Editor, format: string) => {\n // first find if the selected block is currently active\n const isActive = isBlockActive(editor, \"type\", format)\n\n // setNodes is used to set properties at the currently selected element.\n // If the block is active, then we want to toggle it back to the default\n // paragraph type. If the block is not active, we toggle the type to match it.\n Transforms.setNodes(editor, {\n type: isActive ? types.paragraph : format,\n })\n}\n\nexport { isBlockActive, toggleBlock }\n","import React, { MouseEvent } from \"react\"\nimport { useSlate } from \"slate-react\"\nimport IconButton from \"@material-ui/core/IconButton\"\nimport Tooltip from \"@material-ui/core/Tooltip\"\nimport { isBlockActive } from \"../../utils/blocks\"\nimport useStyles from \"../../styles/buttons\"\n\n/**\n * PROCESS:\n *\n * 1. User clicks button\n * 2. Use generator function to find any matching nodes for that block type.\n * 3. If there are no matches then we do not mark that block as active.\n * 4. If the block is not active, then we set the nodes to match that format type.\n * 5. If there is a match, we mark that block as active for the first matching node.\n * 6. If the block is active, then we set the nodes back to the default type of\n * 'paragraph'.\n */\n\ntype Props = {\n /** Type of block (i.e. \"h1\") */\n format: string\n /** Icon to display in button */\n icon: JSX.Element\n /** Function to call when button is clicked */\n clickFn: () => void\n}\n\n/**\n * BlockButton displays a button with associated click logic for toggling a\n * block.\n */\nconst BlockButton = ({ format, icon, clickFn }: Props) => {\n const editor = useSlate()\n const props = {\n active: isBlockActive(editor, \"type\", format),\n }\n const classes = useStyles(props)\n\n // when button is clicked, toggle the block within the editor\n const handleMouseDown = (event: MouseEvent) => {\n event.preventDefault()\n clickFn()\n }\n\n return (\n \n \n {icon}\n \n \n )\n}\n\nexport default BlockButton\n","import React, { MouseEvent } from \"react\"\nimport { Editor, Transforms } from \"slate\"\nimport { useSlate } from \"slate-react\"\nimport IconButton from \"@material-ui/core/IconButton\"\nimport Tooltip from \"@material-ui/core/Tooltip\"\nimport useStyles from \"../../styles/buttons\"\nimport { isBlockActive } from \"../../utils/blocks\"\n\nconst toggleAlign = (editor: Editor, align: string) => {\n const isActive = isBlockActive(editor, \"align\", align)\n\n Transforms.setNodes(editor, {\n align: isActive ? \"left\" : align,\n })\n}\n\ntype Props = {\n /** Icon to display in button */\n icon: JSX.Element\n /** Text alignment property */\n align: string\n}\n\n/**\n * AlignButton displays a button with associated logic for adding the \"align\"\n * attribute.\n */\nconst AlignButton = ({ icon, align }: Props) => {\n const editor = useSlate()\n const props = {\n active: isBlockActive(editor, \"align\", align),\n }\n const classes = useStyles(props)\n\n const handleMouseDown = (event: MouseEvent) => {\n event.preventDefault()\n toggleAlign(editor, align)\n }\n\n return (\n \n \n {icon}\n \n \n )\n}\n\nexport default AlignButton\n","import { makeStyles } from \"@material-ui/core/styles\"\n\nconst useStyles = makeStyles(() => ({\n button: {\n textTransform: \"none\",\n },\n}))\n\nexport default useStyles\n","import React from \"react\"\nimport TextField from \"@material-ui/core/TextField\"\nimport Button from \"@material-ui/core/Button\"\nimport Dialog from \"@material-ui/core/Dialog\"\nimport DialogActions from \"@material-ui/core/DialogActions\"\nimport DialogContent from \"@material-ui/core/DialogContent\"\nimport DialogTitle from \"@material-ui/core/DialogTitle\"\nimport useStyles from \"../../styles/dialog\"\nimport { LinkDialogProps } from \"../../types/dialog\"\n\nconst LinkDialog = ({\n handleAddClick,\n handleClose,\n dialogOpen,\n link,\n setLink,\n}: LinkDialogProps) => {\n const classes = useStyles()\n\n return (\n \n Link Details\n \n \n setLink({\n url: e.target.value,\n text: link.text,\n })\n }\n fullWidth\n />\n \n setLink({\n text: e.target.value,\n url: link.url,\n })\n }\n fullWidth\n />\n \n \n \n Add Link\n \n \n \n )\n}\n\nexport default LinkDialog\n","import { Editor, Element as SlateElement, Transforms, Range, Node } from \"slate\"\nimport { Link } from \"../types/link\"\nimport { types } from \"../constants/types\"\n\n// look for a match of the link type\nconst linkNodeOptions = {\n match: (n: Node) =>\n !Editor.isEditor(n) && SlateElement.isElement(n) && n.type === types.link,\n}\n\nconst isLinkActive = (editor: Editor) => {\n const [match] = Array.from(Editor.nodes(editor, linkNodeOptions))\n return !!match\n}\n\n/**\n * upsertLink updates or adds a new link. If there is no selection,\n * it adds a new link with the provided text. Otherwise it will wrap the\n * selection with a link node using the user's link and text.\n */\nconst upsertLink = (editor: Editor, link: Link, fontColor: string) => {\n const { url, text } = link\n // check if there is an existing link first then unwrap it\n if (isLinkActive(editor)) {\n Transforms.unwrapNodes(editor, linkNodeOptions)\n }\n\n const linkData = {\n type: types.link,\n url,\n children: [{ text: text, fontColor: fontColor }],\n }\n\n const { selection } = editor\n const isCollapsed = selection && Range.isCollapsed(selection)\n\n if (isCollapsed) {\n Transforms.insertNodes(editor, linkData)\n } else {\n Transforms.wrapNodes(editor, linkData, { split: true })\n Editor.insertText(editor, text)\n Transforms.collapse(editor, { edge: \"end\" })\n }\n}\n\n// getLinkSelection gets the current text and URL for the user's current selection.\nconst getLinkSelection = (editor: Editor) => {\n const { selection } = editor\n let prevURL,\n selectedText = \"\"\n // if there is a current selection then pull the text and URL from it\n // and update state accordingly\n if (selection && !Range.isCollapsed(selection)) {\n selectedText = Editor.string(editor, selection)\n const [linkNode] = Array.from(Editor.nodes(editor, linkNodeOptions))\n if (linkNode && SlateElement.isElement(linkNode[0])) {\n prevURL = linkNode[0].url as string\n }\n }\n return {\n url: prevURL || \"\",\n text: selectedText,\n }\n}\n\nexport { linkNodeOptions, isLinkActive, upsertLink, getLinkSelection }\n","import React from \"react\"\nimport { useSlate } from \"slate-react\"\nimport { useTheme } from \"@material-ui/core/styles\"\nimport { upsertLink } from \"../utils/links\"\n\n// useLinks is a hook for internal link state logic.\nconst useLinks = () => {\n const editor = useSlate()\n const theme = useTheme()\n const [linkDialogOpen, setLinkDialogOpen] = React.useState(false)\n const [link, setLink] = React.useState({\n url: \"\",\n text: \"\",\n })\n\n const handleAddLink = () => {\n upsertLink(editor, link, theme.palette.primary.main)\n setLinkDialogOpen(false)\n }\n\n return {\n link,\n setLink,\n linkDialogOpen,\n setLinkDialogOpen,\n handleAddLink,\n }\n}\n\nexport default useLinks\n","import React from \"react\"\nimport { Transforms } from \"slate\"\nimport { useSlate } from \"slate-react\"\nimport IconButton from \"@material-ui/core/IconButton\"\nimport Tooltip from \"@material-ui/core/Tooltip\"\nimport LinkDialog from \"../dialogs/LinkDialog\"\nimport useLinks from \"../../hooks/useLinks\"\nimport { isLinkActive, getLinkSelection } from \"../../utils/links\"\nimport useStyles from \"../../styles/buttons\"\n\n// this is necessary to maintain editor selection when link dialog appears;\n// the deselect method unsets the editor selection\nTransforms.deselect = () => {}\n\ntype Props = {\n /** Icon to display in button */\n icon: JSX.Element\n}\n\n/**\n * LinkButton is a button specifically for adding links.\n */\nconst LinkButton = ({ icon }: Props) => {\n const editor = useSlate()\n const { link, setLink, linkDialogOpen, setLinkDialogOpen, handleAddLink } =\n useLinks()\n const props = {\n active: isLinkActive(editor),\n }\n const classes = useStyles(props)\n\n const handleMouseDown = () => {\n const link = getLinkSelection(editor)\n setLink(link)\n setLinkDialogOpen(true)\n }\n\n // if the user has clicked away without adding the link then we don't need to do anything with their data\n const handleClose = () => setLinkDialogOpen(false)\n\n return (\n \n \n \n {icon}\n \n \n \n \n )\n}\n\nexport default LinkButton\n","import React from \"react\"\n\n// useAnchorElement contains state logic and associated functions for components\n// requiring an anchor element (i.e. dropdown menus, popovers)\nconst useAnchorElement = () => {\n const [anchorEl, setAnchorEl] = React.useState(null)\n\n const handleMouseDown = (event: React.MouseEvent) => {\n setAnchorEl(event.currentTarget)\n }\n\n const handleClose = (_: {}, reason: \"backdropClick\" | \"escapeKeyDown\") => {\n if (reason === \"backdropClick\" || reason === \"escapeKeyDown\") {\n setAnchorEl(null)\n }\n }\n\n return {\n anchorEl,\n setAnchorEl,\n handleMouseDown,\n handleClose,\n }\n}\n\nexport default useAnchorElement\n","import React from \"react\"\nimport { useSlate } from \"slate-react\"\nimport { useTheme } from \"@material-ui/core/styles\"\nimport IconButton from \"@material-ui/core/IconButton\"\nimport Tooltip from \"@material-ui/core/Tooltip\"\nimport Menu from \"@material-ui/core/Menu\"\nimport MenuItem from \"@material-ui/core/MenuItem\"\nimport useAnchorElement from \"../../hooks/useAnchorElement\"\nimport { getLinkSelection, upsertLink } from \"../../utils/links\"\n\nconst ids = {\n pubmed: \"PubMed\",\n go: \"GO\",\n gene: \"Gene\",\n strain: \"Strain ID\",\n plasmid: \"Plasmid ID\",\n}\n\nconst idList = [ids.pubmed, ids.go, ids.gene, ids.strain, ids.plasmid]\n\nconst getURLPrefix = (item: string) => {\n let prefix = \"\"\n switch (item) {\n case ids.pubmed:\n prefix = \"/publication/\"\n break\n case ids.go:\n prefix = \"https://www.ebi.ac.uk/QuickGO/term/\"\n break\n case ids.gene:\n prefix = \"/gene/\"\n break\n case ids.strain:\n prefix = \"/stockcenter/strains/\"\n break\n case ids.plasmid:\n prefix = \"/stockcenter/plasmids/\"\n break\n default:\n return prefix\n }\n return prefix\n}\n\nconst validateText = (item: string, text: string) => {\n let valid = false\n switch (item) {\n case ids.pubmed:\n // check if only numbers\n valid = /^\\d+$/.test(text)\n break\n case ids.go:\n valid = /GO:[0-9]+/.test(text)\n break\n case ids.gene:\n valid = true\n break\n case ids.strain:\n valid = /DBS[0-9]+/.test(text)\n break\n case ids.plasmid:\n valid = /DBP[0-9]+/.test(text)\n break\n default:\n return valid\n }\n return valid\n}\n\n/**\n * AutolinkIDsButton displays a button and dropdown for IDs that can be autolinked.\n */\nconst AutolinkIDsButton = () => {\n const editor = useSlate()\n const theme = useTheme()\n const { anchorEl, setAnchorEl, handleMouseDown } = useAnchorElement()\n\n const handleItemClick = (item: string) => {\n let link = getLinkSelection(editor)\n const prefix = getURLPrefix(item)\n // if the selected text is not a valid ID then throw error\n if (!validateText(item, link.text)) {\n alert(`${link.text} is not a valid ID for ${item}`)\n setAnchorEl(null)\n return\n }\n // if selected link URL doesn't have a prefix then add it\n if (!link.url.includes(prefix)) {\n link.url = `${prefix}${link.text}`\n }\n\n upsertLink(editor, link, theme.palette.primary.main)\n setAnchorEl(null)\n }\n\n return (\n \n \n \n DB\n \n \n setAnchorEl(null)}>\n {idList.map((item: string, index: number) => {\n return (\n handleItemClick(item)}>\n {item}\n \n )\n })}\n \n \n )\n}\n\nexport { ids, getURLPrefix, validateText }\nexport default AutolinkIDsButton\n","import React from \"react\"\nimport SvgIcon from \"@material-ui/core/SvgIcon\"\n\nconst FontColorIcon = () => {\n return (\n \n \n \n )\n}\n\nexport default FontColorIcon\n","import React from \"react\"\nimport { makeStyles, useTheme, Theme } from \"@material-ui/core/styles\"\nimport IconButton from \"@material-ui/core/IconButton\"\nimport { HexColorPicker, HexColorInput } from \"react-colorful\"\n\n// get list of preset colors to show beneath picker\nconst getPresetColors = (theme: Theme) => {\n const { palette } = theme\n return [\n palette.primary.main,\n palette.secondary.main,\n palette.error.main,\n palette.warning.main,\n palette.info.main,\n palette.success.main,\n ]\n}\n\nconst useStyles = makeStyles((theme: Theme) => ({\n buttonContainer: {\n display: \"flex\",\n flexDirection: \"row\",\n },\n button: {\n width: \"24px\",\n height: \"24px\",\n padding: \"0px\",\n margin: theme.spacing(0.5),\n cursor: \"pointer\",\n },\n input: {\n width: \"90%\",\n textTransform: \"uppercase\",\n padding: theme.spacing(1),\n marginTop: theme.spacing(1),\n marginBottom: theme.spacing(1),\n borderRadius: \"4px\",\n },\n popper: {\n padding: theme.spacing(2),\n },\n}))\n\ntype Props = {\n /** Function to call when color is selected */\n handleChange: (value: string) => void\n /** Active color of current text selection */\n activeColor: string\n}\n\n/**\n * ColorPicker handles the display of the color picker.\n */\nconst ColorPicker = ({ handleChange, activeColor }: Props) => {\n const theme = useTheme()\n const classes = useStyles()\n const presetColors = getPresetColors(theme)\n\n return (\n
\n \n \n
\n {presetColors.map((color: string) => (\n handleChange(color)}\n />\n ))}\n
\n
\n )\n}\n\nexport default ColorPicker\n","import { Editor } from \"slate\"\n\n// adds additional check so an inherited font size displays the default \"1rem\"\n// in the dropdown menu\nconst getMarkValue = (value: string) => {\n if (value === \"inherit\") {\n return \"1rem\"\n }\n return value\n}\n\n// get the current mark for a given selection\nconst getCurrentMark = (editor: Editor, mark: string) => {\n const marks = Editor.marks(editor)\n if (!marks || !marks[mark]) {\n return\n }\n return getMarkValue(marks[mark])\n}\n\nexport default getCurrentMark\n","import React from \"react\"\nimport { Editor } from \"slate\"\nimport { useSlate } from \"slate-react\"\nimport Menu from \"@material-ui/core/Menu\"\nimport IconButton from \"@material-ui/core/IconButton\"\nimport Tooltip from \"@material-ui/core/Tooltip\"\nimport FontColorIcon from \"../icons/FontColorIcon\"\nimport ColorPicker from \"../dropdowns/ColorPicker\"\nimport useAnchorElement from \"../../hooks/useAnchorElement\"\nimport getCurrentMark from \"../../utils/getCurrentMark\"\nimport { attributes } from \"../../constants/types\"\n\n/**\n * FontColorButton displays a button with associated click logic for selecting\n * a font color.\n */\nconst FontColorButton = () => {\n const editor = useSlate()\n const { anchorEl, handleClose, handleMouseDown } = useAnchorElement()\n\n const handleChange = (value: string) => {\n Editor.addMark(editor, attributes.fontColor, value)\n }\n const activeColor = getCurrentMark(editor, attributes.fontColor) as string\n\n return (\n \n \n \n \n \n \n \n
\n \n
\n \n
\n )\n}\n\nexport default FontColorButton\n","import React from \"react\"\nimport SvgIcon from \"@material-ui/core/SvgIcon\"\n\nconst CheckIcon = () => {\n return (\n \n \n \n \n )\n}\n\nexport default CheckIcon\n","const FontFamilyList = [\n \"Lato\",\n \"Merriweather\",\n \"Montserrat\",\n \"Roboto\",\n \"Roboto Condensed\",\n \"Roboto Mono\",\n \"Roboto Slab\",\n]\n\nconst FontSizeList = [\n \"0.8rem\",\n \"0.9rem\",\n \"1rem\",\n \"1.1rem\",\n \"1.2rem\",\n \"1.3rem\",\n \"1.4rem\",\n \"1.5rem\",\n \"1.8rem\",\n \"2rem\",\n]\n\nconst LineSpacingList = [\"1.0\", \"1.2\", \"1.5\", \"2.0\", \"2.5\", \"3.0\"]\n\nexport { FontFamilyList, FontSizeList, LineSpacingList }\n","import { Editor, Path, Node } from \"slate\"\n\n// getParentNode is a helper function to get the parent node above the current selection.\nconst getParentNode = (editor: Editor) => {\n if (!editor.selection) {\n return\n }\n\n // need to get the parent path in order to get the parent node above this selection\n const currentPath = editor.selection.anchor.path\n const parentPath = Path.parent(currentPath)\n const node = Node.get(editor, parentPath)\n\n return node\n}\n\nexport default getParentNode\n","import React, { MouseEvent } from \"react\"\nimport { Element, Transforms } from \"slate\"\nimport { useSlate } from \"slate-react\"\nimport { makeStyles } from \"@material-ui/core/styles\"\nimport Menu from \"@material-ui/core/Menu\"\nimport MenuItem from \"@material-ui/core/MenuItem\"\nimport IconButton from \"@material-ui/core/IconButton\"\nimport Tooltip from \"@material-ui/core/Tooltip\"\nimport CheckIcon from \"../icons/CheckIcon\"\nimport { types } from \"../../constants/types\"\nimport { LineSpacingList } from \"../../utils/dropdownValues\"\nimport getParentNode from \"../../utils/getParentNode\"\n\nconst useStyles = makeStyles(() => ({\n menuItem: {\n display: \"flex\",\n justifyContent: \"flex-end\",\n width: \"75px\",\n },\n icon: {\n \"&:hover\": {\n backgroundColor: \"transparent\",\n },\n },\n}))\n\ntype Props = {\n /** Icon to display in button */\n icon: JSX.Element\n}\n\n/**\n * LineSpacingButton displays a button with associated click logic for selecting\n * line spacing.\n */\nconst LineSpacingButton = ({ icon }: Props) => {\n const editor = useSlate()\n const classes = useStyles()\n const [anchorEl, setAnchorEl] = React.useState(null)\n\n const handleItemClick = (item: string) => {\n Transforms.setNodes(editor, {\n type: types.lineSpacing,\n lineSpacing: item,\n })\n setAnchorEl(null)\n }\n\n const handleMenuOpen = (event: MouseEvent) => {\n setAnchorEl(event.currentTarget)\n }\n\n return (\n \n \n \n {icon}\n \n \n setAnchorEl(null)}>\n {LineSpacingList.map((item: string, index: number) => {\n const parentNode = getParentNode(editor)\n const currentLineSpacing =\n (Element.isElement(parentNode) && parentNode.lineSpacing) || \"1.5\"\n return (\n handleItemClick(item)}\n className={classes.menuItem}>\n \n {currentLineSpacing === item && }\n \n {item}\n \n )\n })}\n \n \n )\n}\n\nexport default LineSpacingButton\n","import React from \"react\"\nimport Dialog from \"@material-ui/core/Dialog\"\nimport DialogTitle from \"@material-ui/core/DialogTitle\"\nimport DialogContent from \"@material-ui/core/DialogContent\"\nimport DialogActions from \"@material-ui/core/DialogActions\"\nimport FormControlLabel from \"@material-ui/core/FormControlLabel\"\nimport Checkbox from \"@material-ui/core/Checkbox\"\nimport TextField from \"@material-ui/core/TextField\"\nimport Button from \"@material-ui/core/Button\"\nimport useStyles from \"../../styles/dialog\"\nimport { ImageDialogProps } from \"../../types/dialog\"\n\nconst ImageDialog = ({\n handleAddClick,\n handleClose,\n dialogOpen,\n image,\n setImage,\n}: ImageDialogProps) => {\n const classes = useStyles()\n const [checked, setChecked] = React.useState(false)\n\n const handleCheckboxChange = () => {\n setChecked(!checked)\n }\n\n return (\n \n Image Details\n \n \n setImage({\n ...image,\n url: e.target.value,\n })\n }\n fullWidth\n />\n \n setImage({\n ...image,\n description: e.target.value,\n })\n }\n fullWidth\n />\n \n setImage({\n ...image,\n width: Number(e.target.value),\n })\n }\n fullWidth\n />\n \n setImage({\n ...image,\n height: Number(e.target.value),\n })\n }\n fullWidth\n />\n \n }\n label=\"Is this a link?\"\n />\n {checked && (\n \n setImage({\n ...image,\n linkURL: e.target.value,\n })\n }\n fullWidth\n />\n )}\n \n \n \n Add Image\n \n \n \n )\n}\n\nexport default ImageDialog\n","import React from \"react\"\nimport { Editor, Transforms } from \"slate\"\nimport { useSlate } from \"slate-react\"\nimport IconButton from \"@material-ui/core/IconButton\"\nimport Tooltip from \"@material-ui/core/Tooltip\"\nimport ImageDialog from \"../dialogs/ImageDialog\"\nimport { Image } from \"../../types/image\"\nimport { types } from \"../../constants/types\"\nimport useStyles from \"../../styles/buttons\"\n\n// this is necessary to maintain editor selection when image dialog appears;\n// the deselect method unsets the editor selection\nTransforms.deselect = () => {}\n\nconst insertImage = (editor: Editor, image: Image) => {\n const { url, description, width, height, linkURL } = image\n const imageData = {\n type: types.image,\n url,\n description,\n width,\n height,\n linkURL,\n children: [{ text: \"\" }],\n }\n Transforms.insertNodes(editor, imageData)\n}\n\ntype Props = {\n /** Icon to display in button */\n icon: JSX.Element\n}\n\n/**\n * ImageButton is a button specifically for adding images.\n */\nconst ImageButton = ({ icon }: Props) => {\n const editor = useSlate()\n const [imageDialogOpen, setImageDialogOpen] = React.useState(false)\n const [image, setImage] = React.useState({\n url: \"\",\n description: \"\",\n })\n const props = {\n active: false,\n }\n const classes = useStyles(props)\n\n const handleAddImage = () => {\n insertImage(editor, image)\n setImageDialogOpen(false)\n }\n\n // if the user has clicked away without adding the image then we don't need to do anything with their data\n const handleClose = () => setImageDialogOpen(false)\n\n return (\n \n \n setImageDialogOpen(true)}>\n {icon}\n \n \n \n \n )\n}\n\nexport default ImageButton\n","import React from \"react\"\nimport Dialog from \"@material-ui/core/Dialog\"\nimport DialogTitle from \"@material-ui/core/DialogTitle\"\nimport DialogContent from \"@material-ui/core/DialogContent\"\nimport DialogActions from \"@material-ui/core/DialogActions\"\nimport TextField from \"@material-ui/core/TextField\"\nimport Button from \"@material-ui/core/Button\"\nimport useStyles from \"../../styles/dialog\"\nimport { VideoDialogProps } from \"../../types/dialog\"\n\nconst VideoDialog = ({\n handleAddClick,\n handleClose,\n dialogOpen,\n video,\n setVideo,\n}: VideoDialogProps) => {\n const classes = useStyles()\n\n return (\n \n Video Details\n \n \n setVideo({\n ...video,\n url: e.target.value,\n })\n }\n fullWidth\n />\n \n setVideo({\n ...video,\n width: Number(e.target.value),\n })\n }\n fullWidth\n />\n \n setVideo({\n ...video,\n height: Number(e.target.value),\n })\n }\n fullWidth\n />\n \n \n \n Add Video\n \n \n \n )\n}\n\nexport default VideoDialog\n","const youTubeRegex = /(?:youtube\\.com\\/(?:[^/]+\\/.+\\/|(?:v|e(?:mbed)?)\\/+|.*[?&]v=)|youtu\\.be\\/)([^\"&?/ ]{11})/i\nconst vimeoRegex = /\\/\\/(?:www\\.|player\\.)?vimeo.com\\/(?:channels\\/(?:\\w+\\/)?|groups\\/(?:[^/]*)\\/videos\\/|album\\/(?:\\d+)\\/video\\/|video\\/|)(\\d+)(?:[a-zA-Z0-9_-]+)?/i\n\nconst getVideoID = (url: string) => {\n let match\n if (url.includes(\"youtube\")) {\n match = url.match(youTubeRegex)\n }\n if (url.includes(\"vimeo\")) {\n match = url.match(vimeoRegex)\n }\n if (match && match.length > 0) {\n return match[1]\n }\n\n alert(\"Can only accept YouTube or Vimeo URL\")\n return url\n}\n\nexport default getVideoID\n","import React from \"react\"\nimport { Editor, Transforms } from \"slate\"\nimport { useSlate } from \"slate-react\"\nimport IconButton from \"@material-ui/core/IconButton\"\nimport Tooltip from \"@material-ui/core/Tooltip\"\nimport VideoDialog from \"../dialogs/VideoDialog\"\nimport { types } from \"../../constants/types\"\nimport { Video } from \"../../types/video\"\nimport useStyles from \"../../styles/buttons\"\nimport getVideoID from \"../../utils/getVideoID\"\n\n// this is necessary to maintain editor selection when video dialog appears;\n// the deselect method unsets the editor selection\nTransforms.deselect = () => {}\n\n/**\n * addVideo inserts a new video node.\n */\nconst addVideo = (editor: Editor, video: Video) => {\n const { url, width, height } = video\n let transformedURL = url\n const videoID = getVideoID(url)\n if (url.match(/youtube\\.com/)) {\n transformedURL = `https://www.youtube.com/embed/${videoID}`\n }\n if (url.match(/vimeo\\.com/)) {\n transformedURL = `https://player.vimeo.com/video/${videoID}`\n }\n const videoData = {\n type: types.video,\n url: transformedURL,\n width,\n height,\n children: [{ text: \"\" }],\n }\n Transforms.insertNodes(editor, videoData)\n}\n\ntype Props = {\n /** Icon to display in button */\n icon: JSX.Element\n}\n\n/**\n * VideoButton is a button specifically for adding videos.\n */\nconst VideoButton = ({ icon }: Props) => {\n const editor = useSlate()\n const [videoDialogOpen, setVideoDialogOpen] = React.useState(false)\n const [video, setVideo] = React.useState