element-vir 26.11.2 → 26.12.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 (188) hide show
  1. package/package.json +12 -12
  2. package/src/declarative-element/declarative-element-init.ts +115 -0
  3. package/src/declarative-element/declarative-element.ts +372 -0
  4. package/src/declarative-element/define-element.ts +515 -0
  5. package/{dist/declarative-element/definition-options.d.ts → src/declarative-element/definition-options.ts} +7 -2
  6. package/src/declarative-element/directives/assign.directive.ts +89 -0
  7. package/{dist/declarative-element/directives/async-prop.js → src/declarative-element/directives/async-prop.ts} +42 -8
  8. package/src/declarative-element/directives/attributes.directive.ts +63 -0
  9. package/src/declarative-element/directives/create-attribute-directive.ts +47 -0
  10. package/src/declarative-element/directives/directive-helpers.ts +67 -0
  11. package/{dist/declarative-element/directives/listen-to-activate.js → src/declarative-element/directives/listen-to-activate.ts} +8 -3
  12. package/src/declarative-element/directives/listen.directive.ts +206 -0
  13. package/src/declarative-element/directives/mutate.directive.ts +78 -0
  14. package/src/declarative-element/directives/on-dom-created.directive.ts +68 -0
  15. package/src/declarative-element/directives/on-dom-rendered.directive.ts +61 -0
  16. package/src/declarative-element/directives/on-intersect.directive.ts +139 -0
  17. package/src/declarative-element/directives/on-resize.directive.ts +142 -0
  18. package/src/declarative-element/directives/render-async.directive.ts +111 -0
  19. package/{dist/declarative-element/directives/render-if.directive.js → src/declarative-element/directives/render-if.directive.ts} +12 -3
  20. package/{dist/declarative-element/directives/test-id.directive.js → src/declarative-element/directives/test-id.directive.ts} +7 -2
  21. package/{dist/declarative-element/has-declarative-element-parent.js → src/declarative-element/has-declarative-element-parent.ts} +7 -4
  22. package/{dist/declarative-element/is-declarative-element-definition.js → src/declarative-element/is-declarative-element-definition.ts} +28 -11
  23. package/{dist/declarative-element/is-declarative-element.js → src/declarative-element/is-declarative-element.ts} +11 -5
  24. package/src/declarative-element/properties/assign-inputs.ts +30 -0
  25. package/src/declarative-element/properties/css-vars.ts +24 -0
  26. package/src/declarative-element/properties/element-events.ts +161 -0
  27. package/src/declarative-element/properties/host-classes.ts +63 -0
  28. package/{dist/declarative-element/properties/property-proxy.js → src/declarative-element/properties/property-proxy.ts} +58 -21
  29. package/src/declarative-element/properties/string-names.ts +83 -0
  30. package/src/declarative-element/properties/styles.ts +112 -0
  31. package/src/declarative-element/render-callback.ts +196 -0
  32. package/src/declarative-element/wrap-define-element.ts +127 -0
  33. package/{dist/index.d.ts → src/index.ts} +2 -0
  34. package/{dist/lit-exports/base-lit-exports.js → src/lit-exports/base-lit-exports.ts} +10 -1
  35. package/{dist/lit-exports/lit-repeat-fix.d.ts → src/lit-exports/lit-repeat-fix.ts} +45 -16
  36. package/{dist/readme-examples/my-app.element.js → src/readme-examples/my-app.element.ts} +5 -4
  37. package/src/readme-examples/my-custom-action.event.ts +3 -0
  38. package/{dist/readme-examples/my-custom-define.js → src/readme-examples/my-custom-define.ts} +9 -4
  39. package/{dist/readme-examples/my-simple.element.js → src/readme-examples/my-simple.element.ts} +4 -3
  40. package/src/readme-examples/my-with-assignment.element.ts +16 -0
  41. package/{dist/readme-examples/my-with-async-prop.element.js → src/readme-examples/my-with-async-prop.element.ts} +24 -16
  42. package/{dist/readme-examples/my-with-cleanup-callback.element.js → src/readme-examples/my-with-cleanup-callback.element.ts} +5 -4
  43. package/{dist/readme-examples/my-with-css-vars.element.js → src/readme-examples/my-with-css-vars.element.ts} +5 -4
  44. package/src/readme-examples/my-with-custom-events.element.ts +23 -0
  45. package/{dist/readme-examples/my-with-event-listening.element.js → src/readme-examples/my-with-event-listening.element.ts} +10 -9
  46. package/src/readme-examples/my-with-events.element.ts +23 -0
  47. package/{dist/readme-examples/my-with-host-class-definition.element.js → src/readme-examples/my-with-host-class-definition.element.ts} +7 -6
  48. package/{dist/readme-examples/my-with-host-class-usage.element.js → src/readme-examples/my-with-host-class-usage.element.ts} +5 -4
  49. package/src/readme-examples/my-with-inputs.element.ts +13 -0
  50. package/{dist/readme-examples/my-with-on-dom-created.element.js → src/readme-examples/my-with-on-dom-created.element.ts} +7 -6
  51. package/src/readme-examples/my-with-on-resize.element.ts +19 -0
  52. package/src/readme-examples/my-with-render-if.element.ts +15 -0
  53. package/{dist/readme-examples/my-with-styles-and-interpolated-selector.element.js → src/readme-examples/my-with-styles-and-interpolated-selector.element.ts} +6 -5
  54. package/{dist/readme-examples/my-with-styles.element.js → src/readme-examples/my-with-styles.element.ts} +5 -4
  55. package/{dist/readme-examples/my-with-update-state.element.js → src/readme-examples/my-with-update-state.element.ts} +8 -7
  56. package/src/readme-examples/require-declarative-element.ts +3 -0
  57. package/{dist/require-declarative-element.js → src/require-declarative-element.ts} +1 -0
  58. package/{dist/template-transforms/minimal-element-definition.d.ts → src/template-transforms/minimal-element-definition.ts} +19 -7
  59. package/src/template-transforms/nested-mapped-templates.ts +157 -0
  60. package/{dist/template-transforms/template-transform-type.d.ts → src/template-transforms/template-transform-type.ts} +3 -1
  61. package/{dist/template-transforms/transform-template.js → src/template-transforms/transform-template.ts} +70 -22
  62. package/src/template-transforms/vir-css/css-transform.ts +30 -0
  63. package/src/template-transforms/vir-css/vir-css.ts +30 -0
  64. package/src/template-transforms/vir-html/html-interpolation.ts +103 -0
  65. package/src/template-transforms/vir-html/html-transform.ts +149 -0
  66. package/{dist/template-transforms/vir-html/tag-name-keys.js → src/template-transforms/vir-html/tag-name-keys.ts} +1 -1
  67. package/{dist/template-transforms/vir-html/vir-html.js → src/template-transforms/vir-html/vir-html.ts} +13 -5
  68. package/src/typed-event/typed-event.ts +90 -0
  69. package/{dist/util/array.js → src/util/array.ts} +18 -5
  70. package/{dist/util/increment.d.ts → src/util/increment.ts} +24 -5
  71. package/{dist/util/lit-template.js → src/util/lit-template.ts} +30 -10
  72. package/src/util/map-async-value.ts +33 -0
  73. package/dist/declarative-element/custom-tag-name.js +0 -1
  74. package/dist/declarative-element/declarative-element-init.d.ts +0 -56
  75. package/dist/declarative-element/declarative-element-init.js +0 -1
  76. package/dist/declarative-element/declarative-element.d.ts +0 -114
  77. package/dist/declarative-element/declarative-element.js +0 -36
  78. package/dist/declarative-element/define-element.d.ts +0 -41
  79. package/dist/declarative-element/define-element.js +0 -248
  80. package/dist/declarative-element/definition-options.js +0 -9
  81. package/dist/declarative-element/directives/assign.directive.d.ts +0 -24
  82. package/dist/declarative-element/directives/assign.directive.js +0 -34
  83. package/dist/declarative-element/directives/async-prop.d.ts +0 -61
  84. package/dist/declarative-element/directives/attributes.directive.d.ts +0 -30
  85. package/dist/declarative-element/directives/attributes.directive.js +0 -35
  86. package/dist/declarative-element/directives/create-attribute-directive.d.ts +0 -28
  87. package/dist/declarative-element/directives/create-attribute-directive.js +0 -41
  88. package/dist/declarative-element/directives/directive-helpers.d.ts +0 -27
  89. package/dist/declarative-element/directives/directive-helpers.js +0 -37
  90. package/dist/declarative-element/directives/listen-to-activate.d.ts +0 -15
  91. package/dist/declarative-element/directives/listen.directive.d.ts +0 -92
  92. package/dist/declarative-element/directives/listen.directive.js +0 -48
  93. package/dist/declarative-element/directives/mutate.directive.d.ts +0 -38
  94. package/dist/declarative-element/directives/mutate.directive.js +0 -45
  95. package/dist/declarative-element/directives/on-dom-created.directive.d.ts +0 -44
  96. package/dist/declarative-element/directives/on-dom-created.directive.js +0 -51
  97. package/dist/declarative-element/directives/on-dom-rendered.directive.d.ts +0 -41
  98. package/dist/declarative-element/directives/on-dom-rendered.directive.js +0 -45
  99. package/dist/declarative-element/directives/on-intersect.directive.d.ts +0 -64
  100. package/dist/declarative-element/directives/on-intersect.directive.js +0 -89
  101. package/dist/declarative-element/directives/on-resize.directive.d.ts +0 -74
  102. package/dist/declarative-element/directives/on-resize.directive.js +0 -106
  103. package/dist/declarative-element/directives/render-async.directive.d.ts +0 -45
  104. package/dist/declarative-element/directives/render-async.directive.js +0 -33
  105. package/dist/declarative-element/directives/render-if.directive.d.ts +0 -32
  106. package/dist/declarative-element/directives/test-id.directive.d.ts +0 -52
  107. package/dist/declarative-element/has-declarative-element-parent.d.ts +0 -1
  108. package/dist/declarative-element/is-declarative-element-definition.d.ts +0 -17
  109. package/dist/declarative-element/is-declarative-element.d.ts +0 -15
  110. package/dist/declarative-element/properties/assign-inputs.d.ts +0 -1
  111. package/dist/declarative-element/properties/assign-inputs.js +0 -25
  112. package/dist/declarative-element/properties/css-vars.d.ts +0 -16
  113. package/dist/declarative-element/properties/css-vars.js +0 -1
  114. package/dist/declarative-element/properties/element-events.d.ts +0 -65
  115. package/dist/declarative-element/properties/element-events.js +0 -62
  116. package/dist/declarative-element/properties/element-properties.js +0 -1
  117. package/dist/declarative-element/properties/host-classes.d.ts +0 -36
  118. package/dist/declarative-element/properties/host-classes.js +0 -16
  119. package/dist/declarative-element/properties/property-proxy.d.ts +0 -22
  120. package/dist/declarative-element/properties/string-names.d.ts +0 -28
  121. package/dist/declarative-element/properties/string-names.js +0 -40
  122. package/dist/declarative-element/properties/styles.d.ts +0 -51
  123. package/dist/declarative-element/properties/styles.js +0 -41
  124. package/dist/declarative-element/properties/tag-name.js +0 -1
  125. package/dist/declarative-element/render-callback.d.ts +0 -56
  126. package/dist/declarative-element/render-callback.js +0 -27
  127. package/dist/declarative-element/wrap-define-element.d.ts +0 -36
  128. package/dist/declarative-element/wrap-define-element.js +0 -25
  129. package/dist/index.js +0 -42
  130. package/dist/lit-exports/all-lit-exports.js +0 -2
  131. package/dist/lit-exports/base-lit-exports.d.ts +0 -25
  132. package/dist/lit-exports/lit-repeat-fix.js +0 -37
  133. package/dist/readme-examples/my-app.element.d.ts +0 -1
  134. package/dist/readme-examples/my-custom-action.event.d.ts +0 -1
  135. package/dist/readme-examples/my-custom-action.event.js +0 -2
  136. package/dist/readme-examples/my-custom-define.d.ts +0 -4
  137. package/dist/readme-examples/my-simple.element.d.ts +0 -1
  138. package/dist/readme-examples/my-with-assignment.element.d.ts +0 -1
  139. package/dist/readme-examples/my-with-assignment.element.js +0 -15
  140. package/dist/readme-examples/my-with-async-prop.element.d.ts +0 -10
  141. package/dist/readme-examples/my-with-cleanup-callback.element.d.ts +0 -3
  142. package/dist/readme-examples/my-with-css-vars.element.d.ts +0 -1
  143. package/dist/readme-examples/my-with-custom-events.element.d.ts +0 -1
  144. package/dist/readme-examples/my-with-custom-events.element.js +0 -22
  145. package/dist/readme-examples/my-with-event-listening.element.d.ts +0 -3
  146. package/dist/readme-examples/my-with-events.element.d.ts +0 -4
  147. package/dist/readme-examples/my-with-events.element.js +0 -20
  148. package/dist/readme-examples/my-with-host-class-definition.element.d.ts +0 -3
  149. package/dist/readme-examples/my-with-host-class-usage.element.d.ts +0 -1
  150. package/dist/readme-examples/my-with-inputs.element.d.ts +0 -4
  151. package/dist/readme-examples/my-with-inputs.element.js +0 -9
  152. package/dist/readme-examples/my-with-on-dom-created.element.d.ts +0 -1
  153. package/dist/readme-examples/my-with-on-resize.element.d.ts +0 -1
  154. package/dist/readme-examples/my-with-on-resize.element.js +0 -18
  155. package/dist/readme-examples/my-with-render-if.element.d.ts +0 -3
  156. package/dist/readme-examples/my-with-render-if.element.js +0 -11
  157. package/dist/readme-examples/my-with-styles-and-interpolated-selector.element.d.ts +0 -1
  158. package/dist/readme-examples/my-with-styles.element.d.ts +0 -1
  159. package/dist/readme-examples/my-with-update-state.element.d.ts +0 -8
  160. package/dist/readme-examples/require-declarative-element.d.ts +0 -1
  161. package/dist/readme-examples/require-declarative-element.js +0 -2
  162. package/dist/require-declarative-element.d.ts +0 -14
  163. package/dist/template-transforms/minimal-element-definition.js +0 -19
  164. package/dist/template-transforms/nested-mapped-templates.d.ts +0 -6
  165. package/dist/template-transforms/nested-mapped-templates.js +0 -96
  166. package/dist/template-transforms/template-transform-type.js +0 -1
  167. package/dist/template-transforms/transform-template.d.ts +0 -14
  168. package/dist/template-transforms/vir-css/css-transform.d.ts +0 -4
  169. package/dist/template-transforms/vir-css/css-transform.js +0 -15
  170. package/dist/template-transforms/vir-css/vir-css.d.ts +0 -12
  171. package/dist/template-transforms/vir-css/vir-css.js +0 -21
  172. package/dist/template-transforms/vir-html/html-interpolation.d.ts +0 -42
  173. package/dist/template-transforms/vir-html/html-interpolation.js +0 -1
  174. package/dist/template-transforms/vir-html/html-transform.d.ts +0 -5
  175. package/dist/template-transforms/vir-html/html-transform.js +0 -96
  176. package/dist/template-transforms/vir-html/tag-name-keys.d.ts +0 -7
  177. package/dist/template-transforms/vir-html/vir-html.d.ts +0 -11
  178. package/dist/typed-event/typed-event.d.ts +0 -55
  179. package/dist/typed-event/typed-event.js +0 -50
  180. package/dist/util/array.d.ts +0 -5
  181. package/dist/util/increment.js +0 -1
  182. package/dist/util/lit-template.d.ts +0 -9
  183. package/dist/util/type.js +0 -1
  184. /package/{dist/declarative-element/custom-tag-name.d.ts → src/declarative-element/custom-tag-name.ts} +0 -0
  185. /package/{dist/declarative-element/properties/element-properties.d.ts → src/declarative-element/properties/element-properties.ts} +0 -0
  186. /package/{dist/declarative-element/properties/tag-name.d.ts → src/declarative-element/properties/tag-name.ts} +0 -0
  187. /package/{dist/lit-exports/all-lit-exports.d.ts → src/lit-exports/all-lit-exports.ts} +0 -0
  188. /package/{dist/util/type.d.ts → src/util/type.ts} +0 -0
