B2B-88: add starter kit structure and elements

This commit is contained in:
devmc-ee
2025-06-08 16:18:30 +03:00
parent 657a36a298
commit e7b25600cb
1280 changed files with 77893 additions and 5688 deletions

View 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));
}

View File

@@ -0,0 +1,2 @@
export * from './cn';
export * from './is-route-active';

View 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;
}