element-vir 25.15.0 → 26.0.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 +3 -3
- package/dist/declarative-element/define-element.js +193 -4
- package/dist/declarative-element/wrap-define-element.d.ts +1 -10
- package/dist/declarative-element/wrap-define-element.js +4 -19
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/readme-examples/my-custom-define.js +3 -3
- package/package.json +1 -1
- package/dist/declarative-element/define-element-no-inputs.d.ts +0 -13
- package/dist/declarative-element/define-element-no-inputs.js +0 -200
package/README.md
CHANGED
|
@@ -499,10 +499,10 @@ import {wrapDefineElement} from 'element-vir';
|
|
|
499
499
|
|
|
500
500
|
export type VirTagName = `vir-${string}`;
|
|
501
501
|
|
|
502
|
-
export const
|
|
502
|
+
export const defineVirElement = wrapDefineElement<VirTagName>();
|
|
503
503
|
|
|
504
504
|
// add an optional assert callback
|
|
505
|
-
export const
|
|
505
|
+
export const defineVerifiedVirElement = wrapDefineElement<VirTagName>({
|
|
506
506
|
assertInputs: (inputs) => {
|
|
507
507
|
if (!inputs.tagName.startsWith('vir-')) {
|
|
508
508
|
throw new Error(`all custom elements must start with "vir-"`);
|
|
@@ -511,7 +511,7 @@ export const {defineElement: defineVerifiedVirElement} = wrapDefineElement<VirTa
|
|
|
511
511
|
});
|
|
512
512
|
|
|
513
513
|
// add an optional transform callback
|
|
514
|
-
export const
|
|
514
|
+
export const defineTransformedVirElement = wrapDefineElement<VirTagName>({
|
|
515
515
|
transformInputs: (inputs) => {
|
|
516
516
|
return {
|
|
517
517
|
...inputs,
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
|
2
2
|
import { assert, check } from '@augment-vir/assert';
|
|
3
|
-
|
|
4
|
-
import {
|
|
3
|
+
import { ensureErrorAndPrependMessage, extractErrorMessage, getObjectTypedKeys, kebabCaseToCamelCase, } from '@augment-vir/common';
|
|
4
|
+
import { defineCssVars } from 'lit-css-vars';
|
|
5
|
+
import { css } from '../template-transforms/vir-css/vir-css.js';
|
|
6
|
+
import { DeclarativeElement, } from './declarative-element.js';
|
|
7
|
+
import { defaultDeclarativeElementDefinitionOptions, } from './definition-options.js';
|
|
8
|
+
import { assignInputs } from './properties/assign-inputs.js';
|
|
9
|
+
import { assertValidCssProperties } from './properties/css-properties.js';
|
|
10
|
+
import { createEventDescriptorMap, } from './properties/element-events.js';
|
|
11
|
+
import { createHostClassNamesMap } from './properties/host-classes.js';
|
|
12
|
+
import { bindReactiveProperty, createElementPropertyProxy } from './properties/property-proxy.js';
|
|
13
|
+
import { applyHostClasses, createStylesCallbackInput } from './properties/styles.js';
|
|
14
|
+
import { createRenderParams } from './render-callback.js';
|
|
15
|
+
import { createSlotNamesMap } from './slot-names.js';
|
|
5
16
|
/**
|
|
6
17
|
* Defines an element with inputs. Note that this function must be called twice, due to TypeScript
|
|
7
18
|
* type inference limitations.
|
|
@@ -34,8 +45,7 @@ export function defineElement(
|
|
|
34
45
|
if (!check.isObject(init)) {
|
|
35
46
|
throw new TypeError('Cannot define element with non-object init: ${init}');
|
|
36
47
|
}
|
|
37
|
-
|
|
38
|
-
return defineElementNoInputs({
|
|
48
|
+
return internalDefineElement({
|
|
39
49
|
...init,
|
|
40
50
|
options: {
|
|
41
51
|
...init.options,
|
|
@@ -43,3 +53,182 @@ export function defineElement(
|
|
|
43
53
|
});
|
|
44
54
|
};
|
|
45
55
|
}
|
|
56
|
+
function internalDefineElement(init) {
|
|
57
|
+
if (!check.isObject(init)) {
|
|
58
|
+
throw new TypeError('Cannot define element with non-object init: ${init}');
|
|
59
|
+
}
|
|
60
|
+
if (!check.isString(init.tagName)) {
|
|
61
|
+
throw new TypeError('Missing valid tagName (expected a string).');
|
|
62
|
+
}
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
64
|
+
if (!init.render || typeof init.render === 'string') {
|
|
65
|
+
throw new Error(`Failed to define element '${init.tagName}': render is not a function`);
|
|
66
|
+
}
|
|
67
|
+
const elementOptions = {
|
|
68
|
+
...defaultDeclarativeElementDefinitionOptions,
|
|
69
|
+
...init.options,
|
|
70
|
+
};
|
|
71
|
+
const eventsMap = createEventDescriptorMap(init.tagName, init.events);
|
|
72
|
+
const hostClassNames = createHostClassNamesMap(init.hostClasses);
|
|
73
|
+
if (init.hostClasses) {
|
|
74
|
+
assertValidCssProperties(init.tagName, init.hostClasses);
|
|
75
|
+
}
|
|
76
|
+
if (init.cssVars) {
|
|
77
|
+
assertValidCssProperties(init.tagName, init.cssVars);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* As casts here are to prevent defineCssVars from complaining that our CSS var names are too
|
|
81
|
+
* generic or the names not being in kebab-case. (Which, in this line of code, are indeed true
|
|
82
|
+
* errors. However, this is for internal types only and the user will actually see much more
|
|
83
|
+
* specific types externally.)
|
|
84
|
+
*/
|
|
85
|
+
const cssVars = (init.cssVars ? defineCssVars(init.cssVars) : {});
|
|
86
|
+
const slotNamesMap = createSlotNamesMap(init.slotNames);
|
|
87
|
+
const calculatedStyles = typeof init.styles === 'function'
|
|
88
|
+
? init.styles(createStylesCallbackInput({ hostClassNames, cssVars }))
|
|
89
|
+
: init.styles || css ``;
|
|
90
|
+
const typedRenderCallback = init.render;
|
|
91
|
+
function typedAssignCallback(...[inputs]) {
|
|
92
|
+
const wrappedDefinition = {
|
|
93
|
+
_elementVirIsMinimalDefinitionWithInputs: true,
|
|
94
|
+
definition: anonymousClass,
|
|
95
|
+
inputs,
|
|
96
|
+
};
|
|
97
|
+
return wrappedDefinition;
|
|
98
|
+
}
|
|
99
|
+
const anonymousClass = class extends DeclarativeElement {
|
|
100
|
+
static elementOptions = elementOptions;
|
|
101
|
+
static tagName = init.tagName;
|
|
102
|
+
static styles = calculatedStyles;
|
|
103
|
+
_lastRenderError = undefined;
|
|
104
|
+
_internalRenderCount = 0;
|
|
105
|
+
createRenderParams() {
|
|
106
|
+
return createRenderParams({ element: this, eventsMap, cssVars, slotNamesMap });
|
|
107
|
+
}
|
|
108
|
+
static assign = typedAssignCallback;
|
|
109
|
+
static events = eventsMap;
|
|
110
|
+
static render = typedRenderCallback;
|
|
111
|
+
static hostClasses = hostClassNames;
|
|
112
|
+
static cssVars = cssVars;
|
|
113
|
+
static init = init;
|
|
114
|
+
static slotNames = slotNamesMap;
|
|
115
|
+
get InstanceType() {
|
|
116
|
+
throw new Error(`'InstanceType' was called on ${init.tagName} as a value but it is only a type.`);
|
|
117
|
+
}
|
|
118
|
+
static get InputsType() {
|
|
119
|
+
throw new Error(`'InputsType' was called on ${init.tagName} as a value but it is only a type.`);
|
|
120
|
+
}
|
|
121
|
+
static get StateType() {
|
|
122
|
+
throw new Error(`'StateType' was called on ${init.tagName} as a value but it is only a type.`);
|
|
123
|
+
}
|
|
124
|
+
static get UpdateStateType() {
|
|
125
|
+
throw new Error(`'UpdateStateType' was called on ${init.tagName} as a value but it is only a type.`);
|
|
126
|
+
}
|
|
127
|
+
_initCalled = false;
|
|
128
|
+
_stateCalled = false;
|
|
129
|
+
_hasRendered = false;
|
|
130
|
+
_lastRenderedProps = undefined;
|
|
131
|
+
render() {
|
|
132
|
+
this._internalRenderCount++;
|
|
133
|
+
try {
|
|
134
|
+
this._hasRendered = true;
|
|
135
|
+
const renderParams = this.createRenderParams();
|
|
136
|
+
if (!this._stateCalled && init.state) {
|
|
137
|
+
this._stateCalled = true;
|
|
138
|
+
const stateInit = init.state(renderParams);
|
|
139
|
+
if (stateInit instanceof Promise) {
|
|
140
|
+
throw new TypeError('init cannot be asynchronous');
|
|
141
|
+
}
|
|
142
|
+
getObjectTypedKeys(stateInit).forEach((stateKey) => {
|
|
143
|
+
bindReactiveProperty(this, stateKey);
|
|
144
|
+
this.instanceState[stateKey] = stateInit[stateKey];
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
if (!this._initCalled && init.init) {
|
|
148
|
+
this._initCalled = true;
|
|
149
|
+
if (init.init(renderParams) instanceof Promise) {
|
|
150
|
+
throw new TypeError('init cannot be asynchronous');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const renderResult = typedRenderCallback(renderParams);
|
|
154
|
+
if (renderResult instanceof Promise) {
|
|
155
|
+
throw new TypeError('render cannot be asynchronous');
|
|
156
|
+
}
|
|
157
|
+
applyHostClasses({
|
|
158
|
+
host: renderParams.host,
|
|
159
|
+
hostClassesInit: init.hostClasses,
|
|
160
|
+
hostClassNames,
|
|
161
|
+
state: renderParams.state,
|
|
162
|
+
inputs: renderParams.inputs,
|
|
163
|
+
});
|
|
164
|
+
this._lastRenderedProps = {
|
|
165
|
+
inputs: { ...renderParams.inputs },
|
|
166
|
+
state: { ...renderParams.state },
|
|
167
|
+
};
|
|
168
|
+
return renderResult;
|
|
169
|
+
}
|
|
170
|
+
catch (caught) {
|
|
171
|
+
const error = ensureErrorAndPrependMessage(caught, `Failed to render ${init.tagName}`);
|
|
172
|
+
console.error(error);
|
|
173
|
+
this._lastRenderError = error;
|
|
174
|
+
return extractErrorMessage(error);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
connectedCallback() {
|
|
178
|
+
super.connectedCallback();
|
|
179
|
+
if (this._hasRendered && !this._initCalled && init.init) {
|
|
180
|
+
this._initCalled = true;
|
|
181
|
+
const renderParams = this.createRenderParams();
|
|
182
|
+
if (init.init(renderParams) instanceof Promise) {
|
|
183
|
+
throw new TypeError(`init in '${init.tagName}' cannot be asynchronous`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
destroy() {
|
|
188
|
+
Object.values(this.instanceState).forEach((stateValue) => {
|
|
189
|
+
if (check.hasKey(stateValue, 'destroy') && check.isFunction(stateValue.destroy)) {
|
|
190
|
+
stateValue.destroy();
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
disconnectedCallback() {
|
|
195
|
+
super.disconnectedCallback();
|
|
196
|
+
if (init.cleanup) {
|
|
197
|
+
const renderParams = this.createRenderParams();
|
|
198
|
+
if (init.cleanup(renderParams) instanceof Promise) {
|
|
199
|
+
throw new TypeError(`cleanup in '${init.tagName}' cannot be asynchronous`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
this.destroy();
|
|
203
|
+
this._initCalled = false;
|
|
204
|
+
this._stateCalled = false;
|
|
205
|
+
}
|
|
206
|
+
// this is set below in Object.defineProperties
|
|
207
|
+
definition = {};
|
|
208
|
+
assignInputs(inputs) {
|
|
209
|
+
assignInputs(this, inputs);
|
|
210
|
+
}
|
|
211
|
+
observablePropertyListenerMap = {};
|
|
212
|
+
instanceInputs = createElementPropertyProxy(this, false);
|
|
213
|
+
instanceState = createElementPropertyProxy(this, !elementOptions.allowPolymorphicState);
|
|
214
|
+
constructor() {
|
|
215
|
+
super();
|
|
216
|
+
this.definition = anonymousClass;
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
Object.defineProperties(anonymousClass, {
|
|
220
|
+
name: {
|
|
221
|
+
value: kebabCaseToCamelCase(init.tagName, {
|
|
222
|
+
capitalizeFirstLetter: true,
|
|
223
|
+
}),
|
|
224
|
+
writable: true,
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
if (window.customElements.get(init.tagName)) {
|
|
228
|
+
console.warn(`Tried to define custom element '${init.tagName}' but it is already defined.`);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
window.customElements.define(init.tagName, anonymousClass);
|
|
232
|
+
}
|
|
233
|
+
return anonymousClass;
|
|
234
|
+
}
|
|
@@ -33,13 +33,4 @@ export type WrapDefineElementOptions<TagNameRequirement extends CustomElementTag
|
|
|
33
33
|
*
|
|
34
34
|
* @category Element Definition
|
|
35
35
|
*/
|
|
36
|
-
export declare function wrapDefineElement<TagNameRequirement extends CustomElementTagName = CustomElementTagName, InputsRequirement extends PropertyInitMapBase = {}, StateRequirement extends PropertyInitMapBase = {}, EventsInitRequirement extends EventsInitMap = {}>(options?: WrapDefineElementOptions | undefined): {
|
|
37
|
-
/** A wrapped function for defining an element with inputs. */
|
|
38
|
-
defineElement: <Inputs extends InputsRequirement>(...errorParams: DeclarativeElementInputErrorParams<Inputs>) => <const TagName extends TagNameRequirement, State extends StateRequirement, EventsInit extends EventsInitRequirement, const HostClassKeys extends BaseCssPropertyName<TagName> = `${TagName}-`, const CssVarKeys extends BaseCssPropertyName<TagName> = `${TagName}-`, const SlotNames extends ReadonlyArray<string> = Readonly<[]>>(inputs: DeclarativeElementInit<TagName, Inputs, State, EventsInit, HostClassKeys, CssVarKeys, SlotNames>) => import("./declarative-element.js").DeclarativeElementDefinition<TagName, Inputs, State, EventsInit, HostClassKeys, CssVarKeys, SlotNames>;
|
|
39
|
-
/**
|
|
40
|
-
* A wrapped function for defining an element without inputs.
|
|
41
|
-
*
|
|
42
|
-
* @deprecated Use `defineElement` instead.
|
|
43
|
-
*/
|
|
44
|
-
defineElementNoInputs: <const TagName extends TagNameRequirement, Inputs extends InputsRequirement, State extends StateRequirement, EventsInit extends EventsInitRequirement, const HostClassKeys extends BaseCssPropertyName<TagName> = `${TagName}-`, const CssVarKeys extends BaseCssPropertyName<TagName> = `${TagName}-`, const SlotNames extends ReadonlyArray<string> = readonly []>(inputs: DeclarativeElementInit<TagName, Inputs, State, EventsInit, HostClassKeys, CssVarKeys, SlotNames>) => import("./declarative-element.js").DeclarativeElementDefinition<TagName, Inputs, State, EventsInit, HostClassKeys, CssVarKeys, SlotNames>;
|
|
45
|
-
};
|
|
36
|
+
export declare function wrapDefineElement<TagNameRequirement extends CustomElementTagName = CustomElementTagName, InputsRequirement extends PropertyInitMapBase = {}, StateRequirement extends PropertyInitMapBase = {}, EventsInitRequirement extends EventsInitMap = {}>(options?: WrapDefineElementOptions | undefined): <Inputs extends InputsRequirement>(...errorParams: DeclarativeElementInputErrorParams<Inputs>) => <const TagName extends TagNameRequirement, State extends StateRequirement, EventsInit extends EventsInitRequirement, const HostClassKeys extends BaseCssPropertyName<TagName> = `${TagName}-`, const CssVarKeys extends BaseCssPropertyName<TagName> = `${TagName}-`, const SlotNames extends ReadonlyArray<string> = Readonly<[]>>(inputs: DeclarativeElementInit<TagName, Inputs, State, EventsInit, HostClassKeys, CssVarKeys, SlotNames>) => import("./declarative-element.js").DeclarativeElementDefinition<TagName, Inputs, State, EventsInit, HostClassKeys, CssVarKeys, SlotNames>;
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
|
2
2
|
import { defineElement } from './define-element.js';
|
|
3
|
-
// eslint-disable-next-line sonarjs/deprecation
|
|
4
|
-
import { defineElementNoInputs } from './define-element-no-inputs.js';
|
|
5
3
|
/**
|
|
6
4
|
* Wraps {@link defineElement} in a superset of requirements. For example:
|
|
7
5
|
*
|
|
@@ -18,23 +16,10 @@ export function wrapDefineElement(options) {
|
|
|
18
16
|
assertInputs: options?.assertInputs ?? (() => { }),
|
|
19
17
|
transformInputs: options?.transformInputs ?? ((inputs) => inputs),
|
|
20
18
|
};
|
|
21
|
-
return {
|
|
22
|
-
|
|
23
|
-
defineElement: (...errorParams) => {
|
|
24
|
-
return (inputs) => {
|
|
25
|
-
assertInputs(inputs);
|
|
26
|
-
return defineElement(...errorParams)(transformInputs(inputs));
|
|
27
|
-
};
|
|
28
|
-
},
|
|
29
|
-
/**
|
|
30
|
-
* A wrapped function for defining an element without inputs.
|
|
31
|
-
*
|
|
32
|
-
* @deprecated Use `defineElement` instead.
|
|
33
|
-
*/
|
|
34
|
-
defineElementNoInputs: (inputs) => {
|
|
19
|
+
return (...errorParams) => {
|
|
20
|
+
return (inputs) => {
|
|
35
21
|
assertInputs(inputs);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
},
|
|
22
|
+
return defineElement(...errorParams)(transformInputs(inputs));
|
|
23
|
+
};
|
|
39
24
|
};
|
|
40
25
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ export * from 'observavir';
|
|
|
2
2
|
export * from './declarative-element/custom-tag-name.js';
|
|
3
3
|
export * from './declarative-element/declarative-element-init.js';
|
|
4
4
|
export * from './declarative-element/declarative-element.js';
|
|
5
|
-
export * from './declarative-element/define-element-no-inputs.js';
|
|
6
5
|
export * from './declarative-element/define-element.js';
|
|
7
6
|
export * from './declarative-element/definition-options.js';
|
|
8
7
|
export * from './declarative-element/directives/async-prop.js';
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,6 @@ export * from 'observavir';
|
|
|
2
2
|
export * from './declarative-element/custom-tag-name.js';
|
|
3
3
|
export * from './declarative-element/declarative-element-init.js';
|
|
4
4
|
export * from './declarative-element/declarative-element.js';
|
|
5
|
-
export * from './declarative-element/define-element-no-inputs.js';
|
|
6
5
|
export * from './declarative-element/define-element.js';
|
|
7
6
|
export * from './declarative-element/definition-options.js';
|
|
8
7
|
export * from './declarative-element/directives/async-prop.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { wrapDefineElement } from '../index.js';
|
|
2
|
-
export const
|
|
2
|
+
export const defineVirElement = wrapDefineElement();
|
|
3
3
|
// add an optional assert callback
|
|
4
|
-
export const
|
|
4
|
+
export const defineVerifiedVirElement = wrapDefineElement({
|
|
5
5
|
assertInputs: (inputs) => {
|
|
6
6
|
if (!inputs.tagName.startsWith('vir-')) {
|
|
7
7
|
throw new Error(`all custom elements must start with "vir-"`);
|
|
@@ -9,7 +9,7 @@ export const { defineElement: defineVerifiedVirElement } = wrapDefineElement({
|
|
|
9
9
|
},
|
|
10
10
|
});
|
|
11
11
|
// add an optional transform callback
|
|
12
|
-
export const
|
|
12
|
+
export const defineTransformedVirElement = wrapDefineElement({
|
|
13
13
|
transformInputs: (inputs) => {
|
|
14
14
|
return {
|
|
15
15
|
...inputs,
|
package/package.json
CHANGED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { type CustomElementTagName } from './custom-tag-name.js';
|
|
2
|
-
import { type DeclarativeElementInit } from './declarative-element-init.js';
|
|
3
|
-
import { type DeclarativeElementDefinition } from './declarative-element.js';
|
|
4
|
-
import { type BaseCssPropertyName } from './properties/css-properties.js';
|
|
5
|
-
import { type EventsInitMap } from './properties/element-events.js';
|
|
6
|
-
import { type PropertyInitMapBase } from './properties/element-properties.js';
|
|
7
|
-
/**
|
|
8
|
-
* Defines an element without any inputs.
|
|
9
|
-
*
|
|
10
|
-
* @deprecated Use plain `defineElement` instead.
|
|
11
|
-
* @category Internal
|
|
12
|
-
*/
|
|
13
|
-
export declare function defineElementNoInputs<const TagName extends CustomElementTagName = '-', Inputs extends PropertyInitMapBase = {}, State extends PropertyInitMapBase = {}, EventsInit extends EventsInitMap = {}, const HostClassKeys extends BaseCssPropertyName<TagName> = `${TagName}-`, const CssVarKeys extends BaseCssPropertyName<TagName> = `${TagName}-`, const SlotNames extends ReadonlyArray<string> = Readonly<[]>>(init: DeclarativeElementInit<TagName, Inputs, State, EventsInit, HostClassKeys, CssVarKeys, SlotNames>): DeclarativeElementDefinition<TagName, Inputs, State, EventsInit, HostClassKeys, CssVarKeys, SlotNames>;
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
|
2
|
-
import { check } from '@augment-vir/assert';
|
|
3
|
-
import { ensureErrorAndPrependMessage, extractErrorMessage, getObjectTypedKeys, kebabCaseToCamelCase, } from '@augment-vir/common';
|
|
4
|
-
import { defineCssVars } from 'lit-css-vars';
|
|
5
|
-
import { css } from '../template-transforms/vir-css/vir-css.js';
|
|
6
|
-
import { DeclarativeElement, } from './declarative-element.js';
|
|
7
|
-
import { defaultDeclarativeElementDefinitionOptions, } from './definition-options.js';
|
|
8
|
-
import { assignInputs } from './properties/assign-inputs.js';
|
|
9
|
-
import { assertValidCssProperties } from './properties/css-properties.js';
|
|
10
|
-
import { createEventDescriptorMap, } from './properties/element-events.js';
|
|
11
|
-
import { createHostClassNamesMap } from './properties/host-classes.js';
|
|
12
|
-
import { bindReactiveProperty, createElementPropertyProxy } from './properties/property-proxy.js';
|
|
13
|
-
import { applyHostClasses, createStylesCallbackInput } from './properties/styles.js';
|
|
14
|
-
import { createRenderParams } from './render-callback.js';
|
|
15
|
-
import { createSlotNamesMap } from './slot-names.js';
|
|
16
|
-
/**
|
|
17
|
-
* Defines an element without any inputs.
|
|
18
|
-
*
|
|
19
|
-
* @deprecated Use plain `defineElement` instead.
|
|
20
|
-
* @category Internal
|
|
21
|
-
*/
|
|
22
|
-
export function defineElementNoInputs(init) {
|
|
23
|
-
if (!check.isObject(init)) {
|
|
24
|
-
throw new TypeError('Cannot define element with non-object init: ${init}');
|
|
25
|
-
}
|
|
26
|
-
if (!check.isString(init.tagName)) {
|
|
27
|
-
throw new TypeError('Missing valid tagName (expected a string).');
|
|
28
|
-
}
|
|
29
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
30
|
-
if (!init.render || typeof init.render === 'string') {
|
|
31
|
-
throw new Error(`Failed to define element '${init.tagName}': render is not a function`);
|
|
32
|
-
}
|
|
33
|
-
const elementOptions = {
|
|
34
|
-
...defaultDeclarativeElementDefinitionOptions,
|
|
35
|
-
...init.options,
|
|
36
|
-
};
|
|
37
|
-
const eventsMap = createEventDescriptorMap(init.tagName, init.events);
|
|
38
|
-
const hostClassNames = createHostClassNamesMap(init.hostClasses);
|
|
39
|
-
if (init.hostClasses) {
|
|
40
|
-
assertValidCssProperties(init.tagName, init.hostClasses);
|
|
41
|
-
}
|
|
42
|
-
if (init.cssVars) {
|
|
43
|
-
assertValidCssProperties(init.tagName, init.cssVars);
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* As casts here are to prevent defineCssVars from complaining that our CSS var names are too
|
|
47
|
-
* generic or the names not being in kebab-case. (Which, in this line of code, are indeed true
|
|
48
|
-
* errors. However, this is for internal types only and the user will actually see much more
|
|
49
|
-
* specific types externally.)
|
|
50
|
-
*/
|
|
51
|
-
const cssVars = (init.cssVars ? defineCssVars(init.cssVars) : {});
|
|
52
|
-
const slotNamesMap = createSlotNamesMap(init.slotNames);
|
|
53
|
-
const calculatedStyles = typeof init.styles === 'function'
|
|
54
|
-
? init.styles(createStylesCallbackInput({ hostClassNames, cssVars }))
|
|
55
|
-
: init.styles || css ``;
|
|
56
|
-
const typedRenderCallback = init.render;
|
|
57
|
-
function typedAssignCallback(...[inputs]) {
|
|
58
|
-
const wrappedDefinition = {
|
|
59
|
-
_elementVirIsMinimalDefinitionWithInputs: true,
|
|
60
|
-
definition: anonymousClass,
|
|
61
|
-
inputs,
|
|
62
|
-
};
|
|
63
|
-
return wrappedDefinition;
|
|
64
|
-
}
|
|
65
|
-
const anonymousClass = class extends DeclarativeElement {
|
|
66
|
-
static elementOptions = elementOptions;
|
|
67
|
-
static tagName = init.tagName;
|
|
68
|
-
static styles = calculatedStyles;
|
|
69
|
-
_lastRenderError = undefined;
|
|
70
|
-
_internalRenderCount = 0;
|
|
71
|
-
createRenderParams() {
|
|
72
|
-
return createRenderParams({ element: this, eventsMap, cssVars, slotNamesMap });
|
|
73
|
-
}
|
|
74
|
-
static assign = typedAssignCallback;
|
|
75
|
-
static events = eventsMap;
|
|
76
|
-
static render = typedRenderCallback;
|
|
77
|
-
static hostClasses = hostClassNames;
|
|
78
|
-
static cssVars = cssVars;
|
|
79
|
-
static init = init;
|
|
80
|
-
static slotNames = slotNamesMap;
|
|
81
|
-
get InstanceType() {
|
|
82
|
-
throw new Error(`'InstanceType' was called on ${init.tagName} as a value but it is only a type.`);
|
|
83
|
-
}
|
|
84
|
-
static get InputsType() {
|
|
85
|
-
throw new Error(`'InputsType' was called on ${init.tagName} as a value but it is only a type.`);
|
|
86
|
-
}
|
|
87
|
-
static get StateType() {
|
|
88
|
-
throw new Error(`'StateType' was called on ${init.tagName} as a value but it is only a type.`);
|
|
89
|
-
}
|
|
90
|
-
static get UpdateStateType() {
|
|
91
|
-
throw new Error(`'UpdateStateType' was called on ${init.tagName} as a value but it is only a type.`);
|
|
92
|
-
}
|
|
93
|
-
_initCalled = false;
|
|
94
|
-
_stateCalled = false;
|
|
95
|
-
_hasRendered = false;
|
|
96
|
-
_lastRenderedProps = undefined;
|
|
97
|
-
render() {
|
|
98
|
-
this._internalRenderCount++;
|
|
99
|
-
try {
|
|
100
|
-
this._hasRendered = true;
|
|
101
|
-
const renderParams = this.createRenderParams();
|
|
102
|
-
if (!this._stateCalled && init.state) {
|
|
103
|
-
this._stateCalled = true;
|
|
104
|
-
const stateInit = init.state(renderParams);
|
|
105
|
-
if (stateInit instanceof Promise) {
|
|
106
|
-
throw new TypeError('init cannot be asynchronous');
|
|
107
|
-
}
|
|
108
|
-
getObjectTypedKeys(stateInit).forEach((stateKey) => {
|
|
109
|
-
bindReactiveProperty(this, stateKey);
|
|
110
|
-
this.instanceState[stateKey] = stateInit[stateKey];
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
if (!this._initCalled && init.init) {
|
|
114
|
-
this._initCalled = true;
|
|
115
|
-
if (init.init(renderParams) instanceof Promise) {
|
|
116
|
-
throw new TypeError('init cannot be asynchronous');
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
const renderResult = typedRenderCallback(renderParams);
|
|
120
|
-
if (renderResult instanceof Promise) {
|
|
121
|
-
throw new TypeError('render cannot be asynchronous');
|
|
122
|
-
}
|
|
123
|
-
applyHostClasses({
|
|
124
|
-
host: renderParams.host,
|
|
125
|
-
hostClassesInit: init.hostClasses,
|
|
126
|
-
hostClassNames,
|
|
127
|
-
state: renderParams.state,
|
|
128
|
-
inputs: renderParams.inputs,
|
|
129
|
-
});
|
|
130
|
-
this._lastRenderedProps = {
|
|
131
|
-
inputs: { ...renderParams.inputs },
|
|
132
|
-
state: { ...renderParams.state },
|
|
133
|
-
};
|
|
134
|
-
return renderResult;
|
|
135
|
-
}
|
|
136
|
-
catch (caught) {
|
|
137
|
-
const error = ensureErrorAndPrependMessage(caught, `Failed to render ${init.tagName}`);
|
|
138
|
-
console.error(error);
|
|
139
|
-
this._lastRenderError = error;
|
|
140
|
-
return extractErrorMessage(error);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
connectedCallback() {
|
|
144
|
-
super.connectedCallback();
|
|
145
|
-
if (this._hasRendered && !this._initCalled && init.init) {
|
|
146
|
-
this._initCalled = true;
|
|
147
|
-
const renderParams = this.createRenderParams();
|
|
148
|
-
if (init.init(renderParams) instanceof Promise) {
|
|
149
|
-
throw new TypeError(`init in '${init.tagName}' cannot be asynchronous`);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
destroy() {
|
|
154
|
-
Object.values(this.instanceState).forEach((stateValue) => {
|
|
155
|
-
if (check.hasKey(stateValue, 'destroy') && check.isFunction(stateValue.destroy)) {
|
|
156
|
-
stateValue.destroy();
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
disconnectedCallback() {
|
|
161
|
-
super.disconnectedCallback();
|
|
162
|
-
if (init.cleanup) {
|
|
163
|
-
const renderParams = this.createRenderParams();
|
|
164
|
-
if (init.cleanup(renderParams) instanceof Promise) {
|
|
165
|
-
throw new TypeError(`cleanup in '${init.tagName}' cannot be asynchronous`);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
this.destroy();
|
|
169
|
-
this._initCalled = false;
|
|
170
|
-
this._stateCalled = false;
|
|
171
|
-
}
|
|
172
|
-
// this is set below in Object.defineProperties
|
|
173
|
-
definition = {};
|
|
174
|
-
assignInputs(inputs) {
|
|
175
|
-
assignInputs(this, inputs);
|
|
176
|
-
}
|
|
177
|
-
observablePropertyListenerMap = {};
|
|
178
|
-
instanceInputs = createElementPropertyProxy(this, false);
|
|
179
|
-
instanceState = createElementPropertyProxy(this, !elementOptions.allowPolymorphicState);
|
|
180
|
-
constructor() {
|
|
181
|
-
super();
|
|
182
|
-
this.definition = anonymousClass;
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
Object.defineProperties(anonymousClass, {
|
|
186
|
-
name: {
|
|
187
|
-
value: kebabCaseToCamelCase(init.tagName, {
|
|
188
|
-
capitalizeFirstLetter: true,
|
|
189
|
-
}),
|
|
190
|
-
writable: true,
|
|
191
|
-
},
|
|
192
|
-
});
|
|
193
|
-
if (window.customElements.get(init.tagName)) {
|
|
194
|
-
console.warn(`Tried to define custom element '${init.tagName}' but it is already defined.`);
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
window.customElements.define(init.tagName, anonymousClass);
|
|
198
|
-
}
|
|
199
|
-
return anonymousClass;
|
|
200
|
-
}
|