@vaadin/hilla-lit-form 24.4.0-alpha1
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/.lintstagedrc.js +6 -0
- package/Binder.d.ts +28 -0
- package/Binder.d.ts.map +1 -0
- package/Binder.js +29 -0
- package/Binder.js.map +7 -0
- package/BinderNode.d.ts +123 -0
- package/BinderNode.d.ts.map +1 -0
- package/BinderNode.js +414 -0
- package/BinderNode.js.map +7 -0
- package/BinderRoot.d.ts +98 -0
- package/BinderRoot.d.ts.map +1 -0
- package/BinderRoot.js +242 -0
- package/BinderRoot.js.map +7 -0
- package/Field.d.ts +125 -0
- package/Field.d.ts.map +1 -0
- package/Field.js +293 -0
- package/Field.js.map +7 -0
- package/LICENSE +201 -0
- package/Models.d.ts +92 -0
- package/Models.d.ts.map +1 -0
- package/Models.js +166 -0
- package/Models.js.map +7 -0
- package/README.md +26 -0
- package/Validation.d.ts +33 -0
- package/Validation.d.ts.map +1 -0
- package/Validation.js +86 -0
- package/Validation.js.map +7 -0
- package/Validators.d.ts +165 -0
- package/Validators.d.ts.map +1 -0
- package/Validators.js +342 -0
- package/Validators.js.map +7 -0
- package/Validity.d.ts +9 -0
- package/Validity.d.ts.map +1 -0
- package/Validity.js +19 -0
- package/Validity.js.map +7 -0
- package/index.d.ts +9 -0
- package/index.d.ts.map +1 -0
- package/index.js +18 -0
- package/index.js.map +7 -0
- package/package.json +93 -0
- package/types.d.js +1 -0
- package/types.d.js.map +7 -0
package/.lintstagedrc.js
ADDED
package/Binder.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type BinderConfiguration, BinderRoot } from './BinderRoot.js';
|
|
2
|
+
import type { AbstractModel, DetachedModelConstructor, Value } from './Models.js';
|
|
3
|
+
/**
|
|
4
|
+
* A Binder controls all aspects of a single form.
|
|
5
|
+
* Typically, it is used to get and set the form value,
|
|
6
|
+
* access the form model, validate, reset, and submit the form.
|
|
7
|
+
*
|
|
8
|
+
* @typeParam T - Type of the value that binds to a form
|
|
9
|
+
* @typeParam M - Type of the model that describes the structure of the value
|
|
10
|
+
*/
|
|
11
|
+
export declare class Binder<M extends AbstractModel> extends BinderRoot<M> {
|
|
12
|
+
readonly ['constructor']: Omit<typeof Binder<M>, 'constructor'>;
|
|
13
|
+
context: Element;
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
* @param context - The form view component instance to update.
|
|
17
|
+
* @param Model - The constructor (the class reference) of the form model. The Binder instantiates the top-level model
|
|
18
|
+
* @param config - The options object, which can be used to config the onChange and onSubmit callbacks.
|
|
19
|
+
*
|
|
20
|
+
* ```
|
|
21
|
+
* binder = new Binder(orderView, OrderModel);
|
|
22
|
+
* or
|
|
23
|
+
* binder = new Binder(orderView, OrderModel, {onSubmit: async (order) => {endpoint.save(order)}});
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
constructor(context: Element, Model: DetachedModelConstructor<M>, config?: BinderConfiguration<Value<M>>);
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=Binder.d.ts.map
|
package/Binder.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Binder.d.ts","sourceRoot":"","sources":["src/Binder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,mBAAmB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,wBAAwB,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAElF;;;;;;;GAOG;AACH,qBAAa,MAAM,CAAC,CAAC,SAAS,aAAa,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAC;IAChE,SAAiB,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;IAExE,OAAO,EAAE,OAAO,CAAC;IAEjB;;;;;;;;;;;OAWG;gBACS,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,wBAAwB,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;CAczG"}
|
package/Binder.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { BinderRoot } from "./BinderRoot.js";
|
|
2
|
+
class Binder extends BinderRoot {
|
|
3
|
+
context;
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param context - The form view component instance to update.
|
|
7
|
+
* @param Model - The constructor (the class reference) of the form model. The Binder instantiates the top-level model
|
|
8
|
+
* @param config - The options object, which can be used to config the onChange and onSubmit callbacks.
|
|
9
|
+
*
|
|
10
|
+
* ```
|
|
11
|
+
* binder = new Binder(orderView, OrderModel);
|
|
12
|
+
* or
|
|
13
|
+
* binder = new Binder(orderView, OrderModel, {onSubmit: async (order) => {endpoint.save(order)}});
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
constructor(context, Model, config) {
|
|
17
|
+
const changeCallback = config?.onChange ?? (typeof context.requestUpdate === "function" ? () => context.requestUpdate() : void 0);
|
|
18
|
+
super(Model, {
|
|
19
|
+
...config ?? {},
|
|
20
|
+
onChange: changeCallback,
|
|
21
|
+
context
|
|
22
|
+
});
|
|
23
|
+
this.context = context;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
Binder
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=Binder.js.map
|
package/Binder.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["src/Binder.ts"],
|
|
4
|
+
"sourcesContent": ["import type { LitElement } from 'lit';\nimport { type BinderConfiguration, BinderRoot } from './BinderRoot.js';\nimport type { AbstractModel, DetachedModelConstructor, Value } from './Models.js';\n\n/**\n * A Binder controls all aspects of a single form.\n * Typically, it is used to get and set the form value,\n * access the form model, validate, reset, and submit the form.\n *\n * @typeParam T - Type of the value that binds to a form\n * @typeParam M - Type of the model that describes the structure of the value\n */\nexport class Binder<M extends AbstractModel> extends BinderRoot<M> {\n declare readonly ['constructor']: Omit<typeof Binder<M>, 'constructor'>;\n\n context: Element;\n\n /**\n *\n * @param context - The form view component instance to update.\n * @param Model - The constructor (the class reference) of the form model. The Binder instantiates the top-level model\n * @param config - The options object, which can be used to config the onChange and onSubmit callbacks.\n *\n * ```\n * binder = new Binder(orderView, OrderModel);\n * or\n * binder = new Binder(orderView, OrderModel, {onSubmit: async (order) => {endpoint.save(order)}});\n * ```\n */\n constructor(context: Element, Model: DetachedModelConstructor<M>, config?: BinderConfiguration<Value<M>>) {\n const changeCallback =\n config?.onChange ??\n (typeof (context as LitElement).requestUpdate === 'function'\n ? () => (context as LitElement).requestUpdate()\n : undefined);\n\n super(Model, {\n ...(config ?? {}),\n onChange: changeCallback,\n context,\n });\n this.context = context;\n }\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAmC,kBAAkB;AAW9C,MAAM,eAAwC,WAAc;AAAA,EAGjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,YAAY,SAAkB,OAAoC,QAAwC;AACxG,UAAM,iBACJ,QAAQ,aACP,OAAQ,QAAuB,kBAAkB,aAC9C,MAAO,QAAuB,cAAc,IAC5C;AAEN,UAAM,OAAO;AAAA,MACX,GAAI,UAAU,CAAC;AAAA,MACf,UAAU;AAAA,MACV;AAAA,IACF,CAAC;AACD,SAAK,UAAU;AAAA,EACjB;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/BinderNode.d.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { BinderRoot } from './BinderRoot.js';
|
|
2
|
+
import { AbstractModel, type ArrayItemModel, type Value } from './Models.js';
|
|
3
|
+
import type { ClassStaticProperties } from './types.js';
|
|
4
|
+
import type { Validator, ValueError } from './Validation.js';
|
|
5
|
+
import { _validity } from './Validity.js';
|
|
6
|
+
export declare const _updateValidation: unique symbol;
|
|
7
|
+
export declare const _update: unique symbol;
|
|
8
|
+
export declare const _setErrorsWithDescendants: unique symbol;
|
|
9
|
+
export declare const _clearValidation: unique symbol;
|
|
10
|
+
export declare function getBinderNode<M extends AbstractModel>(model: M): BinderNode<M>;
|
|
11
|
+
export declare const CHANGED: Event;
|
|
12
|
+
/**
|
|
13
|
+
* The BinderNode\<M\> class provides the form binding related APIs
|
|
14
|
+
* with respect to a particular model instance.
|
|
15
|
+
*
|
|
16
|
+
* Structurally, model instances form a tree, in which the object
|
|
17
|
+
* and array models have child nodes of field and array item model
|
|
18
|
+
* instances.
|
|
19
|
+
*/
|
|
20
|
+
export declare class BinderNode<M extends AbstractModel = AbstractModel> extends EventTarget {
|
|
21
|
+
#private;
|
|
22
|
+
readonly ['constructor']: ClassStaticProperties<typeof BinderNode<M>>;
|
|
23
|
+
readonly model: M;
|
|
24
|
+
/**
|
|
25
|
+
* The validity state read from the bound element, if any. Represents the
|
|
26
|
+
* HTML element internal validation.
|
|
27
|
+
*
|
|
28
|
+
* For elements with `validity.valid === false`, the value in the
|
|
29
|
+
* bound element is considered as invalid.
|
|
30
|
+
*/
|
|
31
|
+
[_validity]?: ValidityState;
|
|
32
|
+
constructor(model: M);
|
|
33
|
+
/**
|
|
34
|
+
* The binder for the top-level model.
|
|
35
|
+
*/
|
|
36
|
+
get binder(): BinderRoot;
|
|
37
|
+
/**
|
|
38
|
+
* The default value related to the model
|
|
39
|
+
*/
|
|
40
|
+
get defaultValue(): Value<M> | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* True if the current value is different from the defaultValue.
|
|
43
|
+
*/
|
|
44
|
+
get dirty(): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* The combined array of all errors for this node’s model and all its nested
|
|
47
|
+
* models
|
|
48
|
+
*/
|
|
49
|
+
get errors(): readonly ValueError[];
|
|
50
|
+
/**
|
|
51
|
+
* Indicates if there is any error for the node's model.
|
|
52
|
+
*/
|
|
53
|
+
get invalid(): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* The name generated from the model structure, used to set the name
|
|
56
|
+
* attribute on the field components.
|
|
57
|
+
*/
|
|
58
|
+
get name(): string;
|
|
59
|
+
/**
|
|
60
|
+
* The array of validation errors directly related with the model.
|
|
61
|
+
*/
|
|
62
|
+
get ownErrors(): ReadonlyArray<ValueError<Value<M>>>;
|
|
63
|
+
/**
|
|
64
|
+
* The parent node, if this binder node corresponds to a nested model,
|
|
65
|
+
* otherwise undefined for the top-level binder.
|
|
66
|
+
*/
|
|
67
|
+
get parent(): BinderNode | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* True if the value is required to be non-empty.
|
|
70
|
+
*/
|
|
71
|
+
get required(): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* The array of validators for the model. The default value is defined in the
|
|
74
|
+
* model.
|
|
75
|
+
*/
|
|
76
|
+
get validators(): ReadonlyArray<Validator<Value<M>>>;
|
|
77
|
+
set validators(validators: ReadonlyArray<Validator<Value<M>>>);
|
|
78
|
+
/**
|
|
79
|
+
* The current value related to the model
|
|
80
|
+
*/
|
|
81
|
+
get value(): Value<M> | undefined;
|
|
82
|
+
set value(value: Value<M> | undefined);
|
|
83
|
+
/**
|
|
84
|
+
* True if the bound field was ever focused and blurred by the user.
|
|
85
|
+
*/
|
|
86
|
+
get visited(): boolean;
|
|
87
|
+
set visited(v: boolean);
|
|
88
|
+
/**
|
|
89
|
+
* A helper method to add a validator
|
|
90
|
+
*
|
|
91
|
+
* @param validator - a validator
|
|
92
|
+
*/
|
|
93
|
+
addValidator(validator: Validator<Value<M>>): void;
|
|
94
|
+
/**
|
|
95
|
+
* Append an item to the array value.
|
|
96
|
+
*
|
|
97
|
+
* Requires the context model to be an array reference.
|
|
98
|
+
*
|
|
99
|
+
* @param item - optional new item value, an empty item is
|
|
100
|
+
* appended if the argument is omitted
|
|
101
|
+
*/
|
|
102
|
+
appendItem(item?: Value<ArrayItemModel<M>>): void;
|
|
103
|
+
/**
|
|
104
|
+
* Returns a binder node for the nested model instance.
|
|
105
|
+
*
|
|
106
|
+
* @param model - The nested model instance
|
|
107
|
+
*/
|
|
108
|
+
for<N extends AbstractModel>(model: N): BinderNode<N>;
|
|
109
|
+
prependItem(item?: Value<ArrayItemModel<M>>): void;
|
|
110
|
+
removeSelf(): void;
|
|
111
|
+
/**
|
|
112
|
+
* Runs all validation callbacks potentially affecting this
|
|
113
|
+
* or any nested model. Returns the combined array of all
|
|
114
|
+
* errors as in the errors property.
|
|
115
|
+
*/
|
|
116
|
+
validate(): Promise<readonly ValueError[]>;
|
|
117
|
+
protected [_clearValidation](): boolean;
|
|
118
|
+
protected [_setErrorsWithDescendants](errors?: readonly ValueError[]): void;
|
|
119
|
+
protected [_update](_?: Value<M>): void;
|
|
120
|
+
protected [_updateValidation](): Promise<void>;
|
|
121
|
+
initializeValue(forceInitialize?: boolean): void;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=BinderNode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BinderNode.d.ts","sourceRoot":"","sources":["src/BinderNode.ts"],"names":[],"mappings":"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"}
|
package/BinderNode.js
ADDED
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
import {
|
|
2
|
+
_createEmptyItemValue,
|
|
3
|
+
_key,
|
|
4
|
+
_parent,
|
|
5
|
+
_validators,
|
|
6
|
+
AbstractModel,
|
|
7
|
+
ArrayModel,
|
|
8
|
+
getObjectModelOwnAndParentGetters,
|
|
9
|
+
ObjectModel
|
|
10
|
+
} from "./Models.js";
|
|
11
|
+
import { ValidityStateValidator } from "./Validators.js";
|
|
12
|
+
import { _validity } from "./Validity.js";
|
|
13
|
+
const _updateValidation = Symbol("updateValidation");
|
|
14
|
+
const _update = Symbol("update");
|
|
15
|
+
const _setErrorsWithDescendants = Symbol("setErrorsWithDescendants");
|
|
16
|
+
const _clearValidation = Symbol("clearValidation");
|
|
17
|
+
const nodes = /* @__PURE__ */ new WeakMap();
|
|
18
|
+
function getBinderNode(model) {
|
|
19
|
+
let node = nodes.get(model);
|
|
20
|
+
if (!node) {
|
|
21
|
+
node = new BinderNode(model);
|
|
22
|
+
nodes.set(model, node);
|
|
23
|
+
}
|
|
24
|
+
return node;
|
|
25
|
+
}
|
|
26
|
+
function getErrorPropertyName(valueError) {
|
|
27
|
+
return typeof valueError.property === "string" ? valueError.property : getBinderNode(valueError.property).name;
|
|
28
|
+
}
|
|
29
|
+
function updateObjectOrArrayKey(model, value, key, keyValue) {
|
|
30
|
+
if (model instanceof ObjectModel) {
|
|
31
|
+
return {
|
|
32
|
+
...value,
|
|
33
|
+
[key]: keyValue
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (keyValue === void 0) {
|
|
37
|
+
throw new TypeError("Unexpected undefined value");
|
|
38
|
+
}
|
|
39
|
+
if (model instanceof ArrayModel) {
|
|
40
|
+
const array = value.slice();
|
|
41
|
+
array[key] = keyValue;
|
|
42
|
+
return array;
|
|
43
|
+
}
|
|
44
|
+
throw new TypeError(`Unknown model type ${model.constructor.name}`);
|
|
45
|
+
}
|
|
46
|
+
const CHANGED = new Event("binder-node-changed");
|
|
47
|
+
class NotArrayModelError extends Error {
|
|
48
|
+
constructor() {
|
|
49
|
+
super("The model does not represent array");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
class NotArrayItemModelError extends Error {
|
|
53
|
+
constructor() {
|
|
54
|
+
super("The model does not represent array item");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const defaultArrayItemCache = /* @__PURE__ */ new WeakMap();
|
|
58
|
+
class BinderNode extends EventTarget {
|
|
59
|
+
model;
|
|
60
|
+
/**
|
|
61
|
+
* The validity state read from the bound element, if any. Represents the
|
|
62
|
+
* HTML element internal validation.
|
|
63
|
+
*
|
|
64
|
+
* For elements with `validity.valid === false`, the value in the
|
|
65
|
+
* bound element is considered as invalid.
|
|
66
|
+
*/
|
|
67
|
+
[_validity];
|
|
68
|
+
#ownErrors;
|
|
69
|
+
#validators;
|
|
70
|
+
#validityStateValidator;
|
|
71
|
+
#visited = false;
|
|
72
|
+
constructor(model) {
|
|
73
|
+
super();
|
|
74
|
+
this.model = model;
|
|
75
|
+
nodes.set(model, this);
|
|
76
|
+
this.#validityStateValidator = new ValidityStateValidator();
|
|
77
|
+
this.#validators = model[_validators];
|
|
78
|
+
if (this.constructor === BinderNode) {
|
|
79
|
+
this.initializeValue();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The binder for the top-level model.
|
|
84
|
+
*/
|
|
85
|
+
get binder() {
|
|
86
|
+
const binder = this.parent?.binder;
|
|
87
|
+
if (!binder) {
|
|
88
|
+
throw new TypeError("BinderNode is detached");
|
|
89
|
+
}
|
|
90
|
+
return binder;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The default value related to the model
|
|
94
|
+
*/
|
|
95
|
+
get defaultValue() {
|
|
96
|
+
const key = this.model[_key];
|
|
97
|
+
const parentDefaultValue = this.parent.defaultValue;
|
|
98
|
+
if (this.#isArrayItem() && !(key in parentDefaultValue)) {
|
|
99
|
+
if (defaultArrayItemCache.has(this.parent)) {
|
|
100
|
+
return defaultArrayItemCache.get(this.parent);
|
|
101
|
+
}
|
|
102
|
+
const value = this.model.constructor.createEmptyValue();
|
|
103
|
+
defaultArrayItemCache.set(this.parent, value);
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
return parentDefaultValue[key];
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* True if the current value is different from the defaultValue.
|
|
110
|
+
*/
|
|
111
|
+
get dirty() {
|
|
112
|
+
return this.value !== this.defaultValue;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The combined array of all errors for this node’s model and all its nested
|
|
116
|
+
* models
|
|
117
|
+
*/
|
|
118
|
+
get errors() {
|
|
119
|
+
return [...Array.from(this.#getChildBinderNodes(), (node) => node.errors).flat(), ...this.ownErrors];
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Indicates if there is any error for the node's model.
|
|
123
|
+
*/
|
|
124
|
+
get invalid() {
|
|
125
|
+
return this.errors.length > 0;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* The name generated from the model structure, used to set the name
|
|
129
|
+
* attribute on the field components.
|
|
130
|
+
*/
|
|
131
|
+
get name() {
|
|
132
|
+
let { model } = this;
|
|
133
|
+
let name = "";
|
|
134
|
+
while (model[_parent] instanceof AbstractModel) {
|
|
135
|
+
name = `${String(model[_key])}${name ? `.${name}` : ""}`;
|
|
136
|
+
model = model[_parent];
|
|
137
|
+
}
|
|
138
|
+
return name;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The array of validation errors directly related with the model.
|
|
142
|
+
*/
|
|
143
|
+
get ownErrors() {
|
|
144
|
+
return this.#ownErrors ? this.#ownErrors : [];
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* The parent node, if this binder node corresponds to a nested model,
|
|
148
|
+
* otherwise undefined for the top-level binder.
|
|
149
|
+
*/
|
|
150
|
+
get parent() {
|
|
151
|
+
const modelParent = this.model[_parent];
|
|
152
|
+
return modelParent instanceof AbstractModel ? getBinderNode(modelParent) : void 0;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* True if the value is required to be non-empty.
|
|
156
|
+
*/
|
|
157
|
+
get required() {
|
|
158
|
+
return this.#validators.some((validator) => validator.impliesRequired);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* The array of validators for the model. The default value is defined in the
|
|
162
|
+
* model.
|
|
163
|
+
*/
|
|
164
|
+
get validators() {
|
|
165
|
+
return this.#validators;
|
|
166
|
+
}
|
|
167
|
+
set validators(validators) {
|
|
168
|
+
this.#validators = validators;
|
|
169
|
+
this.dispatchEvent(CHANGED);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* The current value related to the model
|
|
173
|
+
*/
|
|
174
|
+
get value() {
|
|
175
|
+
if (!this.parent) {
|
|
176
|
+
return void 0;
|
|
177
|
+
}
|
|
178
|
+
let { value } = this.parent;
|
|
179
|
+
if (value === void 0) {
|
|
180
|
+
this.parent.initializeValue(true);
|
|
181
|
+
({ value } = this.parent);
|
|
182
|
+
}
|
|
183
|
+
const key = this.model[_key];
|
|
184
|
+
return value[key];
|
|
185
|
+
}
|
|
186
|
+
set value(value) {
|
|
187
|
+
this.initializeValue();
|
|
188
|
+
this.#setValueState(value, void 0);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* True if the bound field was ever focused and blurred by the user.
|
|
192
|
+
*/
|
|
193
|
+
get visited() {
|
|
194
|
+
return this.#visited;
|
|
195
|
+
}
|
|
196
|
+
set visited(v) {
|
|
197
|
+
if (this.#visited !== v) {
|
|
198
|
+
this.#visited = v;
|
|
199
|
+
this[_updateValidation]().catch(() => {
|
|
200
|
+
});
|
|
201
|
+
this.dispatchEvent(CHANGED);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* A helper method to add a validator
|
|
206
|
+
*
|
|
207
|
+
* @param validator - a validator
|
|
208
|
+
*/
|
|
209
|
+
addValidator(validator) {
|
|
210
|
+
this.validators = [...this.#validators, validator];
|
|
211
|
+
this.dispatchEvent(CHANGED);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Append an item to the array value.
|
|
215
|
+
*
|
|
216
|
+
* Requires the context model to be an array reference.
|
|
217
|
+
*
|
|
218
|
+
* @param item - optional new item value, an empty item is
|
|
219
|
+
* appended if the argument is omitted
|
|
220
|
+
*/
|
|
221
|
+
appendItem(item) {
|
|
222
|
+
if (this.#isArray()) {
|
|
223
|
+
const itemValueOrEmptyValue = item ?? this.model[_createEmptyItemValue]();
|
|
224
|
+
const newValue = [...this.value ?? [], itemValueOrEmptyValue];
|
|
225
|
+
const newDefaultValue = [...this.defaultValue ?? [], itemValueOrEmptyValue];
|
|
226
|
+
this.#setValueState(newValue, newDefaultValue);
|
|
227
|
+
} else {
|
|
228
|
+
throw new NotArrayModelError();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Returns a binder node for the nested model instance.
|
|
233
|
+
*
|
|
234
|
+
* @param model - The nested model instance
|
|
235
|
+
*/
|
|
236
|
+
for(model) {
|
|
237
|
+
const binderNode = getBinderNode(model);
|
|
238
|
+
if (binderNode.binder !== this.binder) {
|
|
239
|
+
throw new Error("Unknown binder");
|
|
240
|
+
}
|
|
241
|
+
return binderNode;
|
|
242
|
+
}
|
|
243
|
+
prependItem(item) {
|
|
244
|
+
if (this.#isArray()) {
|
|
245
|
+
const itemValueOrEmptyValue = item ?? this.model[_createEmptyItemValue]();
|
|
246
|
+
const newValue = [itemValueOrEmptyValue, ...this.value ?? []];
|
|
247
|
+
const newDefaultValue = [itemValueOrEmptyValue, ...this.defaultValue ?? []];
|
|
248
|
+
this.#setValueState(newValue, newDefaultValue);
|
|
249
|
+
} else {
|
|
250
|
+
throw new NotArrayModelError();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
removeSelf() {
|
|
254
|
+
if (this.#isArrayItem()) {
|
|
255
|
+
const newValue = (this.parent.value ?? []).filter((_, i) => i !== this.model[_key]);
|
|
256
|
+
const newDefaultValue = (this.parent.defaultValue ?? []).filter((_, i) => i !== this.model[_key]);
|
|
257
|
+
this.parent.#setValueState(newValue, newDefaultValue);
|
|
258
|
+
} else {
|
|
259
|
+
throw new NotArrayItemModelError();
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Runs all validation callbacks potentially affecting this
|
|
264
|
+
* or any nested model. Returns the combined array of all
|
|
265
|
+
* errors as in the errors property.
|
|
266
|
+
*/
|
|
267
|
+
async validate() {
|
|
268
|
+
const errors = await Promise.all([
|
|
269
|
+
...this.#requestValidationOfDescendants(),
|
|
270
|
+
...this.#requestValidationWithAncestors()
|
|
271
|
+
]).then((arr) => arr.flat());
|
|
272
|
+
this[_setErrorsWithDescendants](errors.length ? errors : void 0);
|
|
273
|
+
this[_update]();
|
|
274
|
+
return errors;
|
|
275
|
+
}
|
|
276
|
+
[_clearValidation]() {
|
|
277
|
+
if (this.#visited) {
|
|
278
|
+
this.#visited = false;
|
|
279
|
+
this.dispatchEvent(CHANGED);
|
|
280
|
+
}
|
|
281
|
+
let needsUpdate = false;
|
|
282
|
+
if (this.#ownErrors) {
|
|
283
|
+
this.#ownErrors = void 0;
|
|
284
|
+
needsUpdate = true;
|
|
285
|
+
this.dispatchEvent(CHANGED);
|
|
286
|
+
}
|
|
287
|
+
if ([...this.#getChildBinderNodes()].filter((childBinderNode) => childBinderNode[_clearValidation]()).length > 0) {
|
|
288
|
+
needsUpdate = true;
|
|
289
|
+
}
|
|
290
|
+
return needsUpdate;
|
|
291
|
+
}
|
|
292
|
+
[_setErrorsWithDescendants](errors) {
|
|
293
|
+
const { name } = this;
|
|
294
|
+
const ownErrors = errors ? errors.filter((valueError) => getErrorPropertyName(valueError) === name) : void 0;
|
|
295
|
+
const relatedErrors = errors ? errors.filter((valueError) => getErrorPropertyName(valueError).startsWith(name)) : void 0;
|
|
296
|
+
this.#ownErrors = ownErrors;
|
|
297
|
+
for (const childBinderNode of this.#getChildBinderNodes()) {
|
|
298
|
+
childBinderNode[_setErrorsWithDescendants](relatedErrors);
|
|
299
|
+
}
|
|
300
|
+
this.dispatchEvent(CHANGED);
|
|
301
|
+
}
|
|
302
|
+
[_update](_) {
|
|
303
|
+
if (this.parent) {
|
|
304
|
+
this.parent[_update]();
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
async [_updateValidation]() {
|
|
308
|
+
if (this.#visited) {
|
|
309
|
+
await this.validate();
|
|
310
|
+
} else if (this.dirty || this.invalid) {
|
|
311
|
+
await Promise.all(
|
|
312
|
+
[...this.#getChildBinderNodes()].map(async (childBinderNode) => childBinderNode[_updateValidation]())
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
*#getChildBinderNodes() {
|
|
317
|
+
if (this.value === void 0 || this.defaultValue === void 0) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (this.#isObject()) {
|
|
321
|
+
for (const [, getter] of getObjectModelOwnAndParentGetters(this.model)) {
|
|
322
|
+
const childModel = getter.call(this.model);
|
|
323
|
+
if (childModel[_key] in this.defaultValue) {
|
|
324
|
+
yield getBinderNode(childModel);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
} else if (this.#isArray()) {
|
|
328
|
+
for (const childBinderNode of this.model) {
|
|
329
|
+
yield childBinderNode;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
#isArray() {
|
|
334
|
+
return this.model instanceof ArrayModel;
|
|
335
|
+
}
|
|
336
|
+
#isArrayItem() {
|
|
337
|
+
return this.model[_parent] instanceof ArrayModel;
|
|
338
|
+
}
|
|
339
|
+
#isObject() {
|
|
340
|
+
return this.model instanceof ObjectModel;
|
|
341
|
+
}
|
|
342
|
+
*#requestValidationOfDescendants() {
|
|
343
|
+
for (const node of this.#getChildBinderNodes()) {
|
|
344
|
+
yield* node.#runOwnValidators();
|
|
345
|
+
yield* node.#requestValidationOfDescendants();
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
*#requestValidationWithAncestors() {
|
|
349
|
+
yield* this.#runOwnValidators();
|
|
350
|
+
if (this.parent) {
|
|
351
|
+
yield* this.parent.#requestValidationWithAncestors();
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
*#runOwnValidators() {
|
|
355
|
+
const hasInvalidState = this[_validity] && !this[_validity].valid;
|
|
356
|
+
const hasBadInput = !!this[_validity]?.badInput;
|
|
357
|
+
if (hasInvalidState && !hasBadInput || !hasInvalidState) {
|
|
358
|
+
for (const validator of this.#validators) {
|
|
359
|
+
yield this.binder.requestValidation(this.model, validator);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
if (hasInvalidState) {
|
|
363
|
+
yield this.binder.requestValidation(this.model, this.#validityStateValidator);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
initializeValue(forceInitialize = false) {
|
|
367
|
+
if (this.parent && (this.parent.value === void 0 || this.parent.defaultValue === void 0)) {
|
|
368
|
+
this.parent.initializeValue(true);
|
|
369
|
+
}
|
|
370
|
+
const key = this.model[_key];
|
|
371
|
+
let value = this.parent ? this.parent.value[this.model[_key]] : void 0;
|
|
372
|
+
const defaultValue = this.parent ? this.parent.defaultValue[this.model[_key]] : void 0;
|
|
373
|
+
if (value === void 0) {
|
|
374
|
+
if (forceInitialize || !this.parent) {
|
|
375
|
+
value = this.model.constructor.createEmptyValue();
|
|
376
|
+
this.#setValueState(value, defaultValue === void 0 ? value : defaultValue);
|
|
377
|
+
} else if (this.parent.model instanceof ObjectModel && !(key in (this.parent.value || {}))) {
|
|
378
|
+
this.#setValueState(void 0, defaultValue === void 0 ? value : defaultValue);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
#setValueState(value, defaultValue) {
|
|
383
|
+
const { parent } = this;
|
|
384
|
+
if (parent) {
|
|
385
|
+
const key = this.model[_key];
|
|
386
|
+
const parentValue = updateObjectOrArrayKey(parent.model, parent.value, key, value);
|
|
387
|
+
const keepPristine = value === defaultValue && parent.value === parent.defaultValue;
|
|
388
|
+
if (keepPristine) {
|
|
389
|
+
parent.#setValueState(parentValue, parentValue);
|
|
390
|
+
} else if (defaultValue !== void 0) {
|
|
391
|
+
const parentDefaultValue = updateObjectOrArrayKey(parent.model, parent.defaultValue, key, defaultValue);
|
|
392
|
+
parent.#setValueState(parentValue, parentDefaultValue);
|
|
393
|
+
} else {
|
|
394
|
+
parent.#setValueState(parentValue, void 0);
|
|
395
|
+
}
|
|
396
|
+
} else {
|
|
397
|
+
const binder = this;
|
|
398
|
+
if (defaultValue !== void 0) {
|
|
399
|
+
binder.defaultValue = defaultValue;
|
|
400
|
+
}
|
|
401
|
+
binder.value = value;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
export {
|
|
406
|
+
BinderNode,
|
|
407
|
+
CHANGED,
|
|
408
|
+
_clearValidation,
|
|
409
|
+
_setErrorsWithDescendants,
|
|
410
|
+
_update,
|
|
411
|
+
_updateValidation,
|
|
412
|
+
getBinderNode
|
|
413
|
+
};
|
|
414
|
+
//# sourceMappingURL=BinderNode.js.map
|