@diniz/webcomponents 1.0.0 → 1.0.3

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.
Files changed (2) hide show
  1. package/README.md +1001 -0
  2. package/package.json +8 -2
package/README.md ADDED
@@ -0,0 +1,1001 @@
1
+ # @diniz/webcomponents
2
+
3
+ A lightweight, framework-agnostic web components library built with vanilla TypeScript. Create modern, reactive UIs using native Web Components API with zero dependencies.
4
+
5
+ ## Features
6
+
7
+ ✨ **Native Web Components** - Built on standard Custom Elements API
8
+ ⚡ **Reactive Signals** - Built-in signal-based reactivity system
9
+ 🎨 **Theme Support** - CSS custom properties for easy theming
10
+ 📦 **Zero Dependencies** - No framework required
11
+ 🔒 **TypeScript** - Full type safety and IntelliSense support
12
+ 🎯 **Tree-shakeable** - Import only what you need
13
+ ♿ **Accessible** - ARIA attributes and keyboard navigation
14
+
15
+ ## 🚀 Live Demo
16
+
17
+ Check out the interactive demo and component examples:
18
+
19
+ **[View Live Demo →](https://rodiniz.github.io/webcomponents/)**
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install @diniz/webcomponents
25
+ ```
26
+
27
+ ## Using with Vite (No Framework)
28
+
29
+ This library works seamlessly with Vite without requiring any framework. Here's how to set up a vanilla JavaScript/TypeScript project:
30
+
31
+ ### 1. Create a New Vite Project
32
+
33
+ ```bash
34
+ # Create a new Vite project with vanilla TypeScript template
35
+ npm create vite@latest my-app -- --template vanilla-ts
36
+ cd my-app
37
+ npm install
38
+ ```
39
+
40
+ ### 2. Install the Library
41
+
42
+ ```bash
43
+ npm install @diniz/webcomponents
44
+ ```
45
+
46
+ ### 3. Import Components in Your Main File
47
+
48
+ In your `src/main.ts` file:
49
+
50
+ ```typescript
51
+ import '@diniz/webcomponents';
52
+ import '@diniz/webcomponents/dist/style.css'; // Import styles
53
+
54
+ // Now you can use the components in your HTML
55
+ document.querySelector<HTMLDivElement>('#app')!.innerHTML = `
56
+ <div>
57
+ <h1>My Web Components App</h1>
58
+ <ui-button variant="primary" size="md">Click Me</ui-button>
59
+ <ui-date-picker format="DD/MM/YYYY"></ui-date-picker>
60
+ </div>
61
+ `;
62
+ ```
63
+
64
+ ### 4. Use Components in HTML
65
+
66
+ In your `index.html`:
67
+
68
+ ```html
69
+ <!DOCTYPE html>
70
+ <html lang="en">
71
+ <head>
72
+ <meta charset="UTF-8" />
73
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
74
+ <title>My App</title>
75
+ </head>
76
+ <body>
77
+ <div id="app">
78
+ <ui-button variant="primary">Click Me</ui-button>
79
+ <ui-date-picker format="DD/MM/YYYY"></ui-date-picker>
80
+ <ui-table></ui-table>
81
+ </div>
82
+ <script type="module" src="/src/main.ts"></script>
83
+ </body>
84
+ </html>
85
+ ```
86
+
87
+ ### 5. Add Event Listeners (Optional)
88
+
89
+ ```typescript
90
+ // Wait for components to be defined
91
+ customElements.whenDefined('ui-button').then(() => {
92
+ const button = document.querySelector('ui-button');
93
+ button?.addEventListener('click', () => {
94
+ console.log('Button clicked!');
95
+ });
96
+ });
97
+
98
+ // Listen to custom events
99
+ const picker = document.querySelector('ui-date-picker');
100
+ picker?.addEventListener('date-change', ((e: CustomEvent) => {
101
+ console.log('Date selected:', e.detail.value);
102
+ }) as EventListener);
103
+ ```
104
+
105
+ ### 6. TypeScript Support
106
+
107
+ For full TypeScript support, create a `src/types.d.ts` file:
108
+
109
+ ```typescript
110
+ declare module '@diniz/webcomponents' {
111
+ export interface UIButton extends HTMLElement {
112
+ variant: 'primary' | 'secondary' | 'ghost';
113
+ size: 'sm' | 'md' | 'lg';
114
+ icon?: string;
115
+ disabled?: boolean;
116
+ }
117
+
118
+ export interface UIDatePicker extends HTMLElement {
119
+ format: string;
120
+ value: string;
121
+ min?: string;
122
+ max?: string;
123
+ }
124
+
125
+ // Add other component interfaces as needed
126
+ }
127
+
128
+ declare global {
129
+ interface HTMLElementTagNameMap {
130
+ 'ui-button': import('@diniz/webcomponents').UIButton;
131
+ 'ui-date-picker': import('@diniz/webcomponents').UIDatePicker;
132
+ // Add other components as needed
133
+ }
134
+ }
135
+ ```
136
+
137
+ ### 7. Build for Production
138
+
139
+ ```bash
140
+ npm run build
141
+ ```
142
+
143
+ The build output will be in the `dist` folder, ready to deploy to any static hosting service.
144
+
145
+ ### Tree-shaking (Import Only What You Need)
146
+
147
+ You can import individual components to reduce bundle size:
148
+
149
+ ```typescript
150
+ // Import only specific components
151
+ import { UIButton } from '@diniz/webcomponents';
152
+ import '@diniz/webcomponents/dist/style.css';
153
+
154
+ // The component is automatically registered
155
+ // Now you can use <ui-button> in your HTML
156
+ ```
157
+
158
+ ### Configuration Tips
159
+
160
+ **Vite Config** - No special configuration needed! Web Components work out of the box with Vite.
161
+
162
+ **CSS Customization** - Override CSS custom properties to match your theme:
163
+
164
+ ```css
165
+ :root {
166
+ --color-primary: #3b82f6;
167
+ --color-secondary: #8b5cf6;
168
+ --color-success: #10b981;
169
+ --color-danger: #ef4444;
170
+ --color-warning: #f59e0b;
171
+ --color-info: #06b6d4;
172
+
173
+ --radius-sm: 0.25rem;
174
+ --radius-md: 0.375rem;
175
+ --radius-lg: 0.5rem;
176
+ }
177
+ ```
178
+
179
+ ## Quick Start
180
+
181
+ ```html
182
+ <script type="module">
183
+ import '@diniz/webcomponents';
184
+ </script>
185
+
186
+ <ui-button variant="primary">Click Me</ui-button>
187
+ <ui-date-picker format="DD/MM/YYYY"></ui-date-picker>
188
+ <ui-table></ui-table>
189
+ ```
190
+
191
+ ## Components
192
+
193
+ ### 🔘 Button (`ui-button`)
194
+
195
+ A versatile button component with multiple variants, sizes, and icon support.
196
+
197
+ **Features:**
198
+ - 3 variants: `primary`, `secondary`, `ghost`
199
+ - 3 sizes: `sm`, `md`, `lg`
200
+ - Icon support with [Feather Icons](https://feathericons.com/)
201
+ - Icon positioning (left/right)
202
+ - Icon-only buttons
203
+ - Disabled state support
204
+ - Button type support
205
+ - Smooth transitions and hover effects
206
+
207
+ **Usage:**
208
+ ```html
209
+ <ui-button variant="primary" size="md">Primary Button</ui-button>
210
+ <ui-button variant="secondary" size="sm">Secondary</ui-button>
211
+ <ui-button variant="ghost" disabled>Disabled</ui-button>
212
+
213
+ <!-- With icons -->
214
+ <ui-button variant="primary" icon="check">Save</ui-button>
215
+ <ui-button variant="secondary" icon="trash-2" icon-position="right">Delete</ui-button>
216
+ <ui-button variant="ghost" icon="settings"></ui-button>
217
+ ```
218
+
219
+ **Attributes:**
220
+ - `variant` - Button style (`primary` | `secondary` | `ghost`)
221
+ - `size` - Button size (`sm` | `md` | `lg`)
222
+ - `icon` - Icon name from Feather Icons
223
+ - `icon-position` - Icon position (`left` | `right`, default: `left`)
224
+ - `disabled` - Disable the button
225
+ - `type` - Button type (`button` | `submit` | `reset`)
226
+
227
+ ---
228
+
229
+ ### 📅 Date Picker (`ui-date-picker`)
230
+
231
+ A customizable date picker with multiple format options and calendar support.
232
+
233
+ **Features:**
234
+ - 5 date formats: `YYYY-MM-DD`, `DD/MM/YYYY`, `MM/DD/YYYY`, `DD-MM-YYYY`, `MM-DD-YYYY`
235
+ - Native calendar picker integration
236
+ - Min/max date constraints
237
+ - Text input with format validation
238
+ - Real-time format conversion
239
+ - Disabled state support
240
+ - Custom events for date changes
241
+
242
+ **Usage:**
243
+ ```html
244
+ <ui-date-picker
245
+ format="DD/MM/YYYY"
246
+ value="2026-02-26"
247
+ min="2026-01-01"
248
+ max="2026-12-31"
249
+ ></ui-date-picker>
250
+
251
+ <script>
252
+ const picker = document.querySelector('ui-date-picker');
253
+
254
+ picker.addEventListener('date-change', (e) => {
255
+ console.log('ISO:', e.detail.value);
256
+ console.log('Formatted:', e.detail.formattedValue);
257
+ });
258
+ </script>
259
+ ```
260
+
261
+ **Attributes:**
262
+ - `format` - Date display format
263
+ - `value` - Date value in ISO format (YYYY-MM-DD)
264
+ - `min` - Minimum date (ISO format)
265
+ - `max` - Maximum date (ISO format)
266
+ - `disabled` - Disable the picker
267
+ - `placeholder` - Placeholder text
268
+
269
+ **Methods:**
270
+ - `getISOValue()` - Get date in ISO format
271
+ - `getFormattedValue()` - Get date in display format
272
+ - `setValue(isoDate)` - Set the date value
273
+ - `clear()` - Clear the date
274
+
275
+ **Events:**
276
+ - `date-change` - Fired when date changes
277
+ - `date-input` - Fired during input
278
+
279
+ ---
280
+
281
+ ### 📋 Table (`ui-table`)
282
+
283
+ A dynamic data table with customizable columns and alignment.
284
+
285
+ **Features:**
286
+ - Dynamic column configuration
287
+ - Text alignment per column (left, center, right)
288
+ - Responsive layout
289
+ - Automatic row rendering
290
+ - Theme-aware styling
291
+
292
+ **Usage:**
293
+ ```html
294
+ <ui-table id="myTable"></ui-table>
295
+
296
+ <script type="module">
297
+ const table = document.getElementById('myTable');
298
+
299
+ table.data = {
300
+ columns: [
301
+ { key: 'name', label: 'Name' },
302
+ { key: 'role', label: 'Role' },
303
+ { key: 'score', label: 'Score', align: 'right' }
304
+ ],
305
+ rows: [
306
+ { name: 'Alice', role: 'Admin', score: 95 },
307
+ { name: 'Bob', role: 'User', score: 87 }
308
+ ]
309
+ };
310
+ </script>
311
+ ```
312
+
313
+ **Properties:**
314
+ - `data` - Object with `columns` and `rows`
315
+ - `columns`: Array of `{ key, label, align? }`
316
+ - `rows`: Array of objects matching column keys
317
+
318
+ ---
319
+
320
+ ### 📄 Pagination (`ui-pagination`)
321
+
322
+ Smart pagination component with ellipsis for large page counts.
323
+
324
+ **Features:**
325
+ - Automatic page number generation
326
+ - Smart ellipsis for large page counts
327
+ - Previous/Next navigation
328
+ - "Showing X to Y of Z" info display
329
+ - Disabled states for edge pages
330
+ - Custom events for page changes
331
+ - ARIA labels for accessibility
332
+
333
+ **Usage:**
334
+ ```html
335
+ <ui-pagination
336
+ total="250"
337
+ current-page="5"
338
+ page-size="10"
339
+ ></ui-pagination>
340
+
341
+ <script>
342
+ const pagination = document.querySelector('ui-pagination');
343
+
344
+ pagination.addEventListener('page-change', (e) => {
345
+ console.log('Page:', e.detail.page);
346
+ console.log('Total Pages:', e.detail.totalPages);
347
+ // Load new data...
348
+ });
349
+ </script>
350
+ ```
351
+
352
+ **Attributes/Properties:**
353
+ - `total` - Total number of items
354
+ - `current-page` - Current page number
355
+ - `page-size` - Items per page (default: 10)
356
+
357
+ **Computed Properties:**
358
+ - `totalPages` - Total number of pages
359
+
360
+ **Events:**
361
+ - `page-change` - Fired when page changes, includes pagination details
362
+
363
+ ---
364
+
365
+ ### 📝 Input (`ui-input`)
366
+
367
+ Advanced form input with built-in validation and error handling.
368
+
369
+ **Features:**
370
+ - Multiple input types: `text`, `email`, `password`, `number`, `tel`, `url`
371
+ - Built-in validation rules:
372
+ - Email domain validation
373
+ - Password matching
374
+ - Min/max length
375
+ - Regex patterns
376
+ - Custom validators
377
+ - Real-time validation feedback
378
+ - Error message display
379
+ - Touched state tracking
380
+ - Disabled state support
381
+
382
+ **Usage:**
383
+ ```html
384
+ <ui-input
385
+ type="email"
386
+ label="Email"
387
+ placeholder="you@example.com"
388
+ required
389
+ validate="emailDomain:company.com"
390
+ ></ui-input>
391
+
392
+ <ui-input
393
+ type="password"
394
+ label="Password"
395
+ minlength="8"
396
+ required
397
+ ></ui-input>
398
+ ```
399
+
400
+ **Attributes:**
401
+ - `type` - Input type
402
+ - `label` - Label text
403
+ - `placeholder` - Placeholder text
404
+ - `required` - Required field
405
+ - `pattern` - Regex pattern
406
+ - `minlength` / `maxlength` - Length constraints
407
+ - `min` / `max` - Number constraints
408
+ - `error-message` - Custom error message
409
+ - `disabled` - Disable input
410
+ - `name` - Form field name
411
+ - `validate` - Validation rule (e.g., `emailDomain:company.com`)
412
+
413
+ **State:**
414
+ - `value` - Current input value
415
+ - `valid` - Validation state
416
+ - `touched` - Whether field has been interacted with
417
+ - `error` - Current error message
418
+
419
+ ---
420
+
421
+ ### 🪟 Modal (`ui-modal`)
422
+
423
+ Responsive modal dialog with customizable sizes and behaviors.
424
+
425
+ **Features:**
426
+ - 5 size options: `sm`, `md`, `lg`, `xl`, `full`
427
+ - Auto-close on Escape key (configurable)
428
+ - Auto-close on backdrop click (configurable)
429
+ - Smooth animations (fade in, slide up)
430
+ - Header, body, and footer slots
431
+ - Programmatic open/close API
432
+ - Custom events
433
+ - Body scroll lock when open
434
+
435
+ **Usage:**
436
+ ```html
437
+ <ui-button id="openModal">Open Modal</ui-button>
438
+
439
+ <ui-modal id="myModal" title="Welcome!" size="md">
440
+ <p>This is the modal content.</p>
441
+ <p>You can include any HTML here.</p>
442
+
443
+ <div slot="footer">
444
+ <ui-button id="closeBtn" variant="secondary">Cancel</ui-button>
445
+ <ui-button id="confirmBtn" variant="primary">Confirm</ui-button>
446
+ </div>
447
+ </ui-modal>
448
+
449
+ <script>
450
+ const modal = document.getElementById('myModal');
451
+ const openBtn = document.getElementById('openModal');
452
+ const closeBtn = document.getElementById('closeBtn');
453
+
454
+ openBtn.addEventListener('click', () => modal.open());
455
+ closeBtn.addEventListener('click', () => modal.close());
456
+
457
+ modal.addEventListener('modal-close', () => {
458
+ console.log('Modal closed');
459
+ });
460
+ </script>
461
+ ```
462
+
463
+ **Attributes:**
464
+ - `title` - Modal title text
465
+ - `size` - Modal size (`sm` | `md` | `lg` | `xl` | `full`)
466
+ - `open` - Open state attribute
467
+ - `no-close-on-escape` - Disable closing on Escape key
468
+ - `no-close-on-backdrop` - Disable closing on backdrop click
469
+
470
+ **Methods:**
471
+ - `open()` - Open the modal
472
+ - `close()` - Close the modal
473
+
474
+ **Events:**
475
+ - `modal-open` - Fired when modal opens
476
+ - `modal-close` - Fired when modal closes
477
+
478
+ ---
479
+
480
+ ### 📋 Select (`ui-select`)
481
+
482
+ Customizable dropdown select with search capability.
483
+
484
+ **Features:**
485
+ - JSON-based options configuration
486
+ - Searchable dropdown (optional)
487
+ - Keyboard navigation
488
+ - Disabled options support
489
+ - Custom placeholder text
490
+ - Change events with full option details
491
+ - Click-outside to close
492
+ - Smooth animations
493
+ - Theme-aware styling
494
+
495
+ **Usage:**
496
+ ```html
497
+ <ui-select
498
+ id="mySelect"
499
+ label="Choose a Country"
500
+ placeholder="Select country..."
501
+ searchable
502
+ ></ui-select>
503
+
504
+ <script>
505
+ const select = document.getElementById('mySelect');
506
+
507
+ // Set options
508
+ const options = [
509
+ { value: 'us', label: 'United States' },
510
+ { value: 'uk', label: 'United Kingdom' },
511
+ { value: 'ca', label: 'Canada' },
512
+ { value: 'au', label: 'Australia', disabled: true }
513
+ ];
514
+
515
+ select.setAttribute('options', JSON.stringify(options));
516
+
517
+ // Set initial value
518
+ select.setAttribute('value', 'us');
519
+
520
+ // Listen for changes
521
+ select.addEventListener('select-change', (e) => {
522
+ console.log('Value:', e.detail.value);
523
+ console.log('Option:', e.detail.option);
524
+ });
525
+ </script>
526
+ ```
527
+
528
+ **Attributes:**
529
+ - `label` - Label text above select
530
+ - `placeholder` - Placeholder when no selection
531
+ - `options` - JSON string of options array
532
+ - `value` - Currently selected value
533
+ - `disabled` - Disable the select
534
+ - `searchable` - Enable search functionality
535
+
536
+ **Option Format:**
537
+ ```typescript
538
+ {
539
+ value: string; // The option value
540
+ label: string; // Display text
541
+ disabled?: boolean; // Optional: disable option
542
+ }
543
+ ```
544
+
545
+ **Events:**
546
+ - `select-change` - Fired when selection changes
547
+ - `detail.value` - Selected value
548
+ - `detail.option` - Full option object
549
+
550
+ ---
551
+
552
+ ### ☑️ Checkbox (`ui-checkbox`)
553
+
554
+ Flexible checkbox with indeterminate state support.
555
+
556
+ **Features:**
557
+ - 3 sizes: `sm`, `md`, `lg`
558
+ - Checked/unchecked states
559
+ - Indeterminate state (useful for "select all")
560
+ - Disabled state
561
+ - Label support (attribute or slot)
562
+ - Programmatic API
563
+ - Custom events
564
+ - Smooth animations and transitions
565
+ - Theme-aware styling
566
+
567
+ **Usage:**
568
+ ```html
569
+ <!-- Basic usage -->
570
+ <ui-checkbox label="Accept terms"></ui-checkbox>
571
+ <ui-checkbox label="Subscribe" checked></ui-checkbox>
572
+ <ui-checkbox label="Disabled" disabled></ui-checkbox>
573
+
574
+ <!-- With sizes -->
575
+ <ui-checkbox label="Small" size="sm"></ui-checkbox>
576
+ <ui-checkbox label="Medium" size="md"></ui-checkbox>
577
+ <ui-checkbox label="Large" size="lg"></ui-checkbox>
578
+
579
+ <!-- Programmatic usage -->
580
+ <ui-checkbox id="myCheckbox" label="Select All"></ui-checkbox>
581
+
582
+ <script>
583
+ const checkbox = document.getElementById('myCheckbox');
584
+
585
+ // Listen for changes
586
+ checkbox.addEventListener('checkbox-change', (e) => {
587
+ console.log('Checked:', e.detail.checked);
588
+ });
589
+
590
+ // Set states programmatically
591
+ checkbox.setChecked(true);
592
+ checkbox.setIndeterminate(true);
593
+ </script>
594
+ ```
595
+
596
+ **Attributes:**
597
+ - `label` - Label text
598
+ - `checked` - Checked state
599
+ - `indeterminate` - Indeterminate state
600
+ - `disabled` - Disable checkbox
601
+ - `size` - Checkbox size (`sm` | `md` | `lg`)
602
+
603
+ **Methods:**
604
+ - `setChecked(checked: boolean)` - Set checked state
605
+ - `setIndeterminate(indeterminate: boolean)` - Set indeterminate state
606
+
607
+ **Events:**
608
+ - `checkbox-change` - Fired when state changes
609
+ - `detail.checked` - New checked state
610
+
611
+ ---
612
+
613
+ ### 🎯 Sidebar (`app-sidebar`)
614
+
615
+ Navigation sidebar component with links.
616
+
617
+ **Features:**
618
+ - Workspace navigation
619
+ - Active link highlighting (via routing)
620
+ - Theme-aware styling
621
+
622
+ **Usage:**
623
+ ```html
624
+ <app-sidebar></app-sidebar>
625
+ ```
626
+
627
+ ---
628
+
629
+ ### 📐 Layout (`app-layout`)
630
+
631
+ Application layout wrapper with navigation and sidebar.
632
+
633
+ **Features:**
634
+ - Top navigation bar
635
+ - Sidebar integration
636
+ - Main content area with slot
637
+ - Responsive layout
638
+
639
+ **Usage:**
640
+ ```html
641
+ <app-layout>
642
+ <your-page-component></your-page-component>
643
+ </app-layout>
644
+ ```
645
+
646
+ ---
647
+
648
+ ## Core Features
649
+
650
+ ### Base Component
651
+
652
+ All components extend `BaseComponent` which provides:
653
+
654
+ **Signal-based Reactivity:**
655
+ ```typescript
656
+ class MyComponent extends BaseComponent {
657
+ private count = this.useSignal(0);
658
+
659
+ connectedCallback() {
660
+ super.connectedCallback();
661
+ // count.set() automatically triggers re-render
662
+ this.count.set(this.count.get() + 1);
663
+ }
664
+ }
665
+ ```
666
+
667
+ **State Management:**
668
+ ```typescript
669
+ class MyComponent extends BaseComponent<{ user: string }> {
670
+ constructor() {
671
+ super();
672
+ this.state = { user: '' };
673
+ }
674
+
675
+ updateUser() {
676
+ this.setState({ user: 'Alice' }); // Triggers re-render
677
+ }
678
+ }
679
+ ```
680
+
681
+ ### Router
682
+
683
+ Built-in client-side router with layouts:
684
+
685
+ ```typescript
686
+ const routes = [
687
+ {
688
+ path: '/',
689
+ layout: 'app-layout',
690
+ load: () => import('./features/home/home-page'),
691
+ component: 'home-page'
692
+ }
693
+ ];
694
+ ```
695
+
696
+ ### Store
697
+
698
+ Global state management:
699
+
700
+ ```typescript
701
+ import { store } from './core/store';
702
+
703
+ store.setState({ theme: 'dark' });
704
+ const currentState = store.getState();
705
+
706
+ store.subscribe(state => {
707
+ console.log('State changed:', state);
708
+ });
709
+ ```
710
+
711
+ ---
712
+
713
+ ## Theming
714
+
715
+ All components use CSS custom properties for easy theming:
716
+
717
+ ```css
718
+ :root {
719
+ --color-primary: #24ec71;
720
+ --color-primary-contrast: #ffffff;
721
+ --color-ink: #0f172a;
722
+ --color-muted: #f1f5f9;
723
+ --color-border: #e2e8f0;
724
+ --radius-md: 12px;
725
+ --radius-pill: 999px;
726
+ }
727
+ ```
728
+
729
+ ---
730
+
731
+ ## Browser Support
732
+
733
+ - ✅ Chrome/Edge (latest)
734
+ - ✅ Firefox (latest)
735
+ - ✅ Safari (latest)
736
+ - ✅ All modern browsers with Custom Elements support
737
+
738
+ ---
739
+
740
+ ## Development
741
+
742
+ ```bash
743
+ # Install dependencies
744
+ npm install
745
+
746
+ # Start dev server
747
+ npm run dev
748
+
749
+ # Build library
750
+ npm run build:lib
751
+
752
+ # Build production app
753
+ npm run build:prod
754
+ ```
755
+
756
+ ---
757
+
758
+ ## Project Structure
759
+
760
+ ```
761
+ src/
762
+ ├── core/
763
+ │ ├── base-component.ts # Base class with signals
764
+ │ ├── router.ts # Client-side routing
765
+ │ └── store.ts # Global state management
766
+ ├── shared/
767
+ │ └── components/ # Reusable UI components
768
+ │ ├── button.ts
769
+ │ ├── checkbox.ts
770
+ │ ├── date-picker.ts
771
+ │ ├── input.ts
772
+ │ ├── modal.ts
773
+ │ ├── pagination.ts
774
+ │ ├── select.ts
775
+ │ └── table.ts
776
+ ├── layouts/
777
+ │ └── app-layout.ts # Application shell
778
+ ├── features/ # Page components
779
+ └── styles/
780
+ └── theme.css # Global theme variables
781
+ ```
782
+
783
+ ---
784
+
785
+ ## Examples
786
+
787
+ ### Form with Validation
788
+
789
+ ```html
790
+ <form id="myForm">
791
+ <ui-input
792
+ type="email"
793
+ name="email"
794
+ label="Email"
795
+ required
796
+ ></ui-input>
797
+
798
+ <ui-input
799
+ type="password"
800
+ name="password"
801
+ label="Password"
802
+ minlength="8"
803
+ required
804
+ ></ui-input>
805
+
806
+ <ui-button type="submit">Submit</ui-button>
807
+ </form>
808
+ ```
809
+
810
+ ### Data Table with Pagination
811
+
812
+ ```html
813
+ <ui-table id="dataTable"></ui-table>
814
+ <ui-pagination id="pagination" page-size="10"></ui-pagination>
815
+
816
+ <script type="module">
817
+ const table = document.getElementById('dataTable');
818
+ const pagination = document.getElementById('pagination');
819
+
820
+ async function loadData(page = 1) {
821
+ const response = await fetch(`/api/data?page=${page}`);
822
+ const data = await response.json();
823
+
824
+ table.data = {
825
+ columns: [
826
+ { key: 'name', label: 'Name' },
827
+ { key: 'email', label: 'Email' }
828
+ ],
829
+ rows: data.items
830
+ };
831
+
832
+ pagination.total = data.total;
833
+ pagination.currentPage = page;
834
+ }
835
+
836
+ pagination.addEventListener('page-change', (e) => {
837
+ loadData(e.detail.page);
838
+ });
839
+
840
+ loadData(1);
841
+ </script>
842
+ ```
843
+
844
+ ### Date Range Picker
845
+
846
+ ```html
847
+ <ui-date-picker id="startDate" format="DD/MM/YYYY"></ui-date-picker>
848
+ <ui-date-picker id="endDate" format="DD/MM/YYYY"></ui-date-picker>
849
+
850
+ <script>
851
+ const start = document.getElementById('startDate');
852
+ const end = document.getElementById('endDate');
853
+
854
+ start.addEventListener('date-change', (e) => {
855
+ end.setAttribute('min', e.detail.value);
856
+ });
857
+
858
+ end.addEventListener('date-change', (e) => {
859
+ start.setAttribute('max', e.detail.value);
860
+ });
861
+ </script>
862
+ ```
863
+
864
+ ### Confirmation Modal
865
+
866
+ ```html
867
+ <ui-button id="deleteBtn" variant="primary" icon="trash-2">
868
+ Delete Item
869
+ </ui-button>
870
+
871
+ <ui-modal id="confirmModal" title="Confirm Delete" size="sm">
872
+ <p>Are you sure you want to delete this item?</p>
873
+ <p style="color: #ef4444;">This action cannot be undone.</p>
874
+
875
+ <div slot="footer">
876
+ <ui-button id="cancelBtn" variant="ghost">Cancel</ui-button>
877
+ <ui-button id="confirmBtn" variant="primary">Delete</ui-button>
878
+ </div>
879
+ </ui-modal>
880
+
881
+ <script>
882
+ const deleteBtn = document.getElementById('deleteBtn');
883
+ const modal = document.getElementById('confirmModal');
884
+ const cancelBtn = document.getElementById('cancelBtn');
885
+ const confirmBtn = document.getElementById('confirmBtn');
886
+
887
+ deleteBtn.addEventListener('click', () => modal.open());
888
+ cancelBtn.addEventListener('click', () => modal.close());
889
+ confirmBtn.addEventListener('click', () => {
890
+ // Perform delete action
891
+ console.log('Item deleted');
892
+ modal.close();
893
+ });
894
+ </script>
895
+ ```
896
+
897
+ ### Dynamic Form with Select
898
+
899
+ ```html
900
+ <form id="userForm">
901
+ <ui-select
902
+ id="roleSelect"
903
+ label="User Role"
904
+ placeholder="Select role..."
905
+ ></ui-select>
906
+
907
+ <ui-select
908
+ id="countrySelect"
909
+ label="Country"
910
+ placeholder="Select country..."
911
+ searchable
912
+ ></ui-select>
913
+
914
+ <ui-button type="submit" variant="primary" icon="check">
915
+ Create User
916
+ </ui-button>
917
+ </form>
918
+
919
+ <script>
920
+ const roleSelect = document.getElementById('roleSelect');
921
+ const countrySelect = document.getElementById('countrySelect');
922
+
923
+ // Set options
924
+ roleSelect.setAttribute('options', JSON.stringify([
925
+ { value: 'admin', label: 'Administrator' },
926
+ { value: 'user', label: 'User' },
927
+ { value: 'guest', label: 'Guest' }
928
+ ]));
929
+
930
+ countrySelect.setAttribute('options', JSON.stringify([
931
+ { value: 'us', label: 'United States' },
932
+ { value: 'uk', label: 'United Kingdom' },
933
+ { value: 'ca', label: 'Canada' }
934
+ ]));
935
+
936
+ document.getElementById('userForm').addEventListener('submit', (e) => {
937
+ e.preventDefault();
938
+ const role = roleSelect.getAttribute('value');
939
+ const country = countrySelect.getAttribute('value');
940
+ console.log({ role, country });
941
+ });
942
+ </script>
943
+ ```
944
+
945
+ ### Select All with Checkboxes
946
+
947
+ ```html
948
+ <ui-checkbox id="selectAll" label="Select All"></ui-checkbox>
949
+
950
+ <div style="margin-left: 2rem;">
951
+ <ui-checkbox class="item" label="Item 1" size="sm"></ui-checkbox>
952
+ <ui-checkbox class="item" label="Item 2" size="sm"></ui-checkbox>
953
+ <ui-checkbox class="item" label="Item 3" size="sm"></ui-checkbox>
954
+ </div>
955
+
956
+ <script>
957
+ const selectAll = document.getElementById('selectAll');
958
+ const items = document.querySelectorAll('.item');
959
+
960
+ // Update select all based on items
961
+ function updateSelectAll() {
962
+ const checkedCount = Array.from(items).filter(
963
+ item => item.hasAttribute('checked')
964
+ ).length;
965
+
966
+ if (checkedCount === 0) {
967
+ selectAll.setChecked(false);
968
+ } else if (checkedCount === items.length) {
969
+ selectAll.setChecked(true);
970
+ } else {
971
+ selectAll.setIndeterminate(true);
972
+ }
973
+ }
974
+
975
+ // Handle select all click
976
+ selectAll.addEventListener('checkbox-change', (e) => {
977
+ items.forEach(item => item.setChecked(e.detail.checked));
978
+ });
979
+
980
+ // Handle individual item clicks
981
+ items.forEach(item => {
982
+ item.addEventListener('checkbox-change', updateSelectAll);
983
+ });
984
+
985
+ updateSelectAll();
986
+ </script>
987
+ ```
988
+
989
+ ---
990
+
991
+ ## Contributing
992
+
993
+ Contributions are welcome! Please feel free to submit a Pull Request.
994
+
995
+ ---
996
+
997
+ ## License
998
+
999
+ MIT © Rodrigo Diniz
1000
+
1001
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diniz/webcomponents",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "description": "Lightweight web components library",
5
5
  "type": "module",
6
6
  "main": "./dist/webcomponents.umd.js",
@@ -25,6 +25,7 @@
25
25
  "preview:prod": "vite preview --port 4173"
26
26
  },
27
27
  "devDependencies": {
28
+ "@types/feather-icons": "^4.29.4",
28
29
  "@types/node": "^25.3.1",
29
30
  "typescript": "^5.4.0",
30
31
  "vite": "^5.2.0"
@@ -37,5 +38,10 @@
37
38
  "ui-components",
38
39
  "custom-elements"
39
40
  ],
40
- "license": "MIT"
41
+ "license": "MIT",
42
+ "author": "Rodrigo Diniz",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/rodiniz/webcomponents"
46
+ }
41
47
  }