element-vir 5.6.0 → 6.0.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 (74) hide show
  1. package/README.md +97 -93
  2. package/dist/augments/testing.d.ts +15 -0
  3. package/dist/augments/testing.js +76 -0
  4. package/dist/augments/type.d.ts +7 -0
  5. package/dist/augments/type.js +13 -1
  6. package/dist/{functional-element → declarative-element}/css-vars.d.ts +0 -0
  7. package/dist/{functional-element → declarative-element}/css-vars.js +0 -0
  8. package/dist/declarative-element/declarative-element-init.d.ts +33 -0
  9. package/dist/declarative-element/declarative-element-init.js +1 -0
  10. package/dist/declarative-element/declarative-element.d.ts +44 -0
  11. package/dist/declarative-element/declarative-element.js +18 -0
  12. package/dist/declarative-element/define-element-no-inputs.d.ts +5 -0
  13. package/dist/declarative-element/define-element-no-inputs.js +118 -0
  14. package/dist/declarative-element/define-element.d.ts +5 -0
  15. package/dist/declarative-element/define-element.js +13 -0
  16. package/dist/declarative-element/definition-options.d.ts +5 -0
  17. package/dist/declarative-element/definition-options.js +4 -0
  18. package/dist/declarative-element/directives/assign-with-clean-up.directive.d.ts +23 -0
  19. package/dist/declarative-element/directives/assign-with-clean-up.directive.js +43 -0
  20. package/dist/declarative-element/directives/assign.directive.d.ts +12 -0
  21. package/dist/declarative-element/directives/assign.directive.js +35 -0
  22. package/dist/{functional-element/directives/directive-util.d.ts → declarative-element/directives/directive-helpers.d.ts} +2 -3
  23. package/dist/{functional-element/directives/directive-util.js → declarative-element/directives/directive-helpers.js} +4 -4
  24. package/dist/{functional-element → declarative-element}/directives/listen.directive.d.ts +2 -2
  25. package/dist/{functional-element → declarative-element}/directives/listen.directive.js +1 -1
  26. package/dist/{functional-element → declarative-element}/directives/on-dom-created.directive.d.ts +0 -0
  27. package/dist/{functional-element → declarative-element}/directives/on-dom-created.directive.js +1 -1
  28. package/dist/{functional-element → declarative-element}/directives/on-resize.directive.d.ts +0 -0
  29. package/dist/{functional-element → declarative-element}/directives/on-resize.directive.js +1 -1
  30. package/dist/{functional-element → declarative-element}/element-events.d.ts +0 -0
  31. package/dist/{functional-element → declarative-element}/element-events.js +1 -1
  32. package/dist/{functional-element → declarative-element}/element-properties.d.ts +3 -3
  33. package/dist/declarative-element/element-properties.js +53 -0
  34. package/dist/declarative-element/has-declarative-element-parent.d.ts +1 -0
  35. package/dist/declarative-element/has-declarative-element-parent.js +16 -0
  36. package/dist/declarative-element/host-classes.d.ts +21 -0
  37. package/dist/{functional-element → declarative-element}/host-classes.js +0 -0
  38. package/dist/declarative-element/render-callback.d.ts +22 -0
  39. package/dist/{functional-element → declarative-element}/render-callback.js +4 -3
  40. package/dist/{functional-element → declarative-element}/styles.d.ts +7 -1
  41. package/dist/{functional-element → declarative-element}/styles.js +2 -2
  42. package/dist/{functional-element → declarative-element}/tag-name.d.ts +0 -0
  43. package/dist/{functional-element → declarative-element}/tag-name.js +0 -0
  44. package/dist/declarative-element-marker-symbol.d.ts +1 -0
  45. package/dist/declarative-element-marker-symbol.js +1 -0
  46. package/dist/index.d.ts +14 -12
  47. package/dist/index.js +13 -12
  48. package/dist/require-declarative-element.d.ts +2 -0
  49. package/dist/require-declarative-element.js +4 -0
  50. package/dist/template-transforms/nested-mapped-templates.d.ts +6 -0
  51. package/dist/template-transforms/nested-mapped-templates.js +96 -0
  52. package/dist/template-transforms/transform-template.js +21 -2
  53. package/dist/template-transforms/vir-css/css-transform.d.ts +2 -2
  54. package/dist/template-transforms/vir-css/vir-css.d.ts +2 -2
  55. package/dist/template-transforms/vir-css/vir-css.js +1 -1
  56. package/dist/template-transforms/vir-html/html-transform.js +3 -3
  57. package/dist/template-transforms/vir-html/vir-html.d.ts +1 -1
  58. package/dist/template-transforms/vir-html/vir-html.js +1 -1
  59. package/index.html +14 -0
  60. package/package.json +14 -14
  61. package/public/index.css +7 -0
  62. package/dist/functional-element/define-functional-element.d.ts +0 -4
  63. package/dist/functional-element/define-functional-element.js +0 -70
  64. package/dist/functional-element/directives/assign-with-clean-up.directive.d.ts +0 -19
  65. package/dist/functional-element/directives/assign-with-clean-up.directive.js +0 -36
  66. package/dist/functional-element/directives/assign.directive.d.ts +0 -15
  67. package/dist/functional-element/directives/assign.directive.js +0 -23
  68. package/dist/functional-element/element-properties.js +0 -50
  69. package/dist/functional-element/functional-element.d.ts +0 -57
  70. package/dist/functional-element/functional-element.js +0 -3
  71. package/dist/functional-element/host-classes.d.ts +0 -19
  72. package/dist/functional-element/render-callback.d.ts +0 -21
  73. package/dist/require-functional-element.d.ts +0 -2
  74. package/dist/require-functional-element.js +0 -4
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # element-vir
2
2
 
