@ng-zen/cli 22.0.0 β†’ 22.1.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/CHANGELOG.md CHANGED
@@ -1,21 +1,7 @@
1
- ## [22.0.0](https://github.com/kstepien3/ng-zen/compare/v21.2.0...v22.0.0) (2026-06-16)
2
-
3
- ### ⚠ BREAKING CHANGES
4
-
5
- * **deps:** clean up unused deps and update dependencies (#414)
6
-
7
- ### chore
8
-
9
- * **deps:** clean up unused deps and update dependencies ([#414](https://github.com/kstepien3/ng-zen/issues/414)) ([04b8c7d](https://github.com/kstepien3/ng-zen/commit/04b8c7da59e70181147f105e95b6471234ff21f5))
1
+ ## [22.1.0](https://github.com/kstepien3/ng-zen/compare/v22.0.0...v22.1.0) (2026-06-23)
10
2
 
11
3
  ### πŸš€ New Features
12
4
 
13
- * **button:** enhance component's sizes, variants, and styling updates ([#407](https://github.com/kstepien3/ng-zen/issues/407)) ([f32aad3](https://github.com/kstepien3/ng-zen/commit/f32aad35d5853f832f2eea0d8157884ca7f5cc3e))
14
- * **button:** migrate to dynamic CSS variables and refactor styles ([#411](https://github.com/kstepien3/ng-zen/issues/411)) ([e4464f7](https://github.com/kstepien3/ng-zen/commit/e4464f7580c91f0de30d9139177655915430fd43))
15
- * **popover:** improve popover styles, stories, docs ([#404](https://github.com/kstepien3/ng-zen/issues/404)) ([6515443](https://github.com/kstepien3/ng-zen/commit/65154436cde09f77909638ef84e2213b04f2862c))
16
-
17
- ### πŸ“š Documentation
18
-
19
- * **README:** add DeepWiki link ([#409](https://github.com/kstepien3/ng-zen/issues/409)) ([7875347](https://github.com/kstepien3/ng-zen/commit/78753471c6e56036788c6371ce0f063ab06b524b))
5
+ * **ui:** migrate controls for signals and clean up ([#417](https://github.com/kstepien3/ng-zen/issues/417)) ([1525bc1](https://github.com/kstepien3/ng-zen/commit/1525bc13b10ffc8c0e4281beb3c2eb9771c08675))
20
6
 
21
7
  Development mode - changelog will be generated during build.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-zen/cli",
3
- "version": "22.0.0",
3
+ "version": "22.1.0",
4
4
  "description": "Angular UI components generator – Zen UI Kit CLI for schematics-based creation of customizable components like avatar, button, checkbox, divider, form-control, icon, input, skeleton, switch, textarea with Storybook demos.",
5
5
  "license": "BSD-2-Clause",
6
6
  "private": false,
@@ -1,4 +1,4 @@
1
- import { ChangeDetectionStrategy, Component } from '@angular/core';
1
+ import { Component } from '@angular/core';
2
2
 
3
3
  /**
4
4
  * ZenAlert is a reusable alert component designed to provide
@@ -42,6 +42,5 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
42
42
  </div>
43
43
  `,
44
44
  styleUrl: './alert.scss',
45
- changeDetection: ChangeDetectionStrategy.OnPush,
46
45
  })
47
46
  export class ZenAlert {}
@@ -1,5 +1,5 @@
1
1
  import { NgOptimizedImage } from '@angular/common';
2
- import { ChangeDetectionStrategy, Component, input } from '@angular/core';
2
+ import { Component, input } from '@angular/core';
3
3
 
4
4
  /**
5
5
  * A component to display an avatar image.
@@ -46,7 +46,6 @@ import { ChangeDetectionStrategy, Component, input } from '@angular/core';
46
46
  }
47
47
  `,
48
48
  styleUrl: './avatar.scss',
49
- changeDetection: ChangeDetectionStrategy.OnPush,
50
49
  })
51
50
  export class ZenAvatar {
52
51
  /** Source path for the image which will be processed by Angular's image loader. */
@@ -1,4 +1,4 @@
1
- import { ChangeDetectionStrategy, Component, input } from '@angular/core';
1
+ import { Component, input } from '@angular/core';
2
2
 
3
3
  /**
4
4
  * ZenButton is a reusable button component designed to provide
@@ -39,7 +39,6 @@ import { ChangeDetectionStrategy, Component, input } from '@angular/core';
39
39
  <ng-content />
40
40
  `,
41
41
  styleUrl: './button.scss',
42
- changeDetection: ChangeDetectionStrategy.OnPush,
43
42
  host: {
44
43
  '[attr.type]': '"button"',
45
44
  '[attr.data-size]': 'size()',
@@ -1,5 +1,9 @@
1
+ import { NgComponentOutlet } from '@angular/common';
2
+ import { Component, signal } from '@angular/core';
3
+ import { form, FormField, required } from '@angular/forms/signals';
1
4
  import { Meta, StoryObj } from '@storybook/angular';
2
5
 
6
+ import FormControlStories from '../form-control/form-control.stories';
3
7
  import { ZenCheckbox } from './checkbox';
4
8
 
5
9
  type Options = ZenCheckbox;
@@ -8,53 +12,26 @@ export default {
8
12
  title: 'Ui/Checkbox',
9
13
  component: ZenCheckbox,
10
14
  argTypes: {
15
+ ...FormControlStories.argTypes,
11
16
  value: {
12
17
  table: {
13
18
  category: 'models',
14
- type: {
15
- summary: 'boolean | null',
16
- },
17
- defaultValue: {
18
- summary: 'false',
19
- },
19
+ type: { summary: 'boolean | null' },
20
+ defaultValue: { summary: 'false' },
20
21
  },
21
- control: 'radio',
22
+ control: 'radio' as const,
22
23
  options: [true, false, null],
23
24
  },
24
- disabled: {
25
- control: 'boolean',
26
- table: {
27
- category: 'models',
28
- type: {
29
- summary: 'boolean',
30
- },
31
- },
32
- },
33
- required: {
34
- control: 'boolean',
35
- table: {
36
- category: 'inputs',
37
- type: {
38
- summary: 'boolean',
39
- },
40
- defaultValue: {
41
- summary: 'false',
42
- },
43
- },
44
- },
45
25
  onInput: {
46
26
  table: {
47
27
  readonly: true,
48
- type: {
49
- summary: '(value: boolean | null) => void',
50
- },
28
+ type: { summary: '(value: boolean | null) => void' },
51
29
  },
52
30
  },
53
31
  },
54
32
  args: {
33
+ ...FormControlStories.args,
55
34
  value: false,
56
- disabled: false,
57
- required: false,
58
35
  },
59
36
  } satisfies Meta<Options>;
60
37
 
@@ -72,3 +49,52 @@ export const WithLabel: Story = {
72
49
  `,
73
50
  }),
74
51
  };
52
+
53
+ export const WithSignalForm: Story = {
54
+ render: () => ({
55
+ moduleMetadata: { imports: [NgComponentOutlet] },
56
+ props: { component: CheckboxSignalFormComponent },
57
+ template: '<ng-container *ngComponentOutlet="component" />',
58
+ }),
59
+ parameters: {
60
+ docs: {
61
+ source: {
62
+ code: `
63
+ <div style="display: flex; flex-direction: column; gap: 0.5rem;">
64
+ <div style="display: flex; align-items: center; gap: 0.25rem">
65
+ <zen-checkbox id="agree" [formField]="form.agree" />
66
+ <label for="agree">I agree to the terms</label>
67
+ </div>
68
+ @if (form.agree().invalid()) {
69
+ <p style="color: red; margin: 0; font-size: 0.875rem;">
70
+ {{ form.agree().errors()![0].message }}
71
+ </p>
72
+ }
73
+ </div>`,
74
+ },
75
+ },
76
+ },
77
+ };
78
+
79
+ @Component({
80
+ standalone: true,
81
+ template: `
82
+ <div style="display: flex; flex-direction: column; gap: 0.5rem;">
83
+ <div style="display: flex; align-items: center; gap: 0.25rem">
84
+ <zen-checkbox [formField]="form.agree" id="agree" />
85
+ <label for="agree">I agree to the terms</label>
86
+ </div>
87
+ @if (form.agree().invalid()) {
88
+ <p style="color: red; margin: 0; font-size: 0.875rem;">
89
+ {{ form.agree().errors()![0].message }}
90
+ </p>
91
+ }
92
+ </div>
93
+ `,
94
+ imports: [FormField, ZenCheckbox],
95
+ })
96
+ class CheckboxSignalFormComponent {
97
+ readonly form = form(signal({ agree: false }), s => {
98
+ required(s.agree, { message: 'You must agree to continue' });
99
+ });
100
+ }
@@ -1,34 +1,35 @@
1
- import { ChangeDetectionStrategy, Component, effect, ElementRef, model, viewChild } from '@angular/core';
2
- import { FormsModule } from '@angular/forms';
1
+ import { Component, effect, ElementRef, model, viewChild } from '@angular/core';
3
2
 
4
- import { ZenFormControl, ZenFormControlProvider } from '../form-control';
3
+ import { ZenFormControl } from '../form-control';
5
4
 
6
5
  /**
7
- * ZenCheckbox is a reusable checkbox component designed to provide
8
- * a consistent and customizable checkbox style across the application.
9
- * It supports Angular forms integration and provides two-way data binding
10
- * for boolean values.
6
+ * ZenCheckbox is a checkbox component supporting checked (`true`),
7
+ * unchecked (`false`), and indeterminate (`null`) states.
11
8
  *
12
- * @example
13
- * <zen-checkbox [value]="true" /> Checked
14
- * <zen-checkbox [value]="false" /> Unchecked
15
- * <zen-checkbox [value]="null" /> Indeterminate
9
+ * Connect it to a Signal Forms field with `[formField]`:
10
+ *
11
+ * ```html
12
+ * <zen-checkbox [formField]="myForm.agree" />
13
+ * <label for="agree">I agree to the terms</label>
14
+ * ```
15
+ *
16
+ * When `value` is `null` the native checkbox is set to indeterminate via
17
+ * the `indeterminate` DOM property.
16
18
  *
17
19
  * ### CSS Custom Properties
18
20
  *
19
- * You can customize the component using CSS custom properties:
20
21
  *
21
22
  * ```css
22
23
  * :root {
23
- * --zen-checkbox-size: 1rem;
24
- * --zen-checkbox-border-radius: 0.375rem;
25
- * --zen-checkbox-appearance: hsl(0deg 0% 10%);
26
- * --zen-checkbox-disabled-opacity: 0.6;
27
- * --zen-checkbox-border: 1px solid hsl(0deg 0% 80%);
24
+ * --zen-checkbox-size: 1rem;
25
+ * --zen-checkbox-border-radius: 0.375rem;
26
+ * --zen-checkbox-appearance: hsl(0deg 0% 10%);
27
+ * --zen-checkbox-disabled-opacity: 0.6;
28
+ * --zen-checkbox-border: 1px solid hsl(0deg 0% 80%);
28
29
  * }
29
30
  * ```
30
31
  *
31
- * @implements {ZenFormControl<boolean>}
32
+ * @extends {ZenFormControl<boolean | null>}
32
33
  *
33
34
  * @author Konrad StΔ™pieΕ„
34
35
  * @license {@link https://github.com/kstepien3/ng-zen/blob/master/LICENSE|BSD-2-Clause}
@@ -39,9 +40,10 @@ import { ZenFormControl, ZenFormControlProvider } from '../form-control';
39
40
  template: `
40
41
  <input
41
42
  [attr.aria-disabled]="disabled()"
43
+ [attr.aria-invalid]="invalid() || null"
44
+ [checked]="value()"
42
45
  [disabled]="disabled()"
43
- [ngModel]="value()"
44
- (ngModelChange)="onInput($event)"
46
+ (change)="onInput(inputElement.checked)"
45
47
  #inputElement
46
48
  type="checkbox"
47
49
  />
@@ -53,12 +55,6 @@ import { ZenFormControl, ZenFormControlProvider } from '../form-control';
53
55
  <!-- @else { βœ• } -->
54
56
  `,
55
57
  styleUrls: ['./checkbox.scss'],
56
- changeDetection: ChangeDetectionStrategy.OnPush,
57
- imports: [FormsModule],
58
- providers: [ZenFormControlProvider(ZenCheckbox)],
59
- host: {
60
- '(blur)': 'onTouched()',
61
- },
62
58
  })
63
59
  export class ZenCheckbox extends ZenFormControl<boolean | null> {
64
60
  /**
@@ -1,4 +1,4 @@
1
- import { ChangeDetectionStrategy, Component, effect, ElementRef, inject, input, model, untracked } from '@angular/core';
1
+ import { Component, effect, ElementRef, inject, input, model, untracked } from '@angular/core';
2
2
  import { Cancel01Icon } from '@hugeicons/core-free-icons';
3
3
 
4
4
  import { ZenIcon } from '../icon';
@@ -55,7 +55,6 @@ type DialogSize = 'sm' | 'md' | 'lg' | 'xl' | 'full';
55
55
  </div>
56
56
  `,
57
57
  styleUrl: './dialog.scss',
58
- changeDetection: ChangeDetectionStrategy.OnPush,
59
58
  imports: [ZenIcon],
60
59
  host: {
61
60
  '[attr.aria-label]': 'header()',
@@ -1,4 +1,4 @@
1
- import { ChangeDetectionStrategy, Component, input } from '@angular/core';
1
+ import { Component, input } from '@angular/core';
2
2
 
3
3
  /**
4
4
  * ZenDivider is a reusable divider component that provides a simple way to
@@ -36,7 +36,6 @@ import { ChangeDetectionStrategy, Component, input } from '@angular/core';
36
36
  selector: `zen-divider, zen-divider[vertical],`,
37
37
  template: '<ng-content/>',
38
38
  styleUrl: './divider.scss',
39
- changeDetection: ChangeDetectionStrategy.OnPush,
40
39
  host: {
41
40
  '[class]': '"zen-align-"+align()',
42
41
  },
@@ -2,27 +2,42 @@ import { Component, model, provideZonelessChangeDetection } from '@angular/core'
2
2
  import { TestBed } from '@angular/core/testing';
3
3
  import { beforeEach, describe, expect, it } from 'vitest';
4
4
 
5
- import { ZenFormControl, ZenFormControlProvider } from './form-control';
5
+ import { ZenFormControl } from './form-control';
6
6
 
7
7
  @Component({
8
8
  template: '...',
9
- providers: [ZenFormControlProvider(FormControl)],
10
9
  })
11
- class FormControl extends ZenFormControl<string> {
10
+ class TestFormControl extends ZenFormControl<string> {
12
11
  readonly value = model<string>('');
13
12
  }
14
13
 
15
14
  describe('FormControl', () => {
16
15
  beforeEach(async () => {
17
16
  await TestBed.configureTestingModule({
18
- imports: [FormControl],
17
+ imports: [TestFormControl],
19
18
  providers: [provideZonelessChangeDetection()],
20
19
  }).compileComponents();
21
20
  });
22
21
 
23
22
  it('should create', () => {
24
- const fixture = TestBed.createComponent(FormControl);
23
+ const fixture = TestBed.createComponent(TestFormControl);
25
24
  const component = fixture.componentInstance;
26
25
  expect(component).toBeTruthy();
27
26
  });
27
+
28
+ it('should have FormUiControl properties', () => {
29
+ const fixture = TestBed.createComponent(TestFormControl);
30
+ const component = fixture.componentInstance;
31
+ expect(component.disabled()).toBe(false);
32
+ expect(component.required()).toBe(false);
33
+ expect(component.touched()).toBe(false);
34
+ expect(component.dirty()).toBe(false);
35
+ expect(component.invalid()).toBe(false);
36
+ expect(component.pending()).toBe(false);
37
+ expect(component.hidden()).toBe(false);
38
+ expect(component.readonly()).toBe(false);
39
+ expect(component.name()).toBe('');
40
+ expect(component.errors()).toEqual([]);
41
+ expect(component.disabledReasons()).toEqual([]);
42
+ });
28
43
  });
@@ -16,59 +16,81 @@ export default {
16
16
  story => `<div style="display: flex; flex-direction: column; gap: 2rem; align-items: center">${story}</div>`
17
17
  ),
18
18
  ],
19
- args: {
20
- value: '',
21
- disabled: false,
22
- required: false,
23
- },
24
19
  argTypes: {
25
- value: {
26
- control: false,
27
- table: {
28
- category: 'models',
29
- readonly: true,
30
- type: {
31
- summary: 'T',
32
- },
33
- },
34
- },
35
20
  disabled: {
36
- control: 'boolean',
37
- table: {
38
- category: 'models',
39
- type: {
40
- summary: 'boolean',
41
- },
42
- readonly: true,
43
- },
21
+ control: 'boolean' as const,
22
+ table: { category: 'inputs', type: { summary: 'boolean' }, defaultValue: { summary: 'false' } },
44
23
  },
45
24
  required: {
46
- control: 'boolean',
25
+ control: 'boolean' as const,
26
+ table: { category: 'inputs', type: { summary: 'boolean' }, defaultValue: { summary: 'false' } },
27
+ },
28
+ touched: {
29
+ control: false as const,
30
+ table: { category: 'models', type: { summary: 'boolean' }, readonly: true },
31
+ },
32
+ dirty: {
33
+ control: false as const,
34
+ table: { category: 'inputs', type: { summary: 'boolean' }, readonly: true },
35
+ },
36
+ invalid: {
37
+ control: false as const,
38
+ table: { category: 'inputs', type: { summary: 'boolean' }, readonly: true },
39
+ },
40
+ pending: {
41
+ control: false as const,
42
+ table: { category: 'inputs', type: { summary: 'boolean' }, readonly: true },
43
+ },
44
+ hidden: {
45
+ control: 'boolean' as const,
46
+ table: { category: 'inputs', type: { summary: 'boolean' }, defaultValue: { summary: 'false' } },
47
+ },
48
+ readonly: {
49
+ control: 'boolean' as const,
50
+ table: { category: 'inputs', type: { summary: 'boolean' }, defaultValue: { summary: 'false' } },
51
+ },
52
+ name: {
53
+ control: 'text' as const,
54
+ table: { category: 'models', type: { summary: 'string' } },
55
+ },
56
+ errors: {
57
+ control: false as const,
58
+ table: { category: 'models', type: { summary: 'ValidationError[]' }, readonly: true },
59
+ },
60
+ disabledReasons: {
61
+ control: false as const,
62
+ table: { category: 'models', type: { summary: 'DisabledReason[]' }, readonly: true },
63
+ },
64
+ value: {
65
+ control: false as const,
47
66
  table: {
48
- category: 'inputs',
49
- type: {
50
- summary: 'boolean',
51
- },
52
- defaultValue: {
53
- summary: 'false',
54
- },
67
+ category: 'models',
55
68
  readonly: true,
69
+ type: { summary: 'T' },
56
70
  },
57
71
  },
58
72
  onInput: {
59
73
  table: {
60
74
  readonly: true,
61
- type: {
62
- summary: '(value: T) => void',
63
- },
75
+ type: { summary: '(value: T) => void' },
64
76
  },
65
77
  },
66
78
  },
79
+ args: {
80
+ disabled: false,
81
+ required: false,
82
+ touched: false,
83
+ dirty: false,
84
+ invalid: false,
85
+ pending: false,
86
+ hidden: false,
87
+ readonly: false,
88
+ name: '',
89
+ value: '',
90
+ },
67
91
  parameters: {
68
92
  docs: {
69
93
  canvas: {
70
- // This will remove the "show code" button
71
- // https://storybook.js.org/docs/api/doc-blocks/doc-block-canvas#sourcestate
72
94
  sourceState: 'none',
73
95
  },
74
96
  },
@@ -1,100 +1,85 @@
1
- import { booleanAttribute, Directive, forwardRef, input, model, ModelSignal, Type } from '@angular/core';
2
- import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
3
-
4
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
- export const ZenFormControlProvider = <T extends ZenFormControl<any>>(component: Type<T>) => ({
6
- provide: NG_VALUE_ACCESSOR,
7
- useExisting: forwardRef(() => component),
8
- multi: true,
9
- });
1
+ import { booleanAttribute, Directive, input, ModelSignal, output } from '@angular/core';
2
+ import type { DisabledReason, ValidationError, WithOptionalFieldTree } from '@angular/forms/signals';
3
+ import { FormValueControl } from '@angular/forms/signals';
10
4
 
11
5
  /**
12
- * A base class for creating custom Angular form controls that integrate with both
13
- * template-driven and reactive forms. It simplifies the implementation of the
14
- * ControlValueAccessor interface by using signals for the control's value and state.
6
+ * Abstract base class for form controls that integrate with Signal Forms
7
+ * via the {@link FormValueControl} interface.
8
+ *
9
+ * Provides all {@link FormUiControl} state inputs (`disabled`, `required`,
10
+ * `touched`, `errors`, etc.) and the `touch` output used by the
11
+ * `[formField]` directive.
15
12
  *
16
- * Subclasses must provide an implementation for the abstract `value` property.
13
+ * Subclasses must provide an implementation of the `value` model signal
14
+ * with the appropriate value type.
15
+ *
16
+ * Decorated with `@Directive` only to declare the host event binding
17
+ * `(blur) -> touch.emit()`.
17
18
  *
18
19
  * @example
19
20
  *
20
21
  * ```ts
21
22
  * @Component({
22
- * template: '...',
23
- * providers: [ZenFormControlProvider(ZenInput)]
23
+ * selector: 'my-input',
24
+ * template: `<input [value]="value()" (input)="onInput($event.target.value)" />`,
24
25
  * })
25
- * export class Input extends ZenFormControl<string> {
26
- * readonly value = model<string>('')
26
+ * export class MyInput extends ZenFormControl<string> {
27
+ * readonly value = model<string>('');
27
28
  * }
28
29
  * ```
29
30
  *
30
- * @implements {ControlValueAccessor}
31
+ * @implements {FormValueControl}
31
32
  *
32
33
  * @author Konrad StΔ™pieΕ„
33
34
  * @license {@link https://github.com/kstepien3/ng-zen/blob/master/LICENSE|BSD-2-Clause}
34
35
  * @see [GitHub](https://github.com/kstepien3/ng-zen)
35
36
  */
36
- @Directive({})
37
- export abstract class ZenFormControl<Value> implements ControlValueAccessor {
38
- /**
39
- * The underlying value of the control.
40
- * Subclasses must provide their own implementation using `model()`.
41
- */
37
+ @Directive({
38
+ host: {
39
+ '(blur)': 'touch.emit()',
40
+ },
41
+ })
42
+ export abstract class ZenFormControl<Value> implements FormValueControl<Value> {
43
+ /** The underlying value of the control. Subclasses must provide their own implementation using `model()`. */
42
44
  abstract readonly value: ModelSignal<Value>;
43
45
 
44
- readonly disabled = model(false);
46
+ /** Validation errors for the field. Auto-bound by the Signal Forms directive. */
47
+ readonly errors = input<readonly ValidationError.WithOptionalFieldTree[]>([]);
48
+ /** Whether the control is disabled. Auto-bound by the Signal Forms directive. */
49
+ readonly disabled = input(false);
50
+ /** Reasons why the field is disabled. Auto-bound by the Signal Forms directive. */
51
+ readonly disabledReasons = input<readonly WithOptionalFieldTree<DisabledReason>[]>([]);
52
+ /** Whether the field is read-only. Auto-bound by the Signal Forms directive. */
53
+ readonly readonly = input<boolean>(false);
54
+ /** Whether the field is hidden from view. Auto-bound by the Signal Forms directive. */
55
+ readonly hidden = input<boolean>(false);
56
+ /** Whether the field has validation errors. Auto-bound by the Signal Forms directive. */
57
+ readonly invalid = input<boolean>(false);
58
+ /** Whether async validation is in progress. Auto-bound by the Signal Forms directive. */
59
+ readonly pending = input<boolean>(false);
60
+ /** Whether the user has focused and then blurred the field. */
61
+ readonly touched = input<boolean>(false);
62
+ /** Whether the field value has been modified. Auto-bound by the Signal Forms directive. */
63
+ readonly dirty = input(false);
64
+ /** Field name in the form. Auto-bound by the Signal Forms directive. */
65
+ readonly name = input('');
66
+ /** Whether the field is required. Auto-bound by the Signal Forms directive from schema validators. */
45
67
  readonly required = input(false, { transform: booleanAttribute });
68
+ /** Emitted when the field is blurred. The {@link FormField} directive listens to this output to mark the field as touched. */
69
+ readonly touch = output<void>();
46
70
 
47
- // eslint-disable-next-line @typescript-eslint/no-empty-function
48
- protected onChange: (value: Value) => void = () => {};
71
+ // @ignore
72
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
73
+ focus(_options?: FocusOptions): void {}
49
74
 
75
+ // @ignore
50
76
  // eslint-disable-next-line @typescript-eslint/no-empty-function
51
- protected onTouched: () => void = () => {};
77
+ reset(): void {}
52
78
 
53
- /**
54
- * Should be called by the subclass when the control's value changes
55
- * as a result of user interaction. This method updates the control's value
56
- * and notifies Angular of the change.
57
- */
79
+ /** Should be called by the subclass when the control's value changes as a result of user interaction. */
58
80
  onInput(value: Value): void {
59
81
  if (this.disabled()) return;
60
82
 
61
83
  this.value.set(value);
62
- this.onChange(this.value());
63
- }
64
-
65
- /**
66
- * Writes a new value to the element.
67
- * @internal For internal use by Angular forms.
68
- * @ignore
69
- */
70
- writeValue(value: Value): void {
71
- this.value.set(value);
72
- }
73
-
74
- /**
75
- * Registers a callback function that is called when the control's value changes in the UI.
76
- * @internal For internal use by Angular forms.
77
- * @ignore
78
- */
79
- registerOnChange(fn: (value: Value) => void): void {
80
- this.onChange = fn;
81
- }
82
-
83
- /**
84
- * Registers a callback function that is called by the forms API on component touch.
85
- * @internal For internal use by Angular forms.
86
- * @ignore
87
- */
88
- registerOnTouched(fn: () => void): void {
89
- this.onTouched = fn;
90
- }
91
-
92
- /**
93
- * This function is called by the forms API when the control status changes to or from "DISABLED".
94
- * @internal For internal use by Angular forms.
95
- * @ignore
96
- */
97
- setDisabledState(isDisabled: boolean): void {
98
- this.disabled.set(isDisabled);
99
84
  }
100
85
  }
@@ -1,4 +1,4 @@
1
- import { booleanAttribute, ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';
1
+ import { booleanAttribute, Component, computed, inject, input } from '@angular/core';
2
2
  import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
3
3
 
4
4
  /** SVG icon data structure from HugeIcons library - an array of [tagName, attributes] tuples. */
@@ -43,7 +43,6 @@ export type IconSvgObject = readonly (readonly [string, Record<string, string |
43
43
  ></svg>
44
44
  `,
45
45
  styles: [':host { display: inline-flex; }'],
46
- changeDetection: ChangeDetectionStrategy.OnPush,
47
46
  })
48
47
  export class ZenIcon {
49
48
  /** SVG object imported from HugeIcons library. */