@platforma-sdk/ui-vue 1.4.9 → 1.5.38
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/CHANGELOG.md +13 -0
- package/dist/lib.js +5466 -5383
- package/dist/lib.umd.cjs +55 -55
- package/dist/src/createModel.d.ts.map +1 -1
- package/dist/src/internal/createApp.d.ts +15 -12
- package/dist/src/internal/createApp.d.ts.map +1 -1
- package/dist/src/internal/createAppModel.d.ts +4 -0
- package/dist/src/internal/createAppModel.d.ts.map +1 -0
- package/dist/src/utils.d.ts +5 -0
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/style.css +1 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/createModel.ts +4 -27
- package/src/internal/createApp.ts +73 -29
- package/src/internal/createAppModel.ts +100 -0
- package/src/types.static-test.ts +4 -1
- package/src/utils.ts +23 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platforma-sdk/ui-vue",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.38",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/lib.umd.cjs",
|
|
6
6
|
"module": "dist/lib.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"lru-cache": "^11.0.1",
|
|
22
22
|
"vue": "^3.5.11",
|
|
23
23
|
"canonicalize": "^2.0.0",
|
|
24
|
-
"@milaboratories/uikit": "^1.2.
|
|
24
|
+
"@milaboratories/uikit": "^1.2.17",
|
|
25
25
|
"@platforma-sdk/model": "^1.2.32"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
package/src/createModel.ts
CHANGED
|
@@ -1,30 +1,7 @@
|
|
|
1
1
|
import { reactive, computed, ref, watch, unref } from 'vue';
|
|
2
|
-
import type { ZodError } from 'zod';
|
|
3
2
|
import type { ModelOptions, Model } from './types';
|
|
4
3
|
import { deepClone } from '@milaboratories/helpers';
|
|
5
|
-
import { isJsonEqual } from './utils';
|
|
6
|
-
|
|
7
|
-
const identity = <T, V = T>(v: T): V => v as unknown as V;
|
|
8
|
-
|
|
9
|
-
const ensureError = (cause: unknown) => {
|
|
10
|
-
if (cause instanceof Error) {
|
|
11
|
-
return cause;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return Error(String(cause));
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const isZodError = (err: Error): err is ZodError => {
|
|
18
|
-
return err.name === 'ZodError';
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const formatZodError = (err: ZodError) => {
|
|
22
|
-
const { formErrors, fieldErrors } = err.flatten();
|
|
23
|
-
const _fieldErrors = Object.entries(fieldErrors).map(([field, errors]) => {
|
|
24
|
-
return field + ':' + errors?.join(',');
|
|
25
|
-
});
|
|
26
|
-
return formErrors.concat(_fieldErrors).join('; ');
|
|
27
|
-
};
|
|
4
|
+
import { isJsonEqual, identity, ensureError, isZodError, formatZodError } from './utils';
|
|
28
5
|
|
|
29
6
|
export function createModel<M, V = unknown>(options: ModelOptions<M, V>): Model<M> {
|
|
30
7
|
const validate = options.validate ?? identity;
|
|
@@ -89,9 +66,9 @@ export function createModel<M, V = unknown>(options: ModelOptions<M, V>): Model<
|
|
|
89
66
|
|
|
90
67
|
watch(
|
|
91
68
|
local,
|
|
92
|
-
(
|
|
93
|
-
if (
|
|
94
|
-
setValue(
|
|
69
|
+
(n, o) => {
|
|
70
|
+
if (n && n === o) {
|
|
71
|
+
setValue(n.model);
|
|
95
72
|
}
|
|
96
73
|
},
|
|
97
74
|
{ deep: true },
|
|
@@ -4,9 +4,9 @@ import type { NavigationState, BlockOutputsBase, BlockState, Platforma } from '@
|
|
|
4
4
|
import { reactive, nextTick, computed, watch } from 'vue';
|
|
5
5
|
import type { UnwrapValueOrErrors, StateModelOptions, UnwrapOutputs, OptionalResult, OutputValues, OutputErrors } from '../types';
|
|
6
6
|
import { createModel } from '../createModel';
|
|
7
|
+
import { createAppModel } from './createAppModel';
|
|
7
8
|
import { parseQuery } from '../urls';
|
|
8
9
|
import { MultiError, unwrapValueOrErrors } from '../utils';
|
|
9
|
-
import { pick } from 'lodash';
|
|
10
10
|
|
|
11
11
|
export function createApp<
|
|
12
12
|
Args = unknown,
|
|
@@ -101,22 +101,23 @@ export function createApp<
|
|
|
101
101
|
},
|
|
102
102
|
});
|
|
103
103
|
},
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
104
|
+
// @TODO currently disabled but will be used
|
|
105
|
+
// createAppModel<T extends AppModel = AppModel>(options: StateModelOptions<AppModel, T> = {}) {
|
|
106
|
+
// return createAppModel<T, AppModel>({
|
|
107
|
+
// get() {
|
|
108
|
+
// if (options.transform) {
|
|
109
|
+
// return options.transform(snapshot);
|
|
110
|
+
// }
|
|
110
111
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
},
|
|
112
|
+
// return { args: snapshot.args, ui: snapshot.ui } as T;
|
|
113
|
+
// },
|
|
114
|
+
// validate: options.validate,
|
|
115
|
+
// autoSave: true,
|
|
116
|
+
// onSave(newData) {
|
|
117
|
+
// setBlockArgsAndUiState(newData.args, newData.ui);
|
|
118
|
+
// },
|
|
119
|
+
// });
|
|
120
|
+
// },
|
|
120
121
|
/**
|
|
121
122
|
* Note: Don't forget to list the output names, like: useOutputs('output1', 'output2', ...etc)
|
|
122
123
|
* @param keys - List of output names
|
|
@@ -212,28 +213,71 @@ export function createApp<
|
|
|
212
213
|
},
|
|
213
214
|
};
|
|
214
215
|
|
|
216
|
+
const outputs = computed<OutputValues<Outputs>>(() => {
|
|
217
|
+
const entries = Object.entries(snapshot.outputs).map(([k, vOrErr]) => [k, vOrErr.ok && vOrErr.value !== undefined ? vOrErr.value : undefined]);
|
|
218
|
+
return Object.fromEntries(entries);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
const outputErrors = computed<OutputErrors<Outputs>>(() => {
|
|
222
|
+
const entries = Object.entries(snapshot.outputs).map(([k, vOrErr]) => [k, vOrErr && !vOrErr.ok ? new MultiError(vOrErr.errors) : undefined]);
|
|
223
|
+
return Object.fromEntries(entries);
|
|
224
|
+
});
|
|
225
|
+
|
|
215
226
|
const getters = {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
ui: computed(() => snapshot.ui),
|
|
219
|
-
navigationState: computed(() => snapshot.navigationState),
|
|
227
|
+
snapshot,
|
|
228
|
+
queryParams: computed(() => parseQuery<Href>(snapshot.navigationState.href)),
|
|
220
229
|
href: computed(() => snapshot.navigationState.href),
|
|
230
|
+
hasErrors: computed(() => Object.values(snapshot.outputs).some((v) => !v?.ok)), // @TODO: there is middle-layer error, v sometimes is undefined
|
|
221
231
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
232
|
+
/** @deprecated */
|
|
233
|
+
outputValues: computed(() => {
|
|
234
|
+
console.warn('Change app.outputValues to app.model.outputs');
|
|
235
|
+
return outputs.value;
|
|
225
236
|
}),
|
|
226
237
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
238
|
+
/** @deprecated */
|
|
239
|
+
outputErrors: computed(() => {
|
|
240
|
+
console.warn('Change app.outputErrors to app.model.outputErrors');
|
|
241
|
+
return outputErrors.value;
|
|
230
242
|
}),
|
|
231
243
|
|
|
232
|
-
|
|
233
|
-
|
|
244
|
+
/** @deprecated */
|
|
245
|
+
args: computed(() => {
|
|
246
|
+
console.warn('Change app.args to app.snapshot.args');
|
|
247
|
+
return snapshot.args;
|
|
248
|
+
}),
|
|
249
|
+
/** @deprecated */
|
|
250
|
+
outputs: computed(() => {
|
|
251
|
+
console.warn('Change app.outputs to app.snapshot.outputs');
|
|
252
|
+
return snapshot.outputs;
|
|
253
|
+
}),
|
|
254
|
+
/** @deprecated */
|
|
255
|
+
ui: computed(() => {
|
|
256
|
+
console.warn('Change app.ui to app.snapshot.ui');
|
|
257
|
+
return snapshot.ui;
|
|
258
|
+
}),
|
|
259
|
+
/** @deprecated */
|
|
260
|
+
navigationState: computed(() => {
|
|
261
|
+
console.warn('Change app.navigationState to app.snapshot.navigationState');
|
|
262
|
+
return snapshot.navigationState;
|
|
263
|
+
}),
|
|
234
264
|
};
|
|
235
265
|
|
|
236
|
-
const appModel =
|
|
266
|
+
const appModel = createAppModel(
|
|
267
|
+
{
|
|
268
|
+
get() {
|
|
269
|
+
return { args: snapshot.args, ui: snapshot.ui } as AppModel;
|
|
270
|
+
},
|
|
271
|
+
autoSave: true,
|
|
272
|
+
onSave(newData: AppModel) {
|
|
273
|
+
setBlockArgsAndUiState(newData.args, newData.ui);
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
outputs,
|
|
278
|
+
outputErrors,
|
|
279
|
+
},
|
|
280
|
+
);
|
|
237
281
|
|
|
238
282
|
return reactive(Object.assign(appModel, methods, getters));
|
|
239
283
|
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { reactive, computed, ref, watch, unref, type ComputedRef, type UnwrapNestedRefs } from 'vue';
|
|
2
|
+
import type { ModelOptions, Model } from '../types';
|
|
3
|
+
import { deepClone } from '@milaboratories/helpers';
|
|
4
|
+
import { isJsonEqual, identity, ensureError, isZodError, formatZodError } from '../utils';
|
|
5
|
+
|
|
6
|
+
export function createAppModel<
|
|
7
|
+
M extends Record<string, unknown>,
|
|
8
|
+
V = unknown,
|
|
9
|
+
E extends Record<string, ComputedRef<unknown>> = Record<string, ComputedRef<unknown>>,
|
|
10
|
+
>(options: ModelOptions<M, V>, extended?: E): Model<M & UnwrapNestedRefs<E>> {
|
|
11
|
+
type R = M & UnwrapNestedRefs<E>;
|
|
12
|
+
|
|
13
|
+
const validate = options.validate ?? identity;
|
|
14
|
+
|
|
15
|
+
const { autoSave } = options;
|
|
16
|
+
|
|
17
|
+
const error = ref<Error | undefined>();
|
|
18
|
+
|
|
19
|
+
const local = ref<{ model: R }>();
|
|
20
|
+
|
|
21
|
+
const setSource = (v: M) => {
|
|
22
|
+
local.value = {
|
|
23
|
+
model: Object.assign(deepClone(v), extended ?? {}) as R,
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
watch(
|
|
28
|
+
() => options.get(),
|
|
29
|
+
(v) => setSource(v),
|
|
30
|
+
{ immediate: true },
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const save = () => {
|
|
34
|
+
options.onSave(validate(deepClone(local.value?.model)));
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const revert = () => {
|
|
38
|
+
setSource(options.get());
|
|
39
|
+
error.value = undefined;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const setError = (cause: unknown) => {
|
|
43
|
+
const err = ensureError(cause);
|
|
44
|
+
if (isZodError(err)) {
|
|
45
|
+
error.value = Error(formatZodError(err)); // @todo temp
|
|
46
|
+
} else {
|
|
47
|
+
error.value = err;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const setValue = (v: M) => {
|
|
52
|
+
error.value = undefined;
|
|
53
|
+
try {
|
|
54
|
+
validate(v);
|
|
55
|
+
if (autoSave) {
|
|
56
|
+
save();
|
|
57
|
+
}
|
|
58
|
+
} catch (cause: unknown) {
|
|
59
|
+
setError(cause);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const model = computed<R>({
|
|
64
|
+
get: () => {
|
|
65
|
+
return local.value?.model as R;
|
|
66
|
+
},
|
|
67
|
+
set() {
|
|
68
|
+
throw Error('Cannot replace base model');
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
watch(
|
|
73
|
+
local,
|
|
74
|
+
(n, o) => {
|
|
75
|
+
if (n && n === o) {
|
|
76
|
+
setValue(n.model);
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
{ deep: true },
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const valid = computed(() => !error.value);
|
|
83
|
+
|
|
84
|
+
const isChanged = computed(() => {
|
|
85
|
+
return !isJsonEqual(options.get(), unref(local));
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const errorString = computed(() => (error.value ? error.value.message : ''));
|
|
89
|
+
|
|
90
|
+
return reactive({
|
|
91
|
+
model,
|
|
92
|
+
valid,
|
|
93
|
+
isChanged,
|
|
94
|
+
error,
|
|
95
|
+
errorString,
|
|
96
|
+
save,
|
|
97
|
+
revert,
|
|
98
|
+
setError,
|
|
99
|
+
});
|
|
100
|
+
}
|
package/src/types.static-test.ts
CHANGED
|
@@ -85,6 +85,8 @@ declare const _app: ExtApp;
|
|
|
85
85
|
|
|
86
86
|
type _UpdateArgsParams = Parameters<Parameters<_App1['updateArgs']>[0]>[0];
|
|
87
87
|
|
|
88
|
+
type _ccc = _App1['model']['args'];
|
|
89
|
+
|
|
88
90
|
type __cases = [
|
|
89
91
|
Expect<Equal<Model<string>, typeof __model1>>,
|
|
90
92
|
Expect<Equal<Model<number>, typeof __model2>>,
|
|
@@ -95,7 +97,8 @@ type __cases = [
|
|
|
95
97
|
Expect<Equal<ExtApp['label'], string>>,
|
|
96
98
|
Expect<Equal<ExtApp['method'], () => number>>,
|
|
97
99
|
Expect<Equal<_App1['args'], Readonly<_Args>>>,
|
|
98
|
-
Expect<Equal<_App1['
|
|
100
|
+
Expect<Equal<_App1['model']['outputs']['sum'], number | undefined>>,
|
|
101
|
+
Expect<Equal<_App1['model']['outputErrors']['sum'], Error | undefined>>,
|
|
99
102
|
Expect<Equal<_App1['ui'], Readonly<_UiState>>>,
|
|
100
103
|
Expect<Equal<_App1['model']['args'], _Args>>,
|
|
101
104
|
Expect<Equal<_App1['model']['ui'], _UiState>>,
|
package/src/utils.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ValueOrErrors } from '@platforma-sdk/model';
|
|
2
2
|
import type { OptionalResult } from './types';
|
|
3
|
+
import type { ZodError } from 'zod';
|
|
3
4
|
import canonicalize from 'canonicalize';
|
|
4
5
|
|
|
5
6
|
export class UnresolvedError extends Error {}
|
|
@@ -62,3 +63,25 @@ export function isDefined<T>(v: T | undefined): v is T {
|
|
|
62
63
|
export function isJsonEqual(a: unknown, b: unknown) {
|
|
63
64
|
return canonicalize(a) === canonicalize(b);
|
|
64
65
|
}
|
|
66
|
+
|
|
67
|
+
export const identity = <T, V = T>(v: T): V => v as unknown as V;
|
|
68
|
+
|
|
69
|
+
export const ensureError = (cause: unknown) => {
|
|
70
|
+
if (cause instanceof Error) {
|
|
71
|
+
return cause;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return Error(String(cause));
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const isZodError = (err: Error): err is ZodError => {
|
|
78
|
+
return err.name === 'ZodError';
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const formatZodError = (err: ZodError) => {
|
|
82
|
+
const { formErrors, fieldErrors } = err.flatten();
|
|
83
|
+
const _fieldErrors = Object.entries(fieldErrors).map(([field, errors]) => {
|
|
84
|
+
return field + ':' + errors?.join(',');
|
|
85
|
+
});
|
|
86
|
+
return formErrors.concat(_fieldErrors).join('; ');
|
|
87
|
+
};
|