button-toggle-input 15.0.3 → 15.0.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 CHANGED
@@ -1,105 +1,1078 @@
1
- # Button Toggle Selection Component
1
+ # Button Toggle Input Component
2
2
 
3
- This is a Button Toggle
3
+ ## Overview
4
4
 
5
- ## Usage
5
+ The `button-toggle-input` library provides a comprehensive Material Design toggle button component that works seamlessly with Angular forms. It supports both single and multiple selection modes, customizable styling, tooltips, and various display options including icons, labels, and borders. The component implements Angular's `ControlValueAccessor` interface for seamless form integration.
6
6
 
7
- 1) `npm i button-toggle-input`
7
+ ### Core Capabilities
8
8
 
9
- 2) Import `ButtonToggleInputModule`
9
+ #### 🎯 Advanced Toggle Button Interface
10
10
 
11
- 3) Use the selector in your template
12
- ```html
13
- <app-button-toggle-input
14
- [formControl]="selectionControl"
15
- [data]="data"
16
- [toolTips]="tooltip1.checked"
17
- [fullWidth]="width1.value"
18
- [noBorder]="!border1.checked"
19
- [iconSuffix]="suffix1.checked"
20
- ></app-button-toggle-input>
21
- ```
22
-
23
- ### Input Properties
24
-
25
- | Input | Type | Description | Default |
26
- |------------------|--------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|
27
- | data | ListItem[] &#124; string[] | An array of ListItem objects or strings that define the toggle options. If passed as strings, each string is used as both the value and label. | (Required) |
28
- | multiple | boolean | If true, allows multiple selections. | false |
29
- | toolTips | boolean | If true, displays tooltips on each toggle item. | false |
30
- | toolTipPosition | TooltipPosition | The position of the tooltips ('above', 'below', 'left', 'right'). Imported from @angular/material/tooltip. | "above" |
31
- | toolTipShowDelay | number | The delay in milliseconds before the tooltip is displayed. | 1 |
32
- | color | string | The base color of the toggle buttons, e.g., a hex code. | "#333333" |
33
- | lightColor | string | The color used for the toggle button's light areas (e.g., background when not selected). | "white" |
34
- | darkColor | string | The color used for the toggle button's dark areas (e.g., text when selected). | "black" |
35
- | noBorder | boolean | If true, removes the border from the toggle buttons. | false |
36
- | iconPrefix | boolean | If true, displays the icon before the label. Ignored if iconSuffix is also true. | true |
37
- | iconSuffix | boolean | If true, displays the icon after the label. When true, iconPrefix is automatically set to false. | false |
38
- | fullWidth | boolean | If true, makes the toggle buttons take up the full width of their container. | false |
39
-
40
- ### Usage Example
41
-
42
- ```ts
11
+ - **Single/Multiple Selection**: Support for both single selection and multi-select modes
12
+ - **Rich Display Options**: Icons, labels, tooltips, and custom styling
13
+ - **Form Control Integration**: Native Angular form control support with ControlValueAccessor
14
+ - **Material Design**: Built on Angular Material button toggle foundation
15
+ - **Customizable Styling**: Colors, borders, width, and icon positioning
16
+ - **Tooltip Support**: Contextual tooltips with configurable positioning
17
+ - **Accessibility Ready**: ARIA labels and keyboard navigation support
18
+
19
+ #### 🔧 Features
20
+
21
+ ✅ **ControlValueAccessor Implementation** - Works with Angular forms
22
+ ✅ **Material Design Integration** - Uses Angular Material components
23
+ **Single & Multiple Selection** - Flexible selection modes
24
+ ✅ **Icon Support** - Material icons with prefix/suffix positioning
25
+ **Tooltip Integration** - Contextual help with positioning options
26
+ ✅ **Custom Color Schemes** - Light/dark colors and border control
27
+ **Full Width Option** - Responsive width control
28
+ **Change Detection Optimized** - OnPush change detection strategy
29
+ **Form Validation Support** - Integrates with Angular validation
30
+
31
+ ### Key Benefits
32
+
33
+ | Feature | Description |
34
+ |---------|-------------|
35
+ | **Flexible Selection** | Support for single or multiple item selection |
36
+ | **Rich UI Options** | Icons, labels, tooltips, and styling customization |
37
+ | **Form Integration** | Seamless Angular form control integration |
38
+ | **Material Design** | Consistent Material Design styling and behavior |
39
+ | **Performance Optimized** | OnPush change detection for better performance |
40
+ | **Accessibility** | Full ARIA support and keyboard navigation |
41
+
42
+ ---
43
+
44
+ ## Demo Component (`ButtonToggleDemoComponent`)
45
+
46
+ The demo component showcases various toggle button configurations and demonstrates different use cases including icons-only, labels-only, and combined icon+label modes.
47
+
48
+ ### Usage
49
+
50
+ To use the demo component in your application:
51
+
52
+ ```html
53
+ <app-button-toggle-demo></app-button-toggle-demo>
54
+ ```
55
+
56
+ The demo includes:
57
+ - **Icon-only mode**: Toggle buttons with Material icons
58
+ - **Label-only mode**: Toggle buttons with text labels
59
+ - **Combined mode**: Toggle buttons with both icons and labels
60
+ - **Form controls**: Reactive form integration examples
61
+ - **Dynamic data switching**: Runtime data type changes
62
+ - **Enable/disable controls**: Form control state management
63
+ - **Change detection logging**: Console output for debugging
64
+
65
+ ---
66
+
67
+ ## Summary
68
+
69
+ The `button-toggle-input` library provides a flexible, Material Design-compliant toggle button component with comprehensive form integration, customizable styling, and rich display options for Angular applications.
70
+
71
+ ---
72
+
73
+ ## Quick Start Guide
74
+
75
+ ### Installation & Setup (2 minutes)
76
+
77
+ #### 1. Import Module
78
+
79
+ ```typescript
80
+ // app.module.ts
81
+ import { ButtonToggleInputModule } from 'button-toggle-input';
82
+
83
+ @NgModule({
84
+ imports: [
85
+ ButtonToggleInputModule
86
+ ]
87
+ })
88
+ export class AppModule { }
89
+ ```
90
+
91
+ #### 2. No Module Configuration Required
92
+
93
+ The `ButtonToggleInputModule` does not require global configuration. Components can be used immediately after module import.
94
+
95
+ ### Quick Examples
96
+
97
+ #### Example 1: Basic Single Selection
98
+
99
+ ```typescript
43
100
  import { Component } from '@angular/core';
