@m1z23r/ngx-ui 0.0.1 → 1.0.0

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
@@ -338,6 +338,263 @@ export class MyComponent {
338
338
 
339
339
  ---
340
340
 
341
+ ## Loading System
342
+
343
+ A centralized loading state management system with a service and directive for easy integration.
344
+
345
+ ### LoadingService
346
+
347
+ Injectable service to manage loading states by identifier.
348
+
349
+ ```typescript
350
+ import { LoadingService } from '@m1z23r/ngx-ui';
351
+
352
+ @Component({...})
353
+ export class MyComponent {
354
+ private loadingService = inject(LoadingService);
355
+
356
+ async login() {
357
+ this.loadingService.start('login');
358
+ try {
359
+ await this.authService.login();
360
+ } finally {
361
+ this.loadingService.stop('login');
362
+ }
363
+ }
364
+
365
+ async submitForm() {
366
+ this.loadingService.start('submit');
367
+ try {
368
+ await this.formService.submit();
369
+ } finally {
370
+ this.loadingService.stop('submit');
371
+ }
372
+ }
373
+ }
374
+ ```
375
+
376
+ #### Methods
377
+
378
+ | Method | Parameters | Description |
379
+ |--------|------------|-------------|
380
+ | `start(id)` | `id: string` | Start loading for identifier |
381
+ | `stop(id)` | `id: string` | Stop loading for identifier |
382
+ | `set(id, loading)` | `id: string, loading: boolean` | Set loading state |
383
+ | `toggle(id)` | `id: string` | Toggle loading state |
384
+ | `isLoading(id)` | `id: string` | Returns `Signal<boolean>` for the identifier |
385
+ | `isAnyLoading()` | - | Returns `Signal<boolean>` true if any loading is active |
386
+ | `clear(id)` | `id: string` | Remove loading state for identifier |
387
+ | `clearAll()` | - | Remove all loading states |
388
+
389
+ ### LoadingDirective
390
+
391
+ Directive that automatically connects a component's loading state to the LoadingService.
392
+
393
+ ```typescript
394
+ import { ButtonComponent, LoadingDirective, LoadingService } from '@m1z23r/ngx-ui';
395
+
396
+ @Component({
397
+ imports: [ButtonComponent, LoadingDirective],
398
+ template: `
399
+ <!-- These buttons automatically show loading when their identifier is active -->
400
+ <ui-button uiLoading="login" (clicked)="login()">Login</ui-button>
401
+ <ui-button uiLoading="submit" (clicked)="submit()">Submit</ui-button>
402
+ <ui-button uiLoading="delete" variant="outline">Delete</ui-button>
403
+ `
404
+ })
405
+ export class MyComponent {
406
+ private loadingService = inject(LoadingService);
407
+
408
+ async login() {
409
+ this.loadingService.start('login'); // Button with uiLoading="login" shows spinner
410
+ try {
411
+ await this.authService.login();
412
+ } finally {
413
+ this.loadingService.stop('login'); // Spinner stops
414
+ }
415
+ }
416
+ }
417
+ ```
418
+
419
+ ### Making Custom Components Loadable
420
+
421
+ Any component can support the `uiLoading` directive by implementing the `Loadable` interface:
422
+
423
+ ```typescript
424
+ import { Loadable, LOADABLE } from '@m1z23r/ngx-ui';
425
+
426
+ @Component({
427
+ selector: 'my-custom-button',
428
+ providers: [{ provide: LOADABLE, useExisting: MyCustomButtonComponent }],
429
+ template: `
430
+ <button [disabled]="loading()">
431
+ @if (loading()) {
432
+ <span class="spinner"></span>
433
+ }
434
+ <ng-content />
435
+ </button>
436
+ `
437
+ })
438
+ export class MyCustomButtonComponent implements Loadable {
439
+ private readonly loading = signal(false);
440
+
441
+ // Required by Loadable interface
442
+ setLoading(loading: boolean): void {
443
+ this.loading.set(loading);
444
+ }
445
+ }
446
+ ```
447
+
448
+ Now the directive works with your custom component:
449
+
450
+ ```html
451
+ <my-custom-button uiLoading="save">Save</my-custom-button>
452
+ ```
453
+
454
+ ### Combining with Direct Loading Input
455
+
456
+ The `ui-button` component supports both the directive and direct `loading` input. The button shows loading if either is true:
457
+
458
+ ```html
459
+ <!-- Via directive (controlled by LoadingService) -->
460
+ <ui-button uiLoading="login">Login</ui-button>
461
+
462
+ <!-- Via direct input -->
463
+ <ui-button [loading]="isSubmitting()">Submit</ui-button>
464
+
465
+ <!-- Both work together - loading shows if either is true -->
466
+ <ui-button uiLoading="save" [loading]="manualLoading()">Save</ui-button>
467
+ ```
468
+
469
+ ---
470
+
471
+ ## Dialog System
472
+
473
+ A simple, Promise-based dialog system for opening modal dialogs programmatically. No RxJS required.
474
+
475
+ ### DialogService
476
+
477
+ Injectable service to open dialogs programmatically.
478
+
479
+ ```typescript
480
+ import { DialogService, DIALOG_DATA, DIALOG_REF, DialogRef, ModalComponent, ButtonComponent } from '@m1z23r/ngx-ui';
481
+
482
+ // 1. Create a dialog component
483
+ @Component({
484
+ imports: [ModalComponent, ButtonComponent],
485
+ template: `
486
+ <ui-modal [title]="data.title" size="sm">
487
+ <p>{{ data.message }}</p>
488
+
489
+ <ng-container footer>
490
+ <ui-button variant="outline" (clicked)="dialogRef.close(false)">Cancel</ui-button>
491
+ <ui-button (clicked)="dialogRef.close(true)">Confirm</ui-button>
492
+ </ng-container>
493
+ </ui-modal>
494
+ `
495
+ })
496
+ export class ConfirmDialog {
497
+ dialogRef = inject(DIALOG_REF) as DialogRef<boolean>;
498
+ data = inject(DIALOG_DATA) as { title: string; message: string };
499
+ }
500
+
501
+ // 2. Open the dialog
502
+ @Component({...})
503
+ export class MyComponent {
504
+ private dialogService = inject(DialogService);
505
+
506
+ async confirmAction() {
507
+ const dialogRef = this.dialogService.open(ConfirmDialog, {
508
+ data: { title: 'Confirm', message: 'Are you sure?' }
509
+ });
510
+
511
+ const confirmed = await dialogRef.afterClosed();
512
+ if (confirmed) {
513
+ // User confirmed
514
+ }
515
+ }
516
+ }
517
+ ```
518
+
519
+ ### ModalComponent
520
+
521
+ A wrapper component that provides the modal UI with backdrop, header, body, and footer. Uses content projection.
522
+
523
+ ```html
524
+ <ui-modal [title]="'My Dialog'" [size]="'md'" [closeOnEscape]="true" [closeOnBackdropClick]="true">
525
+ <!-- Body content (default slot) -->
526
+ <p>This is the modal body content.</p>
527
+
528
+ <!-- Footer content (named slot) -->
529
+ <ng-container footer>
530
+ <ui-button variant="outline" (clicked)="cancel()">Cancel</ui-button>
531
+ <ui-button (clicked)="save()">Save</ui-button>
532
+ </ng-container>
533
+ </ui-modal>
534
+ ```
535
+
536
+ #### Modal Inputs
537
+
538
+ | Input | Type | Default | Description |
539
+ |-------|------|---------|-------------|
540
+ | `title` | `string` | - | Title displayed in the modal header |
541
+ | `size` | `'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'` | `'md'` | Modal size preset |
542
+ | `width` | `string` | - | Custom width (overrides size) |
543
+ | `maxWidth` | `string` | - | Custom max-width (overrides size) |
544
+ | `closeOnBackdropClick` | `boolean` | `true` | Close when clicking backdrop |
545
+ | `closeOnEscape` | `boolean` | `true` | Close when pressing Escape |
546
+ | `showCloseButton` | `boolean` | `true` | Show close button in header |
547
+ | `panelClass` | `string` | - | Custom CSS class for container |
548
+
549
+ #### Modal Size Reference
550
+
551
+ | Size | Max Width |
552
+ |------|-----------|
553
+ | `sm` | 400px |
554
+ | `md` | 560px |
555
+ | `lg` | 800px |
556
+ | `xl` | 1140px |
557
+ | `full` | 100vw - padding |
558
+
559
+ ### DialogConfig
560
+
561
+ Configuration options when opening a dialog.
562
+
563
+ ```typescript
564
+ interface DialogConfig<TData = unknown> {
565
+ data?: TData; // Data passed via DIALOG_DATA
566
+ width?: string; // CSS width value
567
+ maxWidth?: string; // CSS max-width value
568
+ size?: ModalSize; // Size preset
569
+ closeOnBackdropClick?: boolean; // Default: true
570
+ closeOnEscape?: boolean; // Default: true
571
+ panelClass?: string; // Custom CSS class
572
+ }
573
+ ```
574
+
575
+ ### DialogRef
576
+
577
+ Reference to an opened dialog, used to close it and get results.
578
+
579
+ ```typescript
580
+ class DialogRef<TResult> {
581
+ // Close the dialog with an optional result
582
+ close(result?: TResult): void;
583
+
584
+ // Get a promise that resolves when the dialog closes
585
+ afterClosed(): Promise<TResult | undefined>;
586
+ }
587
+ ```
588
+
589
+ ### Injection Tokens
590
+
591
+ | Token | Type | Description |
592
+ |-------|------|-------------|
593
+ | `DIALOG_DATA` | `unknown` | Data passed to the dialog via config |
594
+ | `DIALOG_REF` | `DialogRef` | Reference to close the dialog |
595
+
596
+ ---
597
+
341
598
  ## Theming
342
599
 
343
600
  All components use CSS custom properties for styling. Override these in your global stylesheet: