router.mjs 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. // Utilities
  2. import { computed, nextTick, onScopeDispose, reactive, resolveDynamicComponent, toRef } from 'vue';
  3. import { deepEqual, getCurrentInstance, hasEvent, IN_BROWSER, propsFactory } from "../util/index.mjs"; // Types
  4. export function useRoute() {
  5. const vm = getCurrentInstance('useRoute');
  6. return computed(() => vm?.proxy?.$route);
  7. }
  8. export function useRouter() {
  9. return getCurrentInstance('useRouter')?.proxy?.$router;
  10. }
  11. export function useLink(props, attrs) {
  12. const RouterLink = resolveDynamicComponent('RouterLink');
  13. const isLink = computed(() => !!(props.href || props.to));
  14. const isClickable = computed(() => {
  15. return isLink?.value || hasEvent(attrs, 'click') || hasEvent(props, 'click');
  16. });
  17. if (typeof RouterLink === 'string' || !('useLink' in RouterLink)) {
  18. const href = toRef(props, 'href');
  19. return {
  20. isLink,
  21. isClickable,
  22. href,
  23. linkProps: reactive({
  24. href
  25. })
  26. };
  27. }
  28. // vue-router useLink `to` prop needs to be reactive and useLink will crash if undefined
  29. const linkProps = computed(() => ({
  30. ...props,
  31. to: toRef(() => props.to || '')
  32. }));
  33. const routerLink = RouterLink.useLink(linkProps.value);
  34. // Actual link needs to be undefined when to prop is not used
  35. const link = computed(() => props.to ? routerLink : undefined);
  36. const route = useRoute();
  37. const isActive = computed(() => {
  38. if (!link.value) return false;
  39. if (!props.exact) return link.value.isActive?.value ?? false;
  40. if (!route.value) return link.value.isExactActive?.value ?? false;
  41. return link.value.isExactActive?.value && deepEqual(link.value.route.value.query, route.value.query);
  42. });
  43. const href = computed(() => props.to ? link.value?.route.value.href : props.href);
  44. return {
  45. isLink,
  46. isClickable,
  47. isActive,
  48. route: link.value?.route,
  49. navigate: link.value?.navigate,
  50. href,
  51. linkProps: reactive({
  52. href,
  53. 'aria-current': computed(() => isActive.value ? 'page' : undefined)
  54. })
  55. };
  56. }
  57. export const makeRouterProps = propsFactory({
  58. href: String,
  59. replace: Boolean,
  60. to: [String, Object],
  61. exact: Boolean
  62. }, 'router');
  63. let inTransition = false;
  64. export function useBackButton(router, cb) {
  65. let popped = false;
  66. let removeBefore;
  67. let removeAfter;
  68. if (IN_BROWSER) {
  69. nextTick(() => {
  70. window.addEventListener('popstate', onPopstate);
  71. removeBefore = router?.beforeEach((to, from, next) => {
  72. if (!inTransition) {
  73. setTimeout(() => popped ? cb(next) : next());
  74. } else {
  75. popped ? cb(next) : next();
  76. }
  77. inTransition = true;
  78. });
  79. removeAfter = router?.afterEach(() => {
  80. inTransition = false;
  81. });
  82. });
  83. onScopeDispose(() => {
  84. window.removeEventListener('popstate', onPopstate);
  85. removeBefore?.();
  86. removeAfter?.();
  87. });
  88. }
  89. function onPopstate(e) {
  90. if (e.state?.replaced) return;
  91. popped = true;
  92. setTimeout(() => popped = false);
  93. }
  94. }
  95. //# sourceMappingURL=router.mjs.map