@diniz/webcomponents 1.1.6 → 1.1.7
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/README.md +346 -0
- package/dist/README.md +346 -0
- package/dist/button-demo-BcfxxPSq.js +227 -0
- package/dist/card-demo-Cxp-wRGW.js +230 -0
- package/dist/date-picker-demo-B8y3zapN.js +143 -0
- package/dist/form-demo-page-F1iLCgfh.js +351 -0
- package/dist/home-page-XUM8cHP7.js +468 -0
- package/dist/index-DiYekJaQ.js +2424 -0
- package/dist/layout-demo-CJsZ6DI5.js +289 -0
- package/dist/modal-demo-page-YN2KgJ31.js +195 -0
- package/dist/stepper-demo-page-BkcpKk_F.js +312 -0
- package/dist/table-demo-x2ZD8cFh.js +137 -0
- package/dist/tabs-demo-BQBtZzw9.js +76 -0
- package/dist/toast-demo-page-DLVacHXA.js +260 -0
- package/dist/webcomponents.es.js +31 -1912
- package/dist/webcomponents.umd.js +2529 -105
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,6 +47,352 @@ document.body.innerHTML = `
|
|
|
47
47
|
`;
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
+
## Quick Start with Vite (No Framework)
|
|
51
|
+
|
|
52
|
+
Create a new Vite project without any framework to use these web components:
|
|
53
|
+
|
|
54
|
+
### 1. Create a new Vite project
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npm create vite@latest my-app -- --template vanilla-ts
|
|
58
|
+
cd my-app
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 2. Install the web components library
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm install @diniz/webcomponents
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 3. Import components in your `src/main.ts`
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import '@diniz/webcomponents';
|
|
71
|
+
import '@diniz/webcomponents/dist/style.css';
|
|
72
|
+
import './style.css';
|
|
73
|
+
|
|
74
|
+
document.querySelector<HTMLDivElement>('#app')!.innerHTML = `
|
|
75
|
+
<div>
|
|
76
|
+
<h1>My Web Components App</h1>
|
|
77
|
+
|
|
78
|
+
<ui-button variant="primary">Primary Button</ui-button>
|
|
79
|
+
<ui-button variant="secondary">Secondary Button</ui-button>
|
|
80
|
+
|
|
81
|
+
<ui-input
|
|
82
|
+
label="Email"
|
|
83
|
+
type="email"
|
|
84
|
+
placeholder="Enter your email"
|
|
85
|
+
required
|
|
86
|
+
></ui-input>
|
|
87
|
+
|
|
88
|
+
<ui-date-picker
|
|
89
|
+
label="Select Date"
|
|
90
|
+
format="DD/MM/YYYY"
|
|
91
|
+
></ui-date-picker>
|
|
92
|
+
</div>
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
// Listen to component events
|
|
96
|
+
document.querySelector('ui-button')?.addEventListener('click', () => {
|
|
97
|
+
console.log('Button clicked!');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
document.querySelector('ui-input')?.addEventListener('input', (e: Event) => {
|
|
101
|
+
const input = e.target as HTMLInputElement;
|
|
102
|
+
console.log('Input value:', input.value);
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 4. Run the development server
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npm run dev
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Your app is now running with web components! Open your browser and start building.
|
|
113
|
+
|
|
114
|
+
### Example: Building a Counter with Signals
|
|
115
|
+
|
|
116
|
+
Create reactive components using the signals system:
|
|
117
|
+
|
|
118
|
+
**src/components/counter.ts**
|
|
119
|
+
```typescript
|
|
120
|
+
import { BaseComponent } from '@diniz/webcomponents';
|
|
121
|
+
|
|
122
|
+
class CounterComponent extends BaseComponent {
|
|
123
|
+
private count = this.useSignal(0);
|
|
124
|
+
|
|
125
|
+
connectedCallback() {
|
|
126
|
+
super.connectedCallback();
|
|
127
|
+
this.render();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private increment() {
|
|
131
|
+
this.count.set(this.count.get() + 1);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private decrement() {
|
|
135
|
+
this.count.set(this.count.get() - 1);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
render() {
|
|
139
|
+
this.shadowRoot!.innerHTML = `
|
|
140
|
+
<style>
|
|
141
|
+
:host {
|
|
142
|
+
display: block;
|
|
143
|
+
padding: 2rem;
|
|
144
|
+
text-align: center;
|
|
145
|
+
}
|
|
146
|
+
.count {
|
|
147
|
+
font-size: 3rem;
|
|
148
|
+
margin: 1rem 0;
|
|
149
|
+
color: var(--color-primary, #24ec71);
|
|
150
|
+
}
|
|
151
|
+
.buttons {
|
|
152
|
+
display: flex;
|
|
153
|
+
gap: 1rem;
|
|
154
|
+
justify-content: center;
|
|
155
|
+
}
|
|
156
|
+
</style>
|
|
157
|
+
|
|
158
|
+
<div>
|
|
159
|
+
<h2>Counter</h2>
|
|
160
|
+
<div class="count">${this.count.get()}</div>
|
|
161
|
+
<div class="buttons">
|
|
162
|
+
<ui-button id="decrement" variant="secondary">-</ui-button>
|
|
163
|
+
<ui-button id="increment" variant="primary">+</ui-button>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
`;
|
|
167
|
+
|
|
168
|
+
this.shadowRoot!.getElementById('increment')?.addEventListener('click',
|
|
169
|
+
() => this.increment()
|
|
170
|
+
);
|
|
171
|
+
this.shadowRoot!.getElementById('decrement')?.addEventListener('click',
|
|
172
|
+
() => this.decrement()
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
customElements.define('my-counter', CounterComponent);
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**src/main.ts**
|
|
181
|
+
```typescript
|
|
182
|
+
import '@diniz/webcomponents';
|
|
183
|
+
import '@diniz/webcomponents/dist/style.css';
|
|
184
|
+
import './components/counter';
|
|
185
|
+
import './style.css';
|
|
186
|
+
|
|
187
|
+
document.querySelector<HTMLDivElement>('#app')!.innerHTML = `
|
|
188
|
+
<div>
|
|
189
|
+
<h1>My Web Components App</h1>
|
|
190
|
+
<my-counter></my-counter>
|
|
191
|
+
</div>
|
|
192
|
+
`;
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Adding Routing to Your App
|
|
196
|
+
|
|
197
|
+
The library includes a built-in router for client-side navigation. Here's how to set it up:
|
|
198
|
+
|
|
199
|
+
#### 1. Create your route configuration
|
|
200
|
+
|
|
201
|
+
**src/router.ts**
|
|
202
|
+
```typescript
|
|
203
|
+
import { createRouter, type Route } from '@diniz/webcomponents';
|
|
204
|
+
|
|
205
|
+
export const routes: Route[] = [
|
|
206
|
+
{
|
|
207
|
+
path: '/',
|
|
208
|
+
load: () => import('./pages/home'),
|
|
209
|
+
component: 'home-page'
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
path: '/about',
|
|
213
|
+
load: () => import('./pages/about'),
|
|
214
|
+
component: 'about-page'
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
path: '/counter',
|
|
218
|
+
load: () => import('./components/counter'),
|
|
219
|
+
component: 'my-counter'
|
|
220
|
+
}
|
|
221
|
+
];
|
|
222
|
+
|
|
223
|
+
// Initialize the router with your routes
|
|
224
|
+
// The router automatically sets up navigation and loads the initial route
|
|
225
|
+
createRouter(routes);
|
|
226
|
+
|
|
227
|
+
// Optional: specify a custom app container selector (default is '#app')
|
|
228
|
+
// createRouter(routes, '#my-app-container');
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
> **How it works:** The `createRouter()` function sets up the routing system, registers event listeners for navigation, and automatically loads the initial route when the page loads. Navigation happens via links with the `data-link` attribute, and the browser's back/forward buttons work automatically.
|
|
232
|
+
|
|
233
|
+
**Optional: Adding Route Guards**
|
|
234
|
+
|
|
235
|
+
You can protect routes with guard functions that return a boolean or a Promise:
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
import { createRouter, type Route } from '@diniz/webcomponents';
|
|
239
|
+
|
|
240
|
+
// Example: Synchronous guard
|
|
241
|
+
const isAuthenticated = () => {
|
|
242
|
+
return localStorage.getItem('user') !== null;
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// Example: Async guard (e.g., checking with an API)
|
|
246
|
+
const hasPermission = async () => {
|
|
247
|
+
const response = await fetch('/api/check-permission');
|
|
248
|
+
const data = await response.json();
|
|
249
|
+
return data.hasAccess;
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
export const routes: Route[] = [
|
|
253
|
+
{
|
|
254
|
+
path: '/',
|
|
255
|
+
load: () => import('./pages/home'),
|
|
256
|
+
component: 'home-page'
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
path: '/profile',
|
|
260
|
+
load: () => import('./pages/profile'),
|
|
261
|
+
component: 'profile-page',
|
|
262
|
+
guard: isAuthenticated // Redirect to home if guard returns false
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
path: '/admin',
|
|
266
|
+
load: () => import('./pages/admin'),
|
|
267
|
+
component: 'admin-page',
|
|
268
|
+
guard: hasPermission // Supports async guards
|
|
269
|
+
}
|
|
270
|
+
];
|
|
271
|
+
|
|
272
|
+
createRouter(routes);
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
#### 2. Create page components (with optional shared navigation)
|
|
276
|
+
|
|
277
|
+
You can create a reusable navigation component:
|
|
278
|
+
|
|
279
|
+
**src/components/nav.ts**
|
|
280
|
+
```typescript
|
|
281
|
+
import { BaseComponent } from '@diniz/webcomponents';
|
|
282
|
+
|
|
283
|
+
class NavComponent extends BaseComponent {
|
|
284
|
+
connectedCallback() {
|
|
285
|
+
super.connectedCallback();
|
|
286
|
+
this.render();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
render() {
|
|
290
|
+
this.shadowRoot!.innerHTML = `
|
|
291
|
+
<style>
|
|
292
|
+
nav {
|
|
293
|
+
background: var(--color-surface, #1e1e1e);
|
|
294
|
+
padding: 1rem;
|
|
295
|
+
display: flex;
|
|
296
|
+
gap: 1rem;
|
|
297
|
+
margin-bottom: 2rem;
|
|
298
|
+
}
|
|
299
|
+
nav a {
|
|
300
|
+
color: var(--color-text, #fff);
|
|
301
|
+
text-decoration: none;
|
|
302
|
+
padding: 0.5rem 1rem;
|
|
303
|
+
border-radius: 0.25rem;
|
|
304
|
+
}
|
|
305
|
+
nav a:hover {
|
|
306
|
+
background: var(--color-surface-hover, #2a2a2a);
|
|
307
|
+
}
|
|
308
|
+
</style>
|
|
309
|
+
|
|
310
|
+
<nav>
|
|
311
|
+
<a href="/" data-link>Home</a>
|
|
312
|
+
<a href="/about" data-link>About</a>
|
|
313
|
+
<a href="/counter" data-link>Counter</a>
|
|
314
|
+
</nav>
|
|
315
|
+
`;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
customElements.define('app-nav', NavComponent);
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**src/pages/home.ts**
|
|
323
|
+
```typescript
|
|
324
|
+
import { BaseComponent } from '@diniz/webcomponents';
|
|
325
|
+
import '../components/nav';
|
|
326
|
+
|
|
327
|
+
class HomePage extends BaseComponent {
|
|
328
|
+
connectedCallback() {
|
|
329
|
+
super.connectedCallback();
|
|
330
|
+
this.render();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
render() {
|
|
334
|
+
this.shadowRoot!.innerHTML = `
|
|
335
|
+
<app-nav></app-nav>
|
|
336
|
+
<h1>Home Page</h1>
|
|
337
|
+
<p>Welcome to my web components app!</p>
|
|
338
|
+
<ui-button variant="primary">
|
|
339
|
+
<a href="/counter" data-link style="color: inherit; text-decoration: none;">
|
|
340
|
+
Try Counter
|
|
341
|
+
</a>
|
|
342
|
+
</ui-button>
|
|
343
|
+
`;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
customElements.define('home-page', HomePage);
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
**src/pages/about.ts**
|
|
351
|
+
```typescript
|
|
352
|
+
import { BaseComponent } from '@diniz/webcomponents';
|
|
353
|
+
import '../components/nav';
|
|
354
|
+
|
|
355
|
+
class AboutPage extends BaseComponent {
|
|
356
|
+
connectedCallback() {
|
|
357
|
+
super.connectedCallback();
|
|
358
|
+
this.render();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
render() {
|
|
362
|
+
this.shadowRoot!.innerHTML = `
|
|
363
|
+
<app-nav></app-nav>
|
|
364
|
+
<h1>About</h1>
|
|
365
|
+
<p>This is a Vite app using web components with routing.</p>
|
|
366
|
+
`;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
customElements.define('about-page', AboutPage);
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
#### 3. Initialize the router in main.ts
|
|
374
|
+
|
|
375
|
+
**src/main.ts**
|
|
376
|
+
```typescript
|
|
377
|
+
import '@diniz/webcomponents';
|
|
378
|
+
import '@diniz/webcomponents/dist/style.css';
|
|
379
|
+
import './style.css';
|
|
380
|
+
import './router'; // This loads the routes and initializes routing
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
The moment you import `./router`, the routing system:
|
|
384
|
+
1. ✅ Registers all event listeners for navigation
|
|
385
|
+
2. ✅ Automatically loads the initial route based on the current URL
|
|
386
|
+
3. ✅ Starts handling clicks on `[data-link]` elements
|
|
387
|
+
4. ✅ Enables browser back/forward button support
|
|
388
|
+
|
|
389
|
+
That's it! Your app now has client-side routing with:
|
|
390
|
+
- ✅ Lazy-loaded pages
|
|
391
|
+
- ✅ Browser back/forward navigation
|
|
392
|
+
- ✅ Declarative routing with `data-link` attribute
|
|
393
|
+
- ✅ Optional route guards for protected pages
|
|
394
|
+
- ✅ Reusable navigation component
|
|
395
|
+
|
|
50
396
|
## Components
|
|
51
397
|
|
|
52
398
|
- **ui-button** - Button with variants, sizes, icons
|
package/dist/README.md
CHANGED
|
@@ -47,6 +47,352 @@ document.body.innerHTML = `
|
|
|
47
47
|
`;
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
+
## Quick Start with Vite (No Framework)
|
|
51
|
+
|
|
52
|
+
Create a new Vite project without any framework to use these web components:
|
|
53
|
+
|
|
54
|
+
### 1. Create a new Vite project
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npm create vite@latest my-app -- --template vanilla-ts
|
|
58
|
+
cd my-app
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 2. Install the web components library
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm install @diniz/webcomponents
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 3. Import components in your `src/main.ts`
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import '@diniz/webcomponents';
|
|
71
|
+
import '@diniz/webcomponents/dist/style.css';
|
|
72
|
+
import './style.css';
|
|
73
|
+
|
|
74
|
+
document.querySelector<HTMLDivElement>('#app')!.innerHTML = `
|
|
75
|
+
<div>
|
|
76
|
+
<h1>My Web Components App</h1>
|
|
77
|
+
|
|
78
|
+
<ui-button variant="primary">Primary Button</ui-button>
|
|
79
|
+
<ui-button variant="secondary">Secondary Button</ui-button>
|
|
80
|
+
|
|
81
|
+
<ui-input
|
|
82
|
+
label="Email"
|
|
83
|
+
type="email"
|
|
84
|
+
placeholder="Enter your email"
|
|
85
|
+
required
|
|
86
|
+
></ui-input>
|
|
87
|
+
|
|
88
|
+
<ui-date-picker
|
|
89
|
+
label="Select Date"
|
|
90
|
+
format="DD/MM/YYYY"
|
|
91
|
+
></ui-date-picker>
|
|
92
|
+
</div>
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
// Listen to component events
|
|
96
|
+
document.querySelector('ui-button')?.addEventListener('click', () => {
|
|
97
|
+
console.log('Button clicked!');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
document.querySelector('ui-input')?.addEventListener('input', (e: Event) => {
|
|
101
|
+
const input = e.target as HTMLInputElement;
|
|
102
|
+
console.log('Input value:', input.value);
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 4. Run the development server
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npm run dev
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Your app is now running with web components! Open your browser and start building.
|
|
113
|
+
|
|
114
|
+
### Example: Building a Counter with Signals
|
|
115
|
+
|
|
116
|
+
Create reactive components using the signals system:
|
|
117
|
+
|
|
118
|
+
**src/components/counter.ts**
|
|
119
|
+
```typescript
|
|
120
|
+
import { BaseComponent } from '@diniz/webcomponents';
|
|
121
|
+
|
|
122
|
+
class CounterComponent extends BaseComponent {
|
|
123
|
+
private count = this.useSignal(0);
|
|
124
|
+
|
|
125
|
+
connectedCallback() {
|
|
126
|
+
super.connectedCallback();
|
|
127
|
+
this.render();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private increment() {
|
|
131
|
+
this.count.set(this.count.get() + 1);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private decrement() {
|
|
135
|
+
this.count.set(this.count.get() - 1);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
render() {
|
|
139
|
+
this.shadowRoot!.innerHTML = `
|
|
140
|
+
<style>
|
|
141
|
+
:host {
|
|
142
|
+
display: block;
|
|
143
|
+
padding: 2rem;
|
|
144
|
+
text-align: center;
|
|
145
|
+
}
|
|
146
|
+
.count {
|
|
147
|
+
font-size: 3rem;
|
|
148
|
+
margin: 1rem 0;
|
|
149
|
+
color: var(--color-primary, #24ec71);
|
|
150
|
+
}
|
|
151
|
+
.buttons {
|
|
152
|
+
display: flex;
|
|
153
|
+
gap: 1rem;
|
|
154
|
+
justify-content: center;
|
|
155
|
+
}
|
|
156
|
+
</style>
|
|
157
|
+
|
|
158
|
+
<div>
|
|
159
|
+
<h2>Counter</h2>
|
|
160
|
+
<div class="count">${this.count.get()}</div>
|
|
161
|
+
<div class="buttons">
|
|
162
|
+
<ui-button id="decrement" variant="secondary">-</ui-button>
|
|
163
|
+
<ui-button id="increment" variant="primary">+</ui-button>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
`;
|
|
167
|
+
|
|
168
|
+
this.shadowRoot!.getElementById('increment')?.addEventListener('click',
|
|
169
|
+
() => this.increment()
|
|
170
|
+
);
|
|
171
|
+
this.shadowRoot!.getElementById('decrement')?.addEventListener('click',
|
|
172
|
+
() => this.decrement()
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
customElements.define('my-counter', CounterComponent);
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**src/main.ts**
|
|
181
|
+
```typescript
|
|
182
|
+
import '@diniz/webcomponents';
|
|
183
|
+
import '@diniz/webcomponents/dist/style.css';
|
|
184
|
+
import './components/counter';
|
|
185
|
+
import './style.css';
|
|
186
|
+
|
|
187
|
+
document.querySelector<HTMLDivElement>('#app')!.innerHTML = `
|
|
188
|
+
<div>
|
|
189
|
+
<h1>My Web Components App</h1>
|
|
190
|
+
<my-counter></my-counter>
|
|
191
|
+
</div>
|
|
192
|
+
`;
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Adding Routing to Your App
|
|
196
|
+
|
|
197
|
+
The library includes a built-in router for client-side navigation. Here's how to set it up:
|
|
198
|
+
|
|
199
|
+
#### 1. Create your route configuration
|
|
200
|
+
|
|
201
|
+
**src/router.ts**
|
|
202
|
+
```typescript
|
|
203
|
+
import { createRouter, type Route } from '@diniz/webcomponents';
|
|
204
|
+
|
|
205
|
+
export const routes: Route[] = [
|
|
206
|
+
{
|
|
207
|
+
path: '/',
|
|
208
|
+
load: () => import('./pages/home'),
|
|
209
|
+
component: 'home-page'
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
path: '/about',
|
|
213
|
+
load: () => import('./pages/about'),
|
|
214
|
+
component: 'about-page'
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
path: '/counter',
|
|
218
|
+
load: () => import('./components/counter'),
|
|
219
|
+
component: 'my-counter'
|
|
220
|
+
}
|
|
221
|
+
];
|
|
222
|
+
|
|
223
|
+
// Initialize the router with your routes
|
|
224
|
+
// The router automatically sets up navigation and loads the initial route
|
|
225
|
+
createRouter(routes);
|
|
226
|
+
|
|
227
|
+
// Optional: specify a custom app container selector (default is '#app')
|
|
228
|
+
// createRouter(routes, '#my-app-container');
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
> **How it works:** The `createRouter()` function sets up the routing system, registers event listeners for navigation, and automatically loads the initial route when the page loads. Navigation happens via links with the `data-link` attribute, and the browser's back/forward buttons work automatically.
|
|
232
|
+
|
|
233
|
+
**Optional: Adding Route Guards**
|
|
234
|
+
|
|
235
|
+
You can protect routes with guard functions that return a boolean or a Promise:
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
import { createRouter, type Route } from '@diniz/webcomponents';
|
|
239
|
+
|
|
240
|
+
// Example: Synchronous guard
|
|
241
|
+
const isAuthenticated = () => {
|
|
242
|
+
return localStorage.getItem('user') !== null;
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// Example: Async guard (e.g., checking with an API)
|
|
246
|
+
const hasPermission = async () => {
|
|
247
|
+
const response = await fetch('/api/check-permission');
|
|
248
|
+
const data = await response.json();
|
|
249
|
+
return data.hasAccess;
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
export const routes: Route[] = [
|
|
253
|
+
{
|
|
254
|
+
path: '/',
|
|
255
|
+
load: () => import('./pages/home'),
|
|
256
|
+
component: 'home-page'
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
path: '/profile',
|
|
260
|
+
load: () => import('./pages/profile'),
|
|
261
|
+
component: 'profile-page',
|
|
262
|
+
guard: isAuthenticated // Redirect to home if guard returns false
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
path: '/admin',
|
|
266
|
+
load: () => import('./pages/admin'),
|
|
267
|
+
component: 'admin-page',
|
|
268
|
+
guard: hasPermission // Supports async guards
|
|
269
|
+
}
|
|
270
|
+
];
|
|
271
|
+
|
|
272
|
+
createRouter(routes);
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
#### 2. Create page components (with optional shared navigation)
|
|
276
|
+
|
|
277
|
+
You can create a reusable navigation component:
|
|
278
|
+
|
|
279
|
+
**src/components/nav.ts**
|
|
280
|
+
```typescript
|
|
281
|
+
import { BaseComponent } from '@diniz/webcomponents';
|
|
282
|
+
|
|
283
|
+
class NavComponent extends BaseComponent {
|
|
284
|
+
connectedCallback() {
|
|
285
|
+
super.connectedCallback();
|
|
286
|
+
this.render();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
render() {
|
|
290
|
+
this.shadowRoot!.innerHTML = `
|
|
291
|
+
<style>
|
|
292
|
+
nav {
|
|
293
|
+
background: var(--color-surface, #1e1e1e);
|
|
294
|
+
padding: 1rem;
|
|
295
|
+
display: flex;
|
|
296
|
+
gap: 1rem;
|
|
297
|
+
margin-bottom: 2rem;
|
|
298
|
+
}
|
|
299
|
+
nav a {
|
|
300
|
+
color: var(--color-text, #fff);
|
|
301
|
+
text-decoration: none;
|
|
302
|
+
padding: 0.5rem 1rem;
|
|
303
|
+
border-radius: 0.25rem;
|
|
304
|
+
}
|
|
305
|
+
nav a:hover {
|
|
306
|
+
background: var(--color-surface-hover, #2a2a2a);
|
|
307
|
+
}
|
|
308
|
+
</style>
|
|
309
|
+
|
|
310
|
+
<nav>
|
|
311
|
+
<a href="/" data-link>Home</a>
|
|
312
|
+
<a href="/about" data-link>About</a>
|
|
313
|
+
<a href="/counter" data-link>Counter</a>
|
|
314
|
+
</nav>
|
|
315
|
+
`;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
customElements.define('app-nav', NavComponent);
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**src/pages/home.ts**
|
|
323
|
+
```typescript
|
|
324
|
+
import { BaseComponent } from '@diniz/webcomponents';
|
|
325
|
+
import '../components/nav';
|
|
326
|
+
|
|
327
|
+
class HomePage extends BaseComponent {
|
|
328
|
+
connectedCallback() {
|
|
329
|
+
super.connectedCallback();
|
|
330
|
+
this.render();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
render() {
|
|
334
|
+
this.shadowRoot!.innerHTML = `
|
|
335
|
+
<app-nav></app-nav>
|
|
336
|
+
<h1>Home Page</h1>
|
|
337
|
+
<p>Welcome to my web components app!</p>
|
|
338
|
+
<ui-button variant="primary">
|
|
339
|
+
<a href="/counter" data-link style="color: inherit; text-decoration: none;">
|
|
340
|
+
Try Counter
|
|
341
|
+
</a>
|
|
342
|
+
</ui-button>
|
|
343
|
+
`;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
customElements.define('home-page', HomePage);
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
**src/pages/about.ts**
|
|
351
|
+
```typescript
|
|
352
|
+
import { BaseComponent } from '@diniz/webcomponents';
|
|
353
|
+
import '../components/nav';
|
|
354
|
+
|
|
355
|
+
class AboutPage extends BaseComponent {
|
|
356
|
+
connectedCallback() {
|
|
357
|
+
super.connectedCallback();
|
|
358
|
+
this.render();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
render() {
|
|
362
|
+
this.shadowRoot!.innerHTML = `
|
|
363
|
+
<app-nav></app-nav>
|
|
364
|
+
<h1>About</h1>
|
|
365
|
+
<p>This is a Vite app using web components with routing.</p>
|
|
366
|
+
`;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
customElements.define('about-page', AboutPage);
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
#### 3. Initialize the router in main.ts
|
|
374
|
+
|
|
375
|
+
**src/main.ts**
|
|
376
|
+
```typescript
|
|
377
|
+
import '@diniz/webcomponents';
|
|
378
|
+
import '@diniz/webcomponents/dist/style.css';
|
|
379
|
+
import './style.css';
|
|
380
|
+
import './router'; // This loads the routes and initializes routing
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
The moment you import `./router`, the routing system:
|
|
384
|
+
1. ✅ Registers all event listeners for navigation
|
|
385
|
+
2. ✅ Automatically loads the initial route based on the current URL
|
|
386
|
+
3. ✅ Starts handling clicks on `[data-link]` elements
|
|
387
|
+
4. ✅ Enables browser back/forward button support
|
|
388
|
+
|
|
389
|
+
That's it! Your app now has client-side routing with:
|
|
390
|
+
- ✅ Lazy-loaded pages
|
|
391
|
+
- ✅ Browser back/forward navigation
|
|
392
|
+
- ✅ Declarative routing with `data-link` attribute
|
|
393
|
+
- ✅ Optional route guards for protected pages
|
|
394
|
+
- ✅ Reusable navigation component
|
|
395
|
+
|
|
50
396
|
## Components
|
|
51
397
|
|
|
52
398
|
- **ui-button** - Button with variants, sizes, icons
|