@sondor-technology/sondor-components 1.0.1-develop.5 → 1.0.1-develop.6

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,7 +1,31 @@
1
- declare class AppRouter {
2
- constructor();
1
+ export interface RouteDefinition {
2
+ component: string;
3
+ [key: string]: unknown;
4
+ }
5
+ export type RouteConfig = Record<string, RouteDefinition>;
6
+ export interface RouteMatch {
7
+ path: string;
8
+ component: string;
9
+ params: Record<string, string>;
10
+ definition: RouteDefinition;
11
+ }
12
+ export declare class AppRouter {
13
+ private _routes;
14
+ private _initialized;
15
+ constructor(routes?: RouteConfig);
16
+ /**
17
+ * Register or update the route configuration for this application.
18
+ */
19
+ setRoutes(routes: RouteConfig): void;
20
+ /**
21
+ * Programmatically navigate to an internal path.
22
+ */
23
+ navigate(path: string): void;
24
+ /**
25
+ * Matches a URL path against the registered routes.
26
+ */
27
+ matchRoute(currentPath: string): RouteMatch | null;
3
28
  private _notify;
4
29
  }
5
30
  export declare const router: AppRouter;
6
- export {};
7
31
  //# sourceMappingURL=router.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../src/components/router/router.ts"],"names":[],"mappings":"AAAA,cAAM,SAAS;IACb,cAoCC;IAED,OAAO,CAAC,OAAO;CAKhB;AAED,eAAO,MAAM,MAAM,WAAkB,CAAC"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../src/components/router/router.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAE1D,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,UAAU,EAAE,eAAe,CAAC;CAC7B;AASD,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,YAAY,CAAS;IAE7B,YAAY,MAAM,CAAC,EAAE,WAAW,EA+B/B;IAED;;OAEG;IACI,SAAS,CAAC,MAAM,EAAE,WAAW,QAUnC;IAED;;OAEG;IACI,QAAQ,CAAC,IAAI,EAAE,MAAM,QAG3B;IAED;;OAEG;IACI,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CA+BxD;IAED,OAAO,CAAC,OAAO;CAahB;AAGD,eAAO,MAAM,MAAM,WAAkB,CAAC"}
@@ -1,37 +1,97 @@
1
- class AppRouter {
2
- constructor() {
1
+ function getRouteWeight(pattern) {
2
+ const segments = pattern.split('/').filter(Boolean);
3
+ return segments.reduce((weight, seg) => {
4
+ return weight + (seg.startsWith(':') ? 1 : 10);
5
+ }, 0);
6
+ }
7
+ export class AppRouter {
8
+ constructor(routes) {
9
+ this._routes = {};
10
+ this._initialized = false;
11
+ if (routes) {
12
+ this.setRoutes(routes);
13
+ }
3
14
  window.addEventListener('popstate', () => this._notify());
4
15
  document.addEventListener('click', (e) => {
5
- // 1. Ignore modified clicks (Ctrl+Click, etc)
6
16
  if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {
7
17
  return;
8
18
  }
9
- // 2. Safely traverse the path
10
19
  const path = e.composedPath();
11
20
  const link = path.find((el) => el && typeof el.hasAttribute === 'function' && el.hasAttribute('href'));
12
21
  if (!link)
13
22
  return;
14
23
  const href = link.getAttribute('href');
15
24
  const target = link.getAttribute('target');
16
- // 3. Ignore external links
17
25
  if (!href || href.startsWith('http') || href.startsWith('mailto:') || target === '_blank') {
18
26
  return;
19
27
  }
20
- // 4. Intercept internal links
21
28
  if (href.startsWith('/')) {
22
29
  e.preventDefault();
23
- window.history.pushState(null, '', href);
24
- this._notify();
30
+ this.navigate(href);
25
31
  }
26
32
  });
27
- // Broadcast initial route on load
28
- // Use setTimeout to ensure all Lit components have registered first
29
- setTimeout(() => this._notify(), 0);
33
+ }
34
+ /**
35
+ * Register or update the route configuration for this application.
36
+ */
37
+ setRoutes(routes) {
38
+ this._routes = routes;
39
+ // Trigger initial route resolution once routes are registered
40
+ if (!this._initialized) {
41
+ this._initialized = true;
42
+ setTimeout(() => this._notify(), 0);
43
+ }
44
+ else {
45
+ this._notify();
46
+ }
47
+ }
48
+ /**
49
+ * Programmatically navigate to an internal path.
50
+ */
51
+ navigate(path) {
52
+ window.history.pushState(null, '', path);
53
+ this._notify();
54
+ }
55
+ /**
56
+ * Matches a URL path against the registered routes.
57
+ */
58
+ matchRoute(currentPath) {
59
+ const sortedRoutes = Object.entries(this._routes).sort(([patternA], [patternB]) => getRouteWeight(patternB) - getRouteWeight(patternA));
60
+ for (const [pattern, routeDef] of sortedRoutes) {
61
+ const paramNames = [];
62
+ const regexPath = pattern.replace(/:([a-zA-Z0-9_]+)/g, (_, paramName) => {
63
+ paramNames.push(paramName);
64
+ return '([^/]+)';
65
+ });
66
+ const regex = new RegExp(`^${regexPath}$`);
67
+ const match = currentPath.match(regex);
68
+ if (match) {
69
+ const params = {};
70
+ paramNames.forEach((name, index) => {
71
+ params[name] = decodeURIComponent(match[index + 1]);
72
+ });
73
+ return {
74
+ path: currentPath,
75
+ component: routeDef.component,
76
+ params,
77
+ definition: routeDef
78
+ };
79
+ }
80
+ }
81
+ return null;
30
82
  }
31
83
  _notify() {
84
+ const currentPath = window.location.pathname;
85
+ const match = this.matchRoute(currentPath);
32
86
  window.dispatchEvent(new CustomEvent('route-changed', {
33
- detail: window.location.pathname
87
+ detail: {
88
+ path: currentPath,
89
+ component: match ? match.component : 'not-found-page',
90
+ params: match ? match.params : {},
91
+ definition: match ? match.definition : null
92
+ }
34
93
  }));
35
94
  }
36
95
  }
96
+ // Export a singleton instance for standard app-wide use
37
97
  export const router = new AppRouter();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sondor-technology/sondor-components",
3
- "version": "1.0.1-develop.5",
3
+ "version": "1.0.1-develop.6",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,14 +1,40 @@
1
- class AppRouter {
2
- constructor() {
1
+ export interface RouteDefinition {
2
+ component: string;
3
+ [key: string]: unknown; // Allows consuming apps to attach custom metadata (e.g., titles, auth roles)
4
+ }
5
+
6
+ export type RouteConfig = Record<string, RouteDefinition>;
7
+
8
+ export interface RouteMatch {
9
+ path: string;
10
+ component: string;
11
+ params: Record<string, string>;
12
+ definition: RouteDefinition;
13
+ }
14
+
15
+ function getRouteWeight(pattern: string): number {
16
+ const segments = pattern.split('/').filter(Boolean);
17
+ return segments.reduce((weight, seg) => {
18
+ return weight + (seg.startsWith(':') ? 1 : 10);
19
+ }, 0);
20
+ }
21
+
22
+ export class AppRouter {
23
+ private _routes: RouteConfig = {};
24
+ private _initialized = false;
25
+
26
+ constructor(routes?: RouteConfig) {
27
+ if (routes) {
28
+ this.setRoutes(routes);
29
+ }
30
+
3
31
  window.addEventListener('popstate', () => this._notify());
4
32
 
5
33
  document.addEventListener('click', (e) => {
6
- // 1. Ignore modified clicks (Ctrl+Click, etc)
7
34
  if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {
8
35
  return;
9
36
  }
10
37
 
11
- // 2. Safely traverse the path
12
38
  const path = e.composedPath();
13
39
  const link = path.find((el: any) =>
14
40
  el && typeof el.hasAttribute === 'function' && el.hasAttribute('href')
@@ -19,29 +45,90 @@ class AppRouter {
19
45
  const href = link.getAttribute('href');
20
46
  const target = link.getAttribute('target');
21
47
 
22
- // 3. Ignore external links
23
48
  if (!href || href.startsWith('http') || href.startsWith('mailto:') || target === '_blank') {
24
49
  return;
25
50
  }
26
51
 
27
- // 4. Intercept internal links
28
52
  if (href.startsWith('/')) {
29
53
  e.preventDefault();
30
- window.history.pushState(null, '', href);
31
- this._notify();
54
+ this.navigate(href);
32
55
  }
33
56
  });
57
+ }
58
+
59
+ /**
60
+ * Register or update the route configuration for this application.
61
+ */
62
+ public setRoutes(routes: RouteConfig) {
63
+ this._routes = routes;
34
64
 
35
- // Broadcast initial route on load
36
- // Use setTimeout to ensure all Lit components have registered first
37
- setTimeout(() => this._notify(), 0);
65
+ // Trigger initial route resolution once routes are registered
66
+ if (!this._initialized) {
67
+ this._initialized = true;
68
+ setTimeout(() => this._notify(), 0);
69
+ } else {
70
+ this._notify();
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Programmatically navigate to an internal path.
76
+ */
77
+ public navigate(path: string) {
78
+ window.history.pushState(null, '', path);
79
+ this._notify();
80
+ }
81
+
82
+ /**
83
+ * Matches a URL path against the registered routes.
84
+ */
85
+ public matchRoute(currentPath: string): RouteMatch | null {
86
+ const sortedRoutes = Object.entries(this._routes).sort(
87
+ ([patternA], [patternB]) => getRouteWeight(patternB) - getRouteWeight(patternA)
88
+ );
89
+
90
+ for (const [pattern, routeDef] of sortedRoutes) {
91
+ const paramNames: string[] = [];
92
+ const regexPath = pattern.replace(/:([a-zA-Z0-9_]+)/g, (_, paramName) => {
93
+ paramNames.push(paramName);
94
+ return '([^/]+)';
95
+ });
96
+
97
+ const regex = new RegExp(`^${regexPath}$`);
98
+ const match = currentPath.match(regex);
99
+
100
+ if (match) {
101
+ const params: Record<string, string> = {};
102
+ paramNames.forEach((name, index) => {
103
+ params[name] = decodeURIComponent(match[index + 1]);
104
+ });
105
+
106
+ return {
107
+ path: currentPath,
108
+ component: routeDef.component,
109
+ params,
110
+ definition: routeDef
111
+ };
112
+ }
113
+ }
114
+
115
+ return null;
38
116
  }
39
117
 
40
118
  private _notify() {
119
+ const currentPath = window.location.pathname;
120
+ const match = this.matchRoute(currentPath);
121
+
41
122
  window.dispatchEvent(new CustomEvent('route-changed', {
42
- detail: window.location.pathname
123
+ detail: {
124
+ path: currentPath,
125
+ component: match ? match.component : 'not-found-page',
126
+ params: match ? match.params : {},
127
+ definition: match ? match.definition : null
128
+ }
43
129
  }));
44
130
  }
45
131
  }
46
132
 
133
+ // Export a singleton instance for standard app-wide use
47
134
  export const router = new AppRouter();