@@ -0,0 +1,61 @@
1
+ import {type MaybePromise} from '@augment-vir/common';
2
+ import {directive, Directive, type PartInfo} from '../../lit-exports/all-lit-exports.js';
3
+ import {assertIsElementPartInfo} from './directive-helpers.js';
4
+
5
+ /**
6
+ * The callback / listener passed to {@link onDomRendered}. The `element` parameter is a reference to
7
+ * the DOM element that the directive was attached to.
8
+ *
9
+ * @category Internal
10
+ */
11
+ export type OnDomRenderedCallback = (element: Element) => MaybePromise<void>;
12
+
13
+ const directiveName = 'onDomRendered';
14
+
15
+ /**
16
+ * A directive that fires its listener each time the element that it's attached to is rendered.
17
+ *
18
+ * @category Directives
19
+ * @example
20
+ *
21
+ * ```ts
22
+ * import {html, defineElement, onDomRendered} from 'element-vir';
23
+ *
24
+ * const MyElement = defineElement()({
25
+ * tagName: 'my-element',
26
+ * render() {
27
+ * return html`
28
+ * <div
29
+ * ${onDomRendered((element) => {
30
+ * console.log('rendered!', element);
31
+ * })}
32
+ * >
33
+ * Some div
34
+ * </div>
35
+ * `;
36
+ * },
37
+ * });
38
+ * ```
39
+ */
40
+ export const onDomRendered = directive(
41
+ class extends Directive {
42
+ constructor(partInfo: PartInfo) {
43
+ super(partInfo);
44
+
45
+ assertIsElementPartInfo(partInfo, directiveName);
46
+ }
47
+
48
+ public override update(partInfo: PartInfo, [callback]: [OnDomRenderedCallback]) {
49
+ assertIsElementPartInfo(partInfo, directiveName);
50
+ const element = partInfo.element;
51
+ // use `requestAnimationFrame` here so it can fire property changes outside of a render loop
52
+ window.requestAnimationFrame(() => callback(element));
53
+ return this.render(callback);
54
+ }
55
+
56
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
57
+ public render(callback: OnDomRenderedCallback) {
58
+ return undefined;
59
+ }
60
+ },
61
+ );
@@ -0,0 +1,139 @@
1
+ import {assert, assertWrap, check} from '@augment-vir/assert';
2
+ import {type MaybePromise} from '@augment-vir/common';
3
+ import {directive, Directive, type PartInfo} from '../../lit-exports/all-lit-exports.js';
4
+ import {assertIsElementPartInfo} from './directive-helpers.js';
5
+
6
+ const directiveName = 'onIntersect';
7
+
8
+ /**
9
+ * Callback called by the {@link onIntersect} directive.
10
+ *
11
+ * @category Internal
12
+ */
13
+ export type OnIntersectCallback = (params: {
14
+ entry: IntersectionObserverEntry;
15
+ allEntries: IntersectionObserverEntry[];
16
+ observer: IntersectionObserver;
17
+ element: Element;
18
+ }) => MaybePromise<void>;
19
+
20
+ /**
21
+ * Options used for the {@link onIntersect} directive.
22
+ *
23
+ * @category Internal
24
+ */
25
+ export type OnIntersectOptions = IntersectionObserverInit;
26
+
27
+ /**
28
+ * A directive that fires its listener any time the element's configured "intersection" is crossed.
29
+ * This is commonly use for detecting when an element has scrolled into view. This uses the
30
+ * [built-in `IntersectionObserver`
31
+ * API](https://developer.mozilla.org/docs/Web/API/IntersectionObserver/IntersectionObserver), so it
32
+ * is very efficient.
33
+ *
34
+ * @category Directives
35
+ * @example
36
+ *
37
+ * ```ts
38
+ * import {html, defineElement, onIntersect} from 'element-vir';
39
+ *
40
+ * const MyElement = defineElement()({
41
+ * tagName: 'my-element',
42
+ * render() {
43
+ * return html`
44
+ * <div
45
+ * ${onIntersect({threshold: 1}, ({element, entry}) => {
46
+ * if (entry.isIntersecting) {
47
+ * console.log('is intersecting!');
48
+ * } else {
49
+ * console.log('is not intersecting');
50
+ * }
51
+ * })}
52
+ * >
53
+ * Some div
54
+ * </div>
55
+ * `;
56
+ * },
57
+ * });
58
+ * ```
59
+ */
60
+ export const onIntersect = directive(
61
+ class extends Directive {
62
+ public element: Element | undefined;
63
+ public options: OnIntersectOptions | undefined;
64
+ public intersectionObserver: undefined | IntersectionObserver;
65
+ public callback: OnIntersectCallback | undefined;
66
+
67
+ constructor(partInfo: PartInfo) {
68
+ super(partInfo);
69
+
70
+ assertIsElementPartInfo(partInfo, directiveName);
71
+ }
72
+
73
+ public fireCallback(entries: IntersectionObserverEntry[], observer: IntersectionObserver) {
74
+ assert.isLengthAtLeast(entries, 1);
75
+
76
+ void this.callback?.({
77
+ element: assertWrap.isDefined(this.element),
78
+ allEntries: entries,
79
+ observer,
80
+ entry: entries[0],
81
+ });
82
+ }
83
+
84
+ public override update(
85
+ partInfo: PartInfo,
86
+ [
87
+ options,
88
+ callback,
89
+ ]: [
90
+ OnIntersectOptions,
91
+ OnIntersectCallback,
92
+ ],
93
+ ) {
94
+ assertIsElementPartInfo(partInfo, directiveName);
95
+ this.callback = callback;
96
+ let needsObserving = false;
97
+
98
+ const newOptions = options;
99
+ const oldOptions = this.options;
100
+ if (
101
+ !this.intersectionObserver ||
102
+ !oldOptions ||
103
+ !check.entriesEqual(newOptions, oldOptions)
104
+ ) {
105
+ this.options = options;
106
+ this.intersectionObserver?.disconnect();
107
+
108
+ this.intersectionObserver = new IntersectionObserver(
109
+ (entries, observer) => this.fireCallback(entries, observer),
110
+ options,
111
+ );
112
+
113
+ needsObserving = true;
114
+ }
115
+
116
+ const newElement = partInfo.element;
117
+ const oldElement = this.element;
118
+ // if the element changes we need to observe the new one
119
+ if (newElement !== oldElement) {
120
+ this.element = newElement;
121
+ if (oldElement) {
122
+ this.intersectionObserver.unobserve(oldElement);
123
+ }
124
+ needsObserving = true;
125
+ }
126
+
127
+ if (needsObserving) {
128
+ this.intersectionObserver.observe(newElement);
129
+ }
130
+
131
+ return this.render(options, callback);
132
+ }
133
+
134
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
135
+ public render(options: OnIntersectOptions, callback: OnIntersectCallback) {
136
+ return undefined;
137
+ }
138
+ },
139
+ );
@@ -0,0 +1,142 @@
1
+ import {type MaybePromise} from '@augment-vir/common';
2
+ import {directive, Directive, type PartInfo} from '../../lit-exports/all-lit-exports.js';
3
+ import {assertIsElementPartInfo} from './directive-helpers.js';
4
+
5
+ /**
6
+ * Callback called by the {@link onResize} directive.
7
+ *
8
+ * @category Internal
9
+ */
10
+ export type OnResizeCallback = (
11
+ size: Readonly<
12
+ Pick<
13
+ ResizeObserverEntry,
14
+ /** Only these two properties are supported in all major modern browsers */
15
+ 'target' | 'contentRect'
16
+ >
17
+ >,
18
+ element: Element,
19
+ ) => MaybePromise<void>;
20
+
21
+ const directiveName = 'onResize';
22
+
23
+ /**
24
+ * A directive that fires its listener any time the element that it's attached to is resized. This
25
+ * uses the [built-in `ResizeObserver`
26
+ * API](https://developer.mozilla.org/docs/Web/API/ResizeObserver), so it is very efficient.
27
+ *
28
+ * @category Directives
29
+ * @example
30
+ *
31
+ * ```ts
32
+ * import {html, defineElement, onResize} from 'element-vir';
33
+ *
34
+ * const MyElement = defineElement()({
35
+ * tagName: 'my-element',
36
+ * render() {
37
+ * return html`
38
+ * <div
39
+ * ${onResize((size, element) => {
40
+ * console.log('resized!', element, size);
41
+ * })}
42
+ * >
43
+ * Some div
44
+ * </div>
45
+ * `;
46
+ * },
47
+ * });
48
+ * ```
49
+ */
50
+ export const onResize = directive(
51
+ class extends Directive {
52
+ public element: Element | undefined;
53
+ public readonly resizeObserver = new ResizeObserver((entries) => {
54
+ if (this.element && this.callback) {
55
+ handleOnResizeCallback(this.element, this.callback, entries);
56
+ }
57
+ });
58
+ public callback: OnResizeCallback | undefined;
59
+
60
+ constructor(partInfo: PartInfo) {
61
+ super(partInfo);
62
+
63
+ assertIsElementPartInfo(partInfo, directiveName);
64
+ }
65
+
66
+ public override update(partInfo: PartInfo, [callback]: [OnResizeCallback]) {
67
+ assertIsElementPartInfo(partInfo, directiveName);
68
+ this.callback = callback;
69
+ const newElement = partInfo.element;
70
+ const oldElement = this.element;
71
+ // if the element changes we need to observe the new one
72
+ if (newElement !== oldElement) {
73
+ this.element = newElement;
74
+ if (oldElement) {
75
+ this.resizeObserver.unobserve(oldElement);
76
+ }
77
+ this.resizeObserver.observe(newElement);
78
+ }
79
+ return this.render(callback);
80
+ }
81
+
82
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
83
+ public render(callback: OnResizeCallback) {
84
+ return undefined;
85
+ }
86
+ },
87
+ );
88
+
89
+ function handleOnResizeCallback(
90
+ element: Element,
91
+ callback: OnResizeCallback,
92
+ entries: ResizeObserverEntry[],
93
+ ) {
94
+ const resizeEntry = entries[0];
95
+ if (!resizeEntry) {
96
+ console.error(entries);
97
+ throw new Error(`Resize observation triggered but the first entry was empty.`);
98
+ }
99
+ void callback(
100
+ {
101
+ target: resizeEntry.target,
102
+ contentRect: resizeEntry.contentRect,
103
+ },
104
+ element,
105
+ );
106
+ }
107
+
108
+ /**
109
+ * A function that attaches a
110
+ * [`ResizeObserver`](https://developer.mozilla.org/docs/Web/API/ResizeObserver) to any given
111
+ * element, so it is very efficient.
112
+ *
113
+ * @category Directives
114
+ * @example
115
+ *
116
+ * ```ts
117
+ * import {html, defineElement, attachOnResize} from 'element-vir';
118
+ *
119
+ * const MyElement = defineElement()({
120
+ * tagName: 'my-element',
121
+ * render({host}) {
122
+ * attachOnResize(host, (size, element) => {
123
+ * console.log('resized!', element, size);
124
+ * });
125
+ *
126
+ * return '';
127
+ * },
128
+ * });
129
+ * ```
130
+ */
131
+ export function attachOnResize(element: Element, callback: OnResizeCallback) {
132
+ const resizeObserver = new ResizeObserver((entries) => {
133
+ handleOnResizeCallback(element, callback, entries);
134
+ });
135
+
136
+ resizeObserver.observe(element);
137
+
138
+ return {
139
+ resizeObserver,
140
+ element,
141
+ };
142
+ }
@@ -0,0 +1,111 @@
1
+ import {check} from '@augment-vir/assert';
2
+ import {extractErrorMessage} from '@augment-vir/common';
3
+ import {type AsyncProp} from './async-prop.js';
4
+
5
+ /**
6
+ * Given a {@link AsyncProp} instance, call and return the output of the `resolutionRender` parameter
7
+ * once the {@link AsyncProp} has been resolved, call and return the output of the `errorRender`
8
+ * parameter if the {@link AsyncProp} errored out, return the `fallback` parameter in all other
9
+ * cases.
10
+ *
11
+ * This is the overload for when `resolutionRender` and `errorRender` are both provided.
12
+ *
13
+ * @category Async
14
+ */
15
+ export function renderAsync<
16
+ T,
17
+ FallbackResult,
18
+ ResolutionRenderResult = never,
19
+ ErrorRenderResult = never,
20
+ >(
21
+ asyncProp: Pick<AsyncProp<T, any>, 'value'>,
22
+ fallback: FallbackResult,
23
+ resolutionRender: (resolved: Awaited<T>) => ResolutionRenderResult,
24
+ errorRender: (error: Error) => ErrorRenderResult,
25
+ ): FallbackResult | ResolutionRenderResult | ErrorRenderResult;
26
+ /**
27
+ * Given a {@link AsyncProp} instance, call and return the output of the `resolutionRender` parameter
28
+ * once the {@link AsyncProp} has been resolved, call and return the output of the `errorRender`
29
+ * parameter if the {@link AsyncProp} errored out, return the `fallback` parameter in all other
30
+ * cases.
31
+ *
32
+ * This is the overload for when `resolutionRender` is provided but `errorRender` is not.
33
+ *
34
+ * @category Async
35
+ */
36
+ export function renderAsync<T, FallbackResult, ResolutionRenderResult = never>(
37
+ asyncProp: Pick<AsyncProp<T, any>, 'value'>,
38
+ fallback: FallbackResult,
39
+ resolutionRender: (resolved: Awaited<T>) => ResolutionRenderResult,
40
+ errorRender?: undefined,
41
+ ): FallbackResult | ResolutionRenderResult | string;
42
+ /**
43
+ * Given a {@link AsyncProp} instance, call and return the output of the `resolutionRender` parameter
44
+ * once the {@link AsyncProp} has been resolved, call and return the output of the `errorRender`
45
+ * parameter if the {@link AsyncProp} errored out, return the `fallback` parameter in all other
46
+ * cases.
47
+ *
48
+ * This is the overload for when `resolutionRender` is not provided but `errorRender` is.
49
+ *
50
+ * @category Async
51
+ */
52
+ export function renderAsync<T, FallbackResult, ErrorRenderResult = never>(
53
+ asyncProp: Pick<AsyncProp<T, any>, 'value'>,
54
+ fallback: FallbackResult,
55
+ resolutionRender: undefined,
56
+ errorRender: (error: Error) => ErrorRenderResult,
57
+ ): FallbackResult | Awaited<T> | ErrorRenderResult;
58
+ /**
59
+ * Given a {@link AsyncProp} instance, call and return the output of the `resolutionRender` parameter
60
+ * once the {@link AsyncProp} has been resolved, call and return the output of the `errorRender`
61
+ * parameter if the {@link AsyncProp} errored out, return the `fallback` parameter in all other
62
+ * cases.
63
+ *
64
+ * This is the overload for when neither `resolutionRender` or `errorRender` are provided.
65
+ *
66
+ * @category Async
67
+ */
68
+ export function renderAsync<T, FallbackResult>(
69
+ asyncProp: Pick<AsyncProp<T, any>, 'value'>,
70
+ fallback: FallbackResult,
71
+ resolutionRender?: undefined,
72
+ errorRender?: undefined,
73
+ ): FallbackResult | Awaited<T> | string;
74
+ /**
75
+ * Given a {@link AsyncProp} instance, call and return the output of the `resolutionRender` parameter
76
+ * once the {@link AsyncProp} has been resolved, call and return the output of the `errorRender`
77
+ * parameter if the {@link AsyncProp} errored out, return the `fallback` parameter in all other
78
+ * cases.
79
+ *
80
+ * This is the full function definition and implementation.
81
+ *
82
+ * @category Async
83
+ */
84
+ export function renderAsync<
85
+ T,
86
+ FallbackResult,
87
+ ResolutionRenderResult = never,
88
+ ErrorRenderResult = never,
89
+ >(
90
+ asyncProp: Pick<AsyncProp<T, any>, 'value'>,
91
+ /** This value will be rendered if the async prop has not settled yet. */
92
+ fallback: FallbackResult,
93
+ resolutionRender?: ((resolved: Awaited<T>) => ResolutionRenderResult) | undefined,
94
+ errorRender?: ((error: Error) => ErrorRenderResult) | undefined,
95
+ ): FallbackResult | Awaited<T> | ResolutionRenderResult | string | ErrorRenderResult {
96
+ const asyncPropValue = asyncProp.value;
97
+ if (asyncPropValue instanceof Error) {
98
+ const errorResult: string | ErrorRenderResult = errorRender
99
+ ? errorRender(asyncPropValue)
100
+ : extractErrorMessage(asyncPropValue);
101
+ return errorResult as any;
102
+ } else if (check.isPromiseLike(asyncPropValue)) {
103
+ const fallbackResult: FallbackResult = fallback;
104
+ return fallbackResult as any;
105
+ } else {
106
+ const resolutionResult: ResolutionRenderResult | Awaited<T> = resolutionRender
107
+ ? resolutionRender(asyncPropValue)
108
+ : asyncPropValue;
109
+ return resolutionResult as any;
110
+ }
111
+ }
@@ -1,4 +1,5 @@
1
- import { when } from '../../lit-exports/all-lit-exports.js';
1
+ import {when} from '../../lit-exports/all-lit-exports.js';
2
+
2
3
  /**
3
4
  * A directive that, given a condition, chooses between two inputs: `ifTrue` and `ifFalse`. If
4
5
  * `ifFalse` is omitted (which is allowed), the false case defaults to `undefined`, which won't get
@@ -30,6 +31,14 @@ import { when } from '../../lit-exports/all-lit-exports.js';
30
31
  * });
31
32
  * ```
32
33
  */
