Skip to content

Commit

Permalink
fix(radio): updated disabled state (#186)
Browse files Browse the repository at this point in the history
  • Loading branch information
pimenovoleg authored Dec 11, 2024
1 parent d34ca0a commit caf6cd5
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class RdxRadioItemInputDirective {

readonly name = input<string>();
readonly value = computed(() => this.radioItem.value() || undefined);
readonly checked = computed(() => this.radioItem.checked || undefined);
readonly checked = computed(() => this.radioItem.checkedState() || undefined);
readonly required = input<boolean | undefined>(this.radioItem.required());
readonly disabled = input<boolean | undefined>(this.radioItem.disabled());
}
32 changes: 20 additions & 12 deletions packages/primitives/radio/src/radio-item.directive.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { BooleanInput } from '@angular/cdk/coercion';
import { booleanAttribute, Directive, ElementRef, inject, InjectionToken, input, OnInit, signal } from '@angular/core';
import {
booleanAttribute,
computed,
Directive,
ElementRef,
inject,
InjectionToken,
input,
OnInit,
signal
} from '@angular/core';
import { RdxRovingFocusItemDirective } from '@radix-ng/primitives/roving-focus';
import { RDX_RADIO_GROUP } from './radio-tokens';

Expand All @@ -20,9 +30,10 @@ export function injectRadioItem(): RdxRadioItemDirective {
host: {
type: 'button',
role: 'radio',
'[attr.aria-checked]': 'checked',
'[attr.data-disabled]': 'disabled() ? "" : null',
'[attr.data-state]': 'checked ? "checked" : "unchecked"',
'[attr.aria-checked]': 'checkedState()',
'[attr.data-disabled]': 'disabledState() ? "" : null',
'[attr.data-state]': 'checkedState() ? "checked" : "unchecked"',
'[disabled]': 'disabledState()',
'(click)': 'onClick()',
'(keydown)': 'onKeyDown($event)',
'(keyup)': 'onKeyUp()',
Expand All @@ -41,6 +52,10 @@ export class RdxRadioItemDirective implements OnInit {

readonly disabled = input<boolean, BooleanInput>(false, { transform: booleanAttribute });

protected readonly disabledState = computed(() => this.radioGroup.disableState() || this.disabled());

readonly checkedState = computed(() => this.radioGroup.value() === this.value());

private readonly ARROW_KEYS = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
private readonly isArrowKeyPressedSignal = signal(false);

Expand All @@ -51,16 +66,9 @@ export class RdxRadioItemDirective implements OnInit {
}
}

/** @ignore */
get checked(): boolean {
if (this.radioGroup.value == undefined) return false;

return this.radioGroup.value() === this.value();
}

/** @ignore */
onClick() {
if (!this.disabled()) {
if (!this.disabledState()) {
this.radioGroup.select(this.value());
this.isArrowKeyPressedSignal.set(true);
}
Expand Down
25 changes: 15 additions & 10 deletions packages/primitives/radio/src/radio-root.directive.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { booleanAttribute, Directive, EventEmitter, Input, model, Output } from '@angular/core';
import { BooleanInput } from '@angular/cdk/coercion';
import { booleanAttribute, computed, Directive, input, Input, model, output, signal } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Orientation, RdxRovingFocusGroupDirective } from '@radix-ng/primitives/roving-focus';
import { RadioGroupDirective, RadioGroupProps, RDX_RADIO_GROUP } from './radio-tokens';
Expand All @@ -14,26 +15,30 @@ import { RadioGroupDirective, RadioGroupProps, RDX_RADIO_GROUP } from './radio-t
hostDirectives: [{ directive: RdxRovingFocusGroupDirective, inputs: ['dir', 'orientation', 'loop'] }],
host: {
role: 'radiogroup',
'[attr.aria-orientation]': 'orientation',
'[attr.data-disabled]': 'disabled ? "" : null',
'[attr.aria-orientation]': 'orientation()',
'[attr.aria-required]': 'required()',
'[attr.data-disabled]': 'disableState() ? "" : null',
'(keydown)': 'onKeydown()'
}
})
export class RdxRadioGroupDirective implements RadioGroupProps, RadioGroupDirective, ControlValueAccessor {
readonly value = model<string | undefined>();
readonly value = model<string | null>(null);

@Input({ transform: booleanAttribute }) disabled = false;
readonly disabled = input<boolean, BooleanInput>(false, { transform: booleanAttribute });

@Input() defaultValue?: string;

@Input() required: boolean;
readonly required = input<boolean, BooleanInput>(false, { transform: booleanAttribute });

@Input() orientation: Orientation;
readonly orientation = input<Orientation>();

/**
* Event handler called when the value changes.
*/
@Output() readonly onValueChange = new EventEmitter<string>();
readonly onValueChange = output<string>();

private readonly disable = signal<boolean>(this.disabled());
readonly disableState = computed(() => this.disable() || this.disabled());

/**
* The callback function to call when the value of the radio group changes.
Expand Down Expand Up @@ -91,10 +96,10 @@ export class RdxRadioGroupDirective implements RadioGroupProps, RadioGroupDirect
* @ignore
*/
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
this.disable.set(isDisabled);
}

protected onKeydown(): void {
if (this.disabled) return;
if (this.disableState()) return;
}
}
10 changes: 6 additions & 4 deletions packages/primitives/radio/src/radio-tokens.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { InjectionToken, ModelSignal } from '@angular/core';
import { BooleanInput } from '@angular/cdk/coercion';
import { InjectionToken, InputSignalWithTransform, ModelSignal, Signal } from '@angular/core';

export interface RadioGroupProps {
name?: string;
disabled?: boolean;
disabled?: InputSignalWithTransform<boolean, BooleanInput>;
defaultValue?: string;
value?: ModelSignal<string | undefined>;
value: ModelSignal<string | null>;
disableState: Signal<boolean>;
}

export interface RadioGroupDirective extends RadioGroupProps {
select(value: string | undefined): void;
select(value: string | null): void;

onTouched(): void;
}
Expand Down
47 changes: 47 additions & 0 deletions packages/primitives/radio/stories/radio.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ export default {
box-shadow: 0 0 0 2px black;
}
.RadioGroupItem:disabled {
background-color: var(--gray-4);
box-shadow: none;
cursor: not-allowed;
}
.RadioGroupItem:disabled:hover {
background-color: var(--gray-4);
}
.RadioGroupIndicator {
display: flex;
align-items: center;
Expand Down Expand Up @@ -136,3 +146,40 @@ export const RadioGroup: Story = {
template: `<radio-groups-forms-example></radio-groups-forms-example>`
})
};

