99 */
1010
1111import type { IControlType , IDictionary , IJodit } from 'jodit/types' ;
12+ import {
13+ KEY_DOWN ,
14+ KEY_ENTER ,
15+ KEY_ESC ,
16+ KEY_LEFT ,
17+ KEY_RIGHT ,
18+ KEY_UP
19+ } from 'jodit/core/constants' ;
1220import { Dom } from 'jodit/core/dom' ;
1321import { $$ , css , scrollIntoViewIfNeeded } from 'jodit/core/helpers' ;
1422import { attr } from 'jodit/core/helpers/utils' ;
@@ -102,27 +110,31 @@ Config.prototype.controls.table = {
102110
103111 const cnt = default_rows_count * default_cols_count ;
104112
113+ attr ( blocksContainer , {
114+ role : 'grid' ,
115+ ariaLabel : 'Table size' ,
116+ ariaRowcount : default_rows_count ,
117+ ariaColcount : default_cols_count
118+ } ) ;
119+
105120 for ( let i = 0 ; i < cnt ; i += 1 ) {
106121 if ( ! cells [ i ] ) {
122+ const row = Math . floor ( i / default_cols_count ) + 1 ,
123+ col = ( i % default_cols_count ) + 1 ;
124+
107125 cells . push (
108126 editor . c . element ( 'span' , {
109- dataIndex : i
127+ dataIndex : i ,
128+ role : 'gridcell' ,
129+ tabindex : i === 0 ? 0 : - 1 ,
130+ ariaLabel : `${ row } by ${ col } `
110131 } )
111132 ) ;
112133 }
113134 }
114135
115- const mouseenter = ( e : MouseEvent , index ?: number ) : void => {
116- const dv = e . target ;
117-
118- if ( ! Dom . isTag ( dv , 'span' ) ) {
119- return ;
120- }
121-
122- const k =
123- index === undefined || isNaN ( index )
124- ? parseInt ( attr ( dv , '-index' ) || '0' , 10 )
125- : index || 0 ;
136+ const highlightCell = ( cell : HTMLElement ) : void => {
137+ const k = parseInt ( attr ( cell , '-index' ) || '0' , 10 ) ;
126138
127139 const rows_count = Math . ceil ( ( k + 1 ) / default_cols_count ) ,
128140 cols_count = ( k % default_cols_count ) + 1 ;
@@ -142,118 +154,177 @@ Config.prototype.controls.table = {
142154 rows . textContent = rows_count . toString ( ) ;
143155 } ;
144156
145- editor . e
146- . on ( blocksContainer , 'mousemove' , mouseenter )
147- . on ( blocksContainer , 'touchstart mousedown' , ( e : MouseEvent ) => {
148- const dv = e . target ;
157+ const insertTable = ( cell : HTMLElement ) : void => {
158+ const k = parseInt ( attr ( cell , '-index' ) || '0' , 10 ) ;
149159
150- e . preventDefault ( ) ;
151- e . stopImmediatePropagation ( ) ;
160+ const rows_count = Math . ceil ( ( k + 1 ) / default_cols_count ) ,
161+ cols_count = ( k % default_cols_count ) + 1 ;
152162
153- if ( ! Dom . isTag ( dv , 'span' ) ) {
154- return ;
155- }
163+ const crt = editor . createInside ,
164+ tbody = crt . element ( 'tbody' ) ,
165+ table = crt . element ( 'table' ) ;
156166
157- const k = parseInt ( attr ( dv , '-index' ) || '0' , 10 ) ;
167+ table . appendChild ( tbody ) ;
158168
159- const rows_count = Math . ceil ( ( k + 1 ) / default_cols_count ) ,
160- cols_count = ( k % default_cols_count ) + 1 ;
169+ let first_td : HTMLTableCellElement | null = null ,
170+ tr : HTMLTableRowElement ,
171+ td : HTMLTableCellElement ;
161172
162- const crt = editor . createInside ,
163- tbody = crt . element ( 'tbody' ) ,
164- table = crt . element ( 'table' ) ;
173+ for ( let i = 1 ; i <= rows_count ; i += 1 ) {
174+ tr = crt . element ( 'tr' ) ;
165175
166- table . appendChild ( tbody ) ;
176+ for ( let j = 1 ; j <= cols_count ; j += 1 ) {
177+ td = crt . element ( 'td' ) ;
167178
168- let first_td : HTMLTableCellElement | null = null ,
169- tr : HTMLTableRowElement ,
170- td : HTMLTableCellElement ;
179+ if ( ! first_td ) {
180+ first_td = td ;
181+ }
171182
172- for ( let i = 1 ; i <= rows_count ; i += 1 ) {
173- tr = crt . element ( 'tr' ) ;
183+ css ( td , 'width' , ( 100 / cols_count ) . toFixed ( 4 ) + '%' ) ;
174184
175- for ( let j = 1 ; j <= cols_count ; j += 1 ) {
176- td = crt . element ( 'td' ) ;
185+ td . appendChild ( crt . element ( 'br' ) ) ;
186+ tr . appendChild ( crt . text ( '\n' ) ) ;
187+ tr . appendChild ( crt . text ( '\t' ) ) ;
188+ tr . appendChild ( td ) ;
189+ }
177190
178- if ( ! first_td ) {
179- first_td = td ;
180- }
191+ tbody . appendChild ( crt . text ( '\n' ) ) ;
192+ tbody . appendChild ( tr ) ;
193+ }
181194
182- css ( td , 'width' , ( 100 / cols_count ) . toFixed ( 4 ) + '%' ) ;
195+ $$ ( 'input[type=checkbox]:checked' , options ) . forEach (
196+ ( input : HTMLElement ) => {
197+ ( input as HTMLInputElement ) . value
198+ . split ( / [ \s ] + / )
199+ . forEach ( ( className : string ) => {
200+ table . classList . add ( className ) ;
201+ } ) ;
202+ }
203+ ) ;
204+
205+ editor . s . restore ( ) ;
206+ editor . s . removeMarkers ( ) ;
207+ editor . editor . normalize ( ) ;
208+ editor . history . snapshot . restore ( snapshot ) ;
209+
210+ const block = Dom . furthest (
211+ editor . s . current ( ) ,
212+ Dom . isBlock ,
213+ editor . editor
214+ ) ;
215+
216+ if ( block && Dom . isEmpty ( block ) ) {
217+ Dom . replace ( block , table , undefined , false , true ) ;
218+ } else {
219+ if ( block ) {
220+ const fake = crt . text ( '\n' ) ;
221+ if ( ! editor . o . table . splitBlockOnInsertTable ) {
222+ Dom . after ( block , fake ) ;
223+ Dom . after ( fake , table ) ;
224+ } else {
225+ const range = editor . s . range ;
226+ range . collapse ( false ) ;
227+ range . insertNode ( fake ) ;
228+ range . collapse ( false ) ;
229+ editor . s . selectRange ( range ) ;
183230
184- td . appendChild ( crt . element ( 'br' ) ) ;
185- tr . appendChild ( crt . text ( '\n' ) ) ;
186- tr . appendChild ( crt . text ( '\t' ) ) ;
187- tr . appendChild ( td ) ;
188- }
231+ const firstPart = editor . s . splitSelection ( block , fake ) ;
189232
190- tbody . appendChild ( crt . text ( '\n' ) ) ;
191- tbody . appendChild ( tr ) ;
233+ if ( firstPart ) {
234+ Dom . after ( firstPart , table ) ;
235+ } else {
236+ Dom . after ( block , table ) ;
237+ }
238+ }
239+ } else {
240+ editor . s . insertNode ( table , false ) ;
192241 }
242+ }
193243
194- $$ ( 'input[type=checkbox]:checked' , options ) . forEach (
195- ( input : HTMLElement ) => {
196- ( input as HTMLInputElement ) . value
197- . split ( / [ \s ] + / )
198- . forEach ( ( className : string ) => {
199- table . classList . add ( className ) ;
200- } ) ;
201- }
202- ) ;
244+ if ( first_td ) {
245+ editor . s . setCursorIn ( first_td ) ;
246+ scrollIntoViewIfNeeded ( first_td , editor . editor , editor . ed ) ;
247+ }
203248
204- editor . s . restore ( ) ;
205- editor . s . removeMarkers ( ) ;
206- editor . editor . normalize ( ) ;
207- editor . history . snapshot . restore ( snapshot ) ;
249+ close ( ) ;
250+ } ;
208251
209- const block = Dom . furthest (
210- editor . s . current ( ) ,
211- Dom . isBlock ,
212- editor . editor
213- ) ;
252+ editor . e
253+ . on ( blocksContainer , 'mousemove' , ( e : MouseEvent ) => {
254+ if ( Dom . isTag ( e . target , 'span' ) ) {
255+ highlightCell ( e . target ) ;
256+ }
257+ } )
258+ . on ( blocksContainer , 'touchstart mousedown' , ( e : MouseEvent ) => {
259+ if ( ! Dom . isTag ( e . target , 'span' ) ) {
260+ return ;
261+ }
214262
215- if ( block && Dom . isEmpty ( block ) ) {
216- Dom . replace ( block , table , undefined , false , true ) ;
217- } else {
218- if ( block ) {
219- const fake = crt . text ( '\n' ) ;
220- if ( ! editor . o . table . splitBlockOnInsertTable ) {
221- Dom . after ( block , fake ) ;
222- Dom . after ( fake , table ) ;
223- } else {
224- const range = editor . s . range ;
225- range . collapse ( false ) ;
226- range . insertNode ( fake ) ;
227- range . collapse ( false ) ;
228- editor . s . selectRange ( range ) ;
229-
230- const firstPart = editor . s . splitSelection (
231- block ,
232- fake
233- ) ;
234-
235- if ( firstPart ) {
236- Dom . after ( firstPart , table ) ;
237- } else {
238- Dom . after ( block , table ) ;
239- }
240- }
241- } else {
242- editor . s . insertNode ( table , false ) ;
243- }
263+ e . preventDefault ( ) ;
264+ e . stopImmediatePropagation ( ) ;
265+ insertTable ( e . target ) ;
266+ } )
267+ . on ( blocksContainer , 'keydown' , ( e : KeyboardEvent ) => {
268+ if ( ! Dom . isTag ( e . target , 'span' ) ) {
269+ return ;
244270 }
245271
246- if ( first_td ) {
247- editor . s . setCursorIn ( first_td ) ;
248- scrollIntoViewIfNeeded ( first_td , editor . editor , editor . ed ) ;
272+ if ( e . key === KEY_ENTER ) {
273+ e . preventDefault ( ) ;
274+ e . stopImmediatePropagation ( ) ;
275+ insertTable ( e . target ) ;
276+ return ;
277+ }
278+
279+ if ( e . key === KEY_ESC ) {
280+ e . preventDefault ( ) ;
281+ e . stopImmediatePropagation ( ) ;
282+ close ( ) ;
283+ return ;
249284 }
250285
251- close ( ) ;
286+ const index = parseInt ( attr ( e . target , '-index' ) || '0' , 10 ) ,
287+ row = Math . floor ( index / default_cols_count ) ,
288+ col = index % default_cols_count ;
289+ let nextIndex = index ;
290+
291+ switch ( e . key ) {
292+ case KEY_LEFT :
293+ nextIndex = col > 0 ? index - 1 : index ;
294+ break ;
295+ case KEY_RIGHT :
296+ nextIndex =
297+ col < default_cols_count - 1 ? index + 1 : index ;
298+ break ;
299+ case KEY_UP :
300+ nextIndex =
301+ row > 0 ? index - default_cols_count : index ;
302+ break ;
303+ case KEY_DOWN :
304+ nextIndex =
305+ row < default_rows_count - 1
306+ ? index + default_cols_count
307+ : index ;
308+ break ;
309+ default :
310+ return ;
311+ }
312+
313+ e . preventDefault ( ) ;
314+ e . stopImmediatePropagation ( ) ;
315+
316+ if ( nextIndex !== index ) {
317+ cells [ index ] . tabIndex = - 1 ;
318+ cells [ nextIndex ] . tabIndex = 0 ;
319+ cells [ nextIndex ] . focus ( ) ;
320+ highlightCell ( cells [ nextIndex ] ) ;
321+ }
252322 } ) ;
253323
254324 if ( button && button . parentElement ) {
255325 for ( let i = 0 ; i < default_rows_count ; i += 1 ) {
256326 const row = editor . c . div ( ) ;
327+ attr ( row , 'role' , 'row' ) ;
257328
258329 for ( let j = 0 ; j < default_cols_count ; j += 1 ) {
259330 row . appendChild ( cells [ i * default_cols_count + j ] ) ;
0 commit comments