@okyrychenko-dev/react-action-guard-router 0.2.0

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.
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Core types for navigation blocking functionality
3
+ */
4
+ /**
5
+ * Base configuration for navigation blocking
6
+ */
7
+ interface BaseNavigationBlockerOptions {
8
+ /**
9
+ * Condition to determine if blocking should be active
10
+ * Can be a boolean value or a function that returns a boolean
11
+ */
12
+ when?: boolean | (() => boolean);
13
+ /**
14
+ * Scope(s) from react-action-guard to check for blocking
15
+ * When specified, navigation is blocked if any of these scopes are currently blocked
16
+ */
17
+ scope?: string | Array<string>;
18
+ /**
19
+ * Message to show in confirmation dialog
20
+ * @default "Are you sure you want to leave? Changes you made may not be saved."
21
+ */
22
+ message?: string;
23
+ /**
24
+ * Callback when navigation is blocked
25
+ */
26
+ onBlock?: () => void;
27
+ /**
28
+ * Callback when navigation is allowed
29
+ */
30
+ onAllow?: () => void;
31
+ /**
32
+ * Whether to also block browser unload events (tab close, refresh)
33
+ * @default true
34
+ */
35
+ blockBrowserUnload?: boolean;
36
+ /**
37
+ * Custom confirmation handler for showing your own dialog UI.
38
+ *
39
+ * If provided, this will be called instead of window.confirm() to allow
40
+ * custom dialog components. Async handlers are supported, but note:
41
+ * - Next.js Pages Router re-attempts navigation after resolve.
42
+ * - Router adapters may ignore stale resolves if a newer confirm starts.
43
+ *
44
+ * @param message - The confirmation message to display
45
+ * @returns boolean | Promise<boolean> - true to allow navigation, false to block
46
+ *
47
+ * @example
48
+ * ```tsx
49
+ * // With custom dialog component
50
+ * const [dialogState, setDialogState] = useState(null);
51
+ *
52
+ * onConfirm: (message) => {
53
+ * return new Promise((resolve) => {
54
+ * setDialogState({ message, resolve });
55
+ * });
56
+ * }
57
+ *
58
+ * // In JSX:
59
+ * {dialogState && (
60
+ * <CustomDialog
61
+ * message={dialogState.message}
62
+ * onConfirm={() => {
63
+ * dialogState.resolve(true);
64
+ * setDialogState(null);
65
+ * }}
66
+ * onCancel={() => {
67
+ * dialogState.resolve(false);
68
+ * setDialogState(null);
69
+ * }}
70
+ * />
71
+ * )}
72
+ * ```
73
+ */
74
+ onConfirm?: (message: string) => boolean | PromiseLike<boolean>;
75
+ }
76
+ /**
77
+ * Configuration for browser beforeunload event
78
+ */
79
+ interface BeforeUnloadOptions {
80
+ /**
81
+ * Condition to determine if blocking should be active
82
+ */
83
+ when: boolean | (() => boolean);
84
+ /**
85
+ * Custom message (may be ignored by modern browsers)
86
+ * @default "Changes you made may not be saved."
87
+ */
88
+ message?: string;
89
+ }
90
+ /**
91
+ * Return type for navigation blocker hooks
92
+ */
93
+ interface NavigationBlockerReturn {
94
+ /**
95
+ * Whether the blocking condition is currently active.
96
+ */
97
+ isBlocking: boolean;
98
+ /**
99
+ * Whether navigation is currently being intercepted.
100
+ * Only available for adapters that can observe active interception state.
101
+ */
102
+ isIntercepting?: boolean;
103
+ }
104
+
105
+ export type { BaseNavigationBlockerOptions as B, NavigationBlockerReturn as N, BeforeUnloadOptions as a };
package/package.json ADDED
@@ -0,0 +1,139 @@
1
+ {
2
+ "name": "@okyrychenko-dev/react-action-guard-router",
3
+ "version": "0.2.0",
4
+ "type": "module",
5
+ "description": "Navigation blocking adapters for React Action Guard across React Router, TanStack Router, and Next.js",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./react-router": {
16
+ "types": "./dist/react-router/index.d.ts",
17
+ "import": "./dist/react-router/index.js",
18
+ "require": "./dist/react-router/index.cjs"
19
+ },
20
+ "./tanstack-router": {
21
+ "types": "./dist/tanstack-router/index.d.ts",
22
+ "import": "./dist/tanstack-router/index.js",
23
+ "require": "./dist/tanstack-router/index.cjs"
24
+ },
25
+ "./nextjs": {
26
+ "types": "./dist/nextjs/index.d.ts",
27
+ "import": "./dist/nextjs/index.js",
28
+ "require": "./dist/nextjs/index.cjs"
29
+ }
30
+ },
31
+ "sideEffects": false,
32
+ "files": [
33
+ "dist",
34
+ "README.md",
35
+ "LICENSE"
36
+ ],
37
+ "scripts": {
38
+ "build": "tsup",
39
+ "dev": "tsup --watch",
40
+ "clean": "rm -rf dist",
41
+ "prepublishOnly": "npm run clean && npm run build",
42
+ "typecheck": "tsc --noEmit",
43
+ "lint": "eslint src --ext .ts,.tsx",
44
+ "lint:fix": "eslint src --ext .ts,.tsx --fix",
45
+ "format": "npm run lint:fix && prettier --write \"src/**/*.{ts,tsx}\"",
46
+ "format:check": "npm run lint && prettier --check \"src/**/*.{ts,tsx}\"",
47
+ "test": "vitest",
48
+ "test:ui": "vitest --ui",
49
+ "test:run": "vitest run",
50
+ "test:coverage": "vitest run --coverage",
51
+ "prepare": "husky",
52
+ "storybook": "storybook dev -p 6007",
53
+ "build-storybook": "storybook build"
54
+ },
55
+ "keywords": [
56
+ "react",
57
+ "router",
58
+ "navigation",
59
+ "ui-blocking",
60
+ "react-router",
61
+ "tanstack-router",
62
+ "nextjs",
63
+ "navigation-blocking",
64
+ "route-guard",
65
+ "unsaved-changes"
66
+ ],
67
+ "author": "Oleksii Kyrychenko alexey.kirichenko@gmail.com",
68
+ "license": "MIT",
69
+ "repository": {
70
+ "type": "git",
71
+ "url": "https://github.com/okyrychenko-dev/react-action-guard-router"
72
+ },
73
+ "bugs": {
74
+ "url": "https://github.com/okyrychenko-dev/react-action-guard-router/issues"
75
+ },
76
+ "homepage": "https://github.com/okyrychenko-dev/react-action-guard-router#readme",
77
+ "publishConfig": {
78
+ "access": "public"
79
+ },
80
+ "peerDependencies": {
81
+ "@okyrychenko-dev/react-action-guard": "^1.0.1",
82
+ "@tanstack/react-router": "^1.0.0",
83
+ "next": "^13.4.0 || ^14.0.0 || ^15.0.0",
84
+ "react": "^18.0.0 || ^19.0.0",
85
+ "react-router-dom": "^6.0.0 || ^7.0.0"
86
+ },
87
+ "peerDependenciesMeta": {
88
+ "react-router-dom": {
89
+ "optional": true
90
+ },
91
+ "@tanstack/react-router": {
92
+ "optional": true
93
+ },
94
+ "next": {
95
+ "optional": true
96
+ }
97
+ },
98
+ "overrides": {
99
+ "@okyrychenko-dev/react-zustand-toolkit": "^0.3.0"
100
+ },
101
+ "devDependencies": {
102
+ "@eslint/js": "^9.39.1",
103
+ "@okyrychenko-dev/react-action-guard": "^1.0.1",
104
+ "@storybook/addon-a11y": "10.0.8",
105
+ "@storybook/addon-docs": "10.0.8",
106
+ "@storybook/react-vite": "10.0.8",
107
+ "@tanstack/react-router": "^1.94.4",
108
+ "@testing-library/dom": "^10.4.1",
109
+ "@testing-library/jest-dom": "^6.9.1",
110
+ "@testing-library/react": "^16.3.0",
111
+ "@types/node": "^24.10.4",
112
+ "@types/react": "^19.2.5",
113
+ "@typescript-eslint/eslint-plugin": "^8.46.4",
114
+ "@typescript-eslint/parser": "^8.46.4",
115
+ "@vitest/coverage-v8": "^4.0.9",
116
+ "@vitest/ui": "^4.0.9",
117
+ "clsx": "^2.1.1",
118
+ "eslint": "^9.39.1",
119
+ "eslint-config-prettier": "^10.1.8",
120
+ "eslint-plugin-import": "^2.32.0",
121
+ "eslint-plugin-react": "^7.37.5",
122
+ "eslint-plugin-react-hooks": "^7.0.1",
123
+ "eslint-plugin-storybook": "10.0.8",
124
+ "happy-dom": "^20.0.10",
125
+ "husky": "^9.1.7",
126
+ "next": "^15.1.6",
127
+ "prettier": "^3.6.2",
128
+ "react": "^19.2.0",
129
+ "react-dom": "^19.2.0",
130
+ "react-router-dom": "^7.1.3",
131
+ "remark-gfm": "^4.0.1",
132
+ "storybook": "10.0.8",
133
+ "tsup": "^8.5.1",
134
+ "typescript": "^5.3.3",
135
+ "typescript-eslint": "^8.46.4",
136
+ "vitest": "^4.0.9",
137
+ "zustand": "^5.0.8"
138
+ }
139
+ }