@bsuite/nav-core 0.1.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.
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/types.d.ts +70 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +22 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +42 -0
- package/package.json +32 -0
- package/src/tokens/corporate-sidebar.css +28 -0
- package/src/tokens/d2c-sidebar.css +37 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,aAAa,EACb,OAAO,EACP,YAAY,EACZ,UAAU,EACV,SAAS,GACV,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { isActivePath, isSectionActive } from './utils';
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon component type — compatible with Lucide, HeroIcons, and any
|
|
3
|
+
* React component that renders an SVG.
|
|
4
|
+
*
|
|
5
|
+
* Uses a **structural type** instead of importing from `react` to
|
|
6
|
+
* prevent type conflicts when consumed by projects using different
|
|
7
|
+
* React versions (18 vs 19) in a pnpm workspace. The `@types/react`
|
|
8
|
+
* ReactNode definition changed between versions, making any transitive
|
|
9
|
+
* React type reference fail across version boundaries.
|
|
10
|
+
*
|
|
11
|
+
* The structural signature `(props) => unknown` is wide enough that
|
|
12
|
+
* Lucide, HeroIcons, and plain `(props) => JSX.Element` all satisfy
|
|
13
|
+
* it, while still enforcing the expected prop shape.
|
|
14
|
+
*/
|
|
15
|
+
export type IconComponent = (props: {
|
|
16
|
+
className?: string;
|
|
17
|
+
size?: number | string;
|
|
18
|
+
}) => unknown;
|
|
19
|
+
/** A single navigation item (leaf node). */
|
|
20
|
+
export interface NavItem {
|
|
21
|
+
/** Display label */
|
|
22
|
+
label: string;
|
|
23
|
+
/** URL path (or view identifier for state-based routing) */
|
|
24
|
+
href: string;
|
|
25
|
+
/** Optional icon component */
|
|
26
|
+
icon?: IconComponent;
|
|
27
|
+
/** Optional badge text or count shown beside the label */
|
|
28
|
+
badge?: string | number;
|
|
29
|
+
/** If true, opens in a new tab (for cross-app links) */
|
|
30
|
+
external?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A group of nav items. Groups within a section are visually
|
|
34
|
+
* separated by dividers.
|
|
35
|
+
*/
|
|
36
|
+
export type NavItemGroup = NavItem[];
|
|
37
|
+
/** A collapsible navigation section containing grouped items. */
|
|
38
|
+
export interface NavSection {
|
|
39
|
+
/** Section heading label */
|
|
40
|
+
label: string;
|
|
41
|
+
/** Section icon (shown in collapsed mode) */
|
|
42
|
+
icon: IconComponent;
|
|
43
|
+
/** Direct link when section itself is clickable */
|
|
44
|
+
href?: string;
|
|
45
|
+
/** Grouped sub-items — each group separated by a divider */
|
|
46
|
+
groups?: NavItemGroup[];
|
|
47
|
+
/** Whether this section starts expanded (default: false) */
|
|
48
|
+
defaultOpen?: boolean;
|
|
49
|
+
}
|
|
50
|
+
/** Top-level navigation configuration for a BSuite application. */
|
|
51
|
+
export interface NavConfig {
|
|
52
|
+
/** Application identity */
|
|
53
|
+
app: {
|
|
54
|
+
/** Full application name */
|
|
55
|
+
name: string;
|
|
56
|
+
/** Short name / abbreviation */
|
|
57
|
+
shortName: string;
|
|
58
|
+
/** Path to logo image */
|
|
59
|
+
logoSrc?: string;
|
|
60
|
+
/** Link target when clicking the logo */
|
|
61
|
+
homeHref: string;
|
|
62
|
+
};
|
|
63
|
+
/** Main navigation sections */
|
|
64
|
+
sections: NavSection[];
|
|
65
|
+
/** Fixed footer items (settings, profile, etc.) */
|
|
66
|
+
footer?: NavItem[];
|
|
67
|
+
/** Cross-app links shown at the bottom of the sidebar */
|
|
68
|
+
suiteLinks?: NavItem[];
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,KAAK,OAAO,CAAC;AAE/F,4CAA4C;AAC5C,MAAM,WAAW,OAAO;IACtB,oBAAoB;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,EAAE,CAAC;AAErC,iEAAiE;AACjE,MAAM,WAAW,UAAU;IACzB,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,IAAI,EAAE,aAAa,CAAC;IACpB,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;IACxB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,mEAAmE;AACnE,MAAM,WAAW,SAAS;IACxB,2BAA2B;IAC3B,GAAG,EAAE;QACH,4BAA4B;QAC5B,IAAI,EAAE,MAAM,CAAC;QACb,gCAAgC;QAChC,SAAS,EAAE,MAAM,CAAC;QAClB,yBAAyB;QACzB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,yCAAyC;QACzC,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,+BAA+B;IAC/B,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,mDAAmD;IACnD,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IACnB,yDAAyD;IACzD,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC;CACxB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { NavSection } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Check whether `currentPath` matches `itemHref`.
|
|
4
|
+
*
|
|
5
|
+
* Returns true for an exact match **or** a prefix match where the
|
|
6
|
+
* next character is `/` (so `/apprentices` matches
|
|
7
|
+
* `/apprentices/create` but not `/apprentices-old`).
|
|
8
|
+
*
|
|
9
|
+
* Guards:
|
|
10
|
+
* - Empty `itemHref` always returns false.
|
|
11
|
+
* - Root path (`/`) only matches exactly — it does not prefix-match
|
|
12
|
+
* every route.
|
|
13
|
+
* - Trailing slashes are normalised before comparison.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isActivePath(currentPath: string, itemHref: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Check whether any item inside a section is currently active.
|
|
18
|
+
* Useful for auto-expanding a section when one of its children is
|
|
19
|
+
* the current page.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isSectionActive(currentPath: string, section: Pick<NavSection, 'href' | 'groups'>): boolean;
|
|
22
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAO1C;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAOT;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,CAAC,GAC3C,OAAO,CAUT"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Strip a single trailing slash (unless the path is just `/`). */
|
|
2
|
+
function normalize(p) {
|
|
3
|
+
return p.length > 1 && p.endsWith('/') ? p.slice(0, -1) : p;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Check whether `currentPath` matches `itemHref`.
|
|
7
|
+
*
|
|
8
|
+
* Returns true for an exact match **or** a prefix match where the
|
|
9
|
+
* next character is `/` (so `/apprentices` matches
|
|
10
|
+
* `/apprentices/create` but not `/apprentices-old`).
|
|
11
|
+
*
|
|
12
|
+
* Guards:
|
|
13
|
+
* - Empty `itemHref` always returns false.
|
|
14
|
+
* - Root path (`/`) only matches exactly — it does not prefix-match
|
|
15
|
+
* every route.
|
|
16
|
+
* - Trailing slashes are normalised before comparison.
|
|
17
|
+
*/
|
|
18
|
+
export function isActivePath(currentPath, itemHref) {
|
|
19
|
+
if (!itemHref)
|
|
20
|
+
return false;
|
|
21
|
+
const cp = normalize(currentPath);
|
|
22
|
+
const ih = normalize(itemHref);
|
|
23
|
+
if (ih === '/')
|
|
24
|
+
return cp === '/';
|
|
25
|
+
if (cp === ih)
|
|
26
|
+
return true;
|
|
27
|
+
return cp.startsWith(ih + '/');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Check whether any item inside a section is currently active.
|
|
31
|
+
* Useful for auto-expanding a section when one of its children is
|
|
32
|
+
* the current page.
|
|
33
|
+
*/
|
|
34
|
+
export function isSectionActive(currentPath, section) {
|
|
35
|
+
if (section.href && isActivePath(currentPath, section.href)) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
if (section.groups) {
|
|
39
|
+
return section.groups.some((group) => group.some((item) => isActivePath(currentPath, item.href)));
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bsuite/nav-core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/GaryOcean428/bsuite.git",
|
|
9
|
+
"directory": "packages/nav-core"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"src/tokens"
|
|
14
|
+
],
|
|
15
|
+
"main": "dist/index.js",
|
|
16
|
+
"types": "dist/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": { "import": "./dist/index.js", "types": "./dist/index.d.ts" },
|
|
19
|
+
"./types": { "import": "./dist/types.js", "types": "./dist/types.d.ts" },
|
|
20
|
+
"./tokens/d2c-sidebar.css": "./src/tokens/d2c-sidebar.css",
|
|
21
|
+
"./tokens/corporate-sidebar.css": "./src/tokens/corporate-sidebar.css"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc -p tsconfig.build.json",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"test": "vitest run"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"typescript": "~5.7.3",
|
|
30
|
+
"vitest": "^3.2.4"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Corporate Branding — Sidebar Design Tokens
|
|
3
|
+
* ───────────────────────────────────────────
|
|
4
|
+
* Uses shadcn/ui's --sidebar-* CSS variable contract.
|
|
5
|
+
* Import this file in the braden.com.au project.
|
|
6
|
+
*
|
|
7
|
+
* Braden corporate palette:
|
|
8
|
+
* Red #ab233a (primary)
|
|
9
|
+
* Gold #cbb26a (accent)
|
|
10
|
+
* Navy #2c3e50 (text/headings)
|
|
11
|
+
*
|
|
12
|
+
* Values are HSL channel triplets (H S% L%) consumed by
|
|
13
|
+
* Tailwind's hsl() utility wrappers.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
:root {
|
|
17
|
+
--sidebar-background: 350 66% 38%; /* #811a2c dark braden red */
|
|
18
|
+
--sidebar-foreground: 0 0% 95%; /* #f2f2f2 near-white text */
|
|
19
|
+
--sidebar-primary: 44 49% 60%; /* #cbb26a braden gold */
|
|
20
|
+
--sidebar-primary-foreground: 350 66% 15%;/* #3d0d14 deep red text */
|
|
21
|
+
--sidebar-accent: 350 66% 32%; /* #6e1624 deeper red tint */
|
|
22
|
+
--sidebar-accent-foreground: 44 49% 75%; /* #ddd0a0 light gold */
|
|
23
|
+
--sidebar-border: 350 40% 28%; /* #5c1520 subtle red border */
|
|
24
|
+
--sidebar-ring: 44 49% 60%; /* #cbb26a gold ring */
|
|
25
|
+
--sidebar-muted-foreground: 0 0% 75%; /* #bfbfbf muted text */
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* No .dark variant: corporate sidebar is always red-branded */
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* D2C Neon Electric — Sidebar Design Tokens
|
|
3
|
+
* ──────────────────────────────────────────
|
|
4
|
+
* Uses shadcn/ui's --sidebar-* CSS variable contract.
|
|
5
|
+
* Import this file in any D2C-themed BSuite app:
|
|
6
|
+
* CRM7 · Conduit · R80.3 · business-suite-unified
|
|
7
|
+
*
|
|
8
|
+
* Values are HSL channel triplets (H S% L%) consumed by
|
|
9
|
+
* Tailwind's hsl() utility wrappers.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/* ── Light mode (default) ─────────────────────────────── */
|
|
13
|
+
:root,
|
|
14
|
+
.light {
|
|
15
|
+
--sidebar-background: 220 20% 98%; /* #fafbfd off-white */
|
|
16
|
+
--sidebar-foreground: 215 25% 27%; /* #334155 slate-700 */
|
|
17
|
+
--sidebar-primary: 217 91% 60%; /* #2563eb electric blue */
|
|
18
|
+
--sidebar-primary-foreground: 0 0% 100%; /* #ffffff white */
|
|
19
|
+
--sidebar-accent: 217 91% 95%; /* #eff6ff blue-50 tint */
|
|
20
|
+
--sidebar-accent-foreground: 217 91% 40%;/* #1e40af blue-800 */
|
|
21
|
+
--sidebar-border: 214 20% 92%; /* #e5e9f0 cool gray border */
|
|
22
|
+
--sidebar-ring: 217 91% 60%; /* #2563eb electric blue ring */
|
|
23
|
+
--sidebar-muted-foreground: 215 15% 55%; /* #7c8aa0 muted text */
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* ── Dark mode ────────────────────────────────────────── */
|
|
27
|
+
.dark {
|
|
28
|
+
--sidebar-background: 222 47% 7%; /* #0a0e1a deep navy */
|
|
29
|
+
--sidebar-foreground: 213 20% 75%; /* #b0bec5 light slate */
|
|
30
|
+
--sidebar-primary: 178 100% 40%; /* #00cec9 electric cyan */
|
|
31
|
+
--sidebar-primary-foreground: 222 47% 7%;/* #0a0e1a deep navy */
|
|
32
|
+
--sidebar-accent: 174 100% 8%; /* #001f1e cyan tint on dark */
|
|
33
|
+
--sidebar-accent-foreground: 174 100% 60%;/* #33e8e4 bright cyan */
|
|
34
|
+
--sidebar-border: 220 30% 14%; /* #1a2332 subtle border */
|
|
35
|
+
--sidebar-ring: 178 100% 40%; /* #00cec9 cyan ring */
|
|
36
|
+
--sidebar-muted-foreground: 215 15% 45%; /* #64748b muted text */
|
|
37
|
+
}
|