@codeleap/form 4.3.9 → 5.0.0
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/package.json +13 -14
- package/package.json.bak +10 -11
- package/src/fields/bool.ts +21 -0
- package/src/fields/date.ts +22 -0
- package/src/fields/file.ts +19 -0
- package/src/fields/group.ts +21 -0
- package/src/fields/index.ts +28 -0
- package/src/fields/list.ts +36 -0
- package/src/fields/number.ts +33 -0
- package/src/fields/selectable.ts +32 -0
- package/src/fields/text.ts +24 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useField.ts +14 -0
- package/src/index.ts +5 -6
- package/src/lib/Field.ts +310 -0
- package/src/lib/Form.ts +172 -0
- package/src/lib/factories.tsx +19 -0
- package/src/lib/index.ts +2 -0
- package/src/lib/useFieldBinding.ts +40 -0
- package/src/types/field.ts +38 -0
- package/src/types/form.ts +36 -0
- package/src/types/globals.ts +15 -0
- package/src/types/index.ts +4 -0
- package/src/types/validation.ts +10 -0
- package/src/validators/index.ts +1 -0
- package/src/validators/zod.ts +37 -0
- package/src/constants.ts +0 -33
- package/src/createForm.ts +0 -105
- package/src/presets.ts +0 -14
- package/src/types.ts +0 -268
- package/src/useForm.ts +0 -269
- package/src/utils.ts +0 -55
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codeleap/form",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"main": "src/index.ts",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -9,23 +9,22 @@
|
|
|
9
9
|
"directory": "packages/form"
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"@codeleap/config": "
|
|
13
|
-
"@codeleap/types": "
|
|
14
|
-
"@codeleap/
|
|
15
|
-
"
|
|
16
|
-
"@codeleap/i18n": "4.3.9",
|
|
12
|
+
"@codeleap/config": "5.0.0",
|
|
13
|
+
"@codeleap/types": "5.0.0",
|
|
14
|
+
"@codeleap/store": "5.0.0",
|
|
15
|
+
"zod": "3.23.8",
|
|
17
16
|
"ts-node-dev": "1.1.8"
|
|
18
17
|
},
|
|
19
18
|
"scripts": {
|
|
20
|
-
"build": "echo 'No build needed'"
|
|
19
|
+
"build": "echo 'No build needed'",
|
|
20
|
+
"playground": "bun src/test.ts"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"@codeleap/types": "
|
|
24
|
-
"@codeleap/
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"yup": "^0.32.11"
|
|
23
|
+
"@codeleap/types": "5.0.0",
|
|
24
|
+
"@codeleap/store": "5.0.0",
|
|
25
|
+
"zod": "*",
|
|
26
|
+
"react": "18.2.0",
|
|
27
|
+
"typescript": "5.5.2",
|
|
28
|
+
"yup": "1.6.1"
|
|
30
29
|
}
|
|
31
30
|
}
|
package/package.json.bak
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codeleap/form",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"main": "src/index.ts",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -11,21 +11,20 @@
|
|
|
11
11
|
"devDependencies": {
|
|
12
12
|
"@codeleap/config": "workspace:*",
|
|
13
13
|
"@codeleap/types": "workspace:*",
|
|
14
|
-
"@codeleap/
|
|
15
|
-
"
|
|
16
|
-
"@codeleap/i18n": "workspace:*",
|
|
14
|
+
"@codeleap/store": "workspace:*",
|
|
15
|
+
"zod": "3.23.8",
|
|
17
16
|
"ts-node-dev": "1.1.8"
|
|
18
17
|
},
|
|
19
18
|
"scripts": {
|
|
20
|
-
"build": "echo 'No build needed'"
|
|
19
|
+
"build": "echo 'No build needed'",
|
|
20
|
+
"playground": "bun src/test.ts"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
23
|
"@codeleap/types": "workspace:*",
|
|
24
|
-
"@codeleap/
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"yup": "^0.32.11"
|
|
24
|
+
"@codeleap/store": "workspace:*",
|
|
25
|
+
"zod": "*",
|
|
26
|
+
"react": "18.2.0",
|
|
27
|
+
"typescript": "5.5.2",
|
|
28
|
+
"yup": "1.6.1"
|
|
30
29
|
}
|
|
31
30
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Field } from "../lib/Field"
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import { FieldOptions, Validator } from "../types"
|
|
4
|
+
import { zodValidator } from "../validators"
|
|
5
|
+
|
|
6
|
+
type VALUE = boolean
|
|
7
|
+
|
|
8
|
+
export type BooleanValidator<R = any, Err = any> = Validator<VALUE, R, Err>
|
|
9
|
+
|
|
10
|
+
type BooleanFieldOptions<Validate extends BooleanValidator> = FieldOptions<VALUE, Validate>
|
|
11
|
+
|
|
12
|
+
export class BooleanField<Validate extends BooleanValidator> extends Field<boolean, Validate> {
|
|
13
|
+
_type = "BOOLEAN"
|
|
14
|
+
|
|
15
|
+
constructor(options: BooleanFieldOptions<Validate>) {
|
|
16
|
+
super({
|
|
17
|
+
validate: zodValidator(z.boolean()) as unknown as Validate,
|
|
18
|
+
...options
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Field } from "../lib/Field"
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import { FieldOptions, Validator } from "../types"
|
|
4
|
+
import { zodValidator } from "../validators"
|
|
5
|
+
|
|
6
|
+
export type DateValidator<R = any, Err = any> = Validator<Date, R, Err>
|
|
7
|
+
|
|
8
|
+
type DateFieldOptions<Validate extends DateValidator> = FieldOptions<Date, Validate> & {
|
|
9
|
+
minimumDate?: Date
|
|
10
|
+
maximumDate?: Date
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class DateField<Validate extends DateValidator> extends Field<Date, Validate> {
|
|
14
|
+
_type = "DATE"
|
|
15
|
+
|
|
16
|
+
constructor(options: DateFieldOptions<Validate>) {
|
|
17
|
+
super({
|
|
18
|
+
validate: zodValidator(z.date()) as unknown as Validate,
|
|
19
|
+
...options
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Field } from "../lib/Field"
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import { FieldOptions, Validator } from "../types"
|
|
4
|
+
import { zodValidator } from "../validators"
|
|
5
|
+
|
|
6
|
+
export type FileValidator<R = any, Err = any> = Validator<any, R, Err>
|
|
7
|
+
|
|
8
|
+
type FileFieldOptions<Validate extends FileValidator> = FieldOptions<any, Validate>
|
|
9
|
+
|
|
10
|
+
export class FileField<Validate extends FileValidator> extends Field<any, Validate> {
|
|
11
|
+
_type = "FILE"
|
|
12
|
+
|
|
13
|
+
constructor(options: FileFieldOptions<Validate>) {
|
|
14
|
+
super({
|
|
15
|
+
validate: zodValidator(z.any()) as unknown as Validate,
|
|
16
|
+
...options
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Field } from "../lib/Field"
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import { FieldOptions, Validator } from "../types"
|
|
4
|
+
import { zodValidator } from "../validators"
|
|
5
|
+
|
|
6
|
+
type VALUE = string
|
|
7
|
+
|
|
8
|
+
type GroupValidator<R = any, Err = any> = Validator<VALUE, R, Err>
|
|
9
|
+
|
|
10
|
+
type GroupFieldOptions<Validate extends GroupValidator> = FieldOptions<VALUE, Validate>
|
|
11
|
+
|
|
12
|
+
export class GroupField<Validate extends GroupValidator> extends Field<string, Validate> {
|
|
13
|
+
_type = "group"
|
|
14
|
+
|
|
15
|
+
constructor(options: GroupFieldOptions<Validate>) {
|
|
16
|
+
super({
|
|
17
|
+
validate: zodValidator(z.string()) as unknown as Validate,
|
|
18
|
+
...options
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { fieldFactory } from "../lib/factories"
|
|
2
|
+
import { TextField } from "./text"
|
|
3
|
+
import { ListField } from "./list"
|
|
4
|
+
import { NumberField } from "./number"
|
|
5
|
+
import { BooleanField } from "./bool"
|
|
6
|
+
import { SelectableField } from "./selectable"
|
|
7
|
+
import { DateField } from "./date"
|
|
8
|
+
import { FileField } from "./file"
|
|
9
|
+
|
|
10
|
+
export const fields = {
|
|
11
|
+
text: fieldFactory(TextField),
|
|
12
|
+
list: fieldFactory(ListField),
|
|
13
|
+
number: fieldFactory(NumberField),
|
|
14
|
+
boolean: fieldFactory(BooleanField),
|
|
15
|
+
selectable: fieldFactory(SelectableField),
|
|
16
|
+
date: fieldFactory(DateField),
|
|
17
|
+
file: fieldFactory(FileField),
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
TextField,
|
|
22
|
+
ListField,
|
|
23
|
+
NumberField,
|
|
24
|
+
BooleanField,
|
|
25
|
+
SelectableField,
|
|
26
|
+
DateField,
|
|
27
|
+
FileField,
|
|
28
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Field } from "../lib/Field"
|
|
2
|
+
import { FieldOptions, Validator } from "../types"
|
|
3
|
+
|
|
4
|
+
export type ListValidator<Val, R = any, Err = any> = Validator<Val[] | Val, R, Err>
|
|
5
|
+
|
|
6
|
+
type ListFieldOptions<T, Validate extends Validator<T | T[], any, any>> = FieldOptions<T, Validate> & {
|
|
7
|
+
item: Field<T, Validate>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class ListField<T, Validate extends ListValidator<T>> extends Field<T | T[], Validate> {
|
|
11
|
+
_type = "LIST"
|
|
12
|
+
|
|
13
|
+
constructor(options: ListFieldOptions<T[] | T, Validate>) {
|
|
14
|
+
super({
|
|
15
|
+
// @ts-ignore fix this
|
|
16
|
+
validate: (v) => {
|
|
17
|
+
if (!options.validate) return {
|
|
18
|
+
isValid: true
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
for (const value of v as T[]) {
|
|
22
|
+
const validation = options.validate?.(value, null)
|
|
23
|
+
|
|
24
|
+
if (!validation.isValid) {
|
|
25
|
+
return validation
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
isValid: true
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
...options
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Field } from "../lib/Field"
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import { FieldOptions, Validator } from "../types"
|
|
4
|
+
import { zodValidator } from "../validators"
|
|
5
|
+
|
|
6
|
+
export type NumberValidator<R = any, Err = any> = Validator<number | number[], R, Err>
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
min?: number
|
|
10
|
+
max?: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type NumberFieldOptions<Validate extends NumberValidator> = FieldOptions<number | number[], Validate> & Props
|
|
14
|
+
|
|
15
|
+
export class NumberField<Validate extends NumberValidator> extends Field<number | number[], Validate> {
|
|
16
|
+
_type = "NUMBER"
|
|
17
|
+
|
|
18
|
+
constructor(options: NumberFieldOptions<Validate>) {
|
|
19
|
+
const { min = 0, max = 100000, defaultValue, ...others } = options
|
|
20
|
+
|
|
21
|
+
const isMultiple = Array.isArray(defaultValue)
|
|
22
|
+
|
|
23
|
+
const zScheme = z.number().min(min).max(max)
|
|
24
|
+
|
|
25
|
+
super({
|
|
26
|
+
validate: zodValidator(isMultiple ? z.array(zScheme) : zScheme) as unknown as Validate,
|
|
27
|
+
min,
|
|
28
|
+
max,
|
|
29
|
+
defaultValue,
|
|
30
|
+
...others
|
|
31
|
+
} as NumberFieldOptions<Validate>)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Field } from "../lib/Field"
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import { FieldOptions, Validator } from "../types"
|
|
4
|
+
import { zodValidator } from "../validators"
|
|
5
|
+
import { Options } from '@codeleap/types'
|
|
6
|
+
|
|
7
|
+
type VALUE = string | number
|
|
8
|
+
|
|
9
|
+
export type SelectableValidator<V extends VALUE, R = any, Err = any> = Validator<V, R, Err>
|
|
10
|
+
|
|
11
|
+
type SelectableFieldOptions<V extends VALUE, Validate extends SelectableValidator<V>> = FieldOptions<V, Validate> & {
|
|
12
|
+
options: Options<V>
|
|
13
|
+
minItems?: number
|
|
14
|
+
maxItems?: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class SelectableField<V extends VALUE, Validate extends SelectableValidator<V>> extends Field<V, Validate> {
|
|
18
|
+
_type = "SELECTABLE"
|
|
19
|
+
|
|
20
|
+
constructor(options: SelectableFieldOptions<V, Validate>) {
|
|
21
|
+
const { minItems = 1, maxItems = options?.options?.length } = options
|
|
22
|
+
|
|
23
|
+
const zScheme = z.string().or(z.number())
|
|
24
|
+
|
|
25
|
+
super({
|
|
26
|
+
validate: zodValidator(z.array(zScheme).min(minItems).max(maxItems).or(zScheme)) as unknown as Validate,
|
|
27
|
+
minItems,
|
|
28
|
+
maxItems,
|
|
29
|
+
...options,
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Field } from "../lib/Field"
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import { FieldOptions, Validator } from "../types"
|
|
4
|
+
import { zodValidator } from "../validators"
|
|
5
|
+
|
|
6
|
+
type VALUE = string
|
|
7
|
+
|
|
8
|
+
export type TextValidator<R = any, Err = any> = Validator<VALUE, R, Err>
|
|
9
|
+
|
|
10
|
+
type TextFieldOptions<Validate extends TextValidator> = FieldOptions<VALUE, Validate> & {
|
|
11
|
+
secure?: boolean
|
|
12
|
+
multiline?: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class TextField<Validate extends TextValidator> extends Field<string, Validate> {
|
|
16
|
+
_type = "TEXT"
|
|
17
|
+
|
|
18
|
+
constructor(options: TextFieldOptions<Validate>) {
|
|
19
|
+
super({
|
|
20
|
+
validate: zodValidator(z.string()) as unknown as Validate,
|
|
21
|
+
...options
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useField } from './useField'
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useMemo } from "react"
|
|
2
|
+
import type { Field } from "../lib"
|
|
3
|
+
|
|
4
|
+
export function useField<V, T extends Field<V, any, any>>(field: T, params: Parameters<T['use']>, defaultField: () => T): ReturnType<T['use']> {
|
|
5
|
+
if (field) {
|
|
6
|
+
return field.use(params?.[0], params?.[1]) as ReturnType<T['use']>
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const tempField = useMemo(() => {
|
|
10
|
+
return defaultField()
|
|
11
|
+
}, [])
|
|
12
|
+
|
|
13
|
+
return tempField.use(params[0], params?.[1]) as ReturnType<T['use']>
|
|
14
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
export * from './
|
|
2
|
-
export * from './
|
|
3
|
-
export * from './
|
|
4
|
-
export * from './
|
|
5
|
-
export *
|
|
6
|
-
export * as yup from 'yup'
|
|
1
|
+
export * from './fields'
|
|
2
|
+
export * from './types'
|
|
3
|
+
export * from './lib'
|
|
4
|
+
export * from './validators'
|
|
5
|
+
export * from './hooks'
|
package/src/lib/Field.ts
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { FieldState, FieldOptions, FieldMeasureResult } from '../types/field';
|
|
2
|
+
import { ValidationResult, Validator } from '../types/validation';
|
|
3
|
+
import { IFieldRef, IFieldProps } from '../types/globals';
|
|
4
|
+
import { atom } from 'nanostores'
|
|
5
|
+
import { AnyRecord, SecondToLastArguments, TypeGuards } from '@codeleap/types';
|
|
6
|
+
|
|
7
|
+
import { useStore } from '@nanostores/react'
|
|
8
|
+
import { useFieldBinding } from './useFieldBinding';
|
|
9
|
+
import { createRef, useRef, useState } from 'react';
|
|
10
|
+
|
|
11
|
+
class ValidationError extends Error {
|
|
12
|
+
data: any
|
|
13
|
+
|
|
14
|
+
constructor(data: any) {
|
|
15
|
+
super()
|
|
16
|
+
this.data = data
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
export class Field<
|
|
23
|
+
T,
|
|
24
|
+
Validate extends Validator<T,any,any>,
|
|
25
|
+
Result = Validate extends Validator<T, infer R, any> ? R : never,
|
|
26
|
+
Err = Validate extends Validator<T, any, infer E> ? E : never
|
|
27
|
+
> {
|
|
28
|
+
_type: string
|
|
29
|
+
|
|
30
|
+
deep: boolean
|
|
31
|
+
|
|
32
|
+
state: FieldState<T>
|
|
33
|
+
|
|
34
|
+
properties: AnyRecord = {}
|
|
35
|
+
|
|
36
|
+
options: FieldOptions<T, Validate>
|
|
37
|
+
|
|
38
|
+
ref?: React.RefObject<IFieldRef<T>>
|
|
39
|
+
|
|
40
|
+
__validationRes: ValidationResult<Result, Err>
|
|
41
|
+
|
|
42
|
+
private initialValue: T
|
|
43
|
+
|
|
44
|
+
static enableLogs = false
|
|
45
|
+
|
|
46
|
+
static getProps = (field: Field<any,any>) => {
|
|
47
|
+
throw new Error('Field.getProps not implemented')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static methodMeasurePosition = (field: Field<any,any>, wrapperRef: any): Promise<FieldMeasureResult> => {
|
|
51
|
+
throw new Error('Field.measurePosition not implemented')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static methodScrollTo = (field: Field<any,any>, scrollRef: any, measure: FieldMeasureResult): Promise<void> => {
|
|
55
|
+
throw new Error('Field.scrollTo not implemented')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static methodGetPadding = (field: Field<any,any>): number => {
|
|
59
|
+
throw new Error('Field.getPadding not implemented')
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
constructor(options: FieldOptions<T, Validate>) {
|
|
63
|
+
this.options = options
|
|
64
|
+
this.ref = createRef()
|
|
65
|
+
this.loadState()
|
|
66
|
+
|
|
67
|
+
this.setValue = this.setValue.bind(this)
|
|
68
|
+
this.use = this.use.bind(this)
|
|
69
|
+
this.useBinding = this.useBinding.bind(this)
|
|
70
|
+
|
|
71
|
+
this.properties = this.toInternalProperties(options)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
get name(){
|
|
75
|
+
return this.options.name
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get value(){
|
|
79
|
+
return this.state.get()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get isValid(){
|
|
83
|
+
const res = this.validate()
|
|
84
|
+
|
|
85
|
+
return res.isValid
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private toInternalProperties(options: FieldOptions<T, Validate>) {
|
|
89
|
+
const internalKeys = new Set(['name', 'defaultValue', 'state', 'validate', 'loader', 'onValueChange'])
|
|
90
|
+
|
|
91
|
+
return Object.assign(
|
|
92
|
+
{ field: this },
|
|
93
|
+
Object.fromEntries(Object.entries(options).filter(([key]) => !internalKeys.has(key)))
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
attach(to: FieldState<T>) {
|
|
98
|
+
const val = this.value
|
|
99
|
+
|
|
100
|
+
this.state = to
|
|
101
|
+
|
|
102
|
+
this.setValue(val)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
setValue(to: T) {
|
|
106
|
+
if (this.options.onValueChange) this.options.onValueChange(to)
|
|
107
|
+
return this.state.set(to)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
useValue(){
|
|
111
|
+
const value = useStore(this.state)
|
|
112
|
+
|
|
113
|
+
return value
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
use(impl?: Partial<IFieldRef<T>>, deps?: any[]){
|
|
117
|
+
const value = this.useValue()
|
|
118
|
+
|
|
119
|
+
const validation = this.useValidation()
|
|
120
|
+
|
|
121
|
+
// Yes, this is dangerous and doesn't follow the rules, but not passing an implementation to imperative handle after passing it once would break the app anyway
|
|
122
|
+
if(impl) {
|
|
123
|
+
this.useBinding(impl, deps)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const changed = value != this.initialValue
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
validation,
|
|
130
|
+
value,
|
|
131
|
+
setValue: this.setValue,
|
|
132
|
+
changed,
|
|
133
|
+
representation: this.toRepresentation(value),
|
|
134
|
+
options: this.options,
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
useBinding(...args: SecondToLastArguments<typeof useFieldBinding<T>>){
|
|
139
|
+
useFieldBinding(this.ref, ...args)
|
|
140
|
+
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
changed() {
|
|
144
|
+
return this.state.get() != this.initialValue
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// If we make this async, the js engine will not delay further execution while the funcion is not done. This way we wait until we know wheter there's a promise or not
|
|
148
|
+
loadState(){
|
|
149
|
+
let defaultValuePromise: Promise<T> = undefined
|
|
150
|
+
let defaultValue:T = undefined
|
|
151
|
+
|
|
152
|
+
if(TypeGuards.isFunction(this.options.defaultValue)) {
|
|
153
|
+
const v = this.options.defaultValue()
|
|
154
|
+
|
|
155
|
+
if(TypeGuards.isPromise(v)) {
|
|
156
|
+
defaultValuePromise = v
|
|
157
|
+
} else {
|
|
158
|
+
defaultValue = v
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
const v = this.options?.defaultValue
|
|
162
|
+
|
|
163
|
+
if(TypeGuards.isPromise(v)) {
|
|
164
|
+
defaultValuePromise = v
|
|
165
|
+
} else {
|
|
166
|
+
defaultValue = v
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
this.state = this?.options?.state ?? atom(defaultValue)
|
|
171
|
+
|
|
172
|
+
if(!defaultValuePromise) {
|
|
173
|
+
this.initialValue = defaultValue
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if(!!defaultValuePromise) {
|
|
177
|
+
this.log('debug', 'Waiting for initial value')
|
|
178
|
+
|
|
179
|
+
return defaultValuePromise
|
|
180
|
+
.then((v) => {
|
|
181
|
+
this.state.set(v)
|
|
182
|
+
this.initialValue = v
|
|
183
|
+
this.log('debug', `Got initial value`, v)
|
|
184
|
+
})
|
|
185
|
+
.catch(e => {
|
|
186
|
+
this.log('error', `Failed to resolve default value`, e)
|
|
187
|
+
})
|
|
188
|
+
} else {
|
|
189
|
+
return Promise.resolve()
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
formatLog(...args: any[]) {
|
|
194
|
+
const [firstArgument, ...otherArgs] = args
|
|
195
|
+
|
|
196
|
+
let PREFIX = `[FIELD: ${this.name}]`
|
|
197
|
+
|
|
198
|
+
if(TypeGuards.isString(firstArgument)) {
|
|
199
|
+
PREFIX += ` ${firstArgument}`
|
|
200
|
+
} else {
|
|
201
|
+
otherArgs.unshift(firstArgument)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const logArgs = [
|
|
205
|
+
PREFIX,
|
|
206
|
+
...otherArgs
|
|
207
|
+
]
|
|
208
|
+
|
|
209
|
+
return logArgs
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
validate(value?: any): ValidationResult<Result, Err> {
|
|
213
|
+
|
|
214
|
+
const validate = this.options.validate
|
|
215
|
+
|
|
216
|
+
const valueToCheck = TypeGuards.isUndefined(value) ? this.state.get() : this.toInternalValue(value)
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
const result = validate(valueToCheck, {})
|
|
220
|
+
|
|
221
|
+
return result
|
|
222
|
+
|
|
223
|
+
} catch(e) {
|
|
224
|
+
|
|
225
|
+
if(e instanceof ValidationError) {
|
|
226
|
+
return {
|
|
227
|
+
isValid: false,
|
|
228
|
+
error: e.data
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
throw e
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
useValidation() {
|
|
238
|
+
const value = this.useValue()
|
|
239
|
+
|
|
240
|
+
const isUnset = typeof value === 'undefined'
|
|
241
|
+
|
|
242
|
+
const startedUnset = useRef(isUnset).current
|
|
243
|
+
|
|
244
|
+
const isSet = !isUnset
|
|
245
|
+
|
|
246
|
+
const validation = this.validate(value)
|
|
247
|
+
|
|
248
|
+
const isValid = validation.isValid
|
|
249
|
+
|
|
250
|
+
const isInvalid = !isValid
|
|
251
|
+
|
|
252
|
+
const hasChanged = this.initialValue != value
|
|
253
|
+
|
|
254
|
+
const message = validation.readableError ?? validation.error?.[0]?.message
|
|
255
|
+
|
|
256
|
+
const errorDisplayRequiresBlur = startedUnset
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
const [hasBlurred, setHasBlurred] = useState(false)
|
|
260
|
+
|
|
261
|
+
const showError = isInvalid && (errorDisplayRequiresBlur ? hasBlurred : true)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
onInputBlurred(){
|
|
267
|
+
setHasBlurred(true)
|
|
268
|
+
},
|
|
269
|
+
hasBlurred,
|
|
270
|
+
hasChanged,
|
|
271
|
+
startedUnset,
|
|
272
|
+
isSet,
|
|
273
|
+
isInvalid,
|
|
274
|
+
isValid,
|
|
275
|
+
message,
|
|
276
|
+
showError,
|
|
277
|
+
isUnset,
|
|
278
|
+
validation,
|
|
279
|
+
value
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
log(level = 'log', ...args: any[]){
|
|
284
|
+
if (Field.enableLogs) console[level](...this.formatLog(...args))
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
toInternalValue(v: any) {
|
|
288
|
+
return v as T
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
toRepresentation(v: T) {
|
|
292
|
+
return v as any
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
props(): IFieldProps {
|
|
296
|
+
return this.properties
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
measurePosition<T>(wrapperRef: T): Promise<FieldMeasureResult> {
|
|
300
|
+
return Field.methodMeasurePosition(this, wrapperRef)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
scrollTo<T>(scrollRef: T, measure: FieldMeasureResult): Promise<void>{
|
|
304
|
+
return Field.methodScrollTo(this, scrollRef, measure)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
getPadding(): number {
|
|
308
|
+
return Field.methodGetPadding(this)
|
|
309
|
+
}
|
|
310
|
+
}
|