diff --git a/packages/primitives/toggle-group/index.ts b/packages/primitives/toggle-group/index.ts index 37017c69..6385ed93 100644 --- a/packages/primitives/toggle-group/index.ts +++ b/packages/primitives/toggle-group/index.ts @@ -1,4 +1,5 @@ export * from './src/toggle-group-item.directive'; export * from './src/toggle-group-item.token'; +export * from './src/toggle-group-without-focus.directive'; export * from './src/toggle-group.directive'; export * from './src/toggle-group.token'; diff --git a/packages/primitives/toggle-group/src/toggle-group-without-focus.directive.ts b/packages/primitives/toggle-group/src/toggle-group-without-focus.directive.ts new file mode 100644 index 00000000..d4f6fca4 --- /dev/null +++ b/packages/primitives/toggle-group/src/toggle-group-without-focus.directive.ts @@ -0,0 +1,124 @@ +import { BooleanInput } from '@angular/cdk/coercion'; +import { booleanAttribute, Directive, input, model, output, signal } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { RdxToggleGroupToken } from './toggle-group.token'; + +let nextId = 0; + +@Directive({ + selector: '[rdxToggleGroupWithoutFocus]', + exportAs: 'rdxToggleGroupWithoutFocus', + standalone: true, + providers: [ + { provide: RdxToggleGroupToken, useExisting: RdxToggleGroupWithoutFocusDirective }, + { provide: NG_VALUE_ACCESSOR, useExisting: RdxToggleGroupWithoutFocusDirective, multi: true } + ], + host: { + role: 'group', + '(focusout)': 'onTouched?.()' + } +}) +export class RdxToggleGroupWithoutFocusDirective implements ControlValueAccessor { + /** + * @ignore + */ + readonly id: string = `rdx-toggle-group-${nextId++}`; + + /** + * @group Props + */ + readonly value = model(undefined); + + /** + * @group Props + */ + readonly type = input<'single' | 'multiple'>('single'); + + /** + * Whether the toggle group is disabled. + * @defaultValue false + * @group Props + */ + readonly disabled = input(false, { transform: booleanAttribute }); + + /** + * Event emitted when the selected toggle button changes. + * @group Emits + */ + readonly onValueChange = output(); + + /** + * The value change callback. + */ + private onChange?: (value: string | string[] | undefined) => void; + + /** + * onTouch function registered via registerOnTouch (ControlValueAccessor). + */ + protected onTouched?: () => void; + + /** + * Toggle a value. + * @param value The value to toggle. + * @ignore + */ + toggle(value: string): void { + if (this.disabled()) { + return; + } + + if (this.type() === 'single') { + this.value.set(value); + } else { + this.value.set( + ((currentValue) => + currentValue && Array.isArray(currentValue) + ? currentValue.includes(value) + ? currentValue.filter((v) => v !== value) // delete + : [...currentValue, value] // update + : [value])(this.value()) + ); + } + + this.onValueChange.emit(this.value()); + this.onChange?.(this.value()); + } + + /** + * Select a value from Angular forms. + * @param value The value to select. + * @ignore + */ + writeValue(value: string): void { + this.value.set(value); + } + + /** + * Register a callback to be called when the value changes. + * @param fn The callback to register. + * @ignore + */ + registerOnChange(fn: (value: string | string[] | undefined) => void): void { + this.onChange = fn; + } + + /** + * Register a callback to be called when the toggle group is touched. + * @param fn The callback to register. + * @ignore + */ + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + private readonly accessorDisabled = signal(false); + + /** + * Set the disabled state of the toggle group. + * @param isDisabled Whether the toggle group is disabled. + * @ignore + */ + setDisabledState(isDisabled: boolean): void { + this.accessorDisabled.set(isDisabled); + } +} diff --git a/packages/primitives/toggle-group/src/toggle-group.directive.ts b/packages/primitives/toggle-group/src/toggle-group.directive.ts index 73f115d1..2e6966d4 100644 --- a/packages/primitives/toggle-group/src/toggle-group.directive.ts +++ b/packages/primitives/toggle-group/src/toggle-group.directive.ts @@ -1,5 +1,5 @@ import { BooleanInput } from '@angular/cdk/coercion'; -import { booleanAttribute, Directive, input, model, output, signal } from '@angular/core'; +import { booleanAttribute, Directive, forwardRef, input, model, output, signal } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { RdxRovingFocusGroupDirective } from '@radix-ng/primitives/roving-focus'; import { RdxToggleGroupToken } from './toggle-group.token'; @@ -12,10 +12,9 @@ let nextId = 0; @Directive({ selector: '[rdxToggleGroup]', exportAs: 'rdxToggleGroup', - standalone: true, providers: [ - { provide: RdxToggleGroupToken, useExisting: RdxToggleGroupDirective }, - { provide: NG_VALUE_ACCESSOR, useExisting: RdxToggleGroupDirective, multi: true } + { provide: RdxToggleGroupToken, useExisting: forwardRef(() => RdxToggleGroupDirective) }, + { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RdxToggleGroupDirective), multi: true } ], hostDirectives: [{ directive: RdxRovingFocusGroupDirective, inputs: ['dir', 'orientation', 'loop'] }], host: { diff --git a/packages/primitives/toggle-group/src/toggle-group.token.ts b/packages/primitives/toggle-group/src/toggle-group.token.ts index 96a6e597..449f0954 100644 --- a/packages/primitives/toggle-group/src/toggle-group.token.ts +++ b/packages/primitives/toggle-group/src/toggle-group.token.ts @@ -1,8 +1,15 @@ import { inject, InjectionToken } from '@angular/core'; -import type { RdxToggleGroupDirective } from './toggle-group.directive'; -export const RdxToggleGroupToken = new InjectionToken('RdxToggleGroupToken'); +export interface IRdxToggleGroup { + toggle(value: string): void; -export function injectToggleGroup(): RdxToggleGroupDirective { + disabled: any; + value: any; + type: any; +} + +export const RdxToggleGroupToken = new InjectionToken('RdxToggleGroupToken'); + +export function injectToggleGroup(): IRdxToggleGroup { return inject(RdxToggleGroupToken); } diff --git a/packages/primitives/toolbar/src/toolbar-toggle-group.directive.ts b/packages/primitives/toolbar/src/toolbar-toggle-group.directive.ts index 670b46be..8c7f4d71 100644 --- a/packages/primitives/toolbar/src/toolbar-toggle-group.directive.ts +++ b/packages/primitives/toolbar/src/toolbar-toggle-group.directive.ts @@ -1,9 +1,9 @@ import { Directive } from '@angular/core'; -import { RdxToggleGroupDirective } from '@radix-ng/primitives/toggle-group'; +import { RdxToggleGroupWithoutFocusDirective } from '@radix-ng/primitives/toggle-group'; // TODO: set rovingFocus - false @Directive({ selector: '[rdxToolbarToggleGroup]', - hostDirectives: [{ directive: RdxToggleGroupDirective, inputs: ['value', 'type', 'disabled'] }] + hostDirectives: [{ directive: RdxToggleGroupWithoutFocusDirective, inputs: ['value', 'type', 'disabled'] }] }) export class RdxToolbarToggleGroupDirective {}