109 lines
2.7 KiB
TypeScript
109 lines
2.7 KiB
TypeScript
const ROOT_PATH = '/';
|
|
|
|
/**
|
|
* @name isRouteActive
|
|
* @description A function to check if a route is active. This is used to
|
|
* @param end
|
|
* @param path
|
|
* @param currentPath
|
|
*/
|
|
export function isRouteActive(
|
|
path: string,
|
|
currentPath: string,
|
|
end?: boolean | ((path: string) => boolean),
|
|
) {
|
|
// if the path is the same as the current path, we return true
|
|
if (path === currentPath) {
|
|
return true;
|
|
}
|
|
|
|
// if the end prop is a function, we call it with the current path
|
|
if (typeof end === 'function') {
|
|
return !end(currentPath);
|
|
}
|
|
|
|
// otherwise - we use the evaluateIsRouteActive function
|
|
const defaultEnd = end ?? true;
|
|
const oneLevelDeep = 1;
|
|
const threeLevelsDeep = 3;
|
|
|
|
// how far down should segments be matched?
|
|
const depth = defaultEnd ? oneLevelDeep : threeLevelsDeep;
|
|
|
|
return checkIfRouteIsActive(path, currentPath, depth);
|
|
}
|
|
|
|
/**
|
|
* @name checkIfRouteIsActive
|
|
* @description A function to check if a route is active. This is used to
|
|
* highlight the active link in the navigation.
|
|
* @param targetLink - The link to check against
|
|
* @param currentRoute - the current route
|
|
* @param depth - how far down should segments be matched?
|
|
*/
|
|
export function checkIfRouteIsActive(
|
|
targetLink: string,
|
|
currentRoute: string,
|
|
depth = 1,
|
|
) {
|
|
// we remove any eventual query param from the route's URL
|
|
const currentRoutePath = currentRoute.split('?')[0] ?? '';
|
|
|
|
if (!isRoot(currentRoutePath) && isRoot(targetLink)) {
|
|
return false;
|
|
}
|
|
|
|
if (!currentRoutePath.includes(targetLink)) {
|
|
return false;
|
|
}
|
|
|
|
const isSameRoute = targetLink === currentRoutePath;
|
|
|
|
if (isSameRoute) {
|
|
return true;
|
|
}
|
|
|
|
return hasMatchingSegments(targetLink, currentRoutePath, depth);
|
|
}
|
|
|
|
function splitIntoSegments(href: string) {
|
|
return href.split('/').filter(Boolean);
|
|
}
|
|
|
|
function hasMatchingSegments(
|
|
targetLink: string,
|
|
currentRoute: string,
|
|
depth: number,
|
|
) {
|
|
const segments = splitIntoSegments(targetLink);
|
|
const matchingSegments = numberOfMatchingSegments(currentRoute, segments);
|
|
|
|
if (targetLink === currentRoute) {
|
|
return true;
|
|
}
|
|
|
|
// how far down should segments be matched?
|
|
// - if depth = 1 => only highlight the links of the immediate parent
|
|
// - if depth = 2 => for url = /account match /account/organization/members
|
|
return matchingSegments > segments.length - (depth - 1);
|
|
}
|
|
|
|
function numberOfMatchingSegments(href: string, segments: string[]) {
|
|
let count = 0;
|
|
|
|
for (const segment of splitIntoSegments(href)) {
|
|
// for as long as the segments match, keep counting + 1
|
|
if (segments.includes(segment)) {
|
|
count += 1;
|
|
} else {
|
|
return count;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
function isRoot(path: string) {
|
|
return path === ROOT_PATH;
|
|
}
|