fwtoolkit 0.1.0-alpha.6 → 0.1.0-beta.1
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/css/alerts.css +7 -0
- package/css/dialog.css +62 -0
- package/css/overview_menu.css +7 -0
- package/dist/basic.d.ts +49 -36
- package/dist/basic.d.ts.map +1 -1
- package/dist/basic.js +58 -39
- package/dist/basic.js.map +1 -1
- package/dist/blob.d.ts +1 -1
- package/dist/blob.d.ts.map +1 -1
- package/dist/blob.js +0 -1
- package/dist/blob.js.map +1 -1
- package/dist/content_menu.d.ts +63 -20
- package/dist/content_menu.d.ts.map +1 -1
- package/dist/content_menu.js +23 -20
- package/dist/content_menu.js.map +1 -1
- package/dist/datatable_bulk.d.ts +34 -6
- package/dist/datatable_bulk.d.ts.map +1 -1
- package/dist/datatable_bulk.js +4 -5
- package/dist/datatable_bulk.js.map +1 -1
- package/dist/dialog.d.ts +82 -7
- package/dist/dialog.d.ts.map +1 -1
- package/dist/dialog.js +21 -16
- package/dist/dialog.js.map +1 -1
- package/dist/events.d.ts +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +3 -3
- package/dist/events.js.map +1 -1
- package/dist/faq_dialog.d.ts +13 -4
- package/dist/faq_dialog.d.ts.map +1 -1
- package/dist/faq_dialog.js +4 -2
- package/dist/faq_dialog.js.map +1 -1
- package/dist/file/dialog.d.ts +33 -13
- package/dist/file/dialog.d.ts.map +1 -1
- package/dist/file/dialog.js +6 -8
- package/dist/file/dialog.js.map +1 -1
- package/dist/file/index.d.ts.map +1 -1
- package/dist/file/index.js +0 -1
- package/dist/file/index.js.map +1 -1
- package/dist/file/new_folder_dialog.d.ts +5 -2
- package/dist/file/new_folder_dialog.d.ts.map +1 -1
- package/dist/file/new_folder_dialog.js +1 -2
- package/dist/file/new_folder_dialog.js.map +1 -1
- package/dist/file/selector.d.ts +47 -14
- package/dist/file/selector.d.ts.map +1 -1
- package/dist/file/selector.js +11 -9
- package/dist/file/selector.js.map +1 -1
- package/dist/file/templates.d.ts +1 -1
- package/dist/file/templates.d.ts.map +1 -1
- package/dist/file/templates.js +0 -1
- package/dist/file/templates.js.map +1 -1
- package/dist/file/tools.d.ts +4 -4
- package/dist/file/tools.d.ts.map +1 -1
- package/dist/file/tools.js +4 -4
- package/dist/file/tools.js.map +1 -1
- package/dist/focus.d.ts +1 -1
- package/dist/focus.d.ts.map +1 -1
- package/dist/focus.js +5 -5
- package/dist/focus.js.map +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -3
- package/dist/index.js.map +1 -1
- package/dist/network.d.ts +19 -7
- package/dist/network.d.ts.map +1 -1
- package/dist/network.js +15 -12
- package/dist/network.js.map +1 -1
- package/dist/overview_menu.d.ts +77 -18
- package/dist/overview_menu.d.ts.map +1 -1
- package/dist/overview_menu.js +54 -35
- package/dist/overview_menu.js.map +1 -1
- package/dist/settings.d.ts +7 -2
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +0 -1
- package/dist/settings.js.map +1 -1
- package/dist/user.d.ts +8 -2
- package/dist/user.d.ts.map +1 -1
- package/dist/user.js +1 -2
- package/dist/user.js.map +1 -1
- package/dist/worker.d.ts +1 -1
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +1 -2
- package/dist/worker.js.map +1 -1
- package/dist/ws.d.ts +59 -25
- package/dist/ws.d.ts.map +1 -1
- package/dist/ws.js +19 -15
- package/dist/ws.js.map +1 -1
- package/package.json +2 -1
- package/src/basic.ts +136 -69
- package/src/blob.ts +1 -2
- package/src/content_menu.ts +127 -44
- package/src/datatable_bulk.ts +72 -35
- package/src/dialog.ts +156 -61
- package/src/diff-dom.d.ts +16 -0
- package/src/events.ts +3 -3
- package/src/faq_dialog.ts +25 -11
- package/src/file/dialog.ts +48 -14
- package/src/file/index.ts +0 -1
- package/src/file/new_folder_dialog.ts +7 -5
- package/src/file/selector.ts +86 -36
- package/src/file/templates.ts +2 -3
- package/src/file/tools.ts +17 -8
- package/src/focus.ts +11 -13
- package/src/global.d.ts +11 -4
- package/src/index.ts +0 -3
- package/src/network.ts +58 -20
- package/src/overview_menu.ts +183 -109
- package/src/settings.ts +9 -4
- package/src/user.ts +10 -5
- package/src/w3c-keyname.d.ts +3 -0
- package/src/worker.ts +1 -2
- package/src/ws.ts +115 -50
- package/css/ui_dialogs.css +0 -144
- package/dist/templates.d.ts +0 -7
- package/dist/templates.d.ts.map +0 -1
- package/dist/templates.js +0 -43
- package/dist/templates.js.map +0 -1
- package/dist/user_util.d.ts +0 -7
- package/dist/user_util.d.ts.map +0 -1
- package/dist/user_util.js +0 -19
- package/dist/user_util.js.map +0 -1
- package/src/templates.ts +0 -43
- package/src/user_util.ts +0 -17
package/src/overview_menu.ts
CHANGED
|
@@ -1,10 +1,77 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
import {DiffDOM} from "diff-dom"
|
|
3
2
|
import {keyName} from "w3c-keyname"
|
|
3
|
+
|
|
4
4
|
import {escapeText, whenReady} from "./basic.js"
|
|
5
5
|
|
|
6
|
+
export interface OverviewMenuDropdownOption {
|
|
7
|
+
title: string
|
|
8
|
+
action?: (overview: unknown) => void
|
|
9
|
+
[key: string]: unknown
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface OverviewMenuBaseItem {
|
|
13
|
+
id?: string
|
|
14
|
+
title?: string
|
|
15
|
+
keys?: string
|
|
16
|
+
action?: (overview: unknown) => void
|
|
17
|
+
[key: string]: unknown
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface OverviewMenuDropdownItem extends OverviewMenuBaseItem {
|
|
21
|
+
type: "dropdown"
|
|
22
|
+
open?: boolean
|
|
23
|
+
selectedIndex?: number
|
|
24
|
+
content: OverviewMenuDropdownOption[]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface OverviewMenuSelectActionDropdownItem extends OverviewMenuBaseItem {
|
|
28
|
+
type: "select-action-dropdown"
|
|
29
|
+
open?: boolean
|
|
30
|
+
checked?: boolean
|
|
31
|
+
content: OverviewMenuDropdownOption[]
|
|
32
|
+
checkAction?: (overview: unknown) => void
|
|
33
|
+
uncheckAction?: (overview: unknown) => void
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface OverviewMenuTextItem extends OverviewMenuBaseItem {
|
|
37
|
+
type: "text"
|
|
38
|
+
action?: (overview: unknown) => void
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface OverviewMenuButtonItem extends OverviewMenuBaseItem {
|
|
42
|
+
type: "button"
|
|
43
|
+
action?: (overview: unknown) => void
|
|
44
|
+
icon?: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface OverviewMenuSearchItem extends OverviewMenuBaseItem {
|
|
48
|
+
type: "search"
|
|
49
|
+
placeholder?: string
|
|
50
|
+
icon?: string
|
|
51
|
+
input?: (overview: unknown, value: string) => void
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type OverviewMenuItem =
|
|
55
|
+
| OverviewMenuDropdownItem
|
|
56
|
+
| OverviewMenuSelectActionDropdownItem
|
|
57
|
+
| OverviewMenuTextItem
|
|
58
|
+
| OverviewMenuButtonItem
|
|
59
|
+
| OverviewMenuSearchItem
|
|
60
|
+
|
|
61
|
+
export interface OverviewMenuModel {
|
|
62
|
+
content: OverviewMenuItem[]
|
|
63
|
+
}
|
|
64
|
+
|
|
6
65
|
export class OverviewMenuView {
|
|
7
|
-
|
|
66
|
+
overview: unknown
|
|
67
|
+
model: OverviewMenuModel
|
|
68
|
+
dd: DiffDOM
|
|
69
|
+
openedMenu: number | false
|
|
70
|
+
listeners: Record<string, (event: Event) => void>
|
|
71
|
+
keyboardShortcuts: Map<string, OverviewMenuItem>
|
|
72
|
+
menuEl: HTMLElement | null
|
|
73
|
+
|
|
74
|
+
constructor(overview: unknown, model: () => OverviewMenuModel) {
|
|
8
75
|
this.overview = overview
|
|
9
76
|
this.model = model()
|
|
10
77
|
this.dd = new DiffDOM({
|
|
@@ -13,9 +80,10 @@ export class OverviewMenuView {
|
|
|
13
80
|
this.openedMenu = false
|
|
14
81
|
this.listeners = {}
|
|
15
82
|
this.keyboardShortcuts = new Map()
|
|
83
|
+
this.menuEl = null
|
|
16
84
|
}
|
|
17
85
|
|
|
18
|
-
init() {
|
|
86
|
+
init(): void {
|
|
19
87
|
whenReady().then(() => {
|
|
20
88
|
this.addMissingIds(this.model)
|
|
21
89
|
this.bindEvents()
|
|
@@ -23,32 +91,32 @@ export class OverviewMenuView {
|
|
|
23
91
|
})
|
|
24
92
|
}
|
|
25
93
|
|
|
26
|
-
addMissingIds(menu) {
|
|
94
|
+
addMissingIds(menu: {content: Array<{id?: string; type?: string; content?: unknown[]}>}): void {
|
|
27
95
|
// Add missing ids to menu items that don't have an ID.
|
|
28
96
|
menu.content.forEach(item => {
|
|
29
97
|
if (!item.id) {
|
|
30
98
|
item.id = Math.random().toString(36).substring(2)
|
|
31
99
|
}
|
|
32
100
|
if (item.type === "dropdown") {
|
|
33
|
-
this.addMissingIds(item)
|
|
101
|
+
this.addMissingIds(item as {content: Array<{id?: string; type?: string; content?: unknown[]}>})
|
|
34
102
|
}
|
|
35
103
|
})
|
|
36
104
|
}
|
|
37
105
|
|
|
38
|
-
bindEvents() {
|
|
106
|
+
bindEvents(): void {
|
|
39
107
|
this.menuEl = document.getElementById("fw-overview-menu")
|
|
40
|
-
this.listeners.onclick = event => this.onclick(event)
|
|
108
|
+
this.listeners.onclick = event => this.onclick(event as MouseEvent)
|
|
41
109
|
document.body.addEventListener("click", this.listeners.onclick)
|
|
42
|
-
this.listeners.oninput = event => this.oninput(event)
|
|
110
|
+
this.listeners.oninput = event => this.oninput(event as InputEvent)
|
|
43
111
|
document.body.addEventListener("input", this.listeners.oninput)
|
|
44
|
-
this.listeners.onKeydown = event => this.onKeydown(event)
|
|
112
|
+
this.listeners.onKeydown = event => this.onKeydown(event as KeyboardEvent)
|
|
45
113
|
document.body.addEventListener("keydown", this.listeners.onKeydown)
|
|
46
|
-
this.listeners.onFocus = event => this.onFocus(event)
|
|
114
|
+
this.listeners.onFocus = event => this.onFocus(event as FocusEvent)
|
|
47
115
|
document.body.addEventListener("focus", this.listeners.onFocus, true)
|
|
48
116
|
this.update()
|
|
49
117
|
}
|
|
50
118
|
|
|
51
|
-
setupKeyboardShortcuts() {
|
|
119
|
+
setupKeyboardShortcuts(): void {
|
|
52
120
|
// Map all keyboard shortcuts from the menu model
|
|
53
121
|
this.model.content.forEach(menuItem => {
|
|
54
122
|
if (menuItem.keys) {
|
|
@@ -60,7 +128,7 @@ export class OverviewMenuView {
|
|
|
60
128
|
})
|
|
61
129
|
}
|
|
62
130
|
|
|
63
|
-
onKeydown(event) {
|
|
131
|
+
onKeydown(event: KeyboardEvent): void {
|
|
64
132
|
let name = keyName(event)
|
|
65
133
|
if (event.altKey) {
|
|
66
134
|
name = "alt-" + name.toLowerCase()
|
|
@@ -83,21 +151,21 @@ export class OverviewMenuView {
|
|
|
83
151
|
if (
|
|
84
152
|
this.openedMenu === this.model.content.indexOf(menuItem)
|
|
85
153
|
) {
|
|
86
|
-
menuItem.open = false
|
|
154
|
+
;(menuItem as OverviewMenuDropdownItem).open = false
|
|
87
155
|
this.openedMenu = false
|
|
88
156
|
this.update()
|
|
89
157
|
} else {
|
|
90
158
|
if (this.openedMenu !== false) {
|
|
91
|
-
this.model.content[this.openedMenu].open = false
|
|
159
|
+
;(this.model.content[this.openedMenu] as OverviewMenuDropdownItem).open = false
|
|
92
160
|
}
|
|
93
|
-
menuItem.open = true
|
|
161
|
+
;(menuItem as OverviewMenuDropdownItem).open = true
|
|
94
162
|
this.openedMenu = this.model.content.indexOf(menuItem)
|
|
95
163
|
this.update()
|
|
96
|
-
const firstDropdownItem = this.menuEl
|
|
164
|
+
const firstDropdownItem = this.menuEl!.querySelector(
|
|
97
165
|
`.fw-pulldown-item.selected`
|
|
98
166
|
)
|
|
99
167
|
if (firstDropdownItem) {
|
|
100
|
-
firstDropdownItem.focus()
|
|
168
|
+
(firstDropdownItem as HTMLElement).focus()
|
|
101
169
|
}
|
|
102
170
|
}
|
|
103
171
|
} else if (menuItem.action) {
|
|
@@ -110,15 +178,15 @@ export class OverviewMenuView {
|
|
|
110
178
|
// Handle horizontal navigation between menu items
|
|
111
179
|
if (name === "ArrowLeft" || name === "ArrowRight") {
|
|
112
180
|
const menuItems = Array.from(
|
|
113
|
-
this.menuEl
|
|
181
|
+
this.menuEl!.querySelectorAll("#fw-overview-menu > li")
|
|
114
182
|
)
|
|
115
183
|
const focusedElement = document.activeElement
|
|
116
|
-
const currentMenuItem = focusedElement
|
|
184
|
+
const currentMenuItem = focusedElement?.closest("li")
|
|
117
185
|
|
|
118
186
|
if (currentMenuItem) {
|
|
119
187
|
event.preventDefault()
|
|
120
188
|
const currentIndex = menuItems.indexOf(currentMenuItem)
|
|
121
|
-
let newIndex
|
|
189
|
+
let newIndex: number
|
|
122
190
|
|
|
123
191
|
if (name === "ArrowLeft") {
|
|
124
192
|
newIndex =
|
|
@@ -136,7 +204,7 @@ export class OverviewMenuView {
|
|
|
136
204
|
".fw-dropdown-menu, .fw-text-menu, button, input"
|
|
137
205
|
)
|
|
138
206
|
if (nextMenuItem) {
|
|
139
|
-
nextMenuItem.focus()
|
|
207
|
+
(nextMenuItem as HTMLElement).focus()
|
|
140
208
|
}
|
|
141
209
|
}
|
|
142
210
|
return
|
|
@@ -145,23 +213,23 @@ export class OverviewMenuView {
|
|
|
145
213
|
// Handle Enter and Space to open dropdown menus
|
|
146
214
|
if (name === "Enter" || name === " ") {
|
|
147
215
|
const focusedElement = document.activeElement
|
|
148
|
-
if (focusedElement
|
|
216
|
+
if (focusedElement?.matches(".fw-dropdown-menu")) {
|
|
149
217
|
event.preventDefault()
|
|
150
218
|
const menuItem = this.findMenuItemFromElement(focusedElement)
|
|
151
219
|
if (menuItem && menuItem.type === "dropdown") {
|
|
152
220
|
if (this.openedMenu !== false) {
|
|
153
|
-
this.model.content[this.openedMenu].open = false
|
|
221
|
+
;(this.model.content[this.openedMenu] as OverviewMenuDropdownItem).open = false
|
|
154
222
|
}
|
|
155
|
-
menuItem.open = true
|
|
223
|
+
;(menuItem as OverviewMenuDropdownItem).open = true
|
|
156
224
|
this.openedMenu = this.model.content.indexOf(menuItem)
|
|
157
|
-
menuItem.selectedIndex = 0
|
|
225
|
+
;(menuItem as OverviewMenuDropdownItem).selectedIndex = 0
|
|
158
226
|
this.update()
|
|
159
227
|
|
|
160
|
-
const firstDropdownItem = this.menuEl
|
|
228
|
+
const firstDropdownItem = this.menuEl!.querySelector(
|
|
161
229
|
`.fw-pulldown-item.selected`
|
|
162
230
|
)
|
|
163
231
|
if (firstDropdownItem) {
|
|
164
|
-
firstDropdownItem.focus()
|
|
232
|
+
(firstDropdownItem as HTMLElement).focus()
|
|
165
233
|
}
|
|
166
234
|
}
|
|
167
235
|
return
|
|
@@ -172,62 +240,63 @@ export class OverviewMenuView {
|
|
|
172
240
|
const menuItem = this.model.content[this.openedMenu]
|
|
173
241
|
|
|
174
242
|
if (menuItem.type === "dropdown") {
|
|
243
|
+
const dropdownItem = menuItem as OverviewMenuDropdownItem
|
|
175
244
|
if (name === "ArrowDown" || name === "ArrowUp") {
|
|
176
245
|
event.preventDefault()
|
|
177
246
|
event.stopPropagation()
|
|
178
247
|
|
|
179
248
|
// Find currently selected item
|
|
180
249
|
let selectedIndex = -1
|
|
181
|
-
if (
|
|
182
|
-
selectedIndex =
|
|
250
|
+
if (dropdownItem.selectedIndex !== undefined) {
|
|
251
|
+
selectedIndex = dropdownItem.selectedIndex
|
|
183
252
|
}
|
|
184
253
|
|
|
185
254
|
// Calculate new index
|
|
186
255
|
if (name === "ArrowDown") {
|
|
187
256
|
selectedIndex =
|
|
188
|
-
selectedIndex <
|
|
257
|
+
selectedIndex < dropdownItem.content.length - 1
|
|
189
258
|
? selectedIndex + 1
|
|
190
259
|
: 0
|
|
191
260
|
} else {
|
|
192
261
|
selectedIndex -= 1
|
|
193
262
|
if (selectedIndex < 0) {
|
|
194
263
|
// Close menu
|
|
195
|
-
|
|
264
|
+
dropdownItem.open = false
|
|
196
265
|
this.openedMenu = false
|
|
197
|
-
delete
|
|
266
|
+
delete dropdownItem.selectedIndex
|
|
198
267
|
this.update()
|
|
199
|
-
const dropdownButton = this.menuEl
|
|
268
|
+
const dropdownButton = this.menuEl!.querySelector(
|
|
200
269
|
`#${menuItem.id}-button`
|
|
201
270
|
)
|
|
202
271
|
if (dropdownButton) {
|
|
203
|
-
dropdownButton.focus()
|
|
272
|
+
(dropdownButton as HTMLElement).focus()
|
|
204
273
|
}
|
|
205
274
|
}
|
|
206
275
|
}
|
|
207
276
|
|
|
208
|
-
|
|
277
|
+
dropdownItem.selectedIndex = selectedIndex
|
|
209
278
|
this.update()
|
|
210
|
-
const selectedEl = this.menuEl
|
|
279
|
+
const selectedEl = this.menuEl!.querySelector(
|
|
211
280
|
`.fw-pulldown-item.selected`
|
|
212
281
|
)
|
|
213
282
|
if (selectedEl) {
|
|
214
|
-
selectedEl.focus()
|
|
283
|
+
(selectedEl as HTMLElement).focus()
|
|
215
284
|
}
|
|
216
285
|
} else if (name === "Enter" || name === " ") {
|
|
217
286
|
event.preventDefault()
|
|
218
287
|
event.stopPropagation()
|
|
219
288
|
|
|
220
289
|
if (
|
|
221
|
-
|
|
222
|
-
|
|
290
|
+
dropdownItem.selectedIndex !== undefined &&
|
|
291
|
+
dropdownItem.content[dropdownItem.selectedIndex]
|
|
223
292
|
) {
|
|
224
293
|
const selectedItem =
|
|
225
|
-
|
|
294
|
+
dropdownItem.content[dropdownItem.selectedIndex]
|
|
226
295
|
if (selectedItem.action) {
|
|
227
296
|
selectedItem.action(this.overview)
|
|
228
|
-
|
|
297
|
+
dropdownItem.open = false
|
|
229
298
|
this.openedMenu = false
|
|
230
|
-
delete
|
|
299
|
+
delete dropdownItem.selectedIndex
|
|
231
300
|
this.update()
|
|
232
301
|
}
|
|
233
302
|
}
|
|
@@ -235,9 +304,9 @@ export class OverviewMenuView {
|
|
|
235
304
|
event.preventDefault()
|
|
236
305
|
event.stopPropagation()
|
|
237
306
|
|
|
238
|
-
|
|
307
|
+
dropdownItem.open = false
|
|
239
308
|
this.openedMenu = false
|
|
240
|
-
delete
|
|
309
|
+
delete dropdownItem.selectedIndex
|
|
241
310
|
this.update()
|
|
242
311
|
const dropdownButton = document.getElementById(
|
|
243
312
|
`${menuItem.id}-button`
|
|
@@ -250,28 +319,28 @@ export class OverviewMenuView {
|
|
|
250
319
|
}
|
|
251
320
|
}
|
|
252
321
|
|
|
253
|
-
onFocus(event) {
|
|
322
|
+
onFocus(event: FocusEvent): void {
|
|
254
323
|
// Ignore if the focus event is triggered by JavaScript
|
|
255
324
|
if (event.isTrusted === false) {
|
|
256
325
|
return
|
|
257
326
|
}
|
|
258
|
-
const target = event.target
|
|
327
|
+
const target = event.target as Element
|
|
259
328
|
if (this.openedMenu !== false) {
|
|
260
329
|
if (target.matches("#fw-overview-menu li .fw-pulldown-item")) {
|
|
261
330
|
const menuItem = this.model.content[this.openedMenu]
|
|
262
331
|
if (menuItem) {
|
|
263
332
|
const itemNumber = Array.from(
|
|
264
|
-
target.parentElement
|
|
265
|
-
).indexOf(target.parentElement)
|
|
266
|
-
menuItem.selectedIndex = itemNumber
|
|
333
|
+
target.parentElement!.parentElement!.children
|
|
334
|
+
).indexOf(target.parentElement as Element)
|
|
335
|
+
;(menuItem as OverviewMenuDropdownItem).selectedIndex = itemNumber
|
|
267
336
|
this.update()
|
|
268
337
|
}
|
|
269
338
|
} else {
|
|
270
339
|
// Close dropdown menu if focus is outside of the dropdown
|
|
271
340
|
const menuItem = this.model.content[this.openedMenu]
|
|
272
341
|
if (menuItem) {
|
|
273
|
-
menuItem.open = false
|
|
274
|
-
delete menuItem.selectedIndex
|
|
342
|
+
;(menuItem as OverviewMenuDropdownItem).open = false
|
|
343
|
+
delete (menuItem as OverviewMenuDropdownItem).selectedIndex
|
|
275
344
|
this.openedMenu = false
|
|
276
345
|
this.update()
|
|
277
346
|
}
|
|
@@ -279,14 +348,14 @@ export class OverviewMenuView {
|
|
|
279
348
|
}
|
|
280
349
|
}
|
|
281
350
|
|
|
282
|
-
findMenuItemFromElement(element) {
|
|
351
|
+
findMenuItemFromElement(element: Element): OverviewMenuItem | null {
|
|
283
352
|
const menuItem = element.closest("li")
|
|
284
353
|
if (!menuItem) {
|
|
285
354
|
return null
|
|
286
355
|
}
|
|
287
356
|
|
|
288
357
|
let menuNumber = 0
|
|
289
|
-
let seekItem = menuItem
|
|
358
|
+
let seekItem: Element | null = menuItem
|
|
290
359
|
while (seekItem.previousElementSibling) {
|
|
291
360
|
menuNumber++
|
|
292
361
|
seekItem = seekItem.previousElementSibling
|
|
@@ -294,59 +363,60 @@ export class OverviewMenuView {
|
|
|
294
363
|
return this.model.content[menuNumber]
|
|
295
364
|
}
|
|
296
365
|
|
|
297
|
-
focusMenuItem(menuItem) {
|
|
298
|
-
const menuEl = this.menuEl
|
|
366
|
+
focusMenuItem(menuItem: OverviewMenuItem): void {
|
|
367
|
+
const menuEl = this.menuEl!.querySelector(`#${menuItem.id}`)
|
|
299
368
|
if (menuEl) {
|
|
300
|
-
menuEl.focus()
|
|
369
|
+
(menuEl as HTMLElement).focus()
|
|
301
370
|
}
|
|
302
371
|
}
|
|
303
372
|
|
|
304
|
-
oninput(event) {
|
|
305
|
-
const target = event.target
|
|
373
|
+
oninput(event: InputEvent): void {
|
|
374
|
+
const target = event.target as Element
|
|
306
375
|
if (target.matches("#fw-overview-menu > li > .fw-button > input")) {
|
|
307
376
|
// A text was entered in a top entry. we find which one.
|
|
308
377
|
let menuNumber = 0
|
|
309
|
-
let seekItem = target.closest("li")
|
|
310
|
-
while (seekItem
|
|
378
|
+
let seekItem: Element | null = target.closest("li")
|
|
379
|
+
while (seekItem?.previousElementSibling) {
|
|
311
380
|
menuNumber++
|
|
312
381
|
seekItem = seekItem.previousElementSibling
|
|
313
382
|
}
|
|
314
383
|
const menuItem = this.model.content[menuNumber]
|
|
315
|
-
if (menuItem.input) {
|
|
316
|
-
menuItem.input(this.overview, target.value)
|
|
317
|
-
target.focus()
|
|
384
|
+
if (menuItem.type === "search" && menuItem.input) {
|
|
385
|
+
menuItem.input(this.overview, (target as HTMLInputElement).value)
|
|
386
|
+
;(target as HTMLElement).focus()
|
|
318
387
|
}
|
|
319
388
|
}
|
|
320
389
|
}
|
|
321
390
|
|
|
322
|
-
onclick(event) {
|
|
323
|
-
const target = event.target
|
|
391
|
+
onclick(event: MouseEvent): void | false | true {
|
|
392
|
+
const target = event.target as Element
|
|
324
393
|
if (
|
|
325
394
|
target.matches("#fw-overview-menu li li, #fw-overview-menu li li *")
|
|
326
395
|
) {
|
|
327
396
|
event.preventDefault()
|
|
328
397
|
let itemNumber = 0
|
|
329
|
-
let seekItem = target.closest("li")
|
|
330
|
-
while (seekItem
|
|
398
|
+
let seekItem: Element | null = target.closest("li")
|
|
399
|
+
while (seekItem?.previousElementSibling) {
|
|
331
400
|
itemNumber++
|
|
332
401
|
seekItem = seekItem.previousElementSibling
|
|
333
402
|
}
|
|
334
403
|
let menuNumber = 0
|
|
335
|
-
seekItem = seekItem
|
|
336
|
-
while (seekItem
|
|
404
|
+
seekItem = seekItem!.parentElement!.parentElement!.parentElement
|
|
405
|
+
while (seekItem?.previousElementSibling) {
|
|
337
406
|
menuNumber++
|
|
338
407
|
seekItem = seekItem.previousElementSibling
|
|
339
408
|
}
|
|
340
|
-
this.model.content[menuNumber]
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
409
|
+
const menuItem = this.model.content[menuNumber]
|
|
410
|
+
if (menuItem.type === "dropdown") {
|
|
411
|
+
menuItem.content[itemNumber].action!(this.overview)
|
|
412
|
+
menuItem.open = false
|
|
344
413
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
414
|
+
if (menuItem.type === "dropdown") {
|
|
415
|
+
menuItem.title =
|
|
416
|
+
menuItem.content[itemNumber].title
|
|
417
|
+
this.openedMenu = false
|
|
418
|
+
this.update()
|
|
419
|
+
}
|
|
350
420
|
}
|
|
351
421
|
return false
|
|
352
422
|
} else if (
|
|
@@ -360,19 +430,23 @@ export class OverviewMenuView {
|
|
|
360
430
|
// A toolbar dropdown menu item was clicked. We just need to
|
|
361
431
|
// find out which one
|
|
362
432
|
let menuNumber = 0
|
|
363
|
-
let seekItem = target.closest("li")
|
|
364
|
-
while (seekItem
|
|
433
|
+
let seekItem: Element | null = target.closest("li")
|
|
434
|
+
while (seekItem?.previousElementSibling) {
|
|
365
435
|
menuNumber++
|
|
366
436
|
seekItem = seekItem.previousElementSibling
|
|
367
437
|
}
|
|
368
|
-
const menuItem = this.model.content[menuNumber]
|
|
438
|
+
const menuItem = this.model.content[menuNumber] as OverviewMenuSelectActionDropdownItem
|
|
369
439
|
|
|
370
440
|
if (menuItem.checked === true) {
|
|
371
441
|
menuItem.checked = false
|
|
372
|
-
menuItem.uncheckAction
|
|
442
|
+
if (menuItem.uncheckAction) {
|
|
443
|
+
menuItem.uncheckAction(this.overview)
|
|
444
|
+
}
|
|
373
445
|
} else {
|
|
374
446
|
menuItem.checked = true
|
|
375
|
-
menuItem.checkAction
|
|
447
|
+
if (menuItem.checkAction) {
|
|
448
|
+
menuItem.checkAction(this.overview)
|
|
449
|
+
}
|
|
376
450
|
}
|
|
377
451
|
return true
|
|
378
452
|
} else if (
|
|
@@ -381,8 +455,8 @@ export class OverviewMenuView {
|
|
|
381
455
|
// A toolbar dropdown menu item was clicked. We just need to
|
|
382
456
|
// find out which one
|
|
383
457
|
let menuNumber = 0
|
|
384
|
-
let seekItem = target.closest("li")
|
|
385
|
-
while (seekItem
|
|
458
|
+
let seekItem: Element | null = target.closest("li")
|
|
459
|
+
while (seekItem?.previousElementSibling) {
|
|
386
460
|
menuNumber++
|
|
387
461
|
seekItem = seekItem.previousElementSibling
|
|
388
462
|
}
|
|
@@ -390,17 +464,17 @@ export class OverviewMenuView {
|
|
|
390
464
|
// if it is a dropdown menu, open it. Otherwise execute an
|
|
391
465
|
// associated action.
|
|
392
466
|
if (
|
|
393
|
-
["dropdown", "select-action-dropdown"].includes(menuItem.type)
|
|
467
|
+
["dropdown", "select-action-dropdown"].includes(menuItem.type || "")
|
|
394
468
|
) {
|
|
395
469
|
event.preventDefault()
|
|
396
470
|
if (this.openedMenu === menuNumber) {
|
|
397
|
-
this.model.content[this.openedMenu].open = false
|
|
471
|
+
;(this.model.content[this.openedMenu] as OverviewMenuDropdownItem).open = false
|
|
398
472
|
this.openedMenu = false
|
|
399
473
|
} else {
|
|
400
474
|
if (this.openedMenu !== false) {
|
|
401
|
-
this.model.content[this.openedMenu].open = false
|
|
475
|
+
;(this.model.content[this.openedMenu] as OverviewMenuDropdownItem).open = false
|
|
402
476
|
}
|
|
403
|
-
menuItem.open = true
|
|
477
|
+
;(menuItem as OverviewMenuDropdownItem).open = true
|
|
404
478
|
this.openedMenu = menuNumber
|
|
405
479
|
}
|
|
406
480
|
this.update()
|
|
@@ -409,20 +483,20 @@ export class OverviewMenuView {
|
|
|
409
483
|
menuItem.action(this.overview)
|
|
410
484
|
this.announceForScreenReader(gettext("Action completed"))
|
|
411
485
|
if (this.openedMenu !== false) {
|
|
412
|
-
this.model.content[this.openedMenu].open = false
|
|
486
|
+
;(this.model.content[this.openedMenu] as OverviewMenuDropdownItem).open = false
|
|
413
487
|
this.openedMenu = false
|
|
414
488
|
}
|
|
415
489
|
this.update()
|
|
416
490
|
}
|
|
417
491
|
return false
|
|
418
492
|
} else if (this.openedMenu !== false) {
|
|
419
|
-
this.model.content[this.openedMenu].open = false
|
|
493
|
+
;(this.model.content[this.openedMenu] as OverviewMenuDropdownItem).open = false
|
|
420
494
|
this.openedMenu = false
|
|
421
495
|
this.update()
|
|
422
496
|
}
|
|
423
497
|
}
|
|
424
498
|
|
|
425
|
-
update() {
|
|
499
|
+
update(): void {
|
|
426
500
|
if (!this.menuEl) {
|
|
427
501
|
// page has not yet been loaded. abort
|
|
428
502
|
return
|
|
@@ -431,7 +505,7 @@ export class OverviewMenuView {
|
|
|
431
505
|
this.dd.apply(this.menuEl, diff)
|
|
432
506
|
}
|
|
433
507
|
|
|
434
|
-
getMenuHTML() {
|
|
508
|
+
getMenuHTML(): string {
|
|
435
509
|
return `<ul id="fw-overview-menu">${this.model.content
|
|
436
510
|
.map(
|
|
437
511
|
menuItem =>
|
|
@@ -443,7 +517,7 @@ export class OverviewMenuView {
|
|
|
443
517
|
}
|
|
444
518
|
|
|
445
519
|
// Underline access keys
|
|
446
|
-
getAccessKeyHTML(title, accessKey) {
|
|
520
|
+
getAccessKeyHTML(title: string, accessKey: string | undefined): string {
|
|
447
521
|
if (!accessKey) {
|
|
448
522
|
return escapeText(title)
|
|
449
523
|
}
|
|
@@ -456,8 +530,8 @@ export class OverviewMenuView {
|
|
|
456
530
|
)}</span>${escapeText(title.substring(index + 1))}`
|
|
457
531
|
}
|
|
458
532
|
|
|
459
|
-
getMenuItemHTML(menuItem) {
|
|
460
|
-
let returnValue
|
|
533
|
+
getMenuItemHTML(menuItem: OverviewMenuItem): string {
|
|
534
|
+
let returnValue: string
|
|
461
535
|
switch (menuItem.type) {
|
|
462
536
|
case "dropdown":
|
|
463
537
|
returnValue = this.getDropdownHTML(menuItem)
|
|
@@ -481,7 +555,7 @@ export class OverviewMenuView {
|
|
|
481
555
|
return returnValue
|
|
482
556
|
}
|
|
483
557
|
|
|
484
|
-
getSelectionActionDropdownHTML(menuItem) {
|
|
558
|
+
getSelectionActionDropdownHTML(menuItem: OverviewMenuSelectActionDropdownItem): string {
|
|
485
559
|
return `
|
|
486
560
|
<div class="select-action fw-button fw-light fw-large">
|
|
487
561
|
<input type="checkbox" ${menuItem.checked ? "checked" : ""}>
|
|
@@ -491,7 +565,7 @@ export class OverviewMenuView {
|
|
|
491
565
|
`
|
|
492
566
|
}
|
|
493
567
|
|
|
494
|
-
getDropdownHTML(menuItem) {
|
|
568
|
+
getDropdownHTML(menuItem: OverviewMenuDropdownItem): string {
|
|
495
569
|
const accessKey = menuItem.keys?.split("-")[1]
|
|
496
570
|
return `
|
|
497
571
|
<div class="dropdown fw-dropdown-menu"
|
|
@@ -518,7 +592,7 @@ export class OverviewMenuView {
|
|
|
518
592
|
`
|
|
519
593
|
}
|
|
520
594
|
|
|
521
|
-
getDropdownListHTML(menuItem) {
|
|
595
|
+
getDropdownListHTML(menuItem: OverviewMenuDropdownItem | OverviewMenuSelectActionDropdownItem): string {
|
|
522
596
|
if (menuItem.open) {
|
|
523
597
|
return `<div class="fw-pulldown fw-left"
|
|
524
598
|
role="menu"
|
|
@@ -535,8 +609,8 @@ export class OverviewMenuView {
|
|
|
535
609
|
}
|
|
536
610
|
}
|
|
537
611
|
|
|
538
|
-
getDropdownOptionHTML(menuOption, index) {
|
|
539
|
-
const menuItem = this.model.content[this.openedMenu]
|
|
612
|
+
getDropdownOptionHTML(menuOption: OverviewMenuDropdownOption, index: number): string {
|
|
613
|
+
const menuItem = this.model.content[this.openedMenu as number] as OverviewMenuDropdownItem
|
|
540
614
|
const isSelected = menuItem.selectedIndex === index
|
|
541
615
|
return `
|
|
542
616
|
<li role="none">
|
|
@@ -548,7 +622,7 @@ export class OverviewMenuView {
|
|
|
548
622
|
`
|
|
549
623
|
}
|
|
550
624
|
|
|
551
|
-
getButtonHTML(menuItem) {
|
|
625
|
+
getButtonHTML(menuItem: OverviewMenuButtonItem): string {
|
|
552
626
|
return `
|
|
553
627
|
<button class="fw-button fw-light fw-large"
|
|
554
628
|
title="${menuItem.title}"
|
|
@@ -559,7 +633,7 @@ export class OverviewMenuView {
|
|
|
559
633
|
</button>`
|
|
560
634
|
}
|
|
561
635
|
|
|
562
|
-
announceForScreenReader(message) {
|
|
636
|
+
announceForScreenReader(message: string): void {
|
|
563
637
|
const announcement = document.createElement("div")
|
|
564
638
|
announcement.setAttribute("aria-live", "polite")
|
|
565
639
|
announcement.classList.add("sr-only") // CSS to visually hide but keep available to screen readers
|
|
@@ -568,35 +642,35 @@ export class OverviewMenuView {
|
|
|
568
642
|
setTimeout(() => announcement.remove(), 1000)
|
|
569
643
|
}
|
|
570
644
|
|
|
571
|
-
getTextHTML(menuItem) {
|
|
645
|
+
getTextHTML(menuItem: OverviewMenuTextItem): string {
|
|
572
646
|
const accessKey = menuItem.keys?.split("-")[1]
|
|
573
647
|
return `
|
|
574
648
|
<button class="fw-text-menu"
|
|
575
649
|
title="${menuItem.title}${menuItem.keys ? ` (${menuItem.keys})` : ""}"
|
|
576
650
|
>
|
|
577
|
-
${this.getAccessKeyHTML(menuItem.title, accessKey)}
|
|
651
|
+
${this.getAccessKeyHTML(menuItem.title || "", accessKey)}
|
|
578
652
|
</button>`
|
|
579
653
|
}
|
|
580
654
|
|
|
581
|
-
getSearchHTML(menuItem) {
|
|
655
|
+
getSearchHTML(menuItem: OverviewMenuSearchItem): string {
|
|
582
656
|
const accessKey = menuItem.keys?.split("-")[1]
|
|
583
657
|
return `
|
|
584
658
|
<div class="fw-button fw-light fw-large disabled fw-search-field-container">
|
|
585
659
|
<label for="${menuItem.id}-input" class="fw-search-label">
|
|
586
|
-
${this.getAccessKeyHTML(menuItem.title, accessKey)}
|
|
660
|
+
${this.getAccessKeyHTML(menuItem.title || "", accessKey)}
|
|
587
661
|
</label>
|
|
588
662
|
<input type="search"
|
|
589
663
|
class="fw-search-field"
|
|
590
664
|
id="${menuItem.id}-input"
|
|
591
665
|
aria-description="${gettext("Type to search")}"
|
|
592
|
-
placeholder="${menuItem.
|
|
666
|
+
placeholder="${menuItem.placeholder || ""}"
|
|
593
667
|
aria-label="${menuItem.title}"
|
|
594
668
|
>
|
|
595
669
|
${menuItem.icon ? `<i class="fa-solid fa-${menuItem.icon}" aria-hidden="true"></i>` : ""}
|
|
596
670
|
</div>`
|
|
597
671
|
}
|
|
598
672
|
|
|
599
|
-
destroy() {
|
|
673
|
+
destroy(): void {
|
|
600
674
|
// Remove all event listeners
|
|
601
675
|
document.body.removeEventListener("click", this.listeners.onclick)
|
|
602
676
|
document.body.removeEventListener("input", this.listeners.oninput)
|