react-hook-core 0.4.23 → 0.5.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/src/search.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { ChangeEvent } from "react"
2
2
  import { resources, StringMap } from "./core"
3
3
  import { clone } from "./reflect"
4
+ import { updateState } from "./state"
4
5
 
5
6
  export interface PageChange {
6
7
  page: number // currentPage
@@ -352,14 +353,16 @@ export function buildSortFilter<S extends Filter>(obj: S, sortable: Sortable): S
352
353
  const filter: any = clone(obj)
353
354
  if (sortable.sortField && sortable.sortField.length > 0) {
354
355
  filter.sort = sortable.sortType === "-" ? "-" + sortable.sortField : sortable.sortField
356
+ obj.sort = filter.sort
355
357
  } else {
356
358
  delete filter.sort
359
+ delete obj.sort
357
360
  }
358
361
  delete filter.fields
359
362
  return filter
360
363
  }
361
- export function handleToggle(target?: HTMLElement, on?: boolean): boolean {
362
- const off = !on
364
+ export function handleToggle(target?: HTMLElement, off?: boolean): boolean {
365
+ const on = !off
363
366
  if (target) {
364
367
  if (on) {
365
368
  if (!target.classList.contains("on")) {
@@ -369,43 +372,92 @@ export function handleToggle(target?: HTMLElement, on?: boolean): boolean {
369
372
  target.classList.remove("on")
370
373
  }
371
374
  }
372
- return off
375
+ return on
376
+ }
377
+ export function getNumber(e: ChangeEvent<HTMLSelectElement | HTMLInputElement>): number {
378
+ return parseInt(e.currentTarget.value, 10)
373
379
  }
374
380
 
375
- export function getNumber(event: ChangeEvent<HTMLSelectElement | HTMLInputElement>): number {
376
- return parseInt(event.currentTarget.value, 10)
381
+ export function setSortFilter<F extends Filter, T extends Sortable>(
382
+ state: T,
383
+ filter: F,
384
+ setState?: (v: React.SetStateAction<T>) => void,
385
+ setFilter?: (v: React.SetStateAction<F>) => void,
386
+ search?: (first?: boolean) => void,
387
+ ) {
388
+ setSort(state, filter.sort)
389
+ if (setFilter) {
390
+ setFilter(filter)
391
+ }
392
+ if (setState) {
393
+ setState(state)
394
+ }
395
+ if (search) {
396
+ search(true)
397
+ }
398
+ }
399
+ export function onToggleSearch(
400
+ e: React.MouseEvent<HTMLButtonElement, MouseEvent>,
401
+ showFilter: boolean,
402
+ setShowFilter?: (v: React.SetStateAction<boolean>) => void,
403
+ ) {
404
+ const toggleFilter = handleToggle(e.target as HTMLElement, showFilter)
405
+ if (setShowFilter) {
406
+ setShowFilter(toggleFilter)
407
+ }
408
+ }
409
+ export function resetSearch<T extends Filter>(
410
+ e: ChangeEvent<HTMLInputElement, HTMLInputElement>,
411
+ filter: T,
412
+ setFilter: (v: React.SetStateAction<T>) => void,
413
+ search?: () => void,
414
+ ) {
415
+ filter.page = 1
416
+ updateState(e, filter, setFilter)
417
+ if (search) {
418
+ search()
419
+ }
420
+ }
421
+ export function onClearQ<T extends Filter>(filter: T, setFilter?: (v: React.SetStateAction<T>) => void, search?: () => void) {
422
+ filter.q = ""
423
+ if (setFilter) {
424
+ setFilter({ ...filter })
425
+ }
426
+ if (search) {
427
+ search()
428
+ }
377
429
  }
378
430
  export function onPageSizeChanged<T extends Filter>(
379
- event: ChangeEvent<HTMLSelectElement>,
431
+ e: ChangeEvent<HTMLSelectElement>,
380
432
  search: () => void,
381
433
  filter: T,
382
- setState?: (v: React.SetStateAction<T>) => void,
434
+ setFilter?: (v: React.SetStateAction<T>) => void,
383
435
  ) {
384
436
  filter.page = 1
385
- filter.limit = getNumber(event)
386
- if (setState) {
387
- setState(filter)
437
+ filter.limit = getNumber(e)
438
+ if (setFilter) {
439
+ setFilter(filter)
388
440
  }
389
441
  search()
390
442
  }
391
- export function onPageChanged<T extends Filter>(data: PageChange, search: () => void, filter: T, setState?: (v: React.SetStateAction<T>) => void) {
443
+ export function onPageChanged<T extends Filter>(data: PageChange, search: () => void, filter: T, setFilter?: (v: React.SetStateAction<T>) => void) {
392
444
  const { page, size } = data
393
445
  filter.page = page
394
446
  filter.limit = size
395
- if (setState) {
396
- setState(filter)
447
+ if (setFilter) {
448
+ setFilter(filter)
397
449
  }
398
450
  search()
399
451
  }
400
452
  export function onSearch<F extends Filter, T extends Sortable>(
401
- event: React.MouseEvent<HTMLButtonElement, MouseEvent>,
453
+ e: React.MouseEvent<HTMLButtonElement, MouseEvent>,
402
454
  search: () => void,
403
455
  filter: F,
404
456
  state: T,
405
457
  setFilter?: (v: React.SetStateAction<F>) => void,
406
458
  setState?: (v: React.SetStateAction<T>) => void,
407
459
  ): void {
408
- event.preventDefault()
460
+ e.preventDefault()
409
461
  removeSortStatus(state.sortTarget)
410
462
  filter.page = 1
411
463
  state.sortTarget = undefined
@@ -419,12 +471,12 @@ export function onSearch<F extends Filter, T extends Sortable>(
419
471
  search()
420
472
  }
421
473
  export function onSort<T extends Sortable>(
422
- event: React.MouseEvent<HTMLButtonElement, MouseEvent>,
474
+ e: React.MouseEvent<HTMLButtonElement, MouseEvent>,
423
475
  search: () => void,
424
476
  state: T,
425
477
  setState?: (v: React.SetStateAction<T>) => void,
426
478
  ) {
427
- const target = getSortElement(event.target as HTMLElement)
479
+ const target = getSortElement(e.target as HTMLElement)
428
480
  const sort = handleSort(target, state.sortTarget, state.sortField, state.sortType)
429
481
  state.sortField = sort.field
430
482
  state.sortType = sort.type
package/src/state.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ChangeEvent } from "react"
2
- import { Locale } from "./core"
2
+ import { removeFaxFormat, removePhoneFormat } from "./core"
3
3
  import { setValue } from "./reflect"
4
- import { valueOf } from "./util"
5
4
 
6
5
  export function getDecimalSeparator(ele: HTMLInputElement): string {
7
6
  let separator = ele.getAttribute("data-decimal-separator")
@@ -16,45 +15,69 @@ export function getDecimalSeparator(ele: HTMLInputElement): string {
16
15
 
17
16
  const r1 = / |,|\$|€|£|¥|'|٬|،| /g
18
17
  const r2 = / |\.|\$|€|£|¥|'|٬|،| /g
18
+ export function updateNumber<T>(
19
+ e: ChangeEvent<HTMLInputElement>,
20
+ o: T,
21
+ setObj: (v: React.SetStateAction<T>) => void,
22
+ decimalSeparator?: string,
23
+ callback?: () => void,
24
+ formatStr?: (s?: string) => string,
25
+ ) {
26
+ const ctrl = e.target
27
+ const v0: string = formatStr ? formatStr(ctrl.value) : ctrl.value
28
+ const dataField = ctrl.getAttribute("data-field")
29
+ const field = dataField ? dataField : ctrl.name
30
+ let v = decimalSeparator === "," ? v0.replace(r2, "") : v0.replace(r1, "")
31
+ if (v.indexOf(",") >= 0) {
32
+ v = v.replace(",", ".")
33
+ }
34
+ const val = isNaN(v as any) ? undefined : parseInt(v)
35
+ setValue(o, field, val)
36
+ setObj({ ...o })
37
+ if (callback) {
38
+ callback()
39
+ }
40
+ }
41
+ export function formatAndUpdateState<T>(
42
+ e: ChangeEvent<HTMLInputElement> | ChangeEvent<HTMLSelectElement> | ChangeEvent<HTMLTextAreaElement>,
43
+ o: T,
44
+ setObj: (v: React.SetStateAction<T>) => void,
45
+ formatStr?: (s?: string) => string,
46
+ callback?: () => void,
47
+ ) {
48
+ updateState(e, o, setObj, callback, formatStr)
49
+ }
19
50
  export function updateState<T>(
20
- e: ChangeEvent<HTMLInputElement, HTMLInputElement> | ChangeEvent<HTMLTextAreaElement, HTMLTextAreaElement>,
51
+ e: ChangeEvent<HTMLInputElement> | ChangeEvent<HTMLSelectElement> | ChangeEvent<HTMLTextAreaElement>,
21
52
  o: T,
22
53
  setObj: (v: React.SetStateAction<T>) => void,
54
+ callback?: () => void,
55
+ formatStr?: (s?: string) => string,
23
56
  ) {
24
57
  const ctrl = e.target
58
+ const v0: string = formatStr ? formatStr(ctrl.value) : ctrl.value
25
59
  const dataField = ctrl.getAttribute("data-field")
26
60
  const field = dataField ? dataField : ctrl.name
27
61
  if (ctrl.tagName === "SELECT") {
28
62
  if (ctrl.tagName === "SELECT") {
29
- if (ctrl.value === "" || !ctrl.value) {
63
+ if (v0 === "" || !v0) {
30
64
  ctrl.removeAttribute("data-value")
31
65
  } else {
32
- ctrl.setAttribute("data-value", "data-value")
66
+ ctrl.setAttribute("data-value", v0)
33
67
  }
34
68
  }
35
- setValue(o, field, ctrl.value)
69
+ setValue(o, field, v0)
36
70
  } else {
37
71
  let stype = ctrl.getAttribute("type")
38
72
  const type: string = stype ? stype.toLowerCase() : "text"
39
73
  let model: any = o
40
- if (type === "text") {
41
- const datatype = ctrl.getAttribute("data-type")
42
- if (datatype === "number" || datatype === "integer") {
43
- const decimalSeparator = getDecimalSeparator(ctrl as HTMLInputElement)
44
- const v0: string = ctrl.value
45
- const v = decimalSeparator === "," ? v0.replace(r2, "") : v0.replace(r1, "")
46
- const val = isNaN(v as any) ? undefined : parseFloat(v)
47
- setValue(o, field, val)
48
- } else {
49
- setValue(o, field, ctrl.value)
50
- }
51
- } else if (type === "checkbox") {
74
+ if (type === "checkbox") {
52
75
  let value = model[field]
53
76
  if (ctrl.id && ctrl.name !== ctrl.id) {
54
77
  if (!value || !Array.isArray(value)) {
55
78
  value = []
56
79
  }
57
- value.includes(ctrl.value) ? (value = value.filter((v: string) => v !== ctrl.value)) : value.push(ctrl.value)
80
+ value.includes(v0) ? (value = value.filter((v: string) => v !== v0)) : value.push(v0)
58
81
  model[field] = value
59
82
  } else {
60
83
  const v = valueOfCheckbox(ctrl as HTMLInputElement)
@@ -62,197 +85,48 @@ export function updateState<T>(
62
85
  }
63
86
  } else if (type === "radio") {
64
87
  if (field.indexOf(".") < 0 && field.indexOf("[") < 0) {
65
- model[field] = ctrl.value
88
+ model[field] = v0
66
89
  } else {
67
- setValue(model, field, ctrl.value)
90
+ setValue(model, field, v0)
68
91
  }
69
92
  } else if (type === "date" || type === "datetime-local") {
70
- try {
71
- const selectedDate = new Date(ctrl.value)
72
- setValue(model, field, ctrl.value && ctrl.value !== "" ? selectedDate.toISOString() : null)
73
- } catch (error) {
74
- console.error("Error occurred while formatting date:", error)
75
- }
93
+ const date = new Date(v0)
94
+ const val = !isNaN(date.getTime()) ? date.toISOString() : null
95
+ setValue(model, field, val)
76
96
  } else if (type === "time") {
77
- const objSet: any = {}
78
- try {
79
- const selectedDate = new Date(ctrl.value)
80
- setValue(model, field, selectedDate.getTime())
81
- } catch (error) {
82
- console.error("Error occurred while formatting time:", error)
97
+ const date = new Date(v0)
98
+ if (!isNaN(date.getTime())) {
99
+ setValue(model, field, date.getTime())
100
+ } else {
101
+ setValue(model, field, null)
83
102
  }
84
- return objSet
85
103
  } else {
86
- setValue(o, field, ctrl.value)
87
- }
88
- }
89
- setObj({ ...o })
90
- }
91
-
92
- export const enLocale = {
93
- id: "en-US",
94
- countryCode: "US",
95
- dateFormat: "M/d/yyyy",
96
- firstDayOfWeek: 1,
97
- decimalSeparator: ".",
98
- groupSeparator: ",",
99
- decimalDigits: 2,
100
- currencyCode: "USD",
101
- currencySymbol: "$",
102
- currencyPattern: 0,
103
- }
104
- export function localeOf(lc?: Locale, glc?: (() => Locale) | Locale): Locale {
105
- let l: Locale | undefined = lc
106
- if (!l) {
107
- if (glc) {
108
- if (typeof glc === "function") {
109
- l = glc()
104
+ const datatype = ctrl.getAttribute("data-type")
105
+ if (datatype === "phone") {
106
+ const val = removePhoneFormat(v0)
107
+ setValue(o, field, val)
108
+ } else if (datatype === "fax") {
109
+ const val = removeFaxFormat(v0)
110
+ setValue(o, field, val)
111
+ } else if (datatype === "number" || datatype === "int") {
112
+ const decimalSeparator = getDecimalSeparator(ctrl as HTMLInputElement)
113
+ let v = decimalSeparator === "," ? v0.replace(r2, "") : v0.replace(r1, "")
114
+ if (v.indexOf(",") >= 0) {
115
+ v = v.replace(",", ".")
116
+ }
117
+ const val = isNaN(v as any) ? undefined : parseFloat(v)
118
+ setValue(o, field, val)
110
119
  } else {
111
- l = glc
120
+ setValue(o, field, v0)
112
121
  }
113
122
  }
114
- if (!l) {
115
- l = enLocale
116
- }
117
123
  }
118
- return l
119
- }
120
- export function handleEvent(e: any, removeErr?: (ctrl: HTMLInputElement) => void) {
121
- const ctrl = e.currentTarget as HTMLInputElement
122
- const type = ctrl.getAttribute("type")
123
- const isPreventDefault = type && (["checkbox", "radio"].indexOf(type.toLowerCase()) >= 0 ? false : true)
124
- if (isPreventDefault) {
125
- e.preventDefault()
126
- }
127
- if (removeErr && ctrl.nodeName === "SELECT" && ctrl.value && ctrl.classList.contains("invalid")) {
128
- removeErr(ctrl)
124
+ setObj({ ...o })
125
+ if (callback) {
126
+ callback()
129
127
  }
130
128
  }
131
129
 
132
- export interface ModelMap {
133
- [key: string]: any
134
- }
135
- export interface ModelProps {
136
- setGlobalState?: (m: ModelMap) => void
137
- shouldBeCustomized?: boolean
138
- }
139
- export function handleProps<P extends ModelProps>(
140
- e: any,
141
- props: P,
142
- ctrl: HTMLInputElement,
143
- modelName: string,
144
- tloc: Locale,
145
- prepareData?: (data: any) => void,
146
- ) {
147
- if (props.setGlobalState) {
148
- const res = valueOf(ctrl, tloc, e.type)
149
- if (res.mustChange) {
150
- const dataField = ctrl.getAttribute("data-field")
151
- const field = dataField ? dataField : ctrl.name
152
- const propsDataForm = (props as any)[modelName]
153
- const form = ctrl.form
154
- if (form) {
155
- const formName = form.name
156
- if (field.indexOf(".") < 0 && field.indexOf("[") < 0) {
157
- const data = props.shouldBeCustomized && prepareData ? prepareData({ [ctrl.name]: res.value }) : { [ctrl.name]: res.value }
158
- props.setGlobalState({ [formName]: { ...propsDataForm, ...data } })
159
- } else {
160
- setValue(propsDataForm, field, ctrl.value)
161
- props.setGlobalState({ [formName]: { ...propsDataForm } })
162
- }
163
- }
164
- }
165
- }
166
- }
167
- export function buildState<S, K extends keyof S>(e: any, state: Readonly<S>, ctrl: HTMLInputElement, modelName: string, tloc: Locale): K | undefined {
168
- const form = ctrl.form
169
- if (form) {
170
- if (modelName && modelName !== "") {
171
- const type = ctrl.getAttribute("type")
172
- const ex = (state as any)[modelName]
173
- const dataField = ctrl.getAttribute("data-field")
174
- const field = dataField ? dataField : ctrl.name
175
- const model = Object.assign({}, ex)
176
- if (type && type.toLowerCase() === "checkbox") {
177
- let value = model[field]
178
- if (ctrl.id && ctrl.name !== ctrl.id) {
179
- if (!value || !Array.isArray(value)) {
180
- value = []
181
- }
182
- value.includes(ctrl.value) ? (value = value.filter((v: string) => v !== ctrl.value)) : value.push(ctrl.value)
183
- // if (dType == 'array'){
184
- // if (value === 'string'){
185
- // value = [value]
186
- // }
187
- // }
188
- model[field] = value
189
- // console.log(model, modelName, model, model[field], field, value )
190
- // setValue(model, field, value);
191
- } else {
192
- const v = valueOfCheckbox(ctrl)
193
- model[field] = v
194
- }
195
- const objSet: any = {}
196
- objSet[modelName] = model
197
- return objSet
198
- } else if (type && type.toLowerCase() === "radio") {
199
- if (field.indexOf(".") < 0 && field.indexOf("[") < 0) {
200
- model[field] = ctrl.value
201
- } else {
202
- setValue(model, field, ctrl.value)
203
- }
204
- const objSet: any = {}
205
- objSet[modelName] = model
206
- return objSet
207
- } else if (type && (type.toLowerCase() === "date" || type.toLowerCase() === "datetime-local")) {
208
- const objSet: any = {}
209
- try {
210
- const selectedDate = new Date(ctrl.value)
211
- setValue(model, field, ctrl.value && ctrl.value !== "" ? selectedDate.toISOString() : null)
212
- objSet[modelName] = model
213
- return objSet
214
- } catch (error) {
215
- console.error("Error occurred while formatting date:", error)
216
- }
217
- return objSet
218
- } else if (type && type.toLowerCase() === "time") {
219
- const objSet: any = {}
220
- try {
221
- const selectedDate = new Date(ctrl.value)
222
- setValue(model, field, selectedDate.getTime())
223
- objSet[modelName] = model
224
- return objSet
225
- } catch (error) {
226
- console.error("Error occurred while formatting time:", error)
227
- }
228
- return objSet
229
- } else {
230
- if (ctrl.tagName === "SELECT") {
231
- if (ctrl.value === "" || !ctrl.value) {
232
- ctrl.removeAttribute("data-value")
233
- } else {
234
- ctrl.setAttribute("data-value", "data-value")
235
- }
236
- }
237
- const data = valueOf(ctrl, tloc, e.type)
238
- if (data.mustChange) {
239
- if (field.indexOf(".") < 0 && field.indexOf("[") < 0) {
240
- model[field] = data.value
241
- } else {
242
- setValue(model, field, data.value)
243
- }
244
- const objSet: any = {}
245
- objSet[modelName] = model
246
- return objSet
247
- }
248
- }
249
- } else {
250
- return buildFlatState(e, state, tloc)
251
- }
252
- } else {
253
- buildFlatState(e, state, tloc)
254
- }
255
- }
256
130
  export function valueOfCheckbox(ctrl: HTMLInputElement): string | number | boolean {
257
131
  const ctrlOnValue = ctrl.getAttribute("data-on-value")
258
132
  const ctrlOffValue = ctrl.getAttribute("data-off-value")
@@ -264,29 +138,3 @@ export function valueOfCheckbox(ctrl: HTMLInputElement): string | number | boole
264
138
  return ctrl.checked === true
265
139
  }
266
140
  }
267
- export function buildFlatState<S, K extends keyof S>(e: any, state: Readonly<S>, l?: Locale): K | undefined {
268
- const ctrl = e.currentTarget as HTMLInputElement
269
- const stateName = ctrl.name
270
- const objSet: any = {}
271
- const type = ctrl.getAttribute("type")
272
- if (type && type.toLowerCase() === "checkbox") {
273
- if (ctrl.id && stateName === ctrl.id) {
274
- const v = valueOfCheckbox(ctrl)
275
- objSet[stateName] = v
276
- return objSet
277
- } else {
278
- let value = (state as any)[stateName]
279
- value.includes(ctrl.value) ? (value = value.filter((v: string) => v !== ctrl.value)) : value.push(ctrl.value)
280
- const objSet2: any = { [ctrl.name]: value }
281
- return objSet2
282
- }
283
- } else {
284
- const data = valueOf(ctrl, l, e.type)
285
- if (data.mustChange) {
286
- objSet[stateName] = data.value
287
- return objSet
288
- } else {
289
- return undefined
290
- }
291
- }
292
- }
package/src/util.ts CHANGED
@@ -1,5 +1,17 @@
1
- import { Locale } from "./core"
2
- import { enLocale } from "./state"
1
+ import { Locale } from "./core";
2
+
3
+ export const enLocale = {
4
+ id: "en-US",
5
+ countryCode: "US",
6
+ dateFormat: "M/d/yyyy",
7
+ firstDayOfWeek: 1,
8
+ decimalSeparator: ".",
9
+ groupSeparator: ",",
10
+ decimalDigits: 2,
11
+ currencyCode: "USD",
12
+ currencySymbol: "$",
13
+ currencyPattern: 0,
14
+ }
3
15
 
4
16
  const r1 = / |,|\$|€|£|¥|'|٬|،| /g
5
17
  const r2 = / |\.|\$|€|£|¥|'|٬|،| /g
package/lib/formutil.js DELETED
@@ -1,76 +0,0 @@
1
- "use strict"
2
- Object.defineProperty(exports, "__esModule", { value: true })
3
- function setReadOnly(form) {
4
- var args = []
5
- for (var _i = 1; _i < arguments.length; _i++) {
6
- args[_i - 1] = arguments[_i]
7
- }
8
- if (!form) {
9
- return
10
- }
11
- var len = form.length
12
- for (var i = 0; i < len; i++) {
13
- var ctrl = form[i]
14
- var name_1 = ctrl.getAttribute("name")
15
- var skip = false
16
- if (name_1 != null && name_1.length > 0 && name_1 !== "btnBack") {
17
- if (arguments.length > 1) {
18
- for (var j = 1; j < arguments.length; j++) {
19
- if (arguments[j] === name_1) {
20
- skip = true
21
- }
22
- }
23
- }
24
- if (skip === false) {
25
- var nodeName = ctrl.nodeName
26
- var type = ctrl.getAttribute("type")
27
- if (nodeName === "INPUT" && type !== null) {
28
- nodeName = type.toUpperCase()
29
- }
30
- if (nodeName !== "BUTTON" && nodeName !== "RESET" && nodeName !== "SUBMIT" && nodeName !== "SELECT") {
31
- switch (type) {
32
- case "checkbox":
33
- ctrl.disabled = true
34
- break
35
- case "radio":
36
- ctrl.disabled = true
37
- break
38
- default:
39
- ctrl.readOnly = true
40
- }
41
- } else {
42
- ctrl.disabled = true
43
- }
44
- }
45
- }
46
- }
47
- }
48
- exports.setReadOnly = setReadOnly
49
- function focusFirstError(form, className) {
50
- if (!form) {
51
- return
52
- }
53
- var len = form.length
54
- if (className && className.length > 0) {
55
- for (var i = 0; i < len; i++) {
56
- var ctrl = form[i]
57
- var parent_1 = ctrl.parentElement
58
- if (ctrl.classList.contains(className) || (parent_1 && parent_1.classList.contains(className))) {
59
- ctrl.focus()
60
- ctrl.scrollIntoView()
61
- return
62
- }
63
- }
64
- } else {
65
- for (var i = 0; i < len; i++) {
66
- var ctrl = form[i]
67
- var parent_2 = ctrl.parentElement
68
- if (ctrl.classList.contains("invalid") || ctrl.classList.contains(".ng-invalid") || (parent_2 && parent_2.classList.contains("invalid"))) {
69
- ctrl.focus()
70
- ctrl.scrollIntoView()
71
- return
72
- }
73
- }
74
- }
75
- }
76
- exports.focusFirstError = focusFirstError