@real-router/svelte 0.2.5 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { getNavigator } from "@real-router/core";
3
3
  import { createRouteSource } from "@real-router/sources";
4
- import { createRouteAnnouncer } from "dom-utils";
4
+ import { createRouteAnnouncer } from "./dom-utils";
5
5
  import { setContext } from "svelte";
6
6
 
7
7
  import { createReactiveSource } from "./createReactiveSource.svelte";
@@ -1,6 +1,6 @@
1
1
  import { getContext } from "svelte";
2
2
  import { ROUTER_KEY } from "../context";
3
- import { shouldNavigate, applyLinkA11y } from "dom-utils";
3
+ import { shouldNavigate, applyLinkA11y } from "../dom-utils";
4
4
  /**
5
5
  * Factory function that captures router context during component initialization.
6
6
  * Must be called during component init (not inside event handlers or effects).
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { useIsActiveRoute } from "../composables/useIsActiveRoute.svelte";
3
3
  import { useRouter } from "../composables/useRouter.svelte";
4
- import { shouldNavigate, buildHref, buildActiveClassName } from "dom-utils";
4
+ import { shouldNavigate, buildHref, buildActiveClassName } from "../dom-utils";
5
5
 
6
6
  import type { NavigationOptions, Params } from "@real-router/core";
7
7
  import type { Snippet } from "svelte";
@@ -0,0 +1,3 @@
1
+ export { createRouteAnnouncer } from "./route-announcer";
2
+ export { shouldNavigate, buildHref, buildActiveClassName, applyLinkA11y, } from "./link-utils";
3
+ export type { RouteAnnouncerOptions } from "./route-announcer";
@@ -0,0 +1,2 @@
1
+ export { createRouteAnnouncer } from "./route-announcer";
2
+ export { shouldNavigate, buildHref, buildActiveClassName, applyLinkA11y, } from "./link-utils";
@@ -0,0 +1,5 @@
1
+ import type { Router, Params } from "@real-router/core";
2
+ export declare function shouldNavigate(evt: MouseEvent): boolean;
3
+ export declare function buildHref(router: Router, routeName: string, routeParams: Params): string | undefined;
4
+ export declare function buildActiveClassName(isActive: boolean, activeClassName: string | undefined, baseClassName: string | undefined): string | undefined;
5
+ export declare function applyLinkA11y(element: HTMLElement): void;
@@ -0,0 +1,41 @@
1
+ export function shouldNavigate(evt) {
2
+ return (evt.button === 0 &&
3
+ !evt.metaKey &&
4
+ !evt.altKey &&
5
+ !evt.ctrlKey &&
6
+ !evt.shiftKey);
7
+ }
8
+ // Lives in dom-utils (not core/utils) because buildUrl is injected by browser-plugin via extendRouter()
9
+ export function buildHref(router, routeName, routeParams) {
10
+ try {
11
+ const buildUrl = router.buildUrl;
12
+ if (buildUrl) {
13
+ return buildUrl(routeName, routeParams);
14
+ }
15
+ return router.buildPath(routeName, routeParams);
16
+ }
17
+ catch {
18
+ console.error(`[real-router] Route "${routeName}" is not defined. The element will render without an href attribute.`);
19
+ return undefined;
20
+ }
21
+ }
22
+ export function buildActiveClassName(isActive, activeClassName, baseClassName) {
23
+ if (isActive && activeClassName) {
24
+ return baseClassName
25
+ ? `${baseClassName} ${activeClassName}`.trim()
26
+ : activeClassName;
27
+ }
28
+ return baseClassName ?? undefined;
29
+ }
30
+ export function applyLinkA11y(element) {
31
+ if (element instanceof HTMLAnchorElement ||
32
+ element instanceof HTMLButtonElement) {
33
+ return;
34
+ }
35
+ if (!element.getAttribute("role")) {
36
+ element.setAttribute("role", "link");
37
+ }
38
+ if (!element.getAttribute("tabindex")) {
39
+ element.setAttribute("tabindex", "0");
40
+ }
41
+ }
@@ -0,0 +1,8 @@
1
+ import type { Router, State } from "@real-router/core";
2
+ export interface RouteAnnouncerOptions {
3
+ prefix?: string;
4
+ getAnnouncementText?: (route: State) => string;
5
+ }
6
+ export declare function createRouteAnnouncer(router: Router, options?: RouteAnnouncerOptions): {
7
+ destroy: () => void;
8
+ };
@@ -0,0 +1,89 @@
1
+ const CLEAR_DELAY = 7000;
2
+ const SAFARI_READY_DELAY = 100;
3
+ const ANNOUNCER_ATTR = "data-real-router-announcer";
4
+ const INTERNAL_ROUTE_PREFIX = "@@";
5
+ const VISUALLY_HIDDEN = "position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);clip-path:inset(50%);white-space:nowrap;border:0";
6
+ export function createRouteAnnouncer(router, options) {
7
+ const prefix = options?.prefix ?? "Navigated to ";
8
+ const getCustomText = options?.getAnnouncementText;
9
+ let isInitialNavigation = true;
10
+ let isReady = false;
11
+ let isDestroyed = false;
12
+ let lastAnnouncedText = "";
13
+ let clearTimeoutId;
14
+ const announcer = getOrCreateAnnouncer();
15
+ const safariTimeoutId = setTimeout(() => {
16
+ isReady = true;
17
+ }, SAFARI_READY_DELAY);
18
+ const unsubscribe = router.subscribe(({ route }) => {
19
+ if (isInitialNavigation) {
20
+ isInitialNavigation = false;
21
+ return;
22
+ }
23
+ requestAnimationFrame(() => {
24
+ requestAnimationFrame(() => {
25
+ if (isDestroyed) {
26
+ return;
27
+ }
28
+ const text = resolveText(route, prefix, getCustomText);
29
+ if (text && text !== lastAnnouncedText && isReady) {
30
+ lastAnnouncedText = text;
31
+ clearTimeout(clearTimeoutId);
32
+ announcer.textContent = text;
33
+ clearTimeoutId = setTimeout(() => {
34
+ announcer.textContent = "";
35
+ lastAnnouncedText = "";
36
+ }, CLEAR_DELAY);
37
+ manageFocus();
38
+ }
39
+ });
40
+ });
41
+ });
42
+ return {
43
+ destroy() {
44
+ isDestroyed = true;
45
+ unsubscribe();
46
+ clearTimeout(clearTimeoutId);
47
+ clearTimeout(safariTimeoutId);
48
+ removeAnnouncer();
49
+ },
50
+ };
51
+ }
52
+ function getOrCreateAnnouncer() {
53
+ const existing = document.querySelector(`[${ANNOUNCER_ATTR}]`);
54
+ if (existing) {
55
+ return existing;
56
+ }
57
+ const element = document.createElement("div");
58
+ element.setAttribute("style", VISUALLY_HIDDEN);
59
+ element.setAttribute("aria-live", "assertive");
60
+ element.setAttribute("aria-atomic", "true");
61
+ element.setAttribute(ANNOUNCER_ATTR, "");
62
+ document.body.prepend(element);
63
+ return element;
64
+ }
65
+ function removeAnnouncer() {
66
+ document.querySelector(`[${ANNOUNCER_ATTR}]`)?.remove();
67
+ }
68
+ function resolveText(route, prefix, getCustomText) {
69
+ if (getCustomText) {
70
+ return getCustomText(route);
71
+ }
72
+ const h1 = document.querySelector("h1");
73
+ const h1Text = h1?.textContent.trim() ?? "";
74
+ const routeName = route.name.startsWith(INTERNAL_ROUTE_PREFIX)
75
+ ? ""
76
+ : route.name;
77
+ const rawText = h1Text || document.title || routeName || globalThis.location.pathname;
78
+ return `${prefix}${rawText}`;
79
+ }
80
+ function manageFocus() {
81
+ const h1 = document.querySelector("h1");
82
+ if (!h1) {
83
+ return;
84
+ }
85
+ if (!h1.hasAttribute("tabindex")) {
86
+ h1.setAttribute("tabindex", "-1");
87
+ }
88
+ h1.focus({ preventScroll: true });
89
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@real-router/svelte",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "type": "module",
5
5
  "description": "Svelte 5 integration for Real-Router",
6
6
  "svelte": "./dist/index.js",
@@ -44,10 +44,9 @@
44
44
  "license": "MIT",
45
45
  "sideEffects": false,
46
46
  "dependencies": {
47
- "@real-router/core": "^0.44.0",
48
- "@real-router/route-utils": "^0.1.10",
49
- "@real-router/sources": "^0.4.0",
50
- "dom-utils": "^0.2.6"
47
+ "@real-router/core": "^0.45.0",
48
+ "@real-router/route-utils": "^0.1.11",
49
+ "@real-router/sources": "^0.4.1"
51
50
  },
52
51
  "devDependencies": {
53
52
  "@sveltejs/package": "2.5.7",
@@ -59,7 +58,8 @@
59
58
  "svelte": "5.54.0",
60
59
  "svelte-check": "4.4.5",
61
60
  "svelte-eslint-parser": "1.6.0",
62
- "@real-router/browser-plugin": "^0.11.3"
61
+ "@real-router/browser-plugin": "^0.11.4",
62
+ "dom-utils": "^0.2.7"
63
63
  },
64
64
  "peerDependencies": {
65
65
  "svelte": ">=5.7.0"