element-vir 12.5.4 → 13.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 (31) hide show
  1. package/README.md +265 -222
  2. package/dist/declarative-element/declarative-element-init.d.ts +20 -19
  3. package/dist/declarative-element/declarative-element.d.ts +32 -33
  4. package/dist/declarative-element/define-element-no-inputs.d.ts +2 -1
  5. package/dist/declarative-element/define-element-no-inputs.js +25 -14
  6. package/dist/declarative-element/define-element.d.ts +1 -1
  7. package/dist/declarative-element/define-element.js +2 -2
  8. package/dist/declarative-element/definition-options.d.ts +3 -3
  9. package/dist/declarative-element/definition-options.js +2 -2
  10. package/dist/declarative-element/{properties/async-state.d.ts → directives/async-prop.d.ts} +12 -12
  11. package/dist/declarative-element/{properties/async-state.js → directives/async-prop.js} +2 -2
  12. package/dist/declarative-element/directives/is-render-ready.directive.d.ts +2 -2
  13. package/dist/declarative-element/directives/is-render-ready.directive.js +3 -3
  14. package/dist/declarative-element/directives/render-async.directive.d.ts +6 -0
  15. package/dist/declarative-element/directives/{render-async-state.directive.js → render-async.directive.js} +2 -2
  16. package/dist/declarative-element/properties/css-properties.d.ts +3 -0
  17. package/dist/declarative-element/properties/css-properties.js +11 -0
  18. package/dist/declarative-element/properties/css-vars.d.ts +6 -6
  19. package/dist/declarative-element/properties/css-vars.js +1 -22
  20. package/dist/declarative-element/properties/host-classes.d.ts +5 -3
  21. package/dist/declarative-element/properties/host-classes.js +2 -3
  22. package/dist/declarative-element/properties/styles.d.ts +19 -16
  23. package/dist/declarative-element/properties/styles.js +7 -8
  24. package/dist/declarative-element/properties/tag-name.d.ts +0 -1
  25. package/dist/declarative-element/properties/tag-name.js +1 -4
  26. package/dist/declarative-element/render-callback.d.ts +12 -11
  27. package/dist/declarative-element/wrap-define-element.d.ts +7 -6
  28. package/dist/index.d.ts +2 -2
  29. package/dist/index.js +2 -2
  30. package/package.json +3 -2
  31. package/dist/declarative-element/directives/render-async-state.directive.d.ts +0 -6
package/README.md CHANGED
@@ -1,22 +1,21 @@
1
1
  # element-vir
2
2
 
3
- A wrapper for [lit-element](http://lit.dev) that adds type-safe custom element usage and I/O with declarative custom element definition.
4
-
5
3
  Heroic. Reactive. Declarative. Type safe. Web components without compromise.
6
4
 
5
+ A wrapper for [lit-element](http://lit.dev) that adds type-safe custom element usage and I/O with declarative element definition.
6
+
7
7
  No need for an extra build step,<br>
8
8
  no need for side effect imports, <br>
9
9
  no need for unique file extensions,<br>
10
- no need for extra static analysis tools,<br>
10
+ no need for more static analysis tooling,<br>
11
11
  no need for a dedicated, unique syntax.<br>
12
- _**It's just TypeScript.**_
12
+ _**It's just JavaScript.**_<br>
13
+ <sup>Or TypeScript, if you're into that!</sup>
13
14
 
14
- 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).
15
+ Uses the power of _native_ JavaScript custom web elements, _native_ JavaScript template literals, _native_ JavaScript functions, _native_ HTML, and [lit-element](http://lit.dev).
15
16
 
16
17
  [Works in every major web browser except Internet Explorer.](https://caniuse.com/mdn-api_window_customelements)
17
18
 
18
- <sub>\*okay I hope it's obvious that functions are native</sub>
19
-
20
19
  # Install
21
20
 
22
21
  [Published on npm:](https://www.npmjs.com/package/element-vir)
@@ -29,24 +28,26 @@ Make sure to install this as a normal dependency (not just a dev dependency) bec
29
28
 
30
29
  # Usage
31
30
 
32
- Most usage of this package is done through the `defineElement` or `defineElementNoInputs` functions. See the `DeclarativeElementInit` type for that function's inputs. These inputs are also described below with examples.
31
+ Most usage of this package is done through the `defineElement` or `defineElementNoInputs` functions. See the [`DeclarativeElementInit`](https://github.com/electrovir/element-vir/blob/main/src/declarative-element/declarative-element-init.ts) type for that function's full inputs. The inputs are also described below with examples.
33
32
 
34
- All of [`lit`](https://lit.dev)'s syntax and functionality is also available for use if you wish.
33
+ All of [`lit`](https://lit.dev)'s syntax and functionality is available for use if you wish.
35
34
 
36
35
  ## Simple element definition
37
36
 
38
- Use `defineElementNoInputs` to define your element if it's not going to accept any 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:
37
+ Use `defineElementNoInputs` to define your element if it's not going to accept any inputs (or if you're just getting started). It's only input is an object with at least `tagName` and `renderCallback` properties (the types enforce this). Here is a bare-minimum example custom element:
39
38
 
40
39
  <!-- example-link: src/readme-examples/my-simple.element.ts -->
41
40
 
42
41
  ```TypeScript
43
42
  import {defineElementNoInputs, html} from 'element-vir';
44
43
 
45
- export const MySimpleElement = defineElementNoInputs({
44
+ export const MySimple = defineElementNoInputs({
46
45
  tagName: 'my-simple',
47
- renderCallback: () => html`
48
- <span>Hello there!</span>
49
- `,
46
+ renderCallback() {
47
+ return html`
48
+ <span>Hello there!</span>
49
+ `;
50
+ },
50
51
  });
51
52
  ```
52
53
 
@@ -54,27 +55,27 @@ Make sure to export your element definition if you need to use it in other files
54
55
 
55
56
  ## Using in other elements
56
57
 
57
- To use already defined elements (like `my-simple-element` above), they must be interpolated into HTML templates like so:
58
+ To use already defined elements (like the example above), they must be interpolated into HTML templates like so:
58
59
 
59
60
  <!-- example-link: src/readme-examples/my-app.element.ts -->
60
61
 
61
62
  ```TypeScript
62
63
  import {defineElementNoInputs, html} from 'element-vir';
63
- import {MySimpleElement} from './my-simple.element';
64
+ import {MySimple} from './my-simple.element';
64
65
 
65
- export const MyAppElement = defineElementNoInputs({
66
+ export const MyApp = defineElementNoInputs({
66
67
  tagName: 'my-app',
67
- renderCallback: () => html`
68
- <h1>My App</h1>
69
- <${MySimpleElement}></${MySimpleElement}>
70
- `,
68
+ renderCallback() {
69
+ return html`
70
+ <h1>My App</h1>
71
+ <${MySimple}></${MySimple}>
72
+ `;
73
+ },
71
74
  });
72
75
  ```
73
76
 
74
77
  This requirement ensures that the element is properly imported and registered with the browser. (Compare to pure [lit](http://lit.dev) where you must remember to import each element file as a side effect, or without actually referencing any of its exports in your code.)
75
78
 
76
- If you wish to bypass this interpolation, make sure to [import the `html` tagged template directly from `lit`](https://lit.dev/docs/components/overview/), `import {html} from 'lit';`, instead of version contained in `element-vir`.
77
-
78
79
  ## Adding styles
79
80
 
80
81
  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/)):
@@ -84,7 +85,7 @@ Styles are added through the `styles` property when defining a declarative eleme
84
85
  ```TypeScript
85
86
  import {css, defineElementNoInputs, html} from 'element-vir';
86
87
 
87
- export const MyWithStylesElement = defineElementNoInputs({
88
+ export const MyWithStyles = defineElementNoInputs({
88
89
  tagName: 'my-with-styles',
89
90
  styles: css`
90
91
  :host {
@@ -97,14 +98,16 @@ export const MyWithStylesElement = defineElementNoInputs({
97
98
  margin-top: 16px;
98
99
  }
99
100
  `,
100
- renderCallback: () => html`
101
- <span>Hello there!</span>
102
- <span>How are you doing?</span>
103
- `,
101
+ renderCallback() {
102
+ return html`
103
+ <span>Hello there!</span>
104
+ <span>How are you doing?</span>
105
+ `;
106
+ },
104
107
  });
105
108
  ```
106
109
 
107
- ### Element definition as style selector
110
+ ### Interpolated CSS tag selectors
108
111
 
109
112
  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
113
 
@@ -112,18 +115,20 @@ Declarative element definitions can be used in the `css` tagged template just li
112
115
 
113
116
  ```TypeScript
114
117
  import {css, defineElementNoInputs, html} from 'element-vir';
115
- import {MySimpleElement} from './my-simple.element';
118
+ import {MySimple} from './my-simple.element';
116
119
 
117
- export const MyWithStylesAndInterpolatedSelectorElement = defineElementNoInputs({
120
+ export const MyWithStylesAndInterpolatedSelector = defineElementNoInputs({
118
121
  tagName: 'my-with-styles-and-interpolated-selector',
119
122
  styles: css`
120
- ${MySimpleElement} {
123
+ ${MySimple} {
121
124
  background-color: blue;
122
125
  }
123
126
  `,
124
- renderCallback: () => html`
125
- <${MySimpleElement}></${MySimpleElement}>
126
- `,
127
+ renderCallback() {
128
+ return html`
129
+ <${MySimple}></${MySimple}>
130
+ `;
131
+ },
127
132
  });
128
133
  ```
129
134
 
@@ -138,65 +143,75 @@ To use an element's inputs for use in its template, grab `inputs` from `renderCa
138
143
  ```TypeScript
139
144
  import {defineElement, html} from 'element-vir';
140
145
 
141
- export const MyWithInputsElement = defineElement<{
146
+ export const MyWithInputs = defineElement<{
142
147
  username: string;
143
148
  email: string;
144
149
  }>()({
145
150
  tagName: 'my-with-inputs',
146
- renderCallback: ({inputs}) => html`
147
- <span>Hello there ${inputs.username}!</span>
148
- `,
151
+ renderCallback({inputs}) {
152
+ return html`
153
+ <span>Hello there ${inputs.username}!</span>
154
+ `;
155
+ },
149
156
  });
150
157
  ```
151
158
 
152
159
  ## Defining internal state
153
160
 
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:
161
+ Define initial internal state values and types with the `stateInit` property when defining an element. Grab it with `state` in `renderCallback` to use state. Grab `updateState` in `renderCallback` to update state:
155
162
 
156
163
  <!-- example-link: src/readme-examples/my-with-update-state.element.ts -->
157
164
 
158
165
  ```TypeScript
159
166
  import {defineElementNoInputs, html, listen} from 'element-vir';
160
167
 
161
- export const MyWithUpdateStateElement = defineElementNoInputs({
168
+ export const MyWithUpdateState = defineElementNoInputs({
162
169
  tagName: 'my-with-update-state',
163
- stateInit: {
170
+ stateInitStatic: {
164
171
  username: 'dev',
172
+ /**
173
+ * Use "as" to create state properties that can be types other than the initial value's
174
+ * type. This is particularly useful when, as below, the initial value is undefined.
175
+ */
165
176
  email: undefined as string | undefined,
166
177
  },
167
- renderCallback: ({state, updateState}) => html`
168
- <span
169
- ${listen('click', () => {
170
- updateState({username: 'new name!'});
171
- })}
172
- >
173
- Hello there ${state.username}!
174
- </span>
175
- `,
178
+ renderCallback({state, updateState}) {
179
+ return html`
180
+ <span
181
+ ${listen('click', () => {
182
+ updateState({username: 'new name!'});
183
+ })}
184
+ >
185
+ Hello there ${state.username}!
186
+ </span>
187
+ `;
188
+ },
176
189
  });
177
190
  ```
178
191
 
179
- ### Assigning to properties (inputs)
192
+ ### Assigning to inputs
180
193
 
181
- Use the `assign` directive to assign properties to child custom elements:
194
+ Use the `assign` directive to assign values to child custom elements inputs:
182
195
 
183
196
  <!-- example-link: src/readme-examples/my-with-assignment.element.ts -->
184
197
 
185
198
  ```TypeScript
186
199
  import {assign, defineElementNoInputs, html} from 'element-vir';
187
- import {MyWithInputsElement} from './my-with-inputs.element';
200
+ import {MyWithInputs} from './my-with-inputs.element';
188
201
 
189
- export const MyWithAssignmentElement = defineElementNoInputs({
202
+ export const MyWithAssignment = defineElementNoInputs({
190
203
  tagName: 'my-with-assignment',
191
- renderCallback: () => html`
192
- <h1>My App</h1>
193
- <${MyWithInputsElement}
194
- ${assign(MyWithInputsElement, {
195
- email: 'user@example.com',
196
- username: 'user',
197
- })}
198
- ></${MyWithInputsElement}>
199
- `,
204
+ renderCallback() {
205
+ return html`
206
+ <h1>My App</h1>
207
+ <${MyWithInputs}
208
+ ${assign(MyWithInputs, {
209
+ email: 'user@example.com',
210
+ username: 'user',
211
+ })}
212
+ ></${MyWithInputs}>
213
+ `;
214
+ },
200
215
  });
201
216
  ```
202
217
 
@@ -204,7 +219,7 @@ export const MyWithAssignmentElement = defineElementNoInputs({
204
219
 
205
220
  There are two other callbacks you can define that are sort of similar to lifecycle callbacks. They are much simpler than lifecycle callbacks however.
206
221
 
207
- - `initCallback`: called right before the first render, has all state and inputs setup.
222
+ - `initCallback`: called right before the first render and has all state and inputs setup. (This is similar to `connectedCallback` in standard HTMLElement classes but is fired much later, after inputs are assigned, to avoid race conditions.)
208
223
  - `cleanupCallback`: called when an element is removed from the DOM. (This is the same as the `disconnectedCallback` in standard HTMLElement classes.)
209
224
 
210
225
  <!-- example-link: src/readme-examples/my-with-cleanup-callback.element.ts -->
@@ -212,9 +227,9 @@ There are two other callbacks you can define that are sort of similar to lifecyc
212
227
  ```TypeScript
213
228
  import {defineElementNoInputs, html} from 'element-vir';
214
229
 
215
- export const MyWithAssignmentCleanupCallbackElement = defineElementNoInputs({
230
+ export const MyWithAssignmentCleanupCallback = defineElementNoInputs({
216
231
  tagName: 'my-with-cleanup-callback',
217
- stateInit: {
232
+ stateInitStatic: {
218
233
  intervalId: undefined as undefined | number,
219
234
  },
220
235
  initCallback: ({updateState}) => {
@@ -222,9 +237,11 @@ export const MyWithAssignmentCleanupCallbackElement = defineElementNoInputs({
222
237
  intervalId: window.setInterval(() => console.info('hi'), 1000),
223
238
  });
224
239
  },
225
- renderCallback: () => html`
226
- <h1>My App</h1>
227
- `,
240
+ renderCallback() {
241
+ return html`
242
+ <h1>My App</h1>
243
+ `;
244
+ },
228
245
  cleanupCallback: ({state, updateState}) => {
229
246
  window.clearInterval(state.intervalId);
230
247
  updateState({
@@ -236,55 +253,63 @@ export const MyWithAssignmentCleanupCallbackElement = defineElementNoInputs({
236
253
 
237
254
  ## Element events (outputs)
238
255
 
239
- 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.
256
+ When defining a declarative element, use `events` to setup event names and types. Each event must be initialized with `defineElementEvent` and a type parameter but no run-time inputs.
240
257
 
241
- To dispatch an event, grab `dispatch` from `renderCallback`'s parameters.
258
+ To dispatch an event, grab `dispatch` and `events` from `renderCallback`'s parameters.
242
259
 
243
260
  <!-- example-link: src/readme-examples/my-with-events.element.ts -->
244
261
 
245
262
  ```TypeScript
246
263
  import {defineElementEvent, defineElementNoInputs, html, listen} from 'element-vir';
247
264
 
248
- export const MyWithEventsElement = defineElementNoInputs({
265
+ export const MyWithEvents = defineElementNoInputs({
249
266
  tagName: 'my-with-events',
250
267
  events: {
251
268
  logoutClick: defineElementEvent<void>(),
252
269
  randomNumber: defineElementEvent<number>(),
253
270
  },
254
- renderCallback: ({dispatch, events}) => html`
255
- <button ${listen('click', () => dispatch(new events.logoutClick(undefined)))}>
256
- log out
257
- </button>
258
- <button ${listen('click', () => dispatch(new events.randomNumber(Math.random())))}>
259
- generate random number
260
- </button>
261
- `,
271
+ renderCallback({dispatch, events}) {
272
+ return html`
273
+ <button ${listen('click', () => dispatch(new events.logoutClick(undefined)))}>
274
+ log out
275
+ </button>
276
+ <button ${listen('click', () => dispatch(new events.randomNumber(Math.random())))}>
277
+ generate random number
278
+ </button>
279
+ `;
280
+ },
262
281
  });
263
282
  ```
264
283
 
265
- ### Listening to typed events (outputs)
284
+ ### Listening to element events (outputs)
266
285
 
267
- Use the `listen` directive to listen to typed events emitted by your custom elements:
286
+ Use the `listen` directive to listen to events emitted by your custom elements:
268
287
 
269
- <!-- example-link: src/readme-examples/my-with-events.element.ts -->
288
+ <!-- example-link: src/readme-examples/my-with-event-listening.element.ts -->
270
289
 
271
290
  ```TypeScript
272
- import {defineElementEvent, defineElementNoInputs, html, listen} from 'element-vir';
291
+ import {defineElementNoInputs, html, listen} from 'element-vir';
292
+ import {MyWithEvents} from './my-with-events.element';
273
293
 
274
- export const MyWithEventsElement = defineElementNoInputs({
275
- tagName: 'my-with-events',
276
- events: {
277
- logoutClick: defineElementEvent<void>(),
278
- randomNumber: defineElementEvent<number>(),
294
+ export const MyWithEventListening = defineElementNoInputs({
295
+ tagName: 'my-with-event-listening',
296
+ stateInitStatic: {
297
+ myNumber: -1,
298
+ },
299
+ renderCallback({state, updateState}) {
300
+ return html`
301
+ <h1>My App</h1>
302
+ <${MyWithEvents}
303
+ ${listen(MyWithEvents.events.logoutClick, () => {
304
+ console.info('logout triggered');
305
+ })}
306
+ ${listen(MyWithEvents.events.randomNumber, (event) => {
307
+ updateState({myNumber: event.detail});
308
+ })}
309
+ ></${MyWithEvents}>
310
+ <span>${state.myNumber}</span>
311
+ `;
279
312
  },
280
- renderCallback: ({dispatch, events}) => html`
281
- <button ${listen('click', () => dispatch(new events.logoutClick(undefined)))}>
282
- log out
283
- </button>
284
- <button ${listen('click', () => dispatch(new events.randomNumber(Math.random())))}>
285
- generate random number
286
- </button>
287
- `,
288
313
  });
289
314
  ```
290
315
 
@@ -292,9 +317,7 @@ export const MyWithEventsElement = defineElementNoInputs({
292
317
 
293
318
  ## Typed events without an element
294
319
 
295
- Create a custom event type with `defineTypedEvent`. Make sure to include the type generic (like this: `defineTypedEvent<number>`) and call it twice, the second time with the event type string, (like this: `defineTypedEvent<number>()('my-event-type-name')`) to ensure type safety when using your event. Note that event type names should probably be unique, or they may clash with each other.
296
-
297
- ### Creating a typed event
320
+ Create a custom event type with `defineTypedEvent`. Make sure to include the type parameter and call it twice, the second time with the event type name string to ensure type safety when using your event. Note that event type names should be unique, or they will clash with each other.
298
321
 
299
322
  <!-- example-link: src/readme-examples/my-custom-action.event.ts -->
300
323
 
@@ -306,7 +329,7 @@ export const MyCustomActionEvent = defineTypedEvent<number>()('my-custom-action'
306
329
 
307
330
  ### Using a typed event
308
331
 
309
- Both dispatching a custom event and listening to a custom event:
332
+ Dispatching a custom event and listening to a custom event is the same as doing so for element events:
310
333
 
311
334
  <!-- example-link: src/readme-examples/my-with-custom-events.element.ts -->
312
335
 
@@ -314,21 +337,23 @@ Both dispatching a custom event and listening to a custom event:
314
337
  import {defineElementNoInputs, html, listen} from 'element-vir';
315
338
  import {MyCustomActionEvent} from './my-custom-action.event';
316
339
 
317
- export const MyWithCustomEventsElement = defineElementNoInputs({
340
+ export const MyWithCustomEvents = defineElementNoInputs({
318
341
  tagName: 'my-with-custom-events',
319
- renderCallback: ({dispatch}) => html`
320
- <div
321
- ${listen(MyCustomActionEvent, (event) => {
322
- console.info(`Got a number! ${event.detail}`);
323
- })}
324
- >
342
+ renderCallback({dispatch}) {
343
+ return html`
325
344
  <div
326
- ${listen('click', () => {
327
- dispatch(new MyCustomActionEvent(Math.random()));
345
+ ${listen(MyCustomActionEvent, (event) => {
346
+ console.info(`Got a number! ${event.detail}`);
328
347
  })}
329
- ></div>
330
- </div>
331
- `,
348
+ >
349
+ <div
350
+ ${listen('click', () => {
351
+ dispatch(new MyCustomActionEvent(Math.random()));
352
+ })}
353
+ ></div>
354
+ </div>
355
+ `;
356
+ },
332
357
  });
333
358
  ```
334
359
 
@@ -336,34 +361,36 @@ export const MyWithCustomEventsElement = defineElementNoInputs({
336
361
 
337
362
  ### Defining host classes
338
363
 
339
- Host classes can be defined and used with type safety. Host classes are used to provide alternative styles for components. They are purely driven by CSS and are thus applied via the `class` HTML attribute.
364
+ Host classes can be defined and used with type safety. Host classes are used to provide alternative styles for custom elements. They are purely driven by CSS and are thus applied to the the `class` HTML attribute.
340
365
 
341
- Host classes that are defined with a callback will automatically get applied if that callback returns true after a render is executed. These are executed _after_ `renderCallback` is executed. When a definition is set to `false`, it's left to the element's consumer to apply the host class.
366
+ Host classes are defined by passing an object to `hostClasses` at element definition time. Each property name in the `hostClasses` object creates a host class name (note that host class names must start with the element's tag name). Each value in the `hostClasses` object defines behavior for teh host class:
342
367
 
343
- Apply host classes in the element's stylesheet by using a callback for the styles property.
368
+ - if the value is a callback, that host class will automatically be applied if the callback returns true after a render is executed.
369
+ - if the value is `false`, the host class is never automatically applied, it must be manually applied by consumers.
370
+
371
+ Apply host classes in the element's stylesheet by using a callback for the styles property:
344
372
 
345
373
  <!-- example-link: src/readme-examples/my-with-host-class-definition.element.ts -->
346
374
 
347
375
  ```TypeScript
348
376
  import {css, defineElementNoInputs, html} from 'element-vir';
349
377
 
350
- export const MyWithHostClassDefinitionElement = defineElementNoInputs({
378
+ export const MyWithHostClassDefinition = defineElementNoInputs({
351
379
  tagName: 'my-with-host-class-definition',
352
- stateInit: {
380
+ stateInitStatic: {
353
381
  myProp: 'hello there',
354
382
  },
355
383
  hostClasses: {
356
384
  /**
357
- * Setting the value to false means this host class will not ever automatically be applied.
358
- * It will simply be a static member on the element for manual application in consumers when
359
- * desired.
385
+ * Setting the value to false means this host class will never be automatically applied. It
386
+ * will simply be a static member on the element for manual application in consumers.
360
387
  */
361
- styleVariationA: false,
388
+ 'my-with-host-class-definition-a': false,
362
389
  /**
363
- * This host class will be automatically applied if the given callback evaluated to true
390
+ * This host class will be automatically applied if the given callback is evaluated to true
364
391
  * after a call to renderCallback.
365
392
  */
366
- automaticallyAppliedVariation: ({state}) => {
393
+ 'my-with-host-class-definition-automatic': ({state}) => {
367
394
  return state.myProp === 'foo';
368
395
  },
369
396
  },
@@ -371,74 +398,82 @@ export const MyWithHostClassDefinitionElement = defineElementNoInputs({
371
398
  * Apply styles to the host classes by using a callback for "styles". The callback's argument
372
399
  * contains the host classes defined above in the "hostClasses" property.
373
400
  */
374
- styles: ({hostClassSelectors: hostClass}) => css`
375
- ${hostClass.automaticallyAppliedVariation} {
401
+ styles: ({hostClasses}) => css`
402
+ ${hostClasses['my-with-host-class-definition-automatic'].selector} {
376
403
  color: blue;
377
404
  }
378
405
 
379
- ${hostClass.styleVariationA} {
406
+ ${hostClasses['my-with-host-class-definition-a'].selector} {
380
407
  color: red;
381
408
  }
382
409
  `,
383
- renderCallback: ({state}) => html`
384
- ${state.myProp}
385
- `,
410
+ renderCallback({state}) {
411
+ return html`
412
+ ${state.myProp}
413
+ `;
414
+ },
386
415
  });
387
416
  ```
388
417
 
389
418
  ### Applying host classes
390
419
 
391
- To apply a host class in a parent element, access the child element's `.hostClasses` property:
420
+ To apply a host class in a consumer, access the child element's `.hostClasses` property:
392
421
 
393
422
  <!-- example-link: src/readme-examples/my-with-host-class-usage.element.ts -->
394
423
 
395
424
  ```TypeScript
396
425
  import {defineElementNoInputs, html} from 'element-vir';
397
- import {MyWithHostClassDefinitionElement} from './my-with-host-class-definition.element';
426
+ import {MyWithHostClassDefinition} from './my-with-host-class-definition.element';
398
427
 
399
- export const MyWithHostClassUsageElement = defineElementNoInputs({
428
+ export const MyWithHostClassUsage = defineElementNoInputs({
400
429
  tagName: 'my-with-host-class-usage',
401
- renderCallback: () => html`
402
- <${MyWithHostClassDefinitionElement}
403
- class=${MyWithHostClassDefinitionElement.hostClasses.styleVariationA}
404
- ></${MyWithHostClassDefinitionElement}>
405
- `,
430
+ renderCallback() {
431
+ return html`
432
+ <${MyWithHostClassDefinition}
433
+ class=${MyWithHostClassDefinition.hostClasses['my-with-host-class-definition-a']}
434
+ ></${MyWithHostClassDefinition}>
435
+ `;
436
+ },
406
437
  });
407
438
  ```
408
439
 
409
440
  ## CSS Vars
410
441
 
411
- Typed CSS vars are created in a similar way as host classes:
442
+ Typed CSS variables are created in a similar manner to host classes:
412
443
 
413
444
  <!-- example-link: src/readme-examples/my-with-css-vars.element.ts -->
414
445
 
415
446
  ```TypeScript
416
447
  import {css, defineElementNoInputs, html} from 'element-vir';
417
448
 
418
- export const MyWithCssVarsElement = defineElementNoInputs({
449
+ export const MyWithCssVars = defineElementNoInputs({
419
450
  tagName: 'my-with-css-vars',
420
451
  cssVars: {
421
- /**
422
- * The value assigned here ('blue') becomes the fallback value for this CSS var when used
423
- * via "cssVarValue".
424
- */
425
- myCssVar: 'blue',
452
+ /** The value assigned here ('blue') becomes the fallback value for this CSS var. */
453
+ 'my-with-css-vars-my-var': 'blue',
426
454
  },
427
- styles: ({cssVarNames: cssVarName, cssVarValues: cssVarValue}) => css`
455
+ styles: ({cssVars}) => css`
428
456
  :host {
429
- /* Set CSS vars (or reference the name directly) via "cssVarName" */
430
- ${cssVarName.myCssVar}: yellow;
431
- /* Use CSS vars with "cssVarValue". This includes a "var" wrapper and the assigned fallback value (which in this case is 'blue'). */
432
- color: ${cssVarValue.myCssVar};
457
+ /*
458
+ Set CSS vars (or reference the name directly) via the ".name" property
459
+ */
460
+ ${cssVars['my-with-css-vars-my-var'].name}: yellow;
461
+ /*
462
+ Use CSS vars with the ".value" property. This includes a "var" wrapper and the
463
+ assigned fallback value (which in this case is 'blue').
464
+ */
465
+ color: ${cssVars['my-with-css-vars-my-var'].value};
433
466
  }
434
467
  `,
435
- renderCallback: () => html``,
468
+ renderCallback() {
469
+ return html``;
470
+ },
436
471
  });
437
472
  ```
438
473
 
439
474
  ## Custom Type Requirements
440
475
 
441
- Use `wrapDefineElement` to compose `defineElement` and `defineElementNoInputs`. This is particularly useful to adding restrictions on the element `tagName`, but it can be used for restricting any of the inputs:
476
+ Use `wrapDefineElement` to compose `defineElement` and `defineElementNoInputs`. This is particularly useful to adding restrictions on the element `tagName`, but it can be used for restricting any of the type parameters:
442
477
 
443
478
  <!-- example-link: src/readme-examples/my-custom-define.ts -->
444
479
 
@@ -480,64 +515,68 @@ export const {
480
515
 
481
516
  The following custom [`lit` directives](https://lit.dev/docs/templates/custom-directives/) are contained within this package.
482
517
 
483
- ### onDomCreated
518
+ All [built-in `lit` directives](https://lit.dev/docs/templates/directives/) are also exported by `element-vir`.
484
519
 
485
- This directive should be used instead of trying to use `querySelector` directly on the custom element.
520
+ ### onDomCreated
486
521
 
487
- This triggers only once when the element it's attached has actually been created in the DOM. If it's attached element changes, the callback will be triggered again.
522
+ This triggers only once when the element it's attached to has actually been created in the DOM. If the attached element changes, the callback will be triggered again.
488
523
 
489
524
  <!-- example-link: src/readme-examples/my-with-on-dom-created.element.ts -->
490
525
 
491
526
  ```TypeScript
492
527
  import {defineElementNoInputs, html, onDomCreated} from 'element-vir';
493
528
 
494
- export const MyWithOnDomCreatedElement = defineElementNoInputs({
529
+ export const MyWithOnDomCreated = defineElementNoInputs({
495
530
  tagName: 'my-with-on-dom-created',
496
- renderCallback: () => html`
497
- <span
498
- ${onDomCreated((element) => {
499
- // logs a span element
500
- console.info(element);
501
- })}
502
- >
503
- Hello there!
504
- </span>
505
- `,
531
+ renderCallback() {
532
+ return html`
533
+ <span
534
+ ${onDomCreated((element) => {
535
+ // logs a span element
536
+ console.info(element);
537
+ })}
538
+ >
539
+ Hello there!
540
+ </span>
541
+ `;
542
+ },
506
543
  });
507
544
  ```
508
545
 
509
546
  ### onResize
510
547
 
511
- This directive fulfills a common use case of triggering callbacks when something resizes. Instead of just tracking the _globally_ resizing window though, this allows you to track resizes of an individual element. The callback here is passed an object with a portion of the [`ResizeObserverEntry`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry) properties (since not all properties are supported well in browsers).
548
+ This directive fires its callback whenever the element it's attached to resizes. The callback is passed an object with a portion of the [`ResizeObserverEntry`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry) properties.
512
549
 
513
550
  <!-- example-link: src/readme-examples/my-with-on-resize.element.ts -->
514
551
 
515
552
  ```TypeScript
516
553
  import {defineElementNoInputs, html, onResize} from 'element-vir';
517
554
 
518
- export const MyWithOnResizeElement = defineElementNoInputs({
555
+ export const MyWithOnResize = defineElementNoInputs({
519
556
  tagName: 'my-with-on-resize',
520
- renderCallback: () => html`
521
- <span
522
- ${onResize((entry) => {
523
- // this will track resizing of this span
524
- // the entry parameter contains target and contentRect properties
525
- console.info(entry);
526
- })}
527
- >
528
- Hello there!
529
- </span>
530
- `,
557
+ renderCallback() {
558
+ return html`
559
+ <span
560
+ ${onResize((entry) => {
561
+ // this will track resizing of this span
562
+ // the entry parameter contains target and contentRect properties
563
+ console.info(entry);
564
+ })}
565
+ >
566
+ Hello there!
567
+ </span>
568
+ `;
569
+ },
531
570
  });
532
571
  ```
533
572
 
534
573
  ### assign
535
574
 
536
- Assign a value to one of a custom element's properties. This is explained in the **Assigning to properties (inputs)** section earlier in this README.
575
+ Assign a value to one of a custom element's properties. This is explained in the **Assigning to inputs** section earlier.
537
576
 
538
577
  ### listen
539
578
 
540
- Listen to a specific event emitted from a custom element. This is explained in the **Listening to custom events (outputs)** section earlier in this README.
579
+ Listen to a specific event. This is explained in the **Listening to element events (outputs)** section earlier.
541
580
 
542
581
  ### assignWithCleanup
543
582
 
@@ -547,28 +586,30 @@ This directive is the same as the `assign` directive but it accepts an additiona
547
586
 
548
587
  ```TypeScript
549
588
  import {assignWithCleanup, defineElementNoInputs, html} from 'element-vir';
550
- import {MyWithInputsElement} from './my-with-inputs.element';
589
+ import {MyWithInputs} from './my-with-inputs.element';
551
590
 
552
- export const MyWithCleanupElement = defineElementNoInputs({
591
+ export const MyWithCleanup = defineElementNoInputs({
553
592
  tagName: 'my-with-cleanup',
554
- renderCallback: () => html`
555
- <h1>My App</h1>
556
- <${MyWithInputsElement}
557
- ${assignWithCleanup(
558
- MyWithInputsElement,
559
- {
560
- email: 'user@example.com',
561
- username: 'user',
562
- },
563
- (previousValue) => {
564
- // here would be the cleanup code.
565
- // In this specific example the value is just a string, so no cleanup is needed
566
- // and the following line isn't actually doing anything.
567
- previousValue.username.trim();
568
- },
569
- )}
570
- ></${MyWithInputsElement}>
571
- `,
593
+ renderCallback() {
594
+ return html`
595
+ <h1>My App</h1>
596
+ <${MyWithInputs}
597
+ ${assignWithCleanup(
598
+ MyWithInputs,
599
+ {
600
+ email: 'user@example.com',
601
+ username: 'user',
602
+ },
603
+ (previousValue) => {
604
+ // here would be the cleanup code.
605
+ // In this specific example the value is just a string, so no cleanup is needed
606
+ // and the following line isn't actually doing anything.
607
+ previousValue.username.trim();
608
+ },
609
+ )}
610
+ ></${MyWithInputs}>
611
+ `;
612
+ },
572
613
  });
573
614
  ```
574
615
 
@@ -581,16 +622,18 @@ Use the `renderIf` directive to easily render a template if a given condition is
581
622
  ```TypeScript
582
623
  import {defineElement, html, renderIf} from 'element-vir';
583
624
 
584
- export const MyWithRenderIfElement = defineElement<{shouldRender: boolean}>()({
585
- tagName: 'my-simple-with-render-if',
586
- renderCallback: ({inputs}) => html`
587
- ${renderIf(
588
- inputs.shouldRender,
589
- html`
590
- I'm conditionally rendered!
591
- `,
592
- )}
593
- `,
625
+ export const MyWithRenderIf = defineElement<{shouldRender: boolean}>()({
626
+ tagName: 'my-with-render-if',
627
+ renderCallback({inputs}) {
628
+ return html`
629
+ ${renderIf(
630
+ inputs.shouldRender,
631
+ html`
632
+ I'm conditionally rendered!
633
+ `,
634
+ )}
635
+ `;
636
+ },
594
637
  });
595
638
  ```
596
639
 
@@ -601,7 +644,7 @@ Use the `renderAsyncState` directive in conjunction with the `asyncState` proper
601
644
  <!-- example-link: src/readme-examples/my-with-async-prop.element.ts -->
602
645
 
603
646
  ```TypeScript
604
- import {asyncState, defineElement, html, listen, renderAsyncState} from 'element-vir';
647
+ import {asyncProp, defineElement, html, listen, renderAsync} from 'element-vir';
605
648
 
606
649
  type EndpointData = number[];
607
650
 
@@ -619,12 +662,12 @@ async function loadSomething(endpoint: string): Promise<EndpointData> {
619
662
  return data;
620
663
  }
621
664
 
622
- export const MyWithAsyncStateElement = defineElement<{endpoint: string}>()({
623
- tagName: 'my-simple-with-render-if',
624
- stateInit: {
625
- data: asyncState<EndpointData>(),
665
+ export const MyWithAsyncProp = defineElement<{endpoint: string}>()({
666
+ tagName: 'my-with-async-prop',
667
+ stateInitStatic: {
668
+ data: asyncProp<EndpointData>(),
626
669
  },
627
- renderCallback: ({inputs, state, updateState}) => {
670
+ renderCallback({inputs, state, updateState}) {
628
671
  /**
629
672
  * This creates a promise which automatically updates the state.loadsLater prop once the
630
673
  * promise resolves. It only creates a new promise if the "trigger" value changes.
@@ -639,7 +682,7 @@ export const MyWithAsyncStateElement = defineElement<{endpoint: string}>()({
639
682
  return html`
640
683
  Here's the data:
641
684
  <br />
642
- ${renderAsyncState(state.data, 'Loading...', (loadedData) => {
685
+ ${renderAsync(state.data, 'Loading...', (loadedData) => {
643
686
  return html`
644
687
  Got the data: ${loadedData}
645
688
  `;
@@ -649,7 +692,7 @@ export const MyWithAsyncStateElement = defineElement<{endpoint: string}>()({
649
692
  ${listen('click', () => {
650
693
  updateState({
651
694
  data: {
652
- /** You can force asyncState to update by passing in forceUpdate: true. */
695
+ /** You can force asyncProp to update by passing in forceUpdate: true. */
653
696
  forceUpdate: true,
654
697
  },
655
698
  });
@@ -678,7 +721,7 @@ requireAllCustomElementsToBeDeclarativeElements();
678
721
 
679
722
  ## markdown out of date
680
723
 
681
- If you see this: `Code in Markdown file(s) is out of date. Run without --check to update. code-in-markdown failed.`, run `npm run update-docs` to fix it.
724
+ If you see this: `Code in Markdown file(s) is out of date. Run without --check to update. code-in-markdown failed.`, run `npm run docs:update` to fix it.
682
725
 
683
726
  ## Testing source map errors
684
727
 
@@ -688,4 +731,4 @@ If you see
688
731
  Error while reading source maps for ...
689
732
  ```
690
733
 
691
- While running `npm test`, don't worry about it. Those only happen when tests fail.
734
+ While running `npm test`, don't worry about it. Those only happen when tests fail and are not indicative of any problem beyond the test failure reasons.