@rws-framework/client 2.21.2 → 2.21.3

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.
@@ -23,7 +23,7 @@ function processImportPath(importPath, rwsWorkspaceDir, appRootDir, fileRootDir
23
23
  }
24
24
 
25
25
  if (importPath.indexOf('@workspace') === 0) {
26
- return path.join(workspaceDir, importPath.replace('@workspace', ''));
26
+ return path.join(workspaceDir, 'src', importPath.replace('@workspace', ''));
27
27
  }
28
28
 
29
29
  if (importPath.indexOf('@public') === 0) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rws-framework/client",
3
3
  "private": false,
4
- "version": "2.21.2",
4
+ "version": "2.21.3",
5
5
  "main": "src/index.ts",
6
6
  "scripts": {
7
7
  "docs": "typedoc --tsconfig ./tsconfig.json"
@@ -5,6 +5,7 @@ import UtilsService, { UtilsServiceInstance } from '../services/UtilsService';
5
5
  import DOMService, { DOMServiceInstance, DOMOutputType } from '../services/DOMService';
6
6
  import ApiService, { ApiServiceInstance } from '../services/ApiService';
7
7
  import NotifyService, { NotifyServiceInstance } from '../services/NotifyService';
8
+ import IndexedDBService, { IndexedDBServiceInstance } from '../services/IndexedDBService';
8
9
  import { IRWSViewComponent, IAssetShowOptions } from '../types/IRWSViewComponent';
9
10
  import RWSWindow, { RWSWindowComponentInterface, loadRWSRichWindow } from '../types/RWSWindow';
10
11
  import { applyConstructor, RWSInject } from './_decorator';
@@ -14,22 +15,26 @@ import { IFastDefinition, isDefined, defineComponent, getDefinition } from './_d
14
15
  import { on, $emitDown, observe, sendEventToOutside } from './_event_handling';
15
16
 
16
17
  type ComposeMethodType<
17
- T extends FoundationElementDefinition,
18
+ T extends FoundationElementDefinition,
18
19
  K extends Constructable<RWSViewComponent>
19
20
  > = (this: K, elementDefinition: T) => (overrideDefinition?: OverrideFoundationElementDefinition<T>) => FoundationElementRegistry<FoundationElementDefinition, T>;
20
21
 
22
+ type CSSInjectMode = 'adopted' | 'legacy' | 'both';
23
+
24
+ const _DEFAULT_INJECT_CSS_CACHE_LIMIT_DAYS = 1;
25
+
21
26
  export interface IWithCompose<T extends RWSViewComponent> {
22
27
  [key: string]: any
23
- new (...args: any[]): T;
28
+ new(...args: any[]): T;
24
29
  definition?: IFastDefinition
25
30
  defineComponent: <T extends RWSViewComponent>(this: IWithCompose<T>) => void
26
31
  isDefined<T extends RWSViewComponent>(this: IWithCompose<T>): boolean
27
32
  compose: ComposeMethodType<FoundationElementDefinition, Constructable<T>>;
28
33
  define<TType extends (...params: any[]) => any>(type: TType, nameOrDef?: string | PartialFASTElementDefinition | undefined): TType;
29
34
  _verbose: boolean;
30
- _toInject: {[key: string]: TheRWSService};
31
- _depKeys: {[key: string]: string[]};
32
- _externalAttrs: { [key:string]: string[] };
35
+ _toInject: { [key: string]: TheRWSService };
36
+ _depKeys: { [key: string]: string[] };
37
+ _externalAttrs: { [key: string]: string[] };
33
38
  setExternalAttr: (componentName: string, key: string) => void
34
39
  sendEventToOutside: <T>(eventName: string, data: T) => void
35
40
  _EVENTS: {
@@ -48,35 +53,39 @@ abstract class RWSViewComponent extends FoundationElement implements IRWSViewCom
48
53
 
49
54
  static autoLoadFastElement = true;
50
55
  static _defined: { [key: string]: boolean } = {};
51
- static _toInject: {[key: string]: TheRWSService} = {};
52
- static _depKeys: {[key: string]: string[]} = {_all: []};
56
+ static _toInject: { [key: string]: TheRWSService } = {};
57
+ static _depKeys: { [key: string]: string[] } = { _all: [] };
53
58
  static _externalAttrs: { [key: string]: string[] } = {};
54
59
  static _verbose: boolean = false;
55
60
 
61
+ private static FORCE_INJECT_STYLES?: string[] = [];
62
+ private static FORCE_INJECT_MODE?: CSSInjectMode = 'adopted';
63
+
56
64
  static _EVENTS = {
57
65
  component_define: 'rws:lifecycle:defineComponent',
58
66
  component_parted_load: 'rws:lifecycle:loadPartedComponents',
59
67
  }
60
68
 
61
- @RWSInject(ConfigService, true) protected config: ConfigServiceInstance;
69
+ @RWSInject(IndexedDBService, true) protected indexedDBService: IndexedDBServiceInstance;
70
+ @RWSInject(ConfigService, true) protected config: ConfigServiceInstance;
62
71
  @RWSInject(DOMService, true) protected domService: DOMServiceInstance;
63
72
  @RWSInject(UtilsService, true) protected utilsService: UtilsServiceInstance;
64
- @RWSInject(ApiService, true) protected apiService: ApiServiceInstance;
73
+ @RWSInject(ApiService, true) protected apiService: ApiServiceInstance;
65
74
  @RWSInject(NotifyService, true) protected notifyService: NotifyServiceInstance;
66
75
 
67
76
  @observable trashIterator: number = 0;
68
77
  @observable fileAssets: {
69
78
  [key: string]: ViewTemplate
70
- } = {};
79
+ } = {};
71
80
 
72
81
  constructor() {
73
- super();
74
- applyConstructor(this);
82
+ super();
83
+ applyConstructor(this);
75
84
  }
76
85
 
77
- connectedCallback() {
78
- super.connectedCallback();
79
- applyConstructor(this);
86
+ connectedCallback() {
87
+ super.connectedCallback();
88
+ applyConstructor(this);
80
89
 
81
90
  if (!(this.constructor as IWithCompose<this>).definition && (this.constructor as IWithCompose<this>).autoLoadFastElement) {
82
91
  throw new Error('RWS component is not named. Add `static definition = {name, template};`');
@@ -84,8 +93,12 @@ abstract class RWSViewComponent extends FoundationElement implements IRWSViewCom
84
93
 
85
94
  this.applyFileList();
86
95
 
96
+ if (RWSViewComponent.FORCE_INJECT_STYLES) {
97
+ this.injectStyles(RWSViewComponent.FORCE_INJECT_STYLES, RWSViewComponent.FORCE_INJECT_MODE);
98
+ }
99
+
87
100
  RWSViewComponent.instances.push(this);
88
- }
101
+ }
89
102
 
90
103
  passRouteParams(routeParams: Record<string, string> = null) {
91
104
  if (routeParams) {
@@ -111,8 +124,7 @@ abstract class RWSViewComponent extends FoundationElement implements IRWSViewCom
111
124
  return $emitDown.bind(this)(eventName, payload);
112
125
  }
113
126
 
114
- observe(callback: (component: RWSViewComponent, node: Node, observer: MutationObserver) => Promise<void>, condition: (component: RWSViewComponent, node: Node) => boolean = null, observeRemoved: boolean = false)
115
- {
127
+ observe(callback: (component: RWSViewComponent, node: Node, observer: MutationObserver) => Promise<void>, condition: (component: RWSViewComponent, node: Node) => boolean = null, observeRemoved: boolean = false) {
116
128
  return observe.bind(this)(callback, condition, observeRemoved);
117
129
  }
118
130
 
@@ -187,8 +199,15 @@ abstract class RWSViewComponent extends FoundationElement implements IRWSViewCom
187
199
  sendEventToOutside(eventName, data);
188
200
  }
189
201
 
190
- private applyFileList(): void
191
- {
202
+ static injectStyles(linkedStyles: string[], mode?: CSSInjectMode) {
203
+ if (mode) {
204
+ RWSViewComponent.FORCE_INJECT_MODE = mode;
205
+ }
206
+
207
+ RWSViewComponent.FORCE_INJECT_STYLES = linkedStyles;
208
+ }
209
+
210
+ private applyFileList(): void {
192
211
  try {
193
212
  (this.constructor as IWithCompose<this>).fileList.forEach((file: string) => {
194
213
  if (this.fileAssets[file]) {
@@ -203,11 +222,10 @@ abstract class RWSViewComponent extends FoundationElement implements IRWSViewCom
203
222
  console.error('Error loading file content:', e.message);
204
223
  console.error(e.stack);
205
224
  }
206
- }
225
+ }
207
226
 
208
- static setExternalAttr(componentName: string, key: string)
209
- {
210
- if(!Object.keys(RWSViewComponent._externalAttrs).includes(componentName)){
227
+ static setExternalAttr(componentName: string, key: string) {
228
+ if (!Object.keys(RWSViewComponent._externalAttrs).includes(componentName)) {
211
229
  RWSViewComponent._externalAttrs[componentName] = [];
212
230
  }
213
231
 
@@ -218,24 +236,75 @@ abstract class RWSViewComponent extends FoundationElement implements IRWSViewCom
218
236
  this.getInstances().forEach(instance => instance.forceReload());
219
237
  }
220
238
 
221
- static isDefined<T extends RWSViewComponent>(this: IWithCompose<T>): boolean
222
- {
239
+ static isDefined<T extends RWSViewComponent>(this: IWithCompose<T>): boolean {
223
240
  return isDefined<T>(this);
224
241
  }
225
242
 
226
- static defineComponent<T extends RWSViewComponent>(this: IWithCompose<T>): void
227
- {
243
+ static defineComponent<T extends RWSViewComponent>(this: IWithCompose<T>): void {
228
244
  return defineComponent<T>(this);
229
245
  }
230
246
 
231
- static getDefinition(tagName: string, htmlTemplate: ViewTemplate, styles: ElementStyles = null)
232
- {
247
+ static getDefinition(tagName: string, htmlTemplate: ViewTemplate, styles: ElementStyles = null) {
233
248
  return getDefinition(tagName, htmlTemplate, styles);
234
249
  }
235
250
 
236
251
  private static getInstances(): RWSViewComponent[] {
237
252
  return RWSViewComponent.instances;
238
253
  }
254
+
255
+ protected async injectStyles(styleLinks: string[], mode: CSSInjectMode = 'adopted', maxDaysExp?: number) {
256
+ const dbName = 'css-cache';
257
+ const storeName = 'styles';
258
+ const db = await this.indexedDBService.openDB(dbName, storeName);
259
+ const maxAgeMs = 1000 * 60 * 60 * 24; // 24h
260
+ const maxDaysAge = maxDaysExp ? maxDaysExp : _DEFAULT_INJECT_CSS_CACHE_LIMIT_DAYS;
261
+ const maxAgeDays = maxAgeMs * maxDaysAge;
262
+
263
+ let adoptedSheets: CSSStyleSheet[] = [];
264
+
265
+ for (const styleLink of styleLinks) {
266
+ if (mode === 'legacy' || mode === 'both') {
267
+ const link = document.createElement('link');
268
+ link.rel = 'stylesheet';
269
+ link.href = styleLink;
270
+ this.getShadowRoot().appendChild(link);
271
+ }
272
+
273
+ if (mode === 'adopted' || mode === 'both') {
274
+ const entry = await this.indexedDBService.getFromDB(db, storeName, styleLink);
275
+
276
+ let cssText: string | null = null;
277
+
278
+ if (entry && typeof entry === 'object' && 'css' in entry && 'timestamp' in entry) {
279
+ const expired = Date.now() - entry.timestamp > maxAgeDays;
280
+ if (!expired) {
281
+ cssText = entry.css;
282
+ }
283
+ }
284
+
285
+ if (!cssText) {
286
+ cssText = await fetch(styleLink).then(res => res.text());
287
+ await this.indexedDBService.saveToDB(db, storeName, styleLink, {
288
+ css: cssText,
289
+ timestamp: Date.now()
290
+ });
291
+ console.log(`System saved stylesheet: ${styleLink} to IndexedDB`)
292
+ }
293
+
294
+ const sheet = new CSSStyleSheet();
295
+ await sheet.replace(cssText);
296
+
297
+ adoptedSheets.push(sheet);
298
+ }
299
+ }
300
+
301
+ if(adoptedSheets.length){
302
+ this.getShadowRoot().adoptedStyleSheets = [
303
+ ...adoptedSheets,
304
+ ...this.getShadowRoot().adoptedStyleSheets,
305
+ ];
306
+ }
307
+ }
239
308
  }
240
309
 
241
310
  export default RWSViewComponent;
@@ -72,10 +72,12 @@ const applyConstructor = (component: RWSViewComponent, x: boolean = false): void
72
72
 
73
73
  const regServices = loadRWSRichWindow().RWS._registered;
74
74
 
75
+
75
76
 
76
77
  const depsToInject: string[] = (mainConstructor as IWithCompose<RWSViewComponent>)._depKeys[mainConstructor.name] || [];
77
78
  const depsInInjector: string[] = Object.keys(existingInjectedDependencies);
78
79
 
80
+
79
81
  const toInject: string[] = [...depsToInject]
80
82
 
81
83
  type KeyType = {[key: string]: TheRWSService | string };
@@ -84,7 +86,7 @@ const applyConstructor = (component: RWSViewComponent, x: boolean = false): void
84
86
 
85
87
  function inject(services: KeyType){
86
88
  for (const prop in services) {
87
- const service = (typeof services[prop] === 'string' ? existingInjectedDependencies[prop] : services[prop]) as TheRWSService;
89
+ const service = (typeof services[prop] === 'string' ? existingInjectedDependencies[prop] : services[prop]) as TheRWSService;
88
90
  _target[prop] = service;
89
91
  }
90
92
  }
@@ -96,7 +98,7 @@ const applyConstructor = (component: RWSViewComponent, x: boolean = false): void
96
98
 
97
99
  const defaultDeps: [string, TheRWSService][] = Object.keys(existingInjectedDependencies)
98
100
  .filter((depKey: string) => existingInjectedDependencies[depKey].isDefault()).map((depKey => [depKey, existingInjectedDependencies[depKey]]));
99
-
101
+
100
102
  inject(defaultDeps.reduce((acc: KeyType, cur: [string, TheRWSService]) => {
101
103
  acc[cur[0]] = cur[1];
102
104
  return acc;
@@ -8,8 +8,7 @@ import { handleExternalChange } from '../_attrs/_external_handler';
8
8
  type InjectDecoratorReturnType = (target: any, key?: string | number | undefined, parameterIndex?: number) => void;
9
9
  type TargetType = any;
10
10
 
11
- function addToComponentInjection(targetComponentName: string, constructor: any, depKey: string, dependencyClass: Key, isDefaultService: boolean = false){
12
-
11
+ function addToComponentInjection(targetComponentName: string, constructor: any, depKey: string, dependencyClass: Key, isDefaultService: boolean = false){
13
12
  if(isDefaultService){
14
13
  targetComponentName = '_all';
15
14
  }
@@ -0,0 +1,49 @@
1
+ import RWSService from './_service';
2
+
3
+ class IndexedDBService extends RWSService {
4
+ static _DEFAULT: boolean = true;
5
+ openDB(dbName: string, storeName: string): Promise<IDBDatabase> {
6
+ return new Promise((resolve, reject) => {
7
+ const request = indexedDB.open(dbName, 1);
8
+
9
+ request.onupgradeneeded = () => {
10
+ const db = request.result;
11
+ if (!db.objectStoreNames.contains(storeName)) {
12
+ db.createObjectStore(storeName);
13
+ }
14
+ };
15
+
16
+ request.onsuccess = () => resolve(request.result);
17
+ request.onerror = () => reject(request.error);
18
+ });
19
+ }
20
+
21
+ getFromDB(db: IDBDatabase, store: string, key: string): Promise<{
22
+ css: string,
23
+ timestamp: number
24
+ } | null> {
25
+ return new Promise((resolve, reject) => {
26
+ const tx = db.transaction(store, 'readonly');
27
+ const request = tx.objectStore(store).get(key);
28
+
29
+ request.onsuccess = () => resolve(request.result ?? null);
30
+ request.onerror = () => reject(request.error);
31
+ });
32
+ }
33
+
34
+ saveToDB(db: IDBDatabase, store: string, key: string, value: {
35
+ css: string,
36
+ timestamp: number
37
+ }): Promise<void> {
38
+ return new Promise((resolve, reject) => {
39
+ const tx = db.transaction(store, 'readwrite');
40
+ const request = tx.objectStore(store).put(value, key);
41
+
42
+ request.onsuccess = () => resolve();
43
+ request.onerror = () => reject(request.error);
44
+ });
45
+ }
46
+ }
47
+
48
+ export default IndexedDBService.getSingleton();
49
+ export { IndexedDBService as IndexedDBServiceInstance };