@wsxjs/wsx-router 0.0.5
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/LICENSE +21 -0
- package/README.md +245 -0
- package/dist/index.cjs +1 -0
- package/dist/index.js +409 -0
- package/package.json +63 -0
- package/src/RouterUtils.ts +260 -0
- package/src/WsxLink.css +74 -0
- package/src/WsxLink.wsx +162 -0
- package/src/WsxRouter.css +11 -0
- package/src/WsxRouter.wsx +180 -0
- package/src/WsxView.css +51 -0
- package/src/WsxView.wsx +102 -0
- package/src/index.ts +18 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 WSX Framework Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# @wsxjs/wsx-router
|
|
2
|
+
|
|
3
|
+
WSX Router - Native History API-based routing for WSX Framework
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🚀 **Zero dependencies** - Built on native History API
|
|
8
|
+
- 📦 **Lightweight** - Minimal bundle size
|
|
9
|
+
- 🎯 **TypeScript first** - Full type safety
|
|
10
|
+
- 🔄 **Declarative routing** - HTML-based route configuration
|
|
11
|
+
- 📱 **SPA-ready** - Single Page Application support
|
|
12
|
+
- 🎨 **Customizable** - CSS custom properties for styling
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add @wsxjs-router
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### 1. Basic Setup
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<wsx-router>
|
|
26
|
+
<wsx-view route="/" component="home-page"></wsx-view>
|
|
27
|
+
<wsx-view route="/about" component="about-page"></wsx-view>
|
|
28
|
+
<wsx-view route="/users/:id" component="user-detail"></wsx-view>
|
|
29
|
+
<wsx-view route="*" component="not-found"></wsx-view>
|
|
30
|
+
</wsx-router>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 2. Navigation Links
|
|
34
|
+
|
|
35
|
+
```html
|
|
36
|
+
<wsx-link to="/">Home</wsx-link>
|
|
37
|
+
<wsx-link to="/about">About</wsx-link>
|
|
38
|
+
<wsx-link to="/users/123">User 123</wsx-link>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 3. Programmatic Navigation
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { RouterUtils } from '@wsxjs-router';
|
|
45
|
+
|
|
46
|
+
// Navigate to a route
|
|
47
|
+
RouterUtils.navigate('/users/456');
|
|
48
|
+
|
|
49
|
+
// Replace current route
|
|
50
|
+
RouterUtils.replace('/login');
|
|
51
|
+
|
|
52
|
+
// Go back
|
|
53
|
+
RouterUtils.goBack();
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Components
|
|
57
|
+
|
|
58
|
+
### WsxRouter
|
|
59
|
+
|
|
60
|
+
Main router container that manages route matching and rendering.
|
|
61
|
+
|
|
62
|
+
**Attributes:**
|
|
63
|
+
- None (router configuration is defined by child `wsx-view` elements)
|
|
64
|
+
|
|
65
|
+
**Events:**
|
|
66
|
+
- `route-changed`: Fired when route changes with `{ path, view }` detail
|
|
67
|
+
|
|
68
|
+
### WsxView
|
|
69
|
+
|
|
70
|
+
Route view container that conditionally renders components.
|
|
71
|
+
|
|
72
|
+
**Attributes:**
|
|
73
|
+
- `route`: Route pattern (supports parameters like `/users/:id`)
|
|
74
|
+
- `component`: Component name to render
|
|
75
|
+
- `params`: Route parameters (automatically set by router)
|
|
76
|
+
|
|
77
|
+
### WsxLink
|
|
78
|
+
|
|
79
|
+
Navigation link component with active state management.
|
|
80
|
+
|
|
81
|
+
**Attributes:**
|
|
82
|
+
- `to`: Target route path
|
|
83
|
+
- `replace`: Replace history instead of push (default: false)
|
|
84
|
+
- `active-class`: CSS class for active state (default: 'active')
|
|
85
|
+
- `exact`: Exact path matching (default: false)
|
|
86
|
+
|
|
87
|
+
**CSS Custom Properties:**
|
|
88
|
+
- `--link-color`: Link text color
|
|
89
|
+
- `--link-hover-color`: Link hover color
|
|
90
|
+
- `--link-active-color`: Active link color
|
|
91
|
+
|
|
92
|
+
## API Reference
|
|
93
|
+
|
|
94
|
+
### RouterUtils
|
|
95
|
+
|
|
96
|
+
Static utility class for programmatic navigation and route management.
|
|
97
|
+
|
|
98
|
+
#### Navigation Methods
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
// Navigate to a route
|
|
102
|
+
RouterUtils.navigate(path: string, replace?: boolean): void
|
|
103
|
+
|
|
104
|
+
// Get current route information
|
|
105
|
+
RouterUtils.getCurrentRoute(): RouteInfo
|
|
106
|
+
|
|
107
|
+
// Check if route is active
|
|
108
|
+
RouterUtils.isRouteActive(route: string, exact?: boolean): boolean
|
|
109
|
+
|
|
110
|
+
// Build path with parameters
|
|
111
|
+
RouterUtils.buildPath(route: string, params?: Record<string, string>): string
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### Query Parameters
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
// Get query parameter
|
|
118
|
+
RouterUtils.getQueryParam(key: string): string | null
|
|
119
|
+
|
|
120
|
+
// Set query parameter
|
|
121
|
+
RouterUtils.setQueryParam(key: string, value: string, replace?: boolean): void
|
|
122
|
+
|
|
123
|
+
// Remove query parameter
|
|
124
|
+
RouterUtils.removeQueryParam(key: string, replace?: boolean): void
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### History Management
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// Go back/forward
|
|
131
|
+
RouterUtils.goBack(): void
|
|
132
|
+
RouterUtils.goForward(): void
|
|
133
|
+
|
|
134
|
+
// Replace current route
|
|
135
|
+
RouterUtils.replace(path: string): void
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Types
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
interface RouteInfo {
|
|
142
|
+
path: string;
|
|
143
|
+
params: Record<string, string>;
|
|
144
|
+
query: Record<string, string>;
|
|
145
|
+
hash: string;
|
|
146
|
+
meta?: Record<string, string | number | boolean>;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
interface RouteMatch {
|
|
150
|
+
route: string;
|
|
151
|
+
params: Record<string, string>;
|
|
152
|
+
exact: boolean;
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Examples
|
|
157
|
+
|
|
158
|
+
### Parameter Routes
|
|
159
|
+
|
|
160
|
+
```html
|
|
161
|
+
<wsx-router>
|
|
162
|
+
<wsx-view route="/users/:userId/posts/:postId" component="post-detail"></wsx-view>
|
|
163
|
+
</wsx-router>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The component will receive parameters as attributes:
|
|
167
|
+
```html
|
|
168
|
+
<!-- For route: /users/123/posts/456 -->
|
|
169
|
+
<post-detail userid="123" postid="456"></post-detail>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Nested Routing
|
|
173
|
+
|
|
174
|
+
```html
|
|
175
|
+
<!-- Parent route with wildcard -->
|
|
176
|
+
<wsx-view route="/admin/*" component="admin-layout"></wsx-view>
|
|
177
|
+
|
|
178
|
+
<!-- Inside admin-layout component -->
|
|
179
|
+
<wsx-router base="/admin">
|
|
180
|
+
<wsx-view route="/dashboard" component="admin-dashboard"></wsx-view>
|
|
181
|
+
<wsx-view route="/users" component="admin-users"></wsx-view>
|
|
182
|
+
</wsx-router>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Link Variations
|
|
186
|
+
|
|
187
|
+
```html
|
|
188
|
+
<!-- Basic link -->
|
|
189
|
+
<wsx-link to="/about">About</wsx-link>
|
|
190
|
+
|
|
191
|
+
<!-- Button style -->
|
|
192
|
+
<wsx-link to="/login" variant="button">Login</wsx-link>
|
|
193
|
+
|
|
194
|
+
<!-- Tab style -->
|
|
195
|
+
<wsx-link to="/dashboard" variant="tab">Dashboard</wsx-link>
|
|
196
|
+
|
|
197
|
+
<!-- External link -->
|
|
198
|
+
<wsx-link to="https://example.com" external>External Site</wsx-link>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Styling
|
|
202
|
+
|
|
203
|
+
### CSS Custom Properties
|
|
204
|
+
|
|
205
|
+
```css
|
|
206
|
+
wsx-link {
|
|
207
|
+
--link-color: #007bff;
|
|
208
|
+
--link-hover-color: #0056b3;
|
|
209
|
+
--link-active-color: #6c757d;
|
|
210
|
+
--link-focus-color: #007bff;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
wsx-view {
|
|
214
|
+
--transition-duration: 300ms;
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### CSS Parts
|
|
219
|
+
|
|
220
|
+
```css
|
|
221
|
+
/* Style the link element */
|
|
222
|
+
wsx-link::part(link) {
|
|
223
|
+
font-weight: bold;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/* Style the view container */
|
|
227
|
+
wsx-view::part(view) {
|
|
228
|
+
padding: 1rem;
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Browser Support
|
|
233
|
+
|
|
234
|
+
- Modern browsers with Web Components support
|
|
235
|
+
- Shadow DOM v1
|
|
236
|
+
- Custom Elements v1
|
|
237
|
+
- History API
|
|
238
|
+
|
|
239
|
+
## Contributing
|
|
240
|
+
|
|
241
|
+
See the main [WSX Framework Contributing Guide](../../CONTRIBUTING.md).
|
|
242
|
+
|
|
243
|
+
## License
|
|
244
|
+
|
|
245
|
+
MIT License - see [LICENSE](../../LICENSE) for details.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("@wsxjs/wsx-core"),G=":host{display:block;width:100%;height:100%}.router-outlet{width:100%;height:100%}";var K=Object.create,$=Object.defineProperty,X=Object.getOwnPropertyDescriptor,L=(e,t)=>(t=Symbol[e])?t:Symbol.for("Symbol."+e),P=e=>{throw TypeError(e)},I=(e,t,r)=>t in e?$(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,Y=(e,t)=>$(e,"name",{value:t,configurable:!0}),Z=e=>[,,,K((e==null?void 0:e[L("metadata")])??null)],tt=["class","method","getter","setter","accessor","field","value","get","set"],O=e=>e!==void 0&&typeof e!="function"?P("Function expected"):e,et=(e,t,r,i,o)=>({kind:tt[e],name:t,metadata:i,addInitializer:n=>r._?P("Already initialized"):o.push(O(n||null))}),rt=(e,t)=>I(t,L("metadata"),e[3]),ot=(e,t,r,i)=>{for(var o=0,n=e[t>>1],s=n&&n.length;o<s;o++)n[o].call(r);return i},it=(e,t,r,i,o,n)=>{var s,h,c,a=t&7,d=!1,m=0,f=e[m]||(e[m]=[]),u=a&&(o=o.prototype,a<5&&(a>3||!d)&&X(o,r));Y(o,r);for(var p=i.length-1;p>=0;p--)c=et(a,r,h={},e[3],f),s=(0,i[p])(o,c),h._=1,O(s)&&(o=s);return rt(e,o),u&&$(o,r,u),d?a^4?n:u:o},b=(e,t,r)=>I(e,typeof t!="symbol"?t+"":t,r),j,E,N;const v=l.createLogger("WsxRouter");j=[l.autoRegister({tagName:"wsx-router"})];class y extends(N=l.WebComponent){constructor(){super({styles:G}),b(this,"views",new Map),b(this,"currentView",null),b(this,"handleRouteChange",()=>{const t=window.location.pathname;v.debug(`Route changed to: ${t}`),this.currentView&&(this.currentView.style.display="none",v.debug("Hiding previous view"));const r=this.matchRoute(t);if(r){r.style.display="block",this.currentView=r,v.debug(`Showing view for route: ${r.getAttribute("route")}`);const i=this.extractParams(r.getAttribute("route")||"/",t);i&&r.setAttribute("params",JSON.stringify(i))}else v.warn(`No view found for path: ${t}`);this.dispatchEvent(new CustomEvent("route-changed",{detail:{path:t,view:r},bubbles:!0,composed:!0}))}),b(this,"interceptLinks",t=>{const r=t.target.closest("a");if(!r)return;const i=r.getAttribute("href");!i||i.startsWith("http")||i.startsWith("#")||(t.preventDefault(),this.navigate(i))})}render(){return l.jsx("div",{class:"router-outlet"},l.jsx("slot",null))}onConnected(){v.debug("WsxRouter connected to DOM"),this.collectViews(),v.debug("WsxRouter collected views:",this.views.size),window.addEventListener("popstate",this.handleRouteChange),this.addEventListener("click",this.interceptLinks),this.handleRouteChange()}onDisconnected(){window.removeEventListener("popstate",this.handleRouteChange)}collectViews(){var o;const t=(o=this.shadowRoot)==null?void 0:o.querySelector("slot");if(!t){v.error("WsxRouter: No slot found");return}const i=t.assignedElements().filter(n=>n.tagName.toLowerCase()==="wsx-view");v.debug("WsxRouter found views:",i.length),i.forEach(n=>{const s=n.getAttribute("route")||"/";this.views.set(s,n),n.style.display="none",v.debug(`WsxRouter hiding view for route: ${s}`)})}matchRoute(t){if(this.views.has(t))return this.views.get(t);for(const[r,i]of this.views)if(r.includes(":")){const o=r.replace(/:[^/]+/g,"([^/]+)");if(new RegExp(`^${o}$`).test(t))return i}return this.views.get("*")||null}extractParams(t,r){var c;if(!t.includes(":"))return null;const i=((c=t.match(/:([^/]+)/g))==null?void 0:c.map(a=>a.slice(1)))||[],o=t.replace(/:[^/]+/g,"([^/]+)"),n=new RegExp(`^${o}$`),s=r.match(n);if(!s||!i.length)return null;const h={};return i.forEach((a,d)=>{h[a]=s[d+1]}),h}navigate(t){window.history.pushState(null,"",t),this.handleRouteChange()}}E=Z(N);y=it(E,0,"WsxRouter",j,y);ot(E,1,y);const nt=":host{display:block;width:100%;height:100%}.route-view{width:100%;height:100%;position:relative}:host([loading]) .route-view{opacity:.5;transition:opacity .3s ease}:host([error]) .route-view{border:1px solid var(--error-color, #ff0000);padding:1rem}.route-view.entering{animation:fadeIn .3s ease-out}.route-view.leaving{animation:fadeOut .3s ease-in}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}";var st=Object.create,R=Object.defineProperty,at=Object.getOwnPropertyDescriptor,z=(e,t)=>(t=Symbol[e])?t:Symbol.for("Symbol."+e),D=e=>{throw TypeError(e)},F=(e,t,r)=>t in e?R(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,ct=(e,t)=>R(e,"name",{value:t,configurable:!0}),lt=e=>[,,,st((e==null?void 0:e[z("metadata")])??null)],ht=["class","method","getter","setter","accessor","field","value","get","set"],U=e=>e!==void 0&&typeof e!="function"?D("Function expected"):e,dt=(e,t,r,i,o)=>({kind:ht[e],name:t,metadata:i,addInitializer:n=>r._?D("Already initialized"):o.push(U(n||null))}),ut=(e,t)=>F(t,z("metadata"),e[3]),pt=(e,t,r,i)=>{for(var o=0,n=e[t>>1],s=n&&n.length;o<s;o++)n[o].call(r);return i},vt=(e,t,r,i,o,n)=>{var s,h,c,a=t&7,d=!1,m=0,f=e[m]||(e[m]=[]),u=a&&(o=o.prototype,a<5&&(a>3||!d)&&at(o,r));ct(o,r);for(var p=i.length-1;p>=0;p--)c=dt(a,r,h={},e[3],f),s=(0,i[p])(o,c),h._=1,U(s)&&(o=s);return ut(e,o),u&&R(o,r,u),d?a^4?n:u:o},x=(e,t,r)=>F(e,typeof t!="symbol"?t+"":t,r),M,S,q;const k=l.createLogger("WsxView");M=[l.autoRegister({tagName:"wsx-view"})];class g extends(q=l.WebComponent){constructor(){super({styles:nt,styleName:"wsx-view"}),x(this,"component",null),x(this,"params",{}),x(this,"componentInstance",null)}render(){return l.jsx("div",{class:"route-view",part:"view"})}onConnected(){const t=this.getAttribute("component");t&&!this.componentInstance&&this.loadComponent(t)}onAttributeChanged(t,r,i){if(t==="component"&&i&&!this.componentInstance)this.loadComponent(i);else if(t==="params"&&this.componentInstance)try{this.params=JSON.parse(i),Object.entries(this.params).forEach(([o,n])=>{this.componentInstance.setAttribute(o,n)})}catch(o){k.error("Failed to parse params:",o)}}async loadComponent(t){var o;if(this.componentInstance&&(this.componentInstance.remove(),this.componentInstance=null),!customElements.get(t)){k.warn(`Component ${t} not found in customElements registry`);return}this.componentInstance=document.createElement(t),Object.keys(this.params).length>0&&Object.entries(this.params).forEach(([n,s])=>{this.componentInstance.setAttribute(n,s)});const i=(o=this.shadowRoot)==null?void 0:o.querySelector(".route-view");i?i.appendChild(this.componentInstance):k.error("Route view container not found")}onDisconnected(){this.componentInstance&&(this.componentInstance.remove(),this.componentInstance=null)}}S=lt(q);g=vt(S,0,"WsxView",M,g);x(g,"observedAttributes",["route","component","params"]);pt(S,1,g);const mt=':host{display:inline-block;min-width:fit-content;min-height:fit-content;width:auto;height:auto}.wsx-link{color:var(--link-color, #007bff);text-decoration:var(--link-decoration, underline);cursor:pointer;transition:color .2s ease;display:inline-block;min-height:1.2em;line-height:1.2}.wsx-link:hover{color:var(--link-hover-color, #0056b3);text-decoration:var(--link-hover-decoration, underline)}.wsx-link:focus{outline:2px solid var(--link-focus-color, #007bff);outline-offset:2px}.wsx-link.active{color:var(--link-active-color, #6c757d);font-weight:var(--link-active-weight, bold)}:host([disabled]) .wsx-link{color:var(--link-disabled-color, #6c757d);cursor:not-allowed;pointer-events:none}:host([external]) .wsx-link:after{content:"↗";font-size:.8em;margin-left:.2em;opacity:.7}:host([variant="button"]) .wsx-link{background-color:var(--button-bg, #007bff);color:var(--button-color, white);padding:.5rem 1rem;border-radius:.25rem;text-decoration:none;display:inline-block}:host([variant="button"]) .wsx-link:hover{background-color:var(--button-hover-bg, #0056b3);color:var(--button-hover-color, white)}:host([variant="tab"]) .wsx-link{padding:.5rem 1rem;border-bottom:2px solid transparent;text-decoration:none}:host([variant="tab"]) .wsx-link.active{border-bottom-color:var(--tab-active-border, #007bff)}';var wt=Object.create,C=Object.defineProperty,ft=Object.getOwnPropertyDescriptor,V=(e,t)=>(t=Symbol[e])?t:Symbol.for("Symbol."+e),T=e=>{throw TypeError(e)},Q=(e,t,r)=>t in e?C(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,gt=(e,t)=>C(e,"name",{value:t,configurable:!0}),_t=e=>[,,,wt((e==null?void 0:e[V("metadata")])??null)],bt=["class","method","getter","setter","accessor","field","value","get","set"],H=e=>e!==void 0&&typeof e!="function"?T("Function expected"):e,xt=(e,t,r,i,o)=>({kind:bt[e],name:t,metadata:i,addInitializer:n=>r._?T("Already initialized"):o.push(H(n||null))}),yt=(e,t)=>Q(t,V("metadata"),e[3]),kt=(e,t,r,i)=>{for(var o=0,n=e[t>>1],s=n&&n.length;o<s;o++)n[o].call(r);return i},$t=(e,t,r,i,o,n)=>{var s,h,c,a=t&7,d=!1,m=0,f=e[m]||(e[m]=[]),u=a&&(o=o.prototype,a<5&&(a>3||!d)&&ft(o,r));gt(o,r);for(var p=i.length-1;p>=0;p--)c=xt(a,r,h={},e[3],f),s=(0,i[p])(o,c),h._=1,H(s)&&(o=s);return yt(e,o),u&&C(o,r,u),d?a^4?n:u:o},w=(e,t,r)=>Q(e,typeof t!="symbol"?t+"":t,r),J,W,B;const A=l.createLogger("WsxLink");J=[l.autoRegister({tagName:"wsx-link"})];class _ extends(B=l.WebComponent){constructor(){super({styles:mt,styleName:"wsx-link"}),w(this,"to",""),w(this,"replace",!1),w(this,"activeClass","active"),w(this,"exact",!1),w(this,"handleClick",t=>{if(t.preventDefault(),!this.to){A.warn("No 'to' attribute specified");return}if(this.isExternalLink(this.to)){window.open(this.to,"_blank");return}this.replace?window.history.replaceState(null,"",this.to):window.history.pushState(null,"",this.to),window.dispatchEvent(new PopStateEvent("popstate")),A.debug(`Navigated to: ${this.to}`)}),w(this,"updateActiveState",()=>{var o;const t=window.location.pathname,r=this.exact?t===this.to:t.startsWith(this.to)&&this.to!=="/",i=(o=this.shadowRoot)==null?void 0:o.querySelector("a");i&&(r?(i.classList.add(this.activeClass),this.setAttribute("active","")):(i.classList.remove(this.activeClass),this.removeAttribute("active")))})}render(){return l.jsx("a",{href:this.to,class:"wsx-link",onClick:this.handleClick,part:"link"},l.jsx("slot",null))}onConnected(){this.to=this.getAttribute("to")||"",this.replace=this.hasAttribute("replace"),this.activeClass=this.getAttribute("active-class")||"active",this.exact=this.hasAttribute("exact");const t=this.shadowRoot.querySelector(".wsx-link");t&&(t.href=this.to),window.addEventListener("popstate",this.updateActiveState),document.addEventListener("route-changed",this.updateActiveState),this.updateActiveState()}onDisconnected(){window.removeEventListener("popstate",this.updateActiveState),document.removeEventListener("route-changed",this.updateActiveState)}onAttributeChanged(t,r,i){switch(t){case"to":this.to=i||"",this.rerender(),this.updateActiveState();break;case"replace":this.replace=i!==null&&i!=="false";break;case"active-class":this.activeClass=i||"active",this.updateActiveState();break;case"exact":this.exact=i!==null&&i!=="false",this.updateActiveState();break}}isExternalLink(t){return t.startsWith("http://")||t.startsWith("https://")||t.startsWith("mailto:")||t.startsWith("tel:")}navigate(){this.to&&this.handleClick(new MouseEvent("click",{bubbles:!0,cancelable:!0}))}}W=_t(B);_=$t(W,0,"WsxLink",J,_);w(_,"observedAttributes",["to","replace","active-class","exact"]);kt(W,1,_);const Et=l.createLogger("RouterUtils");class Rt{static navigate(t,r=!1){r?window.history.replaceState(null,"",t):window.history.pushState(null,"",t),window.dispatchEvent(new PopStateEvent("popstate")),Et.debug(`Navigated to: ${t} (replace: ${r})`)}static getCurrentRoute(){const t=new URL(window.location.href);return{path:t.pathname,params:{},query:Object.fromEntries(t.searchParams.entries()),hash:t.hash.slice(1)}}static parseRoute(t,r){var i;if(t===r)return{route:t,params:{},exact:!0};if(t==="*")return{route:t,params:{},exact:!1};if(t.includes(":")){const o=((i=t.match(/:([^/]+)/g))==null?void 0:i.map(c=>c.slice(1)))||[],n=t.replace(/:[^/]+/g,"([^/]+)"),s=new RegExp(`^${n}$`),h=r.match(s);if(h&&o.length>0){const c={};return o.forEach((a,d)=>{c[a]=h[d+1]}),{route:t,params:c,exact:!0}}}if(t.endsWith("/*")){const o=t.slice(0,-2);if(r.startsWith(o))return{route:t,params:{},exact:!1}}return null}static buildPath(t,r={}){let i=t;return Object.entries(r).forEach(([o,n])=>{i=i.replace(`:${o}`,encodeURIComponent(n))}),i}static isRouteActive(t,r=!1){const i=window.location.pathname;return r?i===t:t==="/"?i==="/":i.startsWith(t)}static getRouteDepth(t){return t.split("/").filter(r=>r.length>0).length}static getParentRoute(t){const r=t.split("/").filter(i=>i.length>0);return r.length<=1?"/":(r.pop(),"/"+r.join("/"))}static joinPaths(...t){return t.map(r=>r.replace(/^\/+|\/+$/g,"")).filter(r=>r.length>0).join("/").replace(/^/,"/")}static isExternalUrl(t){return/^https?:\/\//.test(t)||/^mailto:/.test(t)||/^tel:/.test(t)}static getQueryParam(t){return new URL(window.location.href).searchParams.get(t)}static setQueryParam(t,r,i=!1){const o=new URL(window.location.href);o.searchParams.set(t,r);const n=o.pathname+o.search+o.hash;this.navigate(n,i)}static removeQueryParam(t,r=!1){const i=new URL(window.location.href);i.searchParams.delete(t);const o=i.pathname+i.search+i.hash;this.navigate(o,r)}static goBack(){window.history.back()}static goForward(){window.history.forward()}static replace(t){this.navigate(t,!0)}static getHistoryLength(){return window.history.length}static onRouteChange(t){const r=()=>{const i=this.getCurrentRoute();t(i)};return window.addEventListener("popstate",r),document.addEventListener("route-changed",r),()=>{window.removeEventListener("popstate",r),document.removeEventListener("route-changed",r)}}}exports.RouterUtils=Rt;exports.WsxLink=_;exports.WsxRouter=y;exports.WsxView=g;
|