@@ -32,18 +32,29 @@ type Props = {
3232 textarea : HTMLTextAreaElement ;
3333 extensionAPI : OnloadArgs [ "extensionAPI" ] ;
3434 trigger ?: JSX . Element ;
35+ isShift ?: boolean ;
3536} ;
3637
3738const NodeMenu = ( {
3839 onClose,
3940 textarea,
4041 extensionAPI,
4142 trigger,
43+ isShift,
4244} : { onClose : ( ) => void } & Props ) => {
43- const discourseNodes = useMemo (
45+ const isInitialTextSelected =
46+ textarea . selectionStart !== textarea . selectionEnd ;
47+
48+ const [ showNodeTypes , setShowNodeTypes ] = useState (
49+ isInitialTextSelected || ( isShift ?? false ) ,
50+ ) ;
51+ const userDiscourseNodes = useMemo (
4452 ( ) => getDiscourseNodes ( ) . filter ( ( n ) => n . backedBy === "user" ) ,
4553 [ ] ,
4654 ) ;
55+ const discourseNodes = userDiscourseNodes . filter (
56+ ( n ) => showNodeTypes || n . tag ,
57+ ) ;
4758 const indexBySC = useMemo (
4859 ( ) => Object . fromEntries ( discourseNodes . map ( ( mi , i ) => [ mi . shortcut , i ] ) ) ,
4960 [ discourseNodes ] ,
@@ -55,52 +66,80 @@ const NodeMenu = ({
5566 const [ isOpen , setIsOpen ] = useState ( ! trigger ) ;
5667
5768 const onSelect = useCallback (
58- ( index ) => {
69+ ( index : number ) => {
5970 const menuItem =
6071 menuRef . current ?. children [ index ] . querySelector ( ".bp3-menu-item" ) ;
6172 if ( ! menuItem ) return ;
62- const nodeUid = menuItem . getAttribute ( "data-node" ) || "" ;
63- const highlighted = textarea . value . substring (
64- textarea . selectionStart ,
65- textarea . selectionEnd ,
66- ) ;
67- setTimeout ( async ( ) => {
68- const pageName = await getNewDiscourseNodeText ( {
69- text : highlighted ,
70- nodeType : nodeUid ,
71- blockUid,
72- } ) ;
7373
74- if ( ! pageName ) {
75- return ;
76- }
77-
78- const currentBlockText = getTextByBlockUid ( blockUid ) ;
79- const newText = `${ currentBlockText . substring (
80- 0 ,
74+ if ( showNodeTypes ) {
75+ const nodeUid = menuItem . getAttribute ( "data-node" ) || "" ;
76+ const highlighted = textarea . value . substring (
8177 textarea . selectionStart ,
82- ) } [[${ pageName } ]]${ currentBlockText . substring ( textarea . selectionEnd ) } `;
78+ textarea . selectionEnd ,
79+ ) ;
80+ setTimeout ( async ( ) => {
81+ const pageName = await getNewDiscourseNodeText ( {
82+ text : highlighted ,
83+ nodeType : nodeUid ,
84+ blockUid,
85+ } ) ;
8386
84- updateBlock ( { text : newText , uid : blockUid } ) ;
85- posthog . capture ( "Discourse Node: Created via Node Menu" , {
86- nodeType : nodeUid ,
87- text : pageName ,
87+ if ( ! pageName ) {
88+ return ;
89+ }
90+
91+ const currentBlockText = getTextByBlockUid ( blockUid ) ;
92+ const newText = `${ currentBlockText . substring (
93+ 0 ,
94+ textarea . selectionStart ,
95+ ) } [[${ pageName } ]]${ currentBlockText . substring ( textarea . selectionEnd ) } `;
96+
97+ updateBlock ( { text : newText , uid : blockUid } ) ;
98+ posthog . capture ( "Discourse Node: Created via Node Menu" , {
99+ nodeType : nodeUid ,
100+ text : pageName ,
101+ } ) ;
102+
103+ createDiscourseNode ( {
104+ text : pageName ,
105+ configPageUid : nodeUid ,
106+ extensionAPI,
107+ } ) ;
88108 } ) ;
109+ } else {
110+ const tag = menuItem . getAttribute ( "data-tag" ) || "" ;
111+ if ( ! tag ) return ;
112+
113+ setTimeout ( ( ) => {
114+ const currentText = textarea . value ;
115+ const cursorPos = textarea . selectionStart ;
116+ const textToInsert = `#${ tag } ` ;
117+
118+ const newText = `${ currentText . substring (
119+ 0 ,
120+ cursorPos ,
121+ ) } ${ textToInsert } ${ currentText . substring ( cursorPos ) } `;
89122
90- createDiscourseNode ( {
91- text : pageName ,
92- configPageUid : nodeUid ,
93- extensionAPI ,
123+ updateBlock ( { text : newText , uid : blockUid } ) ;
124+ posthog . capture ( "Discourse Tag: Created via Node Menu" , {
125+ tag ,
126+ } ) ;
94127 } ) ;
95- } ) ;
128+ }
96129 onClose ( ) ;
97130 } ,
98- [ menuRef , blockUid , onClose , textarea , extensionAPI ] ,
131+ [ menuRef , blockUid , onClose , textarea , extensionAPI , showNodeTypes ] ,
99132 ) ;
100133
101134 const keydownListener = useCallback (
102135 ( e : KeyboardEvent ) => {
103- if ( ! isOpen || e . metaKey || e . ctrlKey || e . shiftKey ) return ;
136+ if ( ! isOpen || e . metaKey || e . ctrlKey ) return ;
137+ if ( e . key === "Shift" ) {
138+ if ( ! isInitialTextSelected ) {
139+ setShowNodeTypes ( true ) ;
140+ }
141+ return ;
142+ }
104143
105144 if ( e . key === "ArrowDown" ) {
106145 const index = Number (
@@ -134,26 +173,46 @@ const NodeMenu = ({
134173 e . stopPropagation ( ) ;
135174 e . preventDefault ( ) ;
136175 } ,
137- [ onSelect , onClose , indexBySC , isOpen ] ,
176+ [ onSelect , onClose , indexBySC , isOpen , isInitialTextSelected ] ,
177+ ) ;
178+
179+ const keyupListener = useCallback (
180+ ( e : KeyboardEvent ) => {
181+ if ( e . key === "Shift" && ! isInitialTextSelected ) {
182+ setShowNodeTypes ( false ) ;
183+ }
184+ } ,
185+ [ isInitialTextSelected ] ,
138186 ) ;
187+
139188 useEffect ( ( ) => {
140189 const eventTarget = trigger ? document : textarea ;
141190 const keydownHandler = ( e : Event ) => {
142191 keydownListener ( e as KeyboardEvent ) ;
143192 } ;
193+
144194 eventTarget . addEventListener ( "keydown" , keydownHandler ) ;
195+ eventTarget . addEventListener ( "keyup" , keyupListener as EventListener ) ;
145196
146197 if ( ! trigger ) {
147198 textarea . addEventListener ( "input" , onClose ) ;
148199 }
149200
150201 return ( ) => {
151202 eventTarget . removeEventListener ( "keydown" , keydownHandler ) ;
203+ eventTarget . removeEventListener ( "keyup" , keyupListener as EventListener ) ;
152204 if ( ! trigger ) {
153205 textarea . removeEventListener ( "input" , onClose ) ;
154206 }
155207 } ;
156- } , [ keydownListener , onClose , textarea , trigger ] ) ;
208+ } , [
209+ keydownListener ,
210+ keyupListener ,
211+ onClose ,
212+ textarea ,
213+ trigger ,
214+ isInitialTextSelected ,
215+ ] ) ;
157216
158217 const handlePopoverInteraction = useCallback (
159218 ( nextOpenState : boolean ) => {
@@ -190,10 +249,14 @@ const NodeMenu = ({
190249 < MenuItem
191250 key = { item . text }
192251 data-node = { item . type }
193- text = { item . text }
252+ data-tag = { item . tag }
253+ text = {
254+ showNodeTypes ? item . text : item . tag ? `#${ item . tag } ` : ""
255+ }
194256 active = { i === activeIndex }
195257 onMouseEnter = { ( ) => setActiveIndex ( i ) }
196258 onClick = { ( ) => onSelect ( i ) }
259+ disabled = { ! showNodeTypes && ! item . tag }
197260 className = "flex items-center"
198261 icon = {
199262 < div
@@ -275,6 +338,7 @@ export const TextSelectionNodeMenu = ({
275338 extensionAPI = { extensionAPI }
276339 trigger = { trigger }
277340 onClose = { onClose }
341+ isShift
278342 />
279343 ) ;
280344} ;
0 commit comments