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.

242 lines (241 loc) 6.8 kB
/*! * NENT 2022 */ import { Component, Element, h, Host, Prop, State, } from '@stencil/core'; import { isValue, logIf } from '../../services/common'; import { getChildInputValidity } from '../n-view-prompt/services/elements'; import { onRoutingChange, routingState, } from '../n-views/services/state'; /** * The element should be used in-place of an `a` tag to navigate without * refreshing the page. This element supports an active-class that will * be applied when the route in **href** matches the route of the app. * * This is helpful for displaying active routes in menus, bread-crumbs and tabs. * * @system routing */ export class ViewLink { constructor() { /** * The class to add when the matching route is active * in the browser */ this.activeClass = 'active'; /** * Only active on the exact href match, * and not on child routes */ this.exact = false; /** * Only active on the exact href match * using every aspect of the URL including * parameters. */ this.strict = true; /** * Provide log messages for path matching. */ this.debug = false; /** * Validates any current-route inputs before navigating. Disables * navigation if any inputs are invalid. */ this.validate = false; } get parentUrl() { var _a, _b; return (((_a = this.el.closest('n-view-prompt')) === null || _a === void 0 ? void 0 : _a.path) || ((_b = this.el.closest('n-view')) === null || _b === void 0 ? void 0 : _b.path)); } componentWillLoad() { this.routeSubscription = onRoutingChange('location', () => { if (routingState.router) { this.path = routingState.router.resolvePathname(this.path, this.parentUrl || '/'); const match = routingState.router.matchPath({ path: this.path, exact: this.exact, strict: this.strict, }); this.match = match ? Object.assign({}, match) : null; } }); } handleClick(e, path) { const router = routingState.router; if (router == null || router.isModifiedEvent(e) || path == undefined) { return true; } else { e.stopImmediatePropagation(); e.preventDefault(); if (this.validate == false || getChildInputValidity(router.exactRoute.routeElement)) { router.goToRoute(path); } return false; } } render() { const { router } = routingState; const path = router === null || router === void 0 ? void 0 : router.resolvePathname(this.path, this.parentUrl); const match = router === null || router === void 0 ? void 0 : router.matchPath({ path: path, exact: this.exact, strict: this.strict, }); const classes = { [this.activeClass]: isValue(match), [this.linkClass || '']: true, }; logIf(this.debug, 'n-view-link re-render ' + path); let anchorAttributes = { title: this.el.title, role: this.el.getAttribute('aria-role'), id: this.el.id, }; return (h(Host, null, h("a", Object.assign({ href: path }, anchorAttributes, { "n-attached-click": true, "n-attached-key-press": true, class: classes, onClick: (e) => { return this.handleClick(e, path); }, onKeyPress: (e) => { return this.handleClick(e, path); } }), h("slot", null)))); } disconnectedCallback() { var _a; (_a = this.routeSubscription) === null || _a === void 0 ? void 0 : _a.call(this); } static get is() { return "n-view-link"; } static get properties() { return { "path": { "type": "string", "mutable": true, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": true, "optional": false, "docs": { "tags": [], "text": "The destination route for this link" }, "attribute": "path", "reflect": false }, "linkClass": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string | undefined", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "The class to add to the anchor tag." }, "attribute": "link-class", "reflect": false }, "activeClass": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "The class to add when the matching route is active\nin the browser" }, "attribute": "active-class", "reflect": false, "defaultValue": "'active'" }, "exact": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Only active on the exact href match,\nand not on child routes" }, "attribute": "exact", "reflect": false, "defaultValue": "false" }, "strict": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Only active on the exact href match\nusing every aspect of the URL including\nparameters." }, "attribute": "strict", "reflect": false, "defaultValue": "true" }, "debug": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Provide log messages for path matching." }, "attribute": "debug", "reflect": false, "defaultValue": "false" }, "validate": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Validates any current-route inputs before navigating. Disables\nnavigation if any inputs are invalid." }, "attribute": "validate", "reflect": true, "defaultValue": "false" } }; } static get states() { return { "match": {} }; } static get elementRef() { return "el"; } }