UNPKG

@esmx/router-vue

Version:

Vue integration for @esmx/router - A universal router that works seamlessly with both Vue 2.7+ and Vue 3

141 lines (140 loc) 4.73 kB
import { defineComponent, h } from "vue"; import { useLink } from "./use.mjs"; import { isVue3 } from "./util.mjs"; export const RouterLink = defineComponent({ name: "RouterLink", props: { /** * Target route location to navigate to. * Can be a string path or route location object. * @example '/home' | { path: '/user', query: { id: '123' } } */ to: { type: [String, Object], required: true }, /** * Navigation type for the link. * @default 'push' * @example 'push' | 'replace' | 'pushWindow' | 'replaceWindow' | 'pushLayer' */ type: { type: String, default: "push" }, /** * @deprecated Use 'type="replace"' instead * @example :replace={true} → type="replace" */ replace: { type: Boolean, default: false }, /** * How to match the active state. * - 'include': Match if current route includes this path * - 'exact': Match only if routes are exactly the same * - 'route': Match based on route configuration * @default 'include' */ exact: { type: String, default: "include" }, /** * CSS class to apply when link is active (route matches). * @example 'nav-active' | 'selected' */ activeClass: { type: String }, /** * Event(s) that trigger navigation. Can be string or array of strings. * @default 'click' * @example 'click' | ['click', 'mouseenter'] */ event: { type: [String, Array], default: "click" }, /** * Custom tag to render instead of 'a'. * @default 'a' * @example 'button' | 'div' | 'span' */ tag: { type: String, default: "a" }, /** * Layer options for layer-based navigation. * Only used when type='pushLayer'. * @example { zIndex: 1000, autoPush: false, routerOptions: { mode: 'memory' } } */ layerOptions: { type: Object }, /** * Custom event handler to control navigation behavior. * Should return `true` to allow router to navigate, otherwise to prevent it. * * @Note you need to call `e.preventDefault()` to prevent default browser navigation. * @default * * (event: Event & Partial<MouseEvent>): boolean => { * // don't redirect with control keys * if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return false; * // don't redirect when preventDefault called * if (e.defaultPrevented) return false; * // don't redirect on right click * if (e.button !== undefined && e.button !== 0) return false; * // don't redirect if `target="_blank"` * const target = e.currentTarget?.getAttribute?.('target') ?? ''; * if (/\b_blank\b/i.test(target)) return false; * // Prevent default browser navigation to enable SPA routing * // Note: this may be a Weex event which doesn't have this method * if (e.preventDefault) e.preventDefault(); * * return true; * } */ eventHandler: { type: Function } }, setup(props, context) { const { slots, attrs } = context; const link = useLink(props); const wrapHandler = (externalHandler, internalHandler) => !internalHandler ? externalHandler : async (e) => { try { await externalHandler(e); } finally { await internalHandler(e); } }; const vue3renderer = () => { var _a; const data = link.value; const genEventName = (name) => "on".concat(name.charAt(0).toUpperCase()).concat(name.slice(1)); const eventHandlers = data.getEventHandlers(genEventName); Object.entries(attrs).forEach(([key, listener]) => { if (!key.startsWith("on") || typeof listener !== "function") return; eventHandlers[key] = wrapHandler(listener, eventHandlers[key]); }); return h( data.tag, { ...data.attributes, ...eventHandlers }, (_a = slots.default) == null ? void 0 : _a.call(slots) ); }; const vue2renderer = () => { var _a; const data = link.value; const eventHandlers = data.getEventHandlers(); const $listeners = context.listeners || {}; Object.entries($listeners).forEach(([key, listener]) => { if (typeof listener !== "function") return; eventHandlers[key] = wrapHandler(listener, eventHandlers[key]); }); const { class: className, ...attrs2 } = data.attributes; return h( data.tag, { attrs: attrs2, class: className, on: eventHandlers }, (_a = slots.default) == null ? void 0 : _a.call(slots) ); }; return isVue3 ? vue3renderer : vue2renderer; } });