@diniz/webcomponents 1.1.2 → 1.1.4
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 +959 -959
- package/dist/README.md +959 -0
- package/dist/style.css +1256 -0
- package/dist/webcomponents.es.js +1721 -0
- package/dist/webcomponents.umd.js +326 -0
- package/package.json +1 -1
- package/dist/assets/checkbox-demo-page-C-tPBGML.js +0 -152
- package/dist/assets/dashboard-page-DG9XqSlN.js +0 -34
- package/dist/assets/date-picker-demo-BFqT8vXb.js +0 -146
- package/dist/assets/index-BiVNQ5Fp.js +0 -343
- package/dist/assets/index-DFxj0POj.css +0 -1
- package/dist/assets/input-demo-C8SDvXHc.js +0 -113
- package/dist/assets/modal-demo-page-DdQtx_aK.js +0 -164
- package/dist/assets/modal-h8khzJNk.js +0 -36
- package/dist/assets/select-demo-page-yyBh86Yw.js +0 -186
- package/dist/assets/table-DAobVRE7.js +0 -12
- package/dist/assets/table-demo-Dv1cyM0s.js +0 -74
- package/dist/assets/tabs-demo-BZZ6mTYB.js +0 -68
- package/dist/assets/vendor-BvJLUv9i.js +0 -5
- package/dist/index.html +0 -35
package/dist/README.md
ADDED
|
@@ -0,0 +1,959 @@
|
|
|
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
|
+
**Update `src/main.ts`:**
|
|
179
|
+
```typescript
|
|
180
|
+
import '@diniz/webcomponents';
|
|
181
|
+
import '@diniz/webcomponents/dist/style.css';
|
|
182
|
+
|
|
183
|
+
document.querySelector<HTMLDivElement>('#app')!.innerHTML = `
|
|
184
|
+
<div>
|
|
185
|
+
<h1>My Web Components App</h1>
|
|
186
|
+
<ui-button variant="primary">Click Me</ui-button>
|
|
187
|
+
<ui-date-picker format="DD/MM/YYYY"></ui-date-picker>
|
|
188
|
+
<ui-table id="myTable"></ui-table>
|
|
189
|
+
</div>
|
|
190
|
+
`;
|
|
191
|
+
|
|
192
|
+
// Add some data to the table
|
|
193
|
+
const table = document.getElementById('myTable') as any;
|
|
194
|
+
table.data = {
|
|
195
|
+
columns: [
|
|
196
|
+
{ key: 'name', label: 'Name' },
|
|
197
|
+
{ key: 'role', label: 'Role' }
|
|
198
|
+
],
|
|
199
|
+
rows: [
|
|
200
|
+
{ name: 'Alice', role: 'Admin' },
|
|
201
|
+
{ name: 'Bob', role: 'User' }
|
|
202
|
+
]
|
|
203
|
+
};
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Using via CDN or Direct Import
|
|
207
|
+
|
|
208
|
+
```html
|
|
209
|
+
<script type="module">
|
|
210
|
+
import '@diniz/webcomponents';
|
|
211
|
+
</script>
|
|
212
|
+
|
|
213
|
+
<ui-button variant="primary">Click Me</ui-button>
|
|
214
|
+
<ui-date-picker format="DD/MM/YYYY"></ui-date-picker>
|
|
215
|
+
<ui-table></ui-table>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Components
|
|
219
|
+
|
|
220
|
+
### 🔘 Button (`ui-button`)
|
|
221
|
+
|
|
222
|
+
A versatile button component with multiple variants, sizes, and icon support.
|
|
223
|
+
|
|
224
|
+
**Features:**
|
|
225
|
+
- 3 variants: `primary`, `secondary`, `ghost`
|
|
226
|
+
- 3 sizes: `sm`, `md`, `lg`
|
|
227
|
+
- Icon support with [Feather Icons](https://feathericons.com/)
|
|
228
|
+
- Icon positioning (left/right)
|
|
229
|
+
- Icon-only buttons
|
|
230
|
+
- Disabled state support
|
|
231
|
+
- Button type support
|
|
232
|
+
- Smooth transitions and hover effects
|
|
233
|
+
|
|
234
|
+
**Usage:**
|
|
235
|
+
```html
|
|
236
|
+
<ui-button variant="primary" size="md">Primary Button</ui-button>
|
|
237
|
+
<ui-button variant="secondary" size="sm">Secondary</ui-button>
|
|
238
|
+
<ui-button variant="ghost" disabled>Disabled</ui-button>
|
|
239
|
+
|
|
240
|
+
<!-- With icons -->
|
|
241
|
+
<ui-button variant="primary" icon="check">Save</ui-button>
|
|
242
|
+
<ui-button variant="secondary" icon="trash-2" icon-position="right">Delete</ui-button>
|
|
243
|
+
<ui-button variant="ghost" icon="settings"></ui-button>
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
**Attributes:**
|
|
247
|
+
- `variant` - Button style (`primary` | `secondary` | `ghost`)
|
|
248
|
+
- `size` - Button size (`sm` | `md` | `lg`)
|
|
249
|
+
- `icon` - Icon name from Feather Icons
|
|
250
|
+
- `icon-position` - Icon position (`left` | `right`, default: `left`)
|
|
251
|
+
- `disabled` - Disable the button
|
|
252
|
+
- `type` - Button type (`button` | `submit` | `reset`)
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
### 📅 Date Picker (`ui-date-picker`)
|
|
257
|
+
|
|
258
|
+
A customizable date picker with multiple format options and calendar support.
|
|
259
|
+
|
|
260
|
+
**Features:**
|
|
261
|
+
- 5 date formats: `YYYY-MM-DD`, `DD/MM/YYYY`, `MM/DD/YYYY`, `DD-MM-YYYY`, `MM-DD-YYYY`
|
|
262
|
+
- Native calendar picker integration
|
|
263
|
+
- Min/max date constraints
|
|
264
|
+
- Text input with format validation
|
|
265
|
+
- Real-time format conversion
|
|
266
|
+
- Disabled state support
|
|
267
|
+
- Custom events for date changes
|
|
268
|
+
|
|
269
|
+
**Usage:**
|
|
270
|
+
```html
|
|
271
|
+
<ui-date-picker
|
|
272
|
+
format="DD/MM/YYYY"
|
|
273
|
+
value="2026-02-26"
|
|
274
|
+
min="2026-01-01"
|
|
275
|
+
max="2026-12-31"
|
|
276
|
+
></ui-date-picker>
|
|
277
|
+
|
|
278
|
+
<script>
|
|
279
|
+
const picker = document.querySelector('ui-date-picker');
|
|
280
|
+
|
|
281
|
+
picker.addEventListener('date-change', (e) => {
|
|
282
|
+
console.log('ISO:', e.detail.value);
|
|
283
|
+
console.log('Formatted:', e.detail.formattedValue);
|
|
284
|
+
});
|
|
285
|
+
</script>
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**Attributes:**
|
|
289
|
+
- `format` - Date display format
|
|
290
|
+
- `value` - Date value in ISO format (YYYY-MM-DD)
|
|
291
|
+
- `min` - Minimum date (ISO format)
|
|
292
|
+
- `max` - Maximum date (ISO format)
|
|
293
|
+
- `disabled` - Disable the picker
|
|
294
|
+
- `placeholder` - Placeholder text
|
|
295
|
+
|
|
296
|
+
**Methods:**
|
|
297
|
+
- `getISOValue()` - Get date in ISO format
|
|
298
|
+
- `getFormattedValue()` - Get date in display format
|
|
299
|
+
- `setValue(isoDate)` - Set the date value
|
|
300
|
+
- `clear()` - Clear the date
|
|
301
|
+
|
|
302
|
+
**Events:**
|
|
303
|
+
- `date-change` - Fired when date changes
|
|
304
|
+
- `date-input` - Fired during input
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
### 📋 Table (`ui-table`)
|
|
309
|
+
|
|
310
|
+
A dynamic data table with customizable columns and alignment.
|
|
311
|
+
|
|
312
|
+
**Features:**
|
|
313
|
+
- Dynamic column configuration
|
|
314
|
+
- Text alignment per column (left, center, right)
|
|
315
|
+
- Responsive layout
|
|
316
|
+
- Automatic row rendering
|
|
317
|
+
- Theme-aware styling
|
|
318
|
+
|
|
319
|
+
**Usage:**
|
|
320
|
+
```html
|
|
321
|
+
<ui-table id="myTable"></ui-table>
|
|
322
|
+
|
|
323
|
+
<script type="module">
|
|
324
|
+
const table = document.getElementById('myTable');
|
|
325
|
+
|
|
326
|
+
table.data = {
|
|
327
|
+
columns: [
|
|
328
|
+
{ key: 'name', label: 'Name' },
|
|
329
|
+
{ key: 'role', label: 'Role' },
|
|
330
|
+
{ key: 'score', label: 'Score', align: 'right' }
|
|
331
|
+
],
|
|
332
|
+
rows: [
|
|
333
|
+
{ name: 'Alice', role: 'Admin', score: 95 },
|
|
334
|
+
{ name: 'Bob', role: 'User', score: 87 }
|
|
335
|
+
]
|
|
336
|
+
};
|
|
337
|
+
</script>
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**Properties:**
|
|
341
|
+
- `data` - Object with `columns` and `rows`
|
|
342
|
+
- `columns`: Array of `{ key, label, align? }`
|
|
343
|
+
- `rows`: Array of objects matching column keys
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
### 📄 Pagination (`ui-pagination`)
|
|
348
|
+
|
|
349
|
+
Smart pagination component with ellipsis for large page counts.
|
|
350
|
+
|
|
351
|
+
**Features:**
|
|
352
|
+
- Automatic page number generation
|
|
353
|
+
- Smart ellipsis for large page counts
|
|
354
|
+
- Previous/Next navigation
|
|
355
|
+
- "Showing X to Y of Z" info display
|
|
356
|
+
- Disabled states for edge pages
|
|
357
|
+
- Custom events for page changes
|
|
358
|
+
- ARIA labels for accessibility
|
|
359
|
+
|
|
360
|
+
**Usage:**
|
|
361
|
+
```html
|
|
362
|
+
<ui-pagination
|
|
363
|
+
total="250"
|
|
364
|
+
current-page="5"
|
|
365
|
+
page-size="10"
|
|
366
|
+
></ui-pagination>
|
|
367
|
+
|
|
368
|
+
<script>
|
|
369
|
+
const pagination = document.querySelector('ui-pagination');
|
|
370
|
+
|
|
371
|
+
pagination.addEventListener('page-change', (e) => {
|
|
372
|
+
console.log('Page:', e.detail.page);
|
|
373
|
+
console.log('Total Pages:', e.detail.totalPages);
|
|
374
|
+
// Load new data...
|
|
375
|
+
});
|
|
376
|
+
</script>
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
**Attributes/Properties:**
|
|
380
|
+
- `total` - Total number of items
|
|
381
|
+
- `current-page` - Current page number
|
|
382
|
+
- `page-size` - Items per page (default: 10)
|
|
383
|
+
|
|
384
|
+
**Computed Properties:**
|
|
385
|
+
- `totalPages` - Total number of pages
|
|
386
|
+
|
|
387
|
+
**Events:**
|
|
388
|
+
- `page-change` - Fired when page changes, includes pagination details
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
### 📝 Input (`ui-input`)
|
|
393
|
+
|
|
394
|
+
Advanced form input with built-in validation and error handling.
|
|
395
|
+
|
|
396
|
+
**Features:**
|
|
397
|
+
- Multiple input types: `text`, `email`, `password`, `number`, `tel`, `url`
|
|
398
|
+
- Built-in validation rules:
|
|
399
|
+
- Email domain validation
|
|
400
|
+
- Password matching
|
|
401
|
+
- Min/max length
|
|
402
|
+
- Regex patterns
|
|
403
|
+
- Custom validators
|
|
404
|
+
- Real-time validation feedback
|
|
405
|
+
- Error message display
|
|
406
|
+
- Touched state tracking
|
|
407
|
+
- Disabled state support
|
|
408
|
+
|
|
409
|
+
**Usage:**
|
|
410
|
+
```html
|
|
411
|
+
<ui-input
|
|
412
|
+
type="email"
|
|
413
|
+
label="Email"
|
|
414
|
+
placeholder="you@example.com"
|
|
415
|
+
required
|
|
416
|
+
validate="emailDomain:company.com"
|
|
417
|
+
></ui-input>
|
|
418
|
+
|
|
419
|
+
<ui-input
|
|
420
|
+
type="password"
|
|
421
|
+
label="Password"
|
|
422
|
+
minlength="8"
|
|
423
|
+
required
|
|
424
|
+
></ui-input>
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
**Attributes:**
|
|
428
|
+
- `type` - Input type
|
|
429
|
+
- `label` - Label text
|
|
430
|
+
- `placeholder` - Placeholder text
|
|
431
|
+
- `required` - Required field
|
|
432
|
+
- `pattern` - Regex pattern
|
|
433
|
+
- `minlength` / `maxlength` - Length constraints
|
|
434
|
+
- `min` / `max` - Number constraints
|
|
435
|
+
- `error-message` - Custom error message
|
|
436
|
+
- `disabled` - Disable input
|
|
437
|
+
- `name` - Form field name
|
|
438
|
+
- `validate` - Validation rule (e.g., `emailDomain:company.com`)
|
|
439
|
+
|
|
440
|
+
**State:**
|
|
441
|
+
- `value` - Current input value
|
|
442
|
+
- `valid` - Validation state
|
|
443
|
+
- `touched` - Whether field has been interacted with
|
|
444
|
+
- `error` - Current error message
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
### 🪟 Modal (`ui-modal`)
|
|
449
|
+
|
|
450
|
+
Responsive modal dialog with customizable sizes and behaviors.
|
|
451
|
+
|
|
452
|
+
**Features:**
|
|
453
|
+
- 5 size options: `sm`, `md`, `lg`, `xl`, `full`
|
|
454
|
+
- Auto-close on Escape key (configurable)
|
|
455
|
+
- Auto-close on backdrop click (configurable)
|
|
456
|
+
- Smooth animations (fade in, slide up)
|
|
457
|
+
- Header, body, and footer slots
|
|
458
|
+
- Programmatic open/close API
|
|
459
|
+
- Custom events
|
|
460
|
+
- Body scroll lock when open
|
|
461
|
+
|
|
462
|
+
**Usage:**
|
|
463
|
+
```html
|
|
464
|
+
<ui-button id="openModal">Open Modal</ui-button>
|
|
465
|
+
|
|
466
|
+
<ui-modal id="myModal" title="Welcome!" size="md">
|
|
467
|
+
<p>This is the modal content.</p>
|
|
468
|
+
<p>You can include any HTML here.</p>
|
|
469
|
+
|
|
470
|
+
<div slot="footer">
|
|
471
|
+
<ui-button id="closeBtn" variant="secondary">Cancel</ui-button>
|
|
472
|
+
<ui-button id="confirmBtn" variant="primary">Confirm</ui-button>
|
|
473
|
+
</div>
|
|
474
|
+
</ui-modal>
|
|
475
|
+
|
|
476
|
+
<script>
|
|
477
|
+
const modal = document.getElementById('myModal');
|
|
478
|
+
const openBtn = document.getElementById('openModal');
|
|
479
|
+
const closeBtn = document.getElementById('closeBtn');
|
|
480
|
+
|
|
481
|
+
openBtn.addEventListener('click', () => modal.open());
|
|
482
|
+
closeBtn.addEventListener('click', () => modal.close());
|
|
483
|
+
|
|
484
|
+
modal.addEventListener('modal-close', () => {
|
|
485
|
+
console.log('Modal closed');
|
|
486
|
+
});
|
|
487
|
+
</script>
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
**Attributes:**
|
|
491
|
+
- `title` - Modal title text
|
|
492
|
+
- `size` - Modal size (`sm` | `md` | `lg` | `xl` | `full`)
|
|
493
|
+
- `open` - Open state attribute
|
|
494
|
+
- `no-close-on-escape` - Disable closing on Escape key
|
|
495
|
+
- `no-close-on-backdrop` - Disable closing on backdrop click
|
|
496
|
+
|
|
497
|
+
**Methods:**
|
|
498
|
+
- `open()` - Open the modal
|
|
499
|
+
- `close()` - Close the modal
|
|
500
|
+
|
|
501
|
+
**Events:**
|
|
502
|
+
- `modal-open` - Fired when modal opens
|
|
503
|
+
- `modal-close` - Fired when modal closes
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
|
|
507
|
+
### 📋 Select (`ui-select`)
|
|
508
|
+
|
|
509
|
+
Customizable dropdown select with search capability.
|
|
510
|
+
|
|
511
|
+
**Features:**
|
|
512
|
+
- JSON-based options configuration
|
|
513
|
+
- Searchable dropdown (optional)
|
|
514
|
+
- Keyboard navigation
|
|
515
|
+
- Disabled options support
|
|
516
|
+
- Custom placeholder text
|
|
517
|
+
- Change events with full option details
|
|
518
|
+
- Click-outside to close
|
|
519
|
+
- Smooth animations
|
|
520
|
+
- Theme-aware styling
|
|
521
|
+
|
|
522
|
+
**Usage:**
|
|
523
|
+
```html
|
|
524
|
+
<ui-select
|
|
525
|
+
id="mySelect"
|
|
526
|
+
label="Choose a Country"
|
|
527
|
+
placeholder="Select country..."
|
|
528
|
+
searchable
|
|
529
|
+
></ui-select>
|
|
530
|
+
|
|
531
|
+
<script>
|
|
532
|
+
const select = document.getElementById('mySelect');
|
|
533
|
+
|
|
534
|
+
// Set options
|
|
535
|
+
const options = [
|
|
536
|
+
{ value: 'us', label: 'United States' },
|
|
537
|
+
{ value: 'uk', label: 'United Kingdom' },
|
|
538
|
+
{ value: 'ca', label: 'Canada' },
|
|
539
|
+
{ value: 'au', label: 'Australia', disabled: true }
|
|
540
|
+
];
|
|
541
|
+
|
|
542
|
+
select.setAttribute('options', JSON.stringify(options));
|
|
543
|
+
|
|
544
|
+
// Set initial value
|
|
545
|
+
select.setAttribute('value', 'us');
|
|
546
|
+
|
|
547
|
+
// Listen for changes
|
|
548
|
+
select.addEventListener('select-change', (e) => {
|
|
549
|
+
console.log('Value:', e.detail.value);
|
|
550
|
+
console.log('Option:', e.detail.option);
|
|
551
|
+
});
|
|
552
|
+
</script>
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
**Attributes:**
|
|
556
|
+
- `label` - Label text above select
|
|
557
|
+
- `placeholder` - Placeholder when no selection
|
|
558
|
+
- `options` - JSON string of options array
|
|
559
|
+
- `value` - Currently selected value
|
|
560
|
+
- `disabled` - Disable the select
|
|
561
|
+
- `searchable` - Enable search functionality
|
|
562
|
+
|
|
563
|
+
**Option Format:**
|
|
564
|
+
```typescript
|
|
565
|
+
{
|
|
566
|
+
value: string; // The option value
|
|
567
|
+
label: string; // Display text
|
|
568
|
+
disabled?: boolean; // Optional: disable option
|
|
569
|
+
}
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
**Events:**
|
|
573
|
+
- `select-change` - Fired when selection changes
|
|
574
|
+
- `detail.value` - Selected value
|
|
575
|
+
- `detail.option` - Full option object
|
|
576
|
+
|
|
577
|
+
---
|
|
578
|
+
|
|
579
|
+
### ☑️ Checkbox (`ui-checkbox`)
|
|
580
|
+
|
|
581
|
+
Flexible checkbox with indeterminate state support.
|
|
582
|
+
|
|
583
|
+
**Features:**
|
|
584
|
+
- 3 sizes: `sm`, `md`, `lg`
|
|
585
|
+
- Checked/unchecked states
|
|
586
|
+
- Indeterminate state (useful for "select all")
|
|
587
|
+
- Disabled state
|
|
588
|
+
- Label support (attribute or slot)
|
|
589
|
+
- Programmatic API
|
|
590
|
+
- Custom events
|
|
591
|
+
- Smooth animations and transitions
|
|
592
|
+
- Theme-aware styling
|
|
593
|
+
|
|
594
|
+
**Usage:**
|
|
595
|
+
```html
|
|
596
|
+
<!-- Basic usage -->
|
|
597
|
+
<ui-checkbox label="Accept terms"></ui-checkbox>
|
|
598
|
+
<ui-checkbox label="Subscribe" checked></ui-checkbox>
|
|
599
|
+
<ui-checkbox label="Disabled" disabled></ui-checkbox>
|
|
600
|
+
|
|
601
|
+
<!-- With sizes -->
|
|
602
|
+
<ui-checkbox label="Small" size="sm"></ui-checkbox>
|
|
603
|
+
<ui-checkbox label="Medium" size="md"></ui-checkbox>
|
|
604
|
+
<ui-checkbox label="Large" size="lg"></ui-checkbox>
|
|
605
|
+
|
|
606
|
+
<!-- Programmatic usage -->
|
|
607
|
+
<ui-checkbox id="myCheckbox" label="Select All"></ui-checkbox>
|
|
608
|
+
|
|
609
|
+
<script>
|
|
610
|
+
const checkbox = document.getElementById('myCheckbox');
|
|
611
|
+
|
|
612
|
+
// Listen for changes
|
|
613
|
+
checkbox.addEventListener('checkbox-change', (e) => {
|
|
614
|
+
console.log('Checked:', e.detail.checked);
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
// Set states programmatically
|
|
618
|
+
checkbox.setChecked(true);
|
|
619
|
+
checkbox.setIndeterminate(true);
|
|
620
|
+
</script>
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
**Attributes:**
|
|
624
|
+
- `label` - Label text
|
|
625
|
+
- `checked` - Checked state
|
|
626
|
+
- `indeterminate` - Indeterminate state
|
|
627
|
+
- `disabled` - Disable checkbox
|
|
628
|
+
- `size` - Checkbox size (`sm` | `md` | `lg`)
|
|
629
|
+
|
|
630
|
+
**Methods:**
|
|
631
|
+
- `setChecked(checked: boolean)` - Set checked state
|
|
632
|
+
- `setIndeterminate(indeterminate: boolean)` - Set indeterminate state
|
|
633
|
+
|
|
634
|
+
**Events:**
|
|
635
|
+
- `checkbox-change` - Fired when state changes
|
|
636
|
+
- `detail.checked` - New checked state
|
|
637
|
+
|
|
638
|
+
---
|
|
639
|
+
|
|
640
|
+
### 🎯 Sidebar (`app-sidebar`)
|
|
641
|
+
|
|
642
|
+
Navigation sidebar component with links.
|
|
643
|
+
|
|
644
|
+
**Features:**
|
|
645
|
+
- Workspace navigation
|
|
646
|
+
- Active link highlighting (via routing)
|
|
647
|
+
- Theme-aware styling
|
|
648
|
+
|
|
649
|
+
**Usage:**
|
|
650
|
+
```html
|
|
651
|
+
<app-sidebar></app-sidebar>
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
---
|
|
655
|
+
|
|
656
|
+
### 📐 Layout (`app-layout`)
|
|
657
|
+
|
|
658
|
+
Application layout wrapper with navigation and sidebar.
|
|
659
|
+
|
|
660
|
+
**Features:**
|
|
661
|
+
- Top navigation bar
|
|
662
|
+
- Sidebar integration
|
|
663
|
+
- Main content area with slot
|
|
664
|
+
- Responsive layout
|
|
665
|
+
|
|
666
|
+
**Usage:**
|
|
667
|
+
```html
|
|
668
|
+
<app-layout>
|
|
669
|
+
<your-page-component></your-page-component>
|
|
670
|
+
</app-layout>
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
---
|
|
674
|
+
|
|
675
|
+
## Core Features
|
|
676
|
+
|
|
677
|
+
### Base Component
|
|
678
|
+
|
|
679
|
+
All components extend `BaseComponent` which provides:
|
|
680
|
+
|
|
681
|
+
**Signal-based Reactivity:**
|
|
682
|
+
```typescript
|
|
683
|
+
class MyComponent extends BaseComponent {
|
|
684
|
+
private count = this.useSignal(0);
|
|
685
|
+
|
|
686
|
+
connectedCallback() {
|
|
687
|
+
super.connectedCallback();
|
|
688
|
+
// count.set() automatically triggers re-render
|
|
689
|
+
this.count.set(this.count.get() + 1);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
**State Management:**
|
|
695
|
+
```typescript
|
|
696
|
+
class MyComponent extends BaseComponent<{ user: string }> {
|
|
697
|
+
constructor() {
|
|
698
|
+
super();
|
|
699
|
+
this.state = { user: '' };
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
updateUser() {
|
|
703
|
+
this.setState({ user: 'Alice' }); // Triggers re-render
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
### Router
|
|
709
|
+
|
|
710
|
+
Built-in client-side router with layouts:
|
|
711
|
+
|
|
712
|
+
```typescript
|
|
713
|
+
const routes = [
|
|
714
|
+
{
|
|
715
|
+
path: '/',
|
|
716
|
+
layout: 'app-layout',
|
|
717
|
+
load: () => import('./features/home/home-page'),
|
|
718
|
+
component: 'home-page'
|
|
719
|
+
}
|
|
720
|
+
];
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
### Store
|
|
724
|
+
|
|
725
|
+
Global state management:
|
|
726
|
+
|
|
727
|
+
```typescript
|
|
728
|
+
import { store } from './core/store';
|
|
729
|
+
|
|
730
|
+
store.setState({ theme: 'dark' });
|
|
731
|
+
const currentState = store.getState();
|
|
732
|
+
|
|
733
|
+
store.subscribe(state => {
|
|
734
|
+
console.log('State changed:', state);
|
|
735
|
+
});
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
### HTTP Client
|
|
739
|
+
|
|
740
|
+
Lightweight HTTP client with interceptor support for API requests:
|
|
741
|
+
|
|
742
|
+
```typescript
|
|
743
|
+
import { http } from '@diniz/webcomponents';
|
|
744
|
+
|
|
745
|
+
// Set base URL for all requests
|
|
746
|
+
http.setBaseURL('https://api.example.com');
|
|
747
|
+
|
|
748
|
+
// Set default headers
|
|
749
|
+
http.setDefaultHeaders({ 'Authorization': 'Bearer token' });
|
|
750
|
+
|
|
751
|
+
// Make requests
|
|
752
|
+
const users = await http.get<User[]>('/users');
|
|
753
|
+
const newUser = await http.post<User>('/users', { name: 'Alice' });
|
|
754
|
+
await http.put(`/users/${id}`, updatedData);
|
|
755
|
+
await http.delete(`/users/${id}`);
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
**Request Interceptors:**
|
|
759
|
+
|
|
760
|
+
```typescript
|
|
761
|
+
// Add auth token to every request
|
|
762
|
+
http.interceptors.request.use((config) => {
|
|
763
|
+
const token = localStorage.getItem('auth_token');
|
|
764
|
+
if (token) {
|
|
765
|
+
config.headers = config.headers || {};
|
|
766
|
+
config.headers['Authorization'] = `Bearer ${token}`;
|
|
767
|
+
}
|
|
768
|
+
return config;
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
// Handle request errors
|
|
772
|
+
http.interceptors.request.use(
|
|
773
|
+
(config) => config,
|
|
774
|
+
(error) => {
|
|
775
|
+
console.error('Request failed:', error);
|
|
776
|
+
throw error;
|
|
777
|
+
}
|
|
778
|
+
);
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
**Response Interceptors:**
|
|
782
|
+
|
|
783
|
+
```typescript
|
|
784
|
+
// Transform response data
|
|
785
|
+
http.interceptors.response.use((response) => {
|
|
786
|
+
// Unwrap API response if it's nested
|
|
787
|
+
if (response.data?.result) {
|
|
788
|
+
response.data = response.data.result;
|
|
789
|
+
}
|
|
790
|
+
return response;
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
// Handle errors globally
|
|
794
|
+
http.interceptors.response.use(
|
|
795
|
+
(response) => response,
|
|
796
|
+
(error) => {
|
|
797
|
+
if (error.response?.status === 401) {
|
|
798
|
+
// Handle unauthorized
|
|
799
|
+
localStorage.removeItem('auth_token');
|
|
800
|
+
window.location.href = '/login';
|
|
801
|
+
}
|
|
802
|
+
throw error;
|
|
803
|
+
}
|
|
804
|
+
);
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
**Methods:**
|
|
808
|
+
|
|
809
|
+
- `get<T>(url, config?)` - GET request
|
|
810
|
+
- `post<T>(url, data?, config?)` - POST request
|
|
811
|
+
- `put<T>(url, data?, config?)` - PUT request
|
|
812
|
+
- `patch<T>(url, data?, config?)` - PATCH request
|
|
813
|
+
- `delete<T>(url, config?)` - DELETE request
|
|
814
|
+
- `head<T>(url, config?)` - HEAD request
|
|
815
|
+
|
|
816
|
+
**Configuration:**
|
|
817
|
+
|
|
818
|
+
```typescript
|
|
819
|
+
interface RequestConfig {
|
|
820
|
+
method?: string;
|
|
821
|
+
headers?: Record<string, string>;
|
|
822
|
+
body?: string | FormData | null;
|
|
823
|
+
timeout?: number; // Default: 30000ms
|
|
824
|
+
}
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
**Features:**
|
|
828
|
+
|
|
829
|
+
- ✅ Request/response interceptors with error handling
|
|
830
|
+
- ✅ Automatic JSON serialization/deserialization
|
|
831
|
+
- ✅ Timeout support (default 30s)
|
|
832
|
+
- ✅ Global headers and base URL configuration
|
|
833
|
+
- ✅ FormData support for file uploads
|
|
834
|
+
- ✅ TypeScript generics for type-safe responses
|
|
835
|
+
- ✅ Automatic error messages with status codes
|
|
836
|
+
|
|
837
|
+
---
|
|
838
|
+
|
|
839
|
+
## Theming
|
|
840
|
+
|
|
841
|
+
All components use CSS custom properties for easy theming:
|
|
842
|
+
|
|
843
|
+
```css
|
|
844
|
+
:root {
|
|
845
|
+
--color-primary: #24ec71;
|
|
846
|
+
--color-primary-contrast: #ffffff;
|
|
847
|
+
--color-ink: #0f172a;
|
|
848
|
+
--color-muted: #f1f5f9;
|
|
849
|
+
--color-border: #e2e8f0;
|
|
850
|
+
--radius-md: 12px;
|
|
851
|
+
--radius-pill: 999px;
|
|
852
|
+
}
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
---
|
|
856
|
+
|
|
857
|
+
## Dependencies
|
|
858
|
+
|
|
859
|
+
### Icons
|
|
860
|
+
|
|
861
|
+
This library uses **[Feather Icons](https://feathericons.com/)** for beautiful, minimal SVG icons. Feather provides a consistent set of 286 icons perfect for UI components.
|
|
862
|
+
|
|
863
|
+
```typescript
|
|
864
|
+
import feather from 'feather-icons';
|
|
865
|
+
|
|
866
|
+
// Use icons in components
|
|
867
|
+
<ui-button icon="plus">Add Item</ui-button>
|
|
868
|
+
<ui-button icon="trash-2" variant="danger">Delete</ui-button>
|
|
869
|
+
```
|
|
870
|
+
|
|
871
|
+
[Browse all available Feather icons →](https://feathericons.com/)
|
|
872
|
+
|
|
873
|
+
---
|
|
874
|
+
|
|
875
|
+
## Bundle Size
|
|
876
|
+
|
|
877
|
+
@diniz/webcomponents is extremely lightweight with zero runtime dependencies:
|
|
878
|
+
|
|
879
|
+
| Package | Size (minified) | Size (gzipped) |
|
|
880
|
+
|---------|-----------------|----------------|
|
|
881
|
+
| **@diniz/webcomponents** | ~45KB | ~12KB |
|
|
882
|
+
| Vue 3 + Router | ~185KB | ~65KB |
|
|
883
|
+
| React 18 + Router | ~245KB | ~85KB |
|
|
884
|
+
| Angular 15 | ~500KB+ | ~150KB+ |
|
|
885
|
+
| Svelte | ~60KB | ~15KB |
|
|
886
|
+
|
|
887
|
+
*Sizes are approximate and vary based on included components and tree-shaking effectiveness*
|
|
888
|
+
|
|
889
|
+
**Why Web Components?**
|
|
890
|
+
- ✅ No framework overhead - use with any framework or vanilla JS
|
|
891
|
+
- ✅ Smaller initial bundle size than traditional frameworks
|
|
892
|
+
- ✅ Progressive enhancement - works without JavaScript
|
|
893
|
+
- ✅ Share components across different projects/frameworks
|
|
894
|
+
- ✅ Built-in browser APIs - no external polyfills needed for modern browsers
|
|
895
|
+
|
|
896
|
+
---
|
|
897
|
+
|
|
898
|
+
## Browser Support
|
|
899
|
+
|
|
900
|
+
- ✅ Chrome/Edge (latest)
|
|
901
|
+
- ✅ Firefox (latest)
|
|
902
|
+
- ✅ Safari (latest)
|
|
903
|
+
- ✅ All modern browsers with Custom Elements support
|
|
904
|
+
|
|
905
|
+
---
|
|
906
|
+
|
|
907
|
+
## Development
|
|
908
|
+
|
|
909
|
+
```bash
|
|
910
|
+
# Install dependencies
|
|
911
|
+
npm install
|
|
912
|
+
|
|
913
|
+
# Start dev server
|
|
914
|
+
npm run dev
|
|
915
|
+
|
|
916
|
+
# Build library
|
|
917
|
+
npm run build:lib
|
|
918
|
+
|
|
919
|
+
# Build production app
|
|
920
|
+
npm run build:prod
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
---
|
|
924
|
+
|
|
925
|
+
## Project Structure
|
|
926
|
+
|
|
927
|
+
```
|
|
928
|
+
src/
|
|
929
|
+
├── core/
|
|
930
|
+
│ ├── base-component.ts # Base class with signals
|
|
931
|
+
│ ├── router.ts # Client-side routing
|
|
932
|
+
│ └── store.ts # Global state management
|
|
933
|
+
├── shared/
|
|
934
|
+
│ └── components/ # Reusable UI components
|
|
935
|
+
│ ├── button.ts
|
|
936
|
+
│ ├── checkbox.ts
|
|
937
|
+
│ ├── date-picker.ts
|
|
938
|
+
│ ├── input.ts
|
|
939
|
+
│ ├── modal.ts
|
|
940
|
+
│ ├── pagination.ts
|
|
941
|
+
│ ├── select.ts
|
|
942
|
+
│ └── table.ts
|
|
943
|
+
├── layouts/
|
|
944
|
+
│ └── app-layout.ts # Application shell
|
|
945
|
+
├── features/ # Page components
|
|
946
|
+
└── styles/
|
|
947
|
+
└── theme.css # Global theme variables
|
|
948
|
+
```
|
|
949
|
+
## Contributing
|
|
950
|
+
|
|
951
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
952
|
+
|
|
953
|
+
---
|
|
954
|
+
|
|
955
|
+
## License
|
|
956
|
+
|
|
957
|
+
MIT © Rodrigo Diniz
|
|
958
|
+
|
|
959
|
+
|