@fluffjs/fluff 0.2.3 → 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 (66) 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 +12 -1
  19. package/runtime/FluffBase.js +70 -22
  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/createPropertyUnwrapNativeTestComponents.d.ts +4 -0
  58. package/runtime/tests/createPropertyUnwrapNativeTestComponents.js +23 -0
  59. package/runtime/tests/createTestInterpolationPipeComponent.js +2 -2
  60. package/runtime/tests/createTestInterpolationPipeWithArgsComponent.js +2 -2
  61. package/runtime/tests/typeguards.d.ts +21 -0
  62. package/runtime/tests/typeguards.js +48 -0
  63. package/utils/Property.d.ts +8 -2
  64. package/utils/Property.js +48 -8
  65. package/utils/DomUtils.d.ts +0 -3
  66. package/utils/DomUtils.js +0 -6
@@ -17,9 +17,9 @@ export class TestChildTasksListComponent extends FluffElement {
17
17
  <div class="item"></div>
18
18
  </template>
19
19
  `;
20
- this.__setMarkerConfigs(JSON.stringify([
20
+ this.__setMarkerConfigs([
21
21
  [0, { type: 'for', iterator: 'task', iterableExprId: 2, deps: ['tasks'], hasEmpty: false }]
22
- ]));
22
+ ]);
23
23
  }
24
24
  __setupBindings() {
25
25
  this.__initializeMarkers(MarkerManager);
@@ -10,9 +10,9 @@ export class TestForComponent extends FluffElement {
10
10
  <test-for-child data-lid="l0"></test-for-child>
11
11
  </template>
12
12
  `;
13
- this.__setMarkerConfigs(JSON.stringify([
13
+ this.__setMarkerConfigs([
14
14
  [0, { type: 'for', iterator: 'item', iterableExprId: 0, deps: ['items'], hasEmpty: false }]
15
- ]));
15
+ ]);
16
16
  }
