@retailcrm/embed-ui 0.1.0 → 0.2.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/.versionrc.json +12 -0
- package/CHANGELOG.md +24 -0
- package/Makefile +48 -0
- package/dist/index.cjs +218 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.mjs +218 -0
- package/dist/meta.json +246 -0
- package/package.json +42 -17
- package/types/context/customer/card-phone.d.ts +6 -0
- package/types/context/customer/card.d.ts +8 -0
- package/types/context/index.d.ts +11 -0
- package/types/context/order/card.d.ts +7 -0
- package/types/context/schema.d.ts +85 -0
- package/types/context/settings.d.ts +9 -0
- package/types/scaffolding.d.ts +6 -0
- package/types/widget.d.ts +56 -0
- package/.github/workflows/tests.yml +0 -39
- package/types/endpoint.d.ts +0 -18
- package/types/pages/order.d.ts +0 -11
package/.versionrc.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"types": [
|
|
3
|
+
{"type": "feat", "section": "Features"},
|
|
4
|
+
{"type": "fix", "section": "Fixes"},
|
|
5
|
+
{"type": "chore", "hidden": true},
|
|
6
|
+
{"type": "docs", "hidden": true},
|
|
7
|
+
{"type": "style", "hidden": true},
|
|
8
|
+
{"type": "refactor", "hidden": true},
|
|
9
|
+
{"type": "perf", "hidden": true},
|
|
10
|
+
{"type": "test", "hidden": true}
|
|
11
|
+
]
|
|
12
|
+
}
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [0.2.1](https://github.com/retailcrm/embed-ui/compare/v0.2.0...v0.2.1) (2024-11-04)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* Added CrmYandexMap to known components ([90621b0](https://github.com/retailcrm/embed-ui/commit/90621b079465970b558d783d5e65bb01aef54586))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Fixes
|
|
14
|
+
|
|
15
|
+
* ContextAccessor getter return type ([5a98241](https://github.com/retailcrm/embed-ui/commit/5a98241fd230ef0341f4bb8b935d8cf33ae8db11))
|
|
16
|
+
|
|
17
|
+
## [0.2.0](https://github.com/retailcrm/embed-ui/compare/v0.1.0...v0.2.0) (2024-11-02)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### ⚠ BREAKING CHANGES
|
|
21
|
+
|
|
22
|
+
* Removed all types from types/endpoint.d.ts, old ones were replaced with types from types/widget.d.ts
|
|
23
|
+
* Removed previous pages API, which was replaced with reactive context API
|
|
24
|
+
|
|
25
|
+
### Features
|
|
26
|
+
|
|
27
|
+
* Scaffolding logic for widgets, reactive context, json metadata for creating documentation ([9ddd8f8](https://github.com/retailcrm/embed-ui/commit/9ddd8f89759fbeb964556a1401c8f23af6c51467))
|
|
28
|
+
|
|
5
29
|
## [0.1.0](https://github.com/retailcrm/embed-ui/compare/v0.0.1...v0.1.0) (2024-04-27)
|
|
6
30
|
|
|
7
31
|
|
package/Makefile
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
TARGET_HEADER=@echo -e '===== \e[34m' $@ '\e[0m'
|
|
2
|
+
YARN=docker-compose run --rm node yarn
|
|
3
|
+
|
|
4
|
+
.PHONY: node_modules
|
|
5
|
+
node_modules: package.json yarn.lock ## Installs dependencies
|
|
6
|
+
$(TARGET_HEADER)
|
|
7
|
+
@docker-compose run --rm node yarn install --silent
|
|
8
|
+
@touch node_modules || true
|
|
9
|
+
|
|
10
|
+
.PHONY: build
|
|
11
|
+
build: ## Builds the package
|
|
12
|
+
$(TARGET_HEADER)
|
|
13
|
+
$(YARN) build
|
|
14
|
+
$(YARN) generate:meta
|
|
15
|
+
|
|
16
|
+
.PHONY: release
|
|
17
|
+
release: ## Bumps version and creates tag
|
|
18
|
+
$(TARGET_HEADER)
|
|
19
|
+
ifdef as
|
|
20
|
+
$(YARN) release:$(as)
|
|
21
|
+
else
|
|
22
|
+
$(YARN) release
|
|
23
|
+
endif
|
|
24
|
+
|
|
25
|
+
.PHONY: tests
|
|
26
|
+
tests: ## Runs autotests
|
|
27
|
+
$(TARGET_HEADER)
|
|
28
|
+
ifdef cli
|
|
29
|
+
$(YARN) test $(cli) --passWithNoTests
|
|
30
|
+
else
|
|
31
|
+
$(YARN) test
|
|
32
|
+
endif
|
|
33
|
+
|
|
34
|
+
.PHONY: help
|
|
35
|
+
help: ## Calls recipes list
|
|
36
|
+
@cat $(MAKEFILE_LIST) | grep -e "^[a-zA-Z_\-]*: *.*## *" | awk '\
|
|
37
|
+
BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
|
38
|
+
|
|
39
|
+
# Colors
|
|
40
|
+
$(call computable,CC_BLACK,$(shell tput -Txterm setaf 0 2>/dev/null))
|
|
41
|
+
$(call computable,CC_RED,$(shell tput -Txterm setaf 1 2>/dev/null))
|
|
42
|
+
$(call computable,CC_GREEN,$(shell tput -Txterm setaf 2 2>/dev/null))
|
|
43
|
+
$(call computable,CC_YELLOW,$(shell tput -Txterm setaf 3 2>/dev/null))
|
|
44
|
+
$(call computable,CC_BLUE,$(shell tput -Txterm setaf 4 2>/dev/null))
|
|
45
|
+
$(call computable,CC_MAGENTA,$(shell tput -Txterm setaf 5 2>/dev/null))
|
|
46
|
+
$(call computable,CC_CYAN,$(shell tput -Txterm setaf 6 2>/dev/null))
|
|
47
|
+
$(call computable,CC_WHITE,$(shell tput -Txterm setaf 7 2>/dev/null))
|
|
48
|
+
$(call computable,CC_END,$(shell tput -Txterm sgr0 2>/dev/null))
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const rpc = require("@remote-ui/rpc");
|
|
4
|
+
const pinia = require("pinia");
|
|
5
|
+
const remote = require("@omnicajs/vue-remote/remote");
|
|
6
|
+
const vue = require("vue");
|
|
7
|
+
const keysOf = (o) => Object.keys(o);
|
|
8
|
+
const injectAccessor = (endpoint) => {
|
|
9
|
+
return (context) => {
|
|
10
|
+
context.store.endpoint = endpoint;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
const defineContext = (id, schema2) => {
|
|
14
|
+
return pinia.defineStore(id, {
|
|
15
|
+
state() {
|
|
16
|
+
return {
|
|
17
|
+
...keysOf(schema2).reduce((state, field) => ({
|
|
18
|
+
...state,
|
|
19
|
+
[field]: schema2[field].defaults()
|
|
20
|
+
}), {})
|
|
21
|
+
};
|
|
22
|
+
},
|
|
23
|
+
getters: {
|
|
24
|
+
schema: () => schema2
|
|
25
|
+
},
|
|
26
|
+
actions: {
|
|
27
|
+
async initialize() {
|
|
28
|
+
const context = this;
|
|
29
|
+
const endpoint = this.endpoint;
|
|
30
|
+
const state = await endpoint.call.get(id, "~");
|
|
31
|
+
keysOf(schema2).forEach((field) => {
|
|
32
|
+
context[field] = state[field];
|
|
33
|
+
endpoint.call.on(id, `change:${String(field)}`, (value) => {
|
|
34
|
+
context[field] = value;
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
set(field, value) {
|
|
39
|
+
if (!(field in schema2)) {
|
|
40
|
+
throw new Error(`[crm:embed:remote] Field ${String(field)} is not present in context ${id}`);
|
|
41
|
+
}
|
|
42
|
+
if (schema2[field].readonly) {
|
|
43
|
+
throw new Error(`[crm:embed:remote] Field ${String(field)} is readonly in context ${id}`);
|
|
44
|
+
}
|
|
45
|
+
if (!schema2[field].accepts(value)) {
|
|
46
|
+
throw new Error(`[crm:embed:remote] Invalid value for field ${String(field)} in context ${id}`);
|
|
47
|
+
}
|
|
48
|
+
const context = this;
|
|
49
|
+
const endpoint = this.endpoint;
|
|
50
|
+
context[field] = value;
|
|
51
|
+
endpoint.call.set(id, field, value);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
const useField = (store, field) => {
|
|
57
|
+
if (store.schema[field].readonly) {
|
|
58
|
+
return vue.computed(() => store[field]);
|
|
59
|
+
}
|
|
60
|
+
return vue.computed({
|
|
61
|
+
get: () => store[field],
|
|
62
|
+
set: (value) => {
|
|
63
|
+
store.set(field, value);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
const withMeta = (predicate, type) => {
|
|
68
|
+
return Object.assign(predicate, { type });
|
|
69
|
+
};
|
|
70
|
+
const isNull = withMeta((value) => value === null, "null");
|
|
71
|
+
const isNumber = withMeta((value) => typeof value === "number", "number");
|
|
72
|
+
const isString = withMeta((value) => typeof value === "string", "string");
|
|
73
|
+
const arrayOf = (predicate) => withMeta(
|
|
74
|
+
(value) => {
|
|
75
|
+
return Array.isArray(value) && value.every(predicate);
|
|
76
|
+
},
|
|
77
|
+
`Array<${predicate.type}>`
|
|
78
|
+
);
|
|
79
|
+
const oneOf = (...predicates) => withMeta(
|
|
80
|
+
(value) => {
|
|
81
|
+
return predicates.some((predicate) => predicate(value));
|
|
82
|
+
},
|
|
83
|
+
predicates.map((p) => p.type).join(" | ")
|
|
84
|
+
);
|
|
85
|
+
const schema$3 = {
|
|
86
|
+
"id": {
|
|
87
|
+
accepts: oneOf(isNumber, isNull),
|
|
88
|
+
defaults: () => null,
|
|
89
|
+
readonly: true
|
|
90
|
+
},
|
|
91
|
+
"externalId": {
|
|
92
|
+
accepts: isString,
|
|
93
|
+
defaults: () => null,
|
|
94
|
+
readonly: true
|
|
95
|
+
},
|
|
96
|
+
"email": {
|
|
97
|
+
accepts: isString,
|
|
98
|
+
defaults: () => "",
|
|
99
|
+
readonly: true
|
|
100
|
+
},
|
|
101
|
+
"phones": {
|
|
102
|
+
accepts: arrayOf(isString),
|
|
103
|
+
defaults: () => [],
|
|
104
|
+
readonly: true
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const useContext$3 = defineContext("customer/card", schema$3);
|
|
108
|
+
const schema$2 = {
|
|
109
|
+
value: {
|
|
110
|
+
accepts: isString,
|
|
111
|
+
defaults: () => "",
|
|
112
|
+
readonly: true
|
|
113
|
+
},
|
|
114
|
+
index: {
|
|
115
|
+
accepts: isNumber,
|
|
116
|
+
defaults: () => 0,
|
|
117
|
+
readonly: true
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
const useContext$2 = defineContext("customer/card:phone", schema$2);
|
|
121
|
+
const schema$1 = {
|
|
122
|
+
"customer.email": {
|
|
123
|
+
accepts: oneOf(isString, isNull),
|
|
124
|
+
defaults: () => null,
|
|
125
|
+
readonly: false
|
|
126
|
+
},
|
|
127
|
+
"customer.phone": {
|
|
128
|
+
accepts: oneOf(isString, isNull),
|
|
129
|
+
defaults: () => null,
|
|
130
|
+
readonly: false
|
|
131
|
+
},
|
|
132
|
+
"delivery.address": {
|
|
133
|
+
accepts: oneOf(isString, isNull),
|
|
134
|
+
defaults: () => null,
|
|
135
|
+
readonly: false
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
const useContext$1 = defineContext("order/card", schema$1);
|
|
139
|
+
const locales = ["en-GB", "es-ES", "ru-RU"];
|
|
140
|
+
const isLocale = withMeta(
|
|
141
|
+
(value) => locales.includes(value),
|
|
142
|
+
locales.map((l) => `'${l}'`).join(" | ")
|
|
143
|
+
);
|
|
144
|
+
const schema = {
|
|
145
|
+
"order.templates.number.api": {
|
|
146
|
+
accepts: isString,
|
|
147
|
+
defaults: () => "",
|
|
148
|
+
readonly: true
|
|
149
|
+
},
|
|
150
|
+
"order.templates.number.crm": {
|
|
151
|
+
accepts: isString,
|
|
152
|
+
defaults: () => "",
|
|
153
|
+
readonly: true
|
|
154
|
+
},
|
|
155
|
+
"system.locale": {
|
|
156
|
+
accepts: isLocale,
|
|
157
|
+
defaults: () => "en-GB",
|
|
158
|
+
readonly: true
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
const useContext = defineContext("settings", schema);
|
|
162
|
+
const createRoot = async (channel) => {
|
|
163
|
+
const root = remote.createRemoteRoot(channel, {
|
|
164
|
+
components: [
|
|
165
|
+
"UiButton",
|
|
166
|
+
"UiError",
|
|
167
|
+
"UiLink",
|
|
168
|
+
"UiLoader",
|
|
169
|
+
"UiMenuItem",
|
|
170
|
+
"UiMenuItemGroup",
|
|
171
|
+
"UiModalSidebar",
|
|
172
|
+
"UiModalWindow",
|
|
173
|
+
"UiModalWindowSurface",
|
|
174
|
+
"UiScrollbar",
|
|
175
|
+
"UiTag",
|
|
176
|
+
"UiTransition",
|
|
177
|
+
"CrmYandexMap"
|
|
178
|
+
]
|
|
179
|
+
});
|
|
180
|
+
await root.mount();
|
|
181
|
+
return root;
|
|
182
|
+
};
|
|
183
|
+
const createWidgetEndpoint = (widget, messenger) => {
|
|
184
|
+
const endpoint = rpc.createEndpoint(messenger);
|
|
185
|
+
const pinia$1 = pinia.createPinia();
|
|
186
|
+
pinia$1.use(injectAccessor(endpoint));
|
|
187
|
+
let onRelease = () => {
|
|
188
|
+
};
|
|
189
|
+
endpoint.expose({
|
|
190
|
+
async run(channel, target) {
|
|
191
|
+
rpc.retain(channel);
|
|
192
|
+
const root = await createRoot(channel);
|
|
193
|
+
await root.mount();
|
|
194
|
+
const { createApp } = remote.createRemoteRenderer(root);
|
|
195
|
+
const destroy = await widget.run(createApp, root, pinia$1, target);
|
|
196
|
+
onRelease = () => {
|
|
197
|
+
destroy();
|
|
198
|
+
rpc.release(channel);
|
|
199
|
+
};
|
|
200
|
+
},
|
|
201
|
+
release() {
|
|
202
|
+
onRelease();
|
|
203
|
+
onRelease = () => {
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
return endpoint;
|
|
208
|
+
};
|
|
209
|
+
exports.createWidgetEndpoint = createWidgetEndpoint;
|
|
210
|
+
exports.customerCardPhoneSchema = schema$2;
|
|
211
|
+
exports.customerCardSchema = schema$3;
|
|
212
|
+
exports.orderCardSchema = schema$1;
|
|
213
|
+
exports.settingsSchema = schema;
|
|
214
|
+
exports.useCustomerCardContext = useContext$3;
|
|
215
|
+
exports.useCustomerCardPhoneContext = useContext$2;
|
|
216
|
+
exports.useField = useField;
|
|
217
|
+
exports.useOrderCardContext = useContext$1;
|
|
218
|
+
exports.useSettingsContext = useContext;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { ComputedRef } from 'vue';
|
|
2
|
+
import { Context } from '../../../types/context/schema';
|
|
3
|
+
import { Context as Context_2 } from '../../types/context/schema';
|
|
4
|
+
import { ContextAccessor } from '../types/context/schema';
|
|
5
|
+
import { ContextSchema } from '../../types/context/schema';
|
|
6
|
+
import { Endpoint } from '@remote-ui/rpc';
|
|
7
|
+
import { IsReadonly } from '../../types/context/schema';
|
|
8
|
+
import { MessageEndpoint } from '@remote-ui/rpc';
|
|
9
|
+
import { Schema } from '../../../types/context/customer/card';
|
|
10
|
+
import { Schema as Schema_2 } from '../../../types/context/customer/card-phone';
|
|
11
|
+
import { Schema as Schema_3 } from '../../../types/context/order/card';
|
|
12
|
+
import { Schema as Schema_4 } from '../../types/context/settings';
|
|
13
|
+
import { SchemaList } from '../types/context';
|
|
14
|
+
import { Store } from 'pinia';
|
|
15
|
+
import { StoreDefinition } from 'pinia';
|
|
16
|
+
import { TypeOf } from '../../../types/context/schema';
|
|
17
|
+
import { TypeOf as TypeOf_2 } from '../../types/context/schema';
|
|
18
|
+
import { WidgetRunner } from '../types/widget';
|
|
19
|
+
import { WritableComputedRef } from 'vue';
|
|
20
|
+
|
|
21
|
+
declare type Computed<S extends ContextSchema, F extends keyof S> = IsReadonly<S[F]> extends true ? ComputedRef<TypeOf_2<S[F]>> : WritableComputedRef<TypeOf_2<S[F]>>;
|
|
22
|
+
|
|
23
|
+
export declare const createWidgetEndpoint: (widget: WidgetRunner, messenger: MessageEndpoint) => Endpoint<ContextAccessor<SchemaList>>;
|
|
24
|
+
|
|
25
|
+
export declare const customerCardPhoneSchema: Schema_2;
|
|
26
|
+
|
|
27
|
+
export declare const customerCardSchema: Schema;
|
|
28
|
+
|
|
29
|
+
export declare const orderCardSchema: Schema_3;
|
|
30
|
+
|
|
31
|
+
export declare const settingsSchema: Schema_4;
|
|
32
|
+
|
|
33
|
+
export declare const useCustomerCardContext: StoreDefinition<"customer/card", Context<Schema>, {
|
|
34
|
+
schema: () => Schema;
|
|
35
|
+
}, {
|
|
36
|
+
initialize(): Promise<void>;
|
|
37
|
+
set<F extends keyof Schema>(field: F, value: TypeOf<Schema[F]>): void;
|
|
38
|
+
}>;
|
|
39
|
+
|
|
40
|
+
export declare const useCustomerCardPhoneContext: StoreDefinition<"customer/card:phone", Context<Schema_2>, {
|
|
41
|
+
schema: () => Schema_2;
|
|
42
|
+
}, {
|
|
43
|
+
initialize(): Promise<void>;
|
|
44
|
+
set<F extends keyof Schema_2>(field: F, value: TypeOf<Schema_2[F]>): void;
|
|
45
|
+
}>;
|
|
46
|
+
|
|
47
|
+
export declare const useField: <S extends ContextSchema, F extends keyof S>(store: Store<string, Context_2<S>, {
|
|
48
|
+
schema(): S;
|
|
49
|
+
}, {
|
|
50
|
+
initialize(): Promise<void>;
|
|
51
|
+
set<F_1 extends keyof S>(field: F_1, value: TypeOf_2<S[F_1]>): void;
|
|
52
|
+
}>, field: F) => Computed<S, F>;
|
|
53
|
+
|
|
54
|
+
export declare const useOrderCardContext: StoreDefinition<"order/card", Context<Schema_3>, {
|
|
55
|
+
schema: () => Schema_3;
|
|
56
|
+
}, {
|
|
57
|
+
initialize(): Promise<void>;
|
|
58
|
+
set<F extends keyof Schema_3>(field: F, value: TypeOf<Schema_3[F]>): void;
|
|
59
|
+
}>;
|
|
60
|
+
|
|
61
|
+
export declare const useSettingsContext: StoreDefinition<"settings", Context_2<Schema_4>, {
|
|
62
|
+
schema: () => Schema_4;
|
|
63
|
+
}, {
|
|
64
|
+
initialize(): Promise<void>;
|
|
65
|
+
set<F extends keyof Schema_4>(field: F, value: TypeOf_2<Schema_4[F]>): void;
|
|
66
|
+
}>;
|
|
67
|
+
|
|
68
|
+
export { }
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
declare module 'pinia' {
|
|
72
|
+
interface PiniaCustomProperties {
|
|
73
|
+
endpoint: Endpoint<ContextAccessor>;
|
|
74
|
+
}
|
|
75
|
+
}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { createEndpoint, retain, release } from "@remote-ui/rpc";
|
|
2
|
+
import { defineStore, createPinia } from "pinia";
|
|
3
|
+
import { createRemoteRenderer, createRemoteRoot } from "@omnicajs/vue-remote/remote";
|
|
4
|
+
import { computed } from "vue";
|
|
5
|
+
const keysOf = (o) => Object.keys(o);
|
|
6
|
+
const injectAccessor = (endpoint) => {
|
|
7
|
+
return (context) => {
|
|
8
|
+
context.store.endpoint = endpoint;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
const defineContext = (id, schema2) => {
|
|
12
|
+
return defineStore(id, {
|
|
13
|
+
state() {
|
|
14
|
+
return {
|
|
15
|
+
...keysOf(schema2).reduce((state, field) => ({
|
|
16
|
+
...state,
|
|
17
|
+
[field]: schema2[field].defaults()
|
|
18
|
+
}), {})
|
|
19
|
+
};
|
|
20
|
+
},
|
|
21
|
+
getters: {
|
|
22
|
+
schema: () => schema2
|
|
23
|
+
},
|
|
24
|
+
actions: {
|
|
25
|
+
async initialize() {
|
|
26
|
+
const context = this;
|
|
27
|
+
const endpoint = this.endpoint;
|
|
28
|
+
const state = await endpoint.call.get(id, "~");
|
|
29
|
+
keysOf(schema2).forEach((field) => {
|
|
30
|
+
context[field] = state[field];
|
|
31
|
+
endpoint.call.on(id, `change:${String(field)}`, (value) => {
|
|
32
|
+
context[field] = value;
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
set(field, value) {
|
|
37
|
+
if (!(field in schema2)) {
|
|
38
|
+
throw new Error(`[crm:embed:remote] Field ${String(field)} is not present in context ${id}`);
|
|
39
|
+
}
|
|
40
|
+
if (schema2[field].readonly) {
|
|
41
|
+
throw new Error(`[crm:embed:remote] Field ${String(field)} is readonly in context ${id}`);
|
|
42
|
+
}
|
|
43
|
+
if (!schema2[field].accepts(value)) {
|
|
44
|
+
throw new Error(`[crm:embed:remote] Invalid value for field ${String(field)} in context ${id}`);
|
|
45
|
+
}
|
|
46
|
+
const context = this;
|
|
47
|
+
const endpoint = this.endpoint;
|
|
48
|
+
context[field] = value;
|
|
49
|
+
endpoint.call.set(id, field, value);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
const useField = (store, field) => {
|
|
55
|
+
if (store.schema[field].readonly) {
|
|
56
|
+
return computed(() => store[field]);
|
|
57
|
+
}
|
|
58
|
+
return computed({
|
|
59
|
+
get: () => store[field],
|
|
60
|
+
set: (value) => {
|
|
61
|
+
store.set(field, value);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
const withMeta = (predicate, type) => {
|
|
66
|
+
return Object.assign(predicate, { type });
|
|
67
|
+
};
|
|
68
|
+
const isNull = withMeta((value) => value === null, "null");
|
|
69
|
+
const isNumber = withMeta((value) => typeof value === "number", "number");
|
|
70
|
+
const isString = withMeta((value) => typeof value === "string", "string");
|
|
71
|
+
const arrayOf = (predicate) => withMeta(
|
|
72
|
+
(value) => {
|
|
73
|
+
return Array.isArray(value) && value.every(predicate);
|
|
74
|
+
},
|
|
75
|
+
`Array<${predicate.type}>`
|
|
76
|
+
);
|
|
77
|
+
const oneOf = (...predicates) => withMeta(
|
|
78
|
+
(value) => {
|
|
79
|
+
return predicates.some((predicate) => predicate(value));
|
|
80
|
+
},
|
|
81
|
+
predicates.map((p) => p.type).join(" | ")
|
|
82
|
+
);
|
|
83
|
+
const schema$3 = {
|
|
84
|
+
"id": {
|
|
85
|
+
accepts: oneOf(isNumber, isNull),
|
|
86
|
+
defaults: () => null,
|
|
87
|
+
readonly: true
|
|
88
|
+
},
|
|
89
|
+
"externalId": {
|
|
90
|
+
accepts: isString,
|
|
91
|
+
defaults: () => null,
|
|
92
|
+
readonly: true
|
|
93
|
+
},
|
|
94
|
+
"email": {
|
|
95
|
+
accepts: isString,
|
|
96
|
+
defaults: () => "",
|
|
97
|
+
readonly: true
|
|
98
|
+
},
|
|
99
|
+
"phones": {
|
|
100
|
+
accepts: arrayOf(isString),
|
|
101
|
+
defaults: () => [],
|
|
102
|
+
readonly: true
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const useContext$3 = defineContext("customer/card", schema$3);
|
|
106
|
+
const schema$2 = {
|
|
107
|
+
value: {
|
|
108
|
+
accepts: isString,
|
|
109
|
+
defaults: () => "",
|
|
110
|
+
readonly: true
|
|
111
|
+
},
|
|
112
|
+
index: {
|
|
113
|
+
accepts: isNumber,
|
|
114
|
+
defaults: () => 0,
|
|
115
|
+
readonly: true
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
const useContext$2 = defineContext("customer/card:phone", schema$2);
|
|
119
|
+
const schema$1 = {
|
|
120
|
+
"customer.email": {
|
|
121
|
+
accepts: oneOf(isString, isNull),
|
|
122
|
+
defaults: () => null,
|
|
123
|
+
readonly: false
|
|
124
|
+
},
|
|
125
|
+
"customer.phone": {
|
|
126
|
+
accepts: oneOf(isString, isNull),
|
|
127
|
+
defaults: () => null,
|
|
128
|
+
readonly: false
|
|
129
|
+
},
|
|
130
|
+
"delivery.address": {
|
|
131
|
+
accepts: oneOf(isString, isNull),
|
|
132
|
+
defaults: () => null,
|
|
133
|
+
readonly: false
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
const useContext$1 = defineContext("order/card", schema$1);
|
|
137
|
+
const locales = ["en-GB", "es-ES", "ru-RU"];
|
|
138
|
+
const isLocale = withMeta(
|
|
139
|
+
(value) => locales.includes(value),
|
|
140
|
+
locales.map((l) => `'${l}'`).join(" | ")
|
|
141
|
+
);
|
|
142
|
+
const schema = {
|
|
143
|
+
"order.templates.number.api": {
|
|
144
|
+
accepts: isString,
|
|
145
|
+
defaults: () => "",
|
|
146
|
+
readonly: true
|
|
147
|
+
},
|
|
148
|
+
"order.templates.number.crm": {
|
|
149
|
+
accepts: isString,
|
|
150
|
+
defaults: () => "",
|
|
151
|
+
readonly: true
|
|
152
|
+
},
|
|
153
|
+
"system.locale": {
|
|
154
|
+
accepts: isLocale,
|
|
155
|
+
defaults: () => "en-GB",
|
|
156
|
+
readonly: true
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
const useContext = defineContext("settings", schema);
|
|
160
|
+
const createRoot = async (channel) => {
|
|
161
|
+
const root = createRemoteRoot(channel, {
|
|
162
|
+
components: [
|
|
163
|
+
"UiButton",
|
|
164
|
+
"UiError",
|
|
165
|
+
"UiLink",
|
|
166
|
+
"UiLoader",
|
|
167
|
+
"UiMenuItem",
|
|
168
|
+
"UiMenuItemGroup",
|
|
169
|
+
"UiModalSidebar",
|
|
170
|
+
"UiModalWindow",
|
|
171
|
+
"UiModalWindowSurface",
|
|
172
|
+
"UiScrollbar",
|
|
173
|
+
"UiTag",
|
|
174
|
+
"UiTransition",
|
|
175
|
+
"CrmYandexMap"
|
|
176
|
+
]
|
|
177
|
+
});
|
|
178
|
+
await root.mount();
|
|
179
|
+
return root;
|
|
180
|
+
};
|
|
181
|
+
const createWidgetEndpoint = (widget, messenger) => {
|
|
182
|
+
const endpoint = createEndpoint(messenger);
|
|
183
|
+
const pinia = createPinia();
|
|
184
|
+
pinia.use(injectAccessor(endpoint));
|
|
185
|
+
let onRelease = () => {
|
|
186
|
+
};
|
|
187
|
+
endpoint.expose({
|
|
188
|
+
async run(channel, target) {
|
|
189
|
+
retain(channel);
|
|
190
|
+
const root = await createRoot(channel);
|
|
191
|
+
await root.mount();
|
|
192
|
+
const { createApp } = createRemoteRenderer(root);
|
|
193
|
+
const destroy = await widget.run(createApp, root, pinia, target);
|
|
194
|
+
onRelease = () => {
|
|
195
|
+
destroy();
|
|
196
|
+
release(channel);
|
|
197
|
+
};
|
|
198
|
+
},
|
|
199
|
+
release() {
|
|
200
|
+
onRelease();
|
|
201
|
+
onRelease = () => {
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
return endpoint;
|
|
206
|
+
};
|
|
207
|
+
export {
|
|
208
|
+
createWidgetEndpoint,
|
|
209
|
+
schema$2 as customerCardPhoneSchema,
|
|
210
|
+
schema$3 as customerCardSchema,
|
|
211
|
+
schema$1 as orderCardSchema,
|
|
212
|
+
schema as settingsSchema,
|
|
213
|
+
useContext$3 as useCustomerCardContext,
|
|
214
|
+
useContext$2 as useCustomerCardPhoneContext,
|
|
215
|
+
useField,
|
|
216
|
+
useContext$1 as useOrderCardContext,
|
|
217
|
+
useContext as useSettingsContext
|
|
218
|
+
};
|
package/dist/meta.json
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
{
|
|
2
|
+
"contexts": {
|
|
3
|
+
"customer/card": [
|
|
4
|
+
{
|
|
5
|
+
"name": "id",
|
|
6
|
+
"type": "number | null",
|
|
7
|
+
"description": {
|
|
8
|
+
"en-GB": "Customer ID",
|
|
9
|
+
"es-ES": "",
|
|
10
|
+
"ru-RU": "ID клиента"
|
|
11
|
+
},
|
|
12
|
+
"readonly": true
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "externalId",
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": {
|
|
18
|
+
"en-GB": "Customer external ID",
|
|
19
|
+
"es-ES": "",
|
|
20
|
+
"ru-RU": "Внешний ID клиента"
|
|
21
|
+
},
|
|
22
|
+
"readonly": true
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "email",
|
|
26
|
+
"type": "string",
|
|
27
|
+
"description": {
|
|
28
|
+
"en-GB": "Customer email",
|
|
29
|
+
"es-ES": "",
|
|
30
|
+
"ru-RU": "Email клиента"
|
|
31
|
+
},
|
|
32
|
+
"readonly": true
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "phones",
|
|
36
|
+
"type": "Array<string>",
|
|
37
|
+
"description": {
|
|
38
|
+
"en-GB": "Customer phone list",
|
|
39
|
+
"es-ES": "",
|
|
40
|
+
"ru-RU": "Список телефонов клиента"
|
|
41
|
+
},
|
|
42
|
+
"readonly": true
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"customer/card.phone": [
|
|
46
|
+
{
|
|
47
|
+
"name": "value",
|
|
48
|
+
"type": "string",
|
|
49
|
+
"description": {
|
|
50
|
+
"en-GB": "Customer phone",
|
|
51
|
+
"es-ES": "",
|
|
52
|
+
"ru-RU": "Телефон клиента"
|
|
53
|
+
},
|
|
54
|
+
"readonly": true
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "index",
|
|
58
|
+
"type": "number",
|
|
59
|
+
"description": {
|
|
60
|
+
"en-GB": "Serial number of the phone in the list",
|
|
61
|
+
"es-ES": "",
|
|
62
|
+
"ru-RU": "Порядковый номер телефона в списке"
|
|
63
|
+
},
|
|
64
|
+
"readonly": true
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
"order/card": [
|
|
68
|
+
{
|
|
69
|
+
"name": "customer.email",
|
|
70
|
+
"type": "string | null",
|
|
71
|
+
"description": {
|
|
72
|
+
"en-GB": "Customer email",
|
|
73
|
+
"es-ES": "",
|
|
74
|
+
"ru-RU": "Email клиента"
|
|
75
|
+
},
|
|
76
|
+
"readonly": false
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"name": "customer.phone",
|
|
80
|
+
"type": "string | null",
|
|
81
|
+
"description": {
|
|
82
|
+
"en-GB": "Customer phone",
|
|
83
|
+
"es-ES": "",
|
|
84
|
+
"ru-RU": "Телефон клиента"
|
|
85
|
+
},
|
|
86
|
+
"readonly": false
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"name": "delivery.address",
|
|
90
|
+
"type": "string | null",
|
|
91
|
+
"description": {
|
|
92
|
+
"en-GB": "Delivery address",
|
|
93
|
+
"es-ES": "",
|
|
94
|
+
"ru-RU": "Адрес доставки"
|
|
95
|
+
},
|
|
96
|
+
"readonly": false
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
"settings": [
|
|
100
|
+
{
|
|
101
|
+
"name": "order.templates.number.api",
|
|
102
|
+
"type": "string",
|
|
103
|
+
"description": {
|
|
104
|
+
"en-GB": "Number template for orders created with API",
|
|
105
|
+
"es-ES": "",
|
|
106
|
+
"ru-RU": "Шаблон номера заказов, создаваемых через API"
|
|
107
|
+
},
|
|
108
|
+
"readonly": true
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"name": "order.templates.number.crm",
|
|
112
|
+
"type": "string",
|
|
113
|
+
"description": {
|
|
114
|
+
"en-GB": "Number template for orders created with CRM's interface",
|
|
115
|
+
"es-ES": "",
|
|
116
|
+
"ru-RU": "Шаблон номера заказов, создаваемых через интерфейс CRM"
|
|
117
|
+
},
|
|
118
|
+
"readonly": true
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"name": "system.locale",
|
|
122
|
+
"type": "'en-GB' | 'es-ES' | 'ru-RU'",
|
|
123
|
+
"description": {
|
|
124
|
+
"en-GB": "Current system's locale",
|
|
125
|
+
"es-ES": "",
|
|
126
|
+
"ru-RU": "Текущая локаль системы"
|
|
127
|
+
},
|
|
128
|
+
"readonly": true
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
},
|
|
132
|
+
"targets": [
|
|
133
|
+
{
|
|
134
|
+
"id": "customer/card:phone",
|
|
135
|
+
"description": {
|
|
136
|
+
"en-GB": "Widget for customer phone list item",
|
|
137
|
+
"es-ES": "",
|
|
138
|
+
"ru-RU": "Виджет для элемента списка телефонов клиента"
|
|
139
|
+
},
|
|
140
|
+
"location": {
|
|
141
|
+
"en-GB": "Right after the phone number in the list",
|
|
142
|
+
"es-ES": "",
|
|
143
|
+
"ru-RU": "Сразу после номера телефона в списке"
|
|
144
|
+
},
|
|
145
|
+
"contexts": [
|
|
146
|
+
"customer/card",
|
|
147
|
+
"settings"
|
|
148
|
+
]
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"id": "order/card:customer.after",
|
|
152
|
+
"description": {
|
|
153
|
+
"en-GB": "Widget for section with customer data",
|
|
154
|
+
"es-ES": "",
|
|
155
|
+
"ru-RU": "Виджет для секции с данными клиента"
|
|
156
|
+
},
|
|
157
|
+
"location": {
|
|
158
|
+
"en-GB": "Right under the input fields",
|
|
159
|
+
"es-ES": "",
|
|
160
|
+
"ru-RU": "Сразу под полями ввода"
|
|
161
|
+
},
|
|
162
|
+
"contexts": [
|
|
163
|
+
"order/card",
|
|
164
|
+
"settings"
|
|
165
|
+
]
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"id": "order/card:customer.email",
|
|
169
|
+
"description": {
|
|
170
|
+
"en-GB": "Widget for customer email input field",
|
|
171
|
+
"es-ES": "",
|
|
172
|
+
"ru-RU": "Виджет для поля ввода email клиента"
|
|
173
|
+
},
|
|
174
|
+
"location": {
|
|
175
|
+
"en-GB": "Right after the input field",
|
|
176
|
+
"es-ES": "",
|
|
177
|
+
"ru-RU": "Сразу после поля ввода"
|
|
178
|
+
},
|
|
179
|
+
"contexts": [
|
|
180
|
+
"order/card",
|
|
181
|
+
"settings"
|
|
182
|
+
]
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"id": "order/card:customer.phone",
|
|
186
|
+
"description": {
|
|
187
|
+
"en-GB": "Widget for customer phone input field",
|
|
188
|
+
"es-ES": "",
|
|
189
|
+
"ru-RU": "Виджет для поля ввода телефона клиента"
|
|
190
|
+
},
|
|
191
|
+
"location": {
|
|
192
|
+
"en-GB": "Right after the input field",
|
|
193
|
+
"es-ES": "",
|
|
194
|
+
"ru-RU": "Сразу после поля ввода"
|
|
195
|
+
},
|
|
196
|
+
"contexts": [
|
|
197
|
+
"order/card",
|
|
198
|
+
"settings"
|
|
199
|
+
]
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"id": "order/card:delivery.address",
|
|
203
|
+
"description": {
|
|
204
|
+
"en-GB": "Widget for delivery address input field",
|
|
205
|
+
"es-ES": "",
|
|
206
|
+
"ru-RU": "Виджет для поля ввода адреса доставки"
|
|
207
|
+
},
|
|
208
|
+
"location": {
|
|
209
|
+
"en-GB": "Right under the input field",
|
|
210
|
+
"es-ES": "",
|
|
211
|
+
"ru-RU": "Под полем ввода адреса"
|
|
212
|
+
},
|
|
213
|
+
"contexts": [
|
|
214
|
+
"order/card",
|
|
215
|
+
"settings"
|
|
216
|
+
]
|
|
217
|
+
}
|
|
218
|
+
],
|
|
219
|
+
"pages": [
|
|
220
|
+
{
|
|
221
|
+
"id": "customer/card",
|
|
222
|
+
"description": {
|
|
223
|
+
"en-GB": "Customer page",
|
|
224
|
+
"es-ES": "",
|
|
225
|
+
"ru-RU": "Страница клиента"
|
|
226
|
+
},
|
|
227
|
+
"targets": [
|
|
228
|
+
"customer/card:phone"
|
|
229
|
+
]
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"id": "order/card",
|
|
233
|
+
"description": {
|
|
234
|
+
"en-GB": "Page with the order creation/editing form",
|
|
235
|
+
"es-ES": "",
|
|
236
|
+
"ru-RU": "Страница с формой создания/редактирования заказа"
|
|
237
|
+
},
|
|
238
|
+
"targets": [
|
|
239
|
+
"order/card:customer.after",
|
|
240
|
+
"order/card:customer.email",
|
|
241
|
+
"order/card:customer.phone",
|
|
242
|
+
"order/card:delivery.address"
|
|
243
|
+
]
|
|
244
|
+
}
|
|
245
|
+
]
|
|
246
|
+
}
|
package/package.json
CHANGED
|
@@ -1,39 +1,64 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retailcrm/embed-ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1
|
|
4
|
+
"version": "0.2.1",
|
|
5
5
|
"description": "API and components for creating RetailCRM UI extensions",
|
|
6
|
-
"main": "index.js",
|
|
7
6
|
"repository": "git@github.com:retailcrm/embed-ui.git",
|
|
8
7
|
"author": "RetailDriverLLC <integration@retailcrm.ru>",
|
|
9
8
|
"license": "MIT",
|
|
10
9
|
"contributors": [
|
|
11
10
|
"Kirill Zaytsev <zaytsev.cmath10@gmail.com>"
|
|
12
11
|
],
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./dist/index.mjs",
|
|
15
|
+
"require": "./dist/index.cjs",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"default": "./dist/index.mjs"
|
|
18
|
+
},
|
|
19
|
+
"./dist/*": "./dist/*",
|
|
20
|
+
"./types/*": "./types/*"
|
|
21
|
+
},
|
|
13
22
|
"scripts": {
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"release
|
|
18
|
-
"release:
|
|
23
|
+
"build": "vite build",
|
|
24
|
+
"eslint": "eslint .",
|
|
25
|
+
"generate:meta": "yes | npx tsx scripts/generate-meta.ts",
|
|
26
|
+
"release": "yes | npx standard-version",
|
|
27
|
+
"release:major": "yes | npx standard-version --release-as major",
|
|
28
|
+
"release:minor": "yes | npx standard-version --release-as minor",
|
|
29
|
+
"release:patch": "yes | npx standard-version --release-as patch",
|
|
30
|
+
"test": "vitest --run"
|
|
19
31
|
},
|
|
20
32
|
"dependencies": {
|
|
21
|
-
"@omnicajs/vue-remote": "^0.
|
|
33
|
+
"@omnicajs/vue-remote": "^0.2.0",
|
|
22
34
|
"@remote-ui/rpc": "^1.4.5"
|
|
23
35
|
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"pinia": "^2.2",
|
|
38
|
+
"vue": "^3.5"
|
|
39
|
+
},
|
|
24
40
|
"devDependencies": {
|
|
25
41
|
"@eslint/eslintrc": "^3.0.2",
|
|
26
|
-
"@eslint/js": "^9.
|
|
42
|
+
"@eslint/js": "^9.13.0",
|
|
27
43
|
"@stylistic/eslint-plugin": "^1.7.2",
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"eslint
|
|
32
|
-
"eslint-plugin-
|
|
33
|
-
"
|
|
34
|
-
"
|
|
44
|
+
"@types/node": "^22.7.9",
|
|
45
|
+
"@vitejs/plugin-vue": "^5.1.4",
|
|
46
|
+
"@vue/test-utils": "^2.4.6",
|
|
47
|
+
"eslint": "^9.13.0",
|
|
48
|
+
"eslint-plugin-import": "^2.31.0",
|
|
49
|
+
"eslint-plugin-n": "^17.11.1",
|
|
50
|
+
"eslint-plugin-promise": "^6.6.0",
|
|
51
|
+
"eslint-plugin-vue": "^9.29.1",
|
|
52
|
+
"globals": "^15.11.0",
|
|
53
|
+
"jsdom": "^25.0.1",
|
|
54
|
+
"pinia": "^2.2.4",
|
|
55
|
+
"tsx": "^4.19.2",
|
|
35
56
|
"typescript": "^5.4.5",
|
|
36
|
-
"typescript-eslint": "^
|
|
57
|
+
"typescript-eslint": "^8.11.0",
|
|
58
|
+
"vite": "^5.4.8",
|
|
59
|
+
"vite-plugin-dts": "^4.3.0",
|
|
60
|
+
"vitest": "^2.1.2",
|
|
61
|
+
"vue": "^3.5.12"
|
|
37
62
|
},
|
|
38
63
|
"publishConfig": {
|
|
39
64
|
"access": "public"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Schema as CustomerCardSchema } from './customer/card'
|
|
2
|
+
import type { Schema as CustomerCardPhoneSchema } from './customer/card-phone'
|
|
3
|
+
import type { Schema as OrderCardSchema } from './order/card'
|
|
4
|
+
import type { Schema as SettingsSchema } from './settings'
|
|
5
|
+
|
|
6
|
+
export type SchemaList = {
|
|
7
|
+
'customer/card': CustomerCardSchema;
|
|
8
|
+
'customer/card.phone': CustomerCardPhoneSchema;
|
|
9
|
+
'order/card': OrderCardSchema;
|
|
10
|
+
'settings': SettingsSchema;
|
|
11
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { IsTilda, If } from '../scaffolding'
|
|
2
|
+
|
|
3
|
+
export type Field<Type, Readonly extends boolean = false> = {
|
|
4
|
+
accepts (value: Type): boolean;
|
|
5
|
+
defaults (): Type;
|
|
6
|
+
readonly: Readonly;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type ReadonlyField<Type> = Field<Type, true>
|
|
10
|
+
|
|
11
|
+
export type TypeOf<F> = F extends Field<infer T>
|
|
12
|
+
? T
|
|
13
|
+
: F extends ReadonlyField<infer T>
|
|
14
|
+
? T
|
|
15
|
+
: never;
|
|
16
|
+
|
|
17
|
+
export type IsReadonly<F> = F extends Field<unknown, infer R>
|
|
18
|
+
? R
|
|
19
|
+
: F extends ReadonlyField
|
|
20
|
+
? true
|
|
21
|
+
: false;
|
|
22
|
+
|
|
23
|
+
export type ContextSchema = {
|
|
24
|
+
[key: string]: Field<unknown, boolean>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type ContextSchemaMap = {
|
|
28
|
+
[key: string]: ContextSchema;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type Context<S extends ContextSchema> = {
|
|
32
|
+
[F in keyof S]: TypeOf<S[F]>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type Writable<S extends ContextSchema> = {
|
|
36
|
+
[F in keyof S]: IsReadonly<S[F]> extends true ? never : S[F];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type EventMap<S extends ContextSchema> = {
|
|
40
|
+
[K in keyof S as `change:${K}`]: K;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type EventHandler<
|
|
44
|
+
S extends ContextSchema,
|
|
45
|
+
E extends keyof EventMap<S>
|
|
46
|
+
> = (payload: TypeOf<S[EventMap<S>[E]]>) => void
|
|
47
|
+
|
|
48
|
+
export type ContextAccessor<M extends ContextSchemaMap = ContextSchemaMap> = {
|
|
49
|
+
get <
|
|
50
|
+
C extends keyof M,
|
|
51
|
+
F extends keyof M[C]
|
|
52
|
+
>(context: C, field: F | '~'): If<
|
|
53
|
+
IsTilda<typeof field>,
|
|
54
|
+
Context<M[C]>,
|
|
55
|
+
Context<M[C]>[F]
|
|
56
|
+
>;
|
|
57
|
+
|
|
58
|
+
set <
|
|
59
|
+
C extends keyof M,
|
|
60
|
+
F extends keyof Writable<M[C]>
|
|
61
|
+
>(context: C, field: F, value: Context<M[C]>[F]): void;
|
|
62
|
+
|
|
63
|
+
on<
|
|
64
|
+
C extends keyof M,
|
|
65
|
+
E extends keyof EventMap<M[C]>
|
|
66
|
+
>(context: C, event: E, handler: EventHandler<M[C], E>): void;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type FieldAccessor<S extends ContextSchema> = {
|
|
70
|
+
get <F extends keyof S>(field: F | '~'): If<
|
|
71
|
+
IsTilda<typeof field>,
|
|
72
|
+
Context<S>,
|
|
73
|
+
Context<S>[F]
|
|
74
|
+
>;
|
|
75
|
+
|
|
76
|
+
set <F extends keyof Writable<S>>(
|
|
77
|
+
field: F,
|
|
78
|
+
value: Context<S>[F]
|
|
79
|
+
): void;
|
|
80
|
+
|
|
81
|
+
on<E extends keyof EventMap<S>>(
|
|
82
|
+
event: E,
|
|
83
|
+
handler: EventHandler<S, E>
|
|
84
|
+
): void;
|
|
85
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReadonlyField } from '~types/context/schema'
|
|
2
|
+
|
|
3
|
+
export type Locale = 'en-GB' | 'es-ES' | 'ru-RU'
|
|
4
|
+
|
|
5
|
+
export type Schema = {
|
|
6
|
+
'order.templates.number.api': ReadonlyField<string>;
|
|
7
|
+
'order.templates.number.crm': ReadonlyField<string>;
|
|
8
|
+
'system.locale': ReadonlyField<Locale>;
|
|
9
|
+
}
|
package/types/scaffolding.d.ts
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1
2
|
export type AnyFunction = (...payload: any[]) => unknown
|
|
2
3
|
export type None = Record<string, never>
|
|
4
|
+
|
|
5
|
+
type IsExact<A, B> = A extends B ? (B extends A ? true : false) : false;
|
|
6
|
+
type IsTilda<A> = IsExact<A, '~'>;
|
|
7
|
+
|
|
8
|
+
type If<Condition, Then, Else> = Condition extends true ? Then : Else;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CreateAppFunction,
|
|
3
|
+
Component,
|
|
4
|
+
} from 'vue'
|
|
5
|
+
|
|
6
|
+
import type { Channel } from '@omnicajs/vue-remote/dist/remote'
|
|
7
|
+
|
|
8
|
+
import type { Pinia } from 'pinia'
|
|
9
|
+
|
|
10
|
+
import type { RemoteRoot } from '@omnicajs/vue-remote/remote'
|
|
11
|
+
|
|
12
|
+
import type { SchemaList } from './context'
|
|
13
|
+
|
|
14
|
+
export interface WidgetRunner {
|
|
15
|
+
run(
|
|
16
|
+
createApp: CreateAppFunction<
|
|
17
|
+
| Component<RemoteRoot<SchemaOf<string>>>
|
|
18
|
+
| RemoteRoot<SchemaOf<string>>
|
|
19
|
+
>,
|
|
20
|
+
root: RemoteRoot<SchemaOf<string>>,
|
|
21
|
+
pinia: Pinia,
|
|
22
|
+
target: WidgetTarget
|
|
23
|
+
): Promise<() => void>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface WidgetEndpoint {
|
|
27
|
+
run (channel: Channel, target: WidgetTarget): Promise<void>;
|
|
28
|
+
release (): void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type WidgetTarget = keyof SchemaListByTarget
|
|
32
|
+
|
|
33
|
+
export type SchemaListOf<T extends WidgetTarget> = SchemaListByTarget[T]
|
|
34
|
+
export type SchemaListByTarget = {
|
|
35
|
+
'customer/card:phone': Pick<SchemaList,
|
|
36
|
+
| 'customer/card'
|
|
37
|
+
| 'customer/card.phone'
|
|
38
|
+
| 'settings'
|
|
39
|
+
>;
|
|
40
|
+
'order/card:customer.after': Pick<SchemaList,
|
|
41
|
+
| 'order/card'
|
|
42
|
+
| 'settings'
|
|
43
|
+
>;
|
|
44
|
+
'order/card:customer.email': Pick<SchemaList,
|
|
45
|
+
| 'order/card'
|
|
46
|
+
| 'settings'
|
|
47
|
+
>;
|
|
48
|
+
'order/card:customer.phone': Pick<SchemaList,
|
|
49
|
+
| 'order/card'
|
|
50
|
+
| 'settings'
|
|
51
|
+
>;
|
|
52
|
+
'order/card:delivery.address': Pick<SchemaList,
|
|
53
|
+
| 'order/card'
|
|
54
|
+
| 'settings'
|
|
55
|
+
>;
|
|
56
|
+
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
name: Tests
|
|
2
|
-
|
|
3
|
-
on: [push, pull_request]
|
|
4
|
-
|
|
5
|
-
concurrency:
|
|
6
|
-
group: ${{ github.workflow }}-${{ github.ref }}
|
|
7
|
-
cancel-in-progress: true
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
eslint:
|
|
11
|
-
runs-on: ubuntu-latest
|
|
12
|
-
|
|
13
|
-
strategy:
|
|
14
|
-
matrix:
|
|
15
|
-
node-version: [ 18.18.x, 20.x ]
|
|
16
|
-
|
|
17
|
-
steps:
|
|
18
|
-
- name: Using branch ${{ github.ref }} for repository ${{ github.repository }}.
|
|
19
|
-
uses: actions/checkout@v4
|
|
20
|
-
|
|
21
|
-
- name: Use Node.js ${{ matrix.node-version }}
|
|
22
|
-
uses: actions/setup-node@v4
|
|
23
|
-
with:
|
|
24
|
-
node-version: ${{ matrix.node-version }}
|
|
25
|
-
|
|
26
|
-
- name: Cache dependencies
|
|
27
|
-
id: cache-deps
|
|
28
|
-
uses: actions/cache@v4
|
|
29
|
-
with:
|
|
30
|
-
path: .yarn
|
|
31
|
-
key: ${{ runner.OS }}-node-${{ matrix.node-version }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
|
32
|
-
restore-keys: |
|
|
33
|
-
${{ runner.OS }}-node-${{ matrix.node-version }}-yarn-
|
|
34
|
-
|
|
35
|
-
- name: Install dependencies
|
|
36
|
-
run: yarn install
|
|
37
|
-
|
|
38
|
-
- name: Run lint
|
|
39
|
-
run: yarn lint
|
package/types/endpoint.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AnyFunction,
|
|
3
|
-
None,
|
|
4
|
-
} from './scaffolding'
|
|
5
|
-
|
|
6
|
-
import type { Channel } from '@omnicajs/vue-remote/host'
|
|
7
|
-
import type { Endpoint as RemoteEndpoint } from '@remote-ui/rpc'
|
|
8
|
-
|
|
9
|
-
export interface EndpointApi<
|
|
10
|
-
PageApi extends Record<string, AnyFunction> = None
|
|
11
|
-
> {
|
|
12
|
-
run (channel: Channel, api: PageApi): Promise<void>;
|
|
13
|
-
release (): void;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export type Endpoint<
|
|
17
|
-
PageApi extends Record<string, AnyFunction> = None
|
|
18
|
-
> = RemoteEndpoint<EndpointApi<PageApi>>
|
package/types/pages/order.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export type OrderCardMethods = {
|
|
2
|
-
getCustomerEmail: () => string | null,
|
|
3
|
-
setCustomerEmail: (value: string) => void,
|
|
4
|
-
|
|
5
|
-
getCustomerPhone: () => string | null,
|
|
6
|
-
setCustomerPhone: (value: string) => void,
|
|
7
|
-
|
|
8
|
-
getDeliveryAddress: () => string | null,
|
|
9
|
-
setDeliveryAddress: (value: string) => void,
|
|
10
|
-
parseDeliveryAddress: () => void,
|
|
11
|
-
}
|