From 4a99a92fdc9698ee147b10de49eb4d2238e416b5 Mon Sep 17 00:00:00 2001 From: Victor Zheng Date: Tue, 10 Oct 2023 19:33:48 -0400 Subject: [PATCH] styling fixes --- frontend/src/components/CommentSection.js | 273 +++++---------------- frontend/src/components/SlateComponents.js | 176 ++++++++++++- 2 files changed, 235 insertions(+), 214 deletions(-) diff --git a/frontend/src/components/CommentSection.js b/frontend/src/components/CommentSection.js index 21031d3..3a3ad8c 100644 --- a/frontend/src/components/CommentSection.js +++ b/frontend/src/components/CommentSection.js @@ -6,12 +6,25 @@ import { Editor, Transforms, createEditor, - Descendant, Element as SlateElement, } from 'slate' import { withHistory } from 'slate-history' -import { Button, Icon, Toolbar } from './SlateComponents' +import { + BlockButton, + Button, + Element, + Icon, + Leaf, + MarkButton, + TEXT_ALIGN_TYPES, + Toolbar, + isBlockActive, + isMarkActive, + toggleBlock, + toggleMark, +} from './SlateComponents' +import { Box, Heading } from '@chakra-ui/react' const HOTKEYS = { 'mod+b': 'bold', @@ -20,225 +33,59 @@ const HOTKEYS = { 'mod+`': 'code', } -const LIST_TYPES = ['numbered-list', 'bulleted-list'] -const TEXT_ALIGN_TYPES = ['left', 'center', 'right', 'justify'] - -const RichTextExample = () => { +const CommentSection = () => { const renderElement = useCallback((props) => , []) const renderLeaf = useCallback((props) => , []) const editor = useMemo(() => withHistory(withReact(createEditor())), []) return ( - - - - - - - - - - - - - - - - - { - for (const hotkey in HOTKEYS) { - if (isHotkey(hotkey, event)) { - event.preventDefault() - const mark = HOTKEYS[hotkey] - toggleMark(editor, mark) + + Comment Section + + + + + + + + + + + + + + + + + { + for (const hotkey in HOTKEYS) { + if (isHotkey(hotkey, event)) { + event.preventDefault() + const mark = HOTKEYS[hotkey] + toggleMark(editor, mark) + } } - } - }} - /> - - ) -} - -const toggleBlock = (editor, format) => { - const isActive = isBlockActive( - editor, - format, - TEXT_ALIGN_TYPES.includes(format) ? 'align' : 'type' - ) - const isList = LIST_TYPES.includes(format) - - Transforms.unwrapNodes(editor, { - match: (n) => - !Editor.isEditor(n) && - SlateElement.isElement(n) && - LIST_TYPES.includes(n.type) && - !TEXT_ALIGN_TYPES.includes(format), - split: true, - }) - let newProperties - if (TEXT_ALIGN_TYPES.includes(format)) { - newProperties = { - align: isActive ? undefined : format, - } - } else { - newProperties = { - type: isActive ? 'paragraph' : isList ? 'list-item' : format, - } - } - Transforms.setNodes(editor, newProperties) - - if (!isActive && isList) { - const block = { type: format, children: [] } - Transforms.wrapNodes(editor, block) - } -} - -const toggleMark = (editor, format) => { - const isActive = isMarkActive(editor, format) - - if (isActive) { - Editor.removeMark(editor, format) - } else { - Editor.addMark(editor, format, true) - } -} - -const isBlockActive = (editor, format, blockType = 'type') => { - const { selection } = editor - if (!selection) return false - - const [match] = Array.from( - Editor.nodes(editor, { - at: Editor.unhangRange(editor, selection), - match: (n) => - !Editor.isEditor(n) && - SlateElement.isElement(n) && - n[blockType] === format, - }) - ) - - return !!match -} - -const isMarkActive = (editor, format) => { - const marks = Editor.marks(editor) - return marks ? marks[format] === true : false -} - -const Element = ({ attributes, children, element }) => { - const style = { textAlign: element.align } - switch (element.type) { - case 'block-quote': - return ( -
- {children} -
- ) - case 'bulleted-list': - return ( -
    - {children} -
- ) - case 'heading-one': - return ( -

- {children} -

- ) - case 'heading-two': - return ( -

- {children} -

- ) - case 'list-item': - return ( -
  • - {children} -
  • - ) - case 'numbered-list': - return ( -
      - {children} -
    - ) - default: - return ( -

    - {children} -

    - ) - } -} - -const Leaf = ({ attributes, children, leaf }) => { - if (leaf.bold) { - children = {children} - } - - if (leaf.code) { - children = {children} - } - - if (leaf.italic) { - children = {children} - } - - if (leaf.underline) { - children = {children} - } - - return {children} -} - -const BlockButton = ({ format, icon }) => { - const editor = useSlate() - return ( - - ) -} - -const MarkButton = ({ format, icon }) => { - const editor = useSlate() - return ( - + }} + /> +
    + ) } +// example taken from https://github.com/ianstormtaylor/slate/blob/main/site/components.tsx +// remove once dynamically fetched from backend const initialValue = [ { type: 'paragraph', @@ -275,4 +122,4 @@ const initialValue = [ }, ] -export default RichTextExample +export default CommentSection diff --git a/frontend/src/components/SlateComponents.js b/frontend/src/components/SlateComponents.js index 64221ce..5d87d43 100644 --- a/frontend/src/components/SlateComponents.js +++ b/frontend/src/components/SlateComponents.js @@ -1,7 +1,181 @@ // from https://github.com/ianstormtaylor/slate/blob/main/site/components.tsx -import React, { Ref, PropsWithChildren } from 'react' +// probably best not to touch. +import React from 'react' +import { Editor, Transforms, Element as SlateElement } from 'slate' import ReactDOM from 'react-dom' import { cx, css } from '@emotion/css' +import { useSlate } from 'slate-react' + +const LIST_TYPES = ['numbered-list', 'bulleted-list'] +export const TEXT_ALIGN_TYPES = ['left', 'center', 'right', 'justify'] + +export const toggleBlock = (editor, format) => { + const isActive = isBlockActive( + editor, + format, + TEXT_ALIGN_TYPES.includes(format) ? 'align' : 'type' + ) + const isList = LIST_TYPES.includes(format) + + Transforms.unwrapNodes(editor, { + match: (n) => + !Editor.isEditor(n) && + SlateElement.isElement(n) && + LIST_TYPES.includes(n.type) && + !TEXT_ALIGN_TYPES.includes(format), + split: true, + }) + let newProperties + if (TEXT_ALIGN_TYPES.includes(format)) { + newProperties = { + align: isActive ? undefined : format, + } + } else { + newProperties = { + type: isActive ? 'paragraph' : isList ? 'list-item' : format, + } + } + Transforms.setNodes(editor, newProperties) + + if (!isActive && isList) { + const block = { type: format, children: [] } + Transforms.wrapNodes(editor, block) + } +} + +export const toggleMark = (editor, format) => { + const isActive = isMarkActive(editor, format) + + if (isActive) { + Editor.removeMark(editor, format) + } else { + Editor.addMark(editor, format, true) + } +} + +export const isBlockActive = (editor, format, blockType = 'type') => { + const { selection } = editor + if (!selection) return false + + const [match] = Array.from( + Editor.nodes(editor, { + at: Editor.unhangRange(editor, selection), + match: (n) => + !Editor.isEditor(n) && + SlateElement.isElement(n) && + n[blockType] === format, + }) + ) + + return !!match +} + +export const BlockButton = ({ format, icon }) => { + const editor = useSlate() + return ( + + ) +} + +export const MarkButton = ({ format, icon }) => { + const editor = useSlate() + return ( + + ) +} + +export const isMarkActive = (editor, format) => { + const marks = Editor.marks(editor) + return marks ? marks[format] === true : false +} + +export const Element = ({ attributes, children, element }) => { + const style = { textAlign: element.align } + switch (element.type) { + case 'block-quote': + return ( +
    + {children} +
    + ) + case 'bulleted-list': + return ( +
      + {children} +
    + ) + case 'heading-one': + return ( +

    + {children} +

    + ) + case 'heading-two': + return ( +

    + {children} +

    + ) + case 'list-item': + return ( +
  • + {children} +
  • + ) + case 'numbered-list': + return ( +
      + {children} +
    + ) + default: + return ( +

    + {children} +

    + ) + } +} + +export const Leaf = ({ attributes, children, leaf }) => { + if (leaf.bold) { + children = {children} + } + + if (leaf.code) { + children = {children} + } + + if (leaf.italic) { + children = {children} + } + + if (leaf.underline) { + children = {children} + } + + return {children} +} export const Button = React.forwardRef( ({ className, active, reversed, ...props }, ref) => (