@xaendar/core 0.0.4 → 0.1.6
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/package.json +21 -11
- package/xaendar-core.es.d.ts +100 -0
- package/xaendar-core.es.js +91 -0
- package/xaendar-core.es.js.map +1 -0
- package/src/costants.ts +0 -2
- package/src/decorators/event.decorator.ts +0 -37
- package/src/decorators/index.ts +0 -3
- package/src/decorators/property.decorator.ts +0 -42
- package/src/decorators/web-component/web-component.decorator.spec.ts +0 -57
- package/src/decorators/web-component/web-component.decorator.ts +0 -43
- package/src/directives/base-web-component.ts +0 -89
- package/src/directives/index.ts +0 -1
- package/src/models/base-web-component-constructor.type.ts +0 -8
- package/src/models/event/event-params.type.ts +0 -6
- package/src/models/event/index.ts +0 -2
- package/src/models/event/output.type.ts +0 -14
- package/src/models/index.ts +0 -2
- package/src/models/property-decorator-params.type.ts +0 -3
- package/src/public-api.ts +0 -3
- package/src/testing/mock-web-component.ts +0 -26
- package/tsconfig.json +0 -3
- package/vite.config.ts +0 -4
package/package.json
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@xaendar/core",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@xaendar/core",
|
|
3
|
+
"version": "0.1.6",
|
|
4
|
+
"description": "A library containing core utils such as webcomponent base classes and theming support",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./xaendar-core.es.js",
|
|
7
|
+
"module": "./xaendar-core.es.js",
|
|
8
|
+
"types": "./xaendar-core.es.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./xaendar-core.es.d.ts",
|
|
13
|
+
"default": "./xaendar-core.es.js"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"peerDependencies": {},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@xaendar/common": "0.1.6"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { AccessorDecorator } from '@xaendar/common';
|
|
2
|
+
import { Beautify } from '@xaendar/common';
|
|
3
|
+
import { ClassDecorator as ClassDecorator_2 } from '@xaendar/common';
|
|
4
|
+
import { RequireOne } from '@xaendar/common';
|
|
5
|
+
import { VoidFunction as VoidFunction_2 } from '@xaendar/common';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Base class for all Web Components in the framework
|
|
9
|
+
*
|
|
10
|
+
* This class internally has an `observedAttributes` property
|
|
11
|
+
* add programmaticaly by the @WebComponent decorator.
|
|
12
|
+
* It won't appear by intellisense but it's there.
|
|
13
|
+
*/
|
|
14
|
+
export declare class BaseWebComponent extends HTMLElement {
|
|
15
|
+
static rendererTimes: number;
|
|
16
|
+
/**
|
|
17
|
+
* Flag to track if the component has been initialized
|
|
18
|
+
* When istance is created, the flag is false
|
|
19
|
+
* After the connectedCallback has been called and all the attributes
|
|
20
|
+
* have been reflected on the relatives properties, the flag is set to true
|
|
21
|
+
*
|
|
22
|
+
* This prevents the render method to be called by the properties setters
|
|
23
|
+
* before the component is fully initialized.
|
|
24
|
+
*
|
|
25
|
+
* Otherwise the render function would be called N times where N is the
|
|
26
|
+
* number of properties decorated with @Property and specified as attributes
|
|
27
|
+
* on the CustomElement tag in the HTML
|
|
28
|
+
*/
|
|
29
|
+
private _initialized;
|
|
30
|
+
private readonly _root;
|
|
31
|
+
constructor();
|
|
32
|
+
/* Excluded from this release type: internalRender */
|
|
33
|
+
/**
|
|
34
|
+
* Method automatically called by the JavascriptEngine when an attribute
|
|
35
|
+
* on the host element is changed
|
|
36
|
+
*
|
|
37
|
+
* This method runs before the connectedCallback method if any observed attribute
|
|
38
|
+
* is specified on the CustomElement tag in the HTML
|
|
39
|
+
*
|
|
40
|
+
* @param name Name of the attribute changed
|
|
41
|
+
* @param _oldValue Old value of the attribute
|
|
42
|
+
* @param newValue New value of the attribute
|
|
43
|
+
*/
|
|
44
|
+
private attributeChangedCallback;
|
|
45
|
+
/**
|
|
46
|
+
* Method automatically called by the JavascriptEngine when a CustomElement
|
|
47
|
+
* is added to the DOM
|
|
48
|
+
*
|
|
49
|
+
* This method is called EVERY time the element is added
|
|
50
|
+
*/
|
|
51
|
+
private connectedCallback;
|
|
52
|
+
/**
|
|
53
|
+
* Method automatically called by the JavascriptEngine when a CustomElement
|
|
54
|
+
* is removed from the DOM
|
|
55
|
+
*
|
|
56
|
+
* This method is called EVERY time the element is removed
|
|
57
|
+
*
|
|
58
|
+
* We use this method to reset the _initialized flag
|
|
59
|
+
* so that if the element is re-added to the DOM
|
|
60
|
+
* the properties initialization won't call the render method
|
|
61
|
+
*/
|
|
62
|
+
private disconnectedCallback;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
declare function Event_2<Class extends BaseWebComponent, Field, Data = void>(params?: EventParams): AccessorDecorator<Class, Field, Output<Data>>;
|
|
66
|
+
export { Event_2 as Event }
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Rapresent the options to configure an @Event Decorator in a Web Component.
|
|
70
|
+
*/
|
|
71
|
+
export declare type EventParams = Beautify<RequireOne<Omit<CustomEventInit, 'detail'>>>;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Rapresent the output type returned by an @Event Decorator in a Web Component.
|
|
75
|
+
*
|
|
76
|
+
* Function type `emit` can be used to emit the event with the specified value and options.
|
|
77
|
+
* @param Value - The type of the value to be emitted with the event. Default is `void`.
|
|
78
|
+
* @param EventParams - The type of the event parameters to configure the event emission.
|
|
79
|
+
* This object's properties override the default event parameters defined in the decorator.
|
|
80
|
+
*/
|
|
81
|
+
export declare type Output<Value = void> = {
|
|
82
|
+
emit: VoidFunction_2<Value extends void ? ([EventParams] | []) : ([Value, EventParams] | [Value])>;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export declare function Property<Class extends BaseWebComponent, Field>(params?: PropertyDecoratorParams): AccessorDecorator<Class, Field>;
|
|
86
|
+
|
|
87
|
+
export declare type PropertyDecoratorParams = {
|
|
88
|
+
required: true;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Decorator to define a web component
|
|
93
|
+
* @param selector Name or names of the custom element
|
|
94
|
+
*/
|
|
95
|
+
export declare function WebComponent<T extends BaseWebComponent>(options: {
|
|
96
|
+
selector: string | string[];
|
|
97
|
+
templateUrl: string;
|
|
98
|
+
}): ClassDecorator_2<T>;
|
|
99
|
+
|
|
100
|
+
export { }
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
//#region ../packages/core/src/decorators/event.decorator.ts
|
|
2
|
+
function e(e) {
|
|
3
|
+
return !!e && typeof e == "object" && ("bubbles" in e || "cancelable" in e || "composed" in e);
|
|
4
|
+
}
|
|
5
|
+
function t(t) {
|
|
6
|
+
return (n, r) => {
|
|
7
|
+
let i = { emit: (e, t) => {} };
|
|
8
|
+
return { get() {
|
|
9
|
+
return i.emit = (n, i) => {
|
|
10
|
+
let a = {};
|
|
11
|
+
a = e(n) ? {
|
|
12
|
+
...t,
|
|
13
|
+
...n
|
|
14
|
+
} : {
|
|
15
|
+
...t,
|
|
16
|
+
...i,
|
|
17
|
+
detail: n
|
|
18
|
+
};
|
|
19
|
+
let o = new CustomEvent(r.name, a);
|
|
20
|
+
this.dispatchEvent(o);
|
|
21
|
+
}, i;
|
|
22
|
+
} };
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
var n = "⛔observedAttributes";
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region ../packages/core/src/decorators/property.decorator.ts
|
|
28
|
+
function r(e) {
|
|
29
|
+
return function(t, r) {
|
|
30
|
+
r.metadata[n] ??= [], r.metadata[n].push(r.name);
|
|
31
|
+
let i = r.name, a = `⛔${i}`;
|
|
32
|
+
return {
|
|
33
|
+
get() {
|
|
34
|
+
return this[`⛔${i}`];
|
|
35
|
+
},
|
|
36
|
+
set(t) {
|
|
37
|
+
let n = this;
|
|
38
|
+
if (e?.required && !t) throw Error(`Property '${i}' is required, current value: ${t}`);
|
|
39
|
+
n[a] !== t && (n[a] = t, n.internalRender());
|
|
40
|
+
},
|
|
41
|
+
init(e) {
|
|
42
|
+
let t = this;
|
|
43
|
+
return t[a] = e, e;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region ../packages/core/src/decorators/web-component/web-component.decorator.ts
|
|
50
|
+
function i(e) {
|
|
51
|
+
return function(t, n) {
|
|
52
|
+
a(t, n), o(t, e.selector);
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function a(e, t) {
|
|
56
|
+
Object.defineProperty(e, "observedAttributes", {
|
|
57
|
+
get: () => t.metadata[n],
|
|
58
|
+
configurable: !1,
|
|
59
|
+
enumerable: !1
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function o(e, t) {
|
|
63
|
+
Array.isArray(t) ? t.forEach((t) => customElements.define(t, e)) : customElements.define(t, e);
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region ../packages/core/src/directives/base-web-component.ts
|
|
67
|
+
var s = class e extends HTMLElement {
|
|
68
|
+
static rendererTimes = 0;
|
|
69
|
+
_initialized = !1;
|
|
70
|
+
_root;
|
|
71
|
+
constructor() {
|
|
72
|
+
super(), this._root = this.attachShadow({ mode: "open" });
|
|
73
|
+
}
|
|
74
|
+
internalRender() {
|
|
75
|
+
this._initialized && console.warn("Render times:", ++e.rendererTimes);
|
|
76
|
+
}
|
|
77
|
+
attributeChangedCallback(e, t, n) {
|
|
78
|
+
let r = this;
|
|
79
|
+
r[e] = n;
|
|
80
|
+
}
|
|
81
|
+
connectedCallback() {
|
|
82
|
+
this._initialized = !0, this.internalRender();
|
|
83
|
+
}
|
|
84
|
+
disconnectedCallback() {
|
|
85
|
+
this._initialized = !1;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
//#endregion
|
|
89
|
+
export { s as BaseWebComponent, t as Event, r as Property, i as WebComponent };
|
|
90
|
+
|
|
91
|
+
//# sourceMappingURL=xaendar-core.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xaendar-core.es.js","names":[],"sources":["../../../packages/core/src/decorators/event.decorator.ts","../../../packages/core/src/costants.ts","../../../packages/core/src/decorators/property.decorator.ts","../../../packages/core/src/decorators/web-component/web-component.decorator.ts","../../../packages/core/src/directives/base-web-component.ts"],"sourcesContent":["import { AccessorDecorator, ClassAccessorDecoratorValue } from '@xaendar/common';\r\nimport { BaseWebComponent } from '../directives/base-web-component';\r\nimport { EventParams } from '../models/event/event-params.type';\r\nimport { Output } from '../models/event/output.type';\r\n\r\nfunction isEventParams(value: EventParams | unknown): value is EventParams {\r\n return !!value && typeof value === 'object' && ('bubbles' in value || 'cancelable' in value || 'composed' in value);\r\n}\r\n\r\nexport function Event<\r\n Class extends BaseWebComponent,\r\n Field,\r\n Data = void,\r\n>(params?: EventParams): AccessorDecorator<Class, Field, Output<Data>> {\r\n return (_value: ClassAccessorDecoratorValue<Field>, context: ClassAccessorDecoratorContext<Class, Output<Data>>): ReturnType<AccessorDecorator<Class, Field, Output<Data>>> => {\r\n const output: Output<Data> = {\r\n emit: (_valueOrOverrideParams?: Data | EventParams, _overrideParams?: EventParams) => { }\r\n };\r\n\r\n return {\r\n get(): Output<Data> {\r\n output.emit = (valueOrOverrideParams?: Data | EventParams, overrideParams?: EventParams) => {\r\n let eventParams: CustomEventInit<Data> = {};\r\n\r\n eventParams = isEventParams(valueOrOverrideParams)\r\n ? { ...params, ...valueOrOverrideParams } \r\n : { ...params, ...overrideParams, detail: valueOrOverrideParams }\r\n\r\n const event = new CustomEvent(context.name as string, eventParams);\r\n const classInstance = (this as Class);\r\n classInstance.dispatchEvent(event);\r\n };\r\n return output;\r\n }\r\n }\r\n }\r\n }","export const INTERNAL_PREFIX = '⛔';\r\nexport const INTERNAL_OBSERVED_ATTRIBUTES = `${INTERNAL_PREFIX}observedAttributes`;","import { AccessorDecorator, ClassAccessorDecoratorValue, Constructor } from '@xaendar/common';\r\nimport { INTERNAL_OBSERVED_ATTRIBUTES, INTERNAL_PREFIX } from '../costants';\r\nimport { BaseWebComponent } from '../directives/base-web-component';\r\nimport { PropertyDecoratorParams } from '../models/property-decorator-params.type';\r\n\r\nexport function Property<\r\n Class extends BaseWebComponent,\r\n Field\r\n >(params?: PropertyDecoratorParams): AccessorDecorator<Class, Field> {\r\n return function (_value: ClassAccessorDecoratorValue<Field>, context: ClassAccessorDecoratorContext<Class, Field>): ReturnType<AccessorDecorator<Class, Field>> {\r\n context.metadata![INTERNAL_OBSERVED_ATTRIBUTES] ??= new Array<string>;\r\n (context.metadata![INTERNAL_OBSERVED_ATTRIBUTES] as string[]).push(context.name as string);\r\n \r\n const propertyKey = context.name as string;\r\n const internalPropertyKey = `${INTERNAL_PREFIX}${propertyKey}`\r\n \r\n return {\r\n get() {\r\n const classInstance = (this as BaseWebComponent & Record<typeof internalPropertyKey, Field>);\r\n return classInstance[`${INTERNAL_PREFIX}${propertyKey}`]!;\r\n },\r\n set(value: Field) {\r\n const classInstance = (this as BaseWebComponent & Record<typeof internalPropertyKey, Field>);\r\n\r\n if (params?.required && !value) {\r\n throw new Error(`Property '${propertyKey}' is required, current value: ${value}`);\r\n }\r\n\r\n const oldValue = classInstance[internalPropertyKey]!;\r\n if (oldValue !== value) {\r\n classInstance[internalPropertyKey] = value;\r\n classInstance.internalRender();\r\n }\r\n },\r\n init(initialValue: Field) {\r\n const classInstance = (this as BaseWebComponent & Record<typeof internalPropertyKey, Field>);\r\n classInstance[internalPropertyKey] = initialValue;\r\n return initialValue;\r\n }\r\n };\r\n }\r\n}\r\n","import { ClassDecorator, Constructor } from '@xaendar/common';\r\nimport { INTERNAL_OBSERVED_ATTRIBUTES } from '../../costants';\r\nimport { BaseWebComponent } from '../../directives/base-web-component';\r\n\r\n/**\r\n * Decorator to define a web component\r\n * @param selector Name or names of the custom element\r\n */\r\nexport function WebComponent<T extends BaseWebComponent>(options: { selector: string | string[], templateUrl: string }): ClassDecorator<T> {\r\n return function (klass: Constructor<T>, context: ClassDecoratorContext<Constructor<T>>): void {\r\n defineObservedAttributes(klass, context);\r\n setSelectors(klass, options.selector);\r\n };\r\n}\r\n\r\n/**\r\n * Function to define the observedAttributes static property on the class.\r\n * We define static get observedAttributes programmatically\r\n * to abstract the manual definition from the user.\r\n *\r\n * We could not define the property in the base class due to the fact\r\n * that is static and each derived class would override the value of the others\r\n * @param klass The class to set the observedAttributes on\r\n * @param attributes The attributes to observe\r\n */\r\nfunction defineObservedAttributes<T extends BaseWebComponent>(klass: Constructor<T>, context: ClassDecoratorContext<Constructor<T>>): void {\r\n Object.defineProperty(klass, 'observedAttributes', {\r\n get: () => context.metadata![INTERNAL_OBSERVED_ATTRIBUTES],\r\n configurable: false,\r\n enumerable: false\r\n });\r\n}\r\n\r\n/**\r\n * Function to add the custom element definition to the browser using the passed selectors.\r\n * @param klass The class to define as a web component\r\n * @param selectors The selector or selectors to reference the web component in HTML\r\n */\r\nfunction setSelectors<T extends BaseWebComponent>(klass: Constructor<T>, selectors: string | string[]): void {\r\n Array.isArray(selectors)\r\n ? selectors.forEach(selector => customElements.define(selector, klass))\r\n : customElements.define(selectors, klass);\r\n}","/**\r\n * Base class for all Web Components in the framework\r\n * \r\n * This class internally has an `observedAttributes` property\r\n * add programmaticaly by the @WebComponent decorator. \r\n * It won't appear by intellisense but it's there.\r\n */\r\nexport class BaseWebComponent extends HTMLElement {\r\n\r\n public static rendererTimes = 0;\r\n\r\n /**\r\n * Flag to track if the component has been initialized\r\n * When istance is created, the flag is false\r\n * After the connectedCallback has been called and all the attributes\r\n * have been reflected on the relatives properties, the flag is set to true\r\n * \r\n * This prevents the render method to be called by the properties setters\r\n * before the component is fully initialized.\r\n * \r\n * Otherwise the render function would be called N times where N is the\r\n * number of properties decorated with @Property and specified as attributes\r\n * on the CustomElement tag in the HTML\r\n */\r\n private _initialized = false;\r\n\r\n private readonly _root: ShadowRoot;\r\n\r\n constructor() {\r\n super();\r\n this._root = this.attachShadow({ mode: 'open' });\r\n }\r\n\r\n /**\r\n * Method called by the @Property decorator to\r\n * update the rendering of the component\r\n * @internal \r\n */\r\n public internalRender(): void {\r\n if (this._initialized) {\r\n console.warn('Render times:', ++BaseWebComponent.rendererTimes);\r\n }\r\n }\r\n\r\n /**\r\n * Method automatically called by the JavascriptEngine when an attribute\r\n * on the host element is changed\r\n * \r\n * This method runs before the connectedCallback method if any observed attribute\r\n * is specified on the CustomElement tag in the HTML\r\n * \r\n * @param name Name of the attribute changed\r\n * @param _oldValue Old value of the attribute\r\n * @param newValue New value of the attribute\r\n */\r\n private attributeChangedCallback(name: string, _oldValue: unknown, newValue: unknown): void {\r\n /*\r\n Since the 'Property Decorator add the property key to the ObservedAttributes\r\n We are sure that the property with the given name exists on the instance of the subclass\r\n */\r\n const context = this as BaseWebComponent & Record<string, unknown>;\r\n context[name] = newValue;\r\n }\r\n\r\n /**\r\n * Method automatically called by the JavascriptEngine when a CustomElement\r\n * is added to the DOM\r\n * \r\n * This method is called EVERY time the element is added\r\n */\r\n private connectedCallback(): void {\r\n this._initialized = true;\r\n this.internalRender();\r\n }\r\n\r\n /**\r\n * Method automatically called by the JavascriptEngine when a CustomElement\r\n * is removed from the DOM\r\n * \r\n * This method is called EVERY time the element is removed\r\n * \r\n * We use this method to reset the _initialized flag\r\n * so that if the element is re-added to the DOM\r\n * the properties initialization won't call the render method\r\n */\r\n private disconnectedCallback(): void {\r\n this._initialized = false\r\n }\r\n}"],"mappings":";AAKA,SAAS,EAAc,GAAoD;AACzE,QAAO,CAAC,CAAC,KAAS,OAAO,KAAU,aAAa,aAAa,KAAS,gBAAgB,KAAS,cAAc;;AAG/G,SAAgB,EAId,GAAqE;AACrE,SAAQ,GAA4C,MAA2H;EAC7K,IAAM,IAAuB,EAC3B,OAAO,GAA6C,MAAkC,IACtF;AAEF,SAAO,EACL,MAAoB;AAYhB,UAXF,EAAO,QAAQ,GAA4C,MAAiC;IAC1F,IAAI,IAAqC,EAAE;AAEzC,QAAc,EAAc,EAAsB,GAC9C;KAAE,GAAG;KAAQ,GAAG;KAAuB,GACvC;KAAE,GAAG;KAAQ,GAAG;KAAgB,QAAQ;KAAuB;IAEnE,IAAM,IAAQ,IAAI,YAAY,EAAQ,MAAgB,EAAY;AAElE,SAAc,cAAc,EAAM;MAE7B;KAEV;;;ACjCP,IAAa,IAA+B;;;ACI5C,SAAgB,EAGZ,GAAmE;AACrE,QAAO,SAAU,GAA4C,GAAmG;AAE7J,EADD,EAAQ,SAAU,OAAkC,EAAiB,EACpE,EAAQ,SAAU,GAA2C,KAAK,EAAQ,KAAe;EAE1F,IAAM,IAAc,EAAQ,MACtB,IAAsB,IAAqB;AAEjD,SAAO;GACL,MAAM;AAEJ,WAAO,KAAc,IAAqB;;GAE5C,IAAI,GAAc;IAChB,IAAM,IAAiB;AAEvB,QAAI,GAAQ,YAAY,CAAC,EACvB,OAAU,MAAM,aAAa,EAAY,gCAAgC,IAAQ;AAInF,IADiB,EAAc,OACd,MACf,EAAc,KAAuB,GACrC,EAAc,gBAAgB;;GAGlC,KAAK,GAAqB;IACxB,IAAM,IAAiB;AAEvB,WADA,EAAc,KAAuB,GAC9B;;GAEV;;;;;AC/BL,SAAgB,EAAyC,GAAkF;AACzI,QAAO,SAAU,GAAuB,GAAsD;AAE5F,EADA,EAAyB,GAAO,EAAQ,EACxC,EAAa,GAAO,EAAQ,SAAS;;;AAczC,SAAS,EAAqD,GAAuB,GAAsD;AACzI,QAAO,eAAe,GAAO,sBAAsB;EACjD,WAAW,EAAQ,SAAU;EAC7B,cAAc;EACd,YAAY;EACb,CAAC;;AAQJ,SAAS,EAAyC,GAAuB,GAAoC;AAC3G,OAAM,QAAQ,EAAU,GACpB,EAAU,SAAQ,MAAY,eAAe,OAAO,GAAU,EAAM,CAAC,GACrE,eAAe,OAAO,GAAW,EAAM;;;;AClC7C,IAAa,IAAb,MAAa,UAAyB,YAAY;CAEhD,OAAc,gBAAgB;CAe9B,eAAuB;CAEvB;CAEA,cAAc;AAEZ,EADA,OAAO,EACP,KAAK,QAAQ,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;;CAQlD,iBAA8B;AAC5B,EAAI,KAAK,gBACP,QAAQ,KAAK,iBAAiB,EAAE,EAAiB,cAAc;;CAenE,yBAAiC,GAAc,GAAoB,GAAyB;EAK1F,IAAM,IAAU;AAChB,IAAQ,KAAQ;;CASlB,oBAAkC;AAEhC,EADA,KAAK,eAAe,IACpB,KAAK,gBAAgB;;CAavB,uBAAqC;AACnC,OAAK,eAAe"}
|
package/src/costants.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { AccessorDecorator, ClassAccessorDecoratorValue } from '@xaendar/common';
|
|
2
|
-
import { BaseWebComponent } from '../directives/base-web-component';
|
|
3
|
-
import { EventParams } from '../models/event/event-params.type';
|
|
4
|
-
import { Output } from '../models/event/output.type';
|
|
5
|
-
|
|
6
|
-
function isEventParams(value: EventParams | unknown): value is EventParams {
|
|
7
|
-
return !!value && typeof value === 'object' && ('bubbles' in value || 'cancelable' in value || 'composed' in value);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function Event<
|
|
11
|
-
Class extends BaseWebComponent,
|
|
12
|
-
Field,
|
|
13
|
-
Data = void,
|
|
14
|
-
>(params?: EventParams): AccessorDecorator<Class, Field, Output<Data>> {
|
|
15
|
-
return (_value: ClassAccessorDecoratorValue<Field>, context: ClassAccessorDecoratorContext<Class, Output<Data>>): ReturnType<AccessorDecorator<Class, Field, Output<Data>>> => {
|
|
16
|
-
const output: Output<Data> = {
|
|
17
|
-
emit: (_valueOrOverrideParams?: Data | EventParams, _overrideParams?: EventParams) => { }
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
return {
|
|
21
|
-
get(): Output<Data> {
|
|
22
|
-
output.emit = (valueOrOverrideParams?: Data | EventParams, overrideParams?: EventParams) => {
|
|
23
|
-
let eventParams: CustomEventInit<Data> = {};
|
|
24
|
-
|
|
25
|
-
eventParams = isEventParams(valueOrOverrideParams)
|
|
26
|
-
? { ...params, ...valueOrOverrideParams }
|
|
27
|
-
: { ...params, ...overrideParams, detail: valueOrOverrideParams }
|
|
28
|
-
|
|
29
|
-
const event = new CustomEvent(context.name as string, eventParams);
|
|
30
|
-
const classInstance = (this as Class);
|
|
31
|
-
classInstance.dispatchEvent(event);
|
|
32
|
-
};
|
|
33
|
-
return output;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
package/src/decorators/index.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { AccessorDecorator, ClassAccessorDecoratorValue, Constructor } from '@xaendar/common';
|
|
2
|
-
import { INTERNAL_OBSERVED_ATTRIBUTES, INTERNAL_PREFIX } from '../costants';
|
|
3
|
-
import { BaseWebComponent } from '../directives/base-web-component';
|
|
4
|
-
import { PropertyDecoratorParams } from '../models/property-decorator-params.type';
|
|
5
|
-
|
|
6
|
-
export function Property<
|
|
7
|
-
Class extends BaseWebComponent,
|
|
8
|
-
Field
|
|
9
|
-
>(params?: PropertyDecoratorParams): AccessorDecorator<Class, Field> {
|
|
10
|
-
return function (_value: ClassAccessorDecoratorValue<Field>, context: ClassAccessorDecoratorContext<Class, Field>): ReturnType<AccessorDecorator<Class, Field>> {
|
|
11
|
-
context.metadata![INTERNAL_OBSERVED_ATTRIBUTES] ??= new Array<string>;
|
|
12
|
-
(context.metadata![INTERNAL_OBSERVED_ATTRIBUTES] as string[]).push(context.name as string);
|
|
13
|
-
|
|
14
|
-
const propertyKey = context.name as string;
|
|
15
|
-
const internalPropertyKey = `${INTERNAL_PREFIX}${propertyKey}`
|
|
16
|
-
|
|
17
|
-
return {
|
|
18
|
-
get() {
|
|
19
|
-
const classInstance = (this as BaseWebComponent & Record<typeof internalPropertyKey, Field>);
|
|
20
|
-
return classInstance[`${INTERNAL_PREFIX}${propertyKey}`]!;
|
|
21
|
-
},
|
|
22
|
-
set(value: Field) {
|
|
23
|
-
const classInstance = (this as BaseWebComponent & Record<typeof internalPropertyKey, Field>);
|
|
24
|
-
|
|
25
|
-
if (params?.required && !value) {
|
|
26
|
-
throw new Error(`Property '${propertyKey}' is required, current value: ${value}`);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const oldValue = classInstance[internalPropertyKey]!;
|
|
30
|
-
if (oldValue !== value) {
|
|
31
|
-
classInstance[internalPropertyKey] = value;
|
|
32
|
-
classInstance.internalRender();
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
init(initialValue: Field) {
|
|
36
|
-
const classInstance = (this as BaseWebComponent & Record<typeof internalPropertyKey, Field>);
|
|
37
|
-
classInstance[internalPropertyKey] = initialValue;
|
|
38
|
-
return initialValue;
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { BaseWebComponent } from '@xaendar/core';
|
|
2
|
-
import { WebComponent } from './web-component.decorator';
|
|
3
|
-
|
|
4
|
-
describe('WebComponent decorator', () => {
|
|
5
|
-
let defineSpy: jest.SpyInstance;
|
|
6
|
-
|
|
7
|
-
beforeEach(() => defineSpy = jest.spyOn(customElements, 'define').mockImplementation(() => { }));
|
|
8
|
-
|
|
9
|
-
afterEach(() => defineSpy.mockRestore());
|
|
10
|
-
|
|
11
|
-
it('should define a web component with a single selector', () => {
|
|
12
|
-
@WebComponent('single-selector')
|
|
13
|
-
class SingleSelectorComponent extends BaseWebComponent {
|
|
14
|
-
public template(): string {
|
|
15
|
-
throw new Error('Method not implemented.');
|
|
16
|
-
}
|
|
17
|
-
public css(): string {
|
|
18
|
-
throw new Error('Method not implemented.');
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
expect(defineSpy).toHaveBeenCalledTimes(1);
|
|
23
|
-
expect(defineSpy).toHaveBeenCalledWith('single-selector', SingleSelectorComponent);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('should define a web component with multiple selectors', () => {
|
|
27
|
-
@WebComponent(['first-selector', 'second-selector'])
|
|
28
|
-
class MultipleSelectorComponent extends BaseWebComponent {
|
|
29
|
-
public template(): string {
|
|
30
|
-
throw new Error('Method not implemented.');
|
|
31
|
-
}
|
|
32
|
-
public css(): string {
|
|
33
|
-
throw new Error('Method not implemented.');
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
expect(defineSpy).toHaveBeenCalledTimes(2);
|
|
38
|
-
expect(defineSpy).toHaveBeenNthCalledWith(1, 'first-selector', MultipleSelectorComponent);
|
|
39
|
-
expect(defineSpy).toHaveBeenNthCalledWith(2, 'second-selector', MultipleSelectorComponent);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it.skip('should define a web component with multiple selectors', () => {
|
|
43
|
-
@WebComponent('selector')
|
|
44
|
-
class SingleSelectorComponent extends BaseWebComponent {
|
|
45
|
-
public template(): string {
|
|
46
|
-
throw new Error('Method not implemented.');
|
|
47
|
-
}
|
|
48
|
-
public css(): string {
|
|
49
|
-
throw new Error('Method not implemented.');
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
expect(defineSpy).toHaveBeenCalledTimes(2);
|
|
54
|
-
expect(defineSpy).toHaveBeenNthCalledWith(1, 'first-selector', SingleSelectorComponent);
|
|
55
|
-
expect(defineSpy).toHaveBeenNthCalledWith(2, 'second-selector', SingleSelectorComponent);
|
|
56
|
-
});
|
|
57
|
-
});
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { ClassDecorator, Constructor } from '@xaendar/common';
|
|
2
|
-
import { INTERNAL_OBSERVED_ATTRIBUTES } from '../../costants';
|
|
3
|
-
import { BaseWebComponent } from '../../directives/base-web-component';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Decorator to define a web component
|
|
7
|
-
* @param selector Name or names of the custom element
|
|
8
|
-
*/
|
|
9
|
-
export function WebComponent<T extends BaseWebComponent>(options: { selector: string | string[], templateUrl: string }): ClassDecorator<T> {
|
|
10
|
-
return function (klass: Constructor<T>, context: ClassDecoratorContext<Constructor<T>>): void {
|
|
11
|
-
defineObservedAttributes(klass, context);
|
|
12
|
-
setSelectors(klass, options.selector);
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Function to define the observedAttributes static property on the class.
|
|
18
|
-
* We define static get observedAttributes programmatically
|
|
19
|
-
* to abstract the manual definition from the user.
|
|
20
|
-
*
|
|
21
|
-
* We could not define the property in the base class due to the fact
|
|
22
|
-
* that is static and each derived class would override the value of the others
|
|
23
|
-
* @param klass The class to set the observedAttributes on
|
|
24
|
-
* @param attributes The attributes to observe
|
|
25
|
-
*/
|
|
26
|
-
function defineObservedAttributes<T extends BaseWebComponent>(klass: Constructor<T>, context: ClassDecoratorContext<Constructor<T>>): void {
|
|
27
|
-
Object.defineProperty(klass, 'observedAttributes', {
|
|
28
|
-
get: () => context.metadata![INTERNAL_OBSERVED_ATTRIBUTES],
|
|
29
|
-
configurable: false,
|
|
30
|
-
enumerable: false
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Function to add the custom element definition to the browser using the passed selectors.
|
|
36
|
-
* @param klass The class to define as a web component
|
|
37
|
-
* @param selectors The selector or selectors to reference the web component in HTML
|
|
38
|
-
*/
|
|
39
|
-
function setSelectors<T extends BaseWebComponent>(klass: Constructor<T>, selectors: string | string[]): void {
|
|
40
|
-
Array.isArray(selectors)
|
|
41
|
-
? selectors.forEach(selector => customElements.define(selector, klass))
|
|
42
|
-
: customElements.define(selectors, klass);
|
|
43
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Base class for all Web Components in the framework
|
|
3
|
-
*
|
|
4
|
-
* This class internally has an `observedAttributes` property
|
|
5
|
-
* add programmaticaly by the @WebComponent decorator.
|
|
6
|
-
* It won't appear by intellisense but it's there.
|
|
7
|
-
*/
|
|
8
|
-
export class BaseWebComponent extends HTMLElement {
|
|
9
|
-
|
|
10
|
-
public static rendererTimes = 0;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Flag to track if the component has been initialized
|
|
14
|
-
* When istance is created, the flag is false
|
|
15
|
-
* After the connectedCallback has been called and all the attributes
|
|
16
|
-
* have been reflected on the relatives properties, the flag is set to true
|
|
17
|
-
*
|
|
18
|
-
* This prevents the render method to be called by the properties setters
|
|
19
|
-
* before the component is fully initialized.
|
|
20
|
-
*
|
|
21
|
-
* Otherwise the render function would be called N times where N is the
|
|
22
|
-
* number of properties decorated with @Property and specified as attributes
|
|
23
|
-
* on the CustomElement tag in the HTML
|
|
24
|
-
*/
|
|
25
|
-
private _initialized = false;
|
|
26
|
-
|
|
27
|
-
private readonly _root: ShadowRoot;
|
|
28
|
-
|
|
29
|
-
constructor() {
|
|
30
|
-
super();
|
|
31
|
-
this._root = this.attachShadow({ mode: 'open' });
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Method called by the @Property decorator to
|
|
36
|
-
* update the rendering of the component
|
|
37
|
-
* @internal
|
|
38
|
-
*/
|
|
39
|
-
public internalRender(): void {
|
|
40
|
-
if (this._initialized) {
|
|
41
|
-
console.warn('Render times:', ++BaseWebComponent.rendererTimes);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Method automatically called by the JavascriptEngine when an attribute
|
|
47
|
-
* on the host element is changed
|
|
48
|
-
*
|
|
49
|
-
* This method runs before the connectedCallback method if any observed attribute
|
|
50
|
-
* is specified on the CustomElement tag in the HTML
|
|
51
|
-
*
|
|
52
|
-
* @param name Name of the attribute changed
|
|
53
|
-
* @param _oldValue Old value of the attribute
|
|
54
|
-
* @param newValue New value of the attribute
|
|
55
|
-
*/
|
|
56
|
-
private attributeChangedCallback(name: string, _oldValue: unknown, newValue: unknown): void {
|
|
57
|
-
/*
|
|
58
|
-
Since the 'Property Decorator add the property key to the ObservedAttributes
|
|
59
|
-
We are sure that the property with the given name exists on the instance of the subclass
|
|
60
|
-
*/
|
|
61
|
-
const context = this as BaseWebComponent & Record<string, unknown>;
|
|
62
|
-
context[name] = newValue;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Method automatically called by the JavascriptEngine when a CustomElement
|
|
67
|
-
* is added to the DOM
|
|
68
|
-
*
|
|
69
|
-
* This method is called EVERY time the element is added
|
|
70
|
-
*/
|
|
71
|
-
private connectedCallback(): void {
|
|
72
|
-
this._initialized = true;
|
|
73
|
-
this.internalRender();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Method automatically called by the JavascriptEngine when a CustomElement
|
|
78
|
-
* is removed from the DOM
|
|
79
|
-
*
|
|
80
|
-
* This method is called EVERY time the element is removed
|
|
81
|
-
*
|
|
82
|
-
* We use this method to reset the _initialized flag
|
|
83
|
-
* so that if the element is re-added to the DOM
|
|
84
|
-
* the properties initialization won't call the render method
|
|
85
|
-
*/
|
|
86
|
-
private disconnectedCallback(): void {
|
|
87
|
-
this._initialized = false
|
|
88
|
-
}
|
|
89
|
-
}
|
package/src/directives/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './base-web-component';
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { Constructor } from '@xaendar/common';
|
|
2
|
-
import { INTERNAL_OBSERVED_ATTRIBUTES } from '../costants';
|
|
3
|
-
import { BaseWebComponent } from '../directives/base-web-component';
|
|
4
|
-
|
|
5
|
-
export type BaseWebComponentConstructor = Constructor<BaseWebComponent, {
|
|
6
|
-
observedAttributes: string[]
|
|
7
|
-
[INTERNAL_OBSERVED_ATTRIBUTES]: string[]
|
|
8
|
-
}>;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { VoidFunction } from '@xaendar/common';
|
|
2
|
-
import { EventParams } from './event-params.type';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Rapresent the output type returned by an @Event Decorator in a Web Component.
|
|
6
|
-
*
|
|
7
|
-
* Function type `emit` can be used to emit the event with the specified value and options.
|
|
8
|
-
* @param Value - The type of the value to be emitted with the event. Default is `void`.
|
|
9
|
-
* @param EventParams - The type of the event parameters to configure the event emission.
|
|
10
|
-
* This object's properties override the default event parameters defined in the decorator.
|
|
11
|
-
*/
|
|
12
|
-
export type Output<Value = void> = {
|
|
13
|
-
emit: VoidFunction<Value extends void ? ([EventParams] | []) : ([Value, EventParams] | [Value])>;
|
|
14
|
-
}
|
package/src/models/index.ts
DELETED
package/src/public-api.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { Event } from '../decorators/event.decorator';
|
|
2
|
-
import { Property } from '../decorators/property.decorator';
|
|
3
|
-
import { WebComponent } from '../decorators/web-component/web-component.decorator';
|
|
4
|
-
import { BaseWebComponent } from '../directives/base-web-component';
|
|
5
|
-
import { Output } from '../models/event/output.type';
|
|
6
|
-
|
|
7
|
-
@WebComponent({
|
|
8
|
-
selector: 'mock-web-component',
|
|
9
|
-
templateUrl: './mock-web-component.html',
|
|
10
|
-
})
|
|
11
|
-
export class MockWebComponent extends BaseWebComponent {
|
|
12
|
-
|
|
13
|
-
@Property()
|
|
14
|
-
public accessor mockProperty = 'default';
|
|
15
|
-
|
|
16
|
-
@Event()
|
|
17
|
-
public accessor mockEvent!: Output<string>;
|
|
18
|
-
|
|
19
|
-
public template(): string {
|
|
20
|
-
throw new Error('Method not implemented.');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
public css(): string {
|
|
24
|
-
throw new Error('Method not implemented.');
|
|
25
|
-
}
|
|
26
|
-
}
|
package/tsconfig.json
DELETED
package/vite.config.ts
DELETED