Skip to content

Commit

Permalink
frontend: add SlashMenu back (#1421)
Browse files Browse the repository at this point in the history
* add default slash menu

* frontend: prevent scroll when slash menu opens
  • Loading branch information
horacioh authored Sep 20, 2023
1 parent ef97399 commit c377cfd
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {editorBlockToServerBlock} from '@mintter/app/src/client/editor-to-server
import {serverChildrenToEditorChildren} from '@mintter/app/src/client/server-to-editor'
import {useOpenUrl} from '@mintter/app/src/open-url'
import {toast} from '@mintter/app/src/toast'
import {insertOrUpdateBlock} from '@mintter/editor'
import {RiFile2Fill, RiImage2Fill, RiText, RiVideoAddFill} from 'react-icons/ri'
import {
Block,
BlockIdentifier,
Expand Down Expand Up @@ -786,18 +788,71 @@ export function useDraftEditor(
queryClient,
openUrl,
},

onEditorReady: (e) => {
readyThings.current[0] = e
handleMaybeReady()
},
blockSchema: hmBlockSchema,
// slashCommands: [
// ...defaultReactSlashMenuItems.slice(0, 2),
// insertImage,
// insertFile,
// insertVideo,
// ...defaultReactSlashMenuItems.slice(2),
// ],
slashMenuItems: [
{
name: 'Paragraph',
aliases: ['p'],
icon: <RiText size={18} />,
execute: (editor) =>
insertOrUpdateBlock(editor, {
type: 'paragraph',
} as PartialBlock<HMBlockSchema>),
},
{
name: 'Heading',
aliases: ['h', 'heading1', 'subheading'],
execute: (editor) =>
insertOrUpdateBlock(editor, {
type: 'heading',
props: {level: '2'},
} as PartialBlock<HMBlockSchema>),
},
{
name: 'Image',
aliases: ['image', 'img', 'picture'],
icon: <RiImage2Fill size={18} />,
hint: 'Insert a Image',
execute: (editor) =>
insertOrUpdateBlock(editor, {
type: 'image',
props: {
url: '',
},
} as PartialBlock<HMBlockSchema>),
},
{
name: 'Video',
aliases: ['video', 'vid', 'media'],
icon: <RiVideoAddFill size={18} />,
hint: 'Insert a video',
execute: (editor) =>
insertOrUpdateBlock(editor, {
type: 'video',
props: {
url: '',
},
} as PartialBlock<HMBlockSchema>),
},
{
name: 'File',
aliases: ['file', 'folder'],
icon: <RiFile2Fill size={18} />,
hint: 'Insert a File',
execute: (editor) =>
insertOrUpdateBlock(editor, {
type: 'file',
props: {
url: '',
},
} as PartialBlock<HMBlockSchema>),
},
],

_tiptapOptions: {
extensions: [
Expand Down
29 changes: 0 additions & 29 deletions frontend/packages/app/src/pages/publication.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ export function PublicationPageEditor() {

const docId = route?.documentId
const versionId = route?.versionId
const blockId = route?.blockId
const accessory = route?.accessory
const accessoryKey = accessory?.key
const replace = useNavigate('replace')
Expand All @@ -146,11 +145,6 @@ export function PublicationPageEditor() {
)
const publication = usePublicationEditor(docId, versionId, route.pubContext)

// this checks if there's a block in the url, so we can highlight and scroll into the selected block
let [focusBlock] = useState(() => blockId)

// useScrollToBlock(editor, scrollWrapperRef, focusBlock)

const {data: changes} = useDocChanges(
publication.status == 'success' ? docId : undefined,
)
Expand Down Expand Up @@ -287,29 +281,6 @@ type ResizablePanelMachineServices = {
}
}

// eslint-disable-next-line
// function useScrollToBlock(editor: SlateEditor, ref: any, blockId?: string) {
// // TODO: find a way to scroll to the block when clicking on a mintter link
// useEffect(() => {
// setTimeout(() => {
// if (blockId) {
// if (ref?.current) {
// let entry = getEditorBlock(editor, {id: blockId})

// if (entry) {
// let [block] = entry
// let elm = ReactEditor.toDOMNode(editor, block)

// let rect = elm.getBoundingClientRect()
// let wrapper = ref.current.getBoundingClientRect()
// ref.current.scrollTo({top: rect.top - wrapper.top - 24})
// }
// }
// }
// }, 1000)
// }, [ref, blockId, editor])
// }

function OutOfDateBanner({docId, version}: {docId: string; version: string}) {
const route = useNavRoute()
const context = route.key === 'publication' ? route.pubContext : undefined
Expand Down
34 changes: 34 additions & 0 deletions frontend/packages/editor/src/block-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {Block as BlockNoteBlock, BlockNoteEditor} from './blocknote'
import {HMBlockSchema} from './schema'
import {useEffect, useState} from 'react'
import {getBlockInfoFromPos} from './blocknote'

export function useSelected(
block: BlockNoteBlock<HMBlockSchema>,
editor: BlockNoteEditor<HMBlockSchema>,
) {
const [selected, setSelected] = useState(false)
const tiptapEditor = editor._tiptapEditor
const selection = tiptapEditor.state.selection

useEffect(() => {
if (editor) {
const selectedNode = getBlockInfoFromPos(
tiptapEditor.state.doc,
tiptapEditor.state.selection.from,
)
if (selectedNode && selectedNode.id) {
if (
selectedNode.id === block.id &&
selectedNode.startPos === selection.$anchor.pos
) {
setSelected(true)
} else if (selectedNode.id !== block.id) {
setSelected(false)
}
}
}
}, [selection])

return selected
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,14 @@ export const SlashMenuPositioner = <
useState<ReactSlashMenuItem<BSchema>[]>()
const [keyboardHoveredItemIndex, setKeyboardHoveredItemIndex] =
useState<number>()
const scroller = useRef<HTMLElement | null>(null)

const referencePos = useRef<DOMRect>()
useEffect(() => {
setTimeout(() => {
scroller.current = document.getElementById('scroll-page-wrapper')
}, 100)
}, [])

useEffect(() => {
return props.editor.slashMenu.onUpdate((slashMenuState) => {
Expand Down Expand Up @@ -76,7 +82,7 @@ export const SlashMenuPositioner = <

return (
<Tippy
appendTo={props.editor.domElement.parentElement!}
appendTo={scroller.current!}
content={slashMenuElement}
getReferenceClientRect={getReferenceClientRect}
interactive={true}
Expand Down
3 changes: 2 additions & 1 deletion frontend/packages/editor/src/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
FormattingToolbarPositioner,
HyperlinkToolbarPositioner,
SideMenuPositioner,
SlashMenuPositioner,
} from './blocknote'
import './blocknote/core/style.css'
import './editor.css'
Expand All @@ -15,7 +16,7 @@ export function HyperMediaEditorView({editor}: {editor: HyperDocsEditor}) {
<BlockNoteView editor={editor}>
<FormattingToolbarPositioner editor={editor} />
<HyperlinkToolbarPositioner editor={editor} />
{/* <SlashMenuPositioner editor={editor} /> */}
<SlashMenuPositioner editor={editor} />
<SideMenuPositioner editor={editor} placement="left" />
</BlockNoteView>
)
Expand Down
31 changes: 1 addition & 30 deletions frontend/packages/editor/src/embed-block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
import {getBlockInfoFromPos} from './blocknote/core'
import {createReactBlockSpec} from './blocknote/react'
import {HMBlockSchema, hmBlockSchema} from './schema'
import {useSelected} from './block-utils'

const EditorText = styled(Text, {
fontSize: '$5',
Expand Down Expand Up @@ -238,36 +239,6 @@ function StaticEmbedPresentation({block}: {block: EmbedBlockType}) {
)
}

function useSelected(
block: BlockNoteBlock<HMBlockSchema>,
editor: BlockNoteEditor<HMBlockSchema>,
) {
const [selected, setSelected] = useState(false)
const tiptapEditor = editor._tiptapEditor
const selection = tiptapEditor.state.selection

useEffect(() => {
if (editor) {
const selectedNode = getBlockInfoFromPos(
tiptapEditor.state.doc,
tiptapEditor.state.selection.from,
)
if (selectedNode && selectedNode.id) {
if (
selectedNode.id === block.id &&
selectedNode.startPos === selection.$anchor.pos
) {
setSelected(true)
} else if (selectedNode.id !== block.id) {
setSelected(false)
}
}
}
}, [selection])

return selected
}

export function StaticBlockNode({block}: {block: BlockNode}) {
const children =
block.children.length > 0 ? (
Expand Down
7 changes: 3 additions & 4 deletions frontend/packages/editor/src/image.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {useAppContext} from '@mintter/app/src/app-context'
import {toast} from '@mintter/app/src/toast'
import {BACKEND_FILE_UPLOAD_URL, BACKEND_FILE_URL} from '@mintter/shared'
import {
Button,
Expand All @@ -13,18 +14,16 @@ import {
useTheme,
} from '@mintter/ui'
import {ChangeEvent, useEffect, useState} from 'react'
import {RiImage2Fill, RiImage2Line} from 'react-icons/ri'
import {RiImage2Line} from 'react-icons/ri'
import {
Block,
BlockNoteEditor,
DefaultBlockSchema,
createReactBlockSpec,
defaultProps,
getBlockInfoFromPos,
insertOrUpdateBlock,
} from './blocknote'
import {HMBlockSchema} from './schema'
import {InlineContent} from './blocknote/react'
import {HMBlockSchema} from './schema'

export const ImageBlock = createReactBlockSpec({
type: 'image',
Expand Down
7 changes: 6 additions & 1 deletion frontend/packages/ui/src/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ export const MainWrapper = ({
noScroll?: boolean
}) => (
<YStack flex={1} className="content-wrapper" {...props}>
{noScroll ? children : <ScrollView>{children}</ScrollView>}
{noScroll ? (
children
) : (
// TODO: we cannot remove this ID here because the SlashMenu is referencing this!
<ScrollView id="scroll-page-wrapper">{children}</ScrollView>
)}
</YStack>
)

Expand Down

0 comments on commit c377cfd

Please sign in to comment.