@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 CHANGED
@@ -1,11 +1,21 @@
1
- {
2
- "name": "@xaendar/core",
3
- "version": "0.0.4",
4
- "type": "module",
5
- "author": "Kaitenjo",
6
- "license": "MIT",
7
- "description": "A library containing core utils such as webcomponent base classes and theming support",
8
- "dependencies": {
9
- "@xaendar/common": "0.0.3"
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,2 +0,0 @@
1
- export const INTERNAL_PREFIX = '⛔';
2
- export const INTERNAL_OBSERVED_ATTRIBUTES = `${INTERNAL_PREFIX}observedAttributes`;
@@ -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
- }
@@ -1,3 +0,0 @@
1
- export * from './event.decorator';
2
- export * from './property.decorator';
3
- export * from './web-component/web-component.decorator';
@@ -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
- }
@@ -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,6 +0,0 @@
1
- import { Beautify, RequireOne } from '@xaendar/common';
2
-
3
- /**
4
- * Rapresent the options to configure an @Event Decorator in a Web Component.
5
- */
6
- export type EventParams = Beautify<RequireOne<Omit<CustomEventInit, 'detail'>>>;
@@ -1,2 +0,0 @@
1
- export * from './event-params.type';
2
- export * from './output.type';
@@ -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
- }
@@ -1,2 +0,0 @@
1
- export * from './event';
2
- export * from './property-decorator-params.type';
@@ -1,3 +0,0 @@
1
- export type PropertyDecoratorParams = {
2
- required: true;
3
- };
package/src/public-api.ts DELETED
@@ -1,3 +0,0 @@
1
- export * from './decorators';
2
- export * from './directives';
3
- export * from './models';
@@ -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
@@ -1,3 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- }
package/vite.config.ts DELETED
@@ -1,4 +0,0 @@
1
- import { defineConfig } from 'vite';
2
- import getViteConfig from '../../vite-config';
3
-
4
- export default defineConfig(getViteConfig('@xaendar/core', __dirname));