valtech-components 4.0.9 → 4.0.10

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
@@ -1,13 +1,13 @@
1
1
  <img style="margin-bottom: 20px;" src="https://myvaltech.s3.us-east-2.amazonaws.com/logo-terminal-rounded.png" width="60px" />
2
2
 
3
- # Valtech Components
3
+ # valtech-components
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/valtech-components.svg)](https://www.npmjs.com/package/valtech-components)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
- Component library for Angular + Ionic + Capacitor apps. **145 components** and **23+ services** for auth, Firebase, i18n, and more.
8
+ Component library for Angular 18 + Ionic 8 apps. 145+ components and 23+ services covering auth, Firebase, i18n, theming, and more.
9
9
 
10
- **[Full documentation → ui.myvaltech.com](https://ui.myvaltech.com)**
10
+ **[Full docs → ui.myvaltech.com](https://ui.myvaltech.com)**
11
11
 
12
12
  ---
13
13
 
@@ -23,11 +23,11 @@ npm install valtech-components
23
23
  npm install @angular/common @angular/core @ionic/angular ionicons rxjs
24
24
  ```
25
25
 
26
- **Optional — only if you use these features:**
26
+ **Optional — only install if you use these features:**
27
27
 
28
28
  ```bash
29
29
  npm install @angular/fire firebase # Firebase services
30
- npm install @capacitor/app @capacitor/clipboard # Native features
30
+ npm install @capacitor/app @capacitor/clipboard # Native (Capacitor) features
31
31
  npm install qr-code-styling # QR code component
32
32
  npm install prismjs # Code highlighting
33
33
  npm install swiper # Carousel component
@@ -35,11 +35,11 @@ npm install swiper # Carousel component
35
35
 
36
36
  ---
37
37
 
38
- ## Usage
38
+ ## Quick start
39
39
 
40
- ### UI only (no backend needed)
40
+ ### UI only (no backend required)
41
41
 
42
- 144 of 145 components work standalone — no Firebase, no backend:
42
+ 144 of 145 components work standalone:
43
43
 
44
44
  ```typescript
45
45
  import { ButtonComponent, CardComponent, TextComponent } from 'valtech-components';
@@ -50,7 +50,7 @@ import { ButtonComponent, CardComponent, TextComponent } from 'valtech-component
50
50
  template: `
51
51
  <val-card [props]="{ title: 'My Card' }">
52
52
  <val-text [props]="{ content: 'Hello World' }" />
53
- <val-button [props]="{ text: 'Click me', color: 'primary' }" />
53
+ <val-button preset="primary-round" [props]="{ text: 'Click me' }" />
54
54
  </val-card>
55
55
  `
56
56
  })
@@ -59,49 +59,156 @@ export class MyComponent {}
59
59
 
60
60
  ### Full integration (auth + Firebase + i18n)
61
61
 
62
- Add providers in `main.ts`:
62
+ Wire providers in `main.ts`:
63
63
 
64
64
  ```typescript
65
65
  import {
66
66
  provideValtechFirebase,
67
67
  provideValtechAuth,
68
68
  provideValtechI18n,
69
- createFirebaseConfig,
69
+ provideValtechErrorHandling,
70
+ provideValtechSkeleton,
71
+ provideValtechPresets,
72
+ HandoffService,
70
73
  } from 'valtech-components';
71
- import firebaseConfig from './config/firebase.config.json';
74
+ import { APP_INITIALIZER } from '@angular/core';
72
75
 
73
76
  bootstrapApplication(AppComponent, {
74
77
  providers: [
75
- provideValtechFirebase(createFirebaseConfig(firebaseConfig)),
76
- provideValtechAuth({ apiUrl: 'https://api.yourapp.com' }),
77
- provideValtechI18n({ defaultLang: 'en', supportedLangs: ['en', 'es', 'pt'] }),
78
+ provideValtechFirebase(environment.valtechFirebase),
79
+
80
+ // Auth must come before provideValtechErrorHandling
81
+ provideValtechAuth({
82
+ apiUrl: environment.apiUrl,
83
+ appId: 'my-app',
84
+ loginRoute: '/login',
85
+ homeRoute: '/app',
86
+ enableFirebaseIntegration: true,
87
+ enableTabSync: true,
88
+ }),
89
+
90
+ // HTTP error observability — always after provideValtechAuth
91
+ provideValtechErrorHandling(),
92
+
93
+ // Cross-app session handoff — always after provideValtechAuth
94
+ {
95
+ provide: APP_INITIALIZER,
96
+ multi: true,
97
+ deps: [HandoffService],
98
+ useFactory: (h: HandoffService) => () => h.detectAndExchangeHandoff(),
99
+ },
100
+
101
+ provideValtechI18n({
102
+ defaultLanguage: 'es',
103
+ supportedLanguages: ['es', 'en'],
104
+ content: APP_I18N_CONTENT,
105
+ }),
106
+
107
+ provideValtechPresets(APP_PRESETS),
108
+ provideValtechSkeleton({ animated: true }),
78
109
  ],
79
110
  });
80
111
  ```
81
112
 
113
+ **Provider order matters:**
114
+
115
+ ```
116
+ provideHttpClient
117
+
118
+ provideValtechFirebase (initializes Firebase app)
119
+
120
+ provideValtechAuth (registers auth HTTP interceptor)
121
+
122
+ provideValtechErrorHandling (registers error interceptor — composes with auth)
123
+
124
+ APP_INITIALIZER handoff (depends on AuthService being configured)
125
+ ```
126
+
82
127
  ---
83
128
 
84
129
  ## Components
85
130
 
86
131
  | Category | Count | Includes |
87
132
  |----------|------:|---------|
88
- | Atoms | 24 | avatar, button, icon, text, image, skeleton, progress-bar, qr-code |
89
- | Molecules | 81 | inputs (text, email, phone, pin, date), cards, tabs, accordion, pagination… |
90
- | Organisms | 31 | forms, data-table, menu, toolbar, login, comment-section, infinite-list… |
91
- | Templates | 9 | page-wrapper, page-content, docs-shell, auth-background, maintenance-page |
92
- | **Total** | **145** | |
133
+ | Atoms | ~24 | `val-button`, `val-text`, `val-icon`, `val-avatar`, `val-image`, `val-badge`, `val-skeleton`, `val-progress-bar`, `val-qr-code`, `val-page-waves` |
134
+ | Molecules | ~81 | `val-card`, `val-input`, `val-select`, `val-date-picker`, `val-tabs`, `val-accordion`, `val-searchbar`, `val-empty-state`, `val-ticket-card`, `val-field-list`, `val-stats-bar`, `val-article-card` |
135
+ | Organisms | ~31 | `val-form`, `val-data-table`, `val-bottom-nav`, `val-toolbar`, `val-menu`, `val-login`, `val-comment-section`, `val-qr-scanner`, `val-search-header`, `val-share-profile-modal`, `val-fun-modal` |
136
+ | Templates | ~9 | `val-page-wrapper`, `val-page-content`, `val-auth-background`, `val-maintenance-page`, `val-docs-shell` |
137
+
138
+ Components requiring a backend API: `val-login`, `val-content-reaction`.
139
+
140
+ ### Buttons — presets, not factories
141
+
142
+ ```html
143
+ <!-- Declarative (preferred) -->
144
+ <val-button preset="primary-round" [props]="{ text: 'Save' }" />
145
+ <val-button preset="secondary" [props]="{ text: 'Cancel' }" />
146
+ <val-button preset="danger-round" [props]="{ text: 'Delete' }" />
147
+ ```
148
+
149
+ Built-in presets: `primary`, `primary-round`, `primary-block`, `primary-full`, `secondary`, `secondary-round`, `solid`, `solid-round`, `solid-block`, `outline`, `outline-round`, `clear`, `clear-round`, `danger`, `danger-round`.
150
+
151
+ ### Date picker
152
+
153
+ Use `val-date-picker` instead of `val-date-input` or `ion-datetime` (ion-datetime has a known bug with empty day grids in modals):
154
+
155
+ ```html
156
+ <val-date-picker
157
+ [props]="{
158
+ control: myControl,
159
+ label: 'Date',
160
+ placeholder: 'Select date',
161
+ locale: 'es-CL',
162
+ firstWeekday: 1
163
+ }"
164
+ (valueChange)="onDate($event)"
165
+ />
166
+ ```
167
+
168
+ Value format: `YYYY-MM-DD` (local date string, no timezone shift). If the backend returns ISO timestamps, slice to 10 chars: `value.slice(0, 10)`.
169
+
170
+ ### Field list (editable repeating items)
171
+
172
+ ```html
173
+ <val-field-list
174
+ [props]="{
175
+ fields: [
176
+ { name: 'name', label: 'Prize name', maxlength: 200 },
177
+ { name: 'description', label: 'Description', maxlength: 300 }
178
+ ],
179
+ items: seedSignal(),
180
+ itemLabel: 'Prize {n}',
181
+ addLabel: 'Add prize'
182
+ }"
183
+ (itemsChange)="onItemsChange($event)"
184
+ />
185
+ ```
186
+
187
+ Pass a **separate seed signal** for `items` — do not bind the editing signal directly (it re-seeds on every change).
188
+
189
+ ### Empty / error state
93
190
 
94
- Components requiring a backend API: `LoginComponent`, `ContentReactionComponent`.
191
+ ```html
192
+ @if (page.loading()) {
193
+ <val-skeleton-layout [props]="{ preset: 'list', rows: 4 }" />
194
+ } @else if (page.errorState(); as e) {
195
+ <val-empty-state [props]="e" />
196
+ } @else if (items().length === 0) {
197
+ <val-empty-state [props]="{ variant: 'empty', title: t('emptyTitle') }" />
198
+ } @else {
199
+ <!-- content -->
200
+ }
201
+ ```
95
202
 
96
203
  ---
97
204
 
98
205
  ## Services
99
206
 
100
207
  **No setup required:**
101
- `ThemeService` · `ToastService` · `NavigationService` · `DownloadService` · `IconsService` · `LocaleService` · `LocalStorageService` · `ModalService` · `ConfirmationDialogService` · `QrGeneratorService` · `PaginationService` · `SkeletonService`
208
+ `ThemeService` · `ToastService` · `NavigationService` · `DownloadService` · `IconService` · `LocaleService` · `LocalStorageService` · `ModalService` · `ConfirmationDialogService` · `QrGeneratorService` · `PaginationService` · `SkeletonService`
102
209
 
103
210
  **Requires `provideValtechAuth`:**
104
- `AuthService` · `OAuthService` · `SessionService` · `TokenService` · `AuthStateService` · `DeviceService`
211
+ `AuthService` · `OAuthService` · `SessionService` · `TokenService` · `AuthStateService` · `DeviceService` · `HandoffService` · `OrgSwitchService`
105
212
 
106
213
  **Requires `provideValtechFirebase`:**
107
214
  `FirebaseService` · `AnalyticsService` · `FirestoreService` · `MessagingService` · `NotificationsService` · `FirebaseStorageService`
@@ -109,23 +216,147 @@ Components requiring a backend API: `LoginComponent`, `ContentReactionComponent`
109
216
  **Requires `provideValtechI18n`:**
110
217
  `I18nService`
111
218
 
219
+ **Requires `provideValtechErrorHandling`:**
220
+ `ValtechErrorService`
221
+
112
222
  ---
113
223
 
114
- ## Technologies
224
+ ## Common patterns
115
225
 
116
- Angular 18+ · Ionic 8+ · Capacitor 6+ · Firebase 10+ · RxJS 7.8+
226
+ ### Page loading state `createPageState`
227
+
228
+ Replaces ~15 lines of boilerplate (loading signal + error signal + try/catch + computed error state):
229
+
230
+ ```typescript
231
+ import { createPageState, connectPageRefresh } from 'valtech-components';
232
+
233
+ export class MyPage {
234
+ private svc = inject(MyService);
235
+ private i18n = inject(I18nService);
236
+ readonly items = signal<Item[]>([]);
237
+
238
+ readonly page = createPageState(this.i18n, async () => {
239
+ const res = await firstValueFrom(this.svc.getItems());
240
+ this.items.set(res.items);
241
+ });
242
+
243
+ constructor() {
244
+ connectPageRefresh(() => this.page.reload());
245
+ }
246
+ }
247
+ ```
248
+
249
+ `page.loading()` · `page.errorState()` · `page.reload()` — all signals/promises, ready to bind.
250
+
251
+ ### Pull-to-refresh
252
+
253
+ Every page that displays remote data must register a refresh handler. Use `connectPageRefresh` in the constructor — it registers and auto-cleans via `DestroyRef`:
254
+
255
+ ```typescript
256
+ import { connectPageRefresh, createRefreshableStream } from 'valtech-components';
257
+
258
+ export class MyPage {
259
+ private data = createRefreshableStream(() => this.svc.getItems());
260
+ readonly items = this.data.data;
261
+ readonly loading = this.data.loading;
262
+
263
+ constructor() {
264
+ connectPageRefresh(() => this.data.reload());
265
+ }
266
+ }
267
+ ```
268
+
269
+ ### Error handling in catch blocks
270
+
271
+ ```typescript
272
+ import { ValtechErrorService } from 'valtech-components';
273
+
274
+ export class MyPage {
275
+ private errors = inject(ValtechErrorService);
276
+
277
+ async save() {
278
+ try {
279
+ await firstValueFrom(this.api.updateX(...));
280
+ } catch (err) {
281
+ this.errors.handle(err, {
282
+ context: 'mypage.save',
283
+ i18nMap: { BACKEND_CODE: 'i18nKey' },
284
+ fallbackKey: 'saveError',
285
+ i18nNamespace: 'MyNamespace',
286
+ });
287
+ }
288
+ }
289
+ }
290
+ ```
291
+
292
+ ### Cross-app session handoff
293
+
294
+ Seamlessly transfer a logged-in session from one factory app to another:
295
+
296
+ ```typescript
297
+ // App A — create handoff and redirect
298
+ const { url } = await handoff.createHandoff({ app: 'sigify', route: '/docs/123' });
299
+ window.location.href = url;
300
+
301
+ // App B — auto-handled in bootstrap via APP_INITIALIZER (see Full integration above)
302
+ ```
303
+
304
+ ### OAuth callback route
305
+
306
+ Add this route to `app.routes.ts` if your app uses social login:
307
+
308
+ ```typescript
309
+ import { OAuthCallbackComponent } from 'valtech-components';
310
+
311
+ { path: 'auth/oauth/callback', component: OAuthCallbackComponent }
312
+ ```
313
+
314
+ Without this route, post-login redirects to a 404.
315
+
316
+ ### Toasts — always `color: 'dark'`
317
+
318
+ ```typescript
319
+ // All toasts use color: 'dark' regardless of message type
320
+ this.toast.show({ message: 'Saved', color: 'dark', duration: 2000 });
321
+ this.toast.show({ message: 'Error: ...', color: 'dark', duration: 3000 });
322
+ ```
323
+
324
+ ### Content reactions (feedback widget)
325
+
326
+ ```html
327
+ <val-content-reaction [props]="{
328
+ entityRef: { entityType: 'blog', entityId: post.slug },
329
+ question: 'Was this article helpful?',
330
+ emojis: ['👎', '👍'],
331
+ commentOnValues: ['negative'],
332
+ allowAnonymous: true
333
+ }" />
334
+ ```
335
+
336
+ Requires `provideValtechFeedback({ apiUrl, appId })` in `main.ts`.
117
337
 
118
338
  ---
119
339
 
120
- ## Development
340
+ ## Styles
121
341
 
122
- ```bash
123
- npm install
124
- npm run storybook # http://localhost:6006
125
- npm run build # build library
126
- npm run test # unit tests
342
+ Import fonts and base styles in your app's `variables.scss`:
343
+
344
+ ```scss
345
+ @import "valtech-components/styles/fonts.scss"; // Nunito Sans (default) + Khula (Bingo brand)
346
+
347
+ :root {
348
+ --ion-font-family: "Nunito Sans", Arial, sans-serif;
349
+ }
127
350
  ```
128
351
 
352
+ All `font-size` values in the lib use `rem` to respect the user's font size preference (`FontSizeService` scales the root `html` element).
353
+
354
+ ---
355
+
356
+ ## Technologies
357
+
358
+ Angular 18+ · Ionic 8+ · Capacitor 6+ · Firebase 10+ · RxJS 7.8+
359
+
129
360
  ---
130
361
 
131
362
  ## License