/* eslint-disable */ // 为了解耦 jinge-material 库和 jinge-router 库之间的直接依赖(使用 jinge-material 不一定必须要使用 jinge-router),在 jinge-material 中重新实现一份 组件并命名为 UISref。 // 这个 UISref 组件用于 md-button 等组件的内部默认 route 功能,不暴露给用户使用。用户应该使用 jinge-router 库提供的 组件。 // 但 ts 类型没有必要重复书写,因此将 jinge-router 以 dev dep 的方式依赖。 import { Component, setAttribute, MessengerListener, removeEvent, addEvent, __, isObject, watch, unwatch, Attributes, isUndefined, } from 'jinge'; import { RouteJumpTarget, RouteParamsOrQuery, RouteLocation, Router } from 'jinge-router'; import _tpl from './index.html'; /** * 从 jinge-router/src/util.ts 拷贝的函数,用于判定 url 是否包含。 */ export function isParamsOrQuerySameOrInclude(src: RouteParamsOrQuery, dst: RouteParamsOrQuery, strict = true): boolean { if (!src) return !dst; if (!dst) return !src; let kc = 0; for (const k in src) { const sv = src[k]; const dv = dst[k]; if (strict) { if (sv !== dv) return false; } else { if (isUndefined(dv) || dv === null) { if (!isUndefined(sv) && sv !== null) { return false; } } else if (sv !== dv) { return false; } } kc++; } if (strict && kc !== Object.keys(dst).length) { return false; } return true; } export class UISref extends Component { static template = _tpl; _router: Router; _el: HTMLElement; _tag: number; _active: string; _target: RouteJumpTarget; _to: string | RouteLocation; /** * router changed handler */ _rch: MessengerListener; /** * click handler */ _clh: EventListener; /** * query watched */ _qw: boolean; /** * router onChange deregister */ _rcd: () => void; replace: boolean; text: string; isActive: boolean; constructor( attrs: Attributes<{ to: string | RouteLocation; text: string; target: RouteJumpTarget; replace: boolean; active: string; }>, ) { super(attrs); this.to = attrs.to; this.text = attrs.text || ''; this.target = attrs.target || '_self'; this.replace = !!attrs.replace; this.active = attrs.active; this._router = this.__getContext('router') as Router; if (!this._router) { throw new Error('Context named "router" not found.'); } this._tag = attrs[__].slots?.default ? 0 : -1; this._el = null; this._qw = false; // query is watched this._clh = this._onClick.bind(this); // click handler this._rch = this._onRc.bind(this); this._rcd = null; // router onChange deregister } /** * @internal * * handle router changed event/guard */ _onRc() { this._upA(); } get target(): RouteJumpTarget { return this._target; } set target(v: RouteJumpTarget) { if (this._target === v) return; this._target = v; this._upT(); } get active(): string { return this._active; } set active(v: string) { if (this._active === v) return; if (this._tag >= 0 && this._active && this._el) { this._el.classList.remove(this._active); // remove previous active class } this._active = v; this.__updateIfNeed(this._upA); } get to(): string | RouteLocation { return this._to; } set to(v: string | RouteLocation) { if (this._to === v) return; this._to = v; this.__updateIfNeed(this._upHa); } /** * @internal */ _onClick(e: KeyboardEvent) { if (e.defaultPrevented || e.metaKey || e.ctrlKey) { return; } if (this._tag <= 0) { e.preventDefault(); // prevent default jump } this._router.go(this._to, { target: this.target, replace: this.replace, }); } __afterRender() { const el = this.__firstDOM as HTMLElement; if (this._tag >= 0) { this._tag = el.tagName === 'A' ? 0 : 1; } this._el = el; this._upT(); this._upHa(); this._rcd = this._router.afterEach(() => { this._onRc(); }); addEvent(el, 'click', this._clh); } __beforeDestroy() { removeEvent(this._el, 'click', this._clh); this._rcd(); if (this._qw) { unwatch(this._router.__info, 'query.*', this._rch); } } /** * @internal * * update target attribute of link */ _upT() { if (this._tag <= 0) { setAttribute(this._el, 'target', this.target); } } /** * @internal * * update href and active class */ _upHa() { this._upH(); this._upA(); } /** * @internal * * update href attribute of link */ _upH() { if (this._tag <= 0) { let href; if (!this._to || !(href = this._router.href(this._to))) { this._el.removeAttribute('href'); } else { setAttribute(this._el, 'href', href); } } } /** * @internal * * update active class of link */ _upA() { let isActive = this._to && this._router.includes(this._to); if (isActive && isObject(this._to) && (this._to as RouteLocation).query) { if (!this._qw) { watch(this._router.__info, 'query.*', this._rch); this._qw = true; } isActive = isParamsOrQuerySameOrInclude((this._to as RouteLocation).query, this._router.__info?.query); } else if (this._qw) { this._qw = false; unwatch(this._router.__info, 'query.*', this._rch); } if (this.isActive === isActive) { return; } this.isActive = isActive; if (!this._active || this._tag < 0) { return; } if (this.isActive) { this._el.classList.add(this._active); } else { this._el.classList.remove(this._active); } } }