3
- Heroic. Reactive. Functional. Type safe. Web components without compromise.
3
+ Heroic. Reactive. Declarative. Type safe. Web components without compromise.
4
4
 
5
5
  No need for an extra build step,<br>
6
6
  no need for side effect imports, <br>
@@ -11,7 +11,7 @@ _**It's just TypeScript.**_
11
11
 
12
12
  Uses the power of _native_ JavaScript custom web elements, _native_ JavaScript template literals, _native_ JavaScript functions<sup>\*</sup>, _native_ HTML, and [lit-element](http://lit.dev).
13
13
 
14
- In reality this is basically a [lit-element](http://lit.dev) wrapper that adds type-safe element tag usage and I/O with functional-programming style component definition. (As functional as possible, element property mutations are still possible cause without that, how would you do anything?)
14
+ In reality this is basically a [lit-element](http://lit.dev) wrapper that adds type-safe element tag usage and I/O with declarative style component definition.
15
15
 
16
16
  [Works in every major web browser except Internet Explorer.](https://caniuse.com/mdn-api_window_customelements)
17
17
 
@@ -29,20 +29,20 @@ Make sure to install this as a normal dependency (not just a dev dependency) bec
29
29
 
30
30
  # Usage
31
31
 
32
- Most usage of this package is done through the [`defineFunctionalElement` function](https://github.com/electrovir/element-vir/blob/main/src/functional-element/define-functional-element.ts#L25-L30). See the [`FunctionalElementInit` type](https://github.com/electrovir/element-vir/blob/main/src/functional-element/functional-element-init.ts#L7-L20) for that function's inputs. These inputs are also described below with examples.
32
+ Most usage of this package is done through the [`defineElementNoInputs` function](https://github.com/electrovir/element-vir/blob/main/src/declarative-element/define-declarative-element.ts#L25-L30). See the [`DeclarativeElementInit` type](https://github.com/electrovir/element-vir/blob/main/src/declarative-element/declarative-element-init.ts#L7-L20) for that function's inputs. These inputs are also described below with examples.
33
33
 
34
34
  All of [`lit`](https://lit.dev)'s syntax and functionality is also available for use if you wish.
35
35
 
36
36
  ## Simple element definition
37
37
 
38
- Use `defineFunctionalElement` to define your element. Tt must be given an object with at least `tagName` and `renderCallback` properties (the types enforce this). Here is a bare-minimum example custom element:
38
+ Use `defineElementNoInputs` to define your element if you're not setting inputs (or just for now as you're getting started). It must be given an object with at least `tagName` and `renderCallback` properties (the types enforce this). Here is a bare-minimum example custom element:
39
39
 
40
40
  <!-- example-link: src/readme-examples/my-simple.element.ts -->
41
41
 
42
42
  ```TypeScript
43
- import {defineFunctionalElement, html} from 'element-vir';
43
+ import {defineElementNoInputs, html} from 'element-vir';
44
44
 
45
- export const MySimpleElement = defineFunctionalElement({
45
+ export const MySimpleElement = defineElementNoInputs({
46
46
  tagName: 'my-simple-element',
47
47
  renderCallback: () => html`
48
48
  <span>Hello there!</span>
@@ -54,15 +54,15 @@ Make sure to export your element definition if you need to use it in other files
54
54
 
55
55
  ## Using in other elements
56
56
 
57
- To use already defined functional elements (like `my-simple-element` above), they must be interpolated into HTML templates like so:
57
+ To use already defined elements (like `my-simple-element` above), they must be interpolated into HTML templates like so:
58
58
 
59
59
  <!-- example-link: src/readme-examples/my-app.element.ts -->
60
60
 
61
61
  ```TypeScript
62
- import {defineFunctionalElement, html} from 'element-vir';
62
+ import {defineElementNoInputs, html} from 'element-vir';
63
63
  import {MySimpleElement} from './my-simple.element';
64
64
 
65
- export const MyAppElement = defineFunctionalElement({
65
+ export const MyAppElement = defineElementNoInputs({
66
66
  tagName: 'my-app-element',
67
67
  renderCallback: () => html`
68
68
  <h1>My App</h1>
@@ -77,14 +77,14 @@ If you wish to bypass this interpolation, make sure to [import the `html` tagged
77
77
 
78
78
  ## Adding styles
79
79
 
80
- Styles are added through the `styles` property when defining a functional element (similar to [how they are defined in `lit`](https://lit.dev/docs/components/styles/)):
80
+ Styles are added through the `styles` property when defining a declarative element (similar to [how they are defined in `lit`](https://lit.dev/docs/components/styles/)):
81
81
 
82
82
  <!-- example-link: src/readme-examples/my-simple-app-with-styles.element.ts -->
83
83
 
84
84
  ```TypeScript
85
- import {css, defineFunctionalElement, html} from 'element-vir';
85
+ import {css, defineElementNoInputs, html} from 'element-vir';
86
86
 
87
- export const MySimpleWithStylesElement = defineFunctionalElement({
87
+ export const MySimpleWithStylesElement = defineElementNoInputs({
88
88
  tagName: 'my-simple-with-styles-element',
89
89
  styles: css`
90
90
  :host {
@@ -106,15 +106,15 @@ export const MySimpleWithStylesElement = defineFunctionalElement({
106
106
 
107
107
  ### Element definition as style selector
108
108
 
109
- Functional element definitions can be used in the `css` tagged template just like in the `html` tagged template. This will be replaced by the element's tag name:
109
+ Declarative element definitions can be used in the `css` tagged template just like in the `html` tagged template. This will be replaced by the element's tag name:
110
110
 
111
111
  <!-- example-link: src/readme-examples/my-simple-app-with-styles-and-interpolated-selector.element.ts -->
112
112
 
113
113
  ```TypeScript
114
- import {css, defineFunctionalElement, html} from 'element-vir';
114
+ import {css, defineElementNoInputs, html} from 'element-vir';
115
115
  import {MySimpleElement} from './my-simple.element';
116
116
 
117
- export const MySimpleWithStylesAndInterpolatedSelectorElement = defineFunctionalElement({
117
+ export const MySimpleWithStylesAndInterpolatedSelectorElement = defineElementNoInputs({
118
118
  tagName: 'my-simple-with-styles-and-interpolated-selector-element',
119
119
  styles: css`
120
120
  ${MySimpleElement} {
@@ -127,51 +127,50 @@ export const MySimpleWithStylesAndInterpolatedSelectorElement = defineFunctional
127
127
  });
128
128
  ```
129
129
 
130
- ## Defining and using properties (inputs)
130
+ ## Defining and using Inputs
131
131
 
132
- Define element properties with `props` when making a functional element. Each property must be given a default value. If you wish to leave the property's default value as `undefined`, give it a type as well (shown below with `as string | undefined`) so you can assign a defined value of that type to it later.
132
+ Define element inputs by using `defineElement` to define a declarative element. Pass your input type as a generic to the `defineElement` call. Then call _that_ with the normal definition input (like when using `defineElementNoInputs`).
133
133
 
134
- To use a custom element's properties, grab `props` from `renderCallback`'s parameters and interpolate it into your HTML template:
134
+ To use an element's inputs for use in its template, grab `inputs` from `renderCallback`'s parameters and interpolate it into your HTML template:
135
135
 
136
- <!-- example-link: src/readme-examples/my-simple-with-props.element.ts -->
136
+ <!-- example-link: src/readme-examples/my-simple-with-inputs.element.ts -->
137
137
 
138
138
  ```TypeScript
139
- import {defineFunctionalElement, html} from 'element-vir';
140
-
141
- export const MySimpleWithPropsElement = defineFunctionalElement({
142
- tagName: 'my-simple-element-with-props',
143
- props: {
144
- currentUsername: 'dev',
145
- currentEmail: undefined as string | undefined,
146
- },
147
- renderCallback: ({props}) => html`
148
- <span>Hello there ${props.currentUsername}!</span>
139
+ import {defineElement, html} from 'element-vir';
140
+
141
+ export const MySimpleWithInputsElement = defineElement<{
142
+ username: string;
143
+ email: string;
144
+ }>()({
145
+ tagName: 'my-simple-element-with-inputs',
146
+ renderCallback: ({inputs}) => html`
147
+ <span>Hello there ${inputs.username}!</span>
149
148
  `,
150
149
  });
151
150
  ```
152
151
 
153
- ### Updating properties
152
+ ## Defining internal state
154
153
 
155
- Grab `setProps` from `renderCallback`'s parameters to update the values in `props`. This causes a re-render. (Note that this example also uses the `listen` directive to respond to click events.)
154
+ Define internal state with the `stateInit` property when defining an element. Grab it with `state` in `renderCallback` to use state. Grab `updateState` in `renderCallback` to update state:
156
155
 
157
- <!-- example-link: src/readme-examples/my-simple-with-set-props.element.ts -->
156
+ <!-- example-link: src/readme-examples/my-simple-with-update-state.element.ts -->
158
157
 
159
158
  ```TypeScript
160
- import {defineFunctionalElement, html, listen} from 'element-vir';
159
+ import {defineElementNoInputs, html, listen} from 'element-vir';
161
160
 
162
- export const MySimpleWithPropsElement = defineFunctionalElement({
163
- tagName: 'my-simple-element-with-props',
164
- props: {
165
- currentUsername: 'dev',
166
- currentEmail: undefined as string | undefined,
161
+ export const MySimpleWithUpdateStateElement = defineElementNoInputs({
162
+ tagName: 'my-simple-element-with-update-state',
163
+ stateInit: {
164
+ username: 'dev',
165
+ email: undefined as string | undefined,
167
166
  },
168
- renderCallback: ({props, setProps}) => html`
167
+ renderCallback: ({state, updateState}) => html`
169
168
  <span
170
169
  ${listen('click', () => {
171
- setProps({currentUsername: 'new name!'});
170
+ updateState({username: 'new name!'});
172
171
  })}
173
172
  >
174
- Hello there ${props.currentUsername}!
173
+ Hello there ${state.username}!
175
174
  </span>
176
175
  `,
177
176
  });
@@ -181,37 +180,39 @@ export const MySimpleWithPropsElement = defineFunctionalElement({
181
180
 
182
181
  Use the `assign` directive to assign properties to child custom elements:
183
182
 
184
- <!-- example-link: src/readme-examples/my-app-with-props.element.ts -->
183
+ <!-- example-link: src/readme-examples/my-app-with-assignment.element.ts -->
185
184
 
186
185
  ```TypeScript
187
- import {assign, defineFunctionalElement, html} from 'element-vir';
188
- import {MySimpleWithPropsElement} from './my-simple-with-props.element';
186
+ import {assign, defineElementNoInputs, html} from 'element-vir';
187
+ import {MySimpleWithInputsElement} from './my-simple-with-inputs.element';
189
188
 
190
- export const MyAppWithPropsElement = defineFunctionalElement({
191
- tagName: 'my-app-with-props-element',
189
+ export const MyAppWithAssignmentElement = defineElementNoInputs({
190
+ tagName: 'my-app-with-assignment-element',
192
191
  renderCallback: () => html`
193
192
  <h1>My App</h1>
194
- <${MySimpleWithPropsElement}
195
- ${assign(MySimpleWithPropsElement.props.currentUsername, 'user')}
196
- ${assign(MySimpleWithPropsElement.props.currentEmail, 'user@example.com')}
193
+ <${MySimpleWithInputsElement}
194
+ ${assign(MySimpleWithInputsElement, {
195
+ email: 'user@example.com',
196
+ username: 'user',
197
+ })}
197
198
  >
198
- </${MySimpleWithPropsElement}>
199
+ </${MySimpleWithInputsElement}>
199
200
  `,
200
201
  });
201
202
  ```
202
203
 
203
204
  ## Element events (outputs)
204
205
 
205
- Define events with `events` when making a functional element. Each event must be initialized with `defineElementEvent` and a type parameter. `defineElementEvent` accepts no inputs as it doesn't make sense for events to have default values.
206
+ Define events with `events` when defining a declarative element. Each event must be initialized with `defineElementEvent` and a type parameter. `defineElementEvent` accepts no inputs as it doesn't make sense for events to have default values.
206
207
 
207
208
  To dispatch an event, grab `dispatch` from `renderCallback`'s parameters.
208
209
 
209
210
  <!-- example-link: src/readme-examples/my-simple-with-events.element.ts -->
210
211
 
211
212
  ```TypeScript
212
- import {defineElementEvent, defineFunctionalElement, html, listen} from 'element-vir';
213
+ import {defineElementEvent, defineElementNoInputs, html, listen} from 'element-vir';
213
214
 
214
- export const MySimpleWithEventsElement = defineFunctionalElement({
215
+ export const MySimpleWithEventsElement = defineElementNoInputs({
215
216
  tagName: 'my-simple-element-with-events',
216
217
  events: {
217
218
  logoutClick: defineElementEvent<void>(),
@@ -230,31 +231,31 @@ export const MySimpleWithEventsElement = defineFunctionalElement({
230
231
 
231
232
  ### Listening to typed events (outputs)
232
233
 
233
- Use the `listen` directive to listen to typed events emitted by your custom functional elements:
234
+ Use the `listen` directive to listen to typed events emitted by your custom elements:
234
235
 
235
236
  <!-- example-link: src/readme-examples/my-app-with-events.element.ts -->
236
237
 
237
238
  ```TypeScript
238
- import {defineFunctionalElement, html, listen} from 'element-vir';
239
+ import {defineElementNoInputs, html, listen} from 'element-vir';
239
240
  import {MySimpleWithEventsElement} from './my-simple-with-events.element';
240
241
 
241
- export const MyAppWithEventsElement = defineFunctionalElement({
242
+ export const MyAppWithEventsElement = defineElementNoInputs({
242
243
  tagName: 'my-app-with-events-element',
243
- props: {
244
+ stateInit: {
244
245
  myNumber: -1,
245
246
  },
246
- renderCallback: ({props, setProps}) => html`
247
+ renderCallback: ({state, updateState}) => html`
247
248
  <h1>My App</h1>
248
249
  <${MySimpleWithEventsElement}
249
250
  ${listen(MySimpleWithEventsElement.events.logoutClick, () => {
250
251
  console.info('logout triggered');
251
252
  })}
252
253
  ${listen(MySimpleWithEventsElement.events.randomNumber, (event) => {
253
- setProps({myNumber: event.detail});
254
+ updateState({myNumber: event.detail});
254
255
  })}
255
256
  >
256
257
  </${MySimpleWithEventsElement}>
257
- <span>${props.myNumber}</span>
258
+ <span>${state.myNumber}</span>
258
259
  `,
259
260
  });
260
261
  ```
@@ -282,15 +283,15 @@ Both dispatching a custom event and listening to a custom event:
282
283
  <!-- example-link: src/readme-examples/custom-event-usage.element.ts -->
283
284
 
284
285
  ```TypeScript
285
- import {defineFunctionalElement, html, listen} from 'element-vir';
286
+ import {defineElementNoInputs, html, listen} from 'element-vir';
286
287
  import {MyCustomEvent} from './custom-event-no-element';
287
288
 
288
- export const MyElementWithCustomEvents = defineFunctionalElement({
289
+ export const MyElementWithCustomEvents = defineElementNoInputs({
289
290
  tagName: 'my-app-with-custom-events',
290
291
  renderCallback: ({genericDispatch}) => html`
291
292
  <div
292
293
  ${listen(MyCustomEvent, (event) => {
293
- console.log(`Got a number! ${event.detail}`);
294
+ console.info(`Got a number! ${event.detail}`);
294
295
  })}
295
296
  >
296
297
  <div
@@ -314,11 +315,11 @@ Apply host classes in the element's stylesheet by using a callback for the style
314
315
  <!-- example-link: src/readme-examples/host-class-definition.ts -->
315
316
 
316
317
  ```TypeScript
317
- import {css, defineFunctionalElement, html} from 'element-vir';
318
+ import {css, defineElementNoInputs, html} from 'element-vir';
318
319
 
319
- export const MyAppWithHostClasses = defineFunctionalElement({
320
+ export const MyAppWithHostClasses = defineElementNoInputs({
320
321
  tagName: 'my-app-with-host-classes',
321
- props: {
322
+ stateInit: {
322
323
  myProp: 'hello there',
323
324
  },
324
325
  hostClasses: {
@@ -331,8 +332,8 @@ export const MyAppWithHostClasses = defineFunctionalElement({
331
332
  * This host class will be automatically applied if the given callback evaluated to true
332
333
  * after a call to renderCallback.
333
334
  */
334
- automaticallyAppliedVariation: ({props}) => {
335
- return props.myProp === 'foo';
335
+ automaticallyAppliedVariation: ({state}) => {
336
+ return state.myProp === 'foo';
336
337
  },
337
338
  },
338
339
  /**
@@ -348,8 +349,8 @@ export const MyAppWithHostClasses = defineFunctionalElement({
348
349
  color: red;
349
350
  }
350
351
  `,
351
- renderCallback: ({props}) => html`
352
- ${props.myProp}
352
+ renderCallback: ({state}) => html`
353
+ ${state.myProp}
353
354
  `,
354
355
  });
355
356
  ```
@@ -361,9 +362,9 @@ Typed CSS vars are created in a similar way as host classes:
361
362
  <!-- example-link: src/readme-examples/css-vars-definition.ts -->
362
363
 
363
364
  ```TypeScript
364
- import {css, defineFunctionalElement, html} from 'element-vir';
365
+ import {css, defineElementNoInputs, html} from 'element-vir';
365
366
 
366
- export const MyAppWithCssVars = defineFunctionalElement({
367
+ export const MyAppWithCssVars = defineElementNoInputs({
367
368
  tagName: 'my-app-with-css-vars',
368
369
  cssVars: {
369
370
  /**
@@ -397,15 +398,15 @@ This triggers only once when the element it's attached has actually been created
397
398
  <!-- example-link: src/readme-examples/my-simple-with-on-dom-created.element.ts -->
398
399
 
399
400
  ```TypeScript
400
- import {defineFunctionalElement, html, onDomCreated} from 'element-vir';
401
+ import {defineElementNoInputs, html, onDomCreated} from 'element-vir';
401
402
 
402
- export const MySimpleWithOnDomCreatedElement = defineFunctionalElement({
403
+ export const MySimpleWithOnDomCreatedElement = defineElementNoInputs({
403
404
  tagName: 'my-simple-with-on-dom-created-element',
404
405
  renderCallback: () => html`
405
406
  <span
406
407
  ${onDomCreated((element) => {
407
408
  // logs a span element
408
- console.log(element);
409
+ console.info(element);
409
410
  })}
410
411
  >
411
412
  Hello there!
@@ -421,16 +422,16 @@ This directive fulfills a common use case of triggering callbacks when something
421
422
  <!-- example-link: src/readme-examples/my-simple-with-on-resize.element.ts -->
422
423
 
423
424
  ```TypeScript
424
- import {defineFunctionalElement, html, onResize} from 'element-vir';
425
+ import {defineElementNoInputs, html, onResize} from 'element-vir';
425
426
 
426
- export const MySimpleWithOnResizeElement = defineFunctionalElement({
427
+ export const MySimpleWithOnResizeElement = defineElementNoInputs({
427
428
  tagName: 'my-simple-with-on-dom-created-element',
428
429
  renderCallback: () => html`
429
430
  <span
430
431
  ${onResize((entry) => {
431
432
  // this will track resizing of this span
432
433
  // the entry parameter contains target and contentRect properties
433
- console.log(entry);
434
+ console.info(entry);
434
435
  })}
435
436
  >
436
437
  Hello there!
@@ -454,40 +455,43 @@ This directive is the same as the `assign` directive but it accepts an additiona
454
455
  <!-- example-link: src/readme-examples/my-app-with-cleanup.element.ts -->
455
456
 
456
457
  ```TypeScript
457
- import {assign, assignWithCleanup, defineFunctionalElement, html} from 'element-vir';
458
- import {MySimpleWithPropsElement} from './my-simple-with-props.element';
458
+ import {assignWithCleanup, defineElementNoInputs, html} from 'element-vir';
459
+ import {MySimpleWithInputsElement} from './my-simple-with-inputs.element';
459
460
 
460
- export const MyAppWithPropsElement = defineFunctionalElement({
461
+ export const MyAppWithAssignmentCleanupElement = defineElementNoInputs({
461
462
  tagName: 'my-app-with-cleanup',
462
463
  renderCallback: () => html`
463
464
  <h1>My App</h1>
464
- <${MySimpleWithPropsElement}
465
+ <${MySimpleWithInputsElement}
465
466
  ${assignWithCleanup(
466
- MySimpleWithPropsElement.props.currentUsername,
467
- 'user',
467
+ MySimpleWithInputsElement,
468
+ {
469
+ email: 'user@example.com',
470
+ username: 'user',
471
+ },
468
472
  (previousValue) => {
469
473
  // here would be the cleanup code.
470
- // In this specific example the value is just a string, so no cleanup is needed.
471
- previousValue.trim();
474
+ // In this specific example the value is just a string, so no cleanup is needed
475
+ // and the following line isn't actually doing anything.
476
+ previousValue.username.trim();
472
477
  },
473
478
  )}
474
- ${assign(MySimpleWithPropsElement.props.currentEmail, 'user@example.com')}
475
479
  >
476
- </${MySimpleWithPropsElement}>
480
+ </${MySimpleWithInputsElement}>
477
481
  `,
478
482
  });
479
483
  ```
480
484
 
481
- ## Require all child custom elements to be functional elements
485
+ ## Require all child custom elements to be declarative elements
482
486
 
483
- To require all child elements to be functional elements defined by this package, call `requireAllCustomElementsToBeFunctionalElement` anywhere in your app. This is a global setting so do not enable it unless you want it to be true _everywhere_ in your current run-time. This should not be used if you're using custom elements from other libraries (unless they happen to also use this package to define their custom elements).
487
+ To require all child elements to be declarative elements defined by this package, call `requireAllCustomElementsToBeDeclarativeElements` anywhere in your app. This is a global setting so do not enable it unless you want it to be true _everywhere_ in your current run-time. This should not be used if you're using custom elements from other libraries (unless they happen to also use this package to define their custom elements).
484
488
 
485
- <!-- example-link: src/readme-examples/require-functional-element.ts -->
489
+ <!-- example-link: src/readme-examples/require-declarative-element.ts -->
486
490
 
487
491
  ```TypeScript
488
- import {requireAllCustomElementsToBeFunctionalElement} from 'element-vir';
492
+ import {requireAllCustomElementsToBeDeclarativeElements} from 'element-vir';
489
493
 
490
- requireAllCustomElementsToBeFunctionalElement();
494
+ requireAllCustomElementsToBeDeclarativeElements();
491
495
  ```
492
496
 
493
497
  # Dev
@@ -0,0 +1,15 @@
1
+ import { DeclarativeElementDefinition } from '../declarative-element/declarative-element';
2
+ /**
3
+ * Wrapper for assert.instanceOf that also works with TypeScript in setting the proper types.
4
+ *
5
+ * Do not use this in production code! It should only be used in testing code.
6
+ */
7
+ export declare function assertInstanceOf<T>(value: unknown, constructor: new (...args: any) => T, message?: string): asserts value is T;
8
+ export declare function isInstanceOf<T>(value: unknown, constructor: new (...args: any) => T): value is T;
9
+ export declare function getAssertedDeclarativeElement<DeclarativeElementGeneric extends DeclarativeElementDefinition>(searchFor: DeclarativeElementGeneric, searchIn: Element): DeclarativeElementGeneric['instanceType'];
10
+ export declare function testIdSelector(testId: string): string;
11
+ export declare function getCenterOfElement(element: Element): [number, number];
12
+ export declare function queryWithAssert<T extends Element>(query: string | [string, ...string[]], constructor: new (...args: any) => T, searchIn: Element | Document): T;
13
+ export declare function assertRejects(input: () => PromiseLike<any>, message?: string): Promise<void>;
14
+ export declare function assertRejects(input: () => PromiseLike<any>, errType: RegExp | ErrorConstructor, message?: string): Promise<void>;
15
+ export declare function assertRejects(input: () => PromiseLike<any>, errType: ErrorConstructor, regExp: RegExp): Promise<void>;
@@ -0,0 +1,76 @@
1
+ import { assert } from '@open-wc/testing';
2
+ /**
3
+ * Wrapper for assert.instanceOf that also works with TypeScript in setting the proper types.
4
+ *
5
+ * Do not use this in production code! It should only be used in testing code.
6
+ */
7
+ export function assertInstanceOf(value, constructor, message) {
8
+ assert.instanceOf(value, constructor, message);
9
+ }
10
+ export function isInstanceOf(value, constructor) {
11
+ return value instanceof constructor;
12
+ }
13
+ export function getAssertedDeclarativeElement(searchFor, searchIn) {
14
+ if (searchIn.tagName.toLowerCase() === searchFor.tagName.toLowerCase()) {
15
+ assertInstanceOf(searchIn, searchFor);
16
+ return searchIn;
17
+ }
18
+ const result = queryTree(searchIn, [searchFor.tagName]);
19
+ assertInstanceOf(result, searchFor);
20
+ assert.strictEqual(result.tagName, searchFor.tagName);
21
+ return result;
22
+ }
23
+ export function testIdSelector(testId) {
24
+ return `[data-test-id="${testId}"]`;
25
+ }
26
+ export function getCenterOfElement(element) {
27
+ const rect = element.getBoundingClientRect();
28
+ return [
29
+ Math.floor((rect.left + rect.right) / 2),
30
+ Math.floor((rect.bottom + rect.top) / 2),
31
+ ];
32
+ }
33
+ export function queryWithAssert(query, constructor, searchIn) {
34
+ if (!Array.isArray(query)) {
35
+ query = [query];
36
+ }
37
+ const result = queryTree(searchIn, query);
38
+ assertInstanceOf(result, constructor);
39
+ return result;
40
+ }
41
+ /** Accounts for shadow DOM */
42
+ function queryTree(context,
43
+ // at least one string is required or this function makes no sense
44
+ selectors) {
45
+ /**
46
+ * The callback is split out here to appease the Type Gods. Without it, finalElement will be the
47
+ * type of the internal currentContext (which is incorrect).
48
+ */
49
+ const reduceCallback = (currentContext, selector) => {
50
+ var _a;
51
+ if (!currentContext) {
52
+ return undefined;
53
+ }
54
+ if ('shadowRoot' in currentContext && currentContext.shadowRoot) {
55
+ currentContext = currentContext.shadowRoot;
56
+ }
57
+ return (_a = currentContext.querySelector(selector)) !== null && _a !== void 0 ? _a : undefined;
58
+ };
59
+ const finalElement = selectors.reduce(reduceCallback, context);
60
+ return finalElement;
61
+ }
62
+ export async function assertRejects(input, messageOrRegExpOrError, messageOrRegExp) {
63
+ let thrown = undefined;
64
+ let errorThrown = false;
65
+ try {
66
+ await input();
67
+ }
68
+ catch (error) {
69
+ errorThrown = true;
70
+ thrown = error;
71
+ }
72
+ assert.isTrue(errorThrown, 'No error was thrown.');
73
+ assert.throws(() => {
74
+ throw thrown;
75
+ }, messageOrRegExpOrError, messageOrRegExp);
76
+ }
@@ -1 +1,8 @@
1
1
  export declare type NonEmptyString<T> = T extends '' ? never : T;
2
+ /**
3
+ * If the given value is not an instance of the given constructor, an error is thrown.
4
+ *
5
+ * This is a variation of assertInstanceOf that can be run in run-time code and doesn't require
6
+ * testing packages.
7
+ */
8
+ export declare function ensureInstanceOf<T>(value: unknown, constructor: new (...args: any) => T, message?: string): asserts value is T;
@@ -1 +1,13 @@
1
- export {};
1
+ /**
2
+ * If the given value is not an instance of the given constructor, an error is thrown.
3
+ *
4
+ * This is a variation of assertInstanceOf that can be run in run-time code and doesn't require
5
+ * testing packages.
6
+ */
7
+ export function ensureInstanceOf(value, constructor, message) {
8
+ if (!(value instanceof constructor)) {
9
+ const extraMessage = message ? `: ${message}` : '';
10
+ const errorMessage = `${value} is not an instanceof of ${constructor.name}${extraMessage}`;
11
+ throw new TypeError(errorMessage);
12
+ }
13
+ }
@@ -0,0 +1,33 @@
1
+ import { CSSResult } from 'lit';
2
+ import { CssVarsInitMap } from './css-vars';
3
+ import { DeclarativeElementDefinitionOptions } from './definition-options';
4
+ import { EventsInitMap } from './element-events';
5
+ import { PropertyInitMapBase } from './element-properties';
6
+ import { HostClassesInitMap } from './host-classes';
7
+ import { InitCallback, RenderCallback } from './render-callback';
8
+ import { StylesCallback } from './styles';
9
+ export declare type CustomElementTagName = `${string}-${string}`;
10
+ export declare type DeclarativeElementInit<InputsGeneric extends PropertyInitMapBase, StateInit extends PropertyInitMapBase, EventsInitGeneric extends EventsInitMap, HostClassKeys extends string, CssVarKeys extends string> = {
11
+ /**
12
+ * HTML tag name. This should not be used directly, as interpolating it with the html tagged
13
+ * template from this package is preferred.
14
+ */
15
+ tagName: CustomElementTagName;
16
+ /** Static styles. These should not and cannot change. */
17
+ styles?: CSSResult | StylesCallback<HostClassKeys, CssVarKeys>;
18
+ /** Element properties. (These can be thought of as "inputs".) */
19
+ stateInit?: StateInit;
20
+ /** Events that the element can dispatch. (These can be thought of as "outputs".) */
21
+ events?: EventsInitGeneric;
22
+ /**
23
+ * CSS host classes. Values can be callbacks to determine when a host class should be defined,
24
+ * based on current instance state or inputs, or just undefined to indicate that the host class
25
+ * will only be manually set.
26
+ */
27
+ hostClasses?: HostClassesInitMap<HostClassKeys, InputsGeneric, StateInit>;
28
+ cssVars?: CssVarsInitMap<CssVarKeys>;
29
+ /** Called as part of the first renderCallback call, before the first renderCallback call. */
30
+ initCallback?: InitCallback<InputsGeneric, StateInit, EventsInitGeneric, HostClassKeys, CssVarKeys>;
31
+ renderCallback: RenderCallback<InputsGeneric, StateInit, EventsInitGeneric, HostClassKeys, CssVarKeys>;
32
+ options?: Partial<DeclarativeElementDefinitionOptions> | undefined;
33
+ };
@@ -0,0 +1,44 @@
1
+ import { RequiredBy } from 'augment-vir';
2
+ import { CSSResult, LitElement, TemplateResult } from 'lit';
3
+ import { CssVarNameOrValueMap } from './css-vars';
4
+ import { DeclarativeElementInit } from './declarative-element-init';
5
+ import { EventDescriptorMap, EventsInitMap } from './element-events';
6
+ import { ElementPropertyDescriptorMap, PropertyInitMapBase } from './element-properties';
7
+ import { HostClassNamesMap } from './host-classes';
8
+ import { RenderCallback } from './render-callback';
9
+ export declare type DeclarativeElementDefinition<InputsGeneric extends PropertyInitMapBase = any, PropertyInitGeneric extends PropertyInitMapBase = any, EventsInitGeneric extends EventsInitMap = any, HostClassKeys extends string = string, CssVarKeys extends string = string> = (new () => DeclarativeElement<InputsGeneric, PropertyInitGeneric, EventsInitGeneric, HostClassKeys, CssVarKeys>) & StaticDeclarativeElementProperties<InputsGeneric, PropertyInitGeneric, EventsInitGeneric, HostClassKeys, CssVarKeys> & {
10
+ instanceType: DeclarativeElement<InputsGeneric, PropertyInitGeneric, EventsInitGeneric, HostClassKeys, CssVarKeys>;
11
+ };
12
+ export declare abstract class DeclarativeElement<InputsGeneric extends PropertyInitMapBase = any, PropertyInitGeneric extends PropertyInitMapBase = any, EventsInitGeneric extends EventsInitMap = any, HostClassKeys extends string = string, CssVarKeys extends string = string> extends LitElement {
13
+ static readonly tagName: StaticDeclarativeElementProperties<PropertyInitMapBase, PropertyInitMapBase, EventsInitMap, string, string>['tagName'];
14
+ static readonly styles: StaticDeclarativeElementProperties<PropertyInitMapBase, PropertyInitMapBase, EventsInitMap, string, string>['styles'];
15
+ static readonly isStrictInstance: StaticDeclarativeElementProperties<PropertyInitMapBase, PropertyInitMapBase, EventsInitMap, string, string>['isStrictInstance'];
16
+ static readonly renderCallback: StaticDeclarativeElementProperties<PropertyInitMapBase, PropertyInitMapBase, EventsInitMap, string, string>['renderCallback'];
17
+ static readonly inputsType: StaticDeclarativeElementProperties<PropertyInitMapBase, PropertyInitMapBase, EventsInitMap, string, string>['inputsType'];
18
+ static readonly events: StaticDeclarativeElementProperties<PropertyInitMapBase, PropertyInitMapBase, EventsInitMap, string, string>['events'];
19
+ static readonly stateInit: StaticDeclarativeElementProperties<PropertyInitMapBase, PropertyInitMapBase, EventsInitMap, string, string>['stateInit'];
20
+ static readonly init: StaticDeclarativeElementProperties<PropertyInitMapBase, PropertyInitMapBase, EventsInitMap, string, string>['init'];
21
+ static readonly hostClasses: StaticDeclarativeElementProperties<PropertyInitMapBase, PropertyInitMapBase, EventsInitMap, string, string>['hostClasses'];
22
+ static readonly cssVarNames: StaticDeclarativeElementProperties<PropertyInitMapBase, PropertyInitMapBase, EventsInitMap, string, string>['cssVarNames'];
23
+ static readonly cssVarValues: StaticDeclarativeElementProperties<PropertyInitMapBase, PropertyInitMapBase, EventsInitMap, string, string>['cssVarValues'];
24
+ abstract render(): TemplateResult | Promise<TemplateResult>;
25
+ abstract readonly instanceState: PropertyInitGeneric;
26
+ abstract readonly instanceInputs: InputsGeneric;
27
+ abstract readonly haveInputsBeenSet: boolean;
28
+ abstract markInputsAsHavingBeenSet(): void;
29
+ abstract readonly creator: DeclarativeElementDefinition<InputsGeneric, PropertyInitGeneric, EventsInitGeneric, HostClassKeys, CssVarKeys>;
30
+ }
31
+ export interface StaticDeclarativeElementProperties<InputsGeneric extends PropertyInitMapBase, PropertyInitGeneric extends PropertyInitMapBase, EventsInitGeneric extends EventsInitMap, HostClassKeys extends string, CssVarKeys extends string> {
32
+ /** Pass through the render callback for direct unit testability */
33
+ readonly renderCallback: RenderCallback<InputsGeneric, PropertyInitGeneric, EventsInitGeneric, HostClassKeys, CssVarKeys>;
34
+ events: EventDescriptorMap<EventsInitGeneric>;
35
+ stateInit: ElementPropertyDescriptorMap<PropertyInitGeneric>;
36
+ init: RequiredBy<DeclarativeElementInit<InputsGeneric, PropertyInitGeneric, EventsInitGeneric, HostClassKeys, CssVarKeys>, 'stateInit' | 'events'>;
37
+ inputsType: InputsGeneric;
38
+ isStrictInstance: (element: unknown) => element is DeclarativeElement<InputsGeneric, PropertyInitGeneric, EventsInitGeneric, HostClassKeys, CssVarKeys>;
39
+ hostClasses: HostClassNamesMap<string, HostClassKeys>;
40
+ cssVarNames: CssVarNameOrValueMap<CssVarKeys>;
41
+ cssVarValues: CssVarNameOrValueMap<CssVarKeys>;
42
+ tagName: string;
43
+ styles: CSSResult;
44
+ }