@fluffjs/fluff 0.2.4 → 0.3.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.
Files changed (62) hide show
  1. package/bundle.min.js +1 -0
  2. package/decorators/Component.d.ts +0 -8
  3. package/decorators/Component.js +0 -11
  4. package/decorators/Input.d.ts +1 -1
  5. package/decorators/InputOutputHelper.d.ts +2 -1
  6. package/decorators/InputOutputHelper.js +3 -1
  7. package/decorators/Output.d.ts +1 -1
  8. package/decorators/Pipe.d.ts +1 -0
  9. package/decorators/Pipe.js +1 -1
  10. package/decorators/Reactive.d.ts +2 -1
  11. package/decorators/Reactive.js +1 -1
  12. package/index.d.ts +2 -1
  13. package/index.js +1 -1
  14. package/interfaces/FluffHostElement.d.ts +13 -3
  15. package/interfaces/ReactiveOptions.d.ts +6 -0
  16. package/interfaces/ReactiveOptions.js +1 -0
  17. package/package.json +1 -1
  18. package/runtime/FluffBase.d.ts +11 -1
  19. package/runtime/FluffBase.js +50 -17
  20. package/runtime/FluffElementImpl.d.ts +9 -21
  21. package/runtime/FluffElementImpl.js +48 -149
  22. package/runtime/ForController.js +1 -2
  23. package/runtime/IfController.js +1 -2
  24. package/runtime/MarkerConfigGuards.d.ts +0 -2
  25. package/runtime/MarkerConfigGuards.js +0 -3
  26. package/runtime/MarkerController.d.ts +1 -9
  27. package/runtime/MarkerController.js +28 -144
  28. package/runtime/MarkerManager.d.ts +2 -1
  29. package/runtime/MarkerManager.js +1 -2
  30. package/runtime/MarkerManagerInterface.d.ts +3 -1
  31. package/runtime/SwitchController.js +1 -2
  32. package/runtime/TextController.d.ts +0 -1
  33. package/runtime/TextController.js +5 -19
  34. package/runtime/tests/TestChildTasksListComponent.js +2 -2
  35. package/runtime/tests/TestForComponent.js +2 -2
  36. package/runtime/tests/TestForReinsertBindsInputParentComponent.js +2 -2
  37. package/runtime/tests/TestForTextMarkerCollisionNoTrackParentComponent.js +2 -2
  38. package/runtime/tests/TestForTextMarkerCollisionParentComponent.js +2 -2
  39. package/runtime/tests/TestForUnsubscribeNestedParentComponent.js +2 -2
  40. package/runtime/tests/TestGetterReactivityComponent.js +2 -2
  41. package/runtime/tests/TestHarness.d.ts +9 -0
  42. package/runtime/tests/TestHarness.js +29 -0
  43. package/runtime/tests/TestIfReinsertBindsInputChildComponent.js +2 -2
  44. package/runtime/tests/TestIfReinsertBindsInputParentComponent.js +2 -2
  45. package/runtime/tests/TestIfUnsubscribeNestedParentComponent.js +2 -2
  46. package/runtime/tests/TestInterpolationNestedPropertyComponentBase.js +2 -2
  47. package/runtime/tests/TestLateDefineForComponent.js +2 -2
  48. package/runtime/tests/TestNullInputTextComponent.js +2 -2
  49. package/runtime/tests/TestOutputBindingParentComponent.js +2 -2
  50. package/runtime/tests/TestParentBindsTasksComponent.js +2 -2
  51. package/runtime/tests/TestSwitchReinsertBindsInputChildComponent.js +2 -2
  52. package/runtime/tests/TestSwitchReinsertBindsInputParentComponent.js +2 -2
  53. package/runtime/tests/TestSwitchUnsubscribeNestedParentComponent.js +2 -2
  54. package/runtime/tests/TestTemplateNestedMarkersComponent.js +2 -2
  55. package/runtime/tests/TestUnsubscribeNestedGrandchildComponent.js +2 -2
  56. package/runtime/tests/createPipeUnwrapTestComponent.js +2 -2
  57. package/runtime/tests/createTestInterpolationPipeComponent.js +2 -2
  58. package/runtime/tests/createTestInterpolationPipeWithArgsComponent.js +2 -2
  59. package/utils/Property.d.ts +8 -2
  60. package/utils/Property.js +48 -8
  61. package/utils/DomUtils.d.ts +0 -3
  62. package/utils/DomUtils.js +0 -6
