funda-ui 4.7.222 → 4.7.252
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Date/index.js +51 -0
- package/EventCalendar/index.js +30 -13
- package/EventCalendarTimeline/index.js +30 -13
- package/ModalDialog/index.d.ts +8 -4
- package/ModalDialog/index.js +30 -13
- package/Table/index.js +304 -183
- package/lib/cjs/Date/index.js +51 -0
- package/lib/cjs/EventCalendar/index.js +30 -13
- package/lib/cjs/EventCalendarTimeline/index.js +30 -13
- package/lib/cjs/ModalDialog/index.d.ts +8 -4
- package/lib/cjs/ModalDialog/index.js +30 -13
- package/lib/cjs/Table/index.js +304 -183
- package/lib/esm/Date/index.tsx +47 -18
- package/lib/esm/Input/index.tsx +1 -0
- package/lib/esm/ModalDialog/index.tsx +39 -20
- package/lib/esm/Table/Table.tsx +74 -15
- package/lib/esm/Table/TableCell.tsx +7 -3
- package/lib/esm/Table/utils/func.ts +12 -1
- package/lib/esm/Table/utils/hooks/useTableKeyPress.tsx +152 -72
- package/package.json +1 -1
|
@@ -2,66 +2,94 @@
|
|
|
2
2
|
* Listens for changes in the pressed state of a given key
|
|
3
3
|
*
|
|
4
4
|
* @usage:
|
|
5
|
+
*
|
|
6
|
+
const App = () => {
|
|
7
|
+
const keyboardFocusable = true;
|
|
8
|
+
const rootRef = useRef<any>(null);
|
|
9
|
+
// Effective element movement on keystroke
|
|
10
|
+
|
|
11
|
+
const refNode = useRef(new Map<string, HTMLTableElement>());
|
|
12
|
+
const [focusableCellId, setFocusableCellId] = useState<string>('');
|
|
13
|
+
|
|
14
|
+
// Count the number of columns per row
|
|
15
|
+
const rootDataInfo = {"totalRow":2,"totalCol":[{"row":0,"colCount":6},{"row":1,"colCount":6},{"row":2,"colCount":6}]};
|
|
5
16
|
|
|
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
|
-
|
|
17
|
+
// Example: handle cell key press with edge detection
|
|
18
|
+
const handleCellKeyPressed = (
|
|
19
|
+
classname: string,
|
|
20
|
+
elem: HTMLTableCellElement,
|
|
21
|
+
event: React.KeyboardEvent<Element>,
|
|
22
|
+
isLeftEdge: boolean,
|
|
23
|
+
isRightEdge: boolean,
|
|
24
|
+
isTopEdge: boolean,
|
|
25
|
+
isBottomEdge: boolean
|
|
26
|
+
) => {
|
|
27
|
+
if (isLeftEdge) {
|
|
28
|
+
// Handle when at the leftmost cell
|
|
29
|
+
}
|
|
30
|
+
if (isRightEdge) {
|
|
31
|
+
// Handle when at the rightmost cell
|
|
32
|
+
}
|
|
33
|
+
if (isTopEdge) {
|
|
34
|
+
// Handle when at the topmost cell
|
|
35
|
+
}
|
|
36
|
+
if (isBottomEdge) {
|
|
37
|
+
// Handle when at the bottommost cell
|
|
38
|
+
}
|
|
39
|
+
// Your business logic here
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const { handleKeyPressed } = useTableKeyPress({
|
|
43
|
+
enabled: keyboardFocusable,
|
|
44
|
+
data: [{ a: 1, b: 2, c: 3 }],
|
|
45
|
+
spyElement: rootRef.current,
|
|
46
|
+
rootDataInfo,
|
|
47
|
+
refNode,
|
|
48
|
+
focusableCellId,
|
|
49
|
+
setFocusableCellId,
|
|
50
|
+
onCellKeyPressed: handleCellKeyPressed,
|
|
51
|
+
onCellPressEnter: () => {},
|
|
52
|
+
}, [rootRef]);
|
|
38
53
|
|
|
54
|
+
return (
|
|
55
|
+
<div
|
|
56
|
+
ref={rootRef}
|
|
57
|
+
tabIndex={-1}
|
|
58
|
+
onKeyDown={handleKeyPressed} // require "tabIndex" attribute
|
|
59
|
+
>Test</div>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
39
62
|
*/
|
|
40
|
-
import { useEffect, useCallback, KeyboardEvent } from "react";
|
|
63
|
+
import { useEffect, useCallback, KeyboardEvent, useRef } from "react";
|
|
41
64
|
|
|
42
65
|
import { initOrderProps, initRowColProps, cellMark, removeCellFocusClassName } from '../func';
|
|
43
66
|
|
|
44
67
|
export interface UseTableKeyPressProps {
|
|
45
68
|
enabled?: boolean;
|
|
46
|
-
data
|
|
69
|
+
data?: any[];
|
|
47
70
|
spyElement?: any;
|
|
48
|
-
rootDataInfo: null | {totalRow: number};
|
|
49
|
-
setRootDataInfo: (s: null | {totalRow: number}) => void;
|
|
71
|
+
rootDataInfo: null | { totalRow: number; totalCol: { row: number; colCount: number }[] };
|
|
50
72
|
refNode: any;
|
|
51
73
|
focusableCellId: string;
|
|
52
74
|
setFocusableCellId: (s: string) => void;
|
|
53
|
-
onCellKeyPressed
|
|
54
|
-
|
|
75
|
+
onCellKeyPressed?: (
|
|
76
|
+
classname: string,
|
|
77
|
+
elem: HTMLTableCellElement,
|
|
78
|
+
event: KeyboardEvent<Element>,
|
|
79
|
+
isLeftEdge: boolean,
|
|
80
|
+
isRightEdge: boolean,
|
|
81
|
+
isTopEdge: boolean,
|
|
82
|
+
isBottomEdge: boolean
|
|
83
|
+
) => void;
|
|
84
|
+
onCellPressEnter?: (classname: string, elem: HTMLTableCellElement, event: KeyboardEvent<Element>) => void;
|
|
55
85
|
onKeyDown?: (e: any) => void;
|
|
56
86
|
}
|
|
57
87
|
|
|
58
|
-
|
|
59
88
|
const useTableKeyPress = ({
|
|
60
89
|
enabled,
|
|
61
90
|
data,
|
|
62
91
|
spyElement,
|
|
63
92
|
rootDataInfo,
|
|
64
|
-
setRootDataInfo,
|
|
65
93
|
refNode,
|
|
66
94
|
focusableCellId,
|
|
67
95
|
setFocusableCellId,
|
|
@@ -69,10 +97,16 @@ const useTableKeyPress = ({
|
|
|
69
97
|
onCellPressEnter,
|
|
70
98
|
onKeyDown
|
|
71
99
|
}: UseTableKeyPressProps, deps: any[]) => {
|
|
100
|
+
const focusableCellIdRef = useRef(focusableCellId);
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
focusableCellIdRef.current = focusableCellId;
|
|
103
|
+
}, [focusableCellId]);
|
|
72
104
|
|
|
73
105
|
const handleKeyPressed = useCallback( async (event: KeyboardEvent<HTMLTableCellElement>) => {
|
|
74
106
|
const key = event.code;
|
|
75
107
|
|
|
108
|
+
|
|
109
|
+
// If Enter is pressed and keyboard navigation is disabled, just trigger onCellPressEnter
|
|
76
110
|
if ((key === 'Enter' || key === 'NumpadEnter') && !enabled) {
|
|
77
111
|
const currentCell = event.target as HTMLTableCellElement;
|
|
78
112
|
const row = Number(currentCell.getAttribute('data-table-row'));
|
|
@@ -82,50 +116,99 @@ const useTableKeyPress = ({
|
|
|
82
116
|
return;
|
|
83
117
|
}
|
|
84
118
|
|
|
119
|
+
// Guard: Only proceed if all required data and enabled flag are valid
|
|
85
120
|
if (!Array.isArray(data) || rootDataInfo === null || spyElement === null || typeof enabled === 'undefined' || enabled === false) return;
|
|
86
121
|
|
|
87
|
-
|
|
122
|
+
|
|
123
|
+
// Parse the current focused cell's row and column
|
|
124
|
+
const oldCellSignal = focusableCellIdRef.current?.replace('cell-', '').split('-');
|
|
88
125
|
let _row = Number(oldCellSignal[0]);
|
|
89
126
|
let _col = Number(oldCellSignal[1]);
|
|
90
127
|
|
|
91
128
|
|
|
129
|
+
// Move function to handle arrow key navigation
|
|
92
130
|
const move = (key: string) => {
|
|
93
|
-
|
|
131
|
+
let isLeftEdge = false;
|
|
132
|
+
let isRightEdge = false;
|
|
133
|
+
let isTopEdge = false;
|
|
134
|
+
let isBottomEdge = false;
|
|
135
|
+
let maxCol = 0;
|
|
136
|
+
if (rootDataInfo && Array.isArray(rootDataInfo.totalCol)) {
|
|
137
|
+
const rowInfo = rootDataInfo.totalCol.find(r => r.row === _row);
|
|
138
|
+
if (rowInfo) {
|
|
139
|
+
maxCol = rowInfo.colCount;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
94
142
|
switch (key) {
|
|
95
143
|
case 'ArrowLeft':
|
|
96
144
|
case 'Numpad4':
|
|
97
|
-
|
|
98
|
-
|
|
145
|
+
if (_col - 1 < 0) {
|
|
146
|
+
isLeftEdge = true;
|
|
147
|
+
_col = 0;
|
|
148
|
+
} else {
|
|
149
|
+
_col = _col - 1;
|
|
150
|
+
}
|
|
151
|
+
break;
|
|
99
152
|
case 'ArrowRight':
|
|
100
|
-
case 'Numpad6':
|
|
101
|
-
|
|
102
|
-
|
|
153
|
+
case 'Numpad6': {
|
|
154
|
+
if (_col + 1 > maxCol - 1) {
|
|
155
|
+
isRightEdge = true;
|
|
156
|
+
_col = maxCol - 1;
|
|
157
|
+
} else {
|
|
158
|
+
_col = _col + 1;
|
|
159
|
+
}
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
103
162
|
case 'ArrowUp':
|
|
104
163
|
case 'Numpad8':
|
|
105
|
-
|
|
106
|
-
|
|
164
|
+
if (_row - 1 < 0) {
|
|
165
|
+
isTopEdge = true;
|
|
166
|
+
_row = 0;
|
|
167
|
+
} else {
|
|
168
|
+
_row = _row - 1;
|
|
169
|
+
}
|
|
170
|
+
break;
|
|
107
171
|
case 'ArrowDown':
|
|
108
172
|
case 'Numpad2':
|
|
109
|
-
|
|
110
|
-
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
if (_row + 1 > rootDataInfo.totalRow - 1) {
|
|
176
|
+
isBottomEdge = true;
|
|
177
|
+
_row = rootDataInfo.totalRow - 1;
|
|
178
|
+
} else {
|
|
179
|
+
_row = _row + 1;
|
|
180
|
+
}
|
|
181
|
+
break;
|
|
111
182
|
}
|
|
112
183
|
|
|
184
|
+
|
|
185
|
+
|
|
113
186
|
const nextCellSignal: string = cellMark(_row, _col);
|
|
114
|
-
|
|
115
|
-
// focus current cell
|
|
187
|
+
// Focus the current cell
|
|
116
188
|
removeCellFocusClassName(spyElement);
|
|
117
|
-
spyElement.querySelector(`.${nextCellSignal}`)?.classList.add('cell-focus');
|
|
118
189
|
|
|
190
|
+
|
|
191
|
+
const targetCell = refNode.current.get(nextCellSignal);
|
|
192
|
+
if (typeof targetCell !== 'undefined') {
|
|
193
|
+
targetCell.classList.add('cell-focus');
|
|
194
|
+
}
|
|
119
195
|
|
|
120
|
-
|
|
196
|
+
|
|
121
197
|
setFocusableCellId(nextCellSignal);
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
198
|
+
// Callback with edge detection
|
|
199
|
+
onCellKeyPressed?.(
|
|
200
|
+
nextCellSignal,
|
|
201
|
+
refNode.current.get(nextCellSignal),
|
|
202
|
+
event,
|
|
203
|
+
isLeftEdge,
|
|
204
|
+
isRightEdge,
|
|
205
|
+
isTopEdge,
|
|
206
|
+
isBottomEdge
|
|
207
|
+
);
|
|
125
208
|
onKeyDown?.(event);
|
|
126
|
-
|
|
127
209
|
};
|
|
128
210
|
|
|
211
|
+
// Handle arrow key navigation
|
|
129
212
|
if (key === 'ArrowLeft' || key === 'Numpad4') {
|
|
130
213
|
move('ArrowLeft');
|
|
131
214
|
}
|
|
@@ -134,7 +217,6 @@ const useTableKeyPress = ({
|
|
|
134
217
|
move('ArrowRight');
|
|
135
218
|
}
|
|
136
219
|
|
|
137
|
-
|
|
138
220
|
if (key === 'ArrowUp' || key === 'Numpad8') {
|
|
139
221
|
move('ArrowUp');
|
|
140
222
|
}
|
|
@@ -143,32 +225,30 @@ const useTableKeyPress = ({
|
|
|
143
225
|
move('ArrowDown');
|
|
144
226
|
}
|
|
145
227
|
|
|
228
|
+
// Handle Enter key
|
|
146
229
|
if (key === 'Enter' || key === 'NumpadEnter') {
|
|
147
230
|
const nextCellSignal: string = cellMark(_row, _col);
|
|
148
231
|
onCellPressEnter?.(nextCellSignal, refNode.current.get(nextCellSignal), event);
|
|
149
232
|
}
|
|
150
233
|
|
|
151
|
-
}, [focusableCellId]);
|
|
234
|
+
}, [focusableCellId, rootDataInfo, data]);
|
|
152
235
|
|
|
153
236
|
useEffect(() => {
|
|
154
237
|
if (enabled) {
|
|
155
|
-
|
|
156
|
-
// Initialize custom props of table elements
|
|
238
|
+
// Initialize custom props of table elements (only once)
|
|
157
239
|
initOrderProps(spyElement);
|
|
158
240
|
initRowColProps(spyElement);
|
|
159
|
-
|
|
160
|
-
// Update cell ids
|
|
161
|
-
if (Array.isArray(data)) {
|
|
162
|
-
setRootDataInfo({
|
|
163
|
-
totalRow: data.length
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
241
|
}
|
|
167
242
|
}, [enabled, spyElement, ...deps]);
|
|
168
243
|
|
|
169
|
-
|
|
170
244
|
return {
|
|
171
|
-
handleKeyPressed
|
|
245
|
+
handleKeyPressed,
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Expose handleKeyPressed for external usage, e.g., via contentRef in Table component.
|
|
249
|
+
* This allows calling handleKeyPressed programmatically from outside, such as with a custom onCellKeyPressed method.
|
|
250
|
+
*/
|
|
251
|
+
triggerCellKeyPressed: handleKeyPressed,
|
|
172
252
|
};
|
|
173
253
|
};
|
|
174
254
|
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"author": "UIUX Lab",
|
|
3
3
|
"email": "uiuxlab@gmail.com",
|
|
4
4
|
"name": "funda-ui",
|
|
5
|
-
"version": "4.7.
|
|
5
|
+
"version": "4.7.252",
|
|
6
6
|
"description": "React components using pure Bootstrap 5+ which does not contain any external style and script libraries.",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|