element-vir 20.0.9 → 22.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.
- package/LICENSE-MIT +1 -1
- package/README.md +3 -5
- package/dist/declarative-element/declarative-element.d.ts +3 -2
- package/dist/declarative-element/define-element-no-inputs.js +13 -4
- package/dist/declarative-element/directives/async-prop.d.ts +11 -35
- package/dist/declarative-element/directives/async-prop.js +8 -128
- package/dist/declarative-element/directives/is-resolved.directive.d.ts +4 -4
- package/dist/declarative-element/directives/is-resolved.directive.js +14 -8
- package/dist/declarative-element/directives/listen.directive.d.ts +6 -2
- package/dist/declarative-element/directives/render-async.directive.d.ts +5 -5
- package/dist/declarative-element/is-declarative-element-definition.d.ts +3 -0
- package/dist/declarative-element/is-declarative-element-definition.js +48 -0
- package/dist/declarative-element/properties/element-vir-state-setup.d.ts +6 -3
- package/dist/declarative-element/properties/element-vir-state-setup.js +2 -2
- package/dist/declarative-element/properties/per-instance.js +2 -1
- package/dist/declarative-element/properties/property-proxy.d.ts +7 -0
- package/dist/declarative-element/properties/{element-updater-proxy.js → property-proxy.js} +10 -12
- package/dist/declarative-element/render-callback.d.ts +1 -1
- package/dist/index.d.ts +2 -4
- package/dist/index.js +2 -4
- package/dist/template-transforms/minimal-element-definition.d.ts +6 -2
- package/dist/template-transforms/minimal-element-definition.js +2 -5
- package/dist/template-transforms/nested-mapped-templates.js +8 -8
- package/dist/template-transforms/vir-css/css-transform.js +2 -2
- package/dist/template-transforms/vir-html/html-interpolation.d.ts +2 -2
- package/dist/template-transforms/vir-html/html-transform.d.ts +2 -0
- package/dist/template-transforms/vir-html/html-transform.js +48 -11
- package/dist/template-transforms/vir-html/tag-name-keys.d.ts +7 -0
- package/dist/template-transforms/vir-html/tag-name-keys.js +5 -0
- package/dist/template-transforms/vir-html/vir-html.js +4 -3
- package/package.json +18 -15
- package/dist/declarative-element/properties/element-updater-proxy.d.ts +0 -5
- package/dist/declarative-element/properties/observable-prop/interval-observable-prop.d.ts +0 -37
- package/dist/declarative-element/properties/observable-prop/interval-observable-prop.js +0 -56
- package/dist/declarative-element/properties/observable-prop/observable-prop.d.ts +0 -13
- package/dist/declarative-element/properties/observable-prop/observable-prop.js +0 -14
- package/dist/declarative-element/properties/observable-prop/setter-observable-prop.d.ts +0 -18
- package/dist/declarative-element/properties/observable-prop/setter-observable-prop.js +0 -42
- package/dist/declarative-element/properties/observable-prop/updatable-observable-prop.d.ts +0 -40
- package/dist/declarative-element/properties/observable-prop/updatable-observable-prop.js +0 -61
- package/dist/util/equality.d.ts +0 -1
- package/dist/util/equality.js +0 -3
package/LICENSE-MIT
CHANGED
package/README.md
CHANGED
|
@@ -268,9 +268,7 @@ export const MyWithEvents = defineElementNoInputs({
|
|
|
268
268
|
},
|
|
269
269
|
renderCallback({dispatch, events}) {
|
|
270
270
|
return html`
|
|
271
|
-
<button ${listen('click', () => dispatch(new events.logoutClick(
|
|
272
|
-
log out
|
|
273
|
-
</button>
|
|
271
|
+
<button ${listen('click', () => dispatch(new events.logoutClick()))}>log out</button>
|
|
274
272
|
<button ${listen('click', () => dispatch(new events.randomNumber(Math.random())))}>
|
|
275
273
|
generate random number
|
|
276
274
|
</button>
|
|
@@ -630,13 +628,13 @@ export const MyWithAsyncProp = defineElement<{endpoint: string}>()({
|
|
|
630
628
|
},
|
|
631
629
|
}),
|
|
632
630
|
},
|
|
633
|
-
renderCallback({inputs, state
|
|
631
|
+
renderCallback({inputs, state}) {
|
|
634
632
|
/**
|
|
635
633
|
* This causes the a promise which automatically updates the state.data prop once the
|
|
636
634
|
* promise resolves. It only creates a new promise if the first input, the trigger, value
|
|
637
635
|
* changes from previous calls.
|
|
638
636
|
*/
|
|
639
|
-
state.data.
|
|
637
|
+
state.data.update(inputs);
|
|
640
638
|
return html`
|
|
641
639
|
Here's the data:
|
|
642
640
|
<br />
|
|
@@ -11,7 +11,7 @@ import { EventDescriptorMap, EventsInitMap } from './properties/element-events';
|
|
|
11
11
|
import { ElementPropertyDescriptorMap, PropertyInitMapBase } from './properties/element-properties';
|
|
12
12
|
import { FlattenElementVirStateSetup } from './properties/element-vir-state-setup';
|
|
13
13
|
import { HostClassNamesMap } from './properties/host-classes';
|
|
14
|
-
import {
|
|
14
|
+
import { ObservableListenerMap } from './properties/property-proxy';
|
|
15
15
|
import type { RenderCallback, RenderParams, UpdateStateCallback } from './render-callback';
|
|
16
16
|
import { SlotNameMap } from './slot-names';
|
|
17
17
|
export type DeclarativeElementHost<TagName extends CustomElementTagName = any, Inputs extends PropertyInitMapBase = any, StateInit extends PropertyInitMapBase = any, EventsInit extends EventsInitMap = any, HostClassKeys extends BaseCssPropertyName<TagName> = any, CssVarKeys extends BaseCssPropertyName<TagName> = any, SlotNames extends ReadonlyArray<string> = any> = RequiredAndNotNullBy<Omit<DeclarativeElement<TagName, Inputs, StateInit, EventsInit, HostClassKeys, CssVarKeys, SlotNames>, Exclude<keyof StaticDeclarativeElementProperties<any, any, any, any, any, any, any>, keyof HTMLElement>>, 'shadowRoot'>;
|
|
@@ -38,9 +38,10 @@ export declare abstract class DeclarativeElement<TagName extends CustomElementTa
|
|
|
38
38
|
abstract _lastRenderError: Error | undefined;
|
|
39
39
|
abstract _internalRenderCount: number;
|
|
40
40
|
abstract _lastRenderedProps: Readonly<Pick<RenderParams<any, Inputs, StateInit, any, any, any, any>, 'inputs' | 'state'>>;
|
|
41
|
+
abstract destroy(): void;
|
|
41
42
|
abstract render(): unknown;
|
|
42
43
|
abstract readonly instanceState: FlattenElementVirStateSetup<StateInit>;
|
|
43
|
-
abstract readonly observablePropertyListenerMap:
|
|
44
|
+
abstract readonly observablePropertyListenerMap: ObservableListenerMap<StateInit & Inputs>;
|
|
44
45
|
abstract readonly instanceInputs: Inputs;
|
|
45
46
|
abstract assignInputs(inputs: {} extends Required<Inputs> ? never : Partial<Inputs>): void;
|
|
46
47
|
abstract _haveInputsBeenSet: boolean;
|
|
@@ -4,7 +4,7 @@ var __setFunctionName = (this && this.__setFunctionName) || function (f, name, p
|
|
|
4
4
|
};
|
|
5
5
|
import { ensureErrorAndPrependMessage, extractErrorMessage, getObjectTypedKeys, kebabCaseToCamelCase, } from '@augment-vir/common';
|
|
6
6
|
import { defineCssVars } from 'lit-css-vars';
|
|
7
|
-
import { isRunTimeType } from 'run-time-assertions';
|
|
7
|
+
import { hasProperty, isRunTimeType } from 'run-time-assertions';
|
|
8
8
|
import { css } from '../template-transforms/vir-css/vir-css';
|
|
9
9
|
import { DeclarativeElement, } from './declarative-element';
|
|
10
10
|
import { defaultDeclarativeElementDefinitionOptions, } from './definition-options';
|
|
@@ -12,8 +12,8 @@ import { hasDeclarativeElementParent } from './has-declarative-element-parent';
|
|
|
12
12
|
import { assignInputs } from './properties/assign-inputs';
|
|
13
13
|
import { assertValidCssProperties } from './properties/css-properties';
|
|
14
14
|
import { createEventDescriptorMap, } from './properties/element-events';
|
|
15
|
-
import { bindReactiveProperty, createElementUpdaterProxy } from './properties/element-updater-proxy';
|
|
16
15
|
import { createHostClassNamesMap } from './properties/host-classes';
|
|
16
|
+
import { bindReactiveProperty, createElementPropertyProxy } from './properties/property-proxy';
|
|
17
17
|
import { applyHostClasses, hostClassNamesToStylesInput } from './properties/styles';
|
|
18
18
|
import { createRenderParams } from './render-callback';
|
|
19
19
|
import { createSlotNamesMap } from './slot-names';
|
|
@@ -128,6 +128,14 @@ export function defineElementNoInputs(initInput) {
|
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
|
+
destroy() {
|
|
132
|
+
Object.values(this.instanceState).forEach((stateValue) => {
|
|
133
|
+
if (hasProperty(stateValue, 'destroy') &&
|
|
134
|
+
isRunTimeType(stateValue.destroy, 'function')) {
|
|
135
|
+
stateValue.destroy();
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
131
139
|
disconnectedCallback() {
|
|
132
140
|
super.disconnectedCallback();
|
|
133
141
|
if (init.cleanupCallback) {
|
|
@@ -136,6 +144,7 @@ export function defineElementNoInputs(initInput) {
|
|
|
136
144
|
throw new Error(`cleanupCallback in '${init.tagName}' cannot be asynchronous`);
|
|
137
145
|
}
|
|
138
146
|
}
|
|
147
|
+
this.destroy();
|
|
139
148
|
this._initCalled = false;
|
|
140
149
|
}
|
|
141
150
|
assignInputs(inputs) {
|
|
@@ -196,13 +205,13 @@ export function defineElementNoInputs(initInput) {
|
|
|
196
205
|
enumerable: true,
|
|
197
206
|
configurable: true,
|
|
198
207
|
writable: true,
|
|
199
|
-
value:
|
|
208
|
+
value: createElementPropertyProxy(this, false)
|
|
200
209
|
});
|
|
201
210
|
Object.defineProperty(this, "instanceState", {
|
|
202
211
|
enumerable: true,
|
|
203
212
|
configurable: true,
|
|
204
213
|
writable: true,
|
|
205
|
-
value:
|
|
214
|
+
value: createElementPropertyProxy(this, !elementOptions.allowPolymorphicState)
|
|
206
215
|
});
|
|
207
216
|
const stateInitStatic = init.stateInitStatic || {};
|
|
208
217
|
getObjectTypedKeys(stateInitStatic).forEach((stateKey) => {
|
|
@@ -1,36 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CallbackObservable, CallbackObservableInit } from 'observavir';
|
|
2
|
+
import { Constructor } from 'type-fest';
|
|
2
3
|
import { ElementVirStateSetup } from '../properties/element-vir-state-setup';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export type
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* When a trigger changes (according to deep equality checking through JSON stringify),
|
|
13
|
-
* the updateCallback callback will be called and the element's state will be updated
|
|
14
|
-
* again. Otherwise, the updateCallback callback will only be called the first time.
|
|
15
|
-
*
|
|
16
|
-
* Set this to undefined to disabled automatic updating. Meaning, updateCallback will only
|
|
17
|
-
* be called the first time.
|
|
18
|
-
*/
|
|
19
|
-
updateCallback: AsyncPropUpdateCallback<TriggerInput, UpdaterInput, Promise<Awaited<ValueType>>>;
|
|
20
|
-
};
|
|
21
|
-
export type AsyncObservableProp<ValueType, TriggerInput extends AsyncPropTriggerInputBase, UpdaterInput> = ObservableProp<AsyncPropValue<ValueType>> & {
|
|
22
|
-
setNewPromise(newPromise: Promise<Awaited<ValueType>>): void;
|
|
23
|
-
updateTrigger: AsyncPropUpdateCallback<TriggerInput, UpdaterInput, void>;
|
|
24
|
-
setResolvedValue(resolvedValue: Awaited<ValueType>): void;
|
|
25
|
-
/**
|
|
26
|
-
* Forces the updater callback to re-run with the last given trigger and updaterInput. If this
|
|
27
|
-
* asyncProp has no updater callback defined, this will result in an error.
|
|
28
|
-
*/
|
|
29
|
-
forceUpdate: AsyncPropUpdateCallback<TriggerInput, UpdaterInput, void>;
|
|
30
|
-
/**
|
|
31
|
-
* The last value that was resolved. This will be undefined if there has never, so far, been a
|
|
32
|
-
* resolved value.
|
|
33
|
-
*/
|
|
34
|
-
latestResolvedValue: ValueType extends Promise<any> ? Awaited<ValueType> | undefined : ValueType;
|
|
35
|
-
};
|
|
36
|
-
export declare function asyncProp<ValueType, TriggerInput extends AsyncPropTriggerInputBase = {}, UpdaterInput = undefined>(...args: [AsyncPropInit<ValueType, TriggerInput, UpdaterInput>] | []): ElementVirStateSetup<AsyncObservableProp<ValueType, TriggerInput, UpdaterInput>>;
|
|
4
|
+
export type { AsyncValue } from 'observavir';
|
|
5
|
+
/** Class for constructing async props. Should not be referenced directly, use `AsyncProp` instead. */
|
|
6
|
+
declare class _AsyncPropClass<Value, Params> extends CallbackObservable<Value, Params> {
|
|
7
|
+
}
|
|
8
|
+
export type AsyncProp<Value, Params> = Omit<_AsyncPropClass<Value, Params>,
|
|
9
|
+
/** Hide these properties to make the `AsyncProp` interface much simpler. */
|
|
10
|
+
'dispatch' | 'equalityCheck' | 'getListenerCount' | 'updateCallback' | 'removeListener' | 'removeAllListeners' | 'listenToEvent' | 'listen'>;
|
|
11
|
+
export declare const AsyncProp: Constructor<AsyncProp<unknown, unknown>, ConstructorParameters<typeof _AsyncPropClass<unknown, unknown>>>;
|
|
12
|
+
export declare function asyncProp<Value, Params = void>(init?: CallbackObservableInit<Value, Params>): ElementVirStateSetup<AsyncProp<Value, Params>>;
|
|
@@ -1,133 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
function setupAsyncProp(init) {
|
|
6
|
-
let lastTrigger = notSetSymbol;
|
|
7
|
-
let lastSetPromise;
|
|
8
|
-
const promiseUpdater = init && 'updateCallback' in init ? init.updateCallback : undefined;
|
|
9
|
-
let waitingForValuePromise = createDeferredPromiseWrapper();
|
|
10
|
-
const baseObservableProp = createSetterObservableProp(waitingForValuePromise.promise);
|
|
11
|
-
function resetWaitingForValuePromise() {
|
|
12
|
-
waitingForValuePromise = createDeferredPromiseWrapper();
|
|
13
|
-
baseObservableProp.setValue(waitingForValuePromise.promise);
|
|
14
|
-
}
|
|
15
|
-
function resolveValue(value) {
|
|
16
|
-
waitingForValuePromise.resolve(value);
|
|
17
|
-
baseObservableProp.setValue(value);
|
|
18
|
-
asyncProp.latestResolvedValue = value;
|
|
19
|
-
}
|
|
20
|
-
function rejectValue(error) {
|
|
21
|
-
waitingForValuePromise.reject(error);
|
|
22
|
-
baseObservableProp.setValue(error);
|
|
23
|
-
}
|
|
24
|
-
function setPromise(newPromise) {
|
|
25
|
-
if (newPromise === lastSetPromise) {
|
|
26
|
-
/** Abort setting the promise if we already have set this promise. */
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
lastSetPromise = newPromise;
|
|
30
|
-
if (waitingForValuePromise.isSettled()) {
|
|
31
|
-
resetWaitingForValuePromise();
|
|
32
|
-
}
|
|
33
|
-
newPromise
|
|
34
|
-
.then((value) => {
|
|
35
|
-
/** Make sure we're still actually waiting for this promise. */
|
|
36
|
-
if (lastSetPromise === newPromise) {
|
|
37
|
-
resolveValue(value);
|
|
38
|
-
}
|
|
39
|
-
})
|
|
40
|
-
.catch((reason) => {
|
|
41
|
-
/** Make sure we're still actually waiting for this promise. */
|
|
42
|
-
if (lastSetPromise === newPromise) {
|
|
43
|
-
waitingForValuePromise.promise.catch(() => {
|
|
44
|
-
/**
|
|
45
|
-
* Don't actually do anything, we just want to make sure the error is
|
|
46
|
-
* handled so it doesn't throw errors in the browser.
|
|
47
|
-
*/
|
|
48
|
-
});
|
|
49
|
-
const error = ensureError(reason);
|
|
50
|
-
console.error(error);
|
|
51
|
-
rejectValue(error);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
function updateTrigger(triggerInput, updaterInput) {
|
|
56
|
-
if (!promiseUpdater) {
|
|
57
|
-
console.error(init);
|
|
58
|
-
throw new Error(`Trigger was updated for asyncProp but no updateCallback has been defined.`);
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* This will expand proxies so that `inputs` or `state` can be used directly as a
|
|
62
|
-
* serializableTrigger without issues.
|
|
63
|
-
*/
|
|
64
|
-
const expandedTrigger = {
|
|
65
|
-
...triggerInput,
|
|
66
|
-
};
|
|
67
|
-
const expandedInputs = { ...updaterInput };
|
|
68
|
-
if (lastTrigger === notSetSymbol ||
|
|
69
|
-
!areJsonEqual(expandedTrigger, lastTrigger, {
|
|
70
|
-
ignoreNonSerializableProperties: true,
|
|
71
|
-
})) {
|
|
72
|
-
lastTrigger = expandedTrigger;
|
|
73
|
-
const newValue = promiseUpdater(lastTrigger, expandedInputs);
|
|
74
|
-
setPromise(newValue);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
function forceUpdate(triggerInput, updaterInput) {
|
|
78
|
-
lastTrigger = notSetSymbol;
|
|
79
|
-
updateTrigger(triggerInput, updaterInput);
|
|
80
|
-
}
|
|
81
|
-
const extraProperties = {
|
|
82
|
-
latestResolvedValue: (init && 'defaultValue' in init && !isPromise(init.defaultValue)
|
|
83
|
-
? init.defaultValue
|
|
84
|
-
: undefined),
|
|
85
|
-
setNewPromise(newPromise) {
|
|
86
|
-
setPromise(newPromise);
|
|
87
|
-
},
|
|
88
|
-
setResolvedValue(value) {
|
|
89
|
-
if (value !== baseObservableProp.value) {
|
|
90
|
-
if (waitingForValuePromise.isSettled()) {
|
|
91
|
-
resetWaitingForValuePromise();
|
|
92
|
-
}
|
|
93
|
-
waitingForValuePromise.resolve(value);
|
|
94
|
-
resolveValue(value);
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
updateTrigger: (promiseUpdater
|
|
98
|
-
? updateTrigger
|
|
99
|
-
: () => {
|
|
100
|
-
throw new Error('Cannot run updateTrigger when updateCallback was not set on the asyncProp.');
|
|
101
|
-
}),
|
|
102
|
-
forceUpdate: (promiseUpdater
|
|
103
|
-
? forceUpdate
|
|
104
|
-
: () => {
|
|
105
|
-
throw new Error('Cannot run forceUpdate when updateCallback was not set on the asyncProp.');
|
|
106
|
-
}),
|
|
107
|
-
};
|
|
108
|
-
const asyncProp = Object.assign(baseObservableProp, extraProperties);
|
|
109
|
-
const initValue = init && 'defaultValue' in init
|
|
110
|
-
? init.defaultValue
|
|
111
|
-
: /** A promise that doesn't resolve because we're waiting for the first value still. */
|
|
112
|
-
new Promise(() => { });
|
|
113
|
-
if (initValue instanceof Promise) {
|
|
114
|
-
setPromise(initValue);
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
resolveValue(initValue);
|
|
118
|
-
}
|
|
119
|
-
if (isPromise(initValue)) {
|
|
120
|
-
setPromise(initValue);
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
resolveValue(initValue);
|
|
124
|
-
}
|
|
125
|
-
return asyncProp;
|
|
1
|
+
import { CallbackObservable } from 'observavir';
|
|
2
|
+
import { stateSetupKey } from '../properties/element-vir-state-setup';
|
|
3
|
+
/** Class for constructing async props. Should not be referenced directly, use `AsyncProp` instead. */
|
|
4
|
+
class _AsyncPropClass extends CallbackObservable {
|
|
126
5
|
}
|
|
127
|
-
export
|
|
6
|
+
export const AsyncProp = _AsyncPropClass;
|
|
7
|
+
export function asyncProp(init) {
|
|
128
8
|
return {
|
|
129
|
-
|
|
130
|
-
return
|
|
9
|
+
[stateSetupKey]() {
|
|
10
|
+
return new _AsyncPropClass(init);
|
|
131
11
|
},
|
|
132
12
|
};
|
|
133
13
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare function isResolved<Value extends
|
|
3
|
-
export declare function
|
|
4
|
-
export declare function resolvedOrUndefined<Value extends
|
|
1
|
+
import { AsyncProp, AsyncValue } from './async-prop';
|
|
2
|
+
export declare function isResolved<Value extends AsyncValue<any>>(asyncValue: Value extends AsyncProp<any, any> ? 'Error: pass AsyncProp.value, not AsyncProp itself.' : Value): asyncValue is Exclude<typeof asyncValue, Promise<any>>;
|
|
3
|
+
export declare function isAsyncError<Value extends AsyncValue<any>>(asyncValue: Value extends AsyncProp<any, any> ? 'Error: pass AsyncProp.value, not AsyncProp itself.' : Value): asyncValue is Extract<typeof asyncValue, Error>;
|
|
4
|
+
export declare function resolvedOrUndefined<Value extends AsyncValue<any>>(asyncValue: Value extends AsyncProp<any, any> ? 'Error: pass AsyncProp.value, not AsyncProp itself.' : Value): Exclude<typeof asyncValue, Promise<any>> | undefined;
|
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export function isResolved(
|
|
3
|
-
|
|
1
|
+
import { AsyncProp } from './async-prop';
|
|
2
|
+
export function isResolved(asyncValue) {
|
|
3
|
+
if (asyncValue instanceof AsyncProp) {
|
|
4
|
+
throw new TypeError('Pass AsyncProp.value, not AsyncProp itself.');
|
|
5
|
+
}
|
|
6
|
+
return !(asyncValue instanceof Promise);
|
|
4
7
|
}
|
|
5
|
-
export function
|
|
6
|
-
|
|
8
|
+
export function isAsyncError(asyncValue) {
|
|
9
|
+
if (asyncValue instanceof AsyncProp) {
|
|
10
|
+
throw new TypeError('Pass AsyncProp.value, not AsyncProp itself.');
|
|
11
|
+
}
|
|
12
|
+
return asyncValue instanceof Error;
|
|
7
13
|
}
|
|
8
|
-
export function resolvedOrUndefined(
|
|
9
|
-
if (isResolved(
|
|
10
|
-
return
|
|
14
|
+
export function resolvedOrUndefined(asyncValue) {
|
|
15
|
+
if (isResolved(asyncValue)) {
|
|
16
|
+
return asyncValue;
|
|
11
17
|
}
|
|
12
18
|
else {
|
|
13
19
|
return undefined;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { MaybePromise } from '@augment-vir/common';
|
|
1
2
|
import { DirectiveResult } from '../../lit-exports/all-lit-exports';
|
|
2
3
|
import { DefinedTypedEvent, TypedEvent } from '../../typed-event/typed-event';
|
|
4
|
+
/** We don't care at all what this returns, just allow anything! */
|
|
5
|
+
type ListenCallbackReturn = MaybePromise<any>;
|
|
3
6
|
/**
|
|
4
7
|
* Listen to events. These can be native DOM events (use a string for the inputType argument) or
|
|
5
8
|
* typed events (pass in a return value from defineTypedEvent).
|
|
@@ -10,5 +13,6 @@ import { DefinedTypedEvent, TypedEvent } from '../../typed-event/typed-event';
|
|
|
10
13
|
* @param listener The callback to fire when an event is caught. Assuming the definedTypedEvent
|
|
11
14
|
* input is properly typed, the event given to this callback will also be typed.
|
|
12
15
|
*/
|
|
13
|
-
export declare function listen<TypedEventTypeNameGeneric extends string, TypedEventDetailGeneric, NativeElementEventNameGeneric extends keyof HTMLElementEventMap>(eventType: DefinedTypedEvent<TypedEventTypeNameGeneric, TypedEventDetailGeneric>, listener: (event: TypedEvent<TypedEventTypeNameGeneric, TypedEventDetailGeneric>) =>
|
|
14
|
-
export declare function listen<TypedEventTypeNameGeneric extends string, TypedEventDetailGeneric, NativeElementEventNameGeneric extends keyof HTMLElementEventMap>(eventType: NativeElementEventNameGeneric, listener: (event: HTMLElementEventMap[NativeElementEventNameGeneric]) =>
|
|
16
|
+
export declare function listen<TypedEventTypeNameGeneric extends string, TypedEventDetailGeneric, NativeElementEventNameGeneric extends keyof HTMLElementEventMap>(eventType: DefinedTypedEvent<TypedEventTypeNameGeneric, TypedEventDetailGeneric>, listener: (event: TypedEvent<TypedEventTypeNameGeneric, TypedEventDetailGeneric>) => ListenCallbackReturn): DirectiveResult<any>;
|
|
17
|
+
export declare function listen<TypedEventTypeNameGeneric extends string, TypedEventDetailGeneric, NativeElementEventNameGeneric extends keyof HTMLElementEventMap>(eventType: NativeElementEventNameGeneric, listener: (event: HTMLElementEventMap[NativeElementEventNameGeneric]) => ListenCallbackReturn): DirectiveResult<any>;
|
|
18
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare function renderAsync<T, FallbackResult, ResolutionRenderResult = never, ErrorRenderResult = never>(asyncProp:
|
|
3
|
-
export declare function renderAsync<T, FallbackResult, ResolutionRenderResult = never, ErrorRenderResult = never>(asyncProp:
|
|
4
|
-
export declare function renderAsync<T, FallbackResult, ResolutionRenderResult = never, ErrorRenderResult = never>(asyncProp:
|
|
5
|
-
export declare function renderAsync<T, FallbackResult, ResolutionRenderResult = never, ErrorRenderResult = never>(asyncProp:
|
|
1
|
+
import { AsyncProp } from './async-prop';
|
|
2
|
+
export declare function renderAsync<T, FallbackResult, ResolutionRenderResult = never, ErrorRenderResult = never>(asyncProp: Pick<AsyncProp<T, any>, 'value'>, fallback: FallbackResult, resolutionRender: (resolved: Awaited<T>) => ResolutionRenderResult, errorRender: (error: Error) => ErrorRenderResult): FallbackResult | ResolutionRenderResult | ErrorRenderResult;
|
|
3
|
+
export declare function renderAsync<T, FallbackResult, ResolutionRenderResult = never, ErrorRenderResult = never>(asyncProp: Pick<AsyncProp<T, any>, 'value'>, fallback: FallbackResult, resolutionRender: (resolved: Awaited<T>) => ResolutionRenderResult, errorRender?: undefined): FallbackResult | ResolutionRenderResult | string;
|
|
4
|
+
export declare function renderAsync<T, FallbackResult, ResolutionRenderResult = never, ErrorRenderResult = never>(asyncProp: Pick<AsyncProp<T, any>, 'value'>, fallback: FallbackResult, resolutionRender: undefined, errorRender: (error: Error) => ErrorRenderResult): FallbackResult | Awaited<T> | ErrorRenderResult;
|
|
5
|
+
export declare function renderAsync<T, FallbackResult, ResolutionRenderResult = never, ErrorRenderResult = never>(asyncProp: Pick<AsyncProp<T, any>, 'value'>, fallback: FallbackResult, resolutionRender?: undefined, errorRender?: undefined): FallbackResult | Awaited<T> | string;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { DeclarativeElementDefinition } from './declarative-element';
|
|
2
|
+
export declare function assertDeclarativeElementDefinition(input: unknown, failMessage?: string | undefined): asserts input is DeclarativeElementDefinition;
|
|
3
|
+
export declare function isDeclarativeElementDefinition(input: unknown): input is DeclarativeElementDefinition;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { getObjectTypedKeys, isTruthy, wrapInTry } from '@augment-vir/common';
|
|
2
|
+
import { AssertionError, hasProperty, isRunTimeType } from 'run-time-assertions';
|
|
3
|
+
const expectedStaticProperties = getObjectTypedKeys({
|
|
4
|
+
assign: '',
|
|
5
|
+
assignedInputs: '',
|
|
6
|
+
cssVars: '',
|
|
7
|
+
elementOptions: '',
|
|
8
|
+
events: '',
|
|
9
|
+
hostClasses: '',
|
|
10
|
+
init: '',
|
|
11
|
+
inputsType: '',
|
|
12
|
+
isStrictInstance: '',
|
|
13
|
+
renderCallback: '',
|
|
14
|
+
slotNames: '',
|
|
15
|
+
stateInitStatic: '',
|
|
16
|
+
stateType: '',
|
|
17
|
+
styles: '',
|
|
18
|
+
tagName: '',
|
|
19
|
+
updateStateType: '',
|
|
20
|
+
});
|
|
21
|
+
export function assertDeclarativeElementDefinition(input, failMessage) {
|
|
22
|
+
if (!isRunTimeType(input, 'function')) {
|
|
23
|
+
throw new AssertionError([
|
|
24
|
+
'input is not an element constructor',
|
|
25
|
+
failMessage,
|
|
26
|
+
]
|
|
27
|
+
.filter(isTruthy)
|
|
28
|
+
.join(': '));
|
|
29
|
+
}
|
|
30
|
+
expectedStaticProperties.forEach((expectedProperty) => {
|
|
31
|
+
if (!hasProperty(input, expectedProperty)) {
|
|
32
|
+
throw new AssertionError([
|
|
33
|
+
`missing prop '${expectedProperty}'`,
|
|
34
|
+
failMessage,
|
|
35
|
+
]
|
|
36
|
+
.filter(isTruthy)
|
|
37
|
+
.join(': '));
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export function isDeclarativeElementDefinition(input) {
|
|
42
|
+
return wrapInTry(() => {
|
|
43
|
+
assertDeclarativeElementDefinition(input);
|
|
44
|
+
return true;
|
|
45
|
+
}, {
|
|
46
|
+
fallbackValue: false,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { PropertyInitMapBase } from './element-properties';
|
|
2
|
+
export declare const stateSetupKey: unique symbol;
|
|
2
3
|
export type ElementVirStateSetup<InnerValue> = {
|
|
3
|
-
|
|
4
|
+
[key in typeof stateSetupKey]: () => InnerValue;
|
|
4
5
|
};
|
|
5
6
|
export declare function isElementVirStateSetup<T = unknown>(input: unknown): input is ElementVirStateSetup<T>;
|
|
7
|
+
export type UnwrapElementVirStateSetup<T> = T extends ElementVirStateSetup<infer U> ? U : T;
|
|
8
|
+
export type MaybeElementVirStateSetup<T> = UnwrapElementVirStateSetup<T> | ElementVirStateSetup<UnwrapElementVirStateSetup<T>>;
|
|
6
9
|
export type FlattenElementVirStateSetup<OriginalObject extends PropertyInitMapBase> = {
|
|
7
10
|
[Prop in keyof OriginalObject]: Extract<OriginalObject[Prop], ElementVirStateSetup<any>> extends never ? OriginalObject[Prop] : Extract<OriginalObject[Prop], ElementVirStateSetup<any>> extends ElementVirStateSetup<infer InnerValue> ? InnerValue | Exclude<OriginalObject[Prop], ElementVirStateSetup<any>> : OriginalObject[Prop];
|
|
8
11
|
};
|
|
9
|
-
export type AllowElementVirStateSetup<OriginalObject extends PropertyInitMapBase
|
|
10
|
-
[Prop in keyof
|
|
12
|
+
export type AllowElementVirStateSetup<OriginalObject extends PropertyInitMapBase> = {
|
|
13
|
+
[Prop in keyof OriginalObject]: MaybeElementVirStateSetup<OriginalObject[Prop]>;
|
|
11
14
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { isObject } from '@augment-vir/common';
|
|
2
|
-
const
|
|
2
|
+
export const stateSetupKey = Symbol('element-vir-state-setup');
|
|
3
3
|
export function isElementVirStateSetup(input) {
|
|
4
4
|
if (!isObject(input)) {
|
|
5
5
|
return false;
|
|
6
6
|
}
|
|
7
|
-
return
|
|
7
|
+
return stateSetupKey in input;
|
|
8
8
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { stateSetupKey } from './element-vir-state-setup';
|
|
1
2
|
/**
|
|
2
3
|
* A state prop helper that sets up the given callback for each instance of the element that this
|
|
3
4
|
* state is contained within.
|
|
4
5
|
*/
|
|
5
6
|
export function perInstance(creationCallback) {
|
|
6
7
|
const stateSetup = {
|
|
7
|
-
|
|
8
|
+
[stateSetupKey]: creationCallback,
|
|
8
9
|
};
|
|
9
10
|
return stateSetup;
|
|
10
11
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ObservableListener } from 'observavir';
|
|
2
|
+
import { DeclarativeElement } from '../declarative-element';
|
|
3
|
+
import { PropertyInitMapBase } from './element-properties';
|
|
4
|
+
export type ObservableListenerMap<OriginalPropertyMap extends PropertyInitMapBase> = Partial<Record<keyof OriginalPropertyMap, ObservableListener<any> | undefined>>;
|
|
5
|
+
/** Binds the given property key as a reactive property on the given element. */
|
|
6
|
+
export declare function bindReactiveProperty(element: HTMLElement, propertyKey: PropertyKey): void;
|
|
7
|
+
export declare function createElementPropertyProxy<PropertyInitGeneric extends PropertyInitMapBase>(element: DeclarativeElement, shouldAlreadyExist: boolean): PropertyInitGeneric;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { isObservableBase } from 'observavir';
|
|
1
2
|
import { property } from '../../lit-exports/all-lit-exports';
|
|
2
|
-
import { isElementVirStateSetup } from './element-vir-state-setup';
|
|
3
|
-
import { isObservableProp } from './observable-prop/observable-prop';
|
|
3
|
+
import { isElementVirStateSetup, stateSetupKey } from './element-vir-state-setup';
|
|
4
4
|
/** Binds the given property key as a reactive property on the given element. */
|
|
5
5
|
export function bindReactiveProperty(element, propertyKey) {
|
|
6
6
|
if (!(propertyKey in element)) {
|
|
@@ -15,7 +15,7 @@ function assertValidPropertyName(propKey, element, elementTagName) {
|
|
|
15
15
|
throw new Error(`Property '${String(propKey)}' does not exist on '${elementTagName.toLowerCase()}'.`);
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
-
export function
|
|
18
|
+
export function createElementPropertyProxy(element, shouldAlreadyExist) {
|
|
19
19
|
/**
|
|
20
20
|
* Lit element updates state and inputs by setting them directly on the element, so we must do
|
|
21
21
|
* that here. DeclarativeElement's types, however, do not expose this behavior, so we add that
|
|
@@ -23,7 +23,7 @@ export function createElementUpdaterProxy(element, verifyExists) {
|
|
|
23
23
|
*/
|
|
24
24
|
const elementAsProps = element;
|
|
25
25
|
function verifyProperty(propertyKey) {
|
|
26
|
-
if (
|
|
26
|
+
if (shouldAlreadyExist) {
|
|
27
27
|
assertValidPropertyName(propertyKey, element, element.tagName);
|
|
28
28
|
}
|
|
29
29
|
else {
|
|
@@ -38,7 +38,7 @@ export function createElementUpdaterProxy(element, verifyExists) {
|
|
|
38
38
|
get: valueGetter,
|
|
39
39
|
set(target, propertyKey, rawNewValue) {
|
|
40
40
|
const newValue = isElementVirStateSetup(rawNewValue)
|
|
41
|
-
? rawNewValue
|
|
41
|
+
? rawNewValue[stateSetupKey]()
|
|
42
42
|
: rawNewValue;
|
|
43
43
|
verifyProperty(propertyKey);
|
|
44
44
|
const oldValue = elementAsProps[propertyKey];
|
|
@@ -52,26 +52,24 @@ export function createElementUpdaterProxy(element, verifyExists) {
|
|
|
52
52
|
elementAsProps[propertyKey] = value;
|
|
53
53
|
}
|
|
54
54
|
const existingPropertyListener = element.observablePropertyListenerMap[propertyKey];
|
|
55
|
-
if (oldValue !== newValue &&
|
|
56
|
-
isObservableProp(oldValue) &&
|
|
57
|
-
existingPropertyListener?.length) {
|
|
55
|
+
if (oldValue !== newValue && isObservableBase(oldValue) && existingPropertyListener) {
|
|
58
56
|
/** Stop listening to the old value now that we have a new value */
|
|
59
57
|
oldValue.removeListener(existingPropertyListener);
|
|
60
58
|
}
|
|
61
|
-
if (
|
|
59
|
+
if (isObservableBase(newValue)) {
|
|
62
60
|
/** If we're using an existing observable property */
|
|
63
61
|
if (existingPropertyListener) {
|
|
64
|
-
newValue.
|
|
62
|
+
newValue.listen(existingPropertyListener);
|
|
65
63
|
}
|
|
66
64
|
else {
|
|
67
65
|
function newListener() {
|
|
68
66
|
element.requestUpdate();
|
|
69
67
|
}
|
|
70
68
|
element.observablePropertyListenerMap[propertyKey] = newListener;
|
|
71
|
-
newValue.
|
|
69
|
+
newValue.listen(newListener);
|
|
72
70
|
}
|
|
73
71
|
}
|
|
74
|
-
else if (
|
|
72
|
+
else if (isObservableBase(oldValue)) {
|
|
75
73
|
/** Clear out old listener that is no longer used. */
|
|
76
74
|
element.observablePropertyListenerMap[propertyKey] = undefined;
|
|
77
75
|
}
|
|
@@ -10,7 +10,7 @@ import { AllowElementVirStateSetup, FlattenElementVirStateSetup } from './proper
|
|
|
10
10
|
import { SlotNameMap } from './slot-names';
|
|
11
11
|
export type RenderCallback<TagName extends CustomElementTagName = any, Inputs extends PropertyInitMapBase = any, StateInit extends PropertyInitMapBase = any, EventsInit extends EventsInitMap = any, HostClassKeys extends BaseCssPropertyName<TagName> = any, CssVarKeys extends BaseCssPropertyName<TagName> = any, SlotNames extends ReadonlyArray<string> = any> = (params: RenderParams<TagName, Inputs, StateInit, EventsInit, HostClassKeys, CssVarKeys, SlotNames>) => HtmlInterpolation;
|
|
12
12
|
export type InitCallback<TagName extends CustomElementTagName, Inputs extends PropertyInitMapBase, StateInit extends PropertyInitMapBase, EventsInit extends EventsInitMap, HostClassKeys extends BaseCssPropertyName<TagName>, CssVarKeys extends BaseCssPropertyName<TagName>, SlotNames extends ReadonlyArray<string>> = (params: RenderParams<TagName, Inputs, StateInit, EventsInit, HostClassKeys, CssVarKeys, SlotNames>) => void;
|
|
13
|
-
export type UpdateStateCallback<StateInit extends PropertyInitMapBase> =
|
|
13
|
+
export type UpdateStateCallback<StateInit extends PropertyInitMapBase> = (newState: Partial<AllowElementVirStateSetup<StateInit>>) => void;
|
|
14
14
|
export type RenderParams<TagName extends CustomElementTagName, Inputs extends PropertyInitMapBase, StateInit extends PropertyInitMapBase, EventsInit extends EventsInitMap, HostClassKeys extends BaseCssPropertyName<TagName>, CssVarKeys extends BaseCssPropertyName<TagName>, SlotNames extends ReadonlyArray<string>> = {
|
|
15
15
|
state: Readonly<FlattenElementVirStateSetup<StateInit>>;
|
|
16
16
|
cssVars: Readonly<CssVars<TagName, CssVarKeys>>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export * from 'observavir';
|
|
1
2
|
export * from './declarative-element/custom-tag-name';
|
|
2
3
|
export * from './declarative-element/declarative-element';
|
|
3
4
|
export * from './declarative-element/declarative-element-init';
|
|
@@ -15,16 +16,13 @@ export * from './declarative-element/directives/render-async.directive';
|
|
|
15
16
|
export * from './declarative-element/directives/render-if.directive';
|
|
16
17
|
export * from './declarative-element/directives/test-id.directive';
|
|
17
18
|
export * from './declarative-element/is-declarative-element';
|
|
19
|
+
export * from './declarative-element/is-declarative-element-definition';
|
|
18
20
|
export * from './declarative-element/properties/css-properties';
|
|
19
21
|
export * from './declarative-element/properties/css-vars';
|
|
20
22
|
export * from './declarative-element/properties/element-events';
|
|
21
23
|
export * from './declarative-element/properties/element-properties';
|
|
22
24
|
export * from './declarative-element/properties/element-vir-state-setup';
|
|
23
25
|
export * from './declarative-element/properties/host-classes';
|
|
24
|
-
export * from './declarative-element/properties/observable-prop/interval-observable-prop';
|
|
25
|
-
export * from './declarative-element/properties/observable-prop/observable-prop';
|
|
26
|
-
export * from './declarative-element/properties/observable-prop/setter-observable-prop';
|
|
27
|
-
export * from './declarative-element/properties/observable-prop/updatable-observable-prop';
|
|
28
26
|
export * from './declarative-element/properties/per-instance';
|
|
29
27
|
export * from './declarative-element/properties/styles';
|
|
30
28
|
export * from './declarative-element/properties/tag-name';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export * from 'observavir';
|
|
1
2
|
export * from './declarative-element/custom-tag-name';
|
|
2
3
|
export * from './declarative-element/declarative-element';
|
|
3
4
|
export * from './declarative-element/declarative-element-init';
|
|
@@ -14,16 +15,13 @@ export * from './declarative-element/directives/render-async.directive';
|
|
|
14
15
|
export * from './declarative-element/directives/render-if.directive';
|
|
15
16
|
export * from './declarative-element/directives/test-id.directive';
|
|
16
17
|
export * from './declarative-element/is-declarative-element';
|
|
18
|
+
export * from './declarative-element/is-declarative-element-definition';
|
|
17
19
|
export * from './declarative-element/properties/css-properties';
|
|
18
20
|
export * from './declarative-element/properties/css-vars';
|
|
19
21
|
export * from './declarative-element/properties/element-events';
|
|
20
22
|
export * from './declarative-element/properties/element-properties';
|
|
21
23
|
export * from './declarative-element/properties/element-vir-state-setup';
|
|
22
24
|
export * from './declarative-element/properties/host-classes';
|
|
23
|
-
export * from './declarative-element/properties/observable-prop/interval-observable-prop';
|
|
24
|
-
export * from './declarative-element/properties/observable-prop/observable-prop';
|
|
25
|
-
export * from './declarative-element/properties/observable-prop/setter-observable-prop';
|
|
26
|
-
export * from './declarative-element/properties/observable-prop/updatable-observable-prop';
|
|
27
25
|
export * from './declarative-element/properties/per-instance';
|
|
28
26
|
export * from './declarative-element/properties/styles';
|
|
29
27
|
export * from './declarative-element/properties/tag-name';
|
|
@@ -2,7 +2,11 @@ import { DeclarativeElementDefinitionOptions } from '../declarative-element/defi
|
|
|
2
2
|
import { PropertyInitMapBase } from '../declarative-element/properties/element-properties';
|
|
3
3
|
export type MinimalElementDefinition = {
|
|
4
4
|
tagName: string;
|
|
5
|
-
elementOptions
|
|
5
|
+
elementOptions?: DeclarativeElementDefinitionOptions | undefined;
|
|
6
|
+
/** This is used when wrapping interpolated raw tag name strings. */
|
|
7
|
+
tagInterpolationKey?: {
|
|
8
|
+
tagName: string;
|
|
9
|
+
} | undefined;
|
|
6
10
|
};
|
|
7
11
|
export type MinimalDefinitionWithInputs = {
|
|
8
12
|
_elementVirIsMinimalDefinitionWithInputs: true;
|
|
@@ -10,4 +14,4 @@ export type MinimalDefinitionWithInputs = {
|
|
|
10
14
|
inputs: PropertyInitMapBase;
|
|
11
15
|
};
|
|
12
16
|
export declare function isMinimalDefinitionWithInputs(value: unknown): value is MinimalDefinitionWithInputs;
|
|
13
|
-
export declare function
|
|
17
|
+
export declare function hasTagName(value: unknown): value is MinimalElementDefinition;
|
|
@@ -3,9 +3,6 @@ export function isMinimalDefinitionWithInputs(value) {
|
|
|
3
3
|
return (typedHasProperty(value, '_elementVirIsMinimalDefinitionWithInputs') &&
|
|
4
4
|
!!value._elementVirIsMinimalDefinitionWithInputs);
|
|
5
5
|
}
|
|
6
|
-
export function
|
|
7
|
-
return (typedHasProperty(value, 'tagName') &&
|
|
8
|
-
!!value.tagName &&
|
|
9
|
-
typeof value.tagName === 'string' &&
|
|
10
|
-
value.tagName.includes('-'));
|
|
6
|
+
export function hasTagName(value) {
|
|
7
|
+
return (typedHasProperty(value, 'tagName') && !!value.tagName && typeof value.tagName === 'string');
|
|
11
8
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { filterMap, isTruthy } from '@augment-vir/common';
|
|
2
|
+
import { hasTagName, isMinimalDefinitionWithInputs } from './minimal-element-definition';
|
|
2
3
|
function extractElementKeys(values) {
|
|
3
|
-
return values
|
|
4
|
-
.map((value) => {
|
|
4
|
+
return filterMap(values, (value) => {
|
|
5
5
|
if (isMinimalDefinitionWithInputs(value)) {
|
|
6
6
|
return value.definition;
|
|
7
7
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
return
|
|
12
|
-
});
|
|
8
|
+
if (hasTagName(value)) {
|
|
9
|
+
return value.tagInterpolationKey || value;
|
|
10
|
+
}
|
|
11
|
+
return undefined;
|
|
12
|
+
}, isTruthy);
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
15
|
* The transformed templates are written to a map so that we can preserve reference equality between
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { hasTagName } from '../minimal-element-definition';
|
|
2
2
|
import { transformTemplate } from '../transform-template';
|
|
3
3
|
function transformCss(...[lastNewString, currentLitString, currentValue,]) {
|
|
4
|
-
if (!
|
|
4
|
+
if (!hasTagName(currentValue)) {
|
|
5
5
|
return undefined;
|
|
6
6
|
}
|
|
7
7
|
return {
|
|
@@ -2,7 +2,7 @@ import { AnyFunction } from '@augment-vir/common';
|
|
|
2
2
|
import { CSSResult, TemplateResult, nothing } from 'lit';
|
|
3
3
|
import { EmptyObject } from 'type-fest';
|
|
4
4
|
import { DeclarativeElementDefinition } from '../../declarative-element/declarative-element';
|
|
5
|
-
import { MinimalDefinitionWithInputs } from '../minimal-element-definition';
|
|
5
|
+
import { MinimalDefinitionWithInputs, MinimalElementDefinition } from '../minimal-element-definition';
|
|
6
6
|
/**
|
|
7
7
|
* Unfortunately the type for `DirectiveResult` means it's just an empty object. So in order to
|
|
8
8
|
* block actual objects, we have to narrow `DirectiveResult` further to this empty object type.
|
|
@@ -12,4 +12,4 @@ export type DirectiveOutput = EmptyObject;
|
|
|
12
12
|
* This is used in order to block accidental object interpolations into HTML, which get stringified
|
|
13
13
|
* into `'[object Object]'`, which nobody ever wants that.
|
|
14
14
|
*/
|
|
15
|
-
export type HtmlInterpolation = null | undefined | string | number | boolean | bigint | CSSResult | Readonly<CSSResult> | Element | Readonly<Element> | TemplateResult | Readonly<TemplateResult> | MinimalDefinitionWithInputs | Readonly<MinimalDefinitionWithInputs> | DeclarativeElementDefinition | Readonly<DeclarativeElementDefinition> | DirectiveOutput | Readonly<DirectiveOutput> | AnyFunction | typeof nothing | HtmlInterpolation[] | ReadonlyArray<HtmlInterpolation>;
|
|
15
|
+
export type HtmlInterpolation = null | undefined | string | number | boolean | bigint | CSSResult | Readonly<CSSResult> | Element | Readonly<Element> | TemplateResult | Readonly<TemplateResult> | MinimalElementDefinition | Readonly<MinimalElementDefinition> | MinimalDefinitionWithInputs | Readonly<MinimalDefinitionWithInputs> | DeclarativeElementDefinition | Readonly<DeclarativeElementDefinition> | DirectiveOutput | Readonly<DirectiveOutput> | AnyFunction | typeof nothing | HtmlInterpolation[] | ReadonlyArray<HtmlInterpolation>;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import { HTMLTemplateResult } from '../../lit-exports/all-lit-exports';
|
|
2
2
|
import { TemplateTransform } from '../template-transform-type';
|
|
3
|
+
import { HtmlInterpolation } from './html-interpolation';
|
|
4
|
+
export declare function mapHtmlValues(inputTemplateStrings: TemplateStringsArray, inputValues: HtmlInterpolation[]): HtmlInterpolation[];
|
|
3
5
|
export declare function transformHtmlTemplate(litTemplate: HTMLTemplateResult): TemplateTransform;
|
|
@@ -1,28 +1,65 @@
|
|
|
1
|
-
import { collapseWhiteSpace, isTruthy, safeMatch } from '@augment-vir/common';
|
|
1
|
+
import { collapseWhiteSpace, getOrSet, isTruthy, safeMatch } from '@augment-vir/common';
|
|
2
|
+
import { isRunTimeType } from 'run-time-assertions';
|
|
2
3
|
import { assign } from '../../declarative-element/directives/assign.directive';
|
|
3
4
|
import { declarativeElementRequired } from '../../require-declarative-element';
|
|
4
|
-
import {
|
|
5
|
+
import { hasTagName, isMinimalDefinitionWithInputs, } from '../minimal-element-definition';
|
|
5
6
|
import { transformTemplate } from '../transform-template';
|
|
6
|
-
|
|
7
|
+
import { tagNameKeys } from './tag-name-keys';
|
|
8
|
+
export function mapHtmlValues(inputTemplateStrings, inputValues) {
|
|
9
|
+
return inputValues.map((currentValue, currentValueIndex) => {
|
|
10
|
+
const lastString = inputTemplateStrings[currentValueIndex];
|
|
11
|
+
const nextString = inputTemplateStrings[currentValueIndex + 1];
|
|
12
|
+
if (lastString && nextString) {
|
|
13
|
+
const { shouldHaveTagNameHere } = classifyValue(lastString, nextString);
|
|
14
|
+
if (shouldHaveTagNameHere && isRunTimeType(currentValue, 'string')) {
|
|
15
|
+
const replacement = {
|
|
16
|
+
tagName: currentValue,
|
|
17
|
+
tagInterpolationKey: getOrSet(tagNameKeys, currentValue, () => {
|
|
18
|
+
return { tagName: currentValue };
|
|
19
|
+
}),
|
|
20
|
+
};
|
|
21
|
+
return replacement;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return currentValue;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function classifyValue(lastNewString, currentTemplateString) {
|
|
28
|
+
const isOpeningTag = lastNewString.trim().endsWith('<') && !!currentTemplateString.match(/^[\s\n>]/);
|
|
29
|
+
const isClosingTag = lastNewString?.trim().endsWith('</') && currentTemplateString.trim().startsWith('>');
|
|
30
|
+
const shouldHaveTagNameHere = isOpeningTag || isClosingTag;
|
|
31
|
+
return {
|
|
32
|
+
isOpeningTag,
|
|
33
|
+
shouldHaveTagNameHere,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function transformHtml(...[lastNewString, currentTemplateString, rawCurrentValue,]) {
|
|
7
37
|
const currentValue = isMinimalDefinitionWithInputs(rawCurrentValue)
|
|
8
38
|
? rawCurrentValue.definition
|
|
9
39
|
: rawCurrentValue;
|
|
10
|
-
const isOpeningTag = lastNewString
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
40
|
+
const { isOpeningTag, shouldHaveTagNameHere } = classifyValue(lastNewString, currentTemplateString);
|
|
41
|
+
const isTagNameWrapper = hasTagName(currentValue);
|
|
42
|
+
if (isTagNameWrapper && shouldHaveTagNameHere && currentValue.tagInterpolationKey) {
|
|
43
|
+
return {
|
|
44
|
+
replacement: currentValue.tagName,
|
|
45
|
+
getExtraValues: undefined,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (shouldHaveTagNameHere && !isTagNameWrapper) {
|
|
15
49
|
console.error({
|
|
16
50
|
lastNewString,
|
|
17
|
-
|
|
51
|
+
currentTemplateString,
|
|
18
52
|
currentValue,
|
|
19
53
|
});
|
|
20
|
-
throw new Error(`Got interpolated tag name but found no tag name on the given value: '${currentValue
|
|
54
|
+
throw new Error(`Got interpolated tag name but found no tag name on the given value: '${currentValue?.tagName ||
|
|
55
|
+
currentValue?.prototype?.constructor?.name ||
|
|
56
|
+
currentValue?.constructor?.name}'`);
|
|
21
57
|
}
|
|
22
|
-
if (!shouldHaveTagNameHere || !
|
|
58
|
+
if (!shouldHaveTagNameHere || !isTagNameWrapper) {
|
|
23
59
|
return undefined;
|
|
24
60
|
}
|
|
25
61
|
if (isOpeningTag &&
|
|
62
|
+
currentValue.elementOptions &&
|
|
26
63
|
!currentValue.elementOptions.ignoreUnsetInputs &&
|
|
27
64
|
!isMinimalDefinitionWithInputs(rawCurrentValue)) {
|
|
28
65
|
throw new Error(`Missing inputs for '${currentValue.tagName}'`);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Used to create objects for each interpolated tag name string (tag names that aren't wrapped in a
|
|
3
|
+
* `DeclarativeElementDefinition`) so that we can use them as keys for the mapped template weakmap.
|
|
4
|
+
*/
|
|
5
|
+
export declare const tagNameKeys: Record<string, {
|
|
6
|
+
tagName: string;
|
|
7
|
+
}>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { html as litHtml } from 'lit';
|
|
2
2
|
import { getTransformedTemplate } from '../transform-template';
|
|
3
|
-
import { transformHtmlTemplate } from './html-transform';
|
|
3
|
+
import { mapHtmlValues, transformHtmlTemplate } from './html-transform';
|
|
4
4
|
/**
|
|
5
5
|
* Interprets a template literal as an HTML template which is lazily rendered to the DOM.
|
|
6
6
|
*
|
|
@@ -8,8 +8,9 @@ import { transformHtmlTemplate } from './html-transform';
|
|
|
8
8
|
* `DeclarativeElementDefinition` for tag names.
|
|
9
9
|
*/
|
|
10
10
|
export function html(inputTemplateStrings, ...inputValues) {
|
|
11
|
-
const
|
|
12
|
-
const
|
|
11
|
+
const mappedValues = mapHtmlValues(inputTemplateStrings, inputValues);
|
|
12
|
+
const litTemplate = litHtml(inputTemplateStrings, ...mappedValues);
|
|
13
|
+
const transformedTemplate = getTransformedTemplate(inputTemplateStrings, mappedValues, () => {
|
|
13
14
|
return transformHtmlTemplate(litTemplate);
|
|
14
15
|
});
|
|
15
16
|
const htmlTemplate = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "element-vir",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "22.0.0",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"custom",
|
|
6
6
|
"web",
|
|
@@ -35,35 +35,38 @@
|
|
|
35
35
|
"test": "virmator test-web",
|
|
36
36
|
"test:coverage": "npm run test coverage",
|
|
37
37
|
"test:docs": "virmator docs check",
|
|
38
|
-
"test:types": "
|
|
38
|
+
"test:types": "tsc --pretty --noEmit"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@augment-vir/common": "^
|
|
42
|
-
"date-vir": "^5.1.
|
|
41
|
+
"@augment-vir/common": "^26.2.1",
|
|
42
|
+
"date-vir": "^5.1.3",
|
|
43
43
|
"lit": "^3.1.2",
|
|
44
|
-
"lit-css-vars": "^3.0.
|
|
44
|
+
"lit-css-vars": "^3.0.9",
|
|
45
45
|
"lit-html": "^3.1.2",
|
|
46
|
-
"object-shape-tester": "^2.3.
|
|
47
|
-
"
|
|
46
|
+
"object-shape-tester": "^2.3.2",
|
|
47
|
+
"observavir": "^1.0.0",
|
|
48
|
+
"run-time-assertions": "^1.2.0",
|
|
49
|
+
"typed-event-target": "^3.2.1"
|
|
48
50
|
},
|
|
49
51
|
"devDependencies": {
|
|
50
|
-
"@augment-vir/browser-testing": "^
|
|
51
|
-
"@augment-vir/node-js": "^
|
|
52
|
+
"@augment-vir/browser-testing": "^26.2.1",
|
|
53
|
+
"@augment-vir/node-js": "^26.2.1",
|
|
52
54
|
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
|
53
55
|
"@open-wc/testing": "^4.0.0",
|
|
54
|
-
"@types/chai": "^4.3.
|
|
56
|
+
"@types/chai": "^4.3.14",
|
|
55
57
|
"@types/mocha": "^10.0.6",
|
|
56
58
|
"@web/dev-server-esbuild": "^1.0.2",
|
|
57
|
-
"@web/test-runner": "^0.18.
|
|
59
|
+
"@web/test-runner": "^0.18.1",
|
|
58
60
|
"@web/test-runner-commands": "^0.9.0",
|
|
59
61
|
"@web/test-runner-playwright": "^0.11.0",
|
|
60
62
|
"@web/test-runner-visual-regression": "^0.9.0",
|
|
63
|
+
"html-spec-tags": "^2.2.0",
|
|
61
64
|
"istanbul-smart-text-reporter": "^1.1.4",
|
|
62
65
|
"markdown-code-example-inserter": "^1.0.0",
|
|
63
|
-
"type-fest": "^4.
|
|
64
|
-
"typedoc": "^0.25.
|
|
65
|
-
"typescript": "
|
|
66
|
+
"type-fest": "^4.14.0",
|
|
67
|
+
"typedoc": "^0.25.12",
|
|
68
|
+
"typescript": "5.3.3",
|
|
66
69
|
"vite": "^4.5.0",
|
|
67
|
-
"vite-tsconfig-paths": "^4.3.
|
|
70
|
+
"vite-tsconfig-paths": "^4.3.2"
|
|
68
71
|
}
|
|
69
72
|
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { DeclarativeElement } from '../declarative-element';
|
|
2
|
-
import { PropertyInitMapBase } from './element-properties';
|
|
3
|
-
/** Binds the given property key as a reactive property on the given element. */
|
|
4
|
-
export declare function bindReactiveProperty(element: HTMLElement, propertyKey: PropertyKey): void;
|
|
5
|
-
export declare function createElementUpdaterProxy<PropertyInitGeneric extends PropertyInitMapBase>(element: DeclarativeElement, verifyExists: boolean): PropertyInitGeneric;
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { Duration, DurationUnit } from 'date-vir';
|
|
2
|
-
import { TriggerUpdateFunction, UpdatableObservableProp, UpdatableObservablePropSetup } from './updatable-observable-prop';
|
|
3
|
-
export type IntervalObservablePropSetup<ValueType, UpdateInputType> = UpdatableObservablePropSetup<ValueType, UpdateInputType> & {
|
|
4
|
-
/** Update interval. */
|
|
5
|
-
updateInterval: Duration<DurationUnit.Milliseconds>;
|
|
6
|
-
/**
|
|
7
|
-
* If set to true, the interval observable prop will not automatically start its internal
|
|
8
|
-
* interval.
|
|
9
|
-
*/
|
|
10
|
-
startPaused?: boolean | undefined;
|
|
11
|
-
/**
|
|
12
|
-
* The minimum duration between triggers. If an automatic or manual trigger happens twice within
|
|
13
|
-
* this duration, the second one will not do anything.
|
|
14
|
-
*/
|
|
15
|
-
rateLimit?: Duration<DurationUnit.Milliseconds> | undefined;
|
|
16
|
-
};
|
|
17
|
-
export type IntervalObservableProp<ValueType, UpdateInputType> = Omit<UpdatableObservableProp<ValueType, UpdateInputType>, 'triggerUpdate'> & {
|
|
18
|
-
/** Manually force the observable prop to update outside of its normal interval. */
|
|
19
|
-
forceUpdate: TriggerUpdateFunction<ValueType, UpdateInputType>;
|
|
20
|
-
/**
|
|
21
|
-
* Pauses the update interval, if it isn't already paused. Use .resumeInterval() to start the
|
|
22
|
-
* interval again. Under the hood, this actually clears the interval entirely.
|
|
23
|
-
*/
|
|
24
|
-
pauseInterval(): void;
|
|
25
|
-
/**
|
|
26
|
-
* Resumes the update interval if it was paused. Under the hood, this creates a new interval
|
|
27
|
-
* entirely, as .pauseInterval() actually clears it.
|
|
28
|
-
*/
|
|
29
|
-
resumeInterval(): void;
|
|
30
|
-
/** Cleans up the interval observable prop. */
|
|
31
|
-
destroy(): void;
|
|
32
|
-
};
|
|
33
|
-
/**
|
|
34
|
-
* This creates an updatable observable prop that will automatically update itself at the given
|
|
35
|
-
* interval.
|
|
36
|
-
*/
|
|
37
|
-
export declare function createIntervalObservableProp<ValueType, UpdateInputType = undefined>(setup: IntervalObservablePropSetup<ValueType, UpdateInputType>): IntervalObservableProp<ValueType, UpdateInputType>;
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { createUpdatableObservableProp, } from './updatable-observable-prop';
|
|
2
|
-
/**
|
|
3
|
-
* This creates an updatable observable prop that will automatically update itself at the given
|
|
4
|
-
* interval.
|
|
5
|
-
*/
|
|
6
|
-
export function createIntervalObservableProp(setup) {
|
|
7
|
-
let latestInputs = setup.initInput;
|
|
8
|
-
const baseObservableProp = createUpdatableObservableProp(setup);
|
|
9
|
-
let latestIntervalId = undefined;
|
|
10
|
-
let lastUpdateTimestamp = 0;
|
|
11
|
-
const shouldRunInterval = setup.updateInterval.milliseconds > 0 &&
|
|
12
|
-
Math.abs(setup.updateInterval.milliseconds) !== Infinity;
|
|
13
|
-
let lastOutput = baseObservableProp.value;
|
|
14
|
-
baseObservableProp.addListener((value) => (lastOutput = value));
|
|
15
|
-
function updateValue(...newInputs) {
|
|
16
|
-
if (newInputs.length) {
|
|
17
|
-
latestInputs = newInputs[0];
|
|
18
|
-
}
|
|
19
|
-
const now = Date.now();
|
|
20
|
-
const shouldIgnoreDueToRateLimiting = setup.rateLimit &&
|
|
21
|
-
setup.rateLimit.milliseconds > 0 &&
|
|
22
|
-
now - lastUpdateTimestamp < setup.rateLimit?.milliseconds;
|
|
23
|
-
if (shouldIgnoreDueToRateLimiting) {
|
|
24
|
-
return lastOutput;
|
|
25
|
-
}
|
|
26
|
-
lastUpdateTimestamp = now;
|
|
27
|
-
return baseObservableProp.triggerUpdate(latestInputs);
|
|
28
|
-
}
|
|
29
|
-
function resumeInterval() {
|
|
30
|
-
if (shouldRunInterval && latestIntervalId == undefined) {
|
|
31
|
-
latestIntervalId = window.setInterval(() => {
|
|
32
|
-
updateValue();
|
|
33
|
-
}, Math.ceil(setup.updateInterval.milliseconds));
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
function pauseInterval() {
|
|
37
|
-
if (latestIntervalId != undefined) {
|
|
38
|
-
window.clearInterval(latestIntervalId);
|
|
39
|
-
latestIntervalId = undefined;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
if (!setup.startPaused) {
|
|
43
|
-
resumeInterval();
|
|
44
|
-
}
|
|
45
|
-
const originalDestroy = baseObservableProp.destroy;
|
|
46
|
-
const observablePropertyWithInterval = Object.assign(baseObservableProp, {
|
|
47
|
-
forceUpdate: updateValue,
|
|
48
|
-
pauseInterval,
|
|
49
|
-
resumeInterval,
|
|
50
|
-
destroy() {
|
|
51
|
-
pauseInterval();
|
|
52
|
-
originalDestroy();
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
return observablePropertyWithInterval;
|
|
56
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { PropertyInitMapBase } from '../element-properties';
|
|
2
|
-
export type ObservablePropListener<T> = (value: T) => void;
|
|
3
|
-
export type ObservablePropListenerMap<OriginalPropertyMap extends PropertyInitMapBase> = Partial<Record<keyof OriginalPropertyMap, ObservablePropListener<any> | undefined>>;
|
|
4
|
-
export type ObservableProp<ValueType> = {
|
|
5
|
-
/** Add the given listener. Returns a callback which will remove the listener. */
|
|
6
|
-
addListener(listener: ObservablePropListener<ValueType>): () => boolean;
|
|
7
|
-
/** Remove the given listener by reference. */
|
|
8
|
-
removeListener(listener: ObservablePropListener<ValueType>): boolean;
|
|
9
|
-
destroy(): void;
|
|
10
|
-
value: ValueType;
|
|
11
|
-
};
|
|
12
|
-
export declare const basicObservablePropShape: import("object-shape-tester").ShapeDefinition<ObservableProp<any>, false>;
|
|
13
|
-
export declare function isObservableProp<ValueType = unknown>(input: unknown): input is ObservableProp<ValueType>;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { defineShape, isValidShape, unknownShape } from 'object-shape-tester';
|
|
2
|
-
export const basicObservablePropShape = defineShape({
|
|
3
|
-
addListener() {
|
|
4
|
-
return () => false;
|
|
5
|
-
},
|
|
6
|
-
removeListener() {
|
|
7
|
-
return false;
|
|
8
|
-
},
|
|
9
|
-
destroy() { },
|
|
10
|
-
value: unknownShape(),
|
|
11
|
-
});
|
|
12
|
-
export function isObservableProp(input) {
|
|
13
|
-
return isValidShape(input, basicObservablePropShape, { allowExtraKeys: true });
|
|
14
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { referenceEqualityCheck } from '../../../util/equality';
|
|
2
|
-
import { ObservableProp } from './observable-prop';
|
|
3
|
-
/** A simple ObservableProp with a setter. */
|
|
4
|
-
export type SetterObservableProp<ValueType> = ObservableProp<ValueType> & {
|
|
5
|
-
setValue(newValue: ValueType): void;
|
|
6
|
-
};
|
|
7
|
-
/**
|
|
8
|
-
* Easy and quick way to create an ObservableProp. Includes a setValue method with equality checking
|
|
9
|
-
* for easily emitting changes.
|
|
10
|
-
*/
|
|
11
|
-
export declare function createSetterObservableProp<ValueType>(
|
|
12
|
-
/** The value at which the `SetterObservableProp` will start at. */
|
|
13
|
-
initValue: ValueType,
|
|
14
|
-
/**
|
|
15
|
-
* The function used to determine if a set value is actually new. Defaults to simple reference
|
|
16
|
-
* equality.
|
|
17
|
-
*/
|
|
18
|
-
equalityCallback?: typeof referenceEqualityCheck): SetterObservableProp<ValueType>;
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { referenceEqualityCheck } from '../../../util/equality';
|
|
2
|
-
/**
|
|
3
|
-
* Easy and quick way to create an ObservableProp. Includes a setValue method with equality checking
|
|
4
|
-
* for easily emitting changes.
|
|
5
|
-
*/
|
|
6
|
-
export function createSetterObservableProp(
|
|
7
|
-
/** The value at which the `SetterObservableProp` will start at. */
|
|
8
|
-
initValue,
|
|
9
|
-
/**
|
|
10
|
-
* The function used to determine if a set value is actually new. Defaults to simple reference
|
|
11
|
-
* equality.
|
|
12
|
-
*/
|
|
13
|
-
equalityCallback = referenceEqualityCheck) {
|
|
14
|
-
const listeners = new Set();
|
|
15
|
-
function fireListeners() {
|
|
16
|
-
listeners.forEach((listener) => listener(observableProperty.value));
|
|
17
|
-
}
|
|
18
|
-
function removeListener(listener) {
|
|
19
|
-
return listeners.delete(listener);
|
|
20
|
-
}
|
|
21
|
-
const observableProperty = {
|
|
22
|
-
value: initValue,
|
|
23
|
-
setValue(newValue) {
|
|
24
|
-
if (!equalityCallback(observableProperty.value, newValue)) {
|
|
25
|
-
observableProperty.value = newValue;
|
|
26
|
-
fireListeners();
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
addListener(listener) {
|
|
30
|
-
const shouldAddListener = !listeners.has(listener);
|
|
31
|
-
if (shouldAddListener) {
|
|
32
|
-
listeners.add(listener);
|
|
33
|
-
}
|
|
34
|
-
return () => removeListener(listener);
|
|
35
|
-
},
|
|
36
|
-
removeListener,
|
|
37
|
-
destroy() {
|
|
38
|
-
listeners.clear();
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
return observableProperty;
|
|
42
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { referenceEqualityCheck } from '../../../util/equality';
|
|
2
|
-
import { ObservableProp } from './observable-prop';
|
|
3
|
-
/** Callback for updating the `UpdatableObservableProp`. */
|
|
4
|
-
export type UpdaterCallback<ValueType, UpdateInputType> = (inputs: UpdateInputType, lastValue: ValueType | undefined) => ValueType;
|
|
5
|
-
/** Function for updating the `UpdatableObservableProp`. */
|
|
6
|
-
export type TriggerUpdateFunction<ValueType, UpdateInputType> = (inputs?: UpdateInputType) => ValueType;
|
|
7
|
-
export type UpdatableObservableProp<ValueType, UpdateInputType> = ObservableProp<ValueType | Awaited<ValueType>> & {
|
|
8
|
-
/**
|
|
9
|
-
* Trigger the `UpdatableObservableProp` to update itself. Requires the inputs that the
|
|
10
|
-
* observable prop's updateCallback requires.
|
|
11
|
-
*/
|
|
12
|
-
triggerUpdate: TriggerUpdateFunction<ValueType, UpdateInputType>;
|
|
13
|
-
/** The number of times that the prop has been updated. Mostly only useful for debugging. */
|
|
14
|
-
readonly updateCount: number;
|
|
15
|
-
/**
|
|
16
|
-
* The last value that was resolved. This will be undefined if there has never, so far, been a
|
|
17
|
-
* resolved value.
|
|
18
|
-
*/
|
|
19
|
-
latestResolvedValue: ValueType extends Promise<any> ? Awaited<ValueType> | undefined : ValueType;
|
|
20
|
-
};
|
|
21
|
-
/** Setup required for creating an `UpdatableObservableProp`. */
|
|
22
|
-
export type UpdatableObservablePropSetup<ValueType, UpdateInputType> = {
|
|
23
|
-
/** Initial value for the observable prop. */
|
|
24
|
-
initInput: UpdateInputType;
|
|
25
|
-
/** The callback which will be called in order to update the observable prop. */
|
|
26
|
-
updateCallback: UpdaterCallback<ValueType, UpdateInputType>;
|
|
27
|
-
/**
|
|
28
|
-
* The function used to determine if a new value is actually new. Defaults to simple reference
|
|
29
|
-
* equality.
|
|
30
|
-
*/
|
|
31
|
-
equalityCallback?: typeof referenceEqualityCheck | undefined;
|
|
32
|
-
};
|
|
33
|
-
/**
|
|
34
|
-
* An ObservableProp that requires an updater callback on setup. Future updates then call this
|
|
35
|
-
* updater callback rather than directly setting a value (`SetterObservableProp` merely sets a
|
|
36
|
-
* value).
|
|
37
|
-
*/
|
|
38
|
-
export declare function createUpdatableObservableProp<ValueType, UpdateInputType = undefined>(
|
|
39
|
-
/** Setup object required for creating the UpdatableObservableProp. */
|
|
40
|
-
setup: UpdatableObservablePropSetup<ValueType, UpdateInputType>): UpdatableObservableProp<ValueType, UpdateInputType>;
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { referenceEqualityCheck } from '../../../util/equality';
|
|
2
|
-
import { createSetterObservableProp } from './setter-observable-prop';
|
|
3
|
-
/**
|
|
4
|
-
* An ObservableProp that requires an updater callback on setup. Future updates then call this
|
|
5
|
-
* updater callback rather than directly setting a value (`SetterObservableProp` merely sets a
|
|
6
|
-
* value).
|
|
7
|
-
*/
|
|
8
|
-
export function createUpdatableObservableProp(
|
|
9
|
-
/** Setup object required for creating the UpdatableObservableProp. */
|
|
10
|
-
setup) {
|
|
11
|
-
const areEqual = setup.equalityCallback ?? referenceEqualityCheck;
|
|
12
|
-
let lastInputs = setup.initInput;
|
|
13
|
-
let internalUpdateCount = 0;
|
|
14
|
-
const innerSimpleObservableProp = createSetterObservableProp(undefined, areEqual);
|
|
15
|
-
function updateValue(...inputs) {
|
|
16
|
-
internalUpdateCount++;
|
|
17
|
-
if (inputs.length) {
|
|
18
|
-
lastInputs = inputs[0];
|
|
19
|
-
}
|
|
20
|
-
const newValue = setup.updateCallback(lastInputs, innerSimpleObservableProp.value);
|
|
21
|
-
if (newValue instanceof Promise) {
|
|
22
|
-
const wrappedPromise = new Promise(async (resolve, reject) => {
|
|
23
|
-
try {
|
|
24
|
-
const resolvedValue = await newValue;
|
|
25
|
-
observableWithUpdater.latestResolvedValue =
|
|
26
|
-
resolvedValue;
|
|
27
|
-
innerSimpleObservableProp.setValue(resolvedValue);
|
|
28
|
-
resolve(resolvedValue);
|
|
29
|
-
}
|
|
30
|
-
catch (error) {
|
|
31
|
-
reject(error);
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
/** Set the promise so consumers know it's loading. */
|
|
35
|
-
innerSimpleObservableProp.setValue(wrappedPromise);
|
|
36
|
-
return wrappedPromise;
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
innerSimpleObservableProp.setValue(newValue);
|
|
40
|
-
observableWithUpdater.latestResolvedValue = newValue;
|
|
41
|
-
return newValue;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
const observableWithUpdater = Object.assign(innerSimpleObservableProp, {
|
|
45
|
-
triggerUpdate: updateValue,
|
|
46
|
-
latestResolvedValue: undefined,
|
|
47
|
-
updateCount: internalUpdateCount,
|
|
48
|
-
});
|
|
49
|
-
Object.defineProperty(observableWithUpdater, 'updateCount', {
|
|
50
|
-
get() {
|
|
51
|
-
return internalUpdateCount;
|
|
52
|
-
},
|
|
53
|
-
});
|
|
54
|
-
try {
|
|
55
|
-
updateValue(setup.initInput);
|
|
56
|
-
}
|
|
57
|
-
catch (error) {
|
|
58
|
-
console.error(error);
|
|
59
|
-
}
|
|
60
|
-
return observableWithUpdater;
|
|
61
|
-
}
|
package/dist/util/equality.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function referenceEqualityCheck<T>(a: T, b: T): boolean;
|
package/dist/util/equality.js
DELETED