UNPKG

@ara-web/sds

Version:

Write code faster without worry about future debts or scalability in the future

227 lines (206 loc) 8.36 kB
/** * A certain element in the Module. * Inspired by the CSS selector. * * The format of the link is: * ``` * obj://<css-selector>?module-link=<module-link>&resource-link=<resource-link> * * obj:id#varName[proppertyName]?module-link=file://path/to/module.ts&resource-link=this * ``` * * Where: * - `obj://` is the schema to identify the link as an object link. * - `<css-selector>` is the CSS selector to identify the element in the module. * - `?module-link=<module-link>` is the module where object located. * - `?resource-link=<resource-link>` is the resource that machine could understand and locate. */ import type { ModuleLink, ModuleURL } from "./module-link.js"; export type Selector = string; export type ResourceURL = string; export type ObjectURL = `obj://${Selector}` | `obj://${Selector}?` | `obj://${Selector}?module-link=${ModuleURL}` | `obj://${Selector}?module-link=${ModuleURL}&resource-link=${ResourceURL}` | `obj://${Selector}?resource-link=${ModuleURL}` | `obj://${Selector}?resource-link=${ModuleURL}&module-link=${ResourceURL}`; type Tagged = {tag?: string, id?: string|number, classes?: string[]}; const ALL_LINK = "*"; /* First component when defined * componentLink = ComponentLevel.getObjectLinkOf<Component>(component, moduleURL); * creates obj://layout#index.astro?module-link=pkg:npm/@ara-web/reflect-astro-ext?category=layouts#/src/layouts/AraWebLayout.astro&resource-link=this * When identifying attributes of the component * componentLink.getObjectLinkOf<Attributes>(attribute, moduleURL); * creates obj://layout#index[attribute_name=1] * nested components are added by calling * componentLink.getObjectLinkOf<Component>(component, moduleURL); * in this case: * creates obj://layout#index[attribute_name=1]>component#componentName[proppertyName]?module-link=pkg:npm/@ara-web/reflect-astro-ext?category=layouts#/src/layouts/AraWebLayout.astro&resource-link=this * The slots are added as: * creates obj://slot#slotName>component#componentName[proppertyName]?module-link=pkg:npm/@ara-web/reflect-astro-ext?category=layouts#/src/layouts/AraWebLayout.astro&resource-link=this */ export class ObjectLink { private _selectors: Tagged[]; private _moduleLink?: ModuleLink; private _resourceLink?: ResourceURL; private _enumeratedCount: number = 0; /** * Creates an empty object link. Optionally, could specify the module link and resource link. * @param moduleLink The module link where the object is located. * @param resourceLink The resource link that machine could understand and locate. */ constructor(moduleLink?: ModuleLink, resourceLink?: ResourceURL) { this._selectors = []; this._moduleLink = moduleLink; this._resourceLink = resourceLink; } public get enumared(): number { return this._enumeratedCount; } public get selector(): Selector { if (this._selectors.length === 0) { return ALL_LINK; } return this._selectors.map((taggedSelector): string => { let url = ``; if (taggedSelector.tag) { url += taggedSelector.tag; } if (taggedSelector.classes !== undefined && taggedSelector.classes.length > 0) { url += `.${taggedSelector.classes.join(".")}`; } if (taggedSelector.id !== undefined) { if (typeof taggedSelector.id === "number") { url += `:nth-child(${taggedSelector.id})`; } else { url += `#${taggedSelector.id}`; } } return url; }).join(">"); } public get moduleLink(): ModuleLink|undefined { return this._moduleLink; } public get resourceLink(): ResourceURL|undefined { return this._resourceLink; } /** * The getId method returns the object's id. * * It gets the last selector as the current object. If it's tagged, then it returns the id. * Otherwise, it returns undefined. */ public getId(): string|number|undefined { if (this._selectors.length === 0) { return undefined; } return this._selectors[this._selectors.length - 1].id; } /** * Puts the id of the last selector. * If id already exists, then it returns false. * Otherwise, it sets the id and returns true. * @param id * @returns */ public putId(id: string|number): boolean { if (this._selectors.length === 0) { return false; } const lastSelector = this._selectors[this._selectors.length - 1]; if (lastSelector.id !== undefined) { return false; } this._selectors[this._selectors.length - 1].id = id; return true; } public getTag(): string|undefined { if (this._selectors.length === 0) { return undefined; } return this._selectors[this._selectors.length - 1].tag; } /** * The getClass method returns the object's first class. */ public getClass(index?: number): string[]|string { if (this._selectors.length === 0) { return []; } const lastSelector = this._selectors[this._selectors.length - 1]; if (lastSelector.classes === undefined || lastSelector.classes.length === 0) { return []; } if (index === undefined) { return lastSelector.classes; } if (index >= lastSelector.classes.length) { return []; } return lastSelector.classes[index]; } public setClasses(classes: string[], index?: number): void { if (this._selectors.length === 0) { return; } const lastIndex = this._selectors.length - 1; const lastSelector = this._selectors[lastIndex]; if (lastSelector.classes === undefined || lastSelector.classes.length === 0) { return; } if (index === undefined) { this._selectors[lastIndex].classes = classes; } if (index! >= lastSelector.classes.length) { return; } this._selectors[index!].classes = classes; } /** * The getAsChildLink method creates and returns * a new ObjectLink instance that copies the current instance's module and resource links * and extends its CSS selector by appending a child selector with the specified tag and id. * Example: * ``` * const childLink = parentLink.getEnumratedChild("div"); * // childLink.cssSelector will be "div#0" * const childLink2 = childLink.getAsParent().getEnumratedChild("varName"); * // childLink2.cssSelector will be "div#0 > varName#1" * ``` * @param tag The tag of the child element. * @returns */ public getEnumuratedChild(tag?: string, classes?: string[]): ObjectLink { const childLink = new ObjectLink(this._moduleLink, this._resourceLink); childLink._selectors = [...this._selectors, {tag, id: this._enumeratedCount++, classes}]; return childLink; } /** * The getTaggedChild method creates and returns * a new ObjectLink instance that copies the current instance's module and resource links * and extends its CSS selector by appending a child selector with the specified tag and id. * Example: * ``` * const childLink = parentLink.getTaggedChild("div", "varName"); * // childLink.cssSelector will be "div#varName" * ``` * @param tag The tag of the child element. * @param id The id of the child element. * @returns */ public getTaggedChild(tag: string, id?: string, classes?: string[]): ObjectLink { const childLink = new ObjectLink(this._moduleLink, this._resourceLink); childLink._selectors = [...this._selectors, {tag, id, classes}]; return childLink; } public toString(): ObjectURL { let url = `obj://${this.selector}`; if (this._moduleLink !== undefined) { url += `?module-link=${this._moduleLink}`; if (this._resourceLink !== undefined) { url += `&resource-link=${this._resourceLink}`; } } return url as ObjectURL; } }