@@ -9,101 +9,136 @@ import {
99} from 'react-icons/fa6' ;
1010import { styled } from 'styled-components' ;
1111import * as RadixPopover from '@radix-ui/react-popover' ;
12- import { Row } from '../../components/Row' ;
12+ import { Column , Row } from '../../components/Row' ;
1313
1414import { Popover } from '../../components/Popover' ;
15- import { useState } from 'react' ;
15+ import { useRef , useState } from 'react' ;
1616import { transparentize } from 'polished' ;
1717import { EditLinkForm } from './EditLinkForm' ;
1818import { useTipTapEditor } from './TiptapContext' ;
1919import { ToggleButton } from './ToggleButton' ;
2020import { NodeSelectMenu } from './NodeSelectMenu' ;
21+ import { useEditorState } from '@tiptap/react' ;
2122
22- export function BubbleMenu ( ) : React . JSX . Element {
23+ interface BubbleMenuProps {
24+ children ?: React . ReactNode ;
25+ extraItems ?: React . ReactNode ;
26+ onShow ?: ( ) => void ;
27+ }
28+
29+ export function BubbleMenu ( {
30+ children,
31+ extraItems,
32+ onShow,
33+ } : BubbleMenuProps ) : React . JSX . Element {
34+ const bubbleMenuElement = useRef < HTMLDivElement > ( null ) ;
2335 const editor = useTipTapEditor ( ) ;
2436 const [ linkMenuOpen , setLinkMenuOpen ] = useState ( false ) ;
2537
26- if ( ! editor ) {
38+ const { isBold, isItalic, isStrikethrough, isBlockquote, isCode, isLink } =
39+ useEditorState ( {
40+ editor,
41+ selector : snapshot => ( {
42+ isBold : snapshot . editor . isActive ( 'bold' ) ,
43+ isItalic : snapshot . editor . isActive ( 'italic' ) ,
44+ isStrikethrough : snapshot . editor . isActive ( 'strike' ) ,
45+ isBlockquote : snapshot . editor . isActive ( 'blockquote' ) ,
46+ isCode : snapshot . editor . isActive ( 'code' ) ,
47+ isLink : snapshot . editor . isActive ( 'link' ) ,
48+ } ) ,
49+ } ) ;
50+
51+ if ( ! editor . view ) {
2752 return < > </ > ;
2853 }
2954
3055 return (
31- < TipTapBubbleMenu editor = { editor } >
32- < BubbleMenuInner gap = '0.5ch' >
33- < NodeSelectMenu />
34- < ToggleButton
35- title = 'Toggle bold'
36- $active = { ! ! editor . isActive ( 'bold' ) }
37- onClick = { ( ) => editor . chain ( ) . focus ( ) . toggleBold ( ) . run ( ) }
38- disabled = { ! editor . can ( ) . chain ( ) . focus ( ) . toggleBold ( ) . run ( ) }
39- type = 'button'
40- >
41- < FaBold />
42- </ ToggleButton >
43- < ToggleButton
44- title = 'Toggle italic'
45- $active = { ! ! editor . isActive ( 'italic' ) }
46- onClick = { ( ) => editor . chain ( ) . focus ( ) . toggleItalic ( ) . run ( ) }
47- disabled = { ! editor . can ( ) . chain ( ) . focus ( ) . toggleItalic ( ) . run ( ) }
48- type = 'button'
49- >
50- < FaItalic />
51- </ ToggleButton >
52- < ToggleButton
53- title = 'Toggle strikethrough'
54- $active = { ! ! editor . isActive ( 'strike' ) }
55- onClick = { ( ) => editor . chain ( ) . focus ( ) . toggleStrike ( ) . run ( ) }
56- disabled = { ! editor . can ( ) . chain ( ) . focus ( ) . toggleStrike ( ) . run ( ) }
57- type = 'button'
58- >
59- < FaStrikethrough />
60- </ ToggleButton >
61- < ToggleButton
62- title = 'Toggle blockquote'
63- $active = { ! ! editor . isActive ( 'blockquote' ) }
64- onClick = { ( ) => editor . chain ( ) . focus ( ) . toggleBlockquote ( ) . run ( ) }
65- disabled = { ! editor . can ( ) . chain ( ) . focus ( ) . toggleBlockquote ( ) . run ( ) }
66- type = 'button'
67- >
68- < FaQuoteLeft />
69- </ ToggleButton >
70- < ToggleButton
71- title = 'Toggle inline code'
72- $active = { ! ! editor . isActive ( 'code' ) }
73- onClick = { ( ) => editor . chain ( ) . focus ( ) . toggleCode ( ) . run ( ) }
74- disabled = { ! editor . can ( ) . chain ( ) . focus ( ) . toggleCode ( ) . run ( ) }
75- type = 'button'
76- >
77- < FaCode />
78- </ ToggleButton >
79- < StyledPopover
80- modal
81- open = { linkMenuOpen }
82- onOpenChange = { setLinkMenuOpen }
83- Trigger = {
84- < ToggleButton
85- as = { RadixPopover . Trigger }
86- $active = { ! ! editor . isActive ( 'link' ) }
87- disabled = { ! editor . can ( ) . chain ( ) . focus ( ) . toggleCode ( ) . run ( ) }
88- type = 'button'
89- >
90- < FaLink />
91- </ ToggleButton >
92- }
93- >
94- < EditLinkForm onDone = { ( ) => setLinkMenuOpen ( false ) } />
95- </ StyledPopover >
56+ < TipTapBubbleMenu
57+ editor = { editor }
58+ ref = { bubbleMenuElement }
59+ options = { { onShow } }
60+ >
61+ < BubbleMenuInner >
62+ < Row gap = '0.5ch' >
63+ < NodeSelectMenu />
64+ < ToggleButton
65+ title = 'Toggle bold'
66+ $active = { isBold }
67+ onClick = { ( ) => editor . chain ( ) . focus ( ) . toggleBold ( ) . run ( ) }
68+ disabled = { ! editor . can ( ) . chain ( ) . focus ( ) . toggleBold ( ) . run ( ) }
69+ type = 'button'
70+ >
71+ < FaBold />
72+ </ ToggleButton >
73+ < ToggleButton
74+ title = 'Toggle italic'
75+ $active = { isItalic }
76+ onClick = { ( ) => editor . chain ( ) . focus ( ) . toggleItalic ( ) . run ( ) }
77+ disabled = { ! editor . can ( ) . chain ( ) . focus ( ) . toggleItalic ( ) . run ( ) }
78+ type = 'button'
79+ >
80+ < FaItalic />
81+ </ ToggleButton >
82+ < ToggleButton
83+ title = 'Toggle strikethrough'
84+ $active = { isStrikethrough }
85+ onClick = { ( ) => editor . chain ( ) . focus ( ) . toggleStrike ( ) . run ( ) }
86+ disabled = { ! editor . can ( ) . chain ( ) . focus ( ) . toggleStrike ( ) . run ( ) }
87+ type = 'button'
88+ >
89+ < FaStrikethrough />
90+ </ ToggleButton >
91+ < ToggleButton
92+ title = 'Toggle blockquote'
93+ $active = { isBlockquote }
94+ onClick = { ( ) => editor . chain ( ) . focus ( ) . toggleBlockquote ( ) . run ( ) }
95+ disabled = { ! editor . can ( ) . chain ( ) . focus ( ) . toggleBlockquote ( ) . run ( ) }
96+ type = 'button'
97+ >
98+ < FaQuoteLeft />
99+ </ ToggleButton >
100+ < ToggleButton
101+ title = 'Toggle inline code'
102+ $active = { isCode }
103+ onClick = { ( ) => editor . chain ( ) . focus ( ) . toggleCode ( ) . run ( ) }
104+ disabled = { ! editor . can ( ) . chain ( ) . focus ( ) . toggleCode ( ) . run ( ) }
105+ type = 'button'
106+ >
107+ < FaCode />
108+ </ ToggleButton >
109+ < StyledPopover
110+ modal
111+ open = { linkMenuOpen }
112+ onOpenChange = { setLinkMenuOpen }
113+ side = 'top'
114+ Trigger = {
115+ < ToggleButton
116+ as = { RadixPopover . Trigger }
117+ $active = { isLink }
118+ disabled = { ! editor . can ( ) . chain ( ) . focus ( ) . toggleLink ( ) . run ( ) }
119+ type = 'button'
120+ >
121+ < FaLink />
122+ </ ToggleButton >
123+ }
124+ >
125+ < EditLinkForm onDone = { ( ) => setLinkMenuOpen ( false ) } />
126+ </ StyledPopover >
127+ { children }
128+ </ Row >
129+ { extraItems }
96130 </ BubbleMenuInner >
97131 </ TipTapBubbleMenu >
98132 ) ;
99133}
100134
101- const BubbleMenuInner = styled ( Row ) `
135+ const BubbleMenuInner = styled ( Column ) `
102136 background-color: ${ p => p . theme . colors . bg } ;
103137 border-radius: ${ p => p . theme . radius } ;
104138 padding: ${ p => p . theme . size ( 2 ) } ;
105139 box-shadow: ${ p => p . theme . boxShadowSoft } ;
106-
140+ border: ${ p =>
141+ p . theme . darkMode ? `1px solid ${ p . theme . colors . bg2 } ` : 'none' } ;
107142 @supports (backdrop-filter: blur(5px)) {
108143 background-color: ${ p => transparentize ( 0.15 , p . theme . colors . bg ) } ;
109144 backdrop-filter: blur(5px);
@@ -115,6 +150,8 @@ const StyledPopover = styled(Popover)`
115150 backdrop-filter: blur(5px);
116151 padding: ${ p => p . theme . size ( ) } ;
117152 border-radius: ${ p => p . theme . radius } ;
153+ border: ${ p =>
154+ p . theme . darkMode ? `1px solid ${ p . theme . colors . bg2 } ` : 'none' } ;
118155
119156 @supports (backdrop-filter: blur(5px)) {
120157 background-color: ${ p => transparentize ( 0.15 , p . theme . colors . bg ) } ;
0 commit comments