export const DisabledGroup: Story = {
render: () => ({
template: html`
<div
class="RadioGroupRoot"
rdxRadioRoot
[value]="'comfortable'"
disabled
orientation="vertical"
aria-label="View density"
>
<div style="display: flex; align-items: center;">
<button class="RadioGroupItem" id="r1" rdxRadioItem value="default">
<div class="RadioGroupIndicator" rdxRadioIndicator></div>
<input class="Input" rdxRadioItemInput feature="fully-hidden" />
</button>
<label class="Label" rdxLabel htmlFor="r1">Default</label>
</div>
<div style="display: flex; align-items: center;">
<button class="RadioGroupItem" id="r2" rdxRadioItem [required]="true" value="comfortable">
<div class="RadioGroupIndicator" rdxRadioIndicator></div>
<input class="Input" rdxRadioItemInput feature="fully-hidden" />
</button>
<label class="Label" rdxLabel htmlFor="r2">Comfortable</label>
</div>
<div style="display: flex; align-items: center;">
<button class="RadioGroupItem" id="r3" rdxRadioItem value="compact">
<div class="RadioGroupIndicator" rdxRadioIndicator></div>
<input class="Input" rdxRadioItemInput feature="fully-hidden" />
</button>
<label class="Label" rdxLabel htmlFor="r3">Compact</label>
</div>
</div>
`
})
};

0 comments on commit caf6cd5

Please sign in to comment.