element-vir 6.1.6 → 6.2.1

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 CHANGED
@@ -1,5 +1,7 @@
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
+
3
5
  Heroic. Reactive. Declarative. Type safe. Web components without compromise.
4
6
 
5
7
  No need for an extra build step,<br>
@@ -11,8 +13,6 @@ _**It's just TypeScript.**_
11
13
 
12
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).
13
15
 
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
-
16
16
  [Works in every major web browser except Internet Explorer.](https://caniuse.com/mdn-api_window_customelements)
17
17
 
18
18
  <sub>\*okay I hope it's obvious that functions are native</sub>
@@ -29,13 +29,13 @@ 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 [`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.
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.
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 `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:
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:
39
39
 
40
40
  <!-- example-link: src/readme-examples/my-simple.element.ts -->
41
41
 
@@ -201,6 +201,40 @@ export const MyAppWithAssignmentElement = defineElementNoInputs({
201
201
  });
202
202
  ```
203
203
 
204
+ ## Other callbacks
205
+
206
+ There are two other callbacks you can define that are sort of similar to lifecycle callbacks. They are much simpler than lifecycle callbacks however.
207
+
208
+ - `initCallback`: called right before the first render, has all state and inputs setup.
209
+ - `cleanupCallback`: called when an element is removed from the DOM. (This is the same as the `disconnectedCallback` in standard HTMLElement classes.)
210
+
211
+ <!-- example-link: src/readme-examples/my-app-with-cleanup-callback.element.ts -->
212
+
213
+ ```TypeScript
214
+ import {defineElementNoInputs, html} from 'element-vir';
215
+
216
+ export const MyAppWithAssignmentCleanupCallbackElement = defineElementNoInputs({
217
+ tagName: 'my-app-with-cleanup-callback',
218
+ stateInit: {
219
+ intervalId: undefined as undefined | number,
220
+ },
221
+ initCallback: ({updateState}) => {
222
+ updateState({
223
+ intervalId: window.setInterval(() => console.log('hi'), 1000),
224
+ });
225
+ },
226
+ renderCallback: () => html`
227
+ <h1>My App</h1>
228
+ `,
229
+ cleanupCallback: ({state, updateState}) => {
230
+ window.clearInterval(state.intervalId);
231
+ updateState({
232
+ intervalId: undefined,
233
+ });
234
+ },
235
+ });
236
+ ```
237
+
204
238
  ## Element events (outputs)
205
239
 
206
240
  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.
@@ -325,7 +359,8 @@ export const MyAppWithHostClasses = defineElementNoInputs({
325
359
  hostClasses: {
326
360
  /**
327
361
  * Setting the value to false means this host class will not ever automatically be applied.
328
- * It will simply be a static member on the element for manual application in consumers when desired.
362
+ * It will simply be a static member on the element for manual application in consumers when
363
+ * desired.
329
364
  */
330
365
  styleVariationA: false,
331
366
  /**
@@ -29,5 +29,6 @@ export declare type DeclarativeElementInit<InputsGeneric extends PropertyInitMap
29
29
  /** Called as part of the first renderCallback call, before the first renderCallback call. */
30
30
  initCallback?: InitCallback<InputsGeneric, StateInit, EventsInitGeneric, HostClassKeys, CssVarKeys>;
31
31
  renderCallback: RenderCallback<InputsGeneric, StateInit, EventsInitGeneric, HostClassKeys, CssVarKeys>;
32
+ cleanupCallback?: InitCallback<InputsGeneric, StateInit, EventsInitGeneric, HostClassKeys, CssVarKeys>;
32
33
  options?: Partial<DeclarativeElementDefinitionOptions> | undefined;
33
34
  };
@@ -83,6 +83,14 @@ export function defineElementNoInputs(initInput) {
83
83
  });
84
84
  return renderResult;
85
85
  }
86
+ disconnectedCallback() {
87
+ super.disconnectedCallback();
88
+ if (initInput.cleanupCallback) {
89
+ const renderParams = this.createRenderParams();
90
+ initInput.cleanupCallback(renderParams);
91
+ }
92
+ this.initCalled = false;
93
+ }
86
94
  assignInputs(inputs) {
87
95
  getObjectTypedKeys(inputs).forEach((key) => {
88
96
  property()(this, key);
@@ -0,0 +1,18 @@
1
+ export declare type MaybePromise<T> = (T extends Promise<infer ValueType> ? T | ValueType : Promise<T> | T) | undefined | {
2
+ error: Error;
3
+ };
4
+ export declare type CreateStateUpdatingPromiseInputs<InnerValueGeneric, KeyGeneric extends PropertyKey, StateGeneric extends Readonly<Record<KeyGeneric, MaybePromise<InnerValueGeneric>>>> = {
5
+ updateState: (newState: Partial<StateGeneric>) => void;
6
+ stateKey: KeyGeneric;
7
+ } & ({
8
+ createPromiseCallback: () => Promise<InnerValueGeneric>;
9
+ promise?: undefined;
10
+ } | {
11
+ promise: Promise<InnerValueGeneric>;
12
+ createPromiseCallback?: undefined;
13
+ });
14
+ export declare function createStateUpdatingPromiseIfUndefined<InnerValueGeneric, KeyGeneric extends PropertyKey, StateGeneric extends Readonly<Record<KeyGeneric, MaybePromise<InnerValueGeneric>>>>(inputs: CreateStateUpdatingPromiseInputs<InnerValueGeneric, KeyGeneric, StateGeneric> & {
15
+ state: StateGeneric;
16
+ }): void;
17
+ export declare function awaiting<ValueGeneric, FallbackGeneric, CallbackReturnGeneric>(input: MaybePromise<ValueGeneric>, notResolvedYetFallback: FallbackGeneric, resolvedCallback: (resolved: ValueGeneric) => CallbackReturnGeneric): CallbackReturnGeneric | FallbackGeneric | Error | undefined;
18
+ export declare function ensureError(input: unknown): Error;
@@ -0,0 +1,42 @@
1
+ import { extractErrorMessage, isPromiseLike, typedHasProperty } from 'augment-vir';
2
+ export function createStateUpdatingPromiseIfUndefined(inputs) {
3
+ const { state, stateKey } = inputs;
4
+ const currentValue = state[stateKey];
5
+ if (currentValue === undefined) {
6
+ createStateUpdatingPromise(inputs);
7
+ }
8
+ }
9
+ function createStateUpdatingPromise({ updateState, stateKey, createPromiseCallback: promiseCallback, promise, }) {
10
+ const output = promise !== null && promise !== void 0 ? promise : promiseCallback();
11
+ // as casts below are required because, even though all the generics agree, TypeScript can't figure that out here
12
+ if (output instanceof Promise) {
13
+ output
14
+ .then((result) => {
15
+ updateState({ [stateKey]: result });
16
+ })
17
+ .catch((thrownError) => {
18
+ const guaranteedError = ensureError(thrownError);
19
+ updateState({ [stateKey]: { error: guaranteedError } });
20
+ });
21
+ }
22
+ updateState({ [stateKey]: output });
23
+ }
24
+ export function awaiting(input, notResolvedYetFallback, resolvedCallback) {
25
+ if (isPromiseLike(input) || input == undefined) {
26
+ return notResolvedYetFallback;
27
+ }
28
+ else if (typedHasProperty(input, 'error')) {
29
+ return input.error;
30
+ }
31
+ else {
32
+ return resolvedCallback(input);
33
+ }
34
+ }
35
+ export function ensureError(input) {
36
+ if (input instanceof Error) {
37
+ return input;
38
+ }
39
+ else {
40
+ return new Error(extractErrorMessage(input));
41
+ }
42
+ }
@@ -5,7 +5,8 @@ import { DefinedTypedEvent, TypedEvent } from '../../typed-event/typed-event';
5
5
  * typed events (pass in a return value from defineTypedEvent).
6
6
  *
7
7
  * @param definedTypedEvent Needs to come either from a declarative element (like
8
- * MyDeclarativeElement.events.eventName) or from a typed event created via the defineTypedEvent function.
8
+ * MyDeclarativeElement.events.eventName) or from a typed event created via the defineTypedEvent
9
+ * function.
9
10
  * @param listener The callback to fire when an event is caught. Assuming the definedTypedEvent
10
11
  * input is properly typed, the event given to this callback will also be typed.
11
12
  */
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ export * from './declarative-element/define-element-no-inputs';
4
4
  export type { DeclarativeElementDefinitionOptions } from './declarative-element/definition-options';
5
5
  export * from './declarative-element/directives/assign-with-clean-up.directive';
6
6
  export * from './declarative-element/directives/assign.directive';
7
+ export * from './declarative-element/directives/awaiting.directive';
7
8
  export * from './declarative-element/directives/directive-helpers';
8
9
  export * from './declarative-element/directives/listen.directive';
9
10
  export * from './declarative-element/directives/on-dom-created.directive';
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ export * from './declarative-element/define-element';
3
3
  export * from './declarative-element/define-element-no-inputs';
4
4
  export * from './declarative-element/directives/assign-with-clean-up.directive';
5
5
  export * from './declarative-element/directives/assign.directive';
6
+ export * from './declarative-element/directives/awaiting.directive';
6
7
  export * from './declarative-element/directives/directive-helpers';
7
8
  export * from './declarative-element/directives/listen.directive';
8
9
  export * from './declarative-element/directives/on-dom-created.directive';
@@ -1,8 +1,8 @@
1
- import { typedHasOwnProperties } from 'augment-vir';
1
+ import { typedHasProperties } from 'augment-vir';
2
2
  import { DeclarativeElementMarkerSymbol } from '../declarative-element-marker-symbol';
3
3
  function extractElementKeys(values) {
4
4
  return values.filter((value) => {
5
- return (typedHasOwnProperties(value, [
5
+ return (typedHasProperties(value, [
6
6
  'tagName',
7
7
  DeclarativeElementMarkerSymbol,
8
8
  ]) &&
@@ -1,4 +1,4 @@
1
- import { typedHasOwnProperties } from 'augment-vir';
1
+ import { typedHasProperties } from 'augment-vir';
2
2
  import { filterOutArrayIndexes } from '../augments/array';
3
3
  import { DeclarativeElementMarkerSymbol } from '../declarative-element-marker-symbol';
4
4
  import { getAlreadyMappedTemplate, setMappedTemplate } from './nested-mapped-templates';
@@ -20,7 +20,7 @@ export function makeCheckTransform(name, check, transform) {
20
20
  const transformedTemplateStrings = new WeakMap();
21
21
  function extractElementValues(values) {
22
22
  return values.filter((value) => {
23
- return (typedHasOwnProperties(value, [
23
+ return (typedHasProperties(value, [
24
24
  'tagName',
25
25
  DeclarativeElementMarkerSymbol,
26
26
  ]) &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "element-vir",
3
- "version": "6.1.6",
3
+ "version": "6.2.1",
4
4
  "keywords": [
5
5
  "custom",
6
6
  "web",
@@ -25,24 +25,51 @@
25
25
  "main": "dist/index.js",
26
26
  "types": "dist/index.d.ts",
27
27
  "scripts": {
28
+ "build": "virmator frontend build",
28
29
  "compile": "rm -rf dist && tsc --project tsconfig.json",
30
+ "docs:update": "virmator code-in-markdown",
29
31
  "format": "virmator format",
30
- "prepublishOnly": "npm run compile && npm run test:full",
32
+ "prepublishOnly": "npm run compile && npm run test:all",
33
+ "preview": "virmator frontend preview",
31
34
  "spellcheck": "virmator spellcheck",
32
35
  "start": "npm install && virmator frontend",
33
36
  "test": "npm run test:types && virmator test-web",
34
- "test:full": "npm test && npm run spellcheck && virmator format check && npm run update-docs -- --check",
37
+ "test:all": "npm run test:types && npm test && npm run spellcheck && npm run test:format && npm run test:docs",
38
+ "test:docs": "virmator code-in-markdown check",
39
+ "test:format": "virmator format check",
35
40
  "test:types": "tsc --noEmit",
36
- "update-docs": "virmator code-in-markdown README.md --index src/index.ts"
41
+ "test:web": "virmator test-web"
37
42
  },
38
43
  "dependencies": {
39
- "augment-vir": "2.5.0",
44
+ "augment-vir": "^3.0.4",
40
45
  "lit": "2.4.0"
41
46
  },
42
47
  "devDependencies": {
43
- "@open-wc/testing": "3.1.6",
44
- "@web/dev-server-esbuild": "0.3.2",
45
- "@web/test-runner": "0.14.0",
46
- "virmator": "3.0.6"
48
+ "@istanbuljs/nyc-config-typescript": "^1.0.2",
49
+ "@open-wc/testing": "^3.1.6",
50
+ "@types/chai": "^4.3.3",
51
+ "@types/mocha": "^10.0.0",
52
+ "@web/dev-server-esbuild": "^0.3.3",
53
+ "@web/test-runner": "^0.15.0",
54
+ "@web/test-runner-commands": "^0.6.5",
55
+ "@web/test-runner-playwright": "^0.9.0",
56
+ "ansi-colors": "^4.1.3",
57
+ "chai": "^4.3.6",
58
+ "cspell": "^6.12.0",
59
+ "istanbul-smart-text-reporter": "^0.0.1",
60
+ "markdown-code-example-inserter": "^0.1.11",
61
+ "mocha": "^10.1.0",
62
+ "mocha-spec-reporter-with-file-names": "^0.0.0",
63
+ "nyc": "^15.1.0",
64
+ "prettier": "^2.7.1",
65
+ "prettier-plugin-jsdoc": "^0.4.2",
66
+ "prettier-plugin-multiline-arrays": "^1.1.1",
67
+ "prettier-plugin-organize-imports": "^3.1.1",
68
+ "prettier-plugin-packagejson": "^2.3.0",
69
+ "prettier-plugin-sort-json": "^0.0.3",
70
+ "prettier-plugin-toml": "^0.3.1",
71
+ "ts-node": "^10.9.1",
72
+ "virmator": "^4.2.13",
73
+ "vite": "^3.2.0"
47
74
  }
48
75
  }