1
|
|
|
// @ts-nocheck |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* @package admin |
5
|
|
|
*/ |
6
|
|
|
import ViewAdapter from 'src/core/adapter/view.adapter'; |
7
|
|
|
|
8
|
|
|
// Vue3 imports |
9
|
|
|
import { createI18n } from 'vue-i18n_v3'; |
10
|
|
|
import type { FallbackLocale, I18n } from 'vue-i18n_v3'; |
11
|
|
|
import type Router from 'vue-router_v3'; |
12
|
|
|
|
13
|
|
|
// Vue2 imports |
14
|
|
|
import VueRouter from 'vue-router'; |
15
|
|
|
import VueI18n from 'vue-i18n'; |
16
|
|
|
import VueMeta from 'vue-meta'; |
17
|
|
|
|
18
|
|
|
import Vue, { createApp, defineAsyncComponent, h } from 'vue'; |
19
|
|
|
import type { AsyncComponent, Component as VueComponent, PluginObject } from 'vue'; |
20
|
|
|
import VuePlugins from 'src/app/plugin'; |
21
|
|
|
import setupShopwareDevtools from 'src/app/adapter/view/sw-vue-devtools'; |
22
|
|
|
import type ApplicationBootstrapper from 'src/core/application'; |
23
|
|
|
import type { ComponentConfig } from 'src/core/factory/async-component.factory'; |
24
|
|
|
import type { Store } from 'vuex'; |
25
|
|
|
|
26
|
|
|
const { Component, State, Mixin } = Shopware; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @deprecated tag:v6.6.0 - Will be private |
30
|
|
|
*/ |
31
|
|
|
export default class VueAdapter extends ViewAdapter { |
32
|
|
|
private resolvedComponentConfigs: Map<string, ComponentConfig>; |
33
|
|
|
|
34
|
|
|
private vueComponents: { |
35
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any |
36
|
|
|
[componentName: string]: VueComponent<any, any, any, any> | AsyncComponent<any, any, any, any> |
37
|
|
|
}; |
38
|
|
|
|
39
|
|
|
private i18n?: I18n; |
40
|
|
|
|
41
|
|
|
public app; |
42
|
|
|
|
43
|
|
|
private vue3 = false; |
44
|
|
|
|
45
|
|
|
constructor(Application: ApplicationBootstrapper) { |
46
|
|
|
super(Application); |
47
|
|
|
|
48
|
|
|
this.i18n = undefined; |
49
|
|
|
this.resolvedComponentConfigs = new Map(); |
50
|
|
|
this.vueComponents = {}; |
51
|
|
|
// @ts-expect-error |
52
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access |
53
|
|
|
this.vue3 = !!window._features_?.vue3; |
54
|
|
|
|
55
|
|
|
if (this.vue3) { |
56
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call |
57
|
|
|
this.app = createApp({ name: 'ShopwareAdministration', template: '<sw-admin />' }) as Vue; |
58
|
|
|
} |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Creates the main instance for the view layer. |
63
|
|
|
* Is used on startup process of the main application. |
64
|
|
|
*/ |
65
|
|
|
// @ts-expect-error |
66
|
|
|
init(renderElement: string, router: Router, providers: { [key: string]: unknown }): Vue { |
67
|
|
|
if (this.vue3) { |
68
|
|
|
return this.initVue3(renderElement, router, providers); |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
return this.initVue2(renderElement, router as VueRouter, providers); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
// @ts-expect-error |
75
|
|
|
initVue3(renderElement: string, router: Router, providers: { [key: string]: unknown }): Vue { |
76
|
|
|
this.initPlugins(); |
77
|
|
|
this.initDirectives(); |
78
|
|
|
this.initFilters(); |
79
|
|
|
this.initTitle(); |
80
|
|
|
|
81
|
|
|
const store = State._store; |
82
|
|
|
const i18n = this.initLocales(store) as VueI18n; |
83
|
|
|
|
84
|
|
|
// add router to View |
85
|
|
|
this.router = router as VueRouter; |
86
|
|
|
// add i18n to View |
87
|
|
|
this.i18n = i18n as I18n; |
88
|
|
|
|
89
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access |
90
|
|
|
this.app.config.compilerOptions.whitespace = 'preserve'; |
91
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access |
92
|
|
|
this.app.config.performance = process.env.NODE_ENV !== 'production'; |
93
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access |
94
|
|
|
this.app.config.globalProperties.$t = i18n.global.t; |
95
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access |
96
|
|
|
this.app.config.globalProperties.$tc = i18n.global.tc; |
97
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access |
98
|
|
|
this.app.config.warnHandler = ( |
99
|
|
|
msg: string, |
100
|
|
|
instance: unknown, |
101
|
|
|
trace: string, |
102
|
|
|
) => { |
103
|
|
|
const warnArgs = [`[Vue warn]: ${msg}`, trace, instance]; |
104
|
|
|
|
105
|
|
|
console.warn(...warnArgs); |
106
|
|
|
|
107
|
|
|
if (msg.includes('Template compilation error')) { |
108
|
|
|
console.error(...[`[Vue error]: ${msg}`, trace, instance]); |
109
|
|
|
throw new Error(msg); |
110
|
|
|
} |
111
|
|
|
}; |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* This is a hack for providing the services to the components. |
115
|
|
|
* We shouldn't use this anymore because it is not supported well |
116
|
|
|
* in Vue3 (because the services are lazy loaded). |
117
|
|
|
* |
118
|
|
|
* So we should convert from provide/inject to Shopware.Service |
119
|
|
|
*/ |
120
|
|
|
Object.keys(providers).forEach((provideKey) => { |
121
|
|
|
// @ts-expect-error |
122
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access |
123
|
|
|
Object.defineProperty(this.app._context.provides, provideKey, { |
124
|
|
|
get: () => providers[provideKey], |
125
|
|
|
enumerable: true, |
126
|
|
|
configurable: true, |
127
|
|
|
// eslint-disable-next-line @typescript-eslint/no-empty-function |
128
|
|
|
set() {}, |
129
|
|
|
}); |
130
|
|
|
}); |
131
|
|
|
|
132
|
|
|
this.root = this.app; |
133
|
|
|
|
134
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call |
135
|
|
|
this.app.use(router); |
136
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call |
137
|
|
|
this.app.use(store); |
138
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call |
139
|
|
|
this.app.use(i18n); |
140
|
|
|
|
141
|
|
|
// Add global properties to root view instance |
142
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access |
143
|
|
|
this.app.$tc = i18n.global.tc; |
144
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access |
145
|
|
|
this.app.$t = i18n.global.t; |
146
|
|
|
|
147
|
|
|
/* eslint-disable max-len */ |
148
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access |
149
|
|
|
this.app.config.globalProperties.$createTitle = function createTitle(this: Vue, identifier: string|null = null, ...additionalParams): string { |
150
|
|
|
if (!this.$root) { |
151
|
|
|
return ''; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
const baseTitle = this.$root.$tc('global.sw-admin-menu.textShopwareAdmin'); |
155
|
|
|
|
156
|
|
|
if (!this.$route.meta || !this.$route.meta.$module) { |
157
|
|
|
return ''; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-member-access |
161
|
|
|
const pageTitle = this.$root.$tc(this.$route.meta.$module.title); |
162
|
|
|
|
163
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment |
164
|
|
|
const params = [baseTitle, pageTitle, identifier, ...additionalParams].filter((item) => { |
165
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access |
166
|
|
|
return item !== null && item.trim() !== ''; |
167
|
|
|
}); |
168
|
|
|
|
169
|
|
|
return params.reverse().join(' | '); |
170
|
|
|
}; |
171
|
|
|
/* eslint-enable max-len */ |
172
|
|
|
|
173
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call |
174
|
|
|
this.app.mount(renderElement); |
175
|
|
|
|
176
|
|
|
if (process.env.NODE_ENV === 'development') { |
177
|
|
|
setupShopwareDevtools(this.root); |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
return this.root; |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
initVue2(renderElement: string, router: VueRouter, providers: { [key: string]: unknown }): Vue { |
184
|
|
|
this.initPlugins(); |
185
|
|
|
this.initDirectives(); |
186
|
|
|
this.initFilters(); |
187
|
|
|
this.initTitle(); |
188
|
|
|
|
189
|
|
|
const store = State._store; |
190
|
|
|
const i18n = this.initLocales(store); |
191
|
|
|
const components = this.getComponents(); |
192
|
|
|
|
193
|
|
|
// add router to View |
194
|
|
|
this.router = router; |
195
|
|
|
// add i18n to View |
196
|
|
|
this.i18n = i18n; |
197
|
|
|
|
198
|
|
|
// Enable performance measurements in development mode |
199
|
|
|
Vue.config.performance = process.env.NODE_ENV !== 'production'; |
200
|
|
|
|
201
|
|
|
this.root = new Vue({ |
202
|
|
|
el: renderElement, |
203
|
|
|
template: '<sw-admin />', |
204
|
|
|
router, |
205
|
|
|
store, |
206
|
|
|
i18n, |
207
|
|
|
provide() { |
208
|
|
|
/** |
209
|
|
|
* Vue 2.7 creates a new copy for each provided value. This caused problems with bottlejs. |
210
|
|
|
* There should be only one instance of each provided value. Therefore we use a getter wrapper. |
211
|
|
|
*/ |
212
|
|
|
return Object.keys(providers).reduce<{ |
213
|
|
|
[key: string]: unknown |
214
|
|
|
}>((acc, provideKey) => { |
215
|
|
|
Object.defineProperty(acc, provideKey, { |
216
|
|
|
get: () => providers[provideKey], |
217
|
|
|
enumerable: true, |
218
|
|
|
configurable: true, |
219
|
|
|
// eslint-disable-next-line @typescript-eslint/no-empty-function |
220
|
|
|
set() {}, |
221
|
|
|
}); |
222
|
|
|
|
223
|
|
|
return acc; |
224
|
|
|
}, {}); |
225
|
|
|
}, |
226
|
|
|
components, |
227
|
|
|
data() { |
228
|
|
|
return { |
229
|
|
|
initError: {}, |
230
|
|
|
}; |
231
|
|
|
}, |
232
|
|
|
}); |
233
|
|
|
|
234
|
|
|
if (process.env.NODE_ENV === 'development') { |
235
|
|
|
setupShopwareDevtools(this.root); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
return this.root; |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
/** |
242
|
|
|
* Initialize of all dependencies. |
243
|
|
|
*/ |
244
|
|
|
async initDependencies() { |
245
|
|
|
const initContainer = this.Application.getContainer('init'); |
246
|
|
|
|
247
|
|
|
// make specific components synchronous |
248
|
|
|
[ |
249
|
|
|
'sw-admin', |
250
|
|
|
'sw-admin-menu', |
251
|
|
|
'sw-button', |
252
|
|
|
'sw-button-process', |
253
|
|
|
'sw-card', |
254
|
|
|
'sw-card-section', |
255
|
|
|
'sw-card-view', |
256
|
|
|
'sw-container', |
257
|
|
|
'sw-desktop', |
258
|
|
|
'sw-empty-state', |
259
|
|
|
'sw-entity-listing', |
260
|
|
|
'sw-entity-multi-select', |
261
|
|
|
'sw-entity-multi-id-select', |
262
|
|
|
'sw-entity-single-select', |
263
|
|
|
'sw-error-boundary', |
264
|
|
|
'sw-extension-component-section', |
265
|
|
|
'sw-field', |
266
|
|
|
'sw-ignore-class', |
267
|
|
|
'sw-loader', |
268
|
|
|
'sw-modal', |
269
|
|
|
'sw-multi-select', |
270
|
|
|
'sw-notification-center', |
271
|
|
|
'sw-notifications', |
272
|
|
|
'sw-page', |
273
|
|
|
'sw-router-link', |
274
|
|
|
'sw-search-bar', |
275
|
|
|
'sw-select-result', |
276
|
|
|
'sw-single-select', |
277
|
|
|
'sw-skeleton', |
278
|
|
|
'sw-skeleton-bar', |
279
|
|
|
'sw-tabs', |
280
|
|
|
'sw-tabs-item', |
281
|
|
|
'sw-version', |
282
|
|
|
/** |
283
|
|
|
* Quickfix for modules with refs and sync behavior. |
284
|
|
|
* They should be removed from the list in the future |
285
|
|
|
* when their async problems got fixed. |
286
|
|
|
*/ |
287
|
|
|
'sw-sales-channel-products-assignment-single-products', |
288
|
|
|
'sw-sales-channel-product-assignment-categories', |
289
|
|
|
'sw-sales-channel-products-assignment-dynamic-product-groups', |
290
|
|
|
'sw-upload-listener', |
291
|
|
|
'sw-media-list-selection-v2', |
292
|
|
|
'sw-media-list-selection-item-v2', |
293
|
|
|
'sw-settings-document-detail', |
294
|
|
|
'sw-settings-product-feature-sets-detail', |
295
|
|
|
'sw-system-config', |
296
|
|
|
'sw-settings-search-searchable-content', |
297
|
|
|
].forEach(componentName => { |
298
|
|
|
Component.markComponentAsSync(componentName); |
299
|
|
|
}); |
300
|
|
|
|
301
|
|
|
// initialize all components |
302
|
|
|
await this.initComponents(); |
303
|
|
|
|
304
|
|
|
// initialize all module locales |
305
|
|
|
this.initModuleLocales(); |
306
|
|
|
|
307
|
|
|
// initialize all module routes |
308
|
|
|
const allRoutes = this.applicationFactory.module.getModuleRoutes(); |
309
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access |
310
|
|
|
initContainer.router.addModuleRoutes(allRoutes); |
311
|
|
|
|
312
|
|
|
// create routes for core and plugins |
313
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access |
314
|
|
|
initContainer.router.createRouterInstance(); |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
|
318
|
|
|
/** |
319
|
|
|
* Initializes all core components as Vue components. |
320
|
|
|
*/ |
321
|
|
|
async initComponents() { |
322
|
|
|
const componentRegistry = this.componentFactory.getComponentRegistry(); |
323
|
|
|
this.componentFactory.resolveComponentTemplates(); |
324
|
|
|
|
325
|
|
|
const initializedComponents = [...componentRegistry.keys()].map((name) => { |
326
|
|
|
return this.createComponent(name); |
327
|
|
|
}); |
328
|
|
|
|
329
|
|
|
await Promise.all(initializedComponents); |
330
|
|
|
|
331
|
|
|
return this.vueComponents; |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
/** |
335
|
|
|
* Initializes all core components as Vue components. |
336
|
|
|
*/ |
337
|
|
|
initModuleLocales() { |
338
|
|
|
// Extend default snippets with module specific snippets |
339
|
|
|
const moduleSnippets = this.applicationFactory.module.getModuleSnippets(); |
340
|
|
|
|
341
|
|
|
Object.entries(moduleSnippets).forEach(([key, moduleSnippet]) => { |
342
|
|
|
this.applicationFactory.locale.extend(key, moduleSnippet); |
343
|
|
|
}); |
344
|
|
|
|
345
|
|
|
return this.applicationFactory.locale; |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
/** |
349
|
|
|
* Returns the component as a Vue component. |
350
|
|
|
* Includes the full rendered template with all overrides. |
351
|
|
|
*/ |
352
|
|
|
createComponent(componentName: string): Promise<Vue> { |
353
|
|
|
return new Promise((resolve) => { |
354
|
|
|
// load sync components directly |
355
|
|
|
if (Component.isSyncComponent && Component.isSyncComponent(componentName)) { |
356
|
|
|
const resolvedComponent = this.componentResolver(componentName); |
357
|
|
|
|
358
|
|
|
if (resolvedComponent === undefined) { |
359
|
|
|
return; |
360
|
|
|
} |
361
|
|
|
|
362
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call |
363
|
|
|
void resolvedComponent.then((component) => { |
364
|
|
|
let vueComponent; |
365
|
|
|
|
366
|
|
|
if (this.vue3) { |
367
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call |
368
|
|
|
this.app.component(componentName, component); |
369
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-assignment |
370
|
|
|
vueComponent = this.app.component(componentName); |
371
|
|
|
} else { |
372
|
|
|
// @ts-expect-error - resolved config does not match completely a standard vue component |
373
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment |
374
|
|
|
vueComponent = Vue.component(componentName, component); |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
|
378
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment |
379
|
|
|
this.vueComponents[componentName] = vueComponent; |
380
|
|
|
resolve(vueComponent as unknown as Vue); |
381
|
|
|
}); |
382
|
|
|
|
383
|
|
|
return; |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
// load async components |
387
|
|
|
let vueComponent; |
388
|
|
|
if (this.vue3) { |
389
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call |
390
|
|
|
this.app.component(componentName, defineAsyncComponent({ |
391
|
|
|
// the loader function |
392
|
|
|
loader: () => this.componentResolver(componentName), |
393
|
|
|
// Delay before showing the loading component. Default: 200ms. |
394
|
|
|
delay: 0, |
395
|
|
|
loadingComponent: { |
396
|
|
|
name: 'async-loading-component', |
397
|
|
|
inheritAttrs: false, |
398
|
|
|
render() { |
399
|
|
|
return h('div', { |
400
|
|
|
style: { display: 'none' }, |
401
|
|
|
}); |
402
|
|
|
}, |
403
|
|
|
}, |
404
|
|
|
})); |
405
|
|
|
|
406
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call |
407
|
|
|
vueComponent = this.app.component(componentName); |
408
|
|
|
} else { |
409
|
|
|
vueComponent = Vue.component(componentName, () => this.componentResolver(componentName)); |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment |
413
|
|
|
this.vueComponents[componentName] = vueComponent; |
414
|
|
|
|
415
|
|
|
resolve(vueComponent as unknown as Vue); |
416
|
|
|
}); |
417
|
|
|
} |
418
|
|
|
|
419
|
|
|
componentResolver(componentName: string) { |
420
|
|
|
if (!this.resolvedComponentConfigs.has(componentName)) { |
421
|
|
|
this.resolvedComponentConfigs.set(componentName, new Promise((resolve) => { |
422
|
|
|
void Component.build(componentName).then((componentConfig) => { |
423
|
|
|
this.resolveMixins(componentConfig); |
424
|
|
|
|
425
|
|
|
resolve(componentConfig); |
426
|
|
|
}); |
427
|
|
|
})); |
428
|
|
|
} |
429
|
|
|
|
430
|
|
|
return this.resolvedComponentConfigs.get(componentName); |
431
|
|
|
} |
432
|
|
|
|
433
|
|
|
/** |
434
|
|
|
* Builds and creates a Vue component using the provided component configuration. |
435
|
|
|
*/ |
436
|
|
|
buildAndCreateComponent(componentConfig: ComponentConfig) { |
437
|
|
|
if (!componentConfig.name) { |
438
|
|
|
throw new Error('Component name is missing'); |
439
|
|
|
} |
440
|
|
|
|
441
|
|
|
const componentName = componentConfig.name; |
442
|
|
|
this.resolveMixins(componentConfig); |
443
|
|
|
|
444
|
|
|
// @ts-expect-error - resolved config does not match completely a standard vue component |
445
|
|
|
const vueComponent = Vue.component(componentConfig.name, componentConfig); |
446
|
|
|
this.vueComponents[componentName] = vueComponent; |
447
|
|
|
|
448
|
|
|
return vueComponent; |
449
|
|
|
} |
450
|
|
|
|
451
|
|
|
/** |
452
|
|
|
* Returns a final Vue component by its name. |
453
|
|
|
*/ |
454
|
|
|
getComponent(componentName: string) { |
455
|
|
|
if (!this.vueComponents[componentName]) { |
456
|
|
|
return null; |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
return this.vueComponents[componentName] as Vue; |
460
|
|
|
} |
461
|
|
|
|
462
|
|
|
/** |
463
|
|
|
* Returns a final Vue component by its name without defineAsyncComponent |
464
|
|
|
* which cannot be used in the router. |
465
|
|
|
*/ |
466
|
|
|
getComponentForRoute(componentName: string) { |
467
|
|
|
return () => this.componentResolver(componentName); |
468
|
|
|
} |
469
|
|
|
|
470
|
|
|
/** |
471
|
|
|
* Returns the complete set of available Vue components. |
472
|
|
|
*/ |
473
|
|
|
// @ts-expect-error - resolved config for each component does not match completely a standard vue component |
474
|
|
|
getComponents() { |
475
|
|
|
return this.vueComponents; |
476
|
|
|
} |
477
|
|
|
|
478
|
|
|
/** |
479
|
|
|
* Returns the adapter wrapper |
480
|
|
|
*/ |
481
|
|
|
getWrapper() { |
482
|
|
|
return Vue; |
483
|
|
|
} |
484
|
|
|
|
485
|
|
|
/** |
486
|
|
|
* Returns the name of the adapter |
487
|
|
|
*/ |
488
|
|
|
getName(): string { |
489
|
|
|
return 'Vue.js'; |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
/** |
493
|
|
|
* Returns the Vue.set function |
494
|
|
|
*/ |
495
|
|
|
setReactive(target: Vue, propertyName: string, value: unknown) { |
496
|
|
|
return Vue.set(target, propertyName, value); |
497
|
|
|
} |
498
|
|
|
|
499
|
|
|
/** |
500
|
|
|
* Returns the Vue.delete function |
501
|
|
|
*/ |
502
|
|
|
deleteReactive(target: Vue, propertyName: string) { |
503
|
|
|
return Vue.delete(target, propertyName); |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
/** |
507
|
|
|
* Private methods |
508
|
|
|
*/ |
509
|
|
|
|
510
|
|
|
/** |
511
|
|
|
* Initialises all plugins for VueJS |
512
|
|
|
* |
513
|
|
|
* @private |
514
|
|
|
*/ |
515
|
|
|
initPlugins() { |
516
|
|
|
// placeholder variable because import is not filterable |
517
|
|
|
let plugins = VuePlugins; |
518
|
|
|
|
519
|
|
|
if (!this.vue3) { |
520
|
|
|
// Add the community plugins to the plugin list |
521
|
|
|
plugins.push(VueRouter, VueI18n, VueMeta); |
522
|
|
|
|
523
|
|
|
// Remove our meta info plugin because we use the vue-meta plugin with Vue 2 |
524
|
|
|
plugins = plugins.filter((plugin) => { |
525
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access |
526
|
|
|
return !plugin?.isMetaInfoPluginInstalled; |
527
|
|
|
}); |
528
|
|
|
} |
529
|
|
|
|
530
|
|
|
VuePlugins.forEach((plugin) => { |
531
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access |
532
|
|
|
if (plugin?.install?.installed) { |
533
|
|
|
return; |
534
|
|
|
} |
535
|
|
|
|
536
|
|
|
if (this.vue3) { |
537
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call |
538
|
|
|
this.app.use(plugin as PluginObject<unknown>); |
539
|
|
|
} else { |
540
|
|
|
Vue.use(plugin as PluginObject<unknown>); |
541
|
|
|
} |
542
|
|
|
}); |
543
|
|
|
|
544
|
|
|
return true; |
545
|
|
|
} |
546
|
|
|
|
547
|
|
|
/** |
548
|
|
|
* Initializes all custom directives. |
549
|
|
|
* |
550
|
|
|
* @private |
551
|
|
|
*/ |
552
|
|
|
initDirectives() { |
553
|
|
|
const registry = this.Application.getContainer('factory').directive.getDirectiveRegistry(); |
554
|
|
|
|
555
|
|
|
registry.forEach((directive, name) => { |
556
|
|
|
Vue.directive(name, directive); |
557
|
|
|
}); |
558
|
|
|
|
559
|
|
|
return true; |
560
|
|
|
} |
561
|
|
|
|
562
|
|
|
/** |
563
|
|
|
* Initialises helpful filters for global use |
564
|
|
|
* |
565
|
|
|
* @private |
566
|
|
|
*/ |
567
|
|
|
initFilters() { |
568
|
|
|
const registry = this.Application.getContainer('factory').filter.getRegistry(); |
569
|
|
|
|
570
|
|
|
registry.forEach((factoryMethod, name) => { |
571
|
|
|
Vue.filter(name, factoryMethod); |
572
|
|
|
}); |
573
|
|
|
|
574
|
|
|
return true; |
575
|
|
|
} |
576
|
|
|
|
577
|
|
|
/** |
578
|
|
|
* Initialises the standard locales. |
579
|
|
|
*/ |
580
|
|
|
initLocales(store: Store<VuexRootState>) { |
581
|
|
|
const registry = this.localeFactory.getLocaleRegistry(); |
582
|
|
|
const messages = {}; |
583
|
|
|
const fallbackLocale = Shopware.Context.app.fallbackLocale as FallbackLocale; |
584
|
|
|
|
585
|
|
|
registry.forEach((localeMessages, key) => { |
586
|
|
|
store.commit('registerAdminLocale', key); |
587
|
|
|
// @ts-expect-error - key is safe because we iterate through the registry |
588
|
|
|
messages[key] = localeMessages; |
589
|
|
|
}); |
590
|
|
|
|
591
|
|
|
const lastKnownLocale = this.localeFactory.getLastKnownLocale(); |
592
|
|
|
void store.dispatch('setAdminLocale', lastKnownLocale); |
593
|
|
|
|
594
|
|
|
const options = { |
595
|
|
|
locale: lastKnownLocale, |
596
|
|
|
fallbackLocale, |
597
|
|
|
silentFallbackWarn: true, |
598
|
|
|
sync: true, |
599
|
|
|
messages, |
600
|
|
|
}; |
601
|
|
|
|
602
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access |
603
|
|
|
const i18n = window._features_?.vue3 ? createI18n(options) : new VueI18n(options); |
604
|
|
|
|
605
|
|
|
store.subscribe(({ type }, state) => { |
606
|
|
|
if (type === 'setAdminLocale') { |
607
|
|
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion |
608
|
|
|
i18n.locale = state.session.currentLocale!; |
609
|
|
|
} |
610
|
|
|
}); |
611
|
|
|
|
612
|
|
|
this.setLocaleFromUser(store); |
613
|
|
|
|
614
|
|
|
return i18n; |
615
|
|
|
} |
616
|
|
|
|
617
|
|
|
setLocaleFromUser(store: Store<VuexRootState>) { |
618
|
|
|
const currentUser = store.state.session.currentUser; |
619
|
|
|
|
620
|
|
|
if (currentUser) { |
621
|
|
|
const userLocaleId = currentUser.localeId; |
622
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access |
623
|
|
|
Shopware.Service('localeHelper').setLocaleWithId(userLocaleId); |
624
|
|
|
} |
625
|
|
|
} |
626
|
|
|
|
627
|
|
|
/** |
628
|
|
|
* Extends Vue prototype to access $createTitle function |
629
|
|
|
* |
630
|
|
|
* @private |
631
|
|
|
*/ |
632
|
|
|
initTitle() { |
633
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access |
634
|
|
|
if (Vue.prototype.hasOwnProperty('$createTitle')) { |
635
|
|
|
return; |
636
|
|
|
} |
637
|
|
|
|
638
|
|
|
/** |
639
|
|
|
* Generates the document title out of the given VueComponent and parameters |
640
|
|
|
*/ |
641
|
|
|
// @ts-expect-error - additionalParams is not typed |
642
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,max-len |
643
|
|
|
Vue.prototype.$createTitle = function createTitle(this: Vue, identifier: string|null = null, ...additionalParams): string { |
644
|
|
|
if (!this.$root) { |
645
|
|
|
return ''; |
646
|
|
|
} |
647
|
|
|
|
648
|
|
|
const baseTitle = this.$root.$tc('global.sw-admin-menu.textShopwareAdmin'); |
649
|
|
|
|
650
|
|
|
if (!this.$route.meta || !this.$route.meta.$module) { |
651
|
|
|
return ''; |
652
|
|
|
} |
653
|
|
|
|
654
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-member-access |
655
|
|
|
const pageTitle = this.$root.$tc(this.$route.meta.$module.title); |
656
|
|
|
|
657
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment |
658
|
|
|
const params = [baseTitle, pageTitle, identifier, ...additionalParams].filter((item) => { |
659
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access |
660
|
|
|
return item !== null && item.trim() !== ''; |
661
|
|
|
}); |
662
|
|
|
|
663
|
|
|
return params.reverse().join(' | '); |
664
|
|
|
}; |
665
|
|
|
} |
666
|
|
|
|
667
|
|
|
/** |
668
|
|
|
* Recursively resolves mixins referenced by name |
669
|
|
|
* |
670
|
|
|
* @private |
671
|
|
|
*/ |
672
|
|
|
resolveMixins(componentConfig: ComponentConfig) { |
673
|
|
|
// If the mixin is a string, use our mixin registry |
674
|
|
|
if (componentConfig.mixins?.length) { |
675
|
|
|
componentConfig.mixins = componentConfig.mixins.map((mixin) => { |
676
|
|
|
if (typeof mixin === 'string') { |
677
|
|
|
return Mixin.getByName(mixin); |
678
|
|
|
} |
679
|
|
|
|
680
|
|
|
return mixin; |
681
|
|
|
}); |
682
|
|
|
} |
683
|
|
|
|
684
|
|
|
if (componentConfig.extends) { |
685
|
|
|
// @ts-expect-error - extends can be a string or a component config |
686
|
|
|
this.resolveMixins(componentConfig.extends); |
687
|
|
|
} |
688
|
|
|
} |
689
|
|
|
} |
690
|
|
|
|