33
- export function renderIf(condition, ifTrue, ifFalse) {
34
- return when(condition, () => ifTrue, () => ifFalse);
34
+ export function renderIf<TrueCondition = unknown, FalseCondition = undefined>(
35
+ condition: boolean,
36
+ ifTrue: TrueCondition,
37
+ ifFalse?: FalseCondition,
38
+ ): TrueCondition | FalseCondition {
39
+ return when(
40
+ condition,
41
+ () => ifTrue,
42
+ () => ifFalse,
43
+ ) as TrueCondition | FalseCondition;
35
44
  }
@@ -1,5 +1,8 @@
1
- import { createAttributeDirective } from './create-attribute-directive.js';
2
- const { attributeDirective, attributeSelector, attributeName } = createAttributeDirective('data-test-id');
1
+ import {createAttributeDirective} from './create-attribute-directive.js';
2
+
3
+ const {attributeDirective, attributeSelector, attributeName} =
4
+ createAttributeDirective('data-test-id');
5
+
3
6
  /**
4
7
  * Assigns the given id as a test id attribute to the attached element.
5
8
  *
@@ -20,6 +23,7 @@ const { attributeDirective, attributeSelector, attributeName } = createAttribute
20
23
  * ```
21
24
  */
22
25
  export const testId = attributeDirective;
26
+
23
27
  /**
24
28
  * Construct an attribute selector for the given test id.
25
29
  *
@@ -39,6 +43,7 @@ export const testId = attributeDirective;
39
43
  * ```
40
44
  */
41
45
  export const testIdSelector = attributeSelector;
46
+
42
47
  /**
43
48
  * The test id attribute name.
44
49
  *
@@ -1,16 +1,19 @@
1
- import { DeclarativeElement } from './declarative-element.js';
2
- export function hasDeclarativeElementParent(input) {
1
+ import {DeclarativeElement} from './declarative-element.js';
2
+
3
+ export function hasDeclarativeElementParent(input: Element): boolean {
3
4
  const rootNode = input.getRootNode();
5
+
4
6
  if (!(rootNode instanceof ShadowRoot)) {
5
7
  // declarative elements all use shadow DOM, so if a shadow root doesn't exist then we're not
6
8
  // in a declarative element.
7
9
  return false;
8
10
  }
11
+
9
12
  const host = rootNode.host;
13
+
10
14
  if (host instanceof DeclarativeElement) {
11
15
  return true;
12
- }
13
- else {
16
+ } else {
14
17
  return hasDeclarativeElementParent(host);
15
18
  }
16
19
  }
@@ -1,5 +1,10 @@
1
- import { AssertionError, check } from '@augment-vir/assert';
2
- import { getObjectTypedKeys, wrapInTry } from '@augment-vir/common';
1
+ import {AssertionError, check} from '@augment-vir/assert';
2
+ import {getObjectTypedKeys, wrapInTry} from '@augment-vir/common';
3
+ import {
4
+ type DeclarativeElementDefinition,
5
+ type StaticDeclarativeElementProperties,
6
+ } from './declarative-element.js';
7
+
3
8
  const expectedStaticProperties = getObjectTypedKeys({
4
9
  assign: '',
5
10
  assignedInputs: '',
@@ -16,7 +21,10 @@ const expectedStaticProperties = getObjectTypedKeys({
16
21
  styles: '',
17
22
  tagName: '',
18
23
  UpdateStateType: '',
19
- });
24
+ } satisfies Readonly<
25
+ Record<keyof StaticDeclarativeElementProperties<any, any, any, any, any, any, any, any>, ''>
26
+ >);
27
+
20
28
  /**
21
29
  * Asserts that the given input is a declarative element definition.
22
30
  *
@@ -24,7 +32,10 @@ const expectedStaticProperties = getObjectTypedKeys({
24
32
  * @see
25
33
  * - {@link isDeclarativeElementDefinition}
26
34
  */
27
- export function assertDeclarativeElementDefinition(input, failMessage) {
35
+ export function assertDeclarativeElementDefinition(
36
+ input: unknown,
37
+ failMessage?: string | undefined,
38
+ ): asserts input is DeclarativeElementDefinition {
28
39
  if (!check.isFunction(input)) {
29
40
  throw new AssertionError('Input is not a declarative element constructor', failMessage);
30
41
  }
@@ -34,6 +45,7 @@ export function assertDeclarativeElementDefinition(input, failMessage) {
34
45
  }
35
46
  });
36
47
  }
