import thisHtml from './breadcrumb.html'; import breadcrumbCss from './breadcrumb.css'; import stylesCss from '../../../styles.css'; import rootCss from '../../../root.css'; import { extractErrorMsg, } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs"; import { IbGib_V1 } from "@ibgib/ts-gib/dist/V1/types.mjs"; import { IbGibAddr } from "@ibgib/ts-gib/dist/types.mjs"; import { MetaspaceService } from "@ibgib/core-gib/dist/witness/space/metaspace/metaspace-types.mjs"; import { IbGibDynamicComponentInstanceBase, IbGibDynamicComponentMetaBase } from "@ibgib/web-gib/dist/ui/component/ibgib-dynamic-component-bases.mjs"; import { IbGibDynamicComponentInstance, IbGibDynamicComponentInstanceInitOpts, } from "@ibgib/web-gib/dist/ui/component/component-types.mjs"; import { GLOBAL_LOG_A_LOT, ARMY_STORE, BEE_KEY, BLANK_GIB_DB_NAME, } from "../../../constants.mjs"; import { BreadcrumbInfo } from "./breadcrumb-types.mjs"; import { getComponentCtorArg } from '../../../helpers.web.mjs'; const logalot = GLOBAL_LOG_A_LOT; export const BREADCRUMB_COMPONENT_NAME: string = 'ibgib-breadcrumb'; export class BreadcrumbComponentMeta extends IbGibDynamicComponentMetaBase { protected override lc: string = `[${BreadcrumbComponentMeta.name}]`; /** * temporary regexp path for our initial dev. this component will become * attached to actual ib^gib addrs */ routeRegExp?: RegExp = new RegExp(BREADCRUMB_COMPONENT_NAME); // routeRegExp?: RegExp = /apps\/web1\/gib\/contact.html/; componentName: string = BREADCRUMB_COMPONENT_NAME; constructor() { super(getComponentCtorArg()); customElements.define(this.componentName, BreadcrumbComponentInstance); } /** * for a breadcrumb, we don't have any additional info in the path. */ async createInstance({ path, ibGibAddr }: { path: string; ibGibAddr: IbGibAddr; }): Promise { const lc = `${this.lc}[${this.createInstance.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: genuuid)`); } const component = document.createElement(this.componentName) as BreadcrumbComponentInstance; await component.initialize({ ibGibAddr, meta: this, html: thisHtml, css: [rootCss, stylesCss, breadcrumbCss], }); return component; } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } } interface BreadcrumbElements { componentEl: HTMLElement; itemEls: HTMLElement[]; } export class BreadcrumbComponentInstance extends IbGibDynamicComponentInstanceBase implements IbGibDynamicComponentInstance { protected override lc: string = `[${BreadcrumbComponentInstance.name}]`; metaspace: MetaspaceService | undefined; breadcrumbs: BreadcrumbInfo[] = []; constructor() { super(); } override async initialize(opts: IbGibDynamicComponentInstanceInitOpts): Promise { const lc = `${this.lc}[${this.initialize.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: genuuid)`); } await super.initialize(opts); // await this.loadIbGib(); // await super.subscribeToIbGibUpdates(); } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } override async created(): Promise { const lc = `${this.lc}[${this.created.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: genuuid)`); } // const { meta, htmlPath, scriptPaths, cssPaths } = opts; await this.initUI(); await this.renderUI(); } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } private async initUI(): Promise { const lc = `${this.lc}[${this.initUI.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: 98c5bd4e6796961b7672cb3258e4da25)`); } const shadowRoot = this.shadowRoot; if (!shadowRoot) { throw new Error(`(UNEXPECTED) shadowRoot falsy? (E: genuuid)`); } const componentEl = shadowRoot.getElementById('breadcrumb-component') as HTMLElement; if (!componentEl) { throw new Error(`(UNEXPECTED) componentEl not found in shadowRoot? (E: genuuid)`); } this.elements = { componentEl, itemEls: [], }; } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } /** * rerender */ protected async renderUI(): Promise { const lc = `${this.lc}[${this.renderUI.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: 5415ec9be03ca640cb1a3d6a17d22625)`); } if (!this.shadowRoot) { throw new Error(`(UNEXPECTED) this.shadowRoot falsy? (E: 48b0f7d56fda65b40a57a11a8557ef25)`); } if (!this.elements) { console.warn(`${lc} (UNEXPECTED) tried to render but haven't initialized elements? (W: genuuid)`); return; /* <<<< returns early */ } const { componentEl, } = this.elements; // render buttons here and hook them up componentEl.innerHTML = ''; for (let i = 0; i < this.breadcrumbs.length; i++) { const breadcrumb = this.breadcrumbs[i]; const delim = document.createElement('span'); delim.textContent = '>'; componentEl.appendChild(delim); // most breadcrumbs are navigable, but the last one is supposed // to be our current location (so we can't nav via the current // breadcrumb) if (i < (this.breadcrumbs.length - 1) && !!breadcrumb.fnClickAction) { const link = document.createElement('a'); link.textContent = breadcrumb.text; componentEl.appendChild(link); link.addEventListener('click', async (e) => { await this.execNav({ breadcrumb, breadcrumbIndex: i }); }); } else { const text = document.createElement('span'); text.textContent = breadcrumb.text; componentEl.appendChild(text); } } } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } private async execNav({ breadcrumb, breadcrumbIndex, }: { breadcrumb: BreadcrumbInfo, breadcrumbIndex: number, }): Promise { const lc = `${this.lc}[${this.execNav.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: f5cb2b0797ed11f0ef21e3ada0b02225)`); } if (breadcrumb.fnClickAction) { // clear any downstream breadcrumbs for (let i = breadcrumbIndex + 1; i < this.breadcrumbs.length; i++) { this.breadcrumbs.pop(); } // update the breadcrumb UI first because it should be quick and // the action might take awhile await this.renderUI(); // execute it await breadcrumb.fnClickAction(); } else { console.warn(`${lc} executing nav but fnClickAction is falsy? (W: f36c438110be36a11494d5e4d3c56625)`); } } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } override async disconnected(): Promise { const lc = `${this.lc}[${this.disconnected.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: genuuid)`); } // no action atow } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } // #region public api public async addBreadcrumb({ info, clear, }: { info: BreadcrumbInfo, clear?: boolean, }): Promise { const lc = `${this.lc}[${this.addBreadcrumb.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: 705301d430c4f65b18e005e3020c7825)`); } if (clear) { this.breadcrumbs = []; } // do the business this.breadcrumbs.push(info); await this.renderUI(); } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } // #endregion public api }