Skip to content

Commit

Permalink
feat: bubble menu for link / text editing (#17550)
Browse files Browse the repository at this point in the history
  • Loading branch information
daibhin authored Sep 22, 2023
1 parent 8bf729f commit 4d8993e
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 19 deletions.
Binary file modified frontend/__snapshots__/lemon-ui-icons--shelf-b.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/__snapshots__/lemon-ui-icons--shelf-i.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions frontend/src/lib/lemon-ui/icons/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1927,6 +1927,27 @@ export function IconMonitor(props: LemonIconProps): JSX.Element {
)
}

/** Material Bold icon. */
export function IconBold(props: LemonIconProps): JSX.Element {
return (
<LemonIconBase {...props}>
<path
fill="currentColor"
d="M15.6 10.79c.97-.67 1.65-1.77 1.65-2.79 0-2.26-1.75-4-4-4H7v14h7.04c2.09 0 3.71-1.7 3.71-3.79 0-1.52-.86-2.82-2.15-3.42zM10 6.5h3c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5h-3v-3zm3.5 9H10v-3h3.5c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5z"
/>
</LemonIconBase>
)
}

/** Material Italic icon. */
export function IconItalic(props: LemonIconProps): JSX.Element {
return (
<LemonIconBase {...props}>
<path fill="currentColor" d="M10 4v3h2.21l-3.42 8H6v3h8v-3h-2.21l3.42-8H18V4h-8z" />
</LemonIconBase>
)
}

/** Material CellPhone icon. */
export function IconPhone(props: LemonIconProps): JSX.Element {
return (
Expand Down
29 changes: 13 additions & 16 deletions frontend/src/scenes/notebooks/Marks/NotebookMarkLink.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Mark, mergeAttributes } from '@tiptap/core'
import { Mark, getMarkRange, mergeAttributes } from '@tiptap/core'
import { linkPasteRule } from '../Nodes/utils'
import { Plugin, PluginKey } from '@tiptap/pm/state'
import { router } from 'kea-router'

export const NotebookMarkLink = Mark.create({
name: 'link',
Expand Down Expand Up @@ -30,28 +29,26 @@ export const NotebookMarkLink = Mark.create({
},

addProseMirrorPlugins() {
const { editor, type: markType } = this
return [
new Plugin({
key: new PluginKey('handleLinkClick'),
props: {
handleDOMEvents: {
click(view, event) {
if (event.button !== 0) {
return false
}

const link = event.target as HTMLAnchorElement

const href = link.href
click(_, event) {
if (event.metaKey) {
const link = event.target as HTMLAnchorElement
const href = link.href

if (link && href && !view.editable) {
event.preventDefault()

if (isPostHogLink(href)) {
router.actions.push(link.pathname)
} else {
if (href) {
event.preventDefault()
window.open(href, link.target)
}
} else {
const range = getMarkRange(editor.state.selection.$anchor, markType)
if (range) {
editor.commands.setTextSelection(range)
}
}
},
},
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/scenes/notebooks/Notebook/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { SlashCommandsExtension } from './SlashCommands'
import { BacklinkCommandsExtension } from './BacklinkCommands'
import { NotebookNodeEarlyAccessFeature } from '../Nodes/NotebookNodeEarlyAccessFeature'
import { NotebookNodeSurvey } from '../Nodes/NotebookNodeSurvey'
import { InlineMenu } from './InlineMenu'

const CustomDocument = ExtensionDocument.extend({
content: 'heading block*',
Expand Down Expand Up @@ -223,6 +224,7 @@ export function Editor({
<>
<EditorContent editor={_editor} className="NotebookEditor flex flex-col flex-1" />
{_editor && <FloatingSuggestions editor={_editor} />}
{_editor && <InlineMenu editor={_editor} />}
</>
)
}
Expand Down
65 changes: 65 additions & 0 deletions frontend/src/scenes/notebooks/Notebook/InlineMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { LemonButton, LemonInput } from '@posthog/lemon-ui'
import { Editor } from '@tiptap/core'
import { BubbleMenu } from '@tiptap/react'
import { IconBold, IconDelete, IconItalic, IconLink, IconOpenInNew } from 'lib/lemon-ui/icons'

export const InlineMenu = ({ editor }: { editor: Editor }): JSX.Element => {
const { href, target } = editor.getAttributes('link')

const setLink = (href: string): void => {
editor.commands.setMark('link', { href: href })
}

const openLink = (): void => {
window.open(href, target)
}

return (
<BubbleMenu editor={editor} tippyOptions={{}}>
<div className="NotebookInlineMenu flex bg-white rounded border items-center text-muted-alt p-1 space-x-1">
{editor.isActive('link') ? (
<>
<LemonInput
size="small"
placeholder="https://posthog.com"
onChange={setLink}
value={href}
className="border-0"
autoFocus
/>
<LemonButton onClick={openLink} icon={<IconOpenInNew />} status="primary" size="small" />
<LemonButton
onClick={() => editor.chain().focus().unsetMark('link').run()}
icon={<IconDelete />}
status="danger"
size="small"
/>
</>
) : (
<>
<LemonButton
onClick={() => editor.chain().focus().toggleMark('bold').run()}
active={editor.isActive('bold')}
icon={<IconBold />}
size="small"
status={editor.isActive('bold') ? 'primary' : 'stealth'}
/>
<LemonButton
onClick={() => editor.chain().focus().toggleMark('italic').run()}
active={editor.isActive('italic')}
icon={<IconItalic />}
status={editor.isActive('italic') ? 'primary' : 'stealth'}
size="small"
/>
<LemonButton
onClick={() => editor.chain().focus().setMark('link').run()}
icon={<IconLink />}
status="stealth"
size="small"
/>
</>
)}
</div>
</BubbleMenu>
)
}
18 changes: 18 additions & 0 deletions frontend/src/scenes/notebooks/Notebook/Notebook.scss
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,24 @@
}
}

.NotebookInlineMenu {
margin-bottom: -0.2rem;
box-shadow: 0px 4px 10px 0px rgba(0, 0, 0, 0.1);

.LemonInput {
border: 0px;
min-height: 0px;
}

.LemonButton {
min-height: 1.75rem;

.LemonButton__icon {
font-size: 1rem;
}
}
}

.NotebookNodeSettings__widgets {
&__content {
max-height: calc(100vh - 220px);
Expand Down
6 changes: 3 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 4d8993e

Please sign in to comment.