@@ -9,7 +9,8 @@ if (!customElements.get('prosekit-autocomplete-item')) customElements.define('pr
99if ( ! customElements . get ( 'prosekit-autocomplete-empty' ) ) customElements . define ( 'prosekit-autocomplete-empty' , AutocompleteEmpty )
1010
1111import { html , LitElement } from 'lit' ;
12- import { canUseRegexLookbehind } from 'prosekit/core'
12+ import { canUseRegexLookbehind , defineUpdateHandler } from 'prosekit/core'
13+ import { Selection } from 'prosekit/pm/state'
1314import { insertChoiceInteraction } from '@qti-editor/interaction-choice' ;
1415import { insertExtendedTextInteraction } from '@qti-editor/interaction-extended-text' ;
1516import { insertInlineChoiceInteraction } from '@qti-editor/interaction-inline-choice' ;
@@ -35,24 +36,90 @@ function canInsert(view, nodeType) {
3536 return false ;
3637}
3738
39+ function isSelectionInsideNodeType ( view , nodeType ) {
40+ const { $from } = view . state . selection ;
41+
42+ for ( let depth = $from . depth ; depth >= 0 ; depth -= 1 ) {
43+ if ( $from . node ( depth ) . type === nodeType ) {
44+ return true ;
45+ }
46+ }
47+
48+ return false ;
49+ }
50+
3851class SlashMenuElement extends LitElement {
3952 static properties = {
4053 editor : {
4154 attribute : false
4255 } ,
4356 } ;
4457
58+ removeUpdateExtension ;
59+ lastSelectionJson = null ;
60+
4561 createRenderRoot ( ) {
4662 return this
4763 }
4864
65+ connectedCallback ( ) {
66+ super . connectedCallback ( )
67+ this . attachEditorListener ( )
68+ }
69+
70+ disconnectedCallback ( ) {
71+ this . detachEditorListener ( )
72+ super . disconnectedCallback ( )
73+ }
74+
75+ updated ( changedProperties ) {
76+ super . updated ( changedProperties )
77+ if ( changedProperties . has ( 'editor' ) ) {
78+ this . attachEditorListener ( )
79+ }
80+ }
81+
4982 getView ( ) {
5083 return this . editor ?. view ?? null
5184 }
5285
86+ attachEditorListener ( ) {
87+ this . detachEditorListener ( )
88+ if ( ! this . editor ) return
89+ this . removeUpdateExtension = this . editor . use ( defineUpdateHandler ( ( ) => {
90+ this . snapshotSelection ( )
91+ this . requestUpdate ( )
92+ } ) )
93+ this . snapshotSelection ( )
94+ }
95+
96+ detachEditorListener ( ) {
97+ this . removeUpdateExtension ?. ( )
98+ this . removeUpdateExtension = undefined
99+ }
100+
101+ snapshotSelection ( ) {
102+ const view = this . getView ( )
103+ if ( ! view ) return
104+ this . lastSelectionJson = view . state . selection . toJSON ( )
105+ }
106+
107+ restoreSelection ( ) {
108+ const view = this . getView ( )
109+ if ( ! view || ! this . lastSelectionJson ) return
110+
111+ try {
112+ const restored = Selection . fromJSON ( view . state . doc , this . lastSelectionJson )
113+ view . dispatch ( view . state . tr . setSelection ( restored ) )
114+ } catch {
115+ return
116+ }
117+ }
118+
53119 runCommand = ( command ) => {
54120 const view = this . getView ( )
55121 if ( ! view ) return
122+ this . restoreSelection ( )
56123 command ( view )
57124 view . focus ( )
58125 } ;
@@ -61,6 +128,8 @@ class SlashMenuElement extends LitElement {
61128 const view = this . getView ( )
62129 if ( ! view ) return
63130
131+ this . restoreSelection ( )
132+
64133 const nodeType = view . state . schema . nodes . qtiTextEntryInteraction
65134 if ( ! nodeType ) return
66135
@@ -107,7 +176,7 @@ class SlashMenuElement extends LitElement {
107176 < lit-editor-slash-menu-item
108177 class ="contents "
109178 label ="Inline choice "
110- ?disabled =${ ! canInsert ( view , schema . nodes . qtiInlineChoiceInteraction ) }
179+ ?disabled =${ isSelectionInsideNodeType ( view , schema . nodes . qtiInlineChoiceInteraction ) || ! canInsert ( view , schema . nodes . qtiInlineChoiceInteraction ) }
111180 @select =${ ( ) => this . runCommand ( ( currentView ) => insertInlineChoiceInteraction ( currentView . state , currentView . dispatch , currentView ) ) }
112181 > </ lit-editor-slash-menu-item >
113182 `
0 commit comments