@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
JavaScript
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;
}
});