101
+ import { ListItem } from 'button-toggle-input';
44
102
 
45
- interface ListItem {
46
- value: string;
47
- label: string;
48
- icon?: string;
103
+ @Component({
104
+ selector: 'app-basic-toggle',
105
+ template: `
106
+ <app-button-toggle-input
107
+ [formControl]="selectionControl"
108
+ [data]="toggleOptions">
109
+ </app-button-toggle-input>
110
+
111
+ <div>Selected: {{ selectionControl.value }}</div>
112
+ `
113
+ })
114
+ export class BasicToggleComponent {
115
+ selectionControl = new FormControl();
116
+
117
+ toggleOptions: ListItem[] = [
118
+ { value: 'option1', label: 'Option 1', icon: 'home' },
119
+ { value: 'option2', label: 'Option 2', icon: 'settings' },
120
+ { value: 'option3', label: 'Option 3', icon: 'star' }
121
+ ];
49
122
  }
123
+ ```
124
+
125
+ #### Example 2: Multiple Selection with Tooltips
126
+
127
+ ```typescript
128
+ import { Component } from '@angular/core';
129
+ import { ListItem } from 'button-toggle-input';
50
130
 
51
131
  @Component({
52
- selector: 'app-example',
132
+ selector: 'app-multi-toggle',
53
133
  template: `
54
134
  <app-button-toggle-input
55
- [data]="items"
56
- [(ngModel)]="selectedItems"
135
+ [formControl]="multiControl"
136
+ [data]="multiOptions"
57
137
  [multiple]="true"
58
138
  [toolTips]="true"
59
139
  toolTipPosition="below"
60
- color="#007bff"
61
- lightColor="#f0f8ff"
62
- darkColor="white"
63
140
  [noBorder]="true"
141
+ [fullWidth]="true">
142
+ </app-button-toggle-input>
143
+
144
+ <div>Selected Items: {{ multiControl.value | json }}</div>
145
+ `
146
+ })
147
+ export class MultiToggleComponent {
148
+ multiControl = new FormControl();
149
+
150
+ multiOptions: ListItem[] = [
151
+ {
152
+ value: 'read',
153
+ label: 'Read',
154
+ icon: 'visibility',
155
+ tootTip: 'Read access to documents'
156
+ },
157
+ {
158
+ value: 'write',
159
+ label: 'Write',
160
+ icon: 'edit',
161
+ tootTip: 'Write access to documents'
162
+ },
163
+ {
164
+ value: 'delete',
165
+ label: 'Delete',
166
+ icon: 'delete',
167
+ tootTip: 'Delete access to documents'
168
+ }
169
+ ];
170
+ }
171
+ ```
172
+
173
+ #### Example 3: Icon-Only Mode
174
+
175
+ ```typescript
176
+ import { Component } from '@angular/core';
177
+ import { ListItem } from 'button-toggle-input';
178
+
179
+ @Component({
180
+ selector: 'app-icon-toggle',
181
+ template: `
182
+ <app-button-toggle-input
183
+ [formControl]="iconControl"
184
+ [data]="iconOptions"
64
185
  [iconPrefix]="true"
65
- [fullWidth]="true"
66
- >
186
+ [noBorder]="false">
67
187
  </app-button-toggle-input>
68
- <p>Selected Items: {{ selectedItems | json }}</p>
188
+
189
+ <div>Selected: {{ iconControl.value }}</div>
69
190
  `
70
191
  })
71
- export class ExampleComponent {
72
- items: ListItem[] = [
73
- { value: 'item1', label: 'Item 1', icon: 'fa fa-check' },
74
- { value: 'item2', label: 'Item 2', icon: 'fa fa-times' },
75
- { value: 'item3', label: 'Item 3' }
192
+ export class IconToggleComponent {
193
+ iconControl = new FormControl();
194
+
195
+ iconOptions: ListItem[] = [
196
+ { value: 'grid', icon: 'grid_on', tootTip: 'Grid View' },
197
+ { value: 'list', icon: 'list', tootTip: 'List View' },
198
+ { value: 'chart', icon: 'bar_chart', tootTip: 'Chart View' }
76
199
  ];
77
- selectedItems: string[] = [];
78
200
  }
79
201
  ```
80
202
 
203
+ #### Example 4: Custom Styled Toggle
204
+
205
+ ```typescript
206
+ import { Component } from '@angular/core';
207
+ import { ListItem } from 'button-toggle-input';
208
+
209
+ @Component({
210
+ selector: 'app-styled-toggle',
211
+ template: `
212
+ <div class="toggle-container">
213
+ <h3>View Options</h3>
214
+
215
+ <app-button-toggle-input
216
+ [formControl]="viewControl"
217
+ [data]="viewOptions"
218
+ color="#007bff"
219
+ lightColor="#f0f8ff"
220
+ darkColor="white"
221
+ [fullWidth]="true">
222
+ </app-button-toggle-input>
223
+
224
+ <div class="result">
225
+ Current view: {{ viewControl.value }}
226
+ </div>
227
+ </div>
228
+ `,
229
+ styles: [`
230
+ .toggle-container {
231
+ max-width: 400px;
232
+ margin: 2rem auto;
233
+ padding: 1rem;
234
+ border: 1px solid #ddd;
235
+ border-radius: 8px;
236
+ }
237
+ h3 {
238
+ text-align: center;
239
+ margin-bottom: 1rem;
240
+ color: #333;
241
+ }
242
+ .result {
243
+ margin-top: 1rem;
244
+ text-align: center;
245
+ font-weight: bold;
246
+ color: #007bff;
247
+ }
248
+ `]
249
+ })
250
+ export class StyledToggleComponent {
251
+ viewControl = new FormControl();
252
+
253
+ viewOptions: ListItem[] = [
254
+ { value: 'compact', label: 'Compact', icon: 'compress' },
255
+ { value: 'comfortable', label: 'Comfortable', icon: 'aspect_ratio' },
256
+ { value: 'spacious', label: 'Spacious', icon: 'open_in_full' }
257
+ ];
258
+ }
259
+ ```
260
+
261
+ #### Example 5: Dynamic Data with Validation
262
+
263
+ ```typescript
264
+ import { Component } from '@angular/core';
265
+ import { FormBuilder, Validators } from '@angular/forms';
266
+ import { ListItem } from 'button-toggle-input';
267
+
268
+ @Component({
269
+ selector: 'app-dynamic-toggle',
270
+ template: `
271
+ <form [formGroup]="toggleForm">
272
+ <app-button-toggle-input
273
+ formControlName="category"
274
+ [data]="categoryOptions"
275
+ [multiple]="false"
276
+ [toolTips]="true">
277
+ </app-button-toggle-input>
278
+
279
+ <app-button-toggle-input
280
+ formControlName="features"
281
+ [data]="featureOptions"
282
+ [multiple]="true"
283
+ [toolTips]="true">
284
+ </app-button-toggle-input>
285
+ </form>
286
+
287
+ <div class="form-status">
288
+ <div>Category Valid: {{ toggleForm.get('category')?.valid }}</div>
289
+ <div>Features Valid: {{ toggleForm.get('features')?.valid }}</div>
290
+ <div>Form Valid: {{ toggleForm.valid }}</div>
291
+ </div>
292
+ `,
293
+ styles: [`
294
+ form {
295
+ display: flex;
296
+ flex-direction: column;
297
+ gap: 2rem;
298
+ max-width: 500px;
299
+ margin: 2rem auto;
300
+ }
301
+ .form-status {
302
+ background: #f5f5f5;
303
+ padding: 1rem;
304
+ border-radius: 4px;
305
+ font-family: monospace;
306
+ }
307
+ `]
308
+ })
309
+ export class DynamicToggleComponent {
310
+ constructor(private fb: FormBuilder) {}
311
+
312
+ toggleForm = this.fb.group({
313
+ category: ['', Validators.required],
314
+ features: [[], Validators.minLength(1)]
315
+ });
316
+
317
+ categoryOptions: ListItem[] = [
318
+ { value: 'web', label: 'Web Application', icon: 'web' },
319
+ { value: 'mobile', label: 'Mobile App', icon: 'phone_android' },
320
+ { value: 'desktop', label: 'Desktop App', icon: 'computer' }
321
+ ];
322
+
323
+ featureOptions: ListItem[] = [
324
+ { value: 'auth', label: 'Authentication', icon: 'security' },
325
+ { value: 'notifications', label: 'Notifications', icon: 'notifications' },
326
+ { value: 'offline', label: 'Offline Support', icon: 'wifi_off' },
327
+ { value: 'analytics', label: 'Analytics', icon: 'analytics' }
328
+ ];
329
+ }
330
+ ```
331
+
332
+ ---
333
+
334
+ ## Component API
335
+
336
+ ### Inputs
337
+
338
+ | Input | Type | Description | Default |
339
+ | :--- | :--- | :--- | :--- |
340
+ | `data` | `ListItem[] \| string[]` | Array of ListItem objects or strings defining toggle options | (Required) |
341
+ | `multiple` | `boolean` | If true, allows multiple selections | `false` |
342
+ | `toolTips` | `boolean` | If true, displays tooltips on each toggle item | `false` |
343
+ | `toolTipPosition` | `TooltipPosition` | Position of tooltips ('above', 'below', 'left', 'right') | `"above"` |
344
+ | `toolTipShowDelay` | `number` | Delay in milliseconds before tooltip is displayed | `1` |
345
+ | `color` | `string` | Base color of toggle buttons (hex code or CSS color) | `"#333333"` |
346
+ | `lightColor` | `string` | Color for light areas (background when not selected) | `"white"` |
347
+ | `darkColor` | `string` | Color for dark areas (text when selected) | `"black"` |
348
+ | `noBorder` | `boolean` | If true, removes border from toggle buttons | `false` |
349
+ | `iconPrefix` | `boolean` | If true, displays icon before label (ignored if iconSuffix is true) | `true` |
350
+ | `iconSuffix` | `boolean` | If true, displays icon after label (sets iconPrefix to false) | `false` |
351
+ | `fullWidth` | `boolean` | If true, makes toggle buttons take full container width | `false` |
352
+
353
+ ### Outputs
354
+
355
+ | Output | Type | Description |
356
+ | :--- | :--- | :--- |
357
+ | (Inherited from ControlValueAccessor) | `Function` | Value changes emitted through Angular's form control |
358
+
359
+ ---
360
+
361
+ ## Model Structures
362
+
363
+ ### ListItem Interface
364
+
365
+ ```typescript
366
+ export interface ListItemInterface {
367
+ value: any; // The value returned when this item is selected
368
+ label?: string; // Optional display label text
369
+ icon?: string; // Optional Material icon name
370
+ tootTip?: string; // Optional tooltip text (note: typo in original)
371
+ selected?: string; // Internal: marks item as selected
372
+ disabled?: string; // Internal: marks item as disabled
373
+ }
374
+ ```
375
+
376
+ ### ListItem Class
377
+
378
+ ```typescript
379
+ export class ListItem implements ListItemInterface {
380
+ constructor(
381
+ public value = '',
382
+ public label?: string,
383
+ public icon?: string,
384
+ public tootTip?: string,
385
+ public selected?: string,
386
+ public disabled?: string,
387
+ ) {}
388
+
389
+ static adapt(item?: any): ListItem {
390
+ return new ListItem(
391
+ item?.value,
392
+ // Label: use provided label, or empty string if icon exists, or fallback to value
393
+ (item?.label) ? item.label : (item?.icon) ? '' : item?.value,
394
+ item?.icon,
395
+ // Tooltip: use provided tooltip, or label if exists, or fallback to value
396
+ (item?.tootTip) ? item.tootTip : (item?.label) ? item.label : item?.value,
397
+ item?.selected,
398
+ item?.disabled,
399
+ );
400
+ }
401
+ }
402
+ ```
403
+
404
+ ### Usage Examples
405
+
406
+ ```typescript
407
+ // Basic list item
408
+ const basicItem = new ListItem('value1', 'Label 1');
409
+
410
+ // With icon
411
+ const iconItem = new ListItem('value2', 'Settings', 'settings');
412
+
413
+ // Using adapt method for flexible data
414
+ const adaptedItem = ListItem.adapt({
415
+ value: 'option1',
416
+ label: 'Option 1',
417
+ icon: 'radio_button_checked',
418
+ tootTip: 'Select this option'
419
+ });
420
+
421
+ // Array configurations
422
+ const toggleOptions: ListItem[] = [
423
+ // String-only data (automatically converted)
424
+ 'option1',
425
+ 'option2',
426
+ 'option3',
427
+
428
+ // Object configuration
429
+ { value: 'custom1', label: 'Custom Option', icon: 'star' },
430
+ { value: 'custom2', label: 'Another Option', icon: 'heart' },
431
+
432
+ // Icon-only (no label)
433
+ { value: 'icon1', icon: 'home', tootTip: 'Home View' },
434
+ { value: 'icon2', icon: 'work', tootTip: 'Work View' }
435
+ ];
436
+
437
+ // Complex configuration with all properties
438
+ const complexItem: ListItem = ListItem.adapt({
439
+ value: 'complex-option',
440
+ label: 'Complex Option',
441
+ icon: 'settings',
442
+ tootTip: 'This is a complex option with all features',
443
+ selected: 'true', // Pre-selected
444
+ disabled: 'false' // Not disabled
445
+ });
446
+ ```
447
+
448
+ ---
449
+
81
450
  ## Form Integration (ControlValueAccessor)
82
451
 
83
- The component implements the ControlValueAccessor interface, making it easy to integrate with Angular forms:
452
+ The component implements Angular's `ControlValueAccessor` interface, making it fully compatible with both template-driven and reactive forms.
84
453
 
85
- writeValue(value: any): void: Sets the value of the control.
86
- registerOnChange(fn: any): void: Registers a callback function to be called when the control's value changes.
87
- registerOnTouched(fn: any): void: Registers a callback function to be called when the control is touched.
88
- setDisabledState(isDisabled: boolean): void: Disables or enables the control.
89
- The component uses a FormControl internally to manage the selected values. The selected values are emitted through the onChange callback.
454
+ ### ControlValueAccessor Implementation
90
455
 
91
- ## Sample Data Structure
456
+ #### Methods Implemented
92
457
 
93
- ```ts
94
- const groupOptions: ListItem[] = [
95
- { label: 'Sample', value: 'test', icon: 'add' },
96
- { value: 'list', icon: 'list' },
97
- { value: 'list', label: 'me' },
98
- { label: '', value: 'grid', icon: 'grid_on', tootTip: 'Grid View'},
99
- ]
458
+ ```typescript
459
+ // writeValue(value: any): void
460
+ // Sets the value of the control
461
+ writeValue(value: any): void {
462
+ // Handle incoming form control value
463
+ // Update component state accordingly
464
+ }
100
465
 
101
- or
466
+ // registerOnChange(fn: any): void
467
+ // Registers a callback for value changes
468
+ registerOnChange(fn: any): void {
469
+ this.onChange = fn;
470
+ }
102
471
 
103
- const groupOptions = ['store', 'data', 'type', 'expiresIn', 'options']
472
+ // registerOnTouched(fn: any): void
473
+ // Registers a callback for touch events
474
+ registerOnTouched(fn: any): void {
475
+ this.onTouch = fn;
476
+ }
477
+
478
+ // setDisabledState(isDisabled: boolean): void
479
+ // Sets disabled state
480
+ setDisabledState?(isDisabled: boolean): void {
481
+ this.disabled = isDisabled;
482
+ }
104
483
  ```
105
484
 
485
+ ### Form Integration Examples
486
+
487
+ #### Reactive Forms
488
+
489
+ ```typescript
490
+ import { Component } from '@angular/core';
491
+ import { FormControl, FormGroup, Validators } from '@angular/forms';
492
+
493
+ @Component({
494
+ selector: 'app-reactive-form-toggle',
495
+ template: `
496
+ <form [formGroup]="toggleForm">
497
+ <app-button-toggle-input
498
+ formControlName="selection"
499
+ [data]="options"
500
+ [multiple]="true">
501
+ </app-button-toggle-input>
502
+
503
+ <div class="form-errors" *ngIf="toggleForm.get('selection')?.errors">
504
+ <div *ngIf="toggleForm.get('selection')?.hasError('required')">
505
+ Selection is required
506
+ </div>
507
+ <div *ngIf="toggleForm.get('selection')?.hasError('minLength')">
508
+ At least one option must be selected
509
+ </div>
510
+ </div>
511
+ </form>
512
+ `
513
+ })
514
+ export class ReactiveFormToggleComponent {
515
+ toggleForm = new FormGroup({
516
+ selection: new FormControl([], Validators.required)
517
+ });
518
+
519
+ options: ListItem[] = [
520
+ { value: 'opt1', label: 'Option 1' },
521
+ { value: 'opt2', label: 'Option 2' },
522
+ { value: 'opt3', label: 'Option 3' }
523
+ ];
524
+ }
525
+ ```
526
+
527
+ #### Template-Driven Forms
528
+
529
+ ```typescript
530
+ import { Component } from '@angular/core';
531
+
532
+ @Component({
533
+ selector: 'app-template-toggle',
534
+ template: `
535
+ <form #toggleForm="ngForm">
536
+ <app-button-toggle-input
537
+ [(ngModel)]="selectedValue"
538
+ name="toggleSelection"
539
+ [data]="options"
540
+ [multiple]="false"
541
+ required>
542
+ </app-button-toggle-input>
543
+
544
+ <div *ngIf="toggleForm.controls.toggleSelection?.invalid && toggleForm.controls.toggleSelection?.touched">
545
+ Selection is required
546
+ </div>
547
+ </form>
548
+ `
549
+ })
550
+ export class TemplateToggleComponent {
551
+ selectedValue: any = null;
552
+
553
+ options: ListItem[] = [
554
+ { value: 'single1', label: 'Single Option 1' },
555
+ { value: 'single2', label: 'Single Option 2' },
556
+ { value: 'single3', label: 'Single Option 3' }
557
+ ];
558
+ }
559
+ ```
560
+
561
+ #### Form Control Methods
562
+
563
+ ```typescript
564
+ // Setting values programmatically
565
+ this.formControl.setValue(['option1', 'option2']); // For multiple mode
566
+ this.formControl.setValue('option1'); // For single mode
567
+
568
+ // Patching values
569
+ this.formControl.patchValue(['option1']); // Partial update
570
+
571
+ // Resetting
572
+ this.formControl.reset(); // Clear selection
573
+
574
+ // Getting current value
575
+ const currentValue = this.formControl.value;
576
+
577
+ // Checking validity
578
+ const isValid = this.formControl.valid;
579
+ const errors = this.formControl.errors;
580
+
581
+ // Setting custom errors
582
+ this.formControl.setErrors({ customError: 'Custom error message' });
583
+ ```
584
+
585
+ ---
586
+
587
+ ## Module Configuration
588
+
589
+ ### ButtonToggleInputModule
590
+
591
+ **No Global Configuration Required**
592
+
593
+ The `ButtonToggleInputModule` does not provide a `forRoot()` method or global configuration options. All configuration is done at the component level through input properties.
594
+
595
+ #### Module Structure
596
+
597
+ ```typescript
598
+ @NgModule({
599
+ imports: [
600
+ CommonModule,
601
+ FormsModule,
602
+ MatIconModule,
603
+ MatButtonModule,
604
+ MatTooltipModule,
605
+ ReactiveFormsModule,
606
+ MatButtonToggleModule,
607
+ MatSlideToggleModule,
608
+ MatDividerModule,
609
+ ],
610
+ declarations: [
611
+ ButtonToggleInputComponent,
612
+ ButtonToggleDemoComponent
613
+ ],
614
+ exports: [
615
+ ButtonToggleInputComponent,
616
+ ButtonToggleDemoComponent
617
+ ]
618
+ })
619
+ export class ButtonToggleInputModule { }
620
+ ```
621
+
622
+ #### Dependencies
623
+
624
+ - **@angular/common**: Core Angular functionality
625
+ - **@angular/forms**: Form control integration (FormsModule, ReactiveFormsModule)
626
+ - **@angular/material**: Material Design components
627
+ - MatIconModule: Icon display
628
+ - MatButtonModule: Button base components
629
+ - MatTooltipModule: Tooltip functionality
630
+ - MatButtonToggleModule: Button toggle foundation
631
+ - MatSlideToggleModule: Slide toggle components
632
+ - MatDividerModule: Visual divider components
633
+
634
+ ---
635
+
636
+ ## Styling and Customization
637
+
638
+ ### CSS Classes and Styling
639
+
640
+ The component uses Material Design styling and can be customized using:
641
+
642
+ 1. **Global Material Theme**: Configure colors in your Angular Material theme
643
+ 2. **Component-specific Styles**: Add custom CSS classes
644
+ 3. **Input Properties**: Use color, lightColor, darkColor, and noBorder properties
645
+ 4. **Container Styling**: Style the parent container for layout control
646
+
647
+ ### Color Customization
648
+
649
+ ```scss
650
+ // Custom color schemes
651
+ .light-theme {
652
+ app-button-toggle-input {
653
+ --toggle-color: #2196f3;
654
+ --toggle-light-color: #e3f2fd;
655
+ --toggle-dark-color: #ffffff;
656
+ }
657
+ }
658
+
659
+ .dark-theme {
660
+ app-button-toggle-input {
661
+ --toggle-color: #64b5f6;
662
+ --toggle-light-color: #1565c0;
663
+ --toggle-dark-color: #ffffff;
664
+ }
665
+ }
666
+ ```
667
+
668
+ ### Layout Customization
669
+
670
+ ```scss
671
+ // Full width toggle
672
+ .full-width-toggle {
673
+ app-button-toggle-input {
674
+ width: 100%;
675
+
676
+ .mat-button-toggle-group {
677
+ width: 100%;
678
+
679
+ .mat-button-toggle {
680
+ flex: 1;
681
+ }
682
+ }
683
+ }
684
+ }
685
+
686
+ // Compact toggle
687
+ .compact-toggle {
688
+ app-button-toggle-input {
689
+ .mat-button-toggle {
690
+ padding: 4px 8px;
691
+ min-width: auto;
692
+ }
693
+ }
694
+ }
695
+ ```
696
+
697
+ ### Border Customization
698
+
699
+ ```scss
700
+ // Remove borders
701
+ .no-borders {
702
+ app-button-toggle-input {
703
+ .mat-button-toggle {
704
+ border: none;
705
+
706
+ &.mat-button-toggle-checked {
707
+ border: none;
708
+ }
709
+ }
710
+ }
711
+ }
712
+
713
+ // Custom border styles
714
+ .custom-borders {
715
+ app-button-toggle-input {
716
+ .mat-button-toggle {
717
+ border: 2px solid #e0e0e0;
718
+ border-radius: 8px;
719
+ margin: 2px;
720
+
721
+ &.mat-button-toggle-checked {
722
+ border-color: #2196f3;
723
+ background-color: #e3f2fd;
724
+ }
725
+ }
726
+ }
727
+ }
728
+ ```
729
+
730
+ ---
731
+
732
+ ## Accessibility
733
+
734
+ ### ARIA Support
735
+
736
+ - Buttons include proper ARIA labels and roles
737
+ - Keyboard navigation is fully supported (Tab, Enter, Space, Arrow keys)
738
+ - Screen reader friendly with appropriate labels and descriptions
739
+ - Tooltip content is accessible to assistive technologies
740
+ - Selection state is properly communicated
741
+
742
+ ### Best Practices
743
+
744
+ 1. **Provide meaningful labels** for all toggle items
745
+ 2. **Use descriptive icons** that match the toggle purpose
746
+ 3. **Set appropriate tooltips** for additional context
747
+ 4. **Consider color contrast** when using custom colors
748
+ 5. **Test with screen readers** to ensure accessibility
749
+ 6. **Use logical tab order** for keyboard navigation
750
+
751
+ ### Keyboard Navigation
752
+
753
+ | Key | Action |
754
+ |-----|--------|
755
+ | `Tab` | Navigate to next toggle item |
756
+ | `Shift+Tab` | Navigate to previous toggle item |
757
+ | `Space` | Toggle selection (single mode) |
758
+ | `Enter` | Toggle selection (single mode) |
759
+ | `Arrow Keys` | Navigate between toggle items |
760
+ | `Ctrl+Click` | Toggle selection (multiple mode) |
761
+
762
+ ---
763
+
764
+ ## Integration Examples
765
+
766
+ ### With Other UI Components
767
+
768
+ ```typescript
769
+ // Integration with display-card
770
+ @Component({
771
+ template: `
772
+ <app-display-card title="Filter Options">
773
+ <app-button-toggle-input
774
+ [data]="filterOptions"
775
+ [multiple]="true"
776
+ [toolTips]="true"
777
+ [fullWidth]="true">
778
+ </app-button-toggle-input>
779
+ </app-display-card>
780
+ `
781
+ })
782
+ export class CardWithToggleComponent {
783
+ filterOptions: ListItem[] = [
784
+ { value: 'active', label: 'Active', icon: 'check_circle' },
785
+ { value: 'pending', label: 'Pending', icon: 'schedule' },
786
+ { value: 'archived', label: 'Archived', icon: 'archive' }
787
+ ];
788
+ }
789
+ ```
790
+
791
+ ### With State Management
792
+
793
+ ```typescript
794
+ // Integration with HTTP Request Manager
795
+ @Component({
796
+ template: `
797
+ <app-button-toggle-input
798
+ [data]="viewOptions$ | async"
799
+ [multiple]="false"
800
+ (selectionChange)="handleViewChange($event)">
801
+ </app-button-toggle-input>
802
+ `
803
+ })
804
+ export class StateManagedToggleComponent {
805
+ viewOptions$ = this.viewStore.options$;
806
+
807
+ constructor(private viewStore: ViewStore) {}
808
+
809
+ handleViewChange(value: any) {
810
+ this.viewStore.setView(value);
811
+ }
812
+ }
813
+ ```
814
+
815
+ ### With Dynamic Forms
816
+
817
+ ```typescript
818
+ @Component({
819
+ template: `
820
+ <div formArrayName="toggles">
821
+ <div *ngFor="let toggle of toggleArray.controls; let i = index">
822
+ <app-button-toggle-input
823
+ [formControlName]="i"
824
+ [data]="dynamicOptions[i]">
825
+ </app-button-toggle-input>
826
+ </div>
827
+ </div>
828
+ `
829
+ })
830
+ export class DynamicFormComponent {
831
+ toggleForm = this.fb.group({
832
+ toggles: this.fb.array([
833
+ this.fb.control('option1'),
834
+ this.fb.control(['option1', 'option2']),
835
+ this.fb.control('option3')
836
+ ])
837
+ });
838
+
839
+ get toggleArray() {
840
+ return this.toggleForm.get('toggles') as FormArray;
841
+ }
842
+
843
+ dynamicOptions: ListItem[][] = [
844
+ // Options for first toggle
845
+ [{ value: 'opt1', label: 'Option 1' }, { value: 'opt2', label: 'Option 2' }],
846
+ // Options for second toggle
847
+ [{ value: 'opt3', label: 'Option 3' }, { value: 'opt4', label: 'Option 4' }],
848
+ // Options for third toggle
849
+ [{ value: 'opt5', label: 'Option 5' }, { value: 'opt6', label: 'Option 6' }]
850
+ ];
851
+ }
852
+ ```
853
+
854
+ ---
855
+
856
+ ## Performance Optimization
857
+
858
+ ### Change Detection Strategy
859
+
860
+ The demo component uses `ChangeDetectionStrategy.OnPush` for optimal performance:
861
+
862
+ ```typescript
863
+ @Component({
864
+ changeDetection: ChangeDetectionStrategy.OnPush,
865
+ // ... other config
866
+ })
867
+ export class ButtonToggleDemoComponent {
868
+ // Component logic
869
+ }
870
+ ```
871
+
872
+ ### Performance Tips
873
+
874
+ 1. **Use OnPush change detection** for better performance with large toggle arrays
875
+ 2. **Implement trackBy** for dynamic toggle lists
876
+ 3. **Avoid frequent data object recreation** to prevent unnecessary re-renders
877
+ 4. **Use immutable data patterns** for toggle option updates
878
+ 5. **Consider virtual scrolling** for very large toggle lists
879
+ 6. **Optimize tooltip content** to avoid performance issues
880
+
881
+ ---
882
+
883
+ ## Testing
884
+
885
+ ### Unit Testing Example
886
+
887
+ ```typescript
888
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
889
+ import { ButtonToggleInputComponent } from './button-toggle-input.component';
890
+ import { ListItem } from './models/list-item.model';
891
+ import { ReactiveFormsModule } from '@angular/forms';
892
+
893
+ describe('ButtonToggleInputComponent', () => {
894
+ let component: ButtonToggleInputComponent;
895
+ let fixture: ComponentFixture<ButtonToggleInputComponent>;
896
+
897
+ beforeEach(async () => {
898
+ await TestBed.configureTestingModule({
899
+ declarations: [ ButtonToggleInputComponent ],
900
+ imports: [ ReactiveFormsModule ]
901
+ }).compileComponents();
902
+
903
+ fixture = TestBed.createComponent(ButtonToggleInputComponent);
904
+ component = fixture.componentInstance;
905
+ });
906
+
907
+ it('should create', () => {
908
+ expect(component).toBeTruthy();
909
+ });
910
+
911
+ it('should display toggle items from data input', () => {
912
+ component.data = [
913
+ { value: 'opt1', label: 'Option 1' },
914
+ { value: 'opt2', label: 'Option 2' }
915
+ ];
916
+ fixture.detectChanges();
917
+
918
+ const compiled = fixture.nativeElement;
919
+ const buttons = compiled.querySelectorAll('.mat-button-toggle');
920
+ expect(buttons.length).toBe(2);
921
+ });
922
+
923
+ it('should emit value changes through form control', () => {
924
+ const formControl = new FormControl();
925
+ component.formControl = formControl;
926
+
927
+ spyOn(formControl, 'setValue');
928
+ component.writeValue('test-value');
929
+
930
+ expect(formControl.setValue).toHaveBeenCalledWith('test-value');
931
+ });
932
+
933
+ it('should support multiple selection mode', () => {
934
+ component.multiple = true;
935
+ component.data = [
936
+ { value: 'opt1', label: 'Option 1' },
937
+ { value: 'opt2', label: 'Option 2' }
938
+ ];
939
+ fixture.detectChanges();
940
+
941
+ // Test multiple selection logic
942
+ expect(component.multiple).toBe(true);
943
+ });
944
+
945
+ it('should handle tooltips correctly', () => {
946
+ component.toolTips = true;
947
+ component.data = [
948
+ { value: 'opt1', label: 'Option 1', tootTip: 'Tooltip 1' }
949
+ ];
950
+ fixture.detectChanges();
951
+
952
+ const compiled = fixture.nativeElement;
953
+ const tooltip = compiled.querySelector('[mattooltip]');
954
+ expect(tooltip).toBeTruthy();
955
+ });
956
+ });
957
+ ```
958
+
959
+ ### Integration Testing
960
+
961
+ ```typescript
962
+ import { TestBed, ComponentFixture } from '@angular/core/testing';
963
+ import { ButtonToggleInputModule } from './button-toggle-input.module';
964
+
965
+ describe('ButtonToggleInput Integration', () => {
966
+ let component: TestHostComponent;
967
+ let fixture: ComponentFixture<TestHostComponent>;
968
+
969
+ @Component({
970
+ template: `
971
+ <app-button-toggle-input
972
+ [formControl]="testControl"
973
+ [data]="testData"
974
+ [multiple]="true">
975
+ </app-button-toggle-input>
976
+ `
977
+ })
978
+ class TestHostComponent {
979
+ testControl = new FormControl();
980
+ testData: ListItem[] = [
981
+ { value: 'test1', label: 'Test 1' },
982
+ { value: 'test2', label: 'Test 2' }
983
+ ];
984
+ }
985
+
986
+ beforeEach(async () => {
987
+ await TestBed.configureTestingModule({
988
+ declarations: [ TestHostComponent ],
989
+ imports: [ ButtonToggleInputModule ]
990
+ }).compileComponents();
991
+
992
+ fixture = TestBed.createComponent(TestHostComponent);
993
+ component = fixture.componentInstance;
994
+ });
995
+
996
+ it('should integrate with form controls', () => {
997
+ expect(component.testControl).toBeDefined();
998
+ expect(component.testData.length).toBe(2);
999
+ });
1000
+
1001
+ it('should update form control value when selection changes', () => {
1002
+ fixture.detectChanges();
1003
+
1004
+ // Simulate user interaction
1005
+ const buttons = fixture.nativeElement.querySelectorAll('.mat-button-toggle');
1006
+ buttons[0].click();
1007
+
1008
+ expect(component.testControl.value).toEqual(['test1']);
1009
+ });
1010
+ });
1011
+ ```
1012
+
1013
+ ---
1014
+
1015
+ ## Troubleshooting
1016
+
1017
+ ### Common Issues
1018
+
1019
+ 1. **Form control not working**: Ensure ReactiveFormsModule is imported
1020
+ 2. **Icons not displaying**: Verify MatIconModule is imported and Material Icons are loaded
1021
+ 3. **Tooltips not showing**: Check that MatTooltipModule is imported
1022
+ 4. **Styling issues**: Ensure Material theme is properly configured
1023
+ 5. **Change detection warnings**: Consider using OnPush change detection strategy
1024
+
1025
+ ### Debug Mode
1026
+
1027
+ ```typescript
1028
+ // Add debugging to track form control changes
1029
+ @Component({
1030
+ template: `
1031
+ <div class="debug-info">
1032
+ Form Control Value: {{ formControl.value | json }}<br>
1033
+ Form Control Valid: {{ formControl.valid }}<br>
1034
+ Data Length: {{ data?.length || 0 }}<br>
1035
+ Multiple Mode: {{ multiple }}
1036
+ </div>
1037
+
1038
+ <app-button-toggle-input
1039
+ [formControl]="formControl"
1040
+ [data]="data"
1041
+ [multiple]="multiple">
1042
+ </app-button-toggle-input>
1043
+ `
1044
+ })
1045
+ export class DebugToggleComponent {
1046
+ formControl = new FormControl();
1047
+ data: ListItem[] = [];
1048
+ multiple = false;
1049
+
1050
+ constructor() {
1051
+ this.formControl.valueChanges.subscribe(value => {
1052
+ console.log('Toggle value changed:', value);
1053
+ });
1054
+ }
1055
+ }
1056
+ ```
1057
+
1058
+ ### Performance Issues
1059
+
1060
+ ```typescript
1061
+ // Optimize for large datasets
1062
+ @Component({
1063
+ changeDetection: ChangeDetectionStrategy.OnPush,
1064
+ template: `
1065
+ <app-button-toggle-input
1066
+ [formControl]="formControl"
1067
+ [data]="data"
1068
+ [multiple]="multiple">
1069
+ </app-button-toggle-input>
1070
+ `
1071
+ })
1072
+ export class OptimizedToggleComponent {
1073
+ // Use immutable data updates
1074
+ updateData(newData: ListItem[]) {
1075
+ this.data = [...newData]; // Create new array reference
1076
+ }
1077
+ }
1078
+ ```