48
+
37
49
  /**
38
50
  * Checks that the given input is a declarative element definition.
39
51
  *
@@ -41,11 +53,16 @@ export function assertDeclarativeElementDefinition(input, failMessage) {
41
53
  * @see
42
54
  * - {@link assertDeclarativeElementDefinition}
43
55
  */
44
- export function isDeclarativeElementDefinition(input) {
45
- return wrapInTry(() => {
46
- assertDeclarativeElementDefinition(input);
47
- return true;
48
- }, {
49
- fallbackValue: false,
50
- });
56
+ export function isDeclarativeElementDefinition(
57
+ input: unknown,
58
+ ): input is DeclarativeElementDefinition {
59
+ return wrapInTry(
60
+ () => {
61
+ assertDeclarativeElementDefinition(input);
62
+ return true;
63
+ },
64
+ {
65
+ fallbackValue: false,
66
+ },
67
+ );
51
68
  }
@@ -1,12 +1,14 @@
1
- import { check } from '@augment-vir/assert';
1
+ import {check} from '@augment-vir/assert';
2
+ import {type DeclarativeElement} from './declarative-element.js';
3
+
2
4
  /**
3
5
  * Checks if the input is an instance of a DeclarativeElement, the super class of all custom
4
6
  * elements defined with element-vir.
5
7
  *
6
8
  * @category Util
7
9
  */
8
- export function isDeclarativeElement(input) {
9
- const markerProperties = [
10
+ export function isDeclarativeElement(input: unknown): input is DeclarativeElement {
11
+ const markerProperties: ReadonlyArray<keyof DeclarativeElement> = [
10
12
  'instanceInputs',
11
13
  'instanceState',
12
14
  'definition',
@@ -19,9 +21,13 @@ export function isDeclarativeElement(input) {
19
21
  *
20
22
  * @category Util
21
23
  */
22
- export function assertIsDeclarativeElement(input) {
24
+ export function assertIsDeclarativeElement(input: unknown): asserts input is DeclarativeElement {
23
25
  if (!isDeclarativeElement(input)) {
24
26
  console.error('this is not a declarative element:', input);
25
- throw new Error(`${String(check.hasKey(input, 'tagName') ? input.tagName : input)} is not a declarative element.`);
27
+ throw new Error(
28
+ `${String(
29
+ check.hasKey(input, 'tagName') ? input.tagName : input,
30
+ )} is not a declarative element.`,
31
+ );
26
32
  }
27
33
  }
@@ -0,0 +1,30 @@
1
+ import {getObjectTypedKeys} from '@augment-vir/common';
2
+ import {type DeclarativeElement} from '../declarative-element.js';
3
+
4
+ export function assignInputs(element: Element, inputs: object): void {
5
+ const instanceState = (element as Partial<DeclarativeElement>).instanceState;
6
+
7
+ getObjectTypedKeys(inputs).forEach((newInputKey) => {
8
+ if (instanceState && newInputKey in instanceState) {
9
+ throw new Error(
10
+ `Cannot set input '${String(newInputKey)}' on '${element.tagName}'. '${element.tagName}' already has a state property with the same name.`,
11
+ );
12
+ }
13
+ if ('instanceInputs' in element) {
14
+ (element.instanceInputs as DeclarativeElement['instanceInputs'])[newInputKey] =
15
+ inputs[newInputKey];
16
+ } else {
17
+ element[newInputKey] = inputs[newInputKey];
18
+ }
19
+ });
20
+
21
+ /** Wipe out all inputs that weren't set to undefined (as expected) */
22
+ if ('instanceInputs' in element) {
23
+ getObjectTypedKeys(element.instanceInputs).forEach((existingKey) => {
24
+ if (!(existingKey in inputs)) {
25
+ (element.instanceInputs as DeclarativeElement['instanceInputs'])[existingKey] =
26
+ undefined;
27
+ }
28
+ });
29
+ }
30
+ }
@@ -0,0 +1,24 @@
1
+ import {type Values} from '@augment-vir/common';
2
+ import {type CssVarDefinitions, type CssVarsSetup} from 'lit-css-vars';
3
+ import {type CustomElementTagName} from '../custom-tag-name.js';
4
+ import {type BaseStringName} from './string-names.js';
5
+
6
+ /**
7
+ * Base type for a declarative element definition's CSS vars.
8
+ *
9
+ * @category Internal
10
+ */
11
+ export type CssVarsInitMap<
12
+ ElementTagName extends CustomElementTagName,
13
+ CssVarKeys extends BaseStringName<ElementTagName>,
14
+ > = Readonly<Record<CssVarKeys, Values<CssVarsSetup>>>;
15
+
16
+ /**
17
+ * CSS vars ready for use within a declarative element's `render` or `styles` method.
18
+ *
19
+ * @category Internal
20
+ */
21
+ export type CssVars<
22
+ ElementTagName extends CustomElementTagName,
23
+ CssVarKeys extends BaseStringName<ElementTagName>,
24
+ > = CssVarDefinitions<CssVarsInitMap<ElementTagName, CssVarKeys>>;