17
17
  __setupBindings() {
18
18
  this.__initializeMarkers(MarkerManager);
@@ -42,9 +42,9 @@ export class TestForReinsertBindsInputParentComponent extends FluffElement {
42
42
  <div class="empty">empty</div>
43
43
  </template>
44
44
  `;
45
- this.__setMarkerConfigs(JSON.stringify([
45
+ this.__setMarkerConfigs([
46
46
  [0, { type: 'for', iterator: 'item', iterableExprId: 0, deps: ['items'], hasEmpty: true }]
47
- ]));
47
+ ]);
48
48
  const bindings = {
49
49
  l0: [{ n: 'stats', b: 'property', e: 1, d: ['stats'] }]
50
50
  };
@@ -17,10 +17,10 @@ export class TestForTextMarkerCollisionNoTrackParentComponent extends FluffEleme
17
17
  <span class="tag"><!--fluff:text:9--><!--/fluff:text:9--></span>
18
18
  </template>
19
19
  `;
20
- this.__setMarkerConfigs(JSON.stringify([
20
+ this.__setMarkerConfigs([
21
21
  [0, { type: 'for', iterator: 'tag', iterableExprId: 0, deps: ['tags'], hasEmpty: false }],
22
22
  [9, { type: 'text', exprId: 1, deps: ['tag'] }]
23
- ]));
23
+ ]);
24
24
  }
25
25
  __setupBindings() {
26
26
  this.__initializeMarkers(MarkerManager);
@@ -28,7 +28,7 @@ export class TestForTextMarkerCollisionParentComponent extends FluffElement {
28
28
  <span class="tag"><!--fluff:text:9--><!--/fluff:text:9--></span>
29
29
  </template>
30
30
  `;
31
- this.__setMarkerConfigs(JSON.stringify([
31
+ this.__setMarkerConfigs([
32
32
  [0, { type: 'for', iterator: 'tag', iterableExprId: 0, deps: ['tags'], trackBy: 'tag', hasEmpty: false }],
33
33
  [
34
34
  9,
@@ -39,7 +39,7 @@ export class TestForTextMarkerCollisionParentComponent extends FluffElement {
39
39
  pipes: [{ name: 'lowercase', argExprIds: [] }, { name: 'capitalize', argExprIds: [] }]
40
40
  }
41
41
  ]
42
- ]));
42
+ ]);
43
43
  }
44
44
  __setupBindings() {
45
45
  this.__initializeMarkers(MarkerManager);
@@ -20,9 +20,9 @@ export class TestForUnsubscribeNestedParentComponent extends TestUnsubscribeNest
20
20
  <div class="empty">empty</div>
21
21
  </template>
22
22
  `;
23
- this.__setMarkerConfigs(JSON.stringify([
23
+ this.__setMarkerConfigs([
24
24
  [0, { type: 'for', iterator: 'item', iterableExprId: 0, deps: ['items'], hasEmpty: true }]
25
- ]));
25
+ ]);
26
26
  const bindings = {
27
27
  l0: [{ n: 'stats', b: 'property', e: 1, d: ['stats'] }]
28
28
  };
@@ -16,9 +16,9 @@ export class TestGetterReactivityComponent extends FluffElement {
16
16
  this.__getShadowRoot().innerHTML = `
17
17
  <div class="count"><!--fluff:text:0--><!--/fluff:text:0--></div>
18
18
  `;
19
- this.__setMarkerConfigs(JSON.stringify([
19
+ this.__setMarkerConfigs([
20
20
  [0, { type: 'text', exprId: 0, deps: ['items'], pipes: [] }]
21
- ]));
21
+ ]);
22
22
  }
23
23
  __setupBindings() {
24
24
  this.__initializeMarkers(MarkerManager);
@@ -0,0 +1,9 @@
1
+ import { type ExpressionFn, type HandlerFn } from '../FluffBase.js';
2
+ export declare class TestHarness {
3
+ static setExpressionTable(expressions: ExpressionFn[], handlers: HandlerFn[]): void;
4
+ static resetExpressionTable(): void;
5
+ static defineCustomElement(tag: string, ctor: CustomElementConstructor): void;
6
+ static mount(tag: string): HTMLElement;
7
+ static tick(count?: number): Promise<void>;
8
+ static waitForTimeout(): Promise<void>;
9
+ }
@@ -0,0 +1,29 @@
1
+ import { FluffBase } from '../FluffBase.js';
2
+ export class TestHarness {
3
+ static setExpressionTable(expressions, handlers) {
4
+ FluffBase.__setExpressionTable(expressions, handlers);
5
+ }
6
+ static resetExpressionTable() {
7
+ FluffBase.__setExpressionTable([], []);
8
+ }
9
+ static defineCustomElement(tag, ctor) {
10
+ if (!customElements.get(tag)) {
11
+ customElements.define(tag, ctor);
12
+ }
13
+ }
14
+ static mount(tag) {
15
+ const el = document.createElement(tag);
16
+ document.body.appendChild(el);
17
+ return el;
18
+ }
19
+ static async tick(count = 1) {
20
+ for (let i = 0; i < count; i++) {
21
+ await Promise.resolve();
22
+ }
23
+ }
24
+ static async waitForTimeout() {
25
+ await new Promise((resolve) => {
26
+ setTimeout(resolve, 0);
27
+ });
28
+ }
29
+ }
@@ -28,9 +28,9 @@ export class TestIfReinsertBindsInputChildComponent 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: 2, deps: ['stats'], pipes: [] }]
33
- ]));
33
+ ]);
34
34
  }
