element-vir 3.0.1 → 4.0.2
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.
- package/README.md +76 -66
- package/dist/augments/type.d.ts +1 -0
- package/dist/augments/type.js +1 -0
- package/dist/functional-element/define-functional-element.js +23 -26
- package/dist/functional-element/directives/assign-with-clean-up.directive.js +3 -0
- package/dist/functional-element/directives/assign.directive.js +1 -0
- package/dist/functional-element/directives/listen.directive.d.ts +10 -25
- package/dist/functional-element/directives/listen.directive.js +7 -14
- package/dist/functional-element/directives/on-dom-created.directive.js +3 -2
- package/dist/functional-element/directives/on-resize.directive.js +4 -3
- package/dist/functional-element/element-events.d.ts +7 -26
- package/dist/functional-element/element-events.js +12 -33
- package/dist/functional-element/functional-element.d.ts +0 -2
- package/dist/functional-element/functional-element.js +2 -0
- package/dist/functional-element/render-callback.d.ts +5 -3
- package/dist/functional-element/render-callback.js +3 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/template-transforms/transform-template.js +3 -4
- package/dist/template-transforms/vir-html/html-transform.js +1 -1
- package/dist/typed-event/typed-event.d.ts +19 -0
- package/dist/typed-event/typed-event.js +30 -0
- package/package.json +17 -12
- package/dist/functional-element/directives/named-listen.directive.d.ts +0 -15
- package/dist/functional-element/directives/named-listen.directive.js +0 -42
package/README.md
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
# element-vir
|
|
2
2
|
|
|
3
|
-
Heroic
|
|
3
|
+
Heroic. Reactive. Functional. Type safe. Web components without compromise.
|
|
4
4
|
|
|
5
5
|
No need for an extra build step,<br>
|
|
6
|
-
no need for
|
|
6
|
+
no need for side effect imports, <br>
|
|
7
|
+
no need for unique file extensions,<br>
|
|
7
8
|
no need for extra static analysis tools,<br>
|
|
8
|
-
no need for a dedicated
|
|
9
|
+
no need for a dedicated, unique syntax.<br>
|
|
9
10
|
_**It's just TypeScript.**_
|
|
10
11
|
|
|
11
|
-
|
|
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).
|
|
12
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.
|
|
14
15
|
|
|
15
|
-
[Works in every major browser except Internet Explorer.](https://caniuse.com/mdn-api_window_customelements)
|
|
16
|
+
[Works in every major web browser except Internet Explorer.](https://caniuse.com/mdn-api_window_customelements)
|
|
16
17
|
|
|
17
18
|
<sub>\*okay I hope it's obvious that functions are native</sub>
|
|
18
19
|
|
|
@@ -24,15 +25,17 @@ This is basically a [lit-element](http://lit.dev) wrapper that adds type-safe I/
|
|
|
24
25
|
npm i element-vir
|
|
25
26
|
```
|
|
26
27
|
|
|
28
|
+
Make sure to install this as a normal dependency (not just a dev dependency) because it needs to exist at run time.
|
|
29
|
+
|
|
27
30
|
# Usage
|
|
28
31
|
|
|
29
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.
|
|
30
33
|
|
|
31
|
-
All of [`lit`](https://lit.dev)'s syntax and functionality is also available for use.
|
|
34
|
+
All of [`lit`](https://lit.dev)'s syntax and functionality is also available for use if you wish.
|
|
32
35
|
|
|
33
36
|
## Simple element definition
|
|
34
37
|
|
|
35
|
-
Use `defineFunctionalElement` to define your element.
|
|
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:
|
|
36
39
|
|
|
37
40
|
<!-- example-link: src/readme-examples/my-simple.element.ts -->
|
|
38
41
|
|
|
@@ -47,7 +50,7 @@ export const MySimpleElement = defineFunctionalElement({
|
|
|
47
50
|
});
|
|
48
51
|
```
|
|
49
52
|
|
|
50
|
-
Make sure to export your element definition
|
|
53
|
+
Make sure to export your element definition if you need to use it in other files.
|
|
51
54
|
|
|
52
55
|
## Using in other elements
|
|
53
56
|
|
|
@@ -70,17 +73,16 @@ export const MyAppElement = defineFunctionalElement({
|
|
|
70
73
|
|
|
71
74
|
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.)
|
|
72
75
|
|
|
73
|
-
If you wish to bypass this interpolation
|
|
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`.
|
|
74
77
|
|
|
75
78
|
## Adding styles
|
|
76
79
|
|
|
77
|
-
Styles are added through `styles` 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 functional element (similar to [how they are defined in `lit`](https://lit.dev/docs/components/styles/)):
|
|
78
81
|
|
|
79
82
|
<!-- example-link: src/readme-examples/my-simple-app-with-styles.element.ts -->
|
|
80
83
|
|
|
81
84
|
```TypeScript
|
|
82
|
-
import {css} from '
|
|
83
|
-
import {defineFunctionalElement, html} from 'element-vir';
|
|
85
|
+
import {css, defineFunctionalElement, html} from 'element-vir';
|
|
84
86
|
|
|
85
87
|
export const MySimpleWithStylesElement = defineFunctionalElement({
|
|
86
88
|
tagName: 'my-simple-with-styles-element',
|
|
@@ -102,9 +104,32 @@ export const MySimpleWithStylesElement = defineFunctionalElement({
|
|
|
102
104
|
});
|
|
103
105
|
```
|
|
104
106
|
|
|
107
|
+
### Element definition as style selector
|
|
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:
|
|
110
|
+
|
|
111
|
+
<!-- example-link: src/readme-examples/my-simple-app-with-styles-and-interpolated-selector.element.ts -->
|
|
112
|
+
|
|
113
|
+
```TypeScript
|
|
114
|
+
import {css, defineFunctionalElement, html} from 'element-vir';
|
|
115
|
+
import {MySimpleElement} from './my-simple.element';
|
|
116
|
+
|
|
117
|
+
export const MySimpleWithStylesAndInterpolatedSelectorElement = defineFunctionalElement({
|
|
118
|
+
tagName: 'my-simple-with-styles-and-interpolated-selector-element',
|
|
119
|
+
styles: css`
|
|
120
|
+
${MySimpleElement} {
|
|
121
|
+
background-color: blue;
|
|
122
|
+
}
|
|
123
|
+
`,
|
|
124
|
+
renderCallback: () => html`
|
|
125
|
+
<${MySimpleElement}></${MySimpleElement}>
|
|
126
|
+
`,
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
105
130
|
## Defining and using properties (inputs)
|
|
106
131
|
|
|
107
|
-
Define properties with `props` when
|
|
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.
|
|
108
133
|
|
|
109
134
|
To use a custom element's properties, grab `props` from `renderCallback`'s parameters and interpolate it into your HTML template:
|
|
110
135
|
|
|
@@ -148,43 +173,37 @@ export const MyAppWithPropsElement = defineFunctionalElement({
|
|
|
148
173
|
});
|
|
149
174
|
```
|
|
150
175
|
|
|
151
|
-
##
|
|
176
|
+
## Element events (outputs)
|
|
152
177
|
|
|
153
|
-
Define events with `events` when
|
|
178
|
+
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.
|
|
154
179
|
|
|
155
|
-
To dispatch an event, grab `
|
|
180
|
+
To dispatch an event, grab `dispatch` from `renderCallback`'s parameters.
|
|
156
181
|
|
|
157
182
|
<!-- example-link: src/readme-examples/my-simple-with-events.element.ts -->
|
|
158
183
|
|
|
159
184
|
```TypeScript
|
|
160
|
-
import {
|
|
185
|
+
import {defineElementEvent, defineFunctionalElement, html, listen} from 'element-vir';
|
|
161
186
|
|
|
162
187
|
export const MySimpleWithEventsElement = defineFunctionalElement({
|
|
163
188
|
tagName: 'my-simple-element-with-events',
|
|
164
189
|
events: {
|
|
165
|
-
logoutClick:
|
|
166
|
-
randomNumber:
|
|
190
|
+
logoutClick: defineElementEvent<void>(),
|
|
191
|
+
randomNumber: defineElementEvent<number>(),
|
|
167
192
|
},
|
|
168
|
-
renderCallback: ({
|
|
169
|
-
|
|
170
|
-
<button
|
|
171
|
-
@click=${() => dispatchElementEvent(new ElementEvent(events.logoutClick, undefined))}
|
|
172
|
-
>
|
|
193
|
+
renderCallback: ({dispatch, events}) => html`
|
|
194
|
+
<button ${listen('click', () => dispatch(new events.logoutClick(undefined)))}>
|
|
173
195
|
log out
|
|
174
196
|
</button>
|
|
175
|
-
<button
|
|
176
|
-
@click=${() =>
|
|
177
|
-
dispatchElementEvent(new ElementEvent(events.randomNumber, Math.random()))}
|
|
178
|
-
>
|
|
197
|
+
<button ${listen('click', () => dispatch(new events.randomNumber(Math.random())))}>
|
|
179
198
|
generate random number
|
|
180
199
|
</button>
|
|
181
200
|
`,
|
|
182
201
|
});
|
|
183
202
|
```
|
|
184
203
|
|
|
185
|
-
## Listening to
|
|
204
|
+
## Listening to typed events (outputs)
|
|
186
205
|
|
|
187
|
-
Use the `listen` directive to listen to
|
|
206
|
+
Use the `listen` directive to listen to typed events emitted by your custom functional elements:
|
|
188
207
|
|
|
189
208
|
<!-- example-link: src/readme-examples/my-app-with-events.element.ts -->
|
|
190
209
|
|
|
@@ -213,21 +232,25 @@ export const MyAppWithEventsElement = defineFunctionalElement({
|
|
|
213
232
|
});
|
|
214
233
|
```
|
|
215
234
|
|
|
216
|
-
|
|
235
|
+
`listen` can also be used to listen to native DOM events (like `click`) and the proper event type will be provided for the listener callback.
|
|
236
|
+
|
|
237
|
+
## Typed events without an element
|
|
217
238
|
|
|
218
|
-
Create a custom event type with `
|
|
239
|
+
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.
|
|
219
240
|
|
|
220
|
-
Creating a
|
|
241
|
+
### Creating a typed event
|
|
221
242
|
|
|
222
243
|
<!-- example-link: src/readme-examples/custom-event-no-element.ts -->
|
|
223
244
|
|
|
224
245
|
```TypeScript
|
|
225
|
-
import {
|
|
246
|
+
import {defineTypedEvent} from 'element-vir';
|
|
226
247
|
|
|
227
|
-
export const MyCustomEvent =
|
|
248
|
+
export const MyCustomEvent = defineTypedEvent<number>()('myCustomEventName');
|
|
228
249
|
```
|
|
229
250
|
|
|
230
|
-
Using a
|
|
251
|
+
### Using a typed event
|
|
252
|
+
|
|
253
|
+
Both dispatching a custom event and listening to a custom event:
|
|
231
254
|
|
|
232
255
|
<!-- example-link: src/readme-examples/custom-event-usage.element.ts -->
|
|
233
256
|
|
|
@@ -237,16 +260,16 @@ import {MyCustomEvent} from './custom-event-no-element';
|
|
|
237
260
|
|
|
238
261
|
export const MyElementWithCustomEvents = defineFunctionalElement({
|
|
239
262
|
tagName: 'my-app-with-custom-events',
|
|
240
|
-
renderCallback: ({
|
|
263
|
+
renderCallback: ({genericDispatch}) => html`
|
|
241
264
|
<div
|
|
242
265
|
${listen(MyCustomEvent, (event) => {
|
|
243
266
|
console.log(`Got a number! ${event.detail}`);
|
|
244
267
|
})}
|
|
245
268
|
>
|
|
246
269
|
<div
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}}
|
|
270
|
+
${listen('click', () => {
|
|
271
|
+
genericDispatch(new MyCustomEvent(Math.random()));
|
|
272
|
+
})}
|
|
250
273
|
></div>
|
|
251
274
|
</div>
|
|
252
275
|
`,
|
|
@@ -255,13 +278,13 @@ export const MyElementWithCustomEvents = defineFunctionalElement({
|
|
|
255
278
|
|
|
256
279
|
## Directives
|
|
257
280
|
|
|
258
|
-
|
|
281
|
+
The following custom [`lit` directives](https://lit.dev/docs/templates/custom-directives/) are contained within this package.
|
|
259
282
|
|
|
260
283
|
### onDomCreated
|
|
261
284
|
|
|
262
285
|
This directive should be used instead of trying to use `querySelector` directly on the custom element.
|
|
263
286
|
|
|
264
|
-
This triggers only once when the element it's
|
|
287
|
+
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.
|
|
265
288
|
|
|
266
289
|
<!-- example-link: src/readme-examples/my-simple-with-on-dom-created.element.ts -->
|
|
267
290
|
|
|
@@ -285,7 +308,7 @@ export const MySimpleWithOnDomCreatedElement = defineFunctionalElement({
|
|
|
285
308
|
|
|
286
309
|
### onResize
|
|
287
310
|
|
|
288
|
-
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
|
|
311
|
+
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).
|
|
289
312
|
|
|
290
313
|
<!-- example-link: src/readme-examples/my-simple-with-on-resize.element.ts -->
|
|
291
314
|
|
|
@@ -316,33 +339,14 @@ Assign a value to one of a custom element's properties. This is explained in the
|
|
|
316
339
|
|
|
317
340
|
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.
|
|
318
341
|
|
|
319
|
-
### namedListen
|
|
320
|
-
|
|
321
|
-
Listen to an event by name directly. This can be used to listen to native events as well as custom events. However, `listen` (explained above) is better for custom events as it'll give you better type information. When using `namedListen` for native DOM events (like `'click'`) the callback will be typed correctly. When listening to custom events, however, it can't guess the callback type from anywhere so the event parameter type will be unknown.
|
|
322
|
-
|
|
323
|
-
<!-- example-link: src/readme-examples/my-simple-with-named-listen.element.ts -->
|
|
324
|
-
|
|
325
|
-
```TypeScript
|
|
326
|
-
import {defineFunctionalElement, html, namedListen} from 'element-vir';
|
|
327
|
-
|
|
328
|
-
export const MySimpleWithNamedListenElement = defineFunctionalElement({
|
|
329
|
-
tagName: 'my-simple-element-with-named-listen',
|
|
330
|
-
renderCallback: () => html`
|
|
331
|
-
<!-- normal DOM events can be listened to -->
|
|
332
|
-
<button ${namedListen('click', (event) => console.log(event.buttons))}>click me</button>
|
|
333
|
-
`,
|
|
334
|
-
});
|
|
335
|
-
```
|
|
336
|
-
|
|
337
342
|
### assignWithCleanup
|
|
338
343
|
|
|
339
|
-
This directive is the same as the `assign` directive but it accepts an additional `cleanupCallback` input. Use this directive to assign values which need some kind of cleanup
|
|
344
|
+
This directive is the same as the `assign` directive but it accepts an additional `cleanupCallback` input. Use this directive to assign values which need some kind of cleanup when they're overwritten. For example, a 3D rendering engine which uses the canvas that should free up memory when it's swapped out.
|
|
340
345
|
|
|
341
346
|
<!-- example-link: src/readme-examples/my-app-with-cleanup.element.ts -->
|
|
342
347
|
|
|
343
348
|
```TypeScript
|
|
344
|
-
import {assign, defineFunctionalElement, html} from 'element-vir';
|
|
345
|
-
import {assignWithCleanup} from '../functional-element/directives/assign-with-clean-up.directive';
|
|
349
|
+
import {assign, assignWithCleanup, defineFunctionalElement, html} from 'element-vir';
|
|
346
350
|
import {MySimpleWithPropsElement} from './my-simple-with-props.element';
|
|
347
351
|
|
|
348
352
|
export const MyAppWithPropsElement = defineFunctionalElement({
|
|
@@ -373,7 +377,13 @@ To require all child elements to be functional elements defined by this package,
|
|
|
373
377
|
<!-- example-link: src/readme-examples/require-functional-element.ts -->
|
|
374
378
|
|
|
375
379
|
```TypeScript
|
|
376
|
-
import {requireAllCustomElementsToBeFunctionalElement} from '
|
|
380
|
+
import {requireAllCustomElementsToBeFunctionalElement} from 'element-vir';
|
|
377
381
|
|
|
378
382
|
requireAllCustomElementsToBeFunctionalElement();
|
|
379
383
|
```
|
|
384
|
+
|
|
385
|
+
# Dev
|
|
386
|
+
|
|
387
|
+
## markdown out of date
|
|
388
|
+
|
|
389
|
+
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.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare type NonEmptyString<T> = T extends '' ? never : T;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -5,33 +5,30 @@ import { createPropertyDescriptorMap, createPropertyProxy, } from './element-pro
|
|
|
5
5
|
import { FunctionalElementBaseClass, } from './functional-element';
|
|
6
6
|
import { createRenderParams } from './render-callback';
|
|
7
7
|
export function defineFunctionalElement(functionalElementInit) {
|
|
8
|
-
var _a;
|
|
9
8
|
const eventsMap = createEventDescriptorMap(functionalElementInit.events);
|
|
10
|
-
const anonymousClass =
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
_a.props = createPropertyDescriptorMap(functionalElementInit.props),
|
|
34
|
-
_a);
|
|
9
|
+
const anonymousClass = class extends FunctionalElementBaseClass {
|
|
10
|
+
static tagName = functionalElementInit.tagName;
|
|
11
|
+
static styles = functionalElementInit.styles || css ``;
|
|
12
|
+
createRenderParams() {
|
|
13
|
+
return createRenderParams(this, eventsMap);
|
|
14
|
+
}
|
|
15
|
+
static events = eventsMap;
|
|
16
|
+
static renderCallback = functionalElementInit.renderCallback;
|
|
17
|
+
static props = createPropertyDescriptorMap(functionalElementInit.props);
|
|
18
|
+
render() {
|
|
19
|
+
return functionalElementInit.renderCallback(this.createRenderParams());
|
|
20
|
+
}
|
|
21
|
+
instanceProps = createPropertyProxy(functionalElementInit.props, this);
|
|
22
|
+
constructor() {
|
|
23
|
+
super();
|
|
24
|
+
const initProps = functionalElementInit.props || {};
|
|
25
|
+
Object.keys(initProps).forEach((propName) => {
|
|
26
|
+
const functionalElementInstance = this;
|
|
27
|
+
property()(functionalElementInstance, propName);
|
|
28
|
+
functionalElementInstance[propName] = initProps[propName];
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
};
|
|
35
32
|
window.customElements.define(functionalElementInit.tagName, anonymousClass);
|
|
36
33
|
return anonymousClass;
|
|
37
34
|
}
|
|
@@ -10,6 +10,9 @@ export function assignWithCleanup(propertyDescriptor, value, cleanupCallback) {
|
|
|
10
10
|
return assignWithCleanupDirective(propertyDescriptor.propName, value, cleanupCallback);
|
|
11
11
|
}
|
|
12
12
|
class AssignWithCleanupDirectiveClass extends AsyncDirective {
|
|
13
|
+
element;
|
|
14
|
+
lastValue;
|
|
15
|
+
lastCallback;
|
|
13
16
|
constructor(partInfo) {
|
|
14
17
|
super(partInfo);
|
|
15
18
|
this.element = extractFunctionalElement(partInfo, 'assign');
|
|
@@ -9,6 +9,7 @@ export function assign(propertyDescriptor, value) {
|
|
|
9
9
|
return assignDirective(propertyDescriptor.propName, value);
|
|
10
10
|
}
|
|
11
11
|
const assignDirective = directive(class extends Directive {
|
|
12
|
+
element;
|
|
12
13
|
constructor(partInfo) {
|
|
13
14
|
super(partInfo);
|
|
14
15
|
this.element = extractFunctionalElement(partInfo, 'assign');
|
|
@@ -1,28 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { DirectiveResult } from 'lit/directive.js';
|
|
2
|
+
import { DefinedTypedEvent, TypedEvent } from '../../typed-event/typed-event';
|
|
3
3
|
/**
|
|
4
|
-
* Listen to
|
|
4
|
+
* Listen to events. These can be native DOM events (use a string for the inputType argument) or
|
|
5
|
+
* typed events (pass in a return value from defineTypedEvent).
|
|
5
6
|
*
|
|
6
|
-
* @param
|
|
7
|
-
* MyFunctionalElement.events.eventName) or from a
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* is properly typed, the event given to this callback will also be typed.
|
|
7
|
+
* @param definedTypedEvent Needs to come either from a functional element (like
|
|
8
|
+
* MyFunctionalElement.events.eventName) or from a typed event created via the defineTypedEvent function.
|
|
9
|
+
* @param listener The callback to fire when an event is caught. Assuming the definedTypedEvent
|
|
10
|
+
* input is properly typed, the event given to this callback will also be typed.
|
|
11
11
|
*/
|
|
12
|
-
export declare function listen<
|
|
13
|
-
|
|
14
|
-
readonly element: HTMLElement;
|
|
15
|
-
lastListenerMetaData: ListenerMetaData<unknown> | undefined;
|
|
16
|
-
resetListener(listenerMetaData: ListenerMetaData<any>): void;
|
|
17
|
-
createListenerMetaData(eventType: string, callback: (event: ElementEvent<string, unknown>) => void): ListenerMetaData<unknown>;
|
|
18
|
-
render(eventObject: EventDescriptor<string, any>, callback: (event: ElementEvent<any, any>) => void): symbol;
|
|
19
|
-
readonly _$isConnected: boolean;
|
|
20
|
-
update(_part: import("lit-html").Part, props: unknown[]): unknown;
|
|
21
|
-
};
|
|
22
|
-
}>;
|
|
23
|
-
declare type ListenerMetaData<EventDetail> = {
|
|
24
|
-
eventType: string;
|
|
25
|
-
callback: (event: ElementEvent<string, EventDetail>) => void;
|
|
26
|
-
listener: (event: any) => void;
|
|
27
|
-
};
|
|
28
|
-
export {};
|
|
12
|
+
export declare function listen<TypedEventTypeNameGeneric extends string, TypedEventDetailGeneric, NativeElementEventNameGeneric extends keyof HTMLElementEventMap>(eventType: DefinedTypedEvent<TypedEventTypeNameGeneric, TypedEventDetailGeneric>, listener: (event: TypedEvent<TypedEventTypeNameGeneric, TypedEventDetailGeneric>) => void): DirectiveResult<any>;
|
|
13
|
+
export declare function listen<TypedEventTypeNameGeneric extends string, TypedEventDetailGeneric, NativeElementEventNameGeneric extends keyof HTMLElementEventMap>(eventType: NativeElementEventNameGeneric, listener: (event: HTMLElementEventMap[NativeElementEventNameGeneric]) => void): DirectiveResult<any>;
|
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
import { noChange } from 'lit';
|
|
2
2
|
import { directive, Directive } from 'lit/directive.js';
|
|
3
3
|
import { extractElement } from './directive-util';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*
|
|
7
|
-
* @param eventDescriptor Needs to come either from a functional element (like
|
|
8
|
-
* MyFunctionalElement.events.eventName) or from a custom element event created via the
|
|
9
|
-
* createCustomEvent function.
|
|
10
|
-
* @param listener The callback to fire when an event is caught. Assuming the eventDescriptor input
|
|
11
|
-
* is properly typed, the event given to this callback will also be typed.
|
|
12
|
-
*/
|
|
13
|
-
export function listen(eventDescriptor, listener) {
|
|
14
|
-
return listenDirective(eventDescriptor, listener);
|
|
4
|
+
export function listen(eventType, listener) {
|
|
5
|
+
return listenDirective(eventType, listener);
|
|
15
6
|
}
|
|
16
7
|
/**
|
|
17
8
|
* The directive generics here are not strong enough to maintain their values. Thus, the directive
|
|
18
9
|
* call is wrapped in the function above.
|
|
19
10
|
*/
|
|
20
11
|
const listenDirective = directive(class extends Directive {
|
|
12
|
+
element;
|
|
13
|
+
lastListenerMetaData;
|
|
21
14
|
constructor(partInfo) {
|
|
22
15
|
super(partInfo);
|
|
23
16
|
this.element = extractElement(partInfo, 'listen', HTMLElement);
|
|
@@ -33,11 +26,11 @@ const listenDirective = directive(class extends Directive {
|
|
|
33
26
|
return {
|
|
34
27
|
eventType,
|
|
35
28
|
callback,
|
|
36
|
-
listener: (event) =>
|
|
29
|
+
listener: (event) => this.lastListenerMetaData?.callback(event),
|
|
37
30
|
};
|
|
38
31
|
}
|
|
39
|
-
render(
|
|
40
|
-
const eventType =
|
|
32
|
+
render(eventTypeInput, callback) {
|
|
33
|
+
const eventType = typeof eventTypeInput === 'string' ? eventTypeInput : eventTypeInput.type;
|
|
41
34
|
if (typeof eventType !== 'string') {
|
|
42
35
|
throw new Error(`Cannot listen to an event with a name that is not a string. Given event name: "${eventType}"`);
|
|
43
36
|
}
|
|
@@ -3,6 +3,7 @@ import { assertsIsElementPartInfo } from './directive-util';
|
|
|
3
3
|
const directiveName = 'onDomCreated';
|
|
4
4
|
/** Only fires once, when the element has been created. */
|
|
5
5
|
export const onDomCreated = directive(class extends Directive {
|
|
6
|
+
element;
|
|
6
7
|
constructor(partInfo) {
|
|
7
8
|
super(partInfo);
|
|
8
9
|
assertsIsElementPartInfo(partInfo, directiveName);
|
|
@@ -11,8 +12,8 @@ export const onDomCreated = directive(class extends Directive {
|
|
|
11
12
|
assertsIsElementPartInfo(partInfo, directiveName);
|
|
12
13
|
const newElement = partInfo.element;
|
|
13
14
|
if (newElement !== this.element) {
|
|
14
|
-
// use
|
|
15
|
-
|
|
15
|
+
// use requestAnimationFrame here so it can fire property changes outside of a render loop
|
|
16
|
+
requestAnimationFrame(() => callback(newElement));
|
|
16
17
|
this.element = newElement;
|
|
17
18
|
}
|
|
18
19
|
return this.render(callback);
|
|
@@ -2,19 +2,20 @@ import { directive, Directive } from 'lit/directive.js';
|
|
|
2
2
|
import { assertsIsElementPartInfo } from './directive-util';
|
|
3
3
|
const directiveName = 'onResize';
|
|
4
4
|
export const onResize = directive(class extends Directive {
|
|
5
|
+
element;
|
|
6
|
+
resizeObserver = new ResizeObserver((entries) => this.fireCallback(entries));
|
|
7
|
+
callback;
|
|
5
8
|
constructor(partInfo) {
|
|
6
9
|
super(partInfo);
|
|
7
|
-
this.resizeObserver = new ResizeObserver((entries) => this.fireCallback(entries));
|
|
8
10
|
assertsIsElementPartInfo(partInfo, directiveName);
|
|
9
11
|
}
|
|
10
12
|
fireCallback(entries) {
|
|
11
|
-
var _a;
|
|
12
13
|
const resizeEntry = entries[0];
|
|
13
14
|
if (!resizeEntry) {
|
|
14
15
|
console.error(entries);
|
|
15
16
|
throw new Error(`${directiveName} observation triggered but the first entry was empty.`);
|
|
16
17
|
}
|
|
17
|
-
|
|
18
|
+
this.callback?.({ target: resizeEntry.target, contentRect: resizeEntry.contentRect });
|
|
18
19
|
}
|
|
19
20
|
update(partInfo, [callback]) {
|
|
20
21
|
assertsIsElementPartInfo(partInfo, directiveName);
|
|
@@ -1,29 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
export declare type
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export declare function eventInit<T>(): new () => ElementEvent<string, T>;
|
|
6
|
-
export declare type EventInitInfo<EventNameGeneric extends string> = {
|
|
7
|
-
eventName: EventNameGeneric;
|
|
8
|
-
};
|
|
9
|
-
export declare class ElementEvent<EventName extends string, EventValue> extends CustomEvent<EventValue> {
|
|
10
|
-
readonly eventInitInfo: EventInitInfo<EventName>;
|
|
11
|
-
readonly eventName: string;
|
|
12
|
-
constructor(eventInitInfo: EventInitInfo<EventName>, initDetail: EventValue);
|
|
13
|
-
}
|
|
14
|
-
export declare function defineCustomEvent<EventName extends string, EventValue>(eventName: EventName): (new (eventValue: EventValue) => ElementEvent<EventName, EventValue>) & EventDescriptor<EventName, EventValue>;
|
|
15
|
-
export declare type EventExtraProperties<DetailType> = {
|
|
16
|
-
/**
|
|
17
|
-
* The event constructor property is needed in order to store sufficient type data for event
|
|
18
|
-
* listeners to work.
|
|
19
|
-
*/
|
|
20
|
-
eventConstructor: new (outputObject: EventDescriptor<string, DetailType>, initDetail: DetailType) => ElementEvent<string, DetailType>;
|
|
21
|
-
};
|
|
22
|
-
export declare type EventObjectEventDetailExtractor<EventObjectGeneric extends EventDescriptor<any, any>> = EventObjectGeneric extends EventDescriptor<string, infer R> ? R : never;
|
|
23
|
-
export declare type ElementEventDetailExtractor<ElementEventGeneric extends ElementEvent<any, any>> = ElementEventGeneric extends ElementEvent<string, infer R> ? R : never;
|
|
24
|
-
export declare type EventInitMapEventDetailExtractor<Property extends keyof EventsInitGeneric, EventsInitGeneric extends EventsInitMap> = InstanceType<EventsInitGeneric[Property]> extends ElementEvent<string, infer R> ? R : never;
|
|
25
|
-
export declare type EventDescriptor<EventNameGeneric extends string, DetailType> = EventInitInfo<EventNameGeneric> & EventExtraProperties<DetailType>;
|
|
1
|
+
import { DefinedTypedEvent, DefinedTypedEventNameDefinition, TypedEvent } from '../typed-event/typed-event';
|
|
2
|
+
export declare type EventsInitMap = Record<string, DefinedTypedEventNameDefinition<any>>;
|
|
3
|
+
export declare function defineElementEvent<EventDetailGeneric>(): DefinedTypedEventNameDefinition<EventDetailGeneric>;
|
|
4
|
+
export declare type EventInitMapEventDetailExtractor<EventTypeNameGeneric extends keyof EventsInitGeneric, EventsInitGeneric extends EventsInitMap> = EventsInitGeneric[EventTypeNameGeneric] extends DefinedTypedEventNameDefinition<infer R> ? R : never;
|
|
26
5
|
export declare type EventDescriptorMap<EventsInitGeneric extends EventsInitMap> = {
|
|
27
|
-
[
|
|
6
|
+
[CurrentEventTypeName in keyof EventsInitGeneric]: DefinedTypedEvent<CurrentEventTypeName extends string ? CurrentEventTypeName : never, EventInitMapEventDetailExtractor<CurrentEventTypeName, EventsInitGeneric>>;
|
|
28
7
|
};
|
|
8
|
+
export declare type EventObjectEventDetailExtractor<EventObjectGeneric extends DefinedTypedEvent<any, any>> = EventObjectGeneric extends DefinedTypedEvent<string, infer R> ? R : never;
|
|
9
|
+
export declare type ElementEventDetailExtractor<ElementEventGeneric extends TypedEvent<any, any>> = ElementEventGeneric extends TypedEvent<string, infer R> ? R : never;
|
|
29
10
|
export declare function createEventDescriptorMap<EventsInitGeneric extends EventsInitMap>(eventsInit: EventsInitGeneric | undefined): EventDescriptorMap<EventsInitGeneric>;
|
|
@@ -1,45 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
return customEventElement;
|
|
5
|
-
}
|
|
6
|
-
export class ElementEvent extends CustomEvent {
|
|
7
|
-
constructor(eventInitInfo, initDetail) {
|
|
8
|
-
super(String(eventInitInfo.eventName), { detail: initDetail, bubbles: true, composed: true });
|
|
9
|
-
this.eventInitInfo = eventInitInfo;
|
|
10
|
-
this.eventName = String(this.eventInitInfo.eventName);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
export function defineCustomEvent(eventName) {
|
|
14
|
-
var _a;
|
|
15
|
-
return _a = class extends ElementEvent {
|
|
16
|
-
constructor(eventValue) {
|
|
17
|
-
super({ eventName: eventName }, eventValue);
|
|
18
|
-
window.ElementEvent = ElementEvent;
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
_a.eventName = eventName,
|
|
22
|
-
// this allows sub classes of ElementEvent to be directly listened to
|
|
23
|
-
_a.eventConstructor = ElementEvent.constructor,
|
|
24
|
-
_a;
|
|
1
|
+
import { defineTypedEvent, } from '../typed-event/typed-event';
|
|
2
|
+
export function defineElementEvent() {
|
|
3
|
+
return defineTypedEvent();
|
|
25
4
|
}
|
|
26
5
|
export function createEventDescriptorMap(eventsInit) {
|
|
27
6
|
if (!eventsInit) {
|
|
28
7
|
return {};
|
|
29
8
|
}
|
|
30
9
|
return Object.keys(eventsInit)
|
|
31
|
-
.filter((
|
|
32
|
-
if (typeof
|
|
33
|
-
throw new Error(`Expected event key of type string but got type "${typeof
|
|
10
|
+
.filter((currentElementEventKey) => {
|
|
11
|
+
if (typeof currentElementEventKey !== 'string') {
|
|
12
|
+
throw new Error(`Expected event key of type string but got type "${typeof currentElementEventKey}" for key ${currentElementEventKey}`);
|
|
13
|
+
}
|
|
14
|
+
if (currentElementEventKey === '') {
|
|
15
|
+
throw new Error(`Got empty string for events key.`);
|
|
34
16
|
}
|
|
35
17
|
return true;
|
|
36
18
|
})
|
|
37
|
-
.reduce((accum,
|
|
38
|
-
const eventObject =
|
|
39
|
-
|
|
40
|
-
eventConstructor: eventsInit[currentKey],
|
|
41
|
-
};
|
|
42
|
-
accum[currentKey] = eventObject;
|
|
19
|
+
.reduce((accum, currentElementEventKey) => {
|
|
20
|
+
const eventObject = defineTypedEvent()(currentElementEventKey);
|
|
21
|
+
accum[currentElementEventKey] = eventObject;
|
|
43
22
|
return accum;
|
|
44
23
|
}, {});
|
|
45
24
|
}
|
|
@@ -20,7 +20,6 @@ export declare type FunctionalElementInit<PropertyInitGeneric extends PropertyIn
|
|
|
20
20
|
export declare abstract class FunctionalElementBaseClass<PropertyInitGeneric extends PropertyInitMapBase> extends LitElement {
|
|
21
21
|
static readonly tagName: string;
|
|
22
22
|
static readonly styles: CSSResult;
|
|
23
|
-
static readonly propNames: string[];
|
|
24
23
|
abstract render(): TemplateResult | Promise<TemplateResult>;
|
|
25
24
|
abstract readonly instanceProps: PropertyInitGeneric;
|
|
26
25
|
}
|
|
@@ -37,5 +36,4 @@ export declare type ExtraStaticFunctionalElementProperties<PropertyInitGeneric e
|
|
|
37
36
|
*/
|
|
38
37
|
tagName: string;
|
|
39
38
|
styles: CSSResult;
|
|
40
|
-
propNames: string[];
|
|
41
39
|
}>;
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { TemplateResult } from 'lit';
|
|
2
|
-
import {
|
|
2
|
+
import { TypedEvent } from '../typed-event/typed-event';
|
|
3
|
+
import { EventDescriptorMap, EventInitMapEventDetailExtractor, EventsInitMap } from './element-events';
|
|
3
4
|
import { PropertyInitMapBase } from './element-properties';
|
|
4
5
|
import { FunctionalElementInstance } from './functional-element';
|
|
5
6
|
export declare type RenderCallback<PropertyInitGeneric extends PropertyInitMapBase, EventsInitGeneric extends EventsInitMap> = (params: RenderParams<PropertyInitGeneric, EventsInitGeneric>) => TemplateResult | Promise<TemplateResult>;
|
|
6
7
|
export declare type RenderParams<PropertyInitGeneric extends PropertyInitMapBase, EventsInitGeneric extends EventsInitMap> = {
|
|
7
8
|
props: PropertyInitGeneric;
|
|
8
9
|
events: EventDescriptorMap<EventsInitGeneric>;
|
|
9
|
-
|
|
10
|
+
host: FunctionalElementInstance<PropertyInitGeneric>;
|
|
11
|
+
dispatch: <EventTypeNameGeneric extends keyof EventsInitGeneric>(event: TypedEvent<EventTypeNameGeneric extends string ? EventTypeNameGeneric : never, EventInitMapEventDetailExtractor<EventTypeNameGeneric, EventsInitGeneric>>) => boolean;
|
|
10
12
|
/**
|
|
11
13
|
* Same as dispatchElementEvent but without the extra types. This allows you to emit any events,
|
|
12
14
|
* even events from other custom elements.
|
|
13
15
|
*/
|
|
14
|
-
|
|
16
|
+
genericDispatch: (event: Event) => boolean;
|
|
15
17
|
};
|
|
16
18
|
export declare function createRenderParams<PropertyInitGeneric extends PropertyInitMapBase, EventsInitGeneric extends EventsInitMap>(element: FunctionalElementInstance<PropertyInitGeneric>, eventsMap: EventDescriptorMap<EventsInitGeneric>): RenderParams<PropertyInitGeneric, EventsInitGeneric>;
|
|
@@ -4,8 +4,9 @@ export function createRenderParams(element, eventsMap) {
|
|
|
4
4
|
* These two dispatch properties do the same thing but their interfaces are different.
|
|
5
5
|
* DispatchEvent's type interface is much stricter.
|
|
6
6
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
dispatch: (event) => element.dispatchEvent(event),
|
|
8
|
+
genericDispatch: (event) => element.dispatchEvent(event),
|
|
9
|
+
host: element,
|
|
9
10
|
props: element.instanceProps,
|
|
10
11
|
events: eventsMap,
|
|
11
12
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ export * from './functional-element/define-functional-element';
|
|
|
2
2
|
export * from './functional-element/directives/assign-with-clean-up.directive';
|
|
3
3
|
export * from './functional-element/directives/assign.directive';
|
|
4
4
|
export * from './functional-element/directives/listen.directive';
|
|
5
|
-
export * from './functional-element/directives/named-listen.directive';
|
|
6
5
|
export * from './functional-element/directives/on-dom-created.directive';
|
|
7
6
|
export * from './functional-element/directives/on-resize.directive';
|
|
8
7
|
export * from './functional-element/element-events';
|
|
@@ -12,3 +11,4 @@ export * from './functional-element/render-callback';
|
|
|
12
11
|
export { requireAllCustomElementsToBeFunctionalElement } from './require-functional-element';
|
|
13
12
|
export * from './template-transforms/vir-css/vir-css';
|
|
14
13
|
export * from './template-transforms/vir-html/vir-html';
|
|
14
|
+
export * from './typed-event/typed-event';
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,6 @@ export * from './functional-element/define-functional-element';
|
|
|
2
2
|
export * from './functional-element/directives/assign-with-clean-up.directive';
|
|
3
3
|
export * from './functional-element/directives/assign.directive';
|
|
4
4
|
export * from './functional-element/directives/listen.directive';
|
|
5
|
-
export * from './functional-element/directives/named-listen.directive';
|
|
6
5
|
export * from './functional-element/directives/on-dom-created.directive';
|
|
7
6
|
export * from './functional-element/directives/on-resize.directive';
|
|
8
7
|
export * from './functional-element/element-events';
|
|
@@ -12,3 +11,4 @@ export * from './functional-element/render-callback';
|
|
|
12
11
|
export { requireAllCustomElementsToBeFunctionalElement } from './require-functional-element';
|
|
13
12
|
export * from './template-transforms/vir-css/vir-css';
|
|
14
13
|
export * from './template-transforms/vir-html/vir-html';
|
|
14
|
+
export * from './typed-event/typed-event';
|
|
@@ -17,7 +17,7 @@ export function makeCheckTransform(name, check, transform) {
|
|
|
17
17
|
const transformedTemplateStrings = new WeakMap();
|
|
18
18
|
export function getTransformedTemplate(templateStringsKey, values, fallbackTransform) {
|
|
19
19
|
const alreadyTransformedTemplateStrings = transformedTemplateStrings.get(templateStringsKey);
|
|
20
|
-
const templateTransform = alreadyTransformedTemplateStrings
|
|
20
|
+
const templateTransform = alreadyTransformedTemplateStrings ?? fallbackTransform();
|
|
21
21
|
if (!alreadyTransformedTemplateStrings) {
|
|
22
22
|
transformedTemplateStrings.set(templateStringsKey, templateTransform);
|
|
23
23
|
}
|
|
@@ -29,7 +29,6 @@ export function transformTemplate(inputTemplateStrings, inputValues, checksAndTr
|
|
|
29
29
|
const newRaws = [];
|
|
30
30
|
const valueDeletions = [];
|
|
31
31
|
inputTemplateStrings.forEach((currentTemplateString, index) => {
|
|
32
|
-
var _a;
|
|
33
32
|
const lastNewStringsIndex = newStrings.length - 1;
|
|
34
33
|
const lastNewString = newStrings[lastNewStringsIndex];
|
|
35
34
|
const currentValueIndex = index - 1;
|
|
@@ -37,9 +36,9 @@ export function transformTemplate(inputTemplateStrings, inputValues, checksAndTr
|
|
|
37
36
|
let validTransform;
|
|
38
37
|
assertValidString && assertValidString(currentTemplateString);
|
|
39
38
|
if (typeof lastNewString === 'string') {
|
|
40
|
-
validTransform =
|
|
39
|
+
validTransform = checksAndTransforms.find((checkAndTransform) => {
|
|
41
40
|
return checkAndTransform.check(lastNewString, currentTemplateString, currentValue);
|
|
42
|
-
})
|
|
41
|
+
})?.transform;
|
|
43
42
|
if (validTransform) {
|
|
44
43
|
newStrings[lastNewStringsIndex] =
|
|
45
44
|
lastNewString + validTransform(currentValue) + currentTemplateString;
|
|
@@ -5,7 +5,7 @@ import { makeCheckTransform, transformTemplate, } from '../transform-template';
|
|
|
5
5
|
const htmlChecksAndTransforms = [
|
|
6
6
|
makeCheckTransform('tag name interpolation', (lastNewString, currentLitString, currentValue) => {
|
|
7
7
|
const shouldHaveTagNameHere = (lastNewString.trim().endsWith('<') && !!currentLitString.match(/^[\s\n>]/)) ||
|
|
8
|
-
(
|
|
8
|
+
(lastNewString?.trim().endsWith('</') && currentLitString.trim().startsWith('>'));
|
|
9
9
|
const staticTagName = hasStaticTagName(currentValue);
|
|
10
10
|
if (shouldHaveTagNameHere && !staticTagName) {
|
|
11
11
|
console.error({
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { NonEmptyString } from '../augments/type';
|
|
2
|
+
export declare class TypedEvent<EventTypeNameGeneric extends string = '', EventDetailGeneric = undefined> extends CustomEvent<EventDetailGeneric> {
|
|
3
|
+
readonly _type: EventTypeNameGeneric;
|
|
4
|
+
get type(): EventTypeNameGeneric;
|
|
5
|
+
constructor(type: EventTypeNameGeneric | {
|
|
6
|
+
type: EventTypeNameGeneric;
|
|
7
|
+
}, value: EventDetailGeneric);
|
|
8
|
+
}
|
|
9
|
+
export declare type DefinedTypedEventNameDefinition<EventDetailGeneric> = <EventTypeNameGeneric extends string>(eventType: NonEmptyString<EventTypeNameGeneric>) => DefinedTypedEvent<EventTypeNameGeneric, EventDetailGeneric>;
|
|
10
|
+
export declare type DefinedTypedEvent<EventTypeNameGeneric extends string, EventDetailGeneric> = (new (eventValue: EventDetailGeneric) => TypedEvent<EventTypeNameGeneric, EventDetailGeneric>) & {
|
|
11
|
+
type: EventTypeNameGeneric;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Defined a typed event. Make sure to use currying and call this function twice! Typescript's
|
|
15
|
+
* generic restrictions require this setup to get the types right without excessive verbosity.
|
|
16
|
+
*
|
|
17
|
+
* Example: const myCustomEvent = defineTypedEvent<number>()('my-custom-event')
|
|
18
|
+
*/
|
|
19
|
+
export declare function defineTypedEvent<EventDetailGeneric>(): <EventTypeNameGeneric extends string>(eventType: NonEmptyString<EventTypeNameGeneric>) => DefinedTypedEvent<EventTypeNameGeneric, EventDetailGeneric>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export class TypedEvent extends CustomEvent {
|
|
2
|
+
_type = '';
|
|
3
|
+
get type() {
|
|
4
|
+
return this._type;
|
|
5
|
+
}
|
|
6
|
+
constructor(type, value) {
|
|
7
|
+
super(typeof type === 'string' ? type : type.type, {
|
|
8
|
+
detail: value,
|
|
9
|
+
bubbles: true,
|
|
10
|
+
composed: true,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Defined a typed event. Make sure to use currying and call this function twice! Typescript's
|
|
16
|
+
* generic restrictions require this setup to get the types right without excessive verbosity.
|
|
17
|
+
*
|
|
18
|
+
* Example: const myCustomEvent = defineTypedEvent<number>()('my-custom-event')
|
|
19
|
+
*/
|
|
20
|
+
export function defineTypedEvent() {
|
|
21
|
+
return (eventType) => {
|
|
22
|
+
return class extends TypedEvent {
|
|
23
|
+
static type = eventType;
|
|
24
|
+
_type = eventType;
|
|
25
|
+
constructor(value) {
|
|
26
|
+
super(eventType, value);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "element-vir",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.2",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"custom",
|
|
6
6
|
"web",
|
|
@@ -25,23 +25,28 @@
|
|
|
25
25
|
"main": "dist/index.js",
|
|
26
26
|
"types": "dist/index.d.ts",
|
|
27
27
|
"scripts": {
|
|
28
|
-
"compile": "
|
|
28
|
+
"compile": "rm -rf dist && tsc",
|
|
29
29
|
"format": "virmator format write",
|
|
30
|
-
"
|
|
30
|
+
"jest": "jest --config ./src/jest/jest.config.ts",
|
|
31
|
+
"prepublishOnly": "npm run compile && npm run test:full",
|
|
31
32
|
"spellcheck": "virmator spellcheck",
|
|
32
|
-
"start": "npm install &&
|
|
33
|
-
"test": "npm run
|
|
33
|
+
"start": "npm install && vite --force --config ./src/vite/vite.config.ts",
|
|
34
|
+
"test": "npm run type-check && npm run jest",
|
|
34
35
|
"test:full": "npm test && npm run spellcheck && virmator format check && npm run update-docs -- --check",
|
|
35
|
-
"
|
|
36
|
+
"type-check": "tsc --noEmit",
|
|
37
|
+
"update-docs": "virmator code-in-markdown README.md --index src/index.ts"
|
|
36
38
|
},
|
|
37
39
|
"dependencies": {
|
|
38
|
-
"augment-vir": "^1.4.
|
|
39
|
-
"lit": "^2.
|
|
40
|
+
"augment-vir": "^1.4.3",
|
|
41
|
+
"lit": "^2.1.1"
|
|
40
42
|
},
|
|
41
43
|
"devDependencies": {
|
|
42
|
-
"@
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
44
|
+
"@types/jest": "^27.4.0",
|
|
45
|
+
"jest": "^27.4.7",
|
|
46
|
+
"ts-jest": "^27.1.3",
|
|
47
|
+
"ts-node": "^10.4.0",
|
|
48
|
+
"typescript": "^4.5.4",
|
|
49
|
+
"virmator": "^1.3.7",
|
|
50
|
+
"vite": "^2.7.12"
|
|
46
51
|
}
|
|
47
52
|
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { DirectiveResult } from 'lit/directive.js';
|
|
2
|
-
/**
|
|
3
|
-
* Listen to events. First parameter is just a string for an event name/type, vs. the more
|
|
4
|
-
* complicated first parameter type for the "listen" directive. If the given eventName is a native
|
|
5
|
-
* DOM event, the event type given to the callback will be the event type associated with that
|
|
6
|
-
* native event type. Otherwise, specific event types given to the callback are unknown.
|
|
7
|
-
*
|
|
8
|
-
* If you are listening to your custom events, it is better to use the "listen" directive directly
|
|
9
|
-
* so your callbacks are more tightly typed.
|
|
10
|
-
*
|
|
11
|
-
* @param eventName Name of the event to listen to.
|
|
12
|
-
* @param listener The callback to fire when an event is caught.
|
|
13
|
-
*/
|
|
14
|
-
export declare function namedListen<EventName extends keyof HTMLElementEventMap>(eventName: EventName, listener: (event: HTMLElementEventMap[EventName]) => void): DirectiveResult;
|
|
15
|
-
export declare function namedListen<EventName extends string>(eventName: EventName, listener: (event: Event) => void): DirectiveResult;
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { noChange } from 'lit';
|
|
2
|
-
import { directive, Directive } from 'lit/directive.js';
|
|
3
|
-
import { extractElement } from './directive-util';
|
|
4
|
-
export function namedListen(eventName, listener) {
|
|
5
|
-
return listenDirective(eventName, listener);
|
|
6
|
-
}
|
|
7
|
-
const listenDirective = directive(class extends Directive {
|
|
8
|
-
constructor(partInfo) {
|
|
9
|
-
super(partInfo);
|
|
10
|
-
this.element = extractElement(partInfo, 'listen', HTMLElement);
|
|
11
|
-
}
|
|
12
|
-
resetListener(listenerMetaData) {
|
|
13
|
-
if (this.lastListenerMetaData) {
|
|
14
|
-
this.element.removeEventListener(this.lastListenerMetaData.eventType, this.lastListenerMetaData.listener);
|
|
15
|
-
}
|
|
16
|
-
this.element.addEventListener(listenerMetaData.eventType, listenerMetaData.listener);
|
|
17
|
-
this.lastListenerMetaData = listenerMetaData;
|
|
18
|
-
}
|
|
19
|
-
createListenerMetaData(eventType, callback) {
|
|
20
|
-
return {
|
|
21
|
-
eventType,
|
|
22
|
-
callback,
|
|
23
|
-
listener: (event) => { var _a; return (_a = this.lastListenerMetaData) === null || _a === void 0 ? void 0 : _a.callback(event); },
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
render(eventName, callback) {
|
|
27
|
-
if (typeof eventName !== 'string') {
|
|
28
|
-
throw new Error(`Cannot listen to an event with a name that is not a string. Given event name: "${eventName}"`);
|
|
29
|
-
}
|
|
30
|
-
if (this.lastListenerMetaData && this.lastListenerMetaData.eventType === eventName) {
|
|
31
|
-
/**
|
|
32
|
-
* Store the callback here so we don't have to update the attached listener every
|
|
33
|
-
* time the callback is updated.
|
|
34
|
-
*/
|
|
35
|
-
this.lastListenerMetaData.callback = callback;
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
this.resetListener(this.createListenerMetaData(eventName, callback));
|
|
39
|
-
}
|
|
40
|
-
return noChange;
|
|
41
|
-
}
|
|
42
|
-
});
|