B2B-88: add starter kit structure and elements
This commit is contained in:
7
packages/ui/src/lib/utils/cn.ts
Normal file
7
packages/ui/src/lib/utils/cn.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { clsx } from 'clsx';
|
||||
import type { ClassValue } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
2
packages/ui/src/lib/utils/index.ts
Normal file
2
packages/ui/src/lib/utils/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './cn';
|
||||
export * from './is-route-active';
|
||||
108
packages/ui/src/lib/utils/is-route-active.ts
Normal file
108
packages/ui/src/lib/utils/is-route-active.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user