11import type { VirtualElement } from '@floating-ui/react' ;
2- import { Popup , type PopupPlacement , type PopupProps } from '@gravity-ui/uikit' ;
3- import type { EditorState } from 'prosemirror-state' ;
2+ import type { PopupPlacement , PopupProps } from '@gravity-ui/uikit' ;
43import type { EditorView } from 'prosemirror-view' ;
54
65import type { ActionStorage } from '../../../core' ;
7- import { isFunction } from '../../../lodash' ;
86import { type Logger2 , globalLogger } from '../../../logger' ;
97import { ErrorLoggerBoundary } from '../../../react-utils/ErrorBoundary' ;
10- import { Toolbar } from '../../../toolbar' ;
11- import type {
12- ToolbarButtonPopupData ,
13- ToolbarGroupItemData ,
14- ToolbarProps ,
15- ToolbarSingleItemData ,
16- } from '../../../toolbar' ;
178import { type RendererItem , getReactRendererFromState } from '../ReactRenderer' ;
189
19- type SelectionTooltipBaseProps = {
20- show ?: boolean ;
21- poppupProps : PopupProps ;
22- } ;
23- type SelectionTooltipProps = SelectionTooltipBaseProps & ToolbarProps < ActionStorage > ;
24-
25- const SelectionTooltip : React . FC < SelectionTooltipProps > = ( {
26- show,
27- poppupProps,
28- ...toolbarProps
29- } ) => {
30- if ( ! show ) return null ;
31- return (
32- < Popup open { ...poppupProps } style = { { padding : '4px 8px' } } >
33- < Toolbar { ...toolbarProps } />
34- </ Popup >
35- ) ;
36- } ;
10+ import { TextSelectionTooltip } from './TextSelectionTooltip' ;
11+ import type { ContextConfig } from './types' ;
3712
38- export type ContextGroupItemData =
39- | ( ToolbarGroupItemData < ActionStorage > & {
40- condition ?: ( state : EditorState ) => void ;
41- } )
42- | ( ( ToolbarSingleItemData < ActionStorage > | ToolbarButtonPopupData < ActionStorage > ) & {
43- condition ?: 'enabled' ;
44- } ) ;
45-
46- export type ContextGroupData = ContextGroupItemData [ ] ;
47- export type ContextConfig = ContextGroupData [ ] ;
13+ export type { ContextGroupItemData , ContextGroupData , ContextConfig } from './types' ;
4814
4915export type TooltipViewParams = {
5016 /** @default 'bottom' */
5117 placement ?: 'top' | 'bottom' ;
5218 /** @default false */
5319 flip ?: boolean ;
20+ onPopupOpenChange : PopupProps [ 'onOpenChange' ] ;
5421} ;
5522
5623export class TooltipView {
@@ -60,9 +27,11 @@ export class TooltipView {
6027 private readonly actions : ActionStorage ;
6128 private readonly menuConfig : ContextConfig ;
6229 private readonly placement : PopupPlacement ;
30+ private readonly onPopupOpenChange : PopupProps [ 'onOpenChange' ] ;
6331
6432 private view ! : EditorView ;
65- private baseProps : SelectionTooltipBaseProps = { show : false , poppupProps : { } } ;
33+ private visible = false ;
34+ private anchor : PopupProps [ 'anchorElement' ] = undefined ;
6635 private _tooltipRenderItem : RendererItem | null = null ;
6736
6837 constructor (
@@ -75,35 +44,32 @@ export class TooltipView {
7544 this . actions = actions ;
7645 this . menuConfig = menuConfig ;
7746
78- const { flip, placement = 'bottom' } = params ;
47+ const { flip, placement = 'bottom' , onPopupOpenChange } = params ;
7948 this . placement = flip ? placement : [ placement ] ;
49+ this . onPopupOpenChange = onPopupOpenChange ;
8050 }
8151
8252 get isTooltipOpen ( ) : boolean {
8353 return this . #isTooltipOpen;
8454 }
8555
86- show ( view : EditorView , popupProps ?: PopupProps ) {
56+ show ( view : EditorView ) {
8757 this . view = view ;
8858 this . #isTooltipOpen = true ;
89- this . baseProps = {
90- show : true ,
91- poppupProps : {
92- ...popupProps ,
93- ...this . calcPosition ( view ) ,
94- } ,
95- } ;
59+ this . visible = true ;
60+ this . anchor ??= this . createVirtualElement ( view ) ;
9661 this . renderPopup ( ) ;
9762 }
9863
9964 hide ( view : EditorView ) {
10065 this . view = view ;
10166
10267 // do not rerender popup if it is already hidden
103- if ( ! this . #isTooltipOpen && ! this . baseProps . show ) return ;
68+ if ( ! this . #isTooltipOpen && ! this . visible ) return ;
10469
10570 this . #isTooltipOpen = false ;
106- this . baseProps = { show : false , poppupProps : { } } ;
71+ this . visible = false ;
72+ this . anchor = undefined ;
10773 this . renderPopup ( ) ;
10874 }
10975
@@ -112,38 +78,11 @@ export class TooltipView {
11278 this . _tooltipRenderItem = null ;
11379 }
11480
115- private getSelectionTooltipProps ( ) : SelectionTooltipProps {
116- return {
117- ...this . baseProps ,
118- qa : 'g-md-toolbar-selection' ,
119- focus : ( ) => this . view . focus ( ) ,
120- data : this . getFilteredConfig ( ) ,
121- editor : this . actions ,
122- onClick : ( id ) => {
123- globalLogger . action ( { mode : 'wysiwyg' , source : 'context-menu' , action : id } ) ;
124- this . logger . action ( { source : 'context-menu' , action : id } ) ;
125- } ,
126- } ;
127- }
128-
129- private getFilteredConfig ( ) : ContextConfig {
130- return this . baseProps . show
131- ? this . menuConfig
132- . map ( ( groupData ) =>
133- groupData . filter ( ( item ) => {
134- const { condition} = item ;
135- if ( condition === 'enabled' ) {
136- return item . isEnable ( this . actions ) ;
137- }
138- if ( isFunction ( condition ) ) {
139- return condition ( this . view . state ) ;
140- }
141- return true ;
142- } ) ,
143- )
144- . filter ( ( groupData ) => Boolean ( groupData . length ) )
145- : [ ] ;
146- }
81+ private readonly handleFocus = ( ) => this . view . focus ( ) ;
82+ private readonly handleClick = ( id : string ) => {
83+ globalLogger . action ( { mode : 'wysiwyg' , source : 'context-menu' , action : id } ) ;
84+ this . logger . action ( { source : 'context-menu' , action : id } ) ;
85+ } ;
14786
14887 private renderPopup ( ) {
14988 this . tooltipRenderItem . rerender ( ) ;
@@ -152,17 +91,29 @@ export class TooltipView {
15291 private get tooltipRenderItem ( ) {
15392 if ( ! this . _tooltipRenderItem ) {
15493 const reactRenderer = getReactRendererFromState ( this . view . state ) ;
155- this . _tooltipRenderItem = reactRenderer . createItem ( 'selection_context' , ( ) => (
156- < ErrorLoggerBoundary >
157- < SelectionTooltip { ...this . getSelectionTooltipProps ( ) } />
158- </ ErrorLoggerBoundary >
159- ) ) ;
94+ this . _tooltipRenderItem = reactRenderer . createItem ( 'selection_context' , ( ) => {
95+ if ( ! this . visible ) return null ;
96+ return (
97+ < ErrorLoggerBoundary >
98+ < TextSelectionTooltip
99+ config = { this . menuConfig }
100+ editor = { this . actions }
101+ editorView = { this . view }
102+ focus = { this . handleFocus }
103+ onClick = { this . handleClick }
104+ popupPlacement = { this . placement }
105+ popupAnchor = { this . anchor }
106+ popupOnOpenChange = { this . onPopupOpenChange }
107+ />
108+ </ ErrorLoggerBoundary >
109+ ) ;
110+ } ) ;
160111 }
161112 return this . _tooltipRenderItem ;
162113 }
163114
164- private calcPosition ( view : EditorView ) : PopupProps {
165- const virtualElem : VirtualElement = {
115+ private createVirtualElement ( view : EditorView ) : VirtualElement {
116+ return {
166117 getBoundingClientRect ( ) {
167118 // These are in screen coordinates
168119 const start = view . coordsAtPos ( view . state . selection . from ) ;
@@ -188,10 +139,5 @@ export class TooltipView {
188139 } ;
189140 } ,
190141 } ;
191-
192- return {
193- placement : this . placement ,
194- anchorElement : virtualElem ,
195- } ;
196142 }
197143}
0 commit comments