@goodie-forms/core 1.1.5-alpha → 1.2.0-alpha
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/LICENSE +426 -426
- package/dist/field/FieldPath.d.ts +32 -0
- package/dist/field/FieldPath.d.ts.map +1 -0
- package/dist/field/FieldPathBuilder.d.ts +25 -0
- package/dist/field/FieldPathBuilder.d.ts.map +1 -0
- package/dist/field/Reconcile.d.ts +9 -0
- package/dist/field/Reconcile.d.ts.map +1 -0
- package/dist/form/FormController.d.ts +35 -39
- package/dist/form/FormController.d.ts.map +1 -1
- package/dist/form/FormField.d.ts +12 -10
- package/dist/form/FormField.d.ts.map +1 -1
- package/dist/form/NonullFormField.d.ts +3 -4
- package/dist/form/NonullFormField.d.ts.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +615 -881
- package/dist/index.js.map +1 -1
- package/dist/validation/CustomValidation.d.ts +10 -0
- package/dist/validation/CustomValidation.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/field/FieldPath.ts +292 -0
- package/src/field/FieldPathBuilder.ts +132 -0
- package/src/field/Reconcile.ts +99 -0
- package/src/form/FormController.ts +342 -310
- package/src/form/FormField.ts +200 -202
- package/src/form/NonullFormField.ts +15 -21
- package/src/index.ts +12 -5
- package/src/types/DeepPartial.ts +7 -7
- package/src/types/Mixin.ts +2 -2
- package/src/utils/ensureImmerability.ts +30 -30
- package/src/utils/getId.ts +5 -5
- package/src/utils/removeBy.ts +11 -11
- package/src/{form → validation}/CustomValidation.ts +52 -52
- package/tsconfig.json +8 -7
- package/vite.config.ts +18 -18
- package/dist/form/CustomValidation.d.ts +0 -10
- package/dist/form/CustomValidation.d.ts.map +0 -1
- package/dist/form/Field.d.ts +0 -31
- package/dist/form/Field.d.ts.map +0 -1
- package/src/form/Field.ts +0 -334
package/src/form/FormField.ts
CHANGED
|
@@ -1,202 +1,200 @@
|
|
|
1
|
-
import { produce } from "immer";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
protected
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
get boundElement() {
|
|
45
|
-
return this.target;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
get issues() {
|
|
49
|
-
return this.controller._issues.filter(
|
|
50
|
-
(
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
get isTouched() {
|
|
55
|
-
return this._isTouched;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
get isDirty() {
|
|
59
|
-
return this._isDirty;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
get isValid() {
|
|
63
|
-
return this.issues.length === 0;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
protected _setTouched(isTouched: boolean) {
|
|
67
|
-
const changed = this._isTouched !== isTouched;
|
|
68
|
-
this._isTouched = isTouched;
|
|
69
|
-
|
|
70
|
-
if (changed) {
|
|
71
|
-
const ascendantFields = this.controller.getAscendantFields(this.path);
|
|
72
|
-
for (let i = ascendantFields.length - 1; i >= 0; i--) {
|
|
73
|
-
const field = ascendantFields[i];
|
|
74
|
-
if (field == null) continue;
|
|
75
|
-
this.controller.events.emit("fieldTouchUpdated", field.path);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
protected _setDirty(isDirty: boolean) {
|
|
81
|
-
const changed = this._isDirty !== isDirty;
|
|
82
|
-
this._isDirty = isDirty;
|
|
83
|
-
|
|
84
|
-
if (changed) {
|
|
85
|
-
const ascendantFields = this.controller.getAscendantFields(this.path);
|
|
86
|
-
for (let i = ascendantFields.length - 1; i >= 0; i--) {
|
|
87
|
-
const field = ascendantFields[i];
|
|
88
|
-
if (field == null) continue;
|
|
89
|
-
this.controller.events.emit("fieldDirtyUpdated", field.path);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
bindElement(el: HTMLElement | undefined) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
) {
|
|
104
|
-
return this.modifyValue(() => value, opts);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
modifyValue(
|
|
108
|
-
modifier: (
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
202
|
-
}
|
|
1
|
+
import { produce } from "immer";
|
|
2
|
+
import { FieldPath } from "../field/FieldPath";
|
|
3
|
+
import { Reconsile } from "../field/Reconcile";
|
|
4
|
+
import { FormController } from "../form/FormController";
|
|
5
|
+
import { ensureImmerability } from "../utils/ensureImmerability";
|
|
6
|
+
import { getId } from "../utils/getId";
|
|
7
|
+
|
|
8
|
+
export class FormField<TOutput extends object, TValue> {
|
|
9
|
+
public readonly id = getId();
|
|
10
|
+
|
|
11
|
+
protected target?: HTMLElement;
|
|
12
|
+
|
|
13
|
+
protected _isTouched = false;
|
|
14
|
+
protected _isDirty = false;
|
|
15
|
+
|
|
16
|
+
constructor(
|
|
17
|
+
public readonly controller: FormController<TOutput>,
|
|
18
|
+
public readonly path: FieldPath.Segments,
|
|
19
|
+
initialState?: {
|
|
20
|
+
isTouched?: boolean;
|
|
21
|
+
isDirty?: boolean;
|
|
22
|
+
},
|
|
23
|
+
) {
|
|
24
|
+
if (initialState?.isTouched) this._setTouched(true);
|
|
25
|
+
if (initialState?.isDirty) this._setDirty(true);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get canonicalPath() {
|
|
29
|
+
return FieldPath.toCanonicalPath(this.path);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get stringPath() {
|
|
33
|
+
return FieldPath.toStringPath(this.path);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get value(): TValue | undefined {
|
|
37
|
+
return FieldPath.getValue(this.controller._data, this.path);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get initialValue(): TValue | undefined {
|
|
41
|
+
return FieldPath.getValue(this.controller._initialData, this.path);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get boundElement() {
|
|
45
|
+
return this.target;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get issues() {
|
|
49
|
+
return this.controller._issues.filter((issue) =>
|
|
50
|
+
FieldPath.equals(FieldPath.normalize(issue.path), this.path),
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get isTouched() {
|
|
55
|
+
return this._isTouched;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get isDirty() {
|
|
59
|
+
return this._isDirty;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get isValid() {
|
|
63
|
+
return this.issues.length === 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
protected _setTouched(isTouched: boolean) {
|
|
67
|
+
const changed = this._isTouched !== isTouched;
|
|
68
|
+
this._isTouched = isTouched;
|
|
69
|
+
|
|
70
|
+
if (changed) {
|
|
71
|
+
const ascendantFields = this.controller.getAscendantFields(this.path);
|
|
72
|
+
for (let i = ascendantFields.length - 1; i >= 0; i--) {
|
|
73
|
+
const field = ascendantFields[i];
|
|
74
|
+
if (field == null) continue;
|
|
75
|
+
this.controller.events.emit("fieldTouchUpdated", field.path);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
protected _setDirty(isDirty: boolean) {
|
|
81
|
+
const changed = this._isDirty !== isDirty;
|
|
82
|
+
this._isDirty = isDirty;
|
|
83
|
+
|
|
84
|
+
if (changed) {
|
|
85
|
+
const ascendantFields = this.controller.getAscendantFields(this.path);
|
|
86
|
+
for (let i = ascendantFields.length - 1; i >= 0; i--) {
|
|
87
|
+
const field = ascendantFields[i];
|
|
88
|
+
if (field == null) continue;
|
|
89
|
+
this.controller.events.emit("fieldDirtyUpdated", field.path);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
bindElement(el: HTMLElement | undefined) {
|
|
95
|
+
this.target = el;
|
|
96
|
+
if (el != null) {
|
|
97
|
+
this.controller.events.emit("elementBound", this.path, el);
|
|
98
|
+
} else {
|
|
99
|
+
this.controller.events.emit("elementUnbound", this.path);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
setValue(value: TValue, opts?: Parameters<typeof this.modifyValue>[1]) {
|
|
104
|
+
return this.modifyValue(() => value, opts);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
modifyValue(
|
|
108
|
+
modifier: (currentValue: TValue | undefined) => TValue | void,
|
|
109
|
+
opts?: {
|
|
110
|
+
shouldTouch?: boolean;
|
|
111
|
+
shouldMarkDirty?: boolean;
|
|
112
|
+
},
|
|
113
|
+
): void {
|
|
114
|
+
if (opts?.shouldTouch == null || opts?.shouldTouch) {
|
|
115
|
+
this.touch();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const ascendantFields = this.controller.getAscendantFields(this.path);
|
|
119
|
+
|
|
120
|
+
const initialValues = ascendantFields.map((field) => field?.initialValue);
|
|
121
|
+
initialValues.forEach((v) => ensureImmerability(v));
|
|
122
|
+
|
|
123
|
+
const oldValues = ascendantFields.map((field) => field?.value);
|
|
124
|
+
oldValues.forEach((v) => ensureImmerability(v));
|
|
125
|
+
|
|
126
|
+
this.controller._data = produce(this.controller._data, (draft) => {
|
|
127
|
+
FieldPath.modifyValue(draft as TOutput, this.path, (oldValue) => {
|
|
128
|
+
return modifier(oldValue);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const newValues = ascendantFields.map((field) => field?.value);
|
|
133
|
+
newValues.forEach((v) => ensureImmerability(v));
|
|
134
|
+
|
|
135
|
+
const compareCustom = (a: any, b: any) => {
|
|
136
|
+
if (typeof a !== "object") return;
|
|
137
|
+
if (typeof b !== "object") return;
|
|
138
|
+
const ctorA = a.constructor;
|
|
139
|
+
const ctorB = b.constructor;
|
|
140
|
+
if (ctorA !== ctorB) return;
|
|
141
|
+
return this.controller.equalityComparators?.[ctorA]?.(a, b);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const valueChanged = !Reconsile.deepEqual(
|
|
145
|
+
oldValues[oldValues.length - 1],
|
|
146
|
+
newValues[newValues.length - 1],
|
|
147
|
+
compareCustom,
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
if (valueChanged) {
|
|
151
|
+
for (let i = ascendantFields.length - 1; i >= 0; i--) {
|
|
152
|
+
const field = ascendantFields[i];
|
|
153
|
+
this.controller.events.emit(
|
|
154
|
+
"valueChanged",
|
|
155
|
+
field.path,
|
|
156
|
+
newValues[i],
|
|
157
|
+
oldValues[i],
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (opts?.shouldMarkDirty == null || opts?.shouldMarkDirty) {
|
|
163
|
+
const gotDirty = !Reconsile.deepEqual(
|
|
164
|
+
initialValues[initialValues.length - 1],
|
|
165
|
+
newValues[newValues.length - 1],
|
|
166
|
+
compareCustom,
|
|
167
|
+
);
|
|
168
|
+
this._setDirty(gotDirty);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
reset() {
|
|
173
|
+
this._setTouched(false);
|
|
174
|
+
this._setDirty(false);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
touch() {
|
|
178
|
+
this._setTouched(true);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
markDirty() {
|
|
182
|
+
this.touch();
|
|
183
|
+
this._setDirty(true);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
triggerValidation() {
|
|
187
|
+
this.controller.validateField(this.path);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
focus(opts?: { shouldTouch?: boolean }) {
|
|
191
|
+
if (opts?.shouldTouch == null || opts.shouldTouch) {
|
|
192
|
+
this.target?.addEventListener("focus", () => this.touch(), {
|
|
193
|
+
once: true,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
this.target?.scrollIntoView();
|
|
198
|
+
this.target?.focus();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -1,21 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
shouldTouch?: boolean;
|
|
17
|
-
shouldMarkDirty?: boolean;
|
|
18
|
-
},
|
|
19
|
-
) => void;
|
|
20
|
-
}
|
|
21
|
-
>;
|
|
1
|
+
import { FormField } from "./FormField";
|
|
2
|
+
import { Mixin } from "../types/Mixin";
|
|
3
|
+
|
|
4
|
+
export type NonnullFormField<TOutput extends object, TValue> = Mixin<
|
|
5
|
+
FormField<TOutput, TValue>,
|
|
6
|
+
{
|
|
7
|
+
modifyValue: (
|
|
8
|
+
modifier: (currentValue: TValue) => TValue | void,
|
|
9
|
+
opts?: {
|
|
10
|
+
shouldTouch?: boolean;
|
|
11
|
+
shouldMarkDirty?: boolean;
|
|
12
|
+
},
|
|
13
|
+
) => void;
|
|
14
|
+
}
|
|
15
|
+
>;
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
export * from "./
|
|
2
|
-
export * from "./
|
|
3
|
-
export * from "./
|
|
4
|
-
|
|
5
|
-
export * from "./form/
|
|
1
|
+
export * from "./field/FieldPath";
|
|
2
|
+
export * from "./field/FieldPathBuilder";
|
|
3
|
+
export * from "./field/Reconcile";
|
|
4
|
+
|
|
5
|
+
export * from "./form/FormController";
|
|
6
|
+
export * from "./form/FormField";
|
|
7
|
+
export * from "./form/NonullFormField";
|
|
8
|
+
|
|
9
|
+
export * from "./validation/CustomValidation";
|
|
10
|
+
|
|
11
|
+
export * from "./types/DeepPartial";
|
|
12
|
+
export * from "./types/Mixin";
|
package/src/types/DeepPartial.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export type DeepPartial<T> = {
|
|
2
|
-
[K in keyof T as T[K] extends (...args: any[]) => any ? K : never]: T[K];
|
|
3
|
-
} & {
|
|
4
|
-
[K in keyof T as T[K] extends (...args: any[]) => any
|
|
5
|
-
? never
|
|
6
|
-
: K]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
|
|
7
|
-
};
|
|
1
|
+
export type DeepPartial<T> = {
|
|
2
|
+
[K in keyof T as T[K] extends (...args: any[]) => any ? K : never]: T[K];
|
|
3
|
+
} & {
|
|
4
|
+
[K in keyof T as T[K] extends (...args: any[]) => any
|
|
5
|
+
? never
|
|
6
|
+
: K]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
|
|
7
|
+
};
|
package/src/types/Mixin.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type Mixin<T extends object, U extends Partial<T>> = Omit<T, keyof U> &
|
|
2
|
-
U;
|
|
1
|
+
export type Mixin<T extends object, U extends Partial<T>> = Omit<T, keyof U> &
|
|
2
|
+
U;
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import { immerable } from "immer";
|
|
2
|
-
|
|
3
|
-
export function ensureImmerability(value: any) {
|
|
4
|
-
if (typeof value !== "object" || value === null) return;
|
|
5
|
-
|
|
6
|
-
// Skip plain objects
|
|
7
|
-
const proto = Object.getPrototypeOf(value);
|
|
8
|
-
if (proto === Object.prototype || proto === null) return;
|
|
9
|
-
|
|
10
|
-
const ctor = proto.constructor;
|
|
11
|
-
if (typeof ctor !== "function") return;
|
|
12
|
-
|
|
13
|
-
// Skip known built-ins
|
|
14
|
-
if (
|
|
15
|
-
value instanceof Date ||
|
|
16
|
-
value instanceof RegExp ||
|
|
17
|
-
value instanceof Map ||
|
|
18
|
-
value instanceof Set ||
|
|
19
|
-
value instanceof WeakMap ||
|
|
20
|
-
value instanceof WeakSet ||
|
|
21
|
-
ArrayBuffer.isView(value)
|
|
22
|
-
) {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (ctor[immerable] === true) return;
|
|
27
|
-
|
|
28
|
-
// Define non-enumerable immerable flag
|
|
29
|
-
ctor[immerable] = true;
|
|
30
|
-
}
|
|
1
|
+
import { immerable } from "immer";
|
|
2
|
+
|
|
3
|
+
export function ensureImmerability(value: any) {
|
|
4
|
+
if (typeof value !== "object" || value === null) return;
|
|
5
|
+
|
|
6
|
+
// Skip plain objects
|
|
7
|
+
const proto = Object.getPrototypeOf(value);
|
|
8
|
+
if (proto === Object.prototype || proto === null) return;
|
|
9
|
+
|
|
10
|
+
const ctor = proto.constructor;
|
|
11
|
+
if (typeof ctor !== "function") return;
|
|
12
|
+
|
|
13
|
+
// Skip known built-ins
|
|
14
|
+
if (
|
|
15
|
+
value instanceof Date ||
|
|
16
|
+
value instanceof RegExp ||
|
|
17
|
+
value instanceof Map ||
|
|
18
|
+
value instanceof Set ||
|
|
19
|
+
value instanceof WeakMap ||
|
|
20
|
+
value instanceof WeakSet ||
|
|
21
|
+
ArrayBuffer.isView(value)
|
|
22
|
+
) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (ctor[immerable] === true) return;
|
|
27
|
+
|
|
28
|
+
// Define non-enumerable immerable flag
|
|
29
|
+
ctor[immerable] = true;
|
|
30
|
+
}
|
package/src/utils/getId.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
let id = 0;
|
|
2
|
-
|
|
3
|
-
export function getId() {
|
|
4
|
-
return id++;
|
|
5
|
-
}
|
|
1
|
+
let id = 0;
|
|
2
|
+
|
|
3
|
+
export function getId() {
|
|
4
|
+
return id++;
|
|
5
|
+
}
|
package/src/utils/removeBy.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export function removeBy<T>(arr: T[], predicate: (item: T) => boolean) {
|
|
2
|
-
let indices: number[] = [];
|
|
3
|
-
|
|
4
|
-
for (let i = arr.length - 1; i >= 0; i--) {
|
|
5
|
-
if (predicate(arr[i])) {
|
|
6
|
-
indices.push(i);
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
indices.forEach((i) => arr.splice(i, 1));
|
|
11
|
-
}
|
|
1
|
+
export function removeBy<T>(arr: T[], predicate: (item: T) => boolean) {
|
|
2
|
+
let indices: number[] = [];
|
|
3
|
+
|
|
4
|
+
for (let i = arr.length - 1; i >= 0; i--) {
|
|
5
|
+
if (predicate(arr[i])) {
|
|
6
|
+
indices.push(i);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
indices.forEach((i) => arr.splice(i, 1));
|
|
11
|
+
}
|