Skip to content

Commit

Permalink
Merge branch 'feature/articles' into fix/#304-softbreak-plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
maerzhase authored Aug 5, 2022
2 parents 37997d0 + a9890be commit 94b6ca8
Show file tree
Hide file tree
Showing 17 changed files with 211 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const codeEditorLangs: ILanguageEntry[] = [
},
]

export function getCodeEditorLang(value: string): ILanguageEntry {
export function getCodeEditorLang(value: string|null): ILanguageEntry {
return codeEditorLangs.find(entry => entry.value === value) || codeEditorLangs[0]
}

Expand Down
31 changes: 29 additions & 2 deletions src/components/NFTArticle/SlateEditor/Elements/Blocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import style from '../../NFTArticle.module.scss';
import { FigureElement } from "../../elements/Figure"
import { FigcaptionElement } from "../../elements/Figcaption"
import { ImageElement } from "../../elements/ImageElement"
import { Element, Node, Transforms } from "slate"
import { Editor, Element, Node, Path, Transforms, Range } from "slate"
import { HeadingAttributeSettings } from "./AttributeSettings/HeadingAttributeSettings"
import { ListAttributeSettings } from "./AttributeSettings/ListAttributeSettings"
import { BlockquoteElement } from "../../elements/Blockquote"
Expand All @@ -21,6 +21,7 @@ import { SlateTable } from "../Plugins/SlateTablePlugin";
import TezosStorageEditor from "./TezosStorageEditor";
import { CodeAttributeSettings } from "./AttributeSettings/CodeAttributeSettings";
import { CodeEditorElement } from "./CodeEditorElement";
import { breakBehaviors, EBreakBehavior, InsertBreakFunction } from "../Plugins/SlateBreaksPlugin";

export enum EArticleBlocks {
"embed-media" = "embed-media",
Expand Down Expand Up @@ -92,6 +93,7 @@ export interface IArticleBlockDefinition {
hideSettingsAfterUpdate?: boolean
// prevent the auto-focus trigger when creating the element
preventAutofocusTrigger?: boolean
insertBreakBehavior?: EBreakBehavior | InsertBreakFunction
}

export const BlockDefinitions: Record<EArticleBlocks, IArticleBlockDefinition> = {
Expand Down Expand Up @@ -194,6 +196,7 @@ export const BlockDefinitions: Record<EArticleBlocks, IArticleBlockDefinition> =
break;
}
},
insertBreakBehavior: EBreakBehavior.insertParagraph,
hasUtilityWrapper: true,
instanciateElement: () => ({
type: "heading",
Expand Down Expand Up @@ -263,6 +266,29 @@ export const BlockDefinitions: Record<EArticleBlocks, IArticleBlockDefinition> =
{children}
</li>
),
insertBreakBehavior: (editor, element) => {
const { selection } = editor;
if (selection && !Range.isCollapsed(selection)) return true;
const [nodeListItem, pathListItem] = element;
const text = Node.string(nodeListItem);
if (text) return true;
const parentList = Editor.above(editor, {
at: pathListItem,
match: n =>
!Editor.isEditor(n) && Element.isElement(n) && n.type === 'list',
mode: 'lowest',
})
if (!parentList) return true;
const [, pathParentList] = parentList
const next = Path.next(pathParentList);
Transforms.setNodes(editor, { type: 'paragraph' }, {
at: pathListItem,
})
Transforms.moveNodes(editor, {
at: pathListItem,
to: next,
})
},
hasUtilityWrapper: false,
},
"table": {
Expand Down Expand Up @@ -453,7 +479,8 @@ export const DefaultBlockDefinition: IArticleBlockDefinition = {
render: ({ attributes, element, children }) => (
<div {...attributes}>{children}</div>
),
hasUtilityWrapper: false
hasUtilityWrapper: false,
insertBreakBehavior: EBreakBehavior.default,
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import style from "./RenderElements.module.scss"
import cs from "classnames"
import { ReactEditor, RenderElementProps, useSlateStatic } from "slate-react"
import React, { PropsWithChildren, useEffect, useMemo, useState } from "react"
import React, { PropsWithChildren, useMemo, useState } from "react"
import { AddBlock } from "../Utils/AddBlock"
import { getArticleBlockDefinition } from "./Blocks"
import { Path, Transforms, Editor, Node } from "slate"
import { Path, Transforms, Node } from "slate"
import { BlockExtraMenu } from "../Utils/BlockExtraMenu"
import { BlockMenu } from "../Utils/BlockMenu"
import { TAttributesEditorWrapper } from "../../../../types/ArticleEditor/ArticleEditorBlocks"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {TextFormatButton} from './TextFormatButton'
import {useClientEffect} from '../../../../utils/hookts'
import { lookupElementAtSelection, lookupElementByType } from '../utils'
import { LinkButton } from './LinkButton'
import { BlockDefinitions } from '../Elements/Blocks'
import { BlockDefinitions, EArticleBlocks } from '../Elements/Blocks'

const FloatingInlineMenu = () => {
const ref = useRef<HTMLDivElement>(null)
Expand All @@ -23,7 +23,7 @@ const FloatingInlineMenu = () => {
const [overrideContent, setOverrideContent] = useState(null)

const [elementUnderCursor] = lookupElementAtSelection(editor, editor.selection as Location) || []
const { hideFloatingInlineMenu } = BlockDefinitions[elementUnderCursor?.type] || {};
const { hideFloatingInlineMenu } = BlockDefinitions[elementUnderCursor?.type as any as EArticleBlocks] || {};

const activeElement = lookupElementByType(editor, 'link') as NodeEntry;

Expand Down
57 changes: 57 additions & 0 deletions src/components/NFTArticle/SlateEditor/Plugins/SlateBreaksPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Transforms, Path, NodeEntry, Node } from "slate";
import { EnhanceEditorWith, FxEditor } from "../../../../types/ArticleEditor/Editor";
import { lookupElementAtSelection } from "../utils";
import { getArticleBlockDefinition } from "../Elements/Blocks";

export enum EBreakBehavior {
"default" = "default",
"insertParagraph" = "insertParagraph",
"insertParagraphIfEmpty" = "insertParagraphIfEmpty",
"nothing" = "nothing",
}

export type ShouldDefaultInsertBreak = boolean;
export type InsertBreakFunction = (editor: FxEditor, element: NodeEntry) => ShouldDefaultInsertBreak | void
export const breakBehaviors: Record<EBreakBehavior, InsertBreakFunction> = {
default: () => true,
insertParagraph: (editor, element) => {
const [, pathEl] = element;
const pathNextEl = Path.next(pathEl)
Transforms.splitNodes(editor, { always: true });
Transforms.setNodes(editor, { type: 'paragraph'}, {
at: pathNextEl,
});
},
insertParagraphIfEmpty: (editor, element) => {
const [node] = element;
const text = Node.string(node);
if (text) return true;
breakBehaviors.insertParagraph(editor, element)
},
nothing: () => {},
}

/**
* Manage break behavior of elements
*/
export const withBreaks: EnhanceEditorWith = (editor) => {
const { insertBreak } = editor

editor.insertBreak = () => {
const { selection } = editor;
const nodeEntry = lookupElementAtSelection(editor, selection);
if (nodeEntry) {
const [node] = nodeEntry;
const definition = getArticleBlockDefinition(node.type);
const behavior = definition.insertBreakBehavior;
if (behavior) {
const insertBreakFunction = typeof behavior === 'function' ? behavior : breakBehaviors[behavior]
const shouldDefaultInsertBreak = insertBreakFunction(editor, nodeEntry);
if (!shouldDefaultInsertBreak) return;
}
}
insertBreak();
}

return editor
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { EnhanceEditorWith } from "../../../../types/ArticleEditor/Editor";
* states
*/
export const withConstraints: EnhanceEditorWith = (editor) => {
const { normalizeNode } = editor
const { normalizeNode, insertBreak } = editor

editor.normalizeNode = (entry) => {
const [node, path] = entry
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
.root {
position: absolute;

visibility: hidden;
&.pos-up {
bottom: -30px;
}

&.pos-down {
top: -3px;
}
}
}
.show {
visibility: visible;
}
24 changes: 15 additions & 9 deletions src/components/NFTArticle/SlateEditor/Utils/BlockMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,21 @@ export function BlockMenu({
onClose,
children,
}: PropsWithChildren<Props>) {
const [isInit, setIsInit] = useState(false);
const markerRef = useRef<HTMLDivElement>(null)
const rootRef = useRef<HTMLDivElement>(null)
const [position, setPosition] = useState<"up"|"down">("up")
const positionRef = useRef<"up"|"down">("up")


// add an event listener to the document to know if a click outside was made
useEffect(() => {
const onClick = (event: MouseEvent) => {
const path = (event as any).path
if (path && path.length > 0 && rootRef.current) {
if (!path.includes(rootRef.current)) {
onClose()
}
if (!rootRef?.current || rootRef.current.contains(event.target as Node)) {
// inside click
return;
}
onClose()
}

const onScroll = () => {
Expand All @@ -37,26 +38,28 @@ export function BlockMenu({
setPosition(pos)
}
}
setIsInit(true)
}

const onKeyPressed = (event: KeyboardEvent) => {
if (event.key === "Escape") {
event.preventDefault();
onClose()
}
}

onScroll();

document.addEventListener("click", onClick)
document.addEventListener("mousedown", onClick)
document.addEventListener("scroll", onScroll)
document.addEventListener("keydown", onKeyPressed)

return () => {
document.removeEventListener("click", onClick)
document.removeEventListener("mousedown", onClick)
document.removeEventListener("scroll", onScroll)
document.removeEventListener("keydown", onKeyPressed)
}
}, [])
}, [onClose])

return (
<>
Expand All @@ -67,6 +70,9 @@ export function BlockMenu({
style.root,
style[`pos-${position}`],
className,
{
[style.show]: isInit
}
)}
>
{children}
Expand Down
3 changes: 2 additions & 1 deletion src/components/NFTArticle/SlateEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import React, {
forwardRef,
KeyboardEvent,
useCallback,
useEffect,
useImperativeHandle,
useMemo,
useState
Expand All @@ -30,6 +29,7 @@ import { EnhanceEditorWith, FxEditor } from "../../../types/ArticleEditor/Editor
import useInit from "../../../hooks/useInit";
import dynamic from 'next/dynamic'
import { onKeyDownTablePlugin, withTables } from "./Plugins/SlateTablePlugin";
import { withBreaks } from "./Plugins/SlateBreaksPlugin";


const FloatingInlineMenu = dynamic(() => import('./FloatingInlineMenu/FloatingInlineMenu'), {
Expand Down Expand Up @@ -117,6 +117,7 @@ export const SlateEditor = forwardRef<FxEditor, SlateEditorProps>(({
{ f: withTables },
{ f: withConstraints },
{ f: withSoftBreak }
{ f: withBreaks },
]
const enhancedEditor = withs.reduce((e, enhanceWith) => {
return enhanceWith.f(e, ...Object.values(enhanceWith.args || {}));
Expand Down
12 changes: 8 additions & 4 deletions src/components/NFTArticle/SlateEditor/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Range, Editor, Text, Transforms, Element, NodeEntry, Location } from 'slate';
import { Range, Editor, Text, Transforms, Element, NodeEntry, Location } from 'slate';

export function getRangeFromBlockStartToCursor(editor: Editor): Range {
const { anchor } = editor.selection as Range;
Expand Down Expand Up @@ -64,17 +64,21 @@ export function toggleFormat(editor: Editor, format: string): void {
)
}

export function lookupElementByType(editor:Editor, type: string): NodeEntry {
const isTypeInArray = (type: string, typesToCheck: string[]) => typesToCheck.indexOf(type) > -1;
const isTypeEqual = (type: string, typeToCheck: string) => type === typeToCheck;
export function lookupElementByType(editor:Editor, type: string | string[]): NodeEntry {
const checkType = Array.isArray(type) ? isTypeInArray : isTypeEqual

const [element] = Editor.nodes(editor, {
match: n =>
!Editor.isEditor(n) && Element.isElement(n) && n.type === type,
!Editor.isEditor(n) && Element.isElement(n) && checkType(n.type, type as any),
})
return element
}

export function lookupElementAtSelection(
editor: Editor,
selection: Location
selection: Location | null
): NodeEntry | null {
if (!selection) return null;
const [, nodePath] = Editor.last(editor, selection)
Expand Down
2 changes: 1 addition & 1 deletion src/components/NFTArticle/elements/CodeElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function CodeElement({
}: PropsWithChildren<Props>) {
// rehype injects "language-js" as a classname, only way to get the lang
const lang = useMemo(() => {
const L = className.split("-")[1]
const L = className?.split("-")[1] || null
return getCodeEditorLang(L)
}, [className])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export default async function getMarkdownFromSlateEditorState(slate: Node[] ) {
.use(slateToRemark, {
overrides: slateToRemarkTransformerOverrides,
})
.use(stringify)
.use(stringify, { bulletOther: '-' })
const ast = await processor.run({
type: "root",
children: slate,
Expand Down
8 changes: 5 additions & 3 deletions src/components/Navigation/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import style from "./Dropdown.module.scss"
import cs from "classnames"
import { PropsWithChildren, useState } from "react"
import { MouseEventHandler, PropsWithChildren, useCallback, useState } from "react"
import { DropdownMenu } from "./DropdownMenu"
import { useClientEffect } from "../../utils/hookts"

Expand All @@ -23,9 +23,11 @@ export function Dropdown({
}: PropsWithChildren<Props>) {
const [opened, setOpened] = useState<boolean>(false)

const toggle = () => {
const toggle: MouseEventHandler = useCallback((evt) => {
evt.preventDefault()
evt.stopPropagation()
setOpened(!opened)
}
}, [opened])

useClientEffect(() => {
if (opened) {
Expand Down
Loading

0 comments on commit 94b6ca8

Please sign in to comment.