@@ -11,9 +11,9 @@ export class TestParentBindsTasksComponent extends FluffElement {
11
11
  <test-child-tasks-list x-fluff-component="" data-lid="l0"></test-child-tasks-list>
12
12
  </template>
13
13
  `;
14
- this.__setMarkerConfigs(JSON.stringify([
14
+ this.__setMarkerConfigs([
15
15
  [0, { type: 'for', iterator: 'child', iterableExprId: 0, deps: ['childList'], hasEmpty: false }]
16
- ]));
16
+ ]);
17
17
  }
18
18
  __setupBindings() {
19
19
  this.__initializeMarkers(MarkerManager);
@@ -28,9 +28,9 @@ export class TestSwitchReinsertBindsInputChildComponent extends FluffElement {
28
28
  this.__getShadowRoot().innerHTML = `
29
29
  <div class="total"><!--fluff:text:0--><!--/fluff:text:0--></div>
30
30
  `;
31
- this.__setMarkerConfigs(JSON.stringify([
31
+ this.__setMarkerConfigs([
32
32
  [0, { type: 'text', exprId: 4, deps: ['stats'], pipes: [] }]
33
- ]));
33
+ ]);
34
34
  }
35
35
  __setupBindings() {
36
36
  this.__initializeMarkers(MarkerManager);
@@ -42,7 +42,7 @@ export class TestSwitchReinsertBindsInputParentComponent extends FluffElement {
42
42
  <test-switch-reinsert-binds-input-child x-fluff-component data-lid="l1"></test-switch-reinsert-binds-input-child>
43
43
  </template>
44
44
  `;
45
- this.__setMarkerConfigs(JSON.stringify([
45
+ this.__setMarkerConfigs([
46
46
  [
47
47
  0, {
48
48
  type: 'switch', expressionExprId: 0, deps: ['mode'], cases: [
@@ -51,7 +51,7 @@ export class TestSwitchReinsertBindsInputParentComponent extends FluffElement {
51
51
  ]
52
52
  }
53
53
  ]
54
- ]));
54
+ ]);
55
55
  const bindings = {
56
56
  l0: [{ n: 'stats', b: 'property', e: 3, d: ['stats'] }],
57
57
  l1: [{ n: 'stats', b: 'property', e: 3, d: ['stats'] }]
@@ -20,7 +20,7 @@ export class TestSwitchUnsubscribeNestedParentComponent extends TestUnsubscribeN
20
20
  <div class="placeholder">placeholder</div>
21
21
  </template>
22
22
  `;
23
- this.__setMarkerConfigs(JSON.stringify([
23
+ this.__setMarkerConfigs([
24
24
  [
25
25
  0, {
26
26
  type: 'switch', expressionExprId: 0, deps: ['mode'], cases: [
@@ -29,7 +29,7 @@ export class TestSwitchUnsubscribeNestedParentComponent extends TestUnsubscribeN
29
29
  ]
30
30
  }
31
31
  ]
32
- ]));
32
+ ]);
33
33
  const bindings = {
34
34
  l0: [{ n: 'stats', b: 'property', e: 3, d: ['stats'] }]
35
35
  };
@@ -27,10 +27,10 @@ export class TestTemplateNestedMarkersComponent extends FluffElement {
27
27
  <div class="fallback">Fallback</div>
28
28
  </template>
29
29
  `;
30
- this.__setMarkerConfigs(JSON.stringify([
30
+ this.__setMarkerConfigs([
31
31
  [0, { type: 'if', branches: [{ exprId: 0, deps: ['show'] }, { exprId: undefined, deps: [] }] }],
32
32
  [1, { type: 'text', exprId: 1, deps: ['text'], pipes: [] }]
33
- ]));
33
+ ]);
34
34
  }
35
35
  __setupBindings() {
36
36
  this.__initializeMarkers(MarkerManager);
@@ -28,9 +28,9 @@ export class TestUnsubscribeNestedGrandchildComponent extends FluffElement {
28
28
  this.__getShadowRoot().innerHTML = `
29
29
  <div class="total"><!--fluff:text:0--><!--/fluff:text:0--></div>
30
30
  `;
31
- this.__setMarkerConfigs(JSON.stringify([
31
+ this.__setMarkerConfigs([
32
32
  [0, { type: 'text', exprId: 5, deps: ['stats'], pipes: [] }]
33
- ]));
33
+ ]);
34
34
  }
35
35
  __setupBindings() {
36
36
  this.__initializeMarkers(MarkerManager);
@@ -19,9 +19,9 @@ export function createPipeUnwrapTestComponent(state) {
19
19
  };
20
20
  __render() {
21
21
  this.__getShadowRoot().innerHTML = '<span><!--fluff:text:0--><!--/fluff:text:0--></span>';
22
- this.__setMarkerConfigs(JSON.stringify([
22
+ this.__setMarkerConfigs([
23
23
  [0, { type: 'text', exprId: 0, deps: ['testProp'], pipes: [{ name: 'capture', argExprIds: [] }] }]
24
- ]));
24
+ ]);
25
25
  }
26
26
  __setupBindings() {
27
27
  this.__initializeMarkers(MarkerManager);
@@ -22,9 +22,9 @@ export function createTestInterpolationPipeComponent() {
22
22
  };
23
23
  __render() {
24
24
  this.__getShadowRoot().innerHTML = '<span><!--fluff:text:0--><!--/fluff:text:0--></span>';
25
- this.__setMarkerConfigs(JSON.stringify([
25
+ this.__setMarkerConfigs([
26
26
  [0, { type: 'text', exprId: 0, deps: ['message'], pipes: [{ name: 'uppercase', argExprIds: [] }] }]
27
- ]));
27
+ ]);
28
28
  }
29
29
  __setupBindings() {
30
30
  this.__initializeMarkers(MarkerManager);
@@ -22,9 +22,9 @@ export function createTestInterpolationPipeWithArgsComponent() {
22
22
  };
23
23
  __render() {
24
24
  this.__getShadowRoot().innerHTML = '<span><!--fluff:text:0--><!--/fluff:text:0--></span>';
25
- this.__setMarkerConfigs(JSON.stringify([
25
+ this.__setMarkerConfigs([
26
26
  [0, { type: 'text', exprId: 0, deps: ['message'], pipes: [{ name: 'truncate', argExprIds: [1] }] }]
27
- ]));
27
+ ]);
28
28
  }
29
29
  __setupBindings() {
30
30
  this.__initializeMarkers(MarkerManager);
@@ -9,12 +9,17 @@ export declare class Property<T> {
9
9
  private value?;
10
10
  private committed;
11
11
  private _isChanging;
12
- private readonly _propName?;
13
12
  private _parentProperty?;
14
13
  private _parentSubscription?;
14
+ private _commitTriggerSub?;
15
+ private _pendingCommitValue?;
16
+ private _options?;
15
17
  constructor(options: {
16
- initialValue: T;
18
+ initialValue?: T;
17
19
  propertyName?: string;
20
+ direction?: Direction;
21
+ commitTrigger?: Property<unknown>;
22
+ linkHandler?: (prop: Property<T>) => void;
18
23
  } | T);
19
24
  prop(): Property<T> | undefined;
20
25
  setValue(val: T | Property<T>, inbound?: boolean, commit?: boolean): void;
@@ -22,6 +27,7 @@ export declare class Property<T> {
22
27
  private setValueInternal;
23
28
  private pushToParent;
24
29
  private clearParentLink;
30
+ setCommitTrigger(trigger: Property<unknown>): void;
25
31
  reset(): void;
26
32
  triggerChange(direction?: Direction): void;
27
33
  subscribe(direction: Direction, cb: (val: T) => void): Subscription;
package/utils/Property.js CHANGED
@@ -20,16 +20,19 @@ export class Property {
20
20
  value;
21
21
  committed = true;
22
22
  _isChanging = false;
23
- _propName;
24
23
  _parentProperty;
25
24
  _parentSubscription;
25
+ _commitTriggerSub;
26
+ _pendingCommitValue;
27
+ _options;
26
28
  constructor(options) {
27
29
  if (this.isOptionsObject(options)) {
30
+ this._options = options;
28
31
  if (options.initialValue !== undefined) {
29
32
  this.value = options.initialValue;
30
33
  }
31
- if (options.propertyName !== undefined) {
32
- this._propName = options.propertyName;
34
+ if (options.commitTrigger !== undefined) {
35
+ this.setCommitTrigger(options.commitTrigger);
33
36
  }
34
37
  }
35
38
  else {
@@ -41,16 +44,22 @@ export class Property {
41
44
  }
42
45
  setValue(val, inbound = false, commit = true) {
43
46
  if (val instanceof Property) {
47
+ const linkHandler = this._options?.linkHandler;
48
+ if (linkHandler) {
49
+ linkHandler(val);
50
+ }
44
51
  this.linkToParent(val);
45
52
  return;
46
53
  }
47
54
  if (this._isChanging) {
48
- console.error((this._propName ? this._propName + ': ' : '') + 'Binding loop detected: setValue called while change is in progress');
55
+ const propName = this._options?.propertyName;
56
+ console.error((propName ? propName + ': ' : '') + 'Binding loop detected: setValue called while change is in progress');
49
57
  return;
50
58
  }
51
59
  const changed = val !== this.value && safeStringify(val) !== safeStringify(this.value);
52
- if (!changed)
60
+ if (!changed && !(commit && !this.committed)) {
53
61
  return;
62
+ }
54
63
  this._isChanging = true;
55
64
  try {
56
65
  this.value = val;
@@ -62,7 +71,7 @@ export class Property {
62
71
  this.committed = true;
63
72
  this.onInboundChange.emit(val);
64
73
  }
65
- else if (commit || !this.committed) {
74
+ else if (commit) {
66
75
  this.committed = true;
67
76
  if (this.value !== undefined) {
68
77
  this.onOutboundChange.emit(this.value);
@@ -83,7 +92,8 @@ export class Property {
83
92
  root = root._parentProperty;
84
93
  }
85
94
  this._parentProperty = root;
86
- this._parentSubscription = root.subscribe(Direction.Any, (val) => {
95
+ const direction = this._options?.direction ?? Direction.Any;
96
+ this._parentSubscription = root.subscribe(direction, (val) => {
87
97
  this.setValueInternal(val, true);
88
98
  });
89
99
  const currentValue = root.getValue();
@@ -112,11 +122,22 @@ export class Property {
112
122
  pushToParent(val) {
113
123
  if (!this._parentProperty)
114
124
  return;
125
+ const commitTrigger = this._options?.commitTrigger;
126
+ if (commitTrigger) {
127
+ const shouldCommit = Boolean(commitTrigger.getValue());
128
+ if (!shouldCommit) {
129
+ this._pendingCommitValue = { value: val };
130
+ this._parentProperty.setValue(val, false, false);
131
+ return;
132
+ }
133
+ this._pendingCommitValue = undefined;
134
+ }
115
135
  const sub = this._parentSubscription;
116
136
  this._parentSubscription = undefined;
117
137
  sub?.unsubscribe();
118
138
  this._parentProperty.setValue(val);
119
- this._parentSubscription = this._parentProperty.subscribe(Direction.Any, (v) => {
139
+ const direction = this._options?.direction ?? Direction.Any;
140
+ this._parentSubscription = this._parentProperty.subscribe(direction, (v) => {
120
141
  this.setValueInternal(v, true);
121
142
  });
122
143
  }
@@ -127,6 +148,25 @@ export class Property {
127
148
  }
128
149
  this._parentProperty = undefined;
129
150
  }
151
+ setCommitTrigger(trigger) {
152
+ if (this._commitTriggerSub) {
153
+ this._commitTriggerSub.unsubscribe();
154
+ this._commitTriggerSub = undefined;
155
+ }
156
+ this._options ??= {};
157
+ this._options.commitTrigger = trigger;
158
+ this._commitTriggerSub = trigger.onChange.subscribe((val) => {
159
+ if (!val)
160
+ return;
161
+ if (!this._parentProperty)
162
+ return;
163
+ if (!this._pendingCommitValue)
164
+ return;
165
+ const pending = this._pendingCommitValue.value;
166
+ this._pendingCommitValue = undefined;
167
+ this._parentProperty.setValue(pending, false, true);
168
+ });
169
+ }
130
170
  reset() {
131
171
  this.clearParentLink();
132
172
  }
@@ -1,3 +0,0 @@
1
- export declare class DomUtils {
2
- static isCustomElement(el: Element): boolean;
3
- }
package/utils/DomUtils.js DELETED
@@ -1,6 +0,0 @@
1
- export class DomUtils {
2
- static isCustomElement(el) {
3
- const tagName = el.tagName.toLowerCase();
4
- return customElements.get(tagName) !== undefined;
5
- }
6
- }