@@ -51,13 +51,15 @@ class ContextMenu implements IREWMenu.IContextMenu {
5151 } else {
5252 this . container = document . createElement ( 'div' ) ;
5353 this . container . setAttribute ( 'data-rewm-contextmenu-container' , id ) ;
54+ this . container . setAttribute ( 'tabindex' , '0' ) ;
5455 document . body . appendChild ( this . container ) ;
5556 }
5657
5758 // set style of this.container
5859 this . container . style . position = 'absolute' ;
5960 this . container . style . left = containerLeft + 'px' ;
6061 this . container . style . top = containerTop + 'px' ;
62+ this . container . style . outline = '0' ;
6163
6264 if ( this . container ) {
6365 this . visible = true ;
@@ -69,15 +71,21 @@ class ContextMenu implements IREWMenu.IContextMenu {
6971 }
7072
7173 onClickItem : IREWMenu . OnClickItem = ( menuItem , w , e ) => {
72- const { type = 'normal' , enabled = true , visible = true } = menuItem ;
74+ const { type = 'normal' , enabled = true } = menuItem ;
7375
7476 if ( enabled ) {
77+ if ( ! menuItem . submenu && menuItem . click ) {
78+ menuItem . click ( menuItem , w , e ) ;
79+ }
80+
7581 if ( type === 'checkbox' ) {
7682 menuItem . checked = ! menuItem . checked ;
83+ this . render ( ) ;
84+ } else {
85+ // 메뉴가 클릭되었다는 것을 인지하는 곳.
86+ this . visible = false ;
7787 }
7888 }
79- // 메뉴가 클릭되었다는 것은 인지하는 곳.
80- this . visible = false ;
8189 } ;
8290
8391 // document.body에서 마우스 다운이 일어난 경우 contextMenu안쪽이 클릭된 것이지 바깥쪽에서 마우스 다운이 일어 난 건지 체크.
@@ -88,9 +96,123 @@ class ContextMenu implements IREWMenu.IContextMenu {
8896 }
8997 } ;
9098
91- onKeyDownWindow = ( e : KeyboardEvent ) => {
92- if ( e . which === 27 ) {
93- this . visible = false ;
99+ getCurrentHoveredIndex = ( ) : number [ ] => {
100+ const currentHoveredIndex : number [ ] = [ ] ;
101+ const findOpenedItem = ( items : IREWMenu . IMenuItem [ ] ) => {
102+ const findex = items . findIndex ( item => item . opened ) ;
103+ if ( findex > - 1 ) {
104+ currentHoveredIndex . push ( findex ) ;
105+ if ( items [ findex ] . submenu ) {
106+ findOpenedItem ( items [ findex ] . submenu ! ) ;
107+ }
108+ }
109+ } ;
110+ findOpenedItem ( this . menuItems ) ;
111+ return currentHoveredIndex ;
112+ } ;
113+
114+ moveHoveredIndex = ( type : 'UP' | 'DOWN' | 'LEFT' | 'RIGHT' ) => {
115+ const currentHoveredIndexes = this . getCurrentHoveredIndex ( ) ;
116+ let targetItems = this . menuItems ;
117+ let targetItemsHoveredIndex = currentHoveredIndexes [ 0 ] ;
118+
119+ currentHoveredIndexes . forEach ( ( v , i ) => {
120+ if ( i < currentHoveredIndexes . length - 1 && targetItems [ v ] . submenu ) {
121+ targetItems = targetItems [ v ] . submenu ! ;
122+ targetItemsHoveredIndex = currentHoveredIndexes [ i + 1 ] ;
123+ }
124+ } ) ;
125+ if ( type === 'UP' || type === 'DOWN' ) {
126+ targetItems . forEach ( item => ( item . opened = false ) ) ;
127+
128+ let ing = true ;
129+ do {
130+ const nextMenuIndex =
131+ type === 'DOWN'
132+ ? targetItemsHoveredIndex === undefined
133+ ? 0
134+ : targetItemsHoveredIndex + 1
135+ : targetItemsHoveredIndex === undefined
136+ ? targetItems . length - 1
137+ : targetItemsHoveredIndex - 1 ;
138+ const nextMenu = targetItems [ nextMenuIndex ] ;
139+ if ( ! nextMenu ) {
140+ targetItems [ targetItemsHoveredIndex ] . opened = true ;
141+ ing = false ;
142+ break ;
143+ }
144+ if ( nextMenu . type === 'separator' || nextMenu . visible === false ) {
145+ targetItemsHoveredIndex = nextMenuIndex ;
146+ continue ;
147+ }
148+ targetItems [ nextMenuIndex ] . opened = true ;
149+ ing = false ;
150+ } while ( ing ) ;
151+
152+ this . render ( ) ;
153+ } else if ( type === 'RIGHT' ) {
154+ if ( targetItemsHoveredIndex === undefined ) targetItemsHoveredIndex = 0 ;
155+ targetItems [ targetItemsHoveredIndex ] . submenu ?. forEach (
156+ item => ( item . opened = false ) ,
157+ ) ;
158+ const submenu = targetItems [ targetItemsHoveredIndex ] . submenu ;
159+ if ( submenu ) {
160+ submenu [ 0 ] . opened = true ;
161+ }
162+ this . render ( ) ;
163+ } else if ( type === 'LEFT' ) {
164+ targetItems . forEach ( item => ( item . opened = false ) ) ;
165+ this . render ( ) ;
166+ }
167+ } ;
168+
169+ handleClickItem = ( e : KeyboardEvent ) => {
170+ let menu : IREWMenu . IMenuItem | undefined ;
171+ const findOpenedItem = ( items : IREWMenu . IMenuItem [ ] ) => {
172+ const findex = items . findIndex ( item => item . opened ) ;
173+ if ( findex > - 1 ) {
174+ menu = items [ findex ] ;
175+ if ( items [ findex ] . submenu ) {
176+ findOpenedItem ( items [ findex ] . submenu ! ) ;
177+ }
178+ }
179+ } ;
180+ findOpenedItem ( this . menuItems ) ;
181+
182+ if ( menu ) {
183+ this . onClickItem ( menu , window ) ;
184+ }
185+ } ;
186+
187+ onKeyDown = ( e : KeyboardEvent ) => {
188+ e . preventDefault ( ) ;
189+
190+ switch ( e . key ) {
191+ case 'Down' : // IE/Edge specific value
192+ case 'ArrowDown' :
193+ this . moveHoveredIndex ( 'DOWN' ) ;
194+ break ;
195+ case 'Up' : // IE/Edge specific value
196+ case 'ArrowUp' :
197+ this . moveHoveredIndex ( 'UP' ) ;
198+ break ;
199+ case 'Left' : // IE/Edge specific value
200+ case 'ArrowLeft' :
201+ this . moveHoveredIndex ( 'LEFT' ) ;
202+ break ;
203+ case 'Right' : // IE/Edge specific value
204+ case 'ArrowRight' :
205+ this . moveHoveredIndex ( 'RIGHT' ) ;
206+ break ;
207+ case 'Enter' :
208+ this . handleClickItem ( e ) ;
209+ break ;
210+ case 'Esc' : // IE/Edge specific value
211+ case 'Escape' :
212+ this . visible = false ;
213+ break ;
214+ default :
215+ return ; // Quit when this doesn't handle the key event.
94216 }
95217 } ;
96218
@@ -115,11 +237,14 @@ class ContextMenu implements IREWMenu.IContextMenu {
115237 ) ;
116238
117239 if ( this . visible ) {
240+ this . container . focus ( ) ;
118241 document . body . addEventListener ( 'mousedown' , this . onMousedownBody ) ;
119- window . addEventListener ( 'keydown' , this . onKeyDownWindow ) ;
242+ this . container . addEventListener ( 'keydown' , this . onKeyDown ) ;
120243 } else {
121244 document . body . removeEventListener ( 'mousedown' , this . onMousedownBody ) ;
122- window . removeEventListener ( 'keydown' , this . onKeyDownWindow ) ;
245+ this . container . removeEventListener ( 'keydown' , this . onKeyDown ) ;
246+ this . container . blur ( ) ;
247+ this . container . remove ( ) ;
123248 }
124249 }
125250}
0 commit comments