ngx-opalbytes-components 1.25.1 → 1.27.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.
Files changed (24) hide show
  1. package/README.md +497 -0
  2. package/fesm2022/ngx-opalbytes-components.mjs +890 -2
  3. package/fesm2022/ngx-opalbytes-components.mjs.map +1 -1
  4. package/package.json +1 -1
  5. package/src/lib/shared/components/checkbox/checkbox.component.css +131 -0
  6. package/src/lib/shared/components/checkbox/checkbox.component.html +84 -0
  7. package/src/lib/shared/components/checkbox/checkbox.component.spec.ts +344 -0
  8. package/src/lib/shared/components/checkbox/checkbox.component.ts +258 -0
  9. package/src/lib/shared/components/checkbox/checkbox.stories.ts +240 -0
  10. package/src/lib/shared/components/index.ts +4 -0
  11. package/src/lib/shared/components/radio/radio-button.component.css +95 -0
  12. package/src/lib/shared/components/radio/radio-button.component.html +32 -0
  13. package/src/lib/shared/components/radio/radio-button.component.ts +133 -0
  14. package/src/lib/shared/components/radio/radio-button.stories.ts +94 -0
  15. package/src/lib/shared/components/radio/radio-group.component.css +21 -0
  16. package/src/lib/shared/components/radio/radio-group.component.ts +216 -0
  17. package/src/lib/shared/components/radio/radio.component.spec.ts +128 -0
  18. package/src/lib/shared/components/radio/radio.stories.ts +128 -0
  19. package/src/lib/shared/components/slide-toggle/slide-toggle.component.css +135 -0
  20. package/src/lib/shared/components/slide-toggle/slide-toggle.component.html +48 -0
  21. package/src/lib/shared/components/slide-toggle/slide-toggle.component.spec.ts +155 -0
  22. package/src/lib/shared/components/slide-toggle/slide-toggle.component.ts +233 -0
  23. package/src/lib/shared/components/slide-toggle/slide-toggle.stories.ts +165 -0
  24. package/types/ngx-opalbytes-components.d.ts +228 -2
@@ -0,0 +1,128 @@
1
+ import { FormsModule } from '@angular/forms';
2
+
3
+ import type { Meta, StoryObj } from '@storybook/angular-vite';
4
+ import { fn } from 'storybook/test';
5
+
6
+ import { CaoRadioButtonComponent } from './radio-button.component';
7
+ import { CaoRadioGroupComponent } from './radio-group.component';
8
+
9
+ const meta: Meta<CaoRadioGroupComponent> = {
10
+ title: 'Componentes/Radio',
11
+ component: CaoRadioGroupComponent,
12
+ tags: ['autodocs'],
13
+ decorators: [
14
+ (story) => ({
15
+ ...story,
16
+ moduleMetadata: {
17
+ imports: [FormsModule, CaoRadioGroupComponent, CaoRadioButtonComponent],
18
+ },
19
+ }),
20
+ ],
21
+ argTypes: {
22
+ orientation: { control: 'select', options: ['vertical', 'horizontal'] },
23
+ labelPosition: { control: 'select', options: ['before', 'after'] },
24
+ isDisabled: { control: 'boolean' },
25
+ disabled: { control: 'boolean' },
26
+ required: { control: 'boolean' },
27
+ color: { control: 'color' },
28
+ unselectedColor: { control: 'color' },
29
+ disabledColor: { control: 'color' },
30
+ labelColor: { control: 'color' },
31
+ disabledLabelColor: { control: 'color' },
32
+ focusRingColor: { control: 'color' },
33
+ size: { control: 'text' },
34
+ animationDuration: { control: 'text' },
35
+ gap: { control: 'text' },
36
+ },
37
+ args: {
38
+ value: 'option-1',
39
+ orientation: 'vertical',
40
+ labelPosition: 'after',
41
+ isDisabled: false,
42
+ required: false,
43
+ valueChange: fn(),
44
+ selectionChange: fn(),
45
+ },
46
+ render: (args) => ({
47
+ props: args,
48
+ template: `
49
+ <cao-radio-group
50
+ [(ngModel)]="value"
51
+ [orientation]="orientation"
52
+ [labelPosition]="labelPosition"
53
+ [isDisabled]="isDisabled"
54
+ [required]="required"
55
+ [color]="color"
56
+ [unselectedColor]="unselectedColor"
57
+ [disabledColor]="disabledColor"
58
+ [labelColor]="labelColor"
59
+ [disabledLabelColor]="disabledLabelColor"
60
+ [focusRingColor]="focusRingColor"
61
+ [size]="size"
62
+ [animationDuration]="animationDuration"
63
+ [gap]="gap"
64
+ (valueChange)="valueChange($event)"
65
+ (selectionChange)="selectionChange($event)">
66
+ <cao-radio-button value="option-1" label="Opção 1"></cao-radio-button>
67
+ <cao-radio-button value="option-2" label="Opção 2"></cao-radio-button>
68
+ <cao-radio-button value="option-3" label="Opção 3"></cao-radio-button>
69
+ </cao-radio-group>
70
+ `,
71
+ }),
72
+ };
73
+
74
+ export default meta;
75
+ type Story = StoryObj<CaoRadioGroupComponent>;
76
+
77
+ export const Padrao: Story = {};
78
+
79
+ export const Horizontal: Story = {
80
+ args: {
81
+ orientation: 'horizontal',
82
+ gap: '16px',
83
+ },
84
+ };
85
+
86
+ export const Desabilitado: Story = {
87
+ args: {
88
+ isDisabled: true,
89
+ },
90
+ };
91
+
92
+ export const OpcaoDesabilitada: Story = {
93
+ render: (args) => ({
94
+ props: args,
95
+ template: `
96
+ <cao-radio-group
97
+ [(ngModel)]="value"
98
+ [orientation]="orientation"
99
+ [labelPosition]="labelPosition"
100
+ [isDisabled]="isDisabled"
101
+ [color]="color"
102
+ [unselectedColor]="unselectedColor"
103
+ [disabledColor]="disabledColor"
104
+ (valueChange)="valueChange($event)"
105
+ (selectionChange)="selectionChange($event)">
106
+ <cao-radio-button value="option-1" label="Disponível"></cao-radio-button>
107
+ <cao-radio-button value="option-2" label="Indisponível" [isDisabled]="true"></cao-radio-button>
108
+ <cao-radio-button value="option-3" label="Disponível"></cao-radio-button>
109
+ </cao-radio-group>
110
+ `,
111
+ }),
112
+ };
113
+
114
+ export const CoresCustomizadas: Story = {
115
+ args: {
116
+ color: '#16a34a',
117
+ unselectedColor: '#94a3b8',
118
+ labelColor: '#166534',
119
+ size: '24px',
120
+ gap: '12px',
121
+ },
122
+ };
123
+
124
+ export const LabelAntes: Story = {
125
+ args: {
126
+ labelPosition: 'before',
127
+ },
128
+ };
@@ -0,0 +1,135 @@
1
+ :host {
2
+ --cao-slide-toggle-track: #d1d5db;
3
+ --cao-slide-toggle-track-checked: #4f46e5;
4
+ --cao-slide-toggle-thumb: #ffffff;
5
+ --cao-slide-toggle-thumb-checked: #ffffff;
6
+ --cao-slide-toggle-track-disabled: #e5e7eb;
7
+ --cao-slide-toggle-thumb-disabled: #f3f4f6;
8
+ --cao-slide-toggle-label: #111827;
9
+ --cao-slide-toggle-label-disabled: #9ca3af;
10
+ --cao-slide-toggle-focus-ring: rgba(79, 70, 229, 0.35);
11
+ --cao-slide-toggle-track-width: 36px;
12
+ --cao-slide-toggle-track-height: 20px;
13
+ --cao-slide-toggle-thumb-size: 16px;
14
+ --cao-slide-toggle-thumb-icon-size: 12px;
15
+ --cao-slide-toggle-thumb-icon-color: currentColor;
16
+ --cao-slide-toggle-duration: 200ms;
17
+
18
+ display: inline-flex;
19
+ }
20
+
21
+ .cao-slide-toggle {
22
+ display: inline-flex;
23
+ align-items: center;
24
+ gap: 8px;
25
+ cursor: pointer;
26
+ user-select: none;
27
+ }
28
+
29
+ .cao-slide-toggle--disabled {
30
+ cursor: not-allowed;
31
+ }
32
+
33
+ .cao-slide-toggle__input {
34
+ position: absolute;
35
+ width: 1px;
36
+ height: 1px;
37
+ margin: -1px;
38
+ padding: 0;
39
+ border: 0;
40
+ overflow: hidden;
41
+ clip: rect(0 0 0 0);
42
+ clip-path: inset(50%);
43
+ white-space: nowrap;
44
+ }
45
+
46
+ .cao-slide-toggle__track {
47
+ position: relative;
48
+ flex: none;
49
+ width: var(--cao-slide-toggle-track-width);
50
+ height: var(--cao-slide-toggle-track-height);
51
+ border-radius: 999px;
52
+ background-color: var(--cao-slide-toggle-track);
53
+ transition: background-color var(--cao-slide-toggle-duration) ease;
54
+ }
55
+
56
+ .cao-slide-toggle__thumb {
57
+ position: absolute;
58
+ top: 50%;
59
+ left: 2px;
60
+ display: flex;
61
+ align-items: center;
62
+ justify-content: center;
63
+ width: var(--cao-slide-toggle-thumb-size);
64
+ height: var(--cao-slide-toggle-thumb-size);
65
+ overflow: hidden;
66
+ border-radius: 50%;
67
+ background-color: var(--cao-slide-toggle-thumb);
68
+ transform: translateY(-50%);
69
+ transition:
70
+ transform var(--cao-slide-toggle-duration) ease,
71
+ background-color var(--cao-slide-toggle-duration) ease;
72
+ }
73
+
74
+ .cao-slide-toggle__thumb-icon {
75
+ flex: none;
76
+ width: var(--cao-slide-toggle-thumb-icon-size);
77
+ height: var(--cao-slide-toggle-thumb-icon-size);
78
+ color: var(--cao-slide-toggle-thumb-icon-color);
79
+ pointer-events: none;
80
+ object-fit: contain;
81
+ }
82
+
83
+ .cao-slide-toggle__label {
84
+ color: var(--cao-slide-toggle-label);
85
+ font-size: 0.875rem;
86
+ line-height: 1.25rem;
87
+ }
88
+
89
+ .cao-slide-toggle__label:empty {
90
+ display: none;
91
+ }
92
+
93
+ .cao-slide-toggle--checked .cao-slide-toggle__track {
94
+ background-color: var(--cao-slide-toggle-track-checked);
95
+ }
96
+
97
+ .cao-slide-toggle--checked .cao-slide-toggle__thumb {
98
+ background-color: var(--cao-slide-toggle-thumb-checked);
99
+ transform: translate(
100
+ calc(
101
+ var(--cao-slide-toggle-track-width) -
102
+ var(--cao-slide-toggle-thumb-size) -
103
+ 4px
104
+ ),
105
+ -50%
106
+ );
107
+ }
108
+
109
+ .cao-slide-toggle--disabled .cao-slide-toggle__track {
110
+ background-color: var(--cao-slide-toggle-track-disabled);
111
+ }
112
+
113
+ .cao-slide-toggle--disabled .cao-slide-toggle__thumb {
114
+ background-color: var(--cao-slide-toggle-thumb-disabled);
115
+ }
116
+
117
+ .cao-slide-toggle--disabled .cao-slide-toggle__thumb-icon {
118
+ opacity: 0.65;
119
+ }
120
+
121
+ .cao-slide-toggle--disabled .cao-slide-toggle__label {
122
+ color: var(--cao-slide-toggle-label-disabled);
123
+ }
124
+
125
+ .cao-slide-toggle__input:focus-visible + .cao-slide-toggle__track {
126
+ box-shadow: 0 0 0 3px var(--cao-slide-toggle-focus-ring);
127
+ }
128
+
129
+ .cao-slide-toggle--label-before .cao-slide-toggle__track {
130
+ order: 2;
131
+ }
132
+
133
+ .cao-slide-toggle--label-before .cao-slide-toggle__label {
134
+ order: 1;
135
+ }
@@ -0,0 +1,48 @@
1
+ <label
2
+ class="cao-slide-toggle"
3
+ [class.cao-slide-toggle--checked]="checked"
4
+ [class.cao-slide-toggle--disabled]="isDisabled"
5
+ [class.cao-slide-toggle--label-before]="labelPosition === 'before'">
6
+ <input
7
+ #input
8
+ class="cao-slide-toggle__input"
9
+ type="checkbox"
10
+ role="switch"
11
+ [checked]="checked"
12
+ [disabled]="isDisabled"
13
+ [required]="required"
14
+ [attr.name]="name || null"
15
+ [attr.id]="id || null"
16
+ [attr.aria-label]="ariaLabel || null"
17
+ [attr.data-cy]="dataCy || null"
18
+ (change)="onInputChange($event)"
19
+ (blur)="onBlur()"
20
+ (keydown.enter)="onEnter($event)" />
21
+
22
+ <span class="cao-slide-toggle__track" aria-hidden="true">
23
+ <span class="cao-slide-toggle__thumb">
24
+ @if (resolvedThumbIcon) {
25
+ @if (isLucideThumbIcon) {
26
+ <svg
27
+ class="cao-slide-toggle__thumb-icon"
28
+ [lucideIcon]="resolvedThumbIcon"
29
+ [size]="thumbIconSize"
30
+ [strokeWidth]="thumbIconStrokeWidth"></svg>
31
+ } @else {
32
+ <img
33
+ class="cao-slide-toggle__thumb-icon"
34
+ alt=""
35
+ [src]="resolvedThumbIcon" />
36
+ }
37
+ }
38
+ </span>
39
+ </span>
40
+
41
+ <span class="cao-slide-toggle__label">
42
+ @if (label) {
43
+ {{ label }}
44
+ } @else {
45
+ <ng-content />
46
+ }
47
+ </span>
48
+ </label>
@@ -0,0 +1,155 @@
1
+ import { ComponentFixture, TestBed } from "@angular/core/testing";
2
+ import { LucideCheck, LucideX, provideLucideIcons } from "@lucide/angular";
3
+
4
+ import { CaoSlideToggleComponent } from "./slide-toggle.component";
5
+
6
+ describe("CaoSlideToggleComponent", () => {
7
+ let component: CaoSlideToggleComponent;
8
+ let fixture: ComponentFixture<CaoSlideToggleComponent>;
9
+
10
+ beforeEach(async () => {
11
+ await TestBed.configureTestingModule({
12
+ imports: [CaoSlideToggleComponent],
13
+ providers: [provideLucideIcons(LucideCheck, LucideX)],
14
+ }).compileComponents();
15
+
16
+ fixture = TestBed.createComponent(CaoSlideToggleComponent);
17
+ component = fixture.componentInstance;
18
+ fixture.detectChanges();
19
+ });
20
+
21
+ it("should create", () => {
22
+ expect(component).toBeTruthy();
23
+ });
24
+
25
+ it("should toggle and emit changes", () => {
26
+ const checkedChange = vi.fn();
27
+ const toggleChange = vi.fn();
28
+ const input: HTMLInputElement =
29
+ fixture.nativeElement.querySelector("input");
30
+
31
+ component.checkedChange.subscribe(checkedChange);
32
+ component.toggleChange.subscribe(toggleChange);
33
+
34
+ input.checked = true;
35
+ input.dispatchEvent(new Event("change"));
36
+ fixture.detectChanges();
37
+
38
+ expect(component.checked).toBe(true);
39
+ expect(checkedChange).toHaveBeenCalledWith(true);
40
+ expect(toggleChange).toHaveBeenCalledWith({
41
+ checked: true,
42
+ source: component,
43
+ });
44
+ });
45
+
46
+ it("should not toggle when disabled", () => {
47
+ const input: HTMLInputElement =
48
+ fixture.nativeElement.querySelector("input");
49
+ component.isDisabled = true;
50
+ fixture.detectChanges();
51
+
52
+ input.checked = true;
53
+ input.dispatchEvent(new Event("change"));
54
+ fixture.detectChanges();
55
+
56
+ expect(component.checked).toBe(false);
57
+ expect(input.disabled).toBe(true);
58
+ });
59
+
60
+ it("should support ControlValueAccessor", () => {
61
+ const onChange = vi.fn();
62
+ const onTouched = vi.fn();
63
+
64
+ component.registerOnChange(onChange);
65
+ component.registerOnTouched(onTouched);
66
+ component.writeValue(true);
67
+ component.onBlur();
68
+
69
+ expect(component.checked).toBe(true);
70
+ expect(onTouched).toHaveBeenCalled();
71
+
72
+ component.toggle();
73
+ expect(onChange).toHaveBeenCalledWith(false);
74
+ });
75
+
76
+ it("should enable and disable programmatically", () => {
77
+ component.disable();
78
+ expect(component.isDisabled).toBe(true);
79
+
80
+ component.enable();
81
+ expect(component.isDisabled).toBe(false);
82
+ });
83
+
84
+ it("should disable through setDisabledState", () => {
85
+ component.setDisabledState(true);
86
+ expect(component.isDisabled).toBe(true);
87
+ });
88
+
89
+ it("should render Lucide icons for both states", () => {
90
+ fixture.componentRef.setInput("thumbIcon", "x");
91
+ fixture.componentRef.setInput("thumbCheckedIcon", "check");
92
+ fixture.componentRef.setInput("isLucideThumbIcon", true);
93
+ fixture.componentRef.setInput("thumbIconColor", "#6b7280");
94
+ fixture.componentRef.setInput("thumbCheckedIconColor", "#16a34a");
95
+ fixture.componentRef.setInput("checked", true);
96
+ fixture.detectChanges();
97
+
98
+ const icon = fixture.nativeElement.querySelector(
99
+ ".cao-slide-toggle__thumb svg",
100
+ );
101
+
102
+ expect(
103
+ fixture.nativeElement.querySelector(".cao-slide-toggle__thumb img"),
104
+ ).toBeNull();
105
+ expect(icon).toBeTruthy();
106
+ expect(component.resolvedThumbIcon).toBe("check");
107
+ expect(component.resolvedThumbIconColor).toBe("#16a34a");
108
+
109
+ fixture.componentRef.setInput("checked", false);
110
+ fixture.detectChanges();
111
+
112
+ expect(component.resolvedThumbIcon).toBe("x");
113
+ expect(component.resolvedThumbIconColor).toBe("#6b7280");
114
+ expect(
115
+ fixture.nativeElement.querySelector(".cao-slide-toggle__thumb svg"),
116
+ ).toBeTruthy();
117
+ });
118
+
119
+ it("should fall back to thumbIcon when thumbCheckedIcon is empty", () => {
120
+ fixture.componentRef.setInput("thumbIcon", "check");
121
+ fixture.componentRef.setInput("isLucideThumbIcon", true);
122
+ fixture.componentRef.setInput("checked", true);
123
+ fixture.detectChanges();
124
+
125
+ expect(component.resolvedThumbIcon).toBe("check");
126
+ expect(
127
+ fixture.nativeElement.querySelector(".cao-slide-toggle__thumb svg"),
128
+ ).toBeTruthy();
129
+ });
130
+
131
+ it("should render an image when isLucideThumbIcon is false", () => {
132
+ fixture.componentRef.setInput("thumbIcon", "icon.svg");
133
+ fixture.detectChanges();
134
+
135
+ const image: HTMLImageElement = fixture.nativeElement.querySelector(
136
+ ".cao-slide-toggle__thumb img",
137
+ );
138
+
139
+ expect(image).toBeTruthy();
140
+ expect(image.getAttribute("src")).toBe("icon.svg");
141
+ expect(
142
+ fixture.nativeElement.querySelector(".cao-slide-toggle__thumb svg"),
143
+ ).toBeNull();
144
+ });
145
+
146
+ it("should not render an icon when no thumb icon is provided", () => {
147
+ fixture.detectChanges();
148
+
149
+ expect(
150
+ fixture.nativeElement.querySelector(
151
+ ".cao-slide-toggle__thumb svg, .cao-slide-toggle__thumb img",
152
+ ),
153
+ ).toBeNull();
154
+ });
155
+ });
@@ -0,0 +1,233 @@
1
+ import { CommonModule } from "@angular/common";
2
+ import {
3
+ ChangeDetectionStrategy,
4
+ ChangeDetectorRef,
5
+ Component,
6
+ EventEmitter,
7
+ forwardRef,
8
+ HostBinding,
9
+ inject,
10
+ Input,
11
+ Output,
12
+ } from "@angular/core";
13
+ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
14
+
15
+ import { LucideDynamicIcon } from "@lucide/angular";
16
+
17
+ export interface ICaoSlideToggleChangeEvent {
18
+ checked: boolean;
19
+ source: CaoSlideToggleComponent;
20
+ }
21
+
22
+ @Component({
23
+ selector: "cao-slide-toggle",
24
+ standalone: true,
25
+ imports: [CommonModule, LucideDynamicIcon],
26
+ templateUrl: "./slide-toggle.component.html",
27
+ styleUrl: "./slide-toggle.component.css",
28
+ changeDetection: ChangeDetectionStrategy.OnPush,
29
+ providers: [
30
+ {
31
+ provide: NG_VALUE_ACCESSOR,
32
+ useExisting: forwardRef(() => CaoSlideToggleComponent),
33
+ multi: true,
34
+ },
35
+ ],
36
+ })
37
+ export class CaoSlideToggleComponent implements ControlValueAccessor {
38
+ @Input() checked = false;
39
+ @Input() isDisabled = false;
40
+ @Input() label = "";
41
+ @Input() labelPosition: "before" | "after" = "after";
42
+ @Input() name = "";
43
+ @Input() id = "";
44
+ @Input() ariaLabel = "";
45
+ @Input() dataCy = "";
46
+ @Input() required = false;
47
+
48
+ @Input() trackColor?: string;
49
+ @Input() trackCheckedColor?: string;
50
+ @Input() thumbColor?: string;
51
+ @Input() thumbCheckedColor?: string;
52
+ @Input() disabledTrackColor?: string;
53
+ @Input() disabledThumbColor?: string;
54
+ @Input() labelColor?: string;
55
+ @Input() disabledLabelColor?: string;
56
+ @Input() focusRingColor?: string;
57
+ @Input() trackWidth?: string;
58
+ @Input() trackHeight?: string;
59
+ @Input() thumbSize?: string;
60
+ @Input() animationDuration?: string;
61
+
62
+ @Input() thumbIcon = "";
63
+ @Input() thumbCheckedIcon = "";
64
+ @Input() isLucideThumbIcon = false;
65
+ @Input() thumbIconColor = "";
66
+ @Input() thumbCheckedIconColor = "";
67
+ @Input() thumbIconSize: string | number = 12;
68
+ @Input() thumbIconStrokeWidth: string | number = 2;
69
+
70
+ @Input() set disabled(value: boolean) {
71
+ this.isDisabled = value;
72
+ this.cdr.markForCheck();
73
+ }
74
+
75
+ get disabled(): boolean {
76
+ return this.isDisabled;
77
+ }
78
+
79
+ @Output() readonly checkedChange = new EventEmitter<boolean>();
80
+ @Output() readonly toggleChange = new EventEmitter<ICaoSlideToggleChangeEvent>();
81
+
82
+ private readonly cdr = inject(ChangeDetectorRef);
83
+ private onChange: (value: boolean) => void = () => {};
84
+ private onTouched: () => void = () => {};
85
+
86
+ get resolvedThumbIcon(): string {
87
+ return this.checked && this.thumbCheckedIcon
88
+ ? this.thumbCheckedIcon
89
+ : this.thumbIcon;
90
+ }
91
+
92
+ get resolvedThumbIconColor(): string {
93
+ return this.checked
94
+ ? this.thumbCheckedIconColor || this.thumbIconColor
95
+ : this.thumbIconColor;
96
+ }
97
+
98
+ @HostBinding("style")
99
+ get hostStyles(): Record<string, string> {
100
+ const styles: Record<string, string> = {};
101
+
102
+ if (this.trackColor) {
103
+ styles["--cao-slide-toggle-track"] = this.trackColor;
104
+ }
105
+ if (this.trackCheckedColor) {
106
+ styles["--cao-slide-toggle-track-checked"] = this.trackCheckedColor;
107
+ }
108
+ if (this.thumbColor) {
109
+ styles["--cao-slide-toggle-thumb"] = this.thumbColor;
110
+ }
111
+ if (this.thumbCheckedColor) {
112
+ styles["--cao-slide-toggle-thumb-checked"] = this.thumbCheckedColor;
113
+ }
114
+ if (this.disabledTrackColor) {
115
+ styles["--cao-slide-toggle-track-disabled"] = this.disabledTrackColor;
116
+ }
117
+ if (this.disabledThumbColor) {
118
+ styles["--cao-slide-toggle-thumb-disabled"] = this.disabledThumbColor;
119
+ }
120
+ if (this.labelColor) {
121
+ styles["--cao-slide-toggle-label"] = this.labelColor;
122
+ }
123
+ if (this.disabledLabelColor) {
124
+ styles["--cao-slide-toggle-label-disabled"] = this.disabledLabelColor;
125
+ }
126
+ if (this.focusRingColor) {
127
+ styles["--cao-slide-toggle-focus-ring"] = this.focusRingColor;
128
+ }
129
+ if (this.trackWidth) {
130
+ styles["--cao-slide-toggle-track-width"] = this.trackWidth;
131
+ }
132
+ if (this.trackHeight) {
133
+ styles["--cao-slide-toggle-track-height"] = this.trackHeight;
134
+ }
135
+ if (this.thumbSize) {
136
+ styles["--cao-slide-toggle-thumb-size"] = this.thumbSize;
137
+ }
138
+ if (this.animationDuration) {
139
+ styles["--cao-slide-toggle-duration"] = this.animationDuration;
140
+ }
141
+ if (this.resolvedThumbIconColor) {
142
+ styles["--cao-slide-toggle-thumb-icon-color"] = this.resolvedThumbIconColor;
143
+ }
144
+
145
+ styles["--cao-slide-toggle-thumb-icon-size"] = this.toCssLength(
146
+ this.thumbIconSize,
147
+ );
148
+
149
+ return styles;
150
+ }
151
+
152
+ toggle(): void {
153
+ if (this.isDisabled) {
154
+ return;
155
+ }
156
+
157
+ this.updateChecked(!this.checked, true);
158
+ }
159
+
160
+ setChecked(value: boolean): void {
161
+ if (this.isDisabled) {
162
+ return;
163
+ }
164
+
165
+ this.updateChecked(value, true);
166
+ }
167
+
168
+ enable(): void {
169
+ this.isDisabled = false;
170
+ this.cdr.markForCheck();
171
+ }
172
+
173
+ disable(): void {
174
+ this.isDisabled = true;
175
+ this.cdr.markForCheck();
176
+ }
177
+
178
+ onInputChange(event: Event): void {
179
+ const target = event.target as HTMLInputElement;
180
+ this.updateChecked(target.checked, true);
181
+ }
182
+
183
+ onEnter(event: Event): void {
184
+ if (this.isDisabled) {
185
+ return;
186
+ }
187
+
188
+ event.preventDefault();
189
+ this.toggle();
190
+ }
191
+
192
+ onBlur(): void {
193
+ this.onTouched();
194
+ }
195
+
196
+ writeValue(value: boolean | null): void {
197
+ this.checked = !!value;
198
+ this.cdr.markForCheck();
199
+ }
200
+
201
+ registerOnChange(fn: (value: boolean) => void): void {
202
+ this.onChange = fn;
203
+ }
204
+
205
+ registerOnTouched(fn: () => void): void {
206
+ this.onTouched = fn;
207
+ }
208
+
209
+ setDisabledState(isDisabled: boolean): void {
210
+ this.isDisabled = isDisabled;
211
+ this.cdr.markForCheck();
212
+ }
213
+
214
+ private toCssLength(value: string | number): string {
215
+ return typeof value === "number" ? `${value}px` : value;
216
+ }
217
+
218
+ private updateChecked(value: boolean, emit: boolean): void {
219
+ if (this.isDisabled) {
220
+ return;
221
+ }
222
+
223
+ this.checked = value;
224
+ this.onChange(value);
225
+
226
+ if (emit) {
227
+ this.checkedChange.emit(value);
228
+ this.toggleChange.emit({ checked: value, source: this });
229
+ }
230
+
231
+ this.cdr.markForCheck();
232
+ }
233
+ }