diff --git a/frontend/__snapshots__/lemon-ui-icons--shelf-b.png b/frontend/__snapshots__/lemon-ui-icons--shelf-b.png index 708f32b5e4aa3..7e81f11adc7f0 100644 Binary files a/frontend/__snapshots__/lemon-ui-icons--shelf-b.png and b/frontend/__snapshots__/lemon-ui-icons--shelf-b.png differ diff --git a/frontend/__snapshots__/lemon-ui-icons--shelf-i.png b/frontend/__snapshots__/lemon-ui-icons--shelf-i.png index c9f4b8b51b6b0..7673cc4d1c96d 100644 Binary files a/frontend/__snapshots__/lemon-ui-icons--shelf-i.png and b/frontend/__snapshots__/lemon-ui-icons--shelf-i.png differ diff --git a/frontend/src/lib/lemon-ui/icons/icons.tsx b/frontend/src/lib/lemon-ui/icons/icons.tsx index d68e9c0934827..3ef6f11d75e1e 100644 --- a/frontend/src/lib/lemon-ui/icons/icons.tsx +++ b/frontend/src/lib/lemon-ui/icons/icons.tsx @@ -1927,6 +1927,27 @@ export function IconMonitor(props: LemonIconProps): JSX.Element { ) } +/** Material Bold icon. */ +export function IconBold(props: LemonIconProps): JSX.Element { + return ( + + + + ) +} + +/** Material Italic icon. */ +export function IconItalic(props: LemonIconProps): JSX.Element { + return ( + + + + ) +} + /** Material CellPhone icon. */ export function IconPhone(props: LemonIconProps): JSX.Element { return ( diff --git a/frontend/src/scenes/notebooks/Marks/NotebookMarkLink.tsx b/frontend/src/scenes/notebooks/Marks/NotebookMarkLink.tsx index 39e5feaa4aead..52a66b633af6d 100644 --- a/frontend/src/scenes/notebooks/Marks/NotebookMarkLink.tsx +++ b/frontend/src/scenes/notebooks/Marks/NotebookMarkLink.tsx @@ -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', @@ -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) + } } }, }, diff --git a/frontend/src/scenes/notebooks/Notebook/Editor.tsx b/frontend/src/scenes/notebooks/Notebook/Editor.tsx index 0f4e83aa16485..e814d4a314023 100644 --- a/frontend/src/scenes/notebooks/Notebook/Editor.tsx +++ b/frontend/src/scenes/notebooks/Notebook/Editor.tsx @@ -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*', @@ -223,6 +224,7 @@ export function Editor({ <> {_editor && } + {_editor && } ) } diff --git a/frontend/src/scenes/notebooks/Notebook/InlineMenu.tsx b/frontend/src/scenes/notebooks/Notebook/InlineMenu.tsx new file mode 100644 index 0000000000000..7a2e837c16853 --- /dev/null +++ b/frontend/src/scenes/notebooks/Notebook/InlineMenu.tsx @@ -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 ( + +
+ {editor.isActive('link') ? ( + <> + + } status="primary" size="small" /> + editor.chain().focus().unsetMark('link').run()} + icon={} + status="danger" + size="small" + /> + + ) : ( + <> + editor.chain().focus().toggleMark('bold').run()} + active={editor.isActive('bold')} + icon={} + size="small" + status={editor.isActive('bold') ? 'primary' : 'stealth'} + /> + editor.chain().focus().toggleMark('italic').run()} + active={editor.isActive('italic')} + icon={} + status={editor.isActive('italic') ? 'primary' : 'stealth'} + size="small" + /> + editor.chain().focus().setMark('link').run()} + icon={} + status="stealth" + size="small" + /> + + )} +
+
+ ) +} diff --git a/frontend/src/scenes/notebooks/Notebook/Notebook.scss b/frontend/src/scenes/notebooks/Notebook/Notebook.scss index 5433abc22cff6..03675b8da8b88 100644 --- a/frontend/src/scenes/notebooks/Notebook/Notebook.scss +++ b/frontend/src/scenes/notebooks/Notebook/Notebook.scss @@ -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); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 67d43ea804243..c8bfe1d9d3974 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5450,8 +5450,8 @@ packages: '@tiptap/core': 2.1.0-rc.12(@tiptap/pm@2.1.0-rc.12) dev: false - /@tiptap/extension-bubble-menu@2.1.0-rc.12(@tiptap/core@2.1.0-rc.12)(@tiptap/pm@2.1.0-rc.12): - resolution: {integrity: sha512-Q8DzlM61KAhrq742b0x4+Ey3WChp6X8mIvHRhNhdbChmgtNyKX1d8k72euUC6hKBCUwH4b+AQ5JVmmhoJTfsjQ==} + /@tiptap/extension-bubble-menu@2.1.10(@tiptap/core@2.1.0-rc.12)(@tiptap/pm@2.1.0-rc.12): + resolution: {integrity: sha512-XxgJajXkfAj/fChXkIwKBs7/3pd7OxV1uGc6Opx1qW/nSRYx/rr97654Sx/sg6auwIlbpRoqTmyqjbykGX1/yA==} peerDependencies: '@tiptap/core': ^2.0.0 '@tiptap/pm': ^2.0.0 @@ -5652,7 +5652,7 @@ packages: react-dom: ^17.0.0 || ^18.0.0 dependencies: '@tiptap/core': 2.1.0-rc.12(@tiptap/pm@2.1.0-rc.12) - '@tiptap/extension-bubble-menu': 2.1.0-rc.12(@tiptap/core@2.1.0-rc.12)(@tiptap/pm@2.1.0-rc.12) + '@tiptap/extension-bubble-menu': 2.1.10(@tiptap/core@2.1.0-rc.12)(@tiptap/pm@2.1.0-rc.12) '@tiptap/extension-floating-menu': 2.1.0-rc.12(@tiptap/core@2.1.0-rc.12)(@tiptap/pm@2.1.0-rc.12) '@tiptap/pm': 2.1.0-rc.12 react: 16.14.0