@brightspace-ui/labs 2.10.1 → 2.11.0

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/package.json CHANGED
@@ -79,5 +79,5 @@
79
79
  "@lit/context": "^1.1.3",
80
80
  "lit": "^3"
81
81
  },
82
- "version": "2.10.1"
82
+ "version": "2.11.0"
83
83
  }
@@ -19,14 +19,22 @@ import ReactiveStore from '@brightspace-ui/labs/utilites/reactive-store.js';
19
19
  class MyStore extends ReactiveStore {
20
20
  static get properties() {
21
21
  return {
22
- foo: { type: Number },
22
+ count: { type: Number },
23
23
  };
24
24
  }
25
25
 
26
26
  constructor() {
27
27
  super();
28
28
 
29
- this.foo = 0;
29
+ this.count = 0;
30
+ }
31
+
32
+ increment() {
33
+ this.count++;
34
+ }
35
+
36
+ decrement() {
37
+ this.count--;
30
38
  }
31
39
  }
32
40
 
@@ -56,15 +64,27 @@ class MyComponent extends LitElement {
56
64
  render() {
57
65
  // The consumer will have all the same properties defined in your store.
58
66
  return html`
59
- <div>Foo: ${this.myStoreConsumer.foo}</div>
60
- <button @click=${this._click}>Update foo</button>
67
+ <div>Count: ${this.myStoreConsumer.count}</div>
68
+ <button @click=${this._increment}>Increment</button>
69
+ <button @click=${this._decrement}>Decrement</button>
70
+ <button @click=${this._reset}>Reset</button>
61
71
  `;
62
72
  }
63
73
 
64
- _click() {
74
+ _reset() {
65
75
  // Updating the values from the consumer will update the store, which will then
66
76
  // notify all consumers of the changes and trigger component updates.
67
- this.myStoreConsumer.foo += 1;
77
+ this.myStoreConsumer.count = 0;
78
+ }
79
+
80
+ _increment() {
81
+ // You can access any method or property defined in the store, not just reactive properties.
82
+ this.myStoreConsumer.increment();
83
+ }
84
+
85
+ _decrement() {
86
+ // You can access any method or property defined in the store, not just reactive properties.
87
+ this.myStoreConsumer.decrement();
68
88
  }
69
89
  }
70
90
 
@@ -86,14 +106,22 @@ import ReactiveStore from '@brightspace-ui/labs/utilites/reactive-store.js';
86
106
  class MyStore extends ReactiveStore {
87
107
  static get properties() {
88
108
  return {
89
- foo: { type: Number },
109
+ count: { type: Number },
90
110
  };
91
111
  }
92
112
 
93
113
  constructor() {
94
114
  super();
95
115
 
96
- this.foo = 0;
116
+ this.count = 0;
117
+ }
118
+
119
+ increment() {
120
+ this.count++;
121
+ }
122
+
123
+ decrement() {
124
+ this.count--;
97
125
  }
98
126
  }
99
127
 
@@ -127,16 +155,28 @@ class MyComponent extends LitElement {
127
155
  // The provider will have all the same properties defined in your store, so you can
128
156
  // access your store data from the provider if you wish.
129
157
  return html`
130
- <div>Foo: ${this.myStoreProvider.foo}</div>
131
- <button @click=${this._click}>Update foo</button>
158
+ <div>Count: ${this.myStoreProvider.count}</div>
159
+ <button @click=${this._increment}>Increment</button>
160
+ <button @click=${this._decrement}>Decrement</button>
161
+ <button @click=${this._reset}>Reset</button>
132
162
  <my-descendant-component></my-descendant-component>
133
163
  `;
134
164
  }
135
165
 
136
- _click() {
166
+ _reset() {
137
167
  // Updating the values from the provider will update the store, which will then
138
168
  // notify all consumers of the changes and trigger component updates.
139
- this.myStoreProvider.foo += 1;
169
+ this.myStoreProvider.count = 0;
170
+ }
171
+
172
+ _increment() {
173
+ // You can access any method or property defined in the store, not just reactive properties.
174
+ this.myStoreProvider.increment();
175
+ }
176
+
177
+ _decrement() {
178
+ // You can access any method or property defined in the store, not just reactive properties.
179
+ this.myStoreProvider.decrement();
140
180
  }
141
181
  }
142
182
 
@@ -162,16 +202,28 @@ class MyDescendantComponent extends LitElement {
162
202
  render() {
163
203
  // The consumer will have all the same properties defined in your store.
164
204
  return html`
165
- <div>Foo: ${this.myStoreConsumer.foo}</div>
166
- <button @click=${this._click}>Update foo</button>
205
+ <div>Count: ${this.myStoreConsumer.count}</div>
206
+ <button @click=${this._increment}>Increment</button>
207
+ <button @click=${this._decrement}>Decrement</button>
208
+ <button @click=${this._reset}>Reset</button>
167
209
  `;
168
210
  }
169
211
 
170
- _click() {
212
+ _reset() {
171
213
  // Updating the values from the consumer will update the store, which will then
172
214
  // notify all consumers of the changes and trigger component updates for all consumers and
173
215
  // the provider as well.
174
- this.myStoreConsumer.foo += 1;
216
+ this.myStoreConsumer.count = 0;
217
+ }
218
+
219
+ _increment() {
220
+ // You can access any method or property defined in the store, not just reactive properties.
221
+ this.myStoreConsumer.increment();
222
+ }
223
+
224
+ _decrement() {
225
+ // You can access any method or property defined in the store, not just reactive properties.
226
+ this.myStoreConsumer.decrement();
175
227
  }
176
228
  }
177
229
 
@@ -193,14 +245,14 @@ import ReactiveStore from '@brightspace-ui/labs/utilites/reactive-store.js';
193
245
  class MyStore extends ReactiveStore {
194
246
  static get properties() {
195
247
  return {
196
- foo: { type: Number },
248
+ count: { type: Number },
197
249
  };
198
250
  }
199
251
 
200
252
  constructor() {
201
253
  super();
202
254
 
203
- this.foo = 0;
255
+ this.count = 0;
204
256
  }
205
257
  }
206
258
 
@@ -220,7 +272,7 @@ function handlePropertyChange({ property, value, prevValue }) {
220
272
  myStore.subscribe(handlePropertyChange);
221
273
 
222
274
  // When a store property is changed, any subscribed callback functions will be invoked synchronously
223
- myStore.foo += 1; // console: The "foo" property changed from 0 to 1
275
+ myStore.count += 1; // console: The "count" property changed from 0 to 1
224
276
 
225
277
  // Unsubscribe your callback function when you no longer want to receive store updates
226
278
  myStore.unsubscribe(handlePropertyChange);
@@ -324,17 +376,19 @@ If the callback function passed in does not match a currently subscribed functio
324
376
 
325
377
  This is the class that is returned by the `createConsumer()` instance method on an instance of the store.
326
378
 
327
- This class is a [Lit Reactive Controller](https://lit.dev/docs/composition/controllers/) that when instantiated can be used by a Lit component to connect to the originating store instance.
379
+ This class is a [Lit Reactive Controller](https://lit.dev/docs/composition/controllers/) that when instantiated acts as a proxy for the originating store itself.
328
380
 
329
- Any Consumer class instances will have access to all the same properties that the originating store does and will automatically trigger the update cycle on the host component whenever a property of the store changes.
381
+ Any Consumer class instances will have access to all the same properties and methods that the originating store does and will automatically trigger the update cycle on the host component whenever a reactive property of the store changes.
330
382
 
331
383
  ### Instance Properties
332
384
 
333
- #### The Reactive `properties`
385
+ #### Proxied Properties and Methods
334
386
 
335
- Just like the store has a set of properties dynamically generated, the Consumer class will have the same property accessors generated at construction time. The Consumer's properties will be directly connected to the corresponding properties on the originating store instance, so they can be used as if connecting to the store directly.
387
+ Since the Consumer acts as a proxy for the originating store, all properties and methods from the original store will also be accessible from the Consumer. The Consumer's properties will be directly connected to the corresponding properties on the originating store instance, so they can be used as if interacting with the store directly.
336
388
 
337
- Setting any of these properties will call the corresponding setter on the originating store, and since the store notifies all consumers of changes, all consumers will then trigger the update cycle for their respective host components.
389
+ The proxied properties and methods includes methods on the `ReactiveStore` base class like `forceUpdate`, `subscribe`, `unsubscribe`, etc. However, any properties/methods that conflict with the properties/methods defined by the Consumer class itself (`changedProperties`, `hostDisconnected`, etc.) will be ignored.
390
+
391
+ Since updating a reactive property on the Consumer is the same as setting it on the store, updating a reactive property will notify all consumers of changes and, in turn, all consumers will then trigger the update cycle for their respective host components.
338
392
 
339
393
  #### `changedProperties`
340
394
 
@@ -356,10 +410,6 @@ The constructor for the Consumer class accepts the following parameters:
356
410
  |---|---|---|---|
357
411
  | `host` | LitElement | The host Lit element that the Consumer is to be connected to. | True |
358
412
 
359
- #### `forceUpdate()`
360
-
361
- This method can be used to call the originating store's own `forceUpdate()` method, which will trigger an update for all consumer host components. See the store's `forceUpdate()` definition for additional details.
362
-
363
413
  ## The context `Provider` class
364
414
 
365
415
  The context `Provider` class is one of the two [Lit Reactive Controllers](https://lit.dev/docs/composition/controllers/) returned by the `createContextControllers()` static method. The context `Consumer` class is the other controller returned and both of these act as a pair. Both of these controllers also have ther functionality tied to the specific store class you used to generate them.
@@ -370,11 +420,13 @@ This class is based on (and internally uses) the `ContextProvider` class from Li
370
420
 
371
421
  ### Instance Properties
372
422
 
373
- #### The Reactive `properties`
423
+ #### Proxied Properties and Methods
424
+
425
+ The context `Provider` acts as a proxy for the originating store, which means that all properties and methods from the original store will also be accessible from the `Provider`. The `Provider`'s properties will be directly connected to the corresponding properties on the originating store instance, so they can be used as if interacting with the store directly.
374
426
 
375
- Just like the store has a set of properties dynamically generated, the context `Provider` class will have the same property accessors generated at construction time. The `Provider`'s properties will be directly connected to the corresponding properties on the store instance that it provides, so they can be used as if connecting to the store directly.
427
+ The proxied properties and methods includes methods on the `ReactiveStore` base class like `forceUpdate`, `subscribe`, `unsubscribe`, etc. However, any properties/methods that conflict with the properties/methods defined by the `Provider` class itself (`changedProperties`, `hostDisconnected`, etc.) will be ignored.
376
428
 
377
- Setting any of these properties will call the corresponding setter on the provided store and the Provider will trigger a corresponding lifecycle update for its hosting component. Any context `Consumer` instances that are descendants of the `Provider` will also trigger the lifecycle update for their corresponding hosting components.
429
+ Since updating a reactive property on the `Provider` is the same as setting it on the store, updating a reactive property will notify the `Provider` and all descendant `Consumer` instances of changes and, in turn, the `Provider` and `Consumer` instances will then trigger the update cycle for their respective host components.
378
430
 
379
431
  #### `changedProperties`
380
432
 
@@ -397,10 +449,6 @@ The constructor for the context `Provider` class accepts the following parameter
397
449
  | `host` | LitElement instance | The host Lit element that the `Provider` is to be connected to. | True | |
398
450
  | `store` | ReactiveStore instance | The instance of your store that you wish to provide to descendant context `Consumer` classes. Note that if no store instance is passed to this parameter, an instance of your store will be instantiated to be used. | True | `new StoreClass()` |
399
451
 
400
- #### `forceUpdate()`
401
-
402
- This method can be used to call the provided store's own `forceUpdate()` method, which will trigger an update for the `Provider`'s host component as well as all context `Consumer` host components. See the store's `forceUpdate()` definition for additional details.
403
-
404
452
  ## The context `Consumer` class
405
453
 
406
454
  The context `Consumer` class is one of the two [Lit Reactive Controllers](https://lit.dev/docs/composition/controllers/) returned by the `createContextControllers()` static method. The context `Provider` class is the other controller returned and both of these act as a pair. Both of these controllers also have ther functionality tied to the specific store class you used to generate them.
@@ -413,11 +461,13 @@ This class is based on (and internally uses) the `ContextConsumer` class from Li
413
461
 
414
462
  ### Instance Properties
415
463
 
416
- #### The Reactive `properties`
464
+ #### Proxied Properties and Methods
417
465
 
418
- Just like the store has a set of properties dynamically generated, the context `Consumer` class will have the same property accessors generated at construction time. The `Consumer`'s properties will be directly connected to the corresponding properties on the store instance that it receives from the context `Provider` ancestor, so they can be used as if connecting to the store directly.
466
+ The context `Consumer` acts as a proxy for the originating store (which it receives from the `Provider` ancestor), which means that all properties and methods from the original store will also be accessible from the `Consumer`. The `Consumer`'s properties will be directly connected to the corresponding properties on the originating store instance, so they can be used as if interacting with the store directly.
419
467
 
420
- Setting any of these properties will call the corresponding setter on the provided store and the `Provider` will trigger a corresponding lifecycle update for its hosting component. Any context `Consumer` instances (including this one) that are descendants of that `Provider` will also trigger the lifecycle update for their corresponding hosting components.
468
+ The proxied properties and methods includes methods on the `ReactiveStore` base class like `forceUpdate`, `subscribe`, `unsubscribe`, etc. However, any properties/methods that conflict with the properties/methods defined by the `Consumer` class itself (`changedProperties`, `hostDisconnected`, etc.) will be ignored.
469
+
470
+ Since updating a reactive property on the `Consumer` is the same as setting it on the store, updating a reactive property will notify the `Provider` and all descendant `Consumer` instances of changes and, in turn, the `Provider` and `Consumer` instances will then trigger the update cycle for their respective host components.
421
471
 
422
472
  #### `changedProperties`
423
473
 
@@ -438,7 +488,3 @@ The constructor for the context `Consumer` class accepts the following parameter
438
488
  | Parameter Name | Type | Description | Required |
439
489
  |---|---|---|---|
440
490
  | `host` | LitElement instance | The host Lit element that the `Consumer` is to be connected to. | True |
441
-
442
- #### `forceUpdate()`
443
-
444
- This method can be used to call the provided store's own `forceUpdate()` method, which will trigger an update for the `Provider`'s host component as well as all context `Consumer` host components. See the store's `forceUpdate()` definition for additional details.
@@ -3,70 +3,59 @@ import {
3
3
  ContextConsumer as LitContextConsumer,
4
4
  ContextProvider as LitContextProvider
5
5
  } from '@lit/context';
6
- import StoreConsumer from './store-consumer.js';
6
+ import StoreReactor from './store-reactor.js';
7
7
 
8
8
  export class ContextProvider {
9
9
  constructor(host, StoreClass, store = new StoreClass()) {
10
10
  const { properties } = StoreClass;
11
- this._storeConsumer = new StoreConsumer(host, store, properties);
12
- this._provider = new LitContextProvider(host, {
11
+ const storeReactor = new StoreReactor(host, store, properties);
12
+ new LitContextProvider(host, {
13
13
  context: createContext(StoreClass),
14
14
  initialValue: store,
15
15
  });
16
- this._defineProperties(properties);
17
- }
18
-
19
- get changedProperties() {
20
- return this._storeConsumer.changedProperties;
21
- }
22
16
 
23
- forceUpdate() {
24
- this._storeConsumer.forceUpdate();
25
- }
26
-
27
- _defineProperties(properties) {
28
- Object.keys(properties).forEach((property) => {
29
- Object.defineProperty(this, property, {
30
- get() {
31
- return this._storeConsumer[property];
32
- },
33
- set(value) {
34
- this._storeConsumer[property] = value;
17
+ return new Proxy(store, {
18
+ get(target, prop) {
19
+ if (prop in storeReactor) return storeReactor[prop];
20
+ return Reflect.get(target, prop);
21
+ },
22
+ set(target, prop, value) {
23
+ if (prop in storeReactor) {
24
+ storeReactor[prop] = value;
25
+ return true;
35
26
  }
36
- });
27
+ return Reflect.set(target, prop, value);
28
+ }
37
29
  });
38
30
  }
39
31
  }
40
32
  export class ContextConsumer {
41
33
  constructor(host, StoreClass) {
42
34
  const { properties } = StoreClass;
43
- this._contextConsumer = new LitContextConsumer(host, {
35
+ const target = {
36
+ store: {},
37
+ storeReactor: {},
38
+ };
39
+ new LitContextConsumer(host, {
44
40
  context: createContext(StoreClass),
45
41
  callback: (store) => {
46
- this._storeConsumer = new StoreConsumer(host, store, properties);
47
- this._defineProperties(properties);
42
+ target.store = store;
43
+ target.storeReactor = new StoreReactor(host, store, properties);
48
44
  },
49
45
  });
50
- }
51
-
52
- get changedProperties() {
53
- return this._storeConsumer?.changedProperties;
54
- }
55
46
 
56
- forceUpdate() {
57
- this._storeConsumer.forceUpdate();
58
- }
59
-
60
- _defineProperties(properties) {
61
- Object.keys(properties).forEach((property) => {
62
- Object.defineProperty(this, property, {
63
- get() {
64
- return this._storeConsumer[property];
65
- },
66
- set(value) {
67
- this._storeConsumer[property] = value;
47
+ return new Proxy(target, {
48
+ get({ store, storeReactor }, prop) {
49
+ if (prop in storeReactor) return storeReactor[prop];
50
+ return Reflect.get(store, prop);
51
+ },
52
+ set({ store, storeReactor }, prop, value) {
53
+ if (prop in storeReactor) {
54
+ storeReactor[prop] = value;
55
+ return true;
68
56
  }
69
- });
57
+ return Reflect.set(store, prop, value);
58
+ }
70
59
  });
71
60
  }
72
61
  }
@@ -1,66 +1,21 @@
1
+ import StoreReactor from './store-reactor.js';
2
+
1
3
  export default class StoreConsumer {
2
4
  constructor(host, store, properties = store.constructor.properties) {
3
- this._host = host;
4
- this._host.addController(this);
5
- this._store = store;
6
-
7
- this.changedProperties = new Map();
8
-
9
- this._onPropertyChange = this._onPropertyChange.bind(this);
10
- this._store.subscribe(this._onPropertyChange);
11
-
12
- this._defineProperties(properties);
13
- this._initializeChangedProperties(properties);
14
- }
15
-
16
- forceUpdate() {
17
- this._store.forceUpdate();
18
- }
19
-
20
- hostDisconnected() {
21
- this._store.unsubscribe(this._onPropertyChange);
22
- }
23
-
24
- _defineProperties(properties) {
25
- Object.keys(properties).forEach((property) => {
26
- Object.defineProperty(this, property, {
27
- get() {
28
- return this._store[property];
29
- },
30
- set(value) {
31
- this._store[property] = value;
5
+ const storeReactor = new StoreReactor(host, store, properties);
6
+
7
+ return new Proxy(store, {
8
+ get(target, prop) {
9
+ if (prop in storeReactor) return storeReactor[prop];
10
+ return Reflect.get(target, prop);
11
+ },
12
+ set(target, prop, value) {
13
+ if (prop in storeReactor) {
14
+ storeReactor[prop] = value;
15
+ return true;
32
16
  }
33
- });
34
- });
35
- }
36
-
37
- _initializeChangedProperties(properties) {
38
- let shouldUpdate = false;
39
- Object.keys(properties).forEach((property) => {
40
- if (this._store[property] === undefined) return;
41
-
42
- this.changedProperties.set(property, undefined);
43
- shouldUpdate = true;
44
- });
45
-
46
- if (!shouldUpdate) return;
47
-
48
- this._host.requestUpdate();
49
- this._host.updateComplete.then(() => {
50
- this.changedProperties.clear();
51
- });
52
- }
53
-
54
- _onPropertyChange({
55
- property,
56
- prevValue,
57
- forceUpdate = false,
58
- }) {
59
- if (!forceUpdate && !this.changedProperties.has(property)) this.changedProperties.set(property, prevValue);
60
-
61
- this._host.requestUpdate();
62
- this._host.updateComplete.then(() => {
63
- this.changedProperties.clear();
17
+ return Reflect.set(target, prop, value);
18
+ }
64
19
  });
65
20
  }
66
21
  }
@@ -0,0 +1,52 @@
1
+ export default class StoreReactor {
2
+ changedProperties;
3
+
4
+ constructor(host, store, properties = store.constructor.properties) {
5
+ this.#host = host;
6
+ this.#host.addController(this);
7
+ this.#store = store;
8
+
9
+ this.changedProperties = new Map();
10
+
11
+ this.#store.subscribe(this.#onPropertyChange);
12
+
13
+ this.#initializeChangedProperties(properties);
14
+ }
15
+
16
+ hostDisconnected() {
17
+ this.#store.unsubscribe(this.#onPropertyChange);
18
+ }
19
+
20
+ #host;
21
+ #store;
22
+
23
+ #onPropertyChange = ({
24
+ property,
25
+ prevValue,
26
+ forceUpdate = false,
27
+ }) => {
28
+ if (!forceUpdate && !this.changedProperties.has(property)) this.changedProperties.set(property, prevValue);
29
+
30
+ this.#host.requestUpdate();
31
+ this.#host.updateComplete.then(() => {
32
+ this.changedProperties.clear();
33
+ });
34
+ };
35
+
36
+ #initializeChangedProperties(properties) {
37
+ let shouldUpdate = false;
38
+ Object.keys(properties).forEach((property) => {
39
+ if (this.#store[property] === undefined) return;
40
+
41
+ this.changedProperties.set(property, undefined);
42
+ shouldUpdate = true;
43
+ });
44
+
45
+ if (!shouldUpdate) return;
46
+
47
+ this.#host.requestUpdate();
48
+ this.#host.updateComplete.then(() => {
49
+ this.changedProperties.clear();
50
+ });
51
+ }
52
+ }