35
35
  __setupBindings() {
36
36
  this.__initializeMarkers(MarkerManager);
@@ -39,9 +39,9 @@ export class TestIfReinsertBindsInputParentComponent extends FluffElement {
39
39
  <test-if-reinsert-binds-input-child x-fluff-component data-lid="l0"></test-if-reinsert-binds-input-child>
40
40
  </template>
41
41
  `;
42
- this.__setMarkerConfigs(JSON.stringify([
42
+ this.__setMarkerConfigs([
43
43
  [0, { type: 'if', branches: [{ exprId: 0, deps: ['show'] }] }]
44
- ]));
44
+ ]);
45
45
  const bindings = {
46
46
  l0: [{ n: 'stats', b: 'property', e: 1, d: ['stats'] }]
47
47
  };
@@ -17,9 +17,9 @@ export class TestIfUnsubscribeNestedParentComponent extends TestUnsubscribeNeste
17
17
  <test-unsubscribe-nested-child x-fluff-component data-lid="l0"></test-unsubscribe-nested-child>
18
18
  </template>
19
19
  `;
20
- this.__setMarkerConfigs(JSON.stringify([
20
+ this.__setMarkerConfigs([
21
21
  [0, { type: 'if', branches: [{ exprId: 0, deps: ['show'] }] }]
22
- ]));
22
+ ]);
23
23
  const bindings = {
24
24
  l0: [{ n: 'stats', b: 'property', e: 1, d: ['stats'] }]
25
25
  };
@@ -19,9 +19,9 @@ export class TestInterpolationNestedPropertyComponentBase extends FluffElement {
19
19
  }
20
20
  __render() {
21
21
  this.__getShadowRoot().innerHTML = '<!--fluff:text:0--><!--/fluff:text:0-->';
22
- this.__setMarkerConfigs(JSON.stringify([
22
+ this.__setMarkerConfigs([
23
23
  [0, { type: 'text', exprId: 0, deps: [['hostClass', 'childProp']], pipes: [] }]
24
- ]));
24
+ ]);
25
25
  }
26
26
  __setupBindings() {
27
27
  this.__initializeMarkers(MarkerManager);
@@ -14,9 +14,9 @@ export class TestLateDefineForComponent extends FluffElement {
14
14
  <late-define-for-child x-fluff-component="" data-lid="l0"></late-define-for-child>
15
15
  </template>
16
16
  `;
17
- this.__setMarkerConfigs(JSON.stringify([
17
+ this.__setMarkerConfigs([
18
18
  [0, { type: 'for', iterator: 'column', iterableExprId: 0, deps: ['columns'], hasEmpty: false }]
19
- ]));
19
+ ]);
20
20
  }
21
21
  __setupBindings() {
22
22
  this.__initializeMarkers(MarkerManager);
@@ -25,10 +25,10 @@ export class TestNullInputTextComponent extends FluffElement {
25
25
  </template>
26
26
  <template data-fluff-branch="test-null-input-text-0-1"></template>
27
27
  `;
28
- this.__setMarkerConfigs(JSON.stringify([
28
+ this.__setMarkerConfigs([
29
29
  [0, { type: 'if', branches: [{ exprId: 0, deps: ['isEditing'] }, { exprId: undefined, deps: [] }] }],
30
30
  [1, { type: 'text', exprId: 1, deps: ['task'], pipes: [] }]
31
- ]));
31
+ ]);
32
32
  }
33
33
  __setupBindings() {
34
34
  this.__initializeMarkers(MarkerManager);
@@ -23,9 +23,9 @@ export class TestOutputBindingParentComponent extends FluffElement {
23
23
  <test-output-binding-child x-fluff-component data-lid="l0"></test-output-binding-child>
24
24
  </template>
25
25
  `;
26
- this.__setMarkerConfigs(JSON.stringify([
26
+ this.__setMarkerConfigs([
27
27
  [0, { type: 'if', branches: [{ exprId: 0, deps: ['show'] }] }]
28
- ]));
28
+ ]);
29
29
  const bindings = {
30
30
  l0: [{ n: 'edit', b: 'event', h: 0, d: ['onChildEdit'] }]
31
31
  };
@@ -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);
@@ -0,0 +1,4 @@
1
+ export declare function createClassBindingTestComponent(): CustomElementConstructor;
2
+ export declare function createStyleBindingTestComponent(): CustomElementConstructor;
3
+ export declare function createPropertyBindingTestComponent(): CustomElementConstructor;
4
+ export declare function createAttributeBindingTestComponent(): CustomElementConstructor;
@@ -0,0 +1,23 @@
1
+ import { FluffElement } from '../FluffElement.js';
2
+ function createTestComponent(html) {
3
+ return class extends FluffElement {
4
+ __render() {
5
+ this.__getShadowRoot().innerHTML = html;
6
+ }
7
+ __setupBindings() {
8
+ this.__processBindings();
9
+ }
10
+ };
11
+ }
12
+ export function createClassBindingTestComponent() {
13
+ return createTestComponent('<div data-lid="l0">test</div>');
14
+ }
15
+ export function createStyleBindingTestComponent() {
16
+ return createTestComponent('<div data-lid="l0">test</div>');
17
+ }
18
+ export function createPropertyBindingTestComponent() {
19
+ return createTestComponent('<button data-lid="l0">test</button>');
20
+ }
21
+ export function createAttributeBindingTestComponent() {
22
+ return createTestComponent('<div data-lid="l0">test</div>');
23
+ }
@@ -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);
@@ -1,3 +1,4 @@
1
+ import type { Property } from '../../utils/Property.js';
1
2
  import type { TestLateDefineForColumn } from './TestLateDefineForComponent.js';
2
3
  import type { TestTask } from './TestNullInputTextComponent.js';
3
4
  export declare function hasItem(t: unknown): t is {
@@ -18,3 +19,23 @@ export declare function hasValue(t: unknown): t is {
18
19
  export declare function hasTaskId(e: unknown): e is {
19
20
  taskId: number;
20
21
  };
22
+ export declare function hasItemSelected(obj: unknown): obj is {
23
+ item: {
24
+ selected: Property<boolean>;
25
+ };
26
+ };
27
+ export declare function hasItemColor(obj: unknown): obj is {
28
+ item: {
29
+ color: Property<string>;
30
+ };
31
+ };
32
+ export declare function hasItemDisabled(obj: unknown): obj is {
33
+ item: {
34
+ disabled: Property<boolean>;
35
+ };
36
+ };
37
+ export declare function hasItemValue(obj: unknown): obj is {
38
+ item: {
39
+ value: Property<string>;
40
+ };
41
+ };
@@ -16,3 +16,51 @@ export function hasValue(t) {
16
16
  export function hasTaskId(e) {
17
17
  return e !== null && typeof e === 'object' && 'taskId' in e && typeof e.taskId === 'number';
18
18
  }
19
+ export function hasItemSelected(obj) {
20
+ if (obj === null || typeof obj !== 'object')
21
+ return false;
22
+ if (!('item' in obj))
23
+ return false;
24
+ const { item } = obj;
25
+ if (item === null || typeof item !== 'object')
26
+ return false;
27
+ if (!('selected' in item))
28
+ return false;
29
+ return true;
30
+ }
31
+ export function hasItemColor(obj) {
32
+ if (obj === null || typeof obj !== 'object')
33
+ return false;
34
+ if (!('item' in obj))
35
+ return false;
36
+ const { item } = obj;
37
+ if (item === null || typeof item !== 'object')
38
+ return false;
39
+ if (!('color' in item))
40
+ return false;
41
+ return true;
42
+ }
43
+ export function hasItemDisabled(obj) {
44
+ if (obj === null || typeof obj !== 'object')
45
+ return false;
46
+ if (!('item' in obj))
47
+ return false;
48
+ const { item } = obj;
49
+ if (item === null || typeof item !== 'object')
50
+ return false;
51
+ if (!('disabled' in item))
52
+ return false;
53
+ return true;
54
+ }
55
+ export function hasItemValue(obj) {
56
+ if (obj === null || typeof obj !== 'object')
57
+ return false;
58
+ if (!('item' in obj))
59
+ return false;
60
+ const { item } = obj;
61
+ if (item === null || typeof item !== 'object')
62
+ return false;
63
+ if (!('value' in item))
64
+ return false;
65
+ return true;
66
+ }
@@ -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
- }