import { kebabToPascal } from './helpers.js'; import { McBase, DC } from './MC.js'; /** * Creates a named class constructor with the specified name. * @param name - The desired name for the class. * @param ClassBody - The class constructor to which the name will be assigned. * @returns The class constructor with the name property set. */ function createNamedClass(name: string, ClassBody: { new (...args: any[]): any }) { return Object.defineProperty(ClassBody, 'name', { value: name }); } /** * Defines a custom HTML element with optional observed attributes and micro component integration. * The class provided in DefinitionClass will be added to the ._mc hash object appended to the custom element instance. * Automatically assigns a "PascalCase" class name derived from the tagName. * If the provided definition class extends McBase (indicating a DC or MC class extension), it wraps the constructor to register * the element instance with DC (DomComponent). * * @param tagName - The HTML tag name for the custom element (must contain at least one hyphen). * @param DefinitionClass - The constructor function or class that defines the element's behavior. * @param observedAttributes - An optional array of attribute names to observe for changes. */ export function defineCE( tagName: string, DefinitionClass: { new (...args: any[]): any } | Function, observedAttributes: string[] = [] ) { //console.warn('defineCE args', arguments); if (customElements.get(tagName)) { console.warn(`IGNORED: Custom element "${tagName}" is already defined!`); return; } if (!tagName.includes('-')) { throw new Error('createCustomElement: tagName must contain at least one hyphen ("-").'); } let name = kebabToPascal(tagName); // a class, but not a MC if (!(DefinitionClass.prototype instanceof McBase)) { //console.warn('pure class', tagName, DefinitionClass); customElements.define( tagName, // @ts-ignore: DefinitionClass exists DefinitionClass ); return; } //console.warn('MC class', tagName, DefinitionClass); // a MC customElements.define( tagName, createNamedClass( name, class extends HTMLElement { constructor(data?: any) { super(); // console.log('[named class constructor]', tagName, name, this, data); // @ts-ignore: DefinitionClass exists DC.add(this, name, new DefinitionClass(this, data)); } connectedCallback() { //console.warn('onConnected', this); this.dispatchEvent(new CustomEvent('_mcConnected')); } connectedMoveCallback() { //console.warn('onConnectedMove', this); this.dispatchEvent(new CustomEvent('_mcConnectedMove')); } disconnectedCallback() { //console.warn('onDisconnected', this); this.dispatchEvent(new CustomEvent('_mcDisconnected')); } adoptedCallback() { //console.warn('onAdopted', this); this.dispatchEvent(new CustomEvent('_mcAdopted')); } attributeChangedCallback(name: string, oldValue: any, newValue: any) { //console.warn('onAttributeChanged', this); this.dispatchEvent( new CustomEvent('_mcAttributeChanged', { detail: { name, oldValue, newValue } }) ); } formResetCallback() { this.dispatchEvent(new CustomEvent('_mcFormReset')); } static get observedAttributes() { return observedAttributes; } } ) ); } /** * Retrieves a custom element constructor by tag name. * @param tagName - The HTML tag name to look up. * @returns The CustomElementConstructor if defined, or undefined if not found. */ export function getCE(tagName: string): CustomElementConstructor | undefined { return customElements.get(tagName); }