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
@@ -1,52 +1,86 @@
1
- import { CallbackObservable } from 'observavir';
1
+ import {type Overwrite} from '@augment-vir/common';
2
+ import {CallbackObservable, type AsyncValue, type CallbackObservableInit} from 'observavir';
3
+
4
+ export type {AsyncValue} from 'observavir';
5
+
2
6
  /**
3
7
  * Class for constructing async props. Do not use this directly as its internal types won't be
4
8
  * inferred correctly. Instead use {@link asyncProp} an async prop or {@link AsyncProp} for types.
5
9
  *
6
10
  * @category Internal
7
11
  */
8
- export class InternalAsyncPropClass extends CallbackObservable {
12
+ export class InternalAsyncPropClass<Value, Params> extends CallbackObservable<Value, Params> {
9
13
  /**
10
14
  * Checks if the current `.value` has resolved (meaning the Promise has settled and it was not
11
15
  * rejected). This type guards the current instance's `.value` property.
12
16
  */
13
- isResolved() {
17
+ public isResolved(): this is Overwrite<
18
+ this,
19
+ {value: Exclude<AsyncValue<Value>, Promise<any> | Error>}
20
+ > {
14
21
  return !(this.value instanceof Promise);
15
22
  }
23
+
16
24
  /**
17
25
  * Checks if the current `.value` has settled (meaning it is either a rejection error or a
18
26
  * resolved value). This type guards the current instance's `.value` property.
19
27
  */
20
- isSettled() {
28
+ public isSettled(): this is Overwrite<this, {value: Exclude<AsyncValue<Value>, Promise<any>>}> {
21
29
  return !(this.value instanceof Promise);
22
30
  }
31
+
23
32
  /**
24
33
  * Checks if the current `.value` has not settled yet settled (meaning it is still an unsettled
25
34
  * Promise). This type guards the current instance's `.value` property.
26
35
  */
27
- isWaiting() {
36
+ public isWaiting(): this is Overwrite<this, {value: Extract<AsyncValue<Value>, Promise<any>>}> {
28
37
  return this.value instanceof Promise;
29
38
  }
39
+
30
40
  /**
31
41
  * Checks if the current `.value` is a rejection error. This type guards the current instance's
32
42
  * `.value` property.
33
43
  */
34
- isError() {
44
+ public isError(): this is Overwrite<this, {value: Extract<AsyncValue<Value>, Error>}> {
35
45
  return this.value instanceof Error;
36
46
  }
47
+
37
48
  /**
38
49
  * Checks if the current `.value` is resolved (and not an error) or still waiting. This type
39
50
  * guards the current instance's `.value` property.
40
51
  */
41
- isNotError() {
52
+ public isNotError(): this is Overwrite<this, {value: Exclude<AsyncValue<Value>, Error>}> {
42
53
  return !(this.value instanceof Error);
43
54
  }
44
55
  }
56
+
57
+ /**
58
+ * An async property created by {@link asyncProp} for use within declarative elements. Do not use
59
+ * this directly as its internal types won't be inferred correctly.
60
+ *
61
+ * @category Internal
62
+ */
63
+ export type AsyncProp<Value, Params> = Omit<
64
+ InternalAsyncPropClass<Value, Params>,
65
+ /** Hide these properties to make the `AsyncProp` interface much simpler. */
66
+ | 'dispatch'
67
+ | 'equalityCheck'
68
+ | 'getListenerCount'
69
+ | 'updateCallback'
70
+ | 'removeListener'
71
+ | 'removeAllListeners'
72
+ | 'listenToEvent'
73
+ | 'listen'
74
+ | 'resolvedValue'
75
+ >;
76
+
45
77
  /**
46
78
  * Create an async prop for a declarative element's state.
47
79
  *
48
80
  * @category Async
49
81
  */
50
- export function asyncProp(init) {
82
+ export function asyncProp<Value, Params = void>(
83
+ init?: CallbackObservableInit<Value, Params>,
84
+ ): AsyncProp<Value, Params> {
51
85
  return new InternalAsyncPropClass(init);
52
86
  }
@@ -0,0 +1,63 @@
1
+ import {getObjectTypedKeys, getOrSet} from '@augment-vir/common';
2
+ import {type Primitive} from 'type-fest';
3
+ import {nothing} from '../../lit-exports/all-lit-exports.js';
4
+ import {createMutateDirective} from './mutate.directive.js';
5
+
6
+ /**
7
+ * Possible attribute values for {@link AttributeValues}.
8
+ *
9
+ * @category Internal
10
+ */
11
+ export type AttributeValue = Exclude<Primitive, symbol> | string | typeof nothing;
12
+
13
+ /**
14
+ * Parameters object for applying attributes to an HTML element via the {@link attributes} directive.
15
+ * Make sure that all keys (attribute names) are lowercase.
16
+ *
17
+ * @category Internal
18
+ */
19
+ export type AttributeValues = {[LowercaseKey in Lowercase<string>]: AttributeValue};
20
+
21
+ /**
22
+ * A directive applies multiple HTML attributes to the parent element all at once.
23
+ *
24
+ * @category Directives
25
+ */
26
+ export const attributes = createMutateDirective<[AttributeValues | undefined]>(
27
+ 'attributes',
28
+ ({element, params: [attributesToApply], directive: rawDirective}) => {
29
+ if (!attributesToApply) {
30
+ return;
31
+ }
32
+
33
+ type DirectiveWithAttributesList = typeof rawDirective & {
34
+ allAttributesApplied?: Set<Lowercase<string>>;
35
+ };
36
+ const directive = rawDirective as DirectiveWithAttributesList;
37
+ const allAttributeNames = getOrSet(directive, 'allAttributesApplied', () => new Set());
38
+ getObjectTypedKeys(attributesToApply).forEach((attributeName) => {
39
+ if (attributeName.toLowerCase() !== attributeName) {
40
+ throw new Error(
41
+ `Cannot assign attribute name with uppercase letters: ${attributeName}`,
42
+ );
43
+ }
44
+ allAttributeNames.add(attributeName);
45
+ });
46
+
47
+ allAttributeNames.forEach((attributeName) => {
48
+ const attributeValue = attributesToApply[attributeName];
49
+
50
+ if (
51
+ attributeValue == undefined ||
52
+ attributeValue === false ||
53
+ attributeValue === nothing
54
+ ) {
55
+ element.removeAttribute(attributeName);
56
+ } else if (attributeValue === '' || attributeValue === true) {
57
+ element.setAttribute(attributeName, '');
58
+ } else {
59
+ element.setAttribute(attributeName, String(attributeValue));
60
+ }
61
+ });
62
+ },
63
+ );
@@ -0,0 +1,47 @@
1
+ import {directive, Directive, noChange, type PartInfo} from '../../lit-exports/all-lit-exports.js';
2
+ import {extractElement} from './directive-helpers.js';
3
+
4
+ /**
5
+ * Creates a lit directive that used simply for setting attributes on its parent element.
6
+ *
7
+ * @category Internal
8
+ */
9
+ export function createAttributeDirective(attributeName: string) {
10
+ const newDirective = directive(
11
+ /** @internal */
12
+ class extends Directive {
13
+ public readonly element: Element;
14
+
15
+ constructor(partInfo: PartInfo) {
16
+ super(partInfo);
17
+
18
+ this.element = extractElement(partInfo, attributeName);
19
+ }
20
+
21
+ public render(attributeValue: string) {
22
+ this.element.setAttribute(attributeName, attributeValue);
23
+ return noChange;
24
+ }
25
+ },
26
+ );
27
+
28
+ return {
29
+ /**
30
+ * Creates a string for use with the
31
+ * [`querySelector`](https://developer.mozilla.org/docs/Web/API/Document/querySelector) API
32
+ * that selects this directive's attribute set to the given `attributeValue`.
33
+ */
34
+ attributeSelector(this: void, attributeValue: string) {
35
+ return `[${attributeName}="${attributeValue}"]`;
36
+ },
37
+ /**
38
+ * Instantiates the attribute directive. This must be used on an element inside of an HTML
39
+ * template.
40
+ */
41
+ attributeDirective(this: void, attributeValue: string) {
42
+ return newDirective(attributeValue);
43
+ },
44
+ /** The name of the attribute used in the directive. */
45
+ attributeName,
46
+ };
47
+ }
@@ -0,0 +1,67 @@
1
+ import {
2
+ type ChildPart,
3
+ type ElementPartInfo,
4
+ type PartInfo,
5
+ PartType,
6
+ } from '../../lit-exports/all-lit-exports.js';
7
+
8
+ /**
9
+ * The full type for `ElementPartInfo` because `lit`'s built-in type leaves out of most of its
10
+ * interface.
11
+ *
12
+ * @category Internal
13
+ */
14
+ export type FullElementPartInfo =
15
+ ElementPartInfo /** For some reason these aren't defined in lit's types already, even though they _do_ exist. */ & {
16
+ element: Element;
17
+ options: {
18
+ host: Element;
19
+ renderBefore: Element;
20
+ isConnected: boolean;
21
+ };
22
+ };
23
+
24
+ /**
25
+ * Extracts the element from the given part info. Used in lit directives.
26
+ *
27
+ * @category Internal
28
+ */
29
+ export function extractElement(partInfo: PartInfo, directiveName: string): Element {
30
+ assertIsElementPartInfo(partInfo, directiveName);
31
+ const element = partInfo.element;
32
+ return element;
33
+ }
34
+
35
+ function getPartHostTagName(partInfo: PartInfo): string | undefined {
36
+ try {
37
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
38
+ const tagName = ((partInfo as ChildPart).options!.host as Element).tagName.toLowerCase();
39
+
40
+ return tagName;
41
+ } catch {
42
+ return undefined;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Asserts that the given part info is an instance of {@link FullElementPartInfo}.
48
+ *
49
+ * @category Internal
50
+ */
51
+ export function assertIsElementPartInfo(
52
+ partInfo: PartInfo,
53
+ directiveName: string,
54
+ ): asserts partInfo is FullElementPartInfo {
55
+ const hostTagName = getPartHostTagName(partInfo);
56
+ const hostTagMessage = hostTagName ? `: in ${hostTagName}` : '';
57
+
58
+ if (partInfo.type !== PartType.ELEMENT) {
59
+ throw new Error(
60
+ `${directiveName} directive can only be attached directly to an element${hostTagMessage}.`,
61
+ );
62
+ }
63
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
64
+ if (!(partInfo as FullElementPartInfo).element) {
65
+ throw new Error(`${directiveName} directive found no element${hostTagMessage}.`);
66
+ }
67
+ }
@@ -1,13 +1,16 @@
1
- import { listen } from './listen.directive.js';
1
+ import {type MaybePromise} from '@augment-vir/common';
2
+ import {listen} from './listen.directive.js';
3
+
2
4
  /**
3
5
  * Listens to enter, return, and space key hits on an element. Similar to {@link listenToEnter} but
4
6
  * includes space.
5
7
  *
6
8
  * @category Directives
7
9
  */
8
- export function listenToActivate(callback) {
10
+ export function listenToActivate(callback: () => MaybePromise<void>) {
9
11
  return listen('keydown', async (event) => {
10
12
  const key = event.code.toLowerCase();
13
+
11
14
  if (key.includes('enter') || key.includes('return') || key === 'space') {
12
15
  event.stopImmediatePropagation();
13
16
  event.preventDefault();
@@ -15,15 +18,17 @@ export function listenToActivate(callback) {
15
18
  }
16
19
  });
17
20
  }
21
+
18
22
  /**
19
23
  * Listens to enter and return key hits on an element. Similar to {@link listenToActivate} but
20
24
  * doesn't include space.
21
25
  *
22
26
  * @category Directives
23
27
  */
24
- export function listenToEnter(callback) {
28
+ export function listenToEnter(callback: () => MaybePromise<void>) {
25
29
  return listen('keydown', async (event) => {
26
30
  const key = event.code.toLowerCase();
31
+
27
32
  if (key.includes('enter') || key.includes('return')) {
28
33
  event.stopImmediatePropagation();
29
34
  event.preventDefault();
@@ -0,0 +1,206 @@
1
+ import {type MaybePromise} from '@augment-vir/common';
2
+ import {
3
+ directive,
4
+ Directive,
5
+ type DirectiveResult,
6
+ noChange,
7
+ type PartInfo,
8
+ } from '../../lit-exports/all-lit-exports.js';
9
+ import {
10
+ type DefinedTypedEvent,
11
+ defineTypedEvent,
12
+ type TypedEvent,
13
+ } from '../../typed-event/typed-event.js';
14
+ import {extractElement} from './directive-helpers.js';
15
+
16
+ /** We don't care at all what this returns, just allow anything! */
17
+ type ListenCallbackReturn = MaybePromise<any>;
18
+
19
+ type PossibleListenerCallbacks<
20
+ TypedEventTypeNameGeneric extends string,
21
+ TypedEventDetailGeneric,
22
+ NativeElementEventNameGeneric extends keyof HTMLElementEventMap,
23
+ > =
24
+ | ((
25
+ event: TypedEvent<TypedEventTypeNameGeneric, TypedEventDetailGeneric>,
26
+ ) => ListenCallbackReturn)
27
+ | ((event: HTMLElementEventMap[NativeElementEventNameGeneric]) => ListenCallbackReturn);
28
+
29
+ /**
30
+ * Listen to events. These can be native DOM events (use a string for the inputType argument) or
31
+ * typed events (pass in a return value from {@link defineTypedEvent}).
32
+ *
33
+ * @category Directives
34
+ * @example
35
+ *
36
+ * ```ts
37
+ * import {html, defineElement, listen} from 'element-vir';
38
+ *
39
+ * const MyElement = defineElement()({
40
+ * tagName: 'my-element',
41
+ * render() {
42
+ * return html`
43
+ * <div
44
+ * ${listen('click', () => {
45
+ * console.log('clicked!');
46
+ * })}
47
+ * >
48
+ * Some div
49
+ * </div>
50
+ * <${MyOtherElement}
51
+ * ${listen(MyOtherElement.events.someEvent, (event) => {
52
+ * console.log('event value', event.detail);
53
+ * })}
54
+ * ></${MyOtherElement}>
55
+ * `;
56
+ * },
57
+ * });
58
+ * ```
59
+ */
60
+ export function listen<TypedEventTypeNameGeneric extends string, TypedEventDetailGeneric>(
61
+ /**
62
+ * Needs to come either from a declarative element (like MyDeclarativeElement.events.eventName),
63
+ * from a typed event created via the {@link defineTypedEvent} function, or be the name of a
64
+ * built-in event (like `'click'`).
65
+ */
66
+ eventType: DefinedTypedEvent<TypedEventTypeNameGeneric, TypedEventDetailGeneric>,
67
+ /**
68
+ * The callback to fire when an event is caught. Assuming the {@link defineTypedEvent} input is
69
+ * properly typed, the event given to this callback will also be typed.
70
+ */
71
+ listener: (
72
+ event: TypedEvent<TypedEventTypeNameGeneric, TypedEventDetailGeneric>,
73
+ ) => ListenCallbackReturn,
74
+ ): DirectiveResult<any>;
75
+ /**
76
+ * Listen to events. These can be native DOM events (use a string for the inputType argument) or
77
+ * typed events (pass in a return value from {@link defineTypedEvent}).
78
+ *
79
+ * @category Directives
80
+ * @example
81
+ *
82
+ * ```ts
83
+ * import {html, defineElement, listen} from 'element-vir';
84
+ *
85
+ * const MyElement = defineElement()({
86
+ * tagName: 'my-element',
87
+ * render() {
88
+ * return html`
89
+ * <div
90
+ * ${listen('click', () => {
91
+ * console.log('clicked!');
92
+ * })}
93
+ * >
94
+ * Some div
95
+ * </div>
96
+ * <${MyOtherElement}
97
+ * ${listen(MyOtherElement.events.someEvent, (event) => {
98
+ * console.log('event value', event.detail);
99
+ * })}
100
+ * ></${MyOtherElement}>
101
+ * `;
102
+ * },
103
+ * });
104
+ * ```
105
+ */
106
+ export function listen<NativeElementEventNameGeneric extends keyof HTMLElementEventMap>(
107
+ /**
108
+ * Needs to come either from a declarative element (like MyDeclarativeElement.events.eventName),
109
+ * from a typed event created via the {@link defineTypedEvent} function, or be the name of a
110
+ * built-in event (like `'click'`).
111
+ */
112
+ eventType: NativeElementEventNameGeneric,
113
+ /**
114
+ * The callback to fire when an event is caught. Assuming the {@link defineTypedEvent} input is
115
+ * properly typed, the event given to this callback will also be typed.
116
+ */
117
+ listener: (event: HTMLElementEventMap[NativeElementEventNameGeneric]) => ListenCallbackReturn,
118
+ ): DirectiveResult<any>;
119
+ export function listen<
120
+ TypedEventTypeNameGeneric extends string,
121
+ TypedEventDetailGeneric,
122
+ NativeElementEventNameGeneric extends keyof HTMLElementEventMap,
123
+ >(
124
+ eventType:
125
+ | DefinedTypedEvent<TypedEventTypeNameGeneric, TypedEventDetailGeneric>
126
+ | NativeElementEventNameGeneric,
127
+ listener: PossibleListenerCallbacks<
128
+ TypedEventTypeNameGeneric,
129
+ TypedEventDetailGeneric,
130
+ NativeElementEventNameGeneric
131
+ >,
132
+ ): DirectiveResult<any> {
133
+ return listenDirective(eventType, listener);
134
+ }
135
+
136
+ type ListenerMetaData = {
137
+ eventType: string;
138
+ callback: PossibleListenerCallbacks<any, any, any>;
139
+ listener: (event: any) => ListenCallbackReturn;
140
+ };
141
+
142
+ /**
143
+ * The directive generics here are not strong enough to maintain their values. Thus, the directive
144
+ * call is wrapped in the function above.
145
+ */
146
+ const listenDirective = directive(
147
+ class extends Directive {
148
+ public readonly element: Element;
149
+ public lastListenerMetaData: ListenerMetaData | undefined;
150
+
151
+ constructor(partInfo: PartInfo) {
152
+ super(partInfo);
153
+
154
+ this.element = extractElement(partInfo, 'listen');
155
+ }
156
+
157
+ public resetListener(listenerMetaData: ListenerMetaData) {
158
+ if (this.lastListenerMetaData) {
159
+ this.element.removeEventListener(
160
+ this.lastListenerMetaData.eventType,
161
+ this.lastListenerMetaData.listener,
162
+ );
163
+ }
164
+ this.element.addEventListener(listenerMetaData.eventType, listenerMetaData.listener);
165
+ this.lastListenerMetaData = listenerMetaData;
166
+ }
167
+
168
+ public createListenerMetaData(
169
+ eventType: string,
170
+ callback: (event: TypedEvent<string, unknown>) => ListenCallbackReturn,
171
+ ): ListenerMetaData {
172
+ return {
173
+ eventType,
174
+ callback,
175
+ listener: (event: TypedEvent<string, unknown>) =>
176
+ this.lastListenerMetaData?.callback(event),
177
+ };
178
+ }
179
+
180
+ public render(
181
+ eventTypeInput: {type: string} | string,
182
+ callback: PossibleListenerCallbacks<any, any, any>,
183
+ ) {
184
+ const eventType =
185
+ typeof eventTypeInput === 'string' ? eventTypeInput : eventTypeInput.type;
186
+
187
+ if (typeof eventType !== 'string') {
188
+ throw new TypeError(
189
+ `Cannot listen to an event with a name that is not a string. Given event name: '${String(eventType)}'`,
190
+ );
191
+ }
192
+
193
+ if (this.lastListenerMetaData && this.lastListenerMetaData.eventType === eventType) {
194
+ /**
195
+ * Store the callback here so we don't have to update the attached listener every
196
+ * time the callback is updated.
197
+ */
198
+ this.lastListenerMetaData.callback = callback;
199
+ } else {
200
+ this.resetListener(this.createListenerMetaData(eventType, callback));
201
+ }
202
+
203
+ return noChange;
204
+ }
205
+ },
206
+ );
@@ -0,0 +1,78 @@
1
+ import {assertWrap} from '@augment-vir/assert';
2
+ import {directive, Directive, noChange, type PartInfo} from '../../lit-exports/all-lit-exports.js';
3
+ import {extractElement} from './directive-helpers.js';
4
+
5
+ /**
6
+ * Parameters for the callback given to the {@link mutate} directive.
7
+ *
8
+ * @category Internal
9
+ */
10
+ export type MutateDirectiveParams<Params extends any[] = []> = {
11
+ directive: Directive;
12
+ element: HTMLElement;
13
+ params: Params;
14
+ };
15
+
16
+ /**
17
+ * A directive that allows arbitrary modifications to be made to the element that it's attached to.
18
+ *
19
+ * @category Directives
20
+ */
21
+ export const mutate = directive(
22
+ class extends Directive {
23
+ public readonly element: HTMLElement;
24
+ public lastKey: string | undefined;
25
+
26
+ constructor(partInfo: PartInfo) {
27
+ super(partInfo);
28
+
29
+ this.element = assertWrap.instanceOf(
30
+ extractElement(partInfo, 'modifyElement'),
31
+ HTMLElement,
32
+ );
33
+ }
34
+
35
+ public render(callback: (params: Omit<MutateDirectiveParams, 'params'>) => void) {
36
+ callback({
37
+ directive: this,
38
+ element: this.element,
39
+ });
40
+ return noChange;
41
+ }
42
+ },
43
+ );
44
+
45
+ /**
46
+ * A helper for making new directives.
47
+ *
48
+ * @category Internal
49
+ */
50
+ export function createMutateDirective<Params extends any[]>(
51
+ directiveName: string,
52
+ callback: (this: void, params: MutateDirectiveParams<Params>) => void,
53
+ ) {
54
+ return directive(
55
+ class extends Directive {
56
+ public readonly element: HTMLElement;
57
+
58
+ constructor(partInfo: PartInfo) {
59
+ super(partInfo);
60
+
61
+ this.element = assertWrap.instanceOf(
62
+ extractElement(partInfo, directiveName),
63
+ HTMLElement,
64
+ );
65
+ }
66
+
67
+ public render(...params: Params) {
68
+ callback({
69
+ params,
70
+ directive: this,
71
+ element: this.element,
72
+ });
73
+
74
+ return noChange;
75
+ }
76
+ },
77
+ );
78
+ }
@@ -0,0 +1,68 @@
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 onDomCreated}. 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 OnDomCreatedCallback = (element: Element) => MaybePromise<void>;
12
+
13
+ const directiveName = 'onDomCreated';
14
+
15
+ /**
16
+ * A directive that fires its listener only once, when the element that it's attached to is
17
+ * constructed. This is particularly useful for getting references to internal elements immediately
18
+ * after they've rendered.
19
+ *
20
+ * @category Directives
21
+ * @example
22
+ *
23
+ * ```ts
24
+ * import {html, defineElement, onDomCreated} from 'element-vir';
25
+ *
26
+ * const MyElement = defineElement()({
27
+ * tagName: 'my-element',
28
+ * render() {
29
+ * return html`
30
+ * <div
31
+ * ${onDomCreated((element) => {
32
+ * console.log('created!', element);
33
+ * })}
34
+ * >
35
+ * Some div
36
+ * </div>
37
+ * `;
38
+ * },
39
+ * });
40
+ * ```
41
+ */
42
+ export const onDomCreated = directive(
43
+ class extends Directive {
44
+ public element: Element | undefined;
45
+
46
+ constructor(partInfo: PartInfo) {
47
+ super(partInfo);
48
+
49
+ assertIsElementPartInfo(partInfo, directiveName);
50
+ }
51
+
52
+ public override update(partInfo: PartInfo, [callback]: [OnDomCreatedCallback]) {
53
+ assertIsElementPartInfo(partInfo, directiveName);
54
+ const newElement = partInfo.element;
55
+ if (newElement !== this.element) {
56
+ // use requestAnimationFrame here so it can fire property changes outside of a render loop
57
+ window.requestAnimationFrame(() => callback(newElement));
58
+ this.element = newElement;
59
+ }
60
+ return this.render(callback);
61
+ }
62
+
63
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
64
+ public render(callback: OnDomCreatedCallback) {
65
+ return undefined;
66
+ }
67
+ },
68
+ );