fwtoolkit 0.1.0-alpha.7 → 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.
Files changed (107) hide show
  1. package/dist/basic.d.ts +49 -36
  2. package/dist/basic.d.ts.map +1 -1
  3. package/dist/basic.js +58 -39
  4. package/dist/basic.js.map +1 -1
  5. package/dist/blob.d.ts +1 -1
  6. package/dist/blob.d.ts.map +1 -1
  7. package/dist/blob.js +0 -1
  8. package/dist/blob.js.map +1 -1
  9. package/dist/content_menu.d.ts +63 -20
  10. package/dist/content_menu.d.ts.map +1 -1
  11. package/dist/content_menu.js +21 -18
  12. package/dist/content_menu.js.map +1 -1
  13. package/dist/datatable_bulk.d.ts +34 -6
  14. package/dist/datatable_bulk.d.ts.map +1 -1
  15. package/dist/datatable_bulk.js +4 -5
  16. package/dist/datatable_bulk.js.map +1 -1
  17. package/dist/dialog.d.ts +82 -7
  18. package/dist/dialog.d.ts.map +1 -1
  19. package/dist/dialog.js +21 -16
  20. package/dist/dialog.js.map +1 -1
  21. package/dist/events.d.ts +1 -1
  22. package/dist/events.d.ts.map +1 -1
  23. package/dist/events.js +3 -3
  24. package/dist/events.js.map +1 -1
  25. package/dist/faq_dialog.d.ts +13 -4
  26. package/dist/faq_dialog.d.ts.map +1 -1
  27. package/dist/faq_dialog.js +4 -2
  28. package/dist/faq_dialog.js.map +1 -1
  29. package/dist/file/dialog.d.ts +33 -13
  30. package/dist/file/dialog.d.ts.map +1 -1
  31. package/dist/file/dialog.js +6 -8
  32. package/dist/file/dialog.js.map +1 -1
  33. package/dist/file/index.d.ts.map +1 -1
  34. package/dist/file/index.js +0 -1
  35. package/dist/file/index.js.map +1 -1
  36. package/dist/file/new_folder_dialog.d.ts +5 -2
  37. package/dist/file/new_folder_dialog.d.ts.map +1 -1
  38. package/dist/file/new_folder_dialog.js +1 -2
  39. package/dist/file/new_folder_dialog.js.map +1 -1
  40. package/dist/file/selector.d.ts +47 -14
  41. package/dist/file/selector.d.ts.map +1 -1
  42. package/dist/file/selector.js +11 -9
  43. package/dist/file/selector.js.map +1 -1
  44. package/dist/file/templates.d.ts +1 -1
  45. package/dist/file/templates.d.ts.map +1 -1
  46. package/dist/file/templates.js +0 -1
  47. package/dist/file/templates.js.map +1 -1
  48. package/dist/file/tools.d.ts +4 -4
  49. package/dist/file/tools.d.ts.map +1 -1
  50. package/dist/file/tools.js +4 -4
  51. package/dist/file/tools.js.map +1 -1
  52. package/dist/focus.d.ts +1 -1
  53. package/dist/focus.d.ts.map +1 -1
  54. package/dist/focus.js +5 -5
  55. package/dist/focus.js.map +1 -1
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +0 -1
  58. package/dist/index.js.map +1 -1
  59. package/dist/network.d.ts +19 -7
  60. package/dist/network.d.ts.map +1 -1
  61. package/dist/network.js +15 -12
  62. package/dist/network.js.map +1 -1
  63. package/dist/overview_menu.d.ts +77 -18
  64. package/dist/overview_menu.d.ts.map +1 -1
  65. package/dist/overview_menu.js +53 -34
  66. package/dist/overview_menu.js.map +1 -1
  67. package/dist/settings.d.ts +7 -2
  68. package/dist/settings.d.ts.map +1 -1
  69. package/dist/settings.js +0 -1
  70. package/dist/settings.js.map +1 -1
  71. package/dist/user.d.ts +8 -2
  72. package/dist/user.d.ts.map +1 -1
  73. package/dist/user.js +1 -2
  74. package/dist/user.js.map +1 -1
  75. package/dist/worker.d.ts +1 -1
  76. package/dist/worker.d.ts.map +1 -1
  77. package/dist/worker.js +1 -2
  78. package/dist/worker.js.map +1 -1
  79. package/dist/ws.d.ts +59 -25
  80. package/dist/ws.d.ts.map +1 -1
  81. package/dist/ws.js +19 -15
  82. package/dist/ws.js.map +1 -1
  83. package/package.json +1 -1
  84. package/src/basic.ts +136 -69
  85. package/src/blob.ts +1 -2
  86. package/src/content_menu.ts +125 -42
  87. package/src/datatable_bulk.ts +72 -35
  88. package/src/dialog.ts +156 -61
  89. package/src/diff-dom.d.ts +16 -0
  90. package/src/events.ts +3 -3
  91. package/src/faq_dialog.ts +25 -11
  92. package/src/file/dialog.ts +48 -14
  93. package/src/file/index.ts +0 -1
  94. package/src/file/new_folder_dialog.ts +7 -5
  95. package/src/file/selector.ts +86 -36
  96. package/src/file/templates.ts +2 -3
  97. package/src/file/tools.ts +17 -8
  98. package/src/focus.ts +11 -13
  99. package/src/global.d.ts +11 -4
  100. package/src/index.ts +0 -1
  101. package/src/network.ts +58 -20
  102. package/src/overview_menu.ts +182 -108
  103. package/src/settings.ts +9 -4
  104. package/src/user.ts +10 -5
  105. package/src/w3c-keyname.d.ts +3 -0
  106. package/src/worker.ts +1 -2
  107. package/src/ws.ts +115 -50
package/src/basic.ts CHANGED
@@ -1,23 +1,41 @@
1
- // @ts-nocheck
2
1
  import {keyName} from "w3c-keyname"
3
2
 
4
- import {ContentMenu} from "./content_menu.js"
3
+ import {ContentMenu, ContentMenuInit} from "./content_menu.js"
5
4
  import {Dialog} from "./dialog.js"
6
5
  import {isActivationEvent} from "./events.js"
7
6
 
7
+ export interface DropdownSelectOptions {
8
+ onChange?: (value: string | false) => void
9
+ width?: number | string | false
10
+ value?: string | false
11
+ button?: HTMLElement | false
12
+ }
13
+
14
+ export interface DropdownSelectAPI {
15
+ setValue: (newValue: string | false) => void
16
+ getValue: () => string | false
17
+ enable: () => void
18
+ disable: () => void
19
+ }
20
+
8
21
  /** Creates a styled select with a contentmenu from a select tag.
9
22
  * @param select The select-tag which is to be replaced.
10
23
  * @param options
11
24
  */
12
25
 
13
26
  export const dropdownSelect = (
14
- selectDOM,
15
- {onChange = _newValue => {}, width = false, value = false, button = false}
16
- ) => {
27
+ selectDOM: HTMLSelectElement,
28
+ {
29
+ onChange = () => {},
30
+ width = false,
31
+ value = false,
32
+ button = false
33
+ }: DropdownSelectOptions = {}
34
+ ): DropdownSelectAPI | undefined => {
17
35
  if (!selectDOM.parentElement) {
18
36
  return
19
37
  }
20
- let buttonDOM
38
+ let buttonDOM: HTMLElement
21
39
  if (button) {
22
40
  buttonDOM = button
23
41
  selectDOM.parentElement.removeChild(selectDOM) // Remove the <select> from the main dom.
@@ -32,9 +50,9 @@ export const dropdownSelect = (
32
50
  "fw-dropdown"
33
51
  )
34
52
  if (width) {
35
- buttonDOM.style.width = Number.isInteger(width)
53
+ buttonDOM.style.width = Number.isInteger(width as number)
36
54
  ? `${width}px`
37
- : width
55
+ : (width as string)
38
56
  }
39
57
  selectDOM.classList.forEach(className =>
40
58
  buttonDOM.classList.add(className)
@@ -55,46 +73,51 @@ export const dropdownSelect = (
55
73
  // There are no options, so we only create the button.
56
74
  return {
57
75
  setValue: () => {},
58
- getValue: () => false
76
+ getValue: () => false,
77
+ enable: () => {},
78
+ disable: () => {}
59
79
  }
60
80
  }
61
- let selected
62
- const menu = {
81
+ let selected: HTMLOptionElement | undefined
82
+ const menu: ContentMenuInit = {
63
83
  content: options.map((option, order) => {
64
- if (option.selected || option.value === value) {
65
- selected = option
84
+ const optionEl = option as HTMLOptionElement
85
+ if (optionEl.selected || optionEl.value === value) {
86
+ selected = optionEl
66
87
  }
67
88
  return {
68
- title: option.innerHTML,
89
+ title: optionEl.innerHTML,
69
90
  type: "action",
70
- tooltip: option.title || "",
91
+ tooltip: optionEl.title || "",
71
92
  order,
72
93
  action: () => {
73
94
  if (!button) {
74
- buttonDOM.firstElementChild.innerText = option.innerText
95
+ ;(buttonDOM.firstElementChild as HTMLElement).innerText =
96
+ optionEl.innerText
75
97
  }
76
- value = option.value || option.dataset.value
98
+ value = optionEl.value || optionEl.dataset.value || false
77
99
  onChange(value)
78
100
  menu.content.forEach(item => (item.selected = false))
79
101
  menu.content[order].selected = true
80
102
  return false
81
103
  },
82
- selected: !!(option.selected || option.dataset.selected)
104
+ selected: !!(optionEl.selected || optionEl.dataset.selected)
83
105
  }
84
106
  })
85
107
  }
86
108
  if (!selected && !button) {
87
- selected = selectDOM.firstElementChild
109
+ selected = selectDOM.firstElementChild as HTMLOptionElement
88
110
  menu.content[0].selected = true
89
111
  }
90
112
 
91
113
  if (!button) {
92
- buttonDOM.firstElementChild.innerText = selected.innerText
114
+ ;(buttonDOM.firstElementChild as HTMLElement).innerText =
115
+ selected ? selected.innerText : ""
93
116
  }
94
117
 
95
118
  value = selected ? selected.value : false
96
119
 
97
- const openMenu = event => {
120
+ const openMenu = (event: Event) => {
98
121
  if (!isActivationEvent(event)) {
99
122
  return
100
123
  }
@@ -105,9 +128,10 @@ export const dropdownSelect = (
105
128
  return
106
129
  }
107
130
  // Determine menu position
108
- let menuPos
131
+ let menuPos: {X: number; Y: number}
109
132
  if (event.type === "click") {
110
- menuPos = {X: event.pageX, Y: event.pageY}
133
+ const mouseEvent = event as MouseEvent
134
+ menuPos = {X: mouseEvent.pageX, Y: mouseEvent.pageY}
111
135
  } else {
112
136
  // Keyboard event
113
137
  const rect = buttonDOM.getBoundingClientRect()
@@ -132,16 +156,17 @@ export const dropdownSelect = (
132
156
  return {
133
157
  setValue: newValue => {
134
158
  const optionIndex = options.findIndex(
135
- option => option.value === newValue
136
- )
159
+ option => (option as HTMLOptionElement).value === newValue
160
+ ) as number | undefined
137
161
  if (optionIndex === undefined) {
138
162
  return
139
163
  }
140
164
  menu.content.forEach(item => (item.selected = false))
141
165
  menu.content[optionIndex].selected = true
142
- const option = options[optionIndex]
166
+ const option = options[optionIndex] as HTMLOptionElement
143
167
  if (!button) {
144
- buttonDOM.firstElementChild.innerText = option.innerText
168
+ ;(buttonDOM.firstElementChild as HTMLElement).innerText =
169
+ option.innerText
145
170
  }
146
171
  value = newValue
147
172
  },
@@ -154,7 +179,7 @@ export const dropdownSelect = (
154
179
  /** Checks or unchecks a checkable label. This is used for example for bibliography categories when editing bibliography items.
155
180
  * @param label The node who's parent has to be checked or unchecked.
156
181
  */
157
- export const setCheckableLabel = labelEl => {
182
+ export const setCheckableLabel = (labelEl: HTMLElement): void => {
158
183
  if (labelEl.classList.contains("checked")) {
159
184
  labelEl.classList.remove("checked")
160
185
  } else {
@@ -162,49 +187,51 @@ export const setCheckableLabel = labelEl => {
162
187
  }
163
188
  }
164
189
 
165
- let messageWaiter = false
190
+ let messageWaiter: number | false = false
166
191
  let waitMessage = ""
167
192
  /** Cover the page signaling to the user to wait.
168
193
  */
169
- export const activateWait = (full = false, message = "") => {
194
+ export const activateWait = (full = false, message = ""): void => {
170
195
  const waitEl = document.getElementById("wait")
196
+ if (!waitEl) {
197
+ return
198
+ }
171
199
  if (message) {
172
200
  let messageEl = waitEl.querySelector("span.message")
173
201
  if (messageEl) {
174
202
  // Another message is already showing. We update directly.
175
- messageEl.innerText = message
203
+ ;(messageEl as HTMLElement).innerText = message
176
204
  } else {
177
205
  waitMessage = message // We update the message if there is one waiting already
178
206
  if (!messageWaiter) {
179
- messageWaiter = setTimeout(() => {
207
+ messageWaiter = window.setTimeout(() => {
180
208
  messageEl = document.createElement("span")
181
209
  messageEl.classList.add("message")
182
- messageEl.innerText = waitMessage
210
+ ;(messageEl as HTMLElement).innerText = waitMessage
183
211
  waitEl.appendChild(messageEl)
184
212
  messageWaiter = false
185
213
  }, 2000)
186
214
  }
187
215
  }
188
216
  }
189
- if (waitEl) {
190
- waitEl.classList.add("active")
191
- if (full) {
192
- waitEl.classList.add("full")
193
- }
217
+ waitEl.classList.add("active")
218
+ if (full) {
219
+ waitEl.classList.add("full")
194
220
  }
195
221
  }
196
222
 
197
223
  /** Remove the wait cover.
198
224
  */
199
- export const deactivateWait = () => {
225
+ export const deactivateWait = (): void => {
200
226
  const waitEl = document.getElementById("wait")
201
- if (waitEl) {
202
- waitEl.classList.remove("active")
203
- waitEl.classList.remove("full")
227
+ if (!waitEl) {
228
+ return
204
229
  }
230
+ waitEl.classList.remove("active")
231
+ waitEl.classList.remove("full")
205
232
  const messageEl = waitEl.querySelector("span.message")
206
233
  if (messageEl) {
207
- messageEl.parentElement.removeChild(messageEl)
234
+ messageEl.parentElement?.removeChild(messageEl)
208
235
  }
209
236
  if (messageWaiter) {
210
237
  clearTimeout(messageWaiter)
@@ -216,11 +243,14 @@ export const deactivateWait = () => {
216
243
  * @param alertType The type of message that is shown (error, warning, info or success).
217
244
  * @param alertMsg The message text.
218
245
  */
219
- export const addAlert = (alertType, alertMsg) => {
246
+ export const addAlert = (
247
+ alertType: "error" | "warning" | "info" | "success",
248
+ alertMsg: string
249
+ ): void => {
220
250
  if (!document.body) {
221
251
  return
222
252
  }
223
- const iconNames = {
253
+ const iconNames: Record<typeof alertType, string> = {
224
254
  error: "circle-exclamation",
225
255
  warning: "circle-exclamation",
226
256
  info: "circle-info",
@@ -233,11 +263,17 @@ export const addAlert = (alertType, alertMsg) => {
233
263
  )
234
264
  }
235
265
  const alertsWrapper = document.getElementById("alerts-wrapper")
266
+ if (!alertsWrapper) {
267
+ return
268
+ }
236
269
  alertsWrapper.insertAdjacentHTML(
237
270
  "beforeend",
238
271
  `<li class="alerts-${alertType} fa-before fa-${iconNames[alertType]}">${alertMsg}</li>`
239
272
  )
240
273
  const alertBox = alertsWrapper.lastElementChild
274
+ if (!alertBox) {
275
+ return
276
+ }
241
277
  setTimeout(() => {
242
278
  alertBox.classList.add("visible")
243
279
  setTimeout(() => {
@@ -247,8 +283,20 @@ export const addAlert = (alertType, alertMsg) => {
247
283
  }, 1)
248
284
  }
249
285
 
250
- // Used for system mesages
251
- export const showSystemMessage = (message, buttons = [{type: "close"}]) => {
286
+ export interface DialogButtonSpec {
287
+ type?: "close" | "cancel" | "ok"
288
+ text?: string
289
+ classes?: string
290
+ click?: (event?: Event) => void
291
+ icon?: string
292
+ dropdown?: boolean
293
+ }
294
+
295
+ // Used for system messages
296
+ export const showSystemMessage = (
297
+ message: string,
298
+ buttons: DialogButtonSpec[] = [{type: "close"}]
299
+ ): Dialog => {
252
300
  const dialog = new Dialog({
253
301
  title: gettext("System message"),
254
302
  body: `<p>${escapeText(message)}</p>`,
@@ -259,22 +307,27 @@ export const showSystemMessage = (message, buttons = [{type: "close"}]) => {
259
307
  }
260
308
 
261
309
  /** Turn milliseconds since epoch (UTC) into a local date string.
262
- * @param {number} milliseconds Number of milliseconds since epoch (1/1/1970 midnight, UTC).
263
- * @param {boolean} type 'full' for full date (default), 'sortable-date' for sortable date, 'minutes' for minute accuracy
310
+ * @param milliseconds Number of milliseconds since epoch (1/1/1970 midnight, UTC).
311
+ * @param type 'full' for full date (default), 'sortable-date' for sortable date, 'minutes' for minute accuracy
264
312
  */
265
- const CACHED_DATES = {
313
+ type DateType = "sortable-date" | "minutes" | "full"
314
+
315
+ const CACHED_DATES: Record<DateType, Record<number, string>> = {
266
316
  "sortable-date": {},
267
317
  minutes: {},
268
318
  full: {}
269
319
  }
270
- export const localizeDate = (milliseconds, type = "full") => {
320
+ export const localizeDate = (
321
+ milliseconds: number,
322
+ type: DateType = "full"
323
+ ): string => {
271
324
  if (milliseconds === 0) {
272
325
  return ""
273
326
  } else if (CACHED_DATES[type][milliseconds]) {
274
327
  return CACHED_DATES[type][milliseconds]
275
328
  }
276
329
  const theDate = new Date(milliseconds)
277
- let returnValue
330
+ let returnValue: string
278
331
  switch (type) {
279
332
  case "sortable-date": {
280
333
  const yyyy = theDate.getFullYear()
@@ -306,7 +359,10 @@ export const localizeDate = (milliseconds, type = "full") => {
306
359
  * Turn string literals into single line, removing spaces at start of line
307
360
  */
308
361
 
309
- export const noSpaceTmp = (strings, ...values) => {
362
+ export const noSpaceTmp = (
363
+ strings: TemplateStringsArray,
364
+ ...values: unknown[]
365
+ ): string => {
310
366
  const tmpStrings = Array.from(strings)
311
367
 
312
368
  let combined = ""
@@ -315,7 +371,8 @@ export const noSpaceTmp = (strings, ...values) => {
315
371
  combined += tmpStrings.shift()
316
372
  }
317
373
  if (values.length > 0) {
318
- combined += values.shift()
374
+ const value = values.shift()
375
+ combined += value !== undefined && value !== null ? String(value) : ""
319
376
  }
320
377
  }
321
378
 
@@ -326,7 +383,7 @@ export const noSpaceTmp = (strings, ...values) => {
326
383
  return out
327
384
  }
328
385
 
329
- export const escapeText = text => {
386
+ export const escapeText = (text: string): string => {
330
387
  return text
331
388
  .replace(/&/g, "&amp;")
332
389
  .replace(/</g, "&lt;")
@@ -340,13 +397,13 @@ export const escapeText = text => {
340
397
  * Return an inline info-icon with a hover tooltip containing the given HTML.
341
398
  * Use only with trusted HTML content.
342
399
  *
343
- * @param {string} html - The tooltip content (HTML string)
344
- * @returns {string} HTML for the info tooltip
400
+ * @param html - The tooltip content (HTML string)
401
+ * @returns HTML for the info tooltip
345
402
  */
346
- export const infoTooltip = html =>
403
+ export const infoTooltip = (html: string): string =>
347
404
  `<span class="fw-info-tooltip"><i class="fa-solid fa-info-circle"></i><span class="fw-info-tooltip-text">${html}</span></span>`
348
405
 
349
- export const unescapeText = text =>
406
+ export const unescapeText = (text: string): string =>
350
407
  text
351
408
  .replace(/&lt;/g, "<")
352
409
  .replace(/&gt;/g, ">")
@@ -357,12 +414,16 @@ export const unescapeText = text =>
357
414
  * ES6 promises are not (yet) cancelable.
358
415
  */
359
416
 
360
- export const cancelPromise = () => new Promise(() => {})
417
+ export const cancelPromise = (): Promise<never> => new Promise(() => {})
361
418
 
362
419
  // Check if selector matches one of the ancestors of the event target.
363
420
  // Used in switch statements of document event listeners.
364
- export const findTarget = (event, selector, el = {}) => {
365
- el.target = event.target.closest(selector)
421
+ export const findTarget = (
422
+ event: Event,
423
+ selector: string,
424
+ el: {target?: Element | null}
425
+ ): boolean => {
426
+ el.target = (event.target as Element).closest(selector)
366
427
  if (el.target) {
367
428
  event.stopPropagation()
368
429
  return true
@@ -371,7 +432,7 @@ export const findTarget = (event, selector, el = {}) => {
371
432
  }
372
433
 
373
434
  // Promise when page has been loaded.
374
- export const whenReady = () => {
435
+ export const whenReady = (): Promise<void> => {
375
436
  if (document.readyState === "complete") {
376
437
  return Promise.resolve()
377
438
  } else {
@@ -385,14 +446,14 @@ export const whenReady = () => {
385
446
  }
386
447
  }
387
448
 
388
- export const setDocTitle = (title, app) => {
449
+ export const setDocTitle = (title: string, app: {name: string}): void => {
389
450
  const titleText = `${title} - ${app.name}`
390
451
  if (document.title !== titleText) {
391
452
  document.title = titleText
392
453
  }
393
454
  }
394
455
 
395
- const LANGUAGES = {
456
+ const LANGUAGES: Record<string, string> = {
396
457
  ar: "العربية",
397
458
  bg: "Български",
398
459
  cs: "Čeština",
@@ -415,16 +476,19 @@ const LANGUAGES = {
415
476
  "zh-hans": "简体中文"
416
477
  }
417
478
 
418
- export const langName = code => {
479
+ export const langName = (code: string): string => {
419
480
  return LANGUAGES[code] || code
420
481
  }
421
482
 
422
483
  /** Enable ISO date picker on a text input by overlaying a native date picker.
423
484
  * The text input displays ISO format (YYYY-MM-DD) while using the native picker for selection.
424
- * @param {HTMLInputElement} inputEl - The text input element to enhance
425
- * @param {boolean} minToday - If true, sets min date to today (default: false)
485
+ * @param inputEl - The text input element to enhance
486
+ * @param minToday - If true, sets min date to today (default: false)
426
487
  */
427
- export const enableDatePicker = (inputEl, minToday = false) => {
488
+ export const enableDatePicker = (
489
+ inputEl: HTMLInputElement,
490
+ minToday = false
491
+ ): void => {
428
492
  const datePicker = document.createElement("input")
429
493
  datePicker.type = "date"
430
494
  if (minToday) {
@@ -435,6 +499,9 @@ export const enableDatePicker = (inputEl, minToday = false) => {
435
499
  datePicker.style.pointerEvents = "none"
436
500
 
437
501
  const parent = inputEl.parentElement
502
+ if (!parent) {
503
+ return
504
+ }
438
505
  parent.style.position = "relative"
439
506
  parent.appendChild(datePicker)
440
507
 
package/src/blob.ts CHANGED
@@ -1,5 +1,4 @@
1
- // @ts-nocheck
2
- export function convertDataURIToBlob(dataURI) {
1
+ export function convertDataURIToBlob(dataURI: string): Blob {
3
2
  const byteString = atob(dataURI.split(",")[1])
4
3
  const mimeString = dataURI.split(",")[0].split(":")[1].split(";")[0]
5
4
  const ab = new ArrayBuffer(byteString.length)