import { CLIENT_CONTEXT, requestNavigation, setupComponentSubscriptions, } from "@helpers"; import { ComponentObjectsHandler } from "@helpers/components/componentObjectsHandler"; import type { Container, LiveOrbit, PropertiesPicker, ProxyValue, Resource, UnknownResource, } from "@src/component"; import { nothing } from "lit"; import { property, state } from "lit/decorators.js"; export default class extends ComponentObjectsHandler { constructor({ defaultRoute = false, setupSubscriptions = true, ignoreRouter = false, }: { defaultRoute?: boolean | string; setupSubscriptions?: boolean; ignoreRouter?: boolean; } = {}) { super(); const attach = () => { if (document.readyState === "complete") { this._attach(defaultRoute, setupSubscriptions, ignoreRouter).then( (attach: boolean) => { if (attach) { this.requestUpdate(); } }, ); } }; if (document.readyState === "complete") { attach(); } else { document.addEventListener("readystatechange", attach); } } @state() ready = false; @property({ attribute: "default-data-src", reflect: true }) defaultDataSrc?: string; @property({ attribute: "data-src", reflect: true }) dataSrc?: string; @property({ attribute: "nested-field" }) nestedField?: string; @property({ attribute: "uniq" }) uniq?: string; @property({ attribute: "route" }) route: string | undefined; @property({ attribute: false }) cherryPickedProperties: PropertiesPicker[] = []; @state() orbit: LiveOrbit | undefined; @state() currentRoute = ""; protected async _attach( defaultRoute: boolean | string, setupSubscriptions: boolean, ignoreRouter: boolean, ) { if (!this.orbit) { if (window.orbit) { this.orbit = window.orbit; if (setupSubscriptions) { setupComponentSubscriptions({ component: this, defaultRoute: defaultRoute, ignoreRouter: ignoreRouter, }); if (this.route) { this.component = this.orbit.getComponentFromRoute(this.route); if (this.component) { for (const c of this.orbit.components) { if (c.uniq === this.component.uniq) { c.instance = this; } } } } } await this._afterAttach(); this.ready = true; this.dispatchEvent( new CustomEvent("component-ready", { detail: { component: this.component, }, }), ); return Promise.resolve(true); } } return Promise.resolve(false); } async _afterAttach() { return Promise.resolve(); } _navigate(e: Event) { window.sibRouter.previousRoute = this.currentRoute; window.sibRouter.previousResource = window.sibRouter.currentResource; const navigator = e.target?.closest("[navigation-target]"); let target = navigator.getAttribute("navigation-target"); const subrouter = navigator.getAttribute("navigation-subrouter"); const resource = navigator.getAttribute("navigation-resource"); const rdfType = navigator.getAttribute("navigation-rdf-type"); if (rdfType) { const compatibleComponents = window.orbit?.components?.filter( (c) => c?.routeAttributes?.["rdf-type"] === rdfType, ); if (compatibleComponents) target = compatibleComponents[0]?.uniq; } if (target) { requestNavigation( (window.orbit ? window.orbit.getRoute(target, true) : target) + (subrouter ? `-${subrouter}` : ""), resource, ); } e.preventDefault(); } _normalizeLdpContains(value: Resource[] | Resource | null): Resource[] { if (value === null) { return []; } if (!Array.isArray(value)) { return [value]; } return value; } async _expandContainer( value: Resource[], recursive = true, targetProperties: PropertiesPicker[] = this.cherryPickedProperties, ): Promise { const expandedContainer: UnknownResource[] = []; await Promise.all( value.map(async (entry) => { const line = await this._getProxyValue( await entry, recursive, targetProperties, ); if (line) expandedContainer.push(line); }), ); return expandedContainer; } async _getProperties( resource: Resource, recursive = true, targetProperties: PropertiesPicker[] = this.cherryPickedProperties, ) { const properties = await resource.properties; const response: Resource = { "@id": resource["@id"], "@type": resource["@type"], "@context": resource.serverContext, _originalResource: resource, }; const propPromises = targetProperties.map(async (prop) => { if (!properties?.includes(prop.key)) return; let value = await resource.get(prop.key); if (prop.expand) { value = await this._getProxyValue(value, recursive, targetProperties); } if (prop.cast) { value = await prop.cast(value); } return { prop, value }; }); const results = (await Promise.all(propPromises)).filter( (r): r is { prop: PropertiesPicker; value: unknown } => r !== undefined, ); for (const { prop, value } of results) { if (value !== undefined) response[prop.value] = value; } return await this._responseAdaptator(response); } async _hasCherryPickedProperties( resource: Resource, targetProperties = this.cherryPickedProperties, ) { const properties = await resource.properties; for (const prop of targetProperties) { if (properties?.includes(prop.key)) { return true; } } return false; } async _getProxyValue( resource: | string | Resource | ProxyValue | Resource>>, recursive = true, targetProperties: PropertiesPicker[] = this.cherryPickedProperties, ) { try { if (!resource) return; let target = resource; if (typeof resource === "string") { target = await window.sibStore.getData(resource, CLIENT_CONTEXT); } else if (resource.isFullResource && !resource.isFullResource?.()) { target = await window.sibStore.getData(resource["@id"], CLIENT_CONTEXT); } if (!target) return { _originalResource: target }; if (typeof target !== "object" || target === null) return; if (target.isContainer?.() && target["ldp:contains"]) { if (await this._hasCherryPickedProperties(target, targetProperties)) { return await this._getProperties(target, recursive, targetProperties); } const value = this._normalizeLdpContains(await target["ldp:contains"]); return await this._expandContainer(value, recursive, targetProperties); } return await this._getProperties(target, recursive, targetProperties); } catch (e) { if (import.meta.env.DEV) console.error(e); } } async _responseAdaptator(response: Resource) { return Promise.resolve(response); } gatekeeper() { if ( !this.orbit || (!this.noRouter && this.route && this.currentRoute && !this.route.startsWith(this.currentRoute)) ) { return nothing; } if (!this.dataSrc) { return nothing; } } }