UNPKG

@nent/core

Version:

Functional elements to add routing, data-binding, dynamic HTML, declarative actions, audio, video, and so much more. Supercharge static HTML files into web apps without script or builds.

258 lines (252 loc) 8.94 kB
/*! * NENT 2022 */ import { proxyCustomElement, HTMLElement, h, Host } from '@stencil/core/internal/client'; import { f as debugIf, w as warn } from './logging.js'; import { c as resolveRemoteContentElement, r as replaceHtmlInElement } from './remote.js'; import { D as DATA_EVENTS } from './interfaces3.js'; import { s as state$1 } from './state4.js'; import { a as state } from './state.js'; import { V as VisitStrategy, h as hasVisited, m as markVisit } from './visits.js'; import { s as slugify } from './strings.js'; import { C as CommonStateSubscriber } from './state-subscriber.js'; /** * It takes a view prompt, and returns a view prompt with the `visited` and `visit` properties set * @param {IViewPrompt} viewPrompt - IViewPrompt * @returns A promise that resolves to an object with the following properties: * when: string * visit: VisitStrategy * visited: boolean * path: string */ async function getViewPromptStateProperties(viewPrompt) { let { when, path, visit = VisitStrategy.once } = viewPrompt; let visited = await hasVisited(path); if (state.dataEnabled && when) { const { evaluatePredicate } = await import('./expressions.js').then(function (n) { return n.f; }); const shouldGo = await evaluatePredicate(when); if (shouldGo) { visit = VisitStrategy.once; visited = false; } else { visit = VisitStrategy.optional; } } return { when, visit, visited, path }; } /** * > Find the first unvisited item in the list that is not optional * @param {IViewPrompt[]} doList - IViewPrompt[] * @returns A Promise that resolves to an IViewPrompt or null */ async function findFirstUnvisited(doList) { const found = doList .filter(d => d.visit !== VisitStrategy.optional) .find(i => i.visited == false); return found || null; } /** * It takes an array of view prompts, and returns the first one that hasn't been visited yet * @param childViewDos - Array<IViewPrompt> * @returns A promise that resolves to the first unvisited view prompt. */ async function resolveNext(childViewDos) { const converted = await Promise.all(childViewDos.map(e => getViewPromptStateProperties(e))); const result = await findFirstUnvisited(converted); return result; } const viewCss = ":host{display:none}:host(.active){display:block}:host(.active) ::slotted([slot='content']),:host(.active) div[slot='content']{display:none}:host(.exact){display:block}:host(.exact) ::slotted([slot='content']),:host(.exact) div[slot='content']{display:block}"; const View = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement { constructor() { super(); this.__registerHost(); this.__attachShadow(); this.match = null; this.exactMatch = false; this.srcElement = null; this.contentElement = null; /** * The title for this view. This is prefixed * before the app title configured in n-views * */ this.pageTitle = ''; /** * The page description for this view. * */ this.pageDescription = ''; /** * The keywords to add to the keywords meta-tag for this view. * */ this.pageKeywords = ''; /** * The robots instruction for search indexing * */ this.pageRobots = 'all'; /** * Header height or offset for scroll-top on this * view. */ this.scrollTopOffset = 0; /** * The path for this route should only be matched * when it is exact. */ this.exact = false; /** * Cross Origin Mode if the content is pulled from * a remote location */ this.mode = 'cors'; /** * Before rendering remote HTML, replace any data-tokens with their * resolved values. This also commands this element to * re-render it's HTML for data-changes. This can affect * performance. * * IMPORTANT: ONLY WORKS ON REMOTE HTML */ this.resolveTokens = false; /** * Turn on debug statements for load, update and render events. */ this.debug = false; /** * Force render with data & route changes. */ this.noCache = false; } /** * Return all child elements used for processing. This function is * primarily meant for testing. * */ async getChildren() { return { activators: this.route.actionActivators, views: this.childViews, dos: this.childPrompts, }; } get parent() { var _a; return ((_a = this.el.parentElement) === null || _a === void 0 ? void 0 : _a.closest('n-view')) || null; } get childPrompts() { return Array.from(this.el.querySelectorAll('n-view-prompt') || []).filter(e => this.route.isChild(e)); } get childViews() { return Array.from(this.el.querySelectorAll('n-view') || []).filter(e => this.route.isChild(e)); } async componentWillLoad() { debugIf(this.debug, `n-view: ${this.path} loading`); this.contentKey = `rem-content-${slugify(this.contentSrc || 'none')}`; this.srcKey = `rem-source-${slugify(this.src || 'none')}`; if (!state$1.router) { warn(`n-view: ${this.path} cannot load outside of an n-views element`); return; } this.route = state$1.router.createRoute(this.el, this.parent, (match) => { this.match = match ? Object.assign({}, match) : null; this.exactMatch = (match === null || match === void 0 ? void 0 : match.isExact) || false; }); if (state.dataEnabled && this.resolveTokens) { this.dataSubscription = new CommonStateSubscriber(this, 'dataEnabled', DATA_EVENTS.DataChanged); } } async componentWillRender() { var _a; debugIf(this.debug, `n-view: ${this.path} will render`); if (this.match) { debugIf(this.debug, `n-view: ${this.path} route is matched `); if (this.src && this.srcElement == null) { this.srcElement = await resolveRemoteContentElement(window, this.src, this.mode, this.srcKey, this.resolveTokens); replaceHtmlInElement(this.el, `#${this.srcKey}`, this.srcElement); } debugIf(this.debug, `n-view: ${this.path} found ${this.childViews.length} child views and` + ` ${this.childPrompts.length} child view-prompts`); // exact-match if (this.match.isExact) { debugIf(this.debug, `n-view: ${this.path} route exactly matched `); const viewDos = this.childPrompts.map(el => { const { path, when, visit } = el; return { path: this.route.normalizeChildUrl(path), when, visit, }; }); const nextDo = await resolveNext(viewDos); if (nextDo) { this.route.replaceWithRoute(nextDo.path); return; } else { if (this.contentSrc && this.contentElement == null) this.contentElement = await resolveRemoteContentElement(window, this.contentSrc, this.mode, this.contentKey, this.resolveTokens, 'content'); markVisit((_a = this.match) === null || _a === void 0 ? void 0 : _a.url); } } } } render() { debugIf(this.debug, `n-view: ${this.path} render`); replaceHtmlInElement(this.el, `#${this.contentKey}`, this.contentElement); return (h(Host, null, h("slot", null), h("slot", { name: "content" }))); } async componentDidRender() { var _a, _b, _c, _d; if (!((_b = (_a = this.route) === null || _a === void 0 ? void 0 : _a.match) === null || _b === void 0 ? void 0 : _b.isExact)) { (_c = this.contentElement) === null || _c === void 0 ? void 0 : _c.remove(); if (this.noCache) this.contentElement = null; } await ((_d = this.route) === null || _d === void 0 ? void 0 : _d.loadCompleted()); } disconnectedCallback() { var _a, _b; (_a = this.dataSubscription) === null || _a === void 0 ? void 0 : _a.destroy(); (_b = this.route) === null || _b === void 0 ? void 0 : _b.destroy(); } get el() { return this; } static get style() { return viewCss; } }, [1, "n-view", { "route": [1040], "pageTitle": [1, "page-title"], "pageDescription": [1, "page-description"], "pageKeywords": [1, "page-keywords"], "pageRobots": [1, "page-robots"], "scrollTopOffset": [2, "scroll-top-offset"], "transition": [1], "path": [1537], "exact": [4], "src": [1], "contentSrc": [1, "content-src"], "mode": [1], "resolveTokens": [4, "resolve-tokens"], "debug": [4], "noCache": [4, "no-cache"], "match": [32], "exactMatch": [32], "getChildren": [64] }]); function defineCustomElement$1() { if (typeof customElements === "undefined") { return; } const components = ["n-view"]; components.forEach(tagName => { switch (tagName) { case "n-view": if (!customElements.get(tagName)) { customElements.define(tagName, View); } break; } }); } const NView = View; const defineCustomElement = defineCustomElement$1; export { NView, defineCustomElement };