@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 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