{"version":3,"file":"ngxtension-control-value-accessor.mjs","sources":["../../../../libs/ngxtension/control-value-accessor/src/control-value-accessor.ts","../../../../libs/ngxtension/control-value-accessor/src/ngxtension-control-value-accessor.ts"],"sourcesContent":["import {\n\tDirective,\n\tInput,\n\tOutput,\n\tbooleanAttribute,\n\teffect,\n\tinject,\n\tsignal,\n\tuntracked,\n} from '@angular/core';\nimport { toObservable } from '@angular/core/rxjs-interop';\nimport { NgControl, NgModel, type ControlValueAccessor } from '@angular/forms';\nimport { createInjectionToken } from 'ngxtension/create-injection-token';\nimport { skip } from 'rxjs';\n\nconst noop = () => undefined;\n\n/** @see {@link NgxControlValueAccessor.compareTo}. */\nexport type NgxControlValueAccessorCompareTo<T = any> = (\n\ta?: T,\n\tb?: T,\n) => boolean;\n\nexport const [injectCvaCompareTo, provideCvaCompareTo] = createInjectionToken<\n\t() => NgxControlValueAccessorCompareTo\n>(() => Object.is);\n\nexport const [injectCvaDefaultValue, provideCvaDefaultValue] =\n\tcreateInjectionToken<() => any>(() => null);\n\n/**\n * Provides a {@link NgxControlValueAccessorCompareTo comparator} based on a property of `T`.\n *\n * @example\n * ```ts\n * interface User {\n * \tid: string;\n * \tname: string;\n * }\n *\n * provideCvaCompareToByProp<User>('id');\n * ```\n */\nexport const provideCvaCompareToByProp = <T>(prop: keyof T) =>\n\tprovideCvaCompareTo((a, b) => Object.is(a?.[prop], b?.[prop]), true);\n\n/**\n * `NgxControlValueAccessor` is a directive to reduce boilerplate when building components, which implement the [ControlValueAccessor](https://angular.dev/api/forms/ControlValueAccessor) interface.\n *\n * ## Usage\n *\n * `NgxControlValueAccessor` implements the [ControlValueAccessor](https://angular.dev/api/forms/ControlValueAccessor) interface and exposes a _simpler_ api. Declare `NgxControlValueAccessor` in the `hostDirectives` section of your component and inject the instance in order to wire up your template:\n *\n * - `NgxControlValueAccessor.value` for syncing the value\n * - `NgxControlValueAccessor.disabled` for syncing the disabled state\n * - `NgxControlValueAccessor.markAsTouched` for marking the view as _touched_\n *\n * The value and disabled state are also available as signals:\n *\n * - `NgxControlValueAccessor.value$`\n * - `NgxControlValueAccessor.disabled$`\n *\n * ### Example\n *\n * In this example `NgxControlValueAccessor` is used to create a `CustomInput` component.\n *\n * ```ts\n * @Component({\n *   selector: 'custom-input',\n *   hostDirectives: [NgxControlValueAccessor],\n *   template: `\n *     <label>\n *       <b>Custom label</b>\n *       <input\n *         type=\"text\"\n *         (input)=\"cva.value = $event.target.value\"\n *         [value]=\"cva.value$()\"\n *         [disabled]=\"cva.disabled$()\"\n *         (blur)=\"cva.markAsTouched()\"\n *       />\n *     </label>\n *   `,\n *   standalone: true,\n * })\n * export class CustomInput {\n *   protected cva = inject<NgxControlValueAccessor<string>>(\n *     NgxControlValueAccessor,\n *   );\n * }\n * ```\n *\n * With usage:\n *\n * ```html\n * <custom-input [formControl]=\"control\" />\n * <custom-input [(ngModel)]=\"value\" />\n * ```\n *\n * ## Non Primitive Values\n *\n * When your model is a non primitive datatype, you should provide a _comparator_. It is a pure function which tells `NgxControlValueAccessor`, whether two values are _semantically_ equal:\n *\n * ```ts\n * (a, b) => boolean;\n * ```\n *\n * ### Example\n *\n * In this example `NgxControlValueAccessor` is used to create a `User` select. A `User` is identified by its `id`.\n *\n * ```ts\n * interface User {\n *   id: string;\n *   name: string;\n * }\n *\n * const userComparator: NgxControlValueAccessorCompareTo<User> = (a, b) =>\n *   a?.id === b?.id;\n *\n * provideCvaCompareTo(userComparator, true);\n *\n * // or\n *\n * provideCvaCompareToByProp<User>('id');\n * ```\n *\n * Full example:\n *\n * ```ts\n * @Component({\n *   selector: 'user-select',\n *   standalone: true,\n *   hostDirectives: [NgxControlValueAccessor],\n *   providers: [provideCvaCompareToByProp<User>('id')],\n *   template: `\n *     <label>\n *       <b>Select a user:</b>\n *       <select\n *         [disabled]=\"cva.disabled$()\"\n *         (blur)=\"cva.markAsTouched()\"\n *         (change)=\"onChange($event)\"\n *       >\n *         <option [selected]=\"cva.value === null\">-- no user selected --</option>\n *         @for (user of users; track user.id) {\n *           <option [value]=\"user.id\" [selected]=\"user.id === cva.value?.id\">\n *             {{ user.name }}\n *           </option>\n *         }\n *       </select>\n *     </label>\n *   `,\n * })\n * export class UserSelect {\n *   protected cva = inject<NgxControlValueAccessor<User | null>>(\n *     NgxControlValueAccessor,\n *   );\n *\n *   protected onChange = (event: Event) =>\n *     (this.cva.value =\n *       this.users.find(({ id }) => event.target.value === id) ?? null);\n *\n *   @Input()\n *   users: User[] = [];\n * }\n * ```\n *\n * With usage:\n *\n * ```html\n * <user-select [formControl]=\"userControl\" [options]=\"users\" />\n * <user-select [(ngModel)]=\"user\" [options]=\"users\" />\n * ```\n *\n * ## Without `NgControl`\n *\n * Optionally you can expose `inputs` and `outputs` in the `hostDirectives` declaration\n * and use it without a `NgControl` directive.\n *\n * ```ts\n * hostDirectives: [\n *   {\n *     directive: NgxControlValueAccessor,\n *     inputs: ['value'],\n *     outputs: ['valueChange'],\n *   },\n * ];\n * ```\n *\n * ```html\n * <custom-input [(value)]=\"value\" />\n * ```\n */\n@Directive({\n\tstandalone: true,\n})\nexport class NgxControlValueAccessor<T = any> implements ControlValueAccessor {\n\t/**\n\t * The `NgControl` instance on this host element. If present, this `NgxControlValueAccessor` instance will be its value accessor.\n\t *\n\t * @see {@link NgControl}\n\t * @see {@link NgControl.valueAccessor}\n\t */\n\tpublic readonly ngControl = inject(NgControl, {\n\t\tself: true,\n\t\toptional: true,\n\t});\n\n\t/** @ignore */\n\tpublic constructor() {\n\t\tif (this.ngControl != null) this.ngControl.valueAccessor = this;\n\t}\n\n\t/** @ignore */\n\tprivate initialValue = (): T => {\n\t\tif (this.ngControl != null) return this.ngControl.value;\n\t\treturn injectCvaDefaultValue();\n\t};\n\n\t/** The value of this. If a control is present, it reflects it's value. */\n\tpublic readonly value$ = signal(this.initialValue(), {\n\t\tequal: (a, b) => this.compareTo(a, b),\n\t});\n\n\t/** Whether this is disabled. If a control is present, it reflects it's disabled state. */\n\tpublic readonly disabled$ = signal(this.ngControl?.disabled ?? false);\n\n\t/**\n\t * A comparator, which determines value changes. Should return true, if two values are considered semanticly equal.\n\t *\n\t * Defaults to {@link Object.is} in order to align with change detection behavior for inputs.\n\t */\n\tpublic readonly compareTo$ =\n\t\tsignal<NgxControlValueAccessorCompareTo<T>>(injectCvaCompareTo());\n\n\tprotected readonly notifyNgControlOnValueChanges = effect(() => {\n\t\tconst value = this.value$();\n\n\t\t/**\n\t\t * no-op if THIS value is already _equal_ to the value of the control => we dont need to notify the control.\n\t\t */\n\t\tif (this.compareTo(this.ngControl?.value, value)) {\n\t\t\treturn;\n\t\t}\n\n\t\tuntracked(() => {\n\t\t\tthis.onChange(value);\n\t\t});\n\t});\n\n\tprotected readonly notifyNgControlOnDisabledChanges = effect(() => {\n\t\tconst disabled = this.disabled$();\n\n\t\tuntracked(() => {\n\t\t\t/**\n\t\t\t * no-op if THIS disabled state is already _equal_ to the disabled state of the control  or there is no control\n\t\t\t */\n\t\t\tif (\n\t\t\t\tthis.ngControl?.control == null ||\n\t\t\t\tthis.ngControl.disabled === disabled\n\t\t\t) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.ngControl.control[disabled ? 'disable' : 'enable']();\n\t\t});\n\t});\n\n\t/** The value of this. If a control is present, it reflects it's value. */\n\t@Input()\n\tpublic set value(value: T) {\n\t\tthis.value$.set(value);\n\t}\n\n\tpublic get value() {\n\t\treturn untracked(this.value$);\n\t}\n\n\t/** Whether this is disabled. If a control is present, it reflects it's disabled state. */\n\t@Input({ transform: booleanAttribute })\n\tpublic set disabled(disabled: boolean) {\n\t\tthis.disabled$.set(disabled);\n\t}\n\n\tpublic get disabled() {\n\t\treturn untracked(this.disabled$);\n\t}\n\n\t/**\n\t * A comparator, which determines value changes. Should return true, if two values are considered semanticly equal.\n\t *\n\t * Defaults to {@link Object.is} in order to align with change detection behavior for inputs.\n\t */\n\t@Input()\n\tpublic set compareTo(compareTo) {\n\t\tif (typeof compareTo === 'function') this.compareTo$.set(compareTo);\n\t}\n\n\tpublic get compareTo() {\n\t\treturn untracked(this.compareTo$);\n\t}\n\n\t/**\n\t * Emits whenever this {@link NgxControlValueAccessor.value$ value} changes.\n\t */\n\t@Output()\n\tpublic readonly valueChange = toObservable(this.value$).pipe(skip(1)); // -> hot observable\n\n\t/**\n\t * This function should be called when this host is considered `touched`.\n\t *\n\t * NOTE: Whenever a `blur` event is triggered on this host, this function is called.\n\t *\n\t * @see {@link NgxControlValueAccessor.registerOnTouched}\n\t * @see {@link NgxControlValueAccessor.ngControl}\n\t */\n\tpublic markAsTouched = () => this.onTouched();\n\n\t/** This function is set by the forms api, if a control is present. */\n\tprivate onChange: (value: T) => void = noop;\n\n\t/** This function is set by the forms api, if a control is present. */\n\tprivate onTouched: () => void = noop;\n\n\t/**\n\t * `NgModel` sets up the control in `ngOnChanges`. Idk if bug or on purpose, but `writeValue` and `setDisabledState` are called before the inputs are set.\n\t * {@link https://github.com/angular/angular/blob/main/packages/forms/src/directives/ng_model.ts#L223}\n\t *\n\t * @ignore\n\t */\n\tprivate get registered() {\n\t\treturn this.ngControl instanceof NgModel\n\t\t\t? (this.ngControl as unknown as { _registered: boolean })._registered\n\t\t\t: true;\n\t}\n\n\t// control value accessor implementation\n\n\tpublic writeValue = (value: T) => {\n\t\tif (this.registered) this.value = value;\n\t};\n\n\tpublic registerOnChange = (onChange: (value: T) => void) =>\n\t\t(this.onChange = onChange);\n\n\tpublic registerOnTouched = (onTouched: () => void) =>\n\t\t(this.onTouched = onTouched);\n\n\tpublic setDisabledState = (disabled: boolean) => {\n\t\tif (this.registered) this.disabled$.set(disabled);\n\t};\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;;AAeA,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC;AAQhB,MAAA,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,GAAG,oBAAoB,CAE3E,MAAM,MAAM,CAAC,EAAE,EAAE;AAEZ,MAAM,CAAC,qBAAqB,EAAE,sBAAsB,CAAC,GAC3D,oBAAoB,CAAY,MAAM,IAAI,EAAE;AAE7C;;;;;;;;;;;;AAYG;AACU,MAAA,yBAAyB,GAAG,CAAI,IAAa,KACzD,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE;AAEtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiJG;MAIU,uBAAuB,CAAA;;AAanC,IAAA,WAAA,GAAA;AAZA;;;;;AAKG;AACa,QAAA,IAAA,CAAA,SAAS,GAAG,MAAM,CAAC,SAAS,EAAE;AAC7C,YAAA,IAAI,EAAE,IAAI;AACV,YAAA,QAAQ,EAAE,IAAI;AACd,SAAA,CAAC,CAAC;;QAQK,IAAY,CAAA,YAAA,GAAG,MAAQ;AAC9B,YAAA,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI;AAAE,gBAAA,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;YACxD,OAAO,qBAAqB,EAAE,CAAC;AAChC,SAAC,CAAC;;AAGc,QAAA,IAAA,CAAA,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE;AACpD,YAAA,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC;AACrC,SAAA,CAAC,CAAC;;QAGa,IAAS,CAAA,SAAA,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,IAAI,KAAK,CAAC,CAAC;AAEtE;;;;AAIG;AACa,QAAA,IAAA,CAAA,UAAU,GACzB,MAAM,CAAsC,kBAAkB,EAAE,CAAC,CAAC;AAEhD,QAAA,IAAA,CAAA,6BAA6B,GAAG,MAAM,CAAC,MAAK;AAC9D,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;AAE5B;;AAEG;AACH,YAAA,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE;gBACjD,OAAO;aACP;YAED,SAAS,CAAC,MAAK;AACd,gBAAA,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACtB,aAAC,CAAC,CAAC;AACJ,SAAC,CAAC,CAAC;AAEgB,QAAA,IAAA,CAAA,gCAAgC,GAAG,MAAM,CAAC,MAAK;AACjE,YAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;YAElC,SAAS,CAAC,MAAK;AACd;;AAEG;AACH,gBAAA,IACC,IAAI,CAAC,SAAS,EAAE,OAAO,IAAI,IAAI;AAC/B,oBAAA,IAAI,CAAC,SAAS,CAAC,QAAQ,KAAK,QAAQ,EACnC;oBACD,OAAO;iBACP;AAED,gBAAA,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC,EAAE,CAAC;AAC3D,aAAC,CAAC,CAAC;AACJ,SAAC,CAAC,CAAC;AAoCH;;AAEG;AAEa,QAAA,IAAA,CAAA,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAEtE;;;;;;;AAOG;QACI,IAAa,CAAA,aAAA,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;;QAGtC,IAAQ,CAAA,QAAA,GAAuB,IAAI,CAAC;;QAGpC,IAAS,CAAA,SAAA,GAAe,IAAI,CAAC;;AAgB9B,QAAA,IAAA,CAAA,UAAU,GAAG,CAAC,KAAQ,KAAI;YAChC,IAAI,IAAI,CAAC,UAAU;AAAE,gBAAA,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;AACzC,SAAC,CAAC;AAEK,QAAA,IAAA,CAAA,gBAAgB,GAAG,CAAC,QAA4B,MACrD,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAC;AAErB,QAAA,IAAA,CAAA,iBAAiB,GAAG,CAAC,SAAqB,MAC/C,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC;AAEvB,QAAA,IAAA,CAAA,gBAAgB,GAAG,CAAC,QAAiB,KAAI;YAC/C,IAAI,IAAI,CAAC,UAAU;AAAE,gBAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACnD,SAAC,CAAC;AA5ID,QAAA,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI;AAAE,YAAA,IAAI,CAAC,SAAS,CAAC,aAAa,GAAG,IAAI,CAAC;KAChE;;IA0DD,IACW,KAAK,CAAC,KAAQ,EAAA;AACxB,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;KACvB;AAED,IAAA,IAAW,KAAK,GAAA;AACf,QAAA,OAAO,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;KAC9B;;IAGD,IACW,QAAQ,CAAC,QAAiB,EAAA;AACpC,QAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;KAC7B;AAED,IAAA,IAAW,QAAQ,GAAA;AAClB,QAAA,OAAO,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;KACjC;AAED;;;;AAIG;IACH,IACW,SAAS,CAAC,SAAS,EAAA;QAC7B,IAAI,OAAO,SAAS,KAAK,UAAU;AAAE,YAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;KACpE;AAED,IAAA,IAAW,SAAS,GAAA;AACnB,QAAA,OAAO,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;KAClC;AAwBD;;;;;AAKG;AACH,IAAA,IAAY,UAAU,GAAA;AACrB,QAAA,OAAO,IAAI,CAAC,SAAS,YAAY,OAAO;AACvC,cAAG,IAAI,CAAC,SAAiD,CAAC,WAAW;cACnE,IAAI,CAAC;KACR;8GA1IW,uBAAuB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA,EAAA;AAAvB,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,uBAAuB,mFAmFf,gBAAgB,CAAA,EAAA,SAAA,EAAA,WAAA,EAAA,EAAA,OAAA,EAAA,EAAA,WAAA,EAAA,aAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA,CAAA,EAAA;;2FAnFxB,uBAAuB,EAAA,UAAA,EAAA,CAAA;kBAHnC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACV,oBAAA,UAAU,EAAE,IAAI;AAChB,iBAAA,CAAA;wDA2EW,KAAK,EAAA,CAAA;sBADf,KAAK;gBAWK,QAAQ,EAAA,CAAA;sBADlB,KAAK;uBAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAA;gBAe3B,SAAS,EAAA,CAAA;sBADnB,KAAK;gBAaU,WAAW,EAAA,CAAA;sBAD1B,MAAM;;;AChTR;;AAEG;;;;"}