@microsoft/fast-element 2.0.0-beta.6 → 2.0.0-beta.8

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.
Files changed (64) hide show
  1. package/CHANGELOG.json +78 -0
  2. package/CHANGELOG.md +25 -1
  3. package/dist/dts/components/attributes.d.ts +10 -0
  4. package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +24 -25
  5. package/dist/dts/components/fast-definitions.d.ts +28 -3
  6. package/dist/dts/components/fast-element.d.ts +2 -2
  7. package/dist/dts/di/di.d.ts +41 -0
  8. package/dist/dts/index.d.ts +2 -2
  9. package/dist/dts/observation/observable.d.ts +86 -47
  10. package/dist/dts/pending-task.d.ts +20 -0
  11. package/dist/dts/platform.d.ts +6 -0
  12. package/dist/dts/styles/css-directive.d.ts +2 -2
  13. package/dist/dts/styles/element-styles.d.ts +3 -3
  14. package/dist/dts/styles/host.d.ts +68 -0
  15. package/dist/dts/templating/binding-signal.d.ts +2 -2
  16. package/dist/dts/templating/binding-two-way.d.ts +11 -3
  17. package/dist/dts/templating/binding.d.ts +21 -119
  18. package/dist/dts/templating/children.d.ts +1 -1
  19. package/dist/dts/templating/html-directive.d.ts +69 -39
  20. package/dist/dts/templating/node-observation.d.ts +4 -5
  21. package/dist/dts/templating/ref.d.ts +5 -13
  22. package/dist/dts/templating/render.d.ts +15 -20
  23. package/dist/dts/templating/repeat.d.ts +11 -16
  24. package/dist/dts/templating/slotted.d.ts +1 -1
  25. package/dist/dts/templating/template.d.ts +4 -4
  26. package/dist/dts/templating/view.d.ts +68 -9
  27. package/dist/dts/templating/when.d.ts +1 -1
  28. package/dist/dts/testing/exports.d.ts +1 -0
  29. package/dist/dts/testing/fakes.d.ts +4 -0
  30. package/dist/dts/testing/fixture.d.ts +0 -6
  31. package/dist/esm/components/attributes.js +13 -4
  32. package/dist/esm/components/{controller.js → element-controller.js} +95 -105
  33. package/dist/esm/components/fast-definitions.js +3 -1
  34. package/dist/esm/components/fast-element.js +4 -4
  35. package/dist/esm/di/di.js +87 -3
  36. package/dist/esm/index.js +2 -1
  37. package/dist/esm/observation/observable.js +59 -126
  38. package/dist/esm/pending-task.js +16 -0
  39. package/dist/esm/platform.js +24 -0
  40. package/dist/esm/styles/css.js +4 -4
  41. package/dist/esm/{observation/behavior.js → styles/host.js} +0 -0
  42. package/dist/esm/templating/binding-signal.js +21 -17
  43. package/dist/esm/templating/binding-two-way.js +32 -27
  44. package/dist/esm/templating/binding.js +73 -177
  45. package/dist/esm/templating/html-directive.js +78 -7
  46. package/dist/esm/templating/node-observation.js +9 -8
  47. package/dist/esm/templating/ref.js +4 -12
  48. package/dist/esm/templating/render.js +30 -31
  49. package/dist/esm/templating/repeat.js +37 -38
  50. package/dist/esm/templating/template.js +3 -4
  51. package/dist/esm/templating/view.js +98 -29
  52. package/dist/esm/testing/exports.js +1 -0
  53. package/dist/esm/testing/fakes.js +76 -0
  54. package/dist/esm/testing/fixture.js +1 -3
  55. package/dist/fast-element.api.json +5720 -5385
  56. package/dist/fast-element.d.ts +510 -399
  57. package/dist/fast-element.debug.js +497 -514
  58. package/dist/fast-element.debug.min.js +1 -1
  59. package/dist/fast-element.js +497 -514
  60. package/dist/fast-element.min.js +1 -1
  61. package/dist/fast-element.untrimmed.d.ts +519 -405
  62. package/docs/api-report.md +197 -129
  63. package/package.json +5 -1
  64. package/dist/dts/observation/behavior.d.ts +0 -19
@@ -1,7 +1,22 @@
1
1
  import { isFunction, isString, } from "../interfaces.js";
2
- import { FAST } from "../platform.js";
2
+ import { createMetadataLocator, FAST } from "../platform.js";
3
3
  import { Updates } from "./update-queue.js";
4
4
  import { PropertyChangeNotifier, SubscriberSet } from "./notifier.js";
5
+ /**
6
+ * Describes how the source's lifetime relates to its controller's lifetime.
7
+ * @public
8
+ */
9
+ export const SourceLifetime = Object.freeze({
10
+ /**
11
+ * The source to controller lifetime relationship is unknown.
12
+ */
13
+ unknown: void 0,
14
+ /**
15
+ * The source and controller lifetimes are coupled to one another.
16
+ * They can/will be GC'd together.
17
+ */
18
+ coupled: 1,
19
+ });
5
20
  /**
6
21
  * Common Observable APIs.
7
22
  * @public
@@ -10,7 +25,6 @@ export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () =>
10
25
  const queueUpdate = Updates.enqueue;
11
26
  const volatileRegex = /(:|&&|\|\||if)/;
12
27
  const notifierLookup = new WeakMap();
13
- const accessorLookup = new WeakMap();
14
28
  let watcher = void 0;
15
29
  let createArrayObserver = (array) => {
16
30
  throw FAST.error(1101 /* Message.needsArrayObservation */);
@@ -25,19 +39,7 @@ export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () =>
25
39
  }
26
40
  return found;
27
41
  }
28
- function getAccessors(target) {
29
- let accessors = accessorLookup.get(target);
30
- if (accessors === void 0) {
31
- let currentTarget = Reflect.getPrototypeOf(target);
32
- while (accessors === void 0 && currentTarget !== null) {
33
- accessors = accessorLookup.get(currentTarget);
34
- currentTarget = Reflect.getPrototypeOf(currentTarget);
35
- }
36
- accessors = accessors === void 0 ? [] : accessors.slice(0);
37
- accessorLookup.set(target, accessors);
38
- }
39
- return accessors;
40
- }
42
+ const getAccessors = createMetadataLocator();
41
43
  class DefaultObservableAccessor {
42
44
  constructor(name) {
43
45
  this.name = name;
@@ -81,6 +83,22 @@ export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () =>
81
83
  setMode(isAsync) {
82
84
  this.isAsync = this.needsQueue = isAsync;
83
85
  }
86
+ bind(controller) {
87
+ this.controller = controller;
88
+ const value = this.observe(controller.source, controller.context);
89
+ if (!controller.isBound && this.requiresUnbind(controller)) {
90
+ controller.onUnbind(this);
91
+ }
92
+ return value;
93
+ }
94
+ requiresUnbind(controller) {
95
+ return (controller.sourceLifetime !== SourceLifetime.coupled ||
96
+ this.first !== this.last ||
97
+ this.first.propertySource !== controller.source);
98
+ }
99
+ unbind(controller) {
100
+ this.dispose();
101
+ }
84
102
  observe(source, context) {
85
103
  if (this.needsRefresh && this.last !== null) {
86
104
  this.dispose();
@@ -90,7 +108,7 @@ export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () =>
90
108
  this.needsRefresh = this.isVolatileBinding;
91
109
  let result;
92
110
  try {
93
- result = this.binding(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
111
+ result = this.binding(source, context);
94
112
  }
95
113
  finally {
96
114
  watcher = previousWatcher;
@@ -281,120 +299,35 @@ const contextEvent = FAST.getById(3 /* KernelServiceId.contextEvent */, () => {
281
299
  * Provides additional contextual information available to behaviors and expressions.
282
300
  * @public
283
301
  */
284
- export class ExecutionContext {
285
- constructor(parentSource = null, parentContext = null) {
286
- /**
287
- * The index of the current item within a repeat context.
288
- */
289
- this.index = 0;
290
- /**
291
- * The length of the current collection within a repeat context.
292
- */
293
- this.length = 0;
294
- this.parent = parentSource;
295
- this.parentContext = parentContext;
296
- }
297
- /**
298
- * The current event within an event handler.
299
- */
300
- get event() {
301
- return contextEvent.get();
302
- }
303
- /**
304
- * Indicates whether the current item within a repeat context
305
- * has an even index.
306
- */
307
- get isEven() {
308
- return this.index % 2 === 0;
309
- }
310
- /**
311
- * Indicates whether the current item within a repeat context
312
- * has an odd index.
313
- */
314
- get isOdd() {
315
- return this.index % 2 !== 0;
316
- }
317
- /**
318
- * Indicates whether the current item within a repeat context
319
- * is the first item in the collection.
320
- */
321
- get isFirst() {
322
- return this.index === 0;
323
- }
324
- /**
325
- * Indicates whether the current item within a repeat context
326
- * is somewhere in the middle of the collection.
327
- */
328
- get isInMiddle() {
329
- return !this.isFirst && !this.isLast;
330
- }
331
- /**
332
- * Indicates whether the current item within a repeat context
333
- * is the last item in the collection.
334
- */
335
- get isLast() {
336
- return this.index === this.length - 1;
337
- }
302
+ export const ExecutionContext = Object.freeze({
338
303
  /**
339
- * Returns the typed event detail of a custom event.
304
+ * A default execution context.
340
305
  */
341
- eventDetail() {
342
- return this.event.detail;
343
- }
344
- /**
345
- * Returns the typed event target of the event.
346
- */
347
- eventTarget() {
348
- return this.event.target;
349
- }
350
- /**
351
- * Updates the position/size on a context associated with a list item.
352
- * @param index - The new index of the item.
353
- * @param length - The new length of the list.
354
- */
355
- updatePosition(index, length) {
356
- this.index = index;
357
- this.length = length;
358
- }
359
- /**
360
- * Creates a new execution context descendent from the current context.
361
- * @param source - The source for the context if different than the parent.
362
- * @returns A child execution context.
363
- */
364
- createChildContext(parentSource) {
365
- return new ExecutionContext(parentSource, this);
366
- }
306
+ default: {
307
+ index: 0,
308
+ length: 0,
309
+ get event() {
310
+ return ExecutionContext.getEvent();
311
+ },
312
+ eventDetail() {
313
+ return this.event.detail;
314
+ },
315
+ eventTarget() {
316
+ return this.event.target;
317
+ },
318
+ },
367
319
  /**
368
- * Creates a new execution context descent suitable for use in list rendering.
369
- * @param item - The list item to serve as the source.
370
- * @param index - The index of the item in the list.
371
- * @param length - The length of the list.
320
+ * Gets the current event.
321
+ * @returns An event object.
372
322
  */
373
- createItemContext(index, length) {
374
- const childContext = Object.create(this);
375
- childContext.index = index;
376
- childContext.length = length;
377
- return childContext;
378
- }
323
+ getEvent() {
324
+ return contextEvent.get();
325
+ },
379
326
  /**
380
- * Sets the event for the current execution context.
381
- * @param event - The event to set.
382
- * @internal
327
+ * Sets the current event.
328
+ * @param event - An event object.
383
329
  */
384
- static setEvent(event) {
330
+ setEvent(event) {
385
331
  contextEvent.set(event);
386
- }
387
- /**
388
- * Creates a new root execution context.
389
- * @returns A new execution context.
390
- */
391
- static create() {
392
- return new ExecutionContext();
393
- }
394
- }
395
- /**
396
- * The default execution context.
397
- */
398
- ExecutionContext.default = new ExecutionContext();
399
- Observable.defineProperty(ExecutionContext.prototype, "index");
400
- Observable.defineProperty(ExecutionContext.prototype, "length");
332
+ },
333
+ });
@@ -0,0 +1,16 @@
1
+ /**
2
+ * A concrete implementation of {@link PendingTask}
3
+ * @beta
4
+ */
5
+ export class PendingTaskEvent extends Event {
6
+ constructor(complete) {
7
+ super(PendingTaskEvent.type, { bubbles: true, composed: true });
8
+ this.complete = complete;
9
+ }
10
+ static isPendingTask(value) {
11
+ var _a;
12
+ return (value.type === PendingTaskEvent.type &&
13
+ typeof ((_a = value.complete) === null || _a === void 0 ? void 0 : _a.then) === "function");
14
+ }
15
+ }
16
+ PendingTaskEvent.type = "pending-task";
@@ -57,7 +57,31 @@ export function createTypeRegistry() {
57
57
  return typeToDefinition.get(key);
58
58
  },
59
59
  getForInstance(object) {
60
+ if (object === null || object === void 0) {
61
+ return void 0;
62
+ }
60
63
  return typeToDefinition.get(object.constructor);
61
64
  },
62
65
  });
63
66
  }
67
+ /**
68
+ * Creates a function capable of locating metadata associated with a type.
69
+ * @returns A metadata locator function.
70
+ * @internal
71
+ */
72
+ export function createMetadataLocator() {
73
+ const metadataLookup = new WeakMap();
74
+ return function (target) {
75
+ let metadata = metadataLookup.get(target);
76
+ if (metadata === void 0) {
77
+ let currentTarget = Reflect.getPrototypeOf(target);
78
+ while (metadata === void 0 && currentTarget !== null) {
79
+ metadata = metadataLookup.get(currentTarget);
80
+ currentTarget = Reflect.getPrototypeOf(currentTarget);
81
+ }
82
+ metadata = metadata === void 0 ? [] : metadata.slice(0);
83
+ metadataLookup.set(target, metadata);
84
+ }
85
+ return metadata;
86
+ };
87
+ }
@@ -71,11 +71,11 @@ class CSSPartial {
71
71
  }
72
72
  return this.css;
73
73
  }
74
- bind(el) {
75
- el.$fastController.addStyles(this.styles);
74
+ addedCallback(controller) {
75
+ controller.addStyles(this.styles);
76
76
  }
77
- unbind(el) {
78
- el.$fastController.removeStyles(this.styles);
77
+ removedCallback(controller) {
78
+ controller.removeStyles(this.styles);
79
79
  }
80
80
  }
81
81
  CSSDirective.define(CSSPartial);
@@ -31,7 +31,7 @@ export const Signal = Object.freeze({
31
31
  const found = subscribers[signal];
32
32
  if (found) {
33
33
  found instanceof Set
34
- ? found.forEach(x => x.handleChange(this, signal))
34
+ ? found.forEach(x => x.handleChange(found, signal))
35
35
  : found.handleChange(this, signal);
36
36
  }
37
37
  },
@@ -40,40 +40,44 @@ class SignalObserver {
40
40
  constructor(dataBinding, subscriber) {
41
41
  this.dataBinding = dataBinding;
42
42
  this.subscriber = subscriber;
43
+ this.isNotBound = true;
43
44
  }
44
- observe(source, context) {
45
- const signal = (this.signal = this.getSignal(source, context));
46
- Signal.subscribe(signal, this);
47
- return this.dataBinding.evaluate(source, context);
45
+ bind(controller) {
46
+ if (this.isNotBound) {
47
+ Signal.subscribe(this.getSignal(controller), this);
48
+ controller.onUnbind(this);
49
+ this.isNotBound = false;
50
+ }
51
+ return this.dataBinding.evaluate(controller.source, controller.context);
48
52
  }
49
- dispose() {
50
- Signal.unsubscribe(this.signal, this);
53
+ unbind(controller) {
54
+ this.isNotBound = true;
55
+ Signal.unsubscribe(this.getSignal(controller), this);
51
56
  }
52
57
  handleChange() {
53
58
  this.subscriber.handleChange(this.dataBinding.evaluate, this);
54
59
  }
55
- getSignal(source, context) {
60
+ getSignal(controller) {
56
61
  const options = this.dataBinding.options;
57
- return isString(options) ? options : options(source, context);
62
+ return isString(options)
63
+ ? options
64
+ : options(controller.source, controller.context);
58
65
  }
59
66
  }
60
67
  class SignalBinding extends Binding {
61
- constructor(evaluate, options) {
62
- super();
63
- this.evaluate = evaluate;
64
- this.options = options;
65
- }
66
68
  createObserver(directive, subscriber) {
67
69
  return new SignalObserver(this, subscriber);
68
70
  }
69
71
  }
70
72
  /**
71
73
  * Creates a signal binding configuration with the supplied options.
72
- * @param binding - The binding to refresh when signaled.
74
+ * @param expression - The binding to refresh when signaled.
73
75
  * @param options - The signal name or a binding to use to retrieve the signal name.
74
76
  * @returns A binding configuration.
75
77
  * @public
76
78
  */
77
- export function signal(binding, options) {
78
- return new SignalBinding(binding, options);
79
+ export function signal(expression, options) {
80
+ const binding = new SignalBinding(expression);
81
+ binding.options = options;
82
+ return binding;
79
83
  }
@@ -10,31 +10,43 @@ let twoWaySettings = {
10
10
  return "change";
11
11
  },
12
12
  };
13
+ export const TwoWaySettings = Object.freeze({
14
+ /**
15
+ * Configures two-way binding.
16
+ * @param settings - The settings to use for the two-way binding system.
17
+ */
18
+ configure(settings) {
19
+ twoWaySettings = settings;
20
+ },
21
+ });
13
22
  class TwoWayObserver {
14
23
  constructor(directive, subscriber, dataBinding) {
15
24
  this.directive = directive;
16
25
  this.subscriber = subscriber;
17
26
  this.dataBinding = dataBinding;
27
+ this.isNotBound = true;
18
28
  this.notifier = Observable.binding(dataBinding.evaluate, this, dataBinding.isVolatile);
19
29
  }
20
- observe(source, context) {
30
+ bind(controller) {
21
31
  var _a;
22
32
  if (!this.changeEvent) {
23
33
  this.changeEvent =
24
34
  (_a = this.dataBinding.options.changeEvent) !== null && _a !== void 0 ? _a : twoWaySettings.determineChangeEvent(this.directive, this.target);
25
35
  }
26
- this.target.addEventListener(this.changeEvent, this);
27
- return this.notifier.observe(source, context);
36
+ if (this.isNotBound) {
37
+ this.target.addEventListener(this.changeEvent, this);
38
+ controller.onUnbind(this);
39
+ this.isNotBound = false;
40
+ }
41
+ return this.notifier.bind(controller);
28
42
  }
29
- dispose() {
30
- this.notifier.dispose();
43
+ unbind(controller) {
44
+ this.isNotBound = true;
31
45
  this.target.removeEventListener(this.changeEvent, this);
32
46
  }
33
- /** @internal */
34
47
  handleChange(subject, args) {
35
48
  this.subscriber.handleChange(this.dataBinding.evaluate, this);
36
49
  }
37
- /** @internal */
38
50
  handleEvent(event) {
39
51
  const directive = this.directive;
40
52
  const target = event.currentTarget;
@@ -63,36 +75,29 @@ class TwoWayObserver {
63
75
  }
64
76
  }
65
77
  class TwoWayBinding extends Binding {
66
- constructor(evaluate, isVolatile, options = defaultOptions) {
67
- super();
68
- this.evaluate = evaluate;
69
- this.isVolatile = isVolatile;
70
- this.options = options;
71
- if (!options.fromView) {
72
- options.fromView = defaultOptions.fromView;
73
- }
74
- }
75
78
  createObserver(directive, subscriber) {
76
79
  return new TwoWayObserver(directive, subscriber, this);
77
80
  }
78
- /**
79
- * Configures two-way binding.
80
- * @param settings - The settings to use for the two-way binding system.
81
- */
82
- static configure(settings) {
83
- twoWaySettings = settings;
84
- }
85
81
  }
86
82
  /**
87
83
  * Creates a default binding.
88
- * @param binding - The binding to refresh when changed.
84
+ * @param expression - The binding to refresh when changed.
85
+ * @param optionsOrChangeEvent - The binding options or the name of the change event to use.
89
86
  * @param isBindingVolatile - Indicates whether the binding is volatile or not.
90
- * @returns A binding configuration.
87
+ * @returns A binding.
91
88
  * @public
92
89
  */
93
- export function twoWay(binding, optionsOrChangeEvent, isBindingVolatile = Observable.isVolatileBinding(binding)) {
90
+ export function twoWay(expression, optionsOrChangeEvent, isBindingVolatile = Observable.isVolatileBinding(expression)) {
94
91
  if (isString(optionsOrChangeEvent)) {
95
92
  optionsOrChangeEvent = { changeEvent: optionsOrChangeEvent };
96
93
  }
97
- return new TwoWayBinding(binding, isBindingVolatile, optionsOrChangeEvent);
94
+ if (!optionsOrChangeEvent) {
95
+ optionsOrChangeEvent = defaultOptions;
96
+ }
97
+ else if (!optionsOrChangeEvent.fromView) {
98
+ optionsOrChangeEvent.fromView = defaultOptions.fromView;
99
+ }
100
+ const binding = new TwoWayBinding(expression, isBindingVolatile);
101
+ binding.options = optionsOrChangeEvent;
102
+ return binding;
98
103
  }