-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
addkeyboardhandlingforgrid.ts
136 lines (119 loc) · 4.36 KB
/
addkeyboardhandlingforgrid.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @module ui/bindings/addkeyboardhandlingforgrid
*/
import type { FocusTracker, KeystrokeHandler } from '@ckeditor/ckeditor5-utils';
import type { FocusableView } from '../focuscycler.js';
import type ViewCollection from '../viewcollection.js';
/**
* A helper that adds a keyboard navigation support (arrow up/down/left/right) for grids.
*
* @param options Configuration options.
* @param options.keystrokeHandler Keystroke handler to register navigation with arrow keys.
* @param options.focusTracker A focus tracker for grid elements.
* @param options.gridItems A collection of grid items.
* @param options.numberOfColumns Number of columns in the grid. Can be specified as a function that returns
* the number (e.g. for responsive grids).
* @param options.uiLanguageDirection String of ui language direction.
*/
export default function addKeyboardHandlingForGrid(
{ keystrokeHandler, focusTracker, gridItems, numberOfColumns, uiLanguageDirection }: {
keystrokeHandler: KeystrokeHandler;
focusTracker: FocusTracker;
gridItems: ViewCollection;
numberOfColumns: number | ( () => number );
uiLanguageDirection?: string;
}
): void {
const getNumberOfColumns = typeof numberOfColumns === 'number' ? () => numberOfColumns : numberOfColumns;
keystrokeHandler.set( 'arrowright', getGridItemFocuser( ( focusedElementIndex, gridItems ) => {
return uiLanguageDirection === 'rtl' ?
getLeftElementIndex( focusedElementIndex, gridItems.length ) :
getRightElementIndex( focusedElementIndex, gridItems.length );
} ) );
keystrokeHandler.set( 'arrowleft', getGridItemFocuser( ( focusedElementIndex, gridItems ) => {
return uiLanguageDirection === 'rtl' ?
getRightElementIndex( focusedElementIndex, gridItems.length ) :
getLeftElementIndex( focusedElementIndex, gridItems.length );
} ) );
keystrokeHandler.set( 'arrowup', getGridItemFocuser( ( focusedElementIndex, gridItems ) => {
let nextIndex = focusedElementIndex - getNumberOfColumns();
if ( nextIndex < 0 ) {
nextIndex = focusedElementIndex + getNumberOfColumns() * Math.floor( gridItems.length / getNumberOfColumns() );
if ( nextIndex > gridItems.length - 1 ) {
nextIndex -= getNumberOfColumns();
}
}
return nextIndex;
} ) );
keystrokeHandler.set( 'arrowdown', getGridItemFocuser( ( focusedElementIndex, gridItems ) => {
let nextIndex = focusedElementIndex + getNumberOfColumns();
if ( nextIndex > gridItems.length - 1 ) {
nextIndex = focusedElementIndex % getNumberOfColumns();
}
return nextIndex;
} ) );
function getGridItemFocuser( getIndexToFocus: ( focusedElementIndex: number, gridItems: ViewCollection ) => number ) {
return ( evt: KeyboardEvent ) => {
const focusedElement = gridItems.find( item => item.element === focusTracker.focusedElement );
const focusedElementIndex = gridItems.getIndex( focusedElement! );
const nextIndexToFocus = getIndexToFocus( focusedElementIndex, gridItems );
( gridItems.get( nextIndexToFocus ) as FocusableView ).focus();
evt.stopPropagation();
evt.preventDefault();
};
}
/**
* Function returning the next index.
*
* ```
* before: [ ][x][ ] after: [ ][ ][x]
* index = 1 index = 2
* ```
*
* If current index is last, function returns first index.
*
* ```
* before: [ ][ ][x] after: [x][ ][ ]
* index = 2 index = 0
* ```
*
* @param elementIndex Number of current index.
* @param collectionLength A count of collection items.
*/
function getRightElementIndex( elementIndex: number, collectionLength: number ) {
if ( elementIndex === collectionLength - 1 ) {
return 0;
} else {
return elementIndex + 1;
}
}
/**
* Function returning the previous index.
*
* ```
* before: [ ][x][ ] after: [x][ ][ ]
* index = 1 index = 0
* ```
*
* If current index is first, function returns last index.
*
* ```
* before: [x][ ][ ] after: [ ][ ][x]
* index = 0 index = 2
* ```
*
* @param elementIndex Number of current index.
* @param collectionLength A count of collection items.
*/
function getLeftElementIndex( elementIndex: number, collectionLength: number ) {
if ( elementIndex === 0 ) {
return collectionLength - 1;
} else {
return elementIndex - 1;
}
}
}