@momentum-design/components 0.25.2 → 0.25.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.
@@ -101,28 +101,29 @@ class Icon extends Component {
101
101
  */
102
102
  async getIconData() {
103
103
  if (this.iconProviderContext.value) {
104
- const { fileExtension, url, iconsCache, shouldCache } = this.iconProviderContext.value;
104
+ const { fileExtension, url, cacheName, cacheStrategy } = this.iconProviderContext.value;
105
105
  if (url && fileExtension && this.name) {
106
- // abort the previous fetch request if it is still pending
107
- // before retreiving from cache
108
- this.abortController.abort();
109
- // check if icon is already fetched and stored in the iconsCache map
110
- if (iconsCache.has(this.name)) {
111
- const iconElement = this.prepareIconElement(iconsCache.get(this.name));
112
- this.handleIconLoadedSuccess(iconElement);
113
- return;
114
- }
115
- this.abortController = new AbortController();
106
+ // function to abort the fetch request and create a new signal
107
+ // (directly passing the abortcontroller to the fetch request per reference
108
+ // will not work due to JS call-by-sharing behavior)
109
+ const renewSignal = () => {
110
+ this.abortController.abort();
111
+ this.abortController = new AbortController();
112
+ return this.abortController.signal;
113
+ };
116
114
  try {
117
- // fetch icon from backend
118
- const iconData = await dynamicSVGImport(url, this.name, fileExtension, this.abortController.signal);
115
+ // fetch icon data (including caching logic)
116
+ const iconData = await dynamicSVGImport({
117
+ url,
118
+ name: this.name,
119
+ fileExtension,
120
+ cacheName,
121
+ cacheStrategy,
122
+ renewSignal,
123
+ });
119
124
  // parse the fetched icon string to an html element and set the attributes
120
125
  const iconElement = this.prepareIconElement(iconData);
121
126
  this.handleIconLoadedSuccess(iconElement);
122
- if (shouldCache) {
123
- // store the fetched icon string in the iconsCache map
124
- iconsCache.set(this.name, iconData);
125
- }
126
127
  }
127
128
  catch (error) {
128
129
  this.handleIconLoadedFailure(error);
@@ -1,5 +1,20 @@
1
+ import type { CacheStrategy } from '../../utils/icon-cache';
2
+ interface Args {
3
+ url: string;
4
+ name: string;
5
+ fileExtension: string;
6
+ cacheStrategy?: CacheStrategy;
7
+ cacheName?: string;
8
+ renewSignal: () => AbortSignal;
9
+ }
1
10
  /**
2
11
  * Fetches a dynamic SVG icon based on the provided `url`, `name` and `fileExtension`.
12
+ * The fetch is aborted if the signal is aborted.
13
+ *
14
+ * This function also includes the logic to cache the fetched icon using the In Memory Cache or Web Cache API.
15
+ * If the `cacheStrategy` is set to `web-cache-api` or `in-memory-cache` and `cacheName` is provided,
16
+ * the fetched icon will be cached using the respective cache.
17
+ *
3
18
  * It will throw an error if the response is not ok.
4
19
  *
5
20
  * @param url - The base url of the icon
@@ -7,8 +22,11 @@
7
22
  * @param fileExtension - The file extension of the icon
8
23
  * @param signal - The signal to abort the fetch.
9
24
  * It is used to cancel the fetch when the component is disconnected or updated.
25
+ * @param cacheStrategy - The cache strategy to use.
26
+ * @param cacheName - The cache name to use.
27
+ *
10
28
  * @returns Response string from the fetch
11
29
  * @throws Error if the response is not ok
12
30
  */
13
- declare const dynamicSVGImport: (url: string, name: string, fileExtension: string, signal: AbortSignal) => Promise<string>;
31
+ declare const dynamicSVGImport: ({ url, name, fileExtension, cacheStrategy, cacheName, renewSignal, }: Args) => Promise<string>;
14
32
  export { dynamicSVGImport };
@@ -1,6 +1,25 @@
1
- /* eslint-disable implicit-arrow-linebreak */
1
+ import { iconsCache } from '../../utils/icon-cache';
2
+ /**
3
+ * Utility function for fetching the icon from the provided `request`.
4
+ * It will throw an error if the response is not ok.
5
+ * @param request - The request object to fetch the icon
6
+ * @returns Promise<Response> - The response from the fetch
7
+ * @throws Error if the response is not ok
8
+ */
9
+ const fetchIcon = async (request) => fetch(request).then((response) => {
10
+ if (!response.ok) {
11
+ throw new Error('There was a problem while fetching the icon!');
12
+ }
13
+ return response;
14
+ });
2
15
  /**
3
16
  * Fetches a dynamic SVG icon based on the provided `url`, `name` and `fileExtension`.
17
+ * The fetch is aborted if the signal is aborted.
18
+ *
19
+ * This function also includes the logic to cache the fetched icon using the In Memory Cache or Web Cache API.
20
+ * If the `cacheStrategy` is set to `web-cache-api` or `in-memory-cache` and `cacheName` is provided,
21
+ * the fetched icon will be cached using the respective cache.
22
+ *
4
23
  * It will throw an error if the response is not ok.
5
24
  *
6
25
  * @param url - The base url of the icon
@@ -8,14 +27,53 @@
8
27
  * @param fileExtension - The file extension of the icon
9
28
  * @param signal - The signal to abort the fetch.
10
29
  * It is used to cancel the fetch when the component is disconnected or updated.
30
+ * @param cacheStrategy - The cache strategy to use.
31
+ * @param cacheName - The cache name to use.
32
+ *
11
33
  * @returns Response string from the fetch
12
34
  * @throws Error if the response is not ok
13
35
  */
14
- const dynamicSVGImport = async (url, name, fileExtension, signal) => {
15
- const response = await fetch(`${url}/${name}.${fileExtension}`, { signal });
16
- if (!response.ok) {
17
- throw new Error('There was a problem while fetching the icon!');
36
+ const dynamicSVGImport = async ({ url, name, fileExtension, cacheStrategy, cacheName, renewSignal, }) => {
37
+ // abort the previous fetch request if it is still pending
38
+ // and create a new signal
39
+ const signal = renewSignal();
40
+ const request = new Request(`${url}/${name}.${fileExtension}`, {
41
+ signal,
42
+ });
43
+ // if there is no cache defined (cacheName and cacheStrategy properly set),
44
+ // fetch the icon and return the response
45
+ if (!cacheName || !cacheStrategy || !['in-memory-cache', 'web-cache-api'].includes(cacheStrategy)) {
46
+ return fetchIcon(request).then((response) => response.text());
18
47
  }
19
- return response.text();
48
+ return iconsCache(cacheName, cacheStrategy).then((iconsCache) => iconsCache
49
+ .get(request)
50
+ .then((responseFromCache) => {
51
+ // **If entry in cache, return**
52
+ if (responseFromCache) {
53
+ return responseFromCache;
54
+ }
55
+ // **Otherwise, fetch and cache if successful**
56
+ // Both fetchIcon() and iconsCache.set() "consume" the request,
57
+ // so we need to make a copy.
58
+ // (see https://developer.mozilla.org/en-US/docs/Web/API/Request/clone)
59
+ return fetchIcon(request.clone()).then((response) => {
60
+ var _a;
61
+ // This avoids caching responses that we know are errors
62
+ // (i.e. HTTP status code of 4xx or 5xx).
63
+ if (response.status < 400 && response.headers.has('content-type')) {
64
+ // Call .clone() on the response to save copy to cache.
65
+ // https://developer.mozilla.org/en-US/docs/Web/API/Request/clone
66
+ return (_a = iconsCache.set) === null || _a === void 0 ? void 0 : _a.call(iconsCache, request, response.clone()).then(() => response.text());
67
+ }
68
+ return response.text();
69
+ });
70
+ })
71
+ .catch((error) => {
72
+ // Note that a HTTP error response (e.g. 404) will NOT trigger
73
+ // an exception.
74
+ // It will return a normal response object that has the appropriate
75
+ // error code set.
76
+ throw new Error(`Error in caching the Icon ${name}, ${error}`);
77
+ }));
20
78
  };
21
79
  export { dynamicSVGImport };
@@ -8,11 +8,15 @@ import IconProviderContext from './iconprovider.context';
8
8
  * that only a url has to be passed in from which the icons will be
9
9
  * fetched.
10
10
  *
11
- * If `shouldCache` is set to true, the IconProvider will cache the icons
12
- * in a Map to avoid fetching the same icon multiple times over the network.
11
+ * If `cacheStrategy` is provided, the IconProvider will cache the icons
12
+ * in the selected cache (either web-api-cache or in-memory-cache),
13
+ * to avoid fetching the same icon multiple times over the network.
13
14
  * This is useful when the same icon is used multiple times in the application.
14
- * Keep in mind that this cache is not persisted and will be lost when the
15
+ * To consider:
16
+ * - The `in-memory-cache` is not persisted and will be lost when the
15
17
  * IconProvider is removed from the DOM.
18
+ * - The `web-api-cache` is persisted, but only works in https environments
19
+ * (https://developer.mozilla.org/en-US/docs/Web/API/Cache).
16
20
  *
17
21
  * @tagname mdc-iconprovider
18
22
  *
@@ -32,11 +36,15 @@ declare class IconProvider extends Provider<IconProviderContext> {
32
36
  * that only a url has to be passed in from which the icons will be
33
37
  * fetched.
34
38
  *
35
- * If `shouldCache` is set to true, the IconProvider will cache the icons
36
- * in a Map to avoid fetching the same icon multiple times over the network.
39
+ * If `cacheStrategy` is provided, the IconProvider will cache the icons
40
+ * in the selected cache (either web-api-cache or in-memory-cache),
41
+ * to avoid fetching the same icon multiple times over the network.
37
42
  * This is useful when the same icon is used multiple times in the application.
38
- * Keep in mind that this cache is not persisted and will be lost when the
43
+ * To consider:
44
+ * - The `in-memory-cache` is not persisted and will be lost when the
39
45
  * IconProvider is removed from the DOM.
46
+ * - The `web-api-cache` is persisted, but only works in https environments
47
+ * (https://developer.mozilla.org/en-US/docs/Web/API/Cache).
40
48
  *
41
49
  * @tagname mdc-iconprovider
42
50
  *
@@ -65,12 +73,29 @@ declare class IconProvider extends Provider<IconProviderContext> {
65
73
  */
66
74
  size?: number;
67
75
  /**
68
- * If the IconProvider should cache the icons
69
- * in a Map to avoid fetching the same icon multiple times
76
+ * Icons Cache Strategy to use
70
77
  *
71
- * @default false
78
+ * Choose `in-memory-cache` to cache icons in a JS cache (in-memory cache).
79
+ * Choose `web-cache-api` to cache icons using the Web Cache API.
80
+ *
81
+ * NOTE: `cache-name` must be provided if `cache-strategy` is provided.
82
+ *
83
+ * If not provided or invalid value provided, the icons will not be cached.
84
+ * @default undefined
85
+ */
86
+ cacheStrategy?: 'in-memory-cache' | 'web-cache-api';
87
+ /**
88
+ * Icons Cache Name to use
89
+ *
90
+ * If provided, Icons inside the provider will be cached in the
91
+ * cache (determined by `cache-strategy`) with the provided name.
92
+ *
93
+ * NOTE: `cache-name` requires `cache-strategy` to be set.
94
+ *
95
+ * If not provided, the icons will not be cached.
96
+ * @default undefined
72
97
  */
73
- shouldCache?: boolean;
98
+ cacheName?: string;
74
99
  private updateValuesInContext;
75
100
  protected updateContext(): void;
76
101
  }
@@ -19,11 +19,15 @@ import { ALLOWED_FILE_EXTENSIONS, DEFAULTS, ALLOWED_LENGTH_UNITS } from './iconp
19
19
  * that only a url has to be passed in from which the icons will be
20
20
  * fetched.
21
21
  *
22
- * If `shouldCache` is set to true, the IconProvider will cache the icons
23
- * in a Map to avoid fetching the same icon multiple times over the network.
22
+ * If `cacheStrategy` is provided, the IconProvider will cache the icons
23
+ * in the selected cache (either web-api-cache or in-memory-cache),
24
+ * to avoid fetching the same icon multiple times over the network.
24
25
  * This is useful when the same icon is used multiple times in the application.
25
- * Keep in mind that this cache is not persisted and will be lost when the
26
+ * To consider:
27
+ * - The `in-memory-cache` is not persisted and will be lost when the
26
28
  * IconProvider is removed from the DOM.
29
+ * - The `web-api-cache` is persisted, but only works in https environments
30
+ * (https://developer.mozilla.org/en-US/docs/Web/API/Cache).
27
31
  *
28
32
  * @tagname mdc-iconprovider
29
33
  *
@@ -52,13 +56,6 @@ class IconProvider extends Provider {
52
56
  * @default 1
53
57
  */
54
58
  this.size = DEFAULTS.SIZE;
55
- /**
56
- * If the IconProvider should cache the icons
57
- * in a Map to avoid fetching the same icon multiple times
58
- *
59
- * @default false
60
- */
61
- this.shouldCache = DEFAULTS.SHOULD_CACHE;
62
59
  }
63
60
  /**
64
61
  * Context object of the IconProvider, to be consumed by child components
@@ -78,7 +75,8 @@ class IconProvider extends Provider {
78
75
  }
79
76
  this.context.value.url = this.url;
80
77
  this.context.value.size = this.size;
81
- this.context.value.shouldCache = this.shouldCache;
78
+ this.context.value.cacheName = this.cacheName;
79
+ this.context.value.cacheStrategy = this.cacheStrategy;
82
80
  if (this.lengthUnit && ALLOWED_LENGTH_UNITS.includes(this.lengthUnit)) {
83
81
  this.context.value.lengthUnit = this.lengthUnit;
84
82
  }
@@ -93,7 +91,8 @@ class IconProvider extends Provider {
93
91
  || this.context.value.url !== this.url
94
92
  || this.context.value.lengthUnit !== this.lengthUnit
95
93
  || this.context.value.size !== this.size
96
- || this.context.value.shouldCache !== this.shouldCache) {
94
+ || this.context.value.cacheName !== this.cacheName
95
+ || this.context.value.cacheStrategy !== this.cacheStrategy) {
97
96
  this.updateValuesInContext();
98
97
  this.context.updateObservers();
99
98
  }
@@ -116,7 +115,11 @@ __decorate([
116
115
  __metadata("design:type", Number)
117
116
  ], IconProvider.prototype, "size", void 0);
118
117
  __decorate([
119
- property({ type: Boolean, attribute: 'should-cache', reflect: true }),
120
- __metadata("design:type", Boolean)
121
- ], IconProvider.prototype, "shouldCache", void 0);
118
+ property({ type: String, attribute: 'cache-strategy' }),
119
+ __metadata("design:type", String)
120
+ ], IconProvider.prototype, "cacheStrategy", void 0);
121
+ __decorate([
122
+ property({ type: String, attribute: 'cache-name' }),
123
+ __metadata("design:type", String)
124
+ ], IconProvider.prototype, "cacheName", void 0);
122
125
  export default IconProvider;
@@ -3,8 +3,8 @@ declare class IconProviderContext {
3
3
  url?: string;
4
4
  lengthUnit?: string;
5
5
  size?: number;
6
- iconsCache: Map<string, string>;
7
- shouldCache?: boolean;
6
+ cacheName?: string;
7
+ cacheStrategy?: 'in-memory-cache' | 'web-cache-api';
8
8
  static readonly context: {
9
9
  __context__: IconProviderContext;
10
10
  };
@@ -1,9 +1,6 @@
1
1
  import { createContext } from '@lit/context';
2
2
  import { TAG_NAME } from './iconprovider.constants';
3
3
  class IconProviderContext {
4
- constructor() {
5
- this.iconsCache = new Map();
6
- }
7
4
  }
8
5
  // create typed lit context as part of the IconProviderContext
9
6
  IconProviderContext.context = createContext(TAG_NAME);