UNPKG

@web-atoms/core

Version:
338 lines (283 loc) • 10.4 kB
import type { ObjectPositionType } from "../style/StyleRule"; import type { AtomControl } from "../web/controls/AtomControl"; import Bind, { bindSymbol } from "./Bind"; import type { ColorItem } from "./Colors"; import { IClassOf, IDisposable } from "./types"; export interface IAttributes { [key: string]: string | number | null | any; } declare var bridge: any; export class RootObject { public get vsProps(): { [k in keyof this]?: this[k] | Bind } | { [k: string]: any } | {} { return undefined; } public addEventListener(name: string, handler: EventListenerOrEventListenerObject): IDisposable { return bridge.addEventHandler(this, name, handler); } public appendChild(e: any) { bridge.appendChild(this, e); } public dispatchEvent(evt: Event) { bridge.dispatchEvent(evt); } } export interface IElementAttributes { [key: string]: unknown; "data-click-event"?: string; /** * Useful only inside `Form` element. */ "data-submit-event"?: string; eventClick?: any; eventBlur?: any; eventFocus?: any; eventKeydown?: any; eventKeyup?: any; eventKeypress?: any; text?: string | any; "event-click"?: (e: MouseEvent) => any; "event-blur"?: (e: Event) => any; "event-focus"?: (e: Event) => any; "on-create"?: (ctrl: AtomControl, element: HTMLElement) => any; /** This will fire up `watch` custom event, that you can observe * for many elements in document.body instead of setting up too * many bindings */ watch?: any; /** * If display is set to true, it will be set as empty string, * which will unset the value and it will inherit the style from stylesheet. * If it is set to false, it will be set to "none" */ "style-display"?: boolean | string; /** number will be converted to pixels */ "style-left"?: number | string; /** number will be converted to pixels */ "style-top"?: number | string; /** number will be converted to pixels */ "style-bottom"?: number | string; /** number will be converted to pixels */ "style-right"?: number | string; /** number will be converted to pixels */ "style-width"?: number | string; /** number will be converted to pixels */ "style-height"?: number | string; /** number will be converted to pixels */ "style-position"?: ObjectPositionType; /** number will be converted to pixels */ "style-font-size"?: number | string; "style-font-family"?: string; "style-font-weight"?: string; "style-border"?: string; "style-border-width"?: string; "style-border-color"?: string | ColorItem; "style-color"?: string | ColorItem; "style-background-color"?: string | ColorItem; /** Data Style Attributes */ } // tslint:disable-next-line: no-namespace declare global { namespace JSX { // tslint:disable-next-line: interface-name interface ElementAttributesProperty { vsProps; } type HtmlPartial<T> = { [k in keyof T]?: IElementAttributes | { [tx in keyof T[k]]?: T[k][tx]; } }; type IAllHtmlElements = HtmlPartial<HTMLElementTagNameMap>; // tslint:disable-next-line interface IntrinsicElements extends IAllHtmlElements { } } } export type IMergedControl<T, T1> = { [P in keyof (T & T1)]?: (T & T1)[P]; } & { vsProps: { [P in keyof (T & T1)]?: (T & T1)[P]; } }; export type NodeFactory = (a?: any, ... nodes: XNode[]) => XNode; export type AttachedNode = (n: any) => { [key: string]: any}; export const xnodeSymbol = Symbol("XNode"); export const isControl = Symbol("isControl"); export const elementFactorySymbol = Symbol("elementFactory"); export const isFactorySymbol = Symbol("isFactory"); export const attachedSymbol = Symbol("attached"); export const isTemplateSymbol = Symbol("isTemplate"); export const constructorNeedsArgumentsSymbol = Symbol("constructorNeedsArguments"); export const attachedProperties: { [key: string]: (e, v) => void } = {}; let attachedId = 1; const attach = (name, attacher) => { const key = `:${attachedId++}`; const fx = (v) => { return { [key]: v }; }; attachedProperties[key] = attacher; fx[attachedSymbol] = attacher; fx[isFactorySymbol] = key; return fx; }; export default class XNode { public static isFactory = isFactorySymbol; public static elementFactory = elementFactorySymbol; public static bindSymbol = bindSymbol; public static isTemplate = isTemplateSymbol; public static prepareAttached = attach; public static constructorNeedsArguments = constructorNeedsArgumentsSymbol; public static classes: {[key: string]: any } = {}; public static attach<T, T1 extends HTMLElementTagNameMap, K extends keyof T1>( n: IClassOf<T>, tag: K): new (... a: any[]) => IMergedControl<T, T1[K]> ; public static attach<T, T1>( n: IClassOf<T>, tag: (a?: Partial<T1>, ... nodes: XNode[]) => XNode): new (... a: any[]) => IMergedControl<T, T1> ; public static attach( n: any, tag: any): any { return { factory: (attributes: any, ... nodes: XNode[] ) => new XNode(n, attributes ? { ... attributes, for: tag } : { for: tag}, nodes) }; } public static prepare<T>( n: any, isProperty?: boolean, isTemplate?: boolean): ((attributes: Partial<T>, ... nodes: XNode[]) => XNode) { function px(v) { return ({ [n]: v }); } px.factory = (a: any, ... nodes: any[]) => { return new XNode(n, a, nodes, isProperty, isTemplate); }; px.toString = () => n; return px as any; // return { // factory(a: any, ... nodes: any[]) { // return new XNode(n, a, nodes, isProperty , isTemplate); // }, // toString() { // return n; // } // } as any; } // public static template(): NodeFactory { // return { // factory: true, // isTemplate: true, // } as any; // } // public static attached = (name: string): AttachedNode => (n) => ({ [name]: n }); // public static property(): NodeFactory { // return { // factory: true // } as any; // } public static getClass(fullTypeName: string, assemblyName: string) { const n = fullTypeName + ";" + assemblyName; const cx = XNode.classes[n] || (XNode.classes[n] = bridge.getClass( fullTypeName, assemblyName, RootObject, (name, isProperty, isTemplate) => (a?: any, ... nodes: any[]) => new XNode(name, a, nodes, isProperty, isTemplate ))); return cx; } public static factory = (name, isProperty, isTemplate) => (a?: any, ... nodes: any[]) => { return new XNode(name, a, nodes, isProperty, isTemplate); } /** * Declares Root Namespace and Assembly. You can use return function to * to declare the type * @param ns Root Namespace */ public static namespace(ns: string, assemblyName: string) { return (type: string, isTemplate?: boolean) => { return (c) => { // static properties !! for (const key in c) { if (c.hasOwnProperty(key)) { const element = c[key]; if (element) { const n = ns + "." + type + ":" + key + ";" + assemblyName; const af: any = (a) => { const r = { [n]: a }; Object.defineProperty(r, "toString", { value: () => n, enumerable: false, configurable: false }); return r; }; af.factory = (a?: any, ... nodes: any[]) => new XNode(n, a, nodes, true, element.isTemplate ); af.toString = () => n; c[key] = af; } } } const tn = ns + "." + type + ";" + assemblyName; c.factory = (a?: any, ... nodes: XNode[]) => { return new XNode(tn, a, nodes, false, isTemplate); }; c.toString = () => tn; }; }; } public static create( // tslint:disable-next-line: ban-types name: string | Function, attributes: IAttributes, ... children: Array<XNode | XNode[] | any>): XNode { if (typeof name === "string") { return new XNode(name, attributes, children); } if ((name as any)[isFactorySymbol]) { return new XNode(name as any, attributes, children); } if ((name as any).factory) { return ((name as any).factory)(attributes, ... children); } switch (typeof name) { case "object": name = (name as any).toString(); break; case "function": return name(attributes || {}, ... children); } return new XNode(name as any, attributes, children); } public nameArgs: any; constructor( // tslint:disable-next-line: ban-types public name: string | Function, public attributes: IAttributes, public children: XNode[] | XNode[][] | any[], public isProperty?: boolean, public isTemplate?: boolean) { this[xnodeSymbol] = true; } public toString(): string { if (typeof this.name === "string") { return `name is of type string and value is ${this.name}`; } return `name is of type ${typeof this.name}`; } } if (typeof bridge !== "undefined") { bridge.XNode = XNode; }