@hitachivantara/app-shell-navigation 2.1.12 → 2.1.13

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,27 +1,22 @@
1
1
  import { useMemo } from "react";
2
2
  import { useHvMenuItems } from "@hitachivantara/app-shell-shared";
3
- const useHvCurrentNavigationPath = () => {
4
- const { items, selectedMenuItemId } = useHvMenuItems();
5
- return useMemo(() => {
6
- let currentItems = items;
7
- if (!selectedMenuItemId) {
8
- return [];
9
- }
10
- const paths = [];
11
- const selectedPathIds = selectedMenuItemId.split("-");
12
- selectedPathIds.forEach((item) => {
13
- const currentItem = currentItems[Number(item)];
14
- paths.push({
15
- label: currentItem.label,
16
- path: currentItem.data ? void 0 : currentItem.href
17
- });
18
- if (currentItem.data) {
19
- currentItems = currentItem.data;
20
- }
21
- });
22
- return paths;
23
- }, [items, selectedMenuItemId]);
24
- };
25
- export {
26
- useHvCurrentNavigationPath
3
+ //#region src/hooks/useCurrentNavigationPath.ts
4
+ var useHvCurrentNavigationPath = () => {
5
+ const { items, selectedMenuItemId } = useHvMenuItems();
6
+ return useMemo(() => {
7
+ let currentItems = items;
8
+ if (!selectedMenuItemId) return [];
9
+ const paths = [];
10
+ selectedMenuItemId.split("-").forEach((item) => {
11
+ const currentItem = currentItems[Number(item)];
12
+ paths.push({
13
+ label: currentItem.label,
14
+ path: currentItem.data ? void 0 : currentItem.href
15
+ });
16
+ if (currentItem.data) currentItems = currentItem.data;
17
+ });
18
+ return paths;
19
+ }, [items, selectedMenuItemId]);
27
20
  };
21
+ //#endregion
22
+ export { useHvCurrentNavigationPath };
@@ -1,54 +1,51 @@
1
1
  import { useMemo } from "react";
2
- import { useLocation, matchRoutes } from "react-router-dom";
3
2
  import { useHvAppShellModel } from "@hitachivantara/app-shell-shared";
3
+ import { matchRoutes, useLocation } from "react-router-dom";
4
+ //#region src/hooks/useLocation.ts
4
5
  function indexViews(views) {
5
- return views.map((view) => {
6
- const path = view.route.slice(1);
7
- const isIndex = path === "" && (view.views == null || view.views.length === 0);
8
- if (isIndex) {
9
- return {
10
- path,
11
- view
12
- };
13
- }
14
- return {
15
- path,
16
- view,
17
- children: view.views ? indexViews(view.views) : void 0
18
- };
19
- });
6
+ return views.map((view) => {
7
+ const path = view.route.slice(1);
8
+ if (path === "" && (view.views == null || view.views.length === 0)) return {
9
+ path,
10
+ view
11
+ };
12
+ return {
13
+ path,
14
+ view,
15
+ children: view.views ? indexViews(view.views) : void 0
16
+ };
17
+ });
20
18
  }
21
- class LocationWithViews {
22
- state;
23
- key;
24
- pathname;
25
- search;
26
- hash;
27
- #configViews;
28
- matches = null;
29
- constructor(location, views) {
30
- this.state = location.state;
31
- this.key = location.key;
32
- this.pathname = location.pathname;
33
- this.search = location.search;
34
- this.hash = location.hash;
35
- this.#configViews = views ?? [];
36
- }
37
- get views() {
38
- if (this.matches == null) {
39
- const indexedViews = indexViews(this.#configViews);
40
- this.matches = matchRoutes(indexedViews, this)?.map((match) => match.route.view) ?? [];
41
- }
42
- return this.matches;
43
- }
44
- }
45
- const useHvLocation = () => {
46
- const location = useLocation();
47
- const { mainPanel } = useHvAppShellModel();
48
- return useMemo(() => {
49
- return new LocationWithViews(location, mainPanel?.views);
50
- }, [location, mainPanel?.views]);
19
+ var LocationWithViews = class {
20
+ state;
21
+ key;
22
+ pathname;
23
+ search;
24
+ hash;
25
+ #configViews;
26
+ matches = null;
27
+ constructor(location, views) {
28
+ this.state = location.state;
29
+ this.key = location.key;
30
+ this.pathname = location.pathname;
31
+ this.search = location.search;
32
+ this.hash = location.hash;
33
+ this.#configViews = views ?? [];
34
+ }
35
+ get views() {
36
+ if (this.matches == null) {
37
+ const indexedViews = indexViews(this.#configViews);
38
+ this.matches = matchRoutes(indexedViews, this)?.map((match) => match.route.view) ?? [];
39
+ }
40
+ return this.matches;
41
+ }
51
42
  };
52
- export {
53
- useHvLocation
43
+ var useHvLocation = () => {
44
+ const location = useLocation();
45
+ const { mainPanel } = useHvAppShellModel();
46
+ return useMemo(() => {
47
+ return new LocationWithViews(location, mainPanel?.views);
48
+ }, [location, mainPanel?.views]);
54
49
  };
50
+ //#endregion
51
+ export { useHvLocation };
@@ -1,130 +1,114 @@
1
- import { useMemo, useContext, useRef, useCallback } from "react";
2
- import { useNavigate } from "react-router-dom";
3
- import { useHvAppShellModel, HvAppShellViewContext } from "@hitachivantara/app-shell-shared";
4
1
  import compileHref from "../utils/navigationUtil.js";
5
2
  import { useHvLocation } from "./useLocation.js";
6
- const isViewDestination = (to) => {
7
- return to.viewBundle !== void 0;
3
+ import { useCallback, useContext, useMemo, useRef } from "react";
4
+ import { HvAppShellViewContext, useHvAppShellModel } from "@hitachivantara/app-shell-shared";
5
+ import { useNavigate } from "react-router-dom";
6
+ //#region src/hooks/useNavigation.ts
7
+ var isViewDestination = (to) => {
8
+ return to.viewBundle !== void 0;
8
9
  };
9
- const isPathDestination = (to) => {
10
- return typeof to !== "string" && !isViewDestination(to);
10
+ var isPathDestination = (to) => {
11
+ return typeof to !== "string" && !isViewDestination(to);
11
12
  };
12
13
  function getActiveViewRoute(activeViews, depth) {
13
- return `/${activeViews.map((view) => view.route.slice(1)).slice(0, activeViews.length).filter((route) => route !== "").join("/")}`;
14
+ return `/${activeViews.map((view) => view.route.slice(1)).slice(0, depth ?? activeViews.length).filter((route) => route !== "").join("/")}`;
14
15
  }
15
16
  function isSameBundle(v, bundle, appId) {
16
- return v.bundle === `${bundle}` || v.bundle === `${bundle}.js` || v.bundle === `${appId}/${bundle}.js` || v.bundle === `${appId}/${bundle}`;
17
+ return v.bundle === `${bundle}` || v.bundle === `${bundle}.js` || v.bundle === `${appId}/${bundle}.js` || v.bundle === `${appId}/${bundle}`;
17
18
  }
18
- const useHvNavigation = () => {
19
- const { mainPanel } = useHvAppShellModel();
20
- const flattenViews = useMemo(() => {
21
- const flatten = (views, base = "") => {
22
- return views.reduce((acc, view) => {
23
- const route = `${base}${view.route}`;
24
- acc.push({ ...view, route });
25
- if (view.views != null) {
26
- acc.push(...flatten(view.views, route));
27
- }
28
- return acc;
29
- }, []);
30
- };
31
- return flatten(mainPanel?.views ?? []);
32
- }, [mainPanel?.views]);
33
- const viewContext = useContext(HvAppShellViewContext);
34
- const navigateReactRouter = useNavigate();
35
- const location = useHvLocation();
36
- const locationRef = useRef(location);
37
- locationRef.current = location;
38
- const getViewRoute = useCallback(
39
- (viewBundleDir, { mode = "auto" } = {}) => {
40
- let viewBundle;
41
- let pathParams;
42
- let search;
43
- let hash;
44
- if (isViewDestination(viewBundleDir)) {
45
- ({ viewBundle, pathParams, search, hash } = viewBundleDir);
46
- } else {
47
- viewBundle = viewBundleDir;
48
- }
49
- const bundleWithReplacedPlaceholders = viewBundle.replace(/\$/, "_");
50
- let appId;
51
- let bundle;
52
- if (bundleWithReplacedPlaceholders.startsWith("/")) {
53
- appId = viewContext?.id;
54
- bundle = bundleWithReplacedPlaceholders.slice(1);
55
- } else {
56
- bundle = bundleWithReplacedPlaceholders;
57
- }
58
- let activeViews = [];
59
- if (mode !== "top") {
60
- activeViews = locationRef.current.views;
61
- }
62
- let route;
63
- const matchingViews = flattenViews.filter((v) => isSameBundle(v, bundle, appId)).toSorted((a, b) => {
64
- const aSlashCount = (a.route.match(/\//g) ?? []).length;
65
- const bSlashCount = (b.route.match(/\//g) ?? []).length;
66
- return aSlashCount - bSlashCount;
67
- });
68
- if (matchingViews.length > 0) {
69
- if (mode === "top" || matchingViews.length === 1) {
70
- route = matchingViews[0].route;
71
- } else if (mode === "auto") {
72
- const activeViewRoute = getActiveViewRoute(activeViews);
73
- let path = `${activeViewRoute}/`;
74
- while (route == null) {
75
- const innerPath = path;
76
- route = matchingViews.find(
77
- (v) => v.route.startsWith(innerPath)
78
- )?.route;
79
- path = path.replace(/\/[^/]*\/?$/, "/");
80
- if (path === "") {
81
- break;
82
- }
83
- }
84
- }
85
- }
86
- return route ? `${compileHref(route, pathParams)}${search ?? ""}${hash ?? ""}` : void 0;
87
- },
88
- [flattenViews, viewContext?.id]
89
- );
90
- const navigate = useCallback(
91
- (to, options) => {
92
- let path;
93
- if (isPathDestination(to)) {
94
- path = to;
95
- } else {
96
- const route = getViewRoute(to, { mode: options?.mode });
97
- if (route == null) {
98
- if (typeof to === "string") {
99
- path = to;
100
- } else {
101
- console.warn(
102
- `Navigate request to a non existing path [${to.viewBundle}]. Skipping`
103
- );
104
- return;
105
- }
106
- } else {
107
- path = route;
108
- }
109
- }
110
- let navigateOptions;
111
- if (options != null) {
112
- if (options.mode == null) {
113
- navigateOptions = options;
114
- } else {
115
- navigateOptions = { ...options };
116
- delete navigateOptions.mode;
117
- if (Object.keys(navigateOptions).length === 0) {
118
- navigateOptions = void 0;
119
- }
120
- }
121
- }
122
- navigateReactRouter(path, navigateOptions);
123
- },
124
- [getViewRoute, navigateReactRouter]
125
- );
126
- return { getViewRoute, navigate };
127
- };
128
- export {
129
- useHvNavigation
19
+ var useHvNavigation = () => {
20
+ const { mainPanel } = useHvAppShellModel();
21
+ const flattenViews = useMemo(() => {
22
+ const flatten = (views, base = "") => {
23
+ return views.reduce((acc, view) => {
24
+ const route = `${base}${view.route}`;
25
+ acc.push({
26
+ ...view,
27
+ route
28
+ });
29
+ if (view.views != null) acc.push(...flatten(view.views, route));
30
+ return acc;
31
+ }, []);
32
+ };
33
+ return flatten(mainPanel?.views ?? []);
34
+ }, [mainPanel?.views]);
35
+ const viewContext = useContext(HvAppShellViewContext);
36
+ const navigateReactRouter = useNavigate();
37
+ const location = useHvLocation();
38
+ const locationRef = useRef(location);
39
+ locationRef.current = location;
40
+ /**
41
+ * Utility to search for the route of a View on the App Shell configuration.
42
+ *
43
+ * The search can be performed in different modes:
44
+ * - "auto": Searches within views whose route is a subpath of the current active view, progressively going up path segments until a match is found.
45
+ * - "top": Finds the view closest to the root, i.e. with the least number of path segments.
46
+ *
47
+ * If multiple views match the search criteria, the function returns the one that appears first in the App Shell configuration.
48
+ *
49
+ * @param viewBundleDir The application view bundle directory name and optional route parameters.
50
+ * @param mode The search mode to use. Defaults to "auto".
51
+ *
52
+ * @returns The compiled route or undefined if none was found.
53
+ */
54
+ const getViewRoute = useCallback((viewBundleDir, { mode = "auto" } = {}) => {
55
+ let viewBundle;
56
+ let pathParams;
57
+ let search;
58
+ let hash;
59
+ if (isViewDestination(viewBundleDir)) ({viewBundle, pathParams, search, hash} = viewBundleDir);
60
+ else viewBundle = viewBundleDir;
61
+ const bundleWithReplacedPlaceholders = viewBundle.replace(/\$/, "_");
62
+ let appId;
63
+ let bundle;
64
+ if (bundleWithReplacedPlaceholders.startsWith("/")) {
65
+ appId = viewContext?.id;
66
+ bundle = bundleWithReplacedPlaceholders.slice(1);
67
+ } else bundle = bundleWithReplacedPlaceholders;
68
+ let activeViews = [];
69
+ if (mode !== "top") activeViews = locationRef.current.views;
70
+ let route;
71
+ const matchingViews = flattenViews.filter((v) => isSameBundle(v, bundle, appId)).toSorted((a, b) => {
72
+ return (a.route.match(/\//g) ?? []).length - (b.route.match(/\//g) ?? []).length;
73
+ });
74
+ if (matchingViews.length > 0) {
75
+ if (mode === "top" || matchingViews.length === 1) route = matchingViews[0].route;
76
+ else if (mode === "auto") {
77
+ let path = `${getActiveViewRoute(activeViews)}/`;
78
+ while (route == null) {
79
+ const innerPath = path;
80
+ route = matchingViews.find((v) => v.route.startsWith(innerPath))?.route;
81
+ path = path.replace(/\/[^/]*\/?$/, "/");
82
+ if (path === "") break;
83
+ }
84
+ }
85
+ }
86
+ return route ? `${compileHref(route, pathParams)}${search ?? ""}${hash ?? ""}` : void 0;
87
+ }, [flattenViews, viewContext?.id]);
88
+ return {
89
+ getViewRoute,
90
+ navigate: useCallback((to, options) => {
91
+ let path;
92
+ if (isPathDestination(to)) path = to;
93
+ else {
94
+ const route = getViewRoute(to, { mode: options?.mode });
95
+ if (route == null) if (typeof to === "string") path = to;
96
+ else {
97
+ console.warn(`Navigate request to a non existing path [${to.viewBundle}]. Skipping`);
98
+ return;
99
+ }
100
+ else path = route;
101
+ }
102
+ let navigateOptions;
103
+ if (options != null) if (options.mode == null) navigateOptions = options;
104
+ else {
105
+ navigateOptions = { ...options };
106
+ delete navigateOptions.mode;
107
+ if (Object.keys(navigateOptions).length === 0) navigateOptions = void 0;
108
+ }
109
+ navigateReactRouter(path, navigateOptions);
110
+ }, [getViewRoute, navigateReactRouter])
111
+ };
130
112
  };
113
+ //#endregion
114
+ export { useHvNavigation };
package/dist/index.js CHANGED
@@ -1,8 +1,4 @@
1
1
  import { useHvCurrentNavigationPath } from "./hooks/useCurrentNavigationPath.js";
2
- import { useHvNavigation } from "./hooks/useNavigation.js";
3
2
  import { useHvLocation } from "./hooks/useLocation.js";
4
- export {
5
- useHvCurrentNavigationPath,
6
- useHvLocation,
7
- useHvNavigation
8
- };
3
+ import { useHvNavigation } from "./hooks/useNavigation.js";
4
+ export { useHvCurrentNavigationPath, useHvLocation, useHvNavigation };
@@ -1,11 +1,20 @@
1
1
  import { compile } from "path-to-regexp";
2
- const compileHref = (href, hrefOptions) => {
3
- if (!hrefOptions) {
4
- return href;
5
- }
6
- const compiler = compile(href);
7
- return compiler(hrefOptions);
8
- };
9
- export {
10
- compileHref as default
2
+ //#region src/utils/navigationUtil.ts
3
+ /**
4
+ * Utility that replaces the href placeholders with the href options provided.
5
+ * The href parameter must not contain 'search' nor 'hash' parts.
6
+ * @example
7
+ * // returns '/home/2'
8
+ * // href: '/home/:id', hrefOptions: '{id: 2}'
9
+ *
10
+ * @param href The string to be compiled.
11
+ * @param hrefOptions The options to replace the placeholders on the href.
12
+ *
13
+ * @returns The compiled href
14
+ */
15
+ var compileHref = (href, hrefOptions) => {
16
+ if (!hrefOptions) return href;
17
+ return compile(href)(hrefOptions);
11
18
  };
19
+ //#endregion
20
+ export { compileHref as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hitachivantara/app-shell-navigation",
3
- "version": "2.1.12",
3
+ "version": "2.1.13",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "author": "Hitachi Vantara UI Kit Team",
@@ -15,7 +15,7 @@
15
15
  },
16
16
  "bugs": "https://github.com/pentaho/hv-uikit-react/issues",
17
17
  "dependencies": {
18
- "@hitachivantara/app-shell-shared": "^2.3.1",
18
+ "@hitachivantara/app-shell-shared": "^2.3.2",
19
19
  "path-to-regexp": "^8.1.0"
20
20
  },
21
21
  "peerDependencies": {
@@ -29,7 +29,7 @@
29
29
  "access": "public",
30
30
  "directory": "package"
31
31
  },
32
- "gitHead": "333e132a9823018d508ebbe71c81d0b467f9008d",
32
+ "gitHead": "65c4f4394e8f8c7cccb58203e1c08c6832434638",
33
33
  "exports": {
34
34
  ".": {
35
35
  "types": "./dist/index.d.ts",