-
Notifications
You must be signed in to change notification settings - Fork 160
refactor(button): Migrate to signal backed state management #17155
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,18 +1,6 @@ | ||||||||||||||
| import { | ||||||||||||||
| Directive, | ||||||||||||||
| ElementRef, | ||||||||||||||
| EventEmitter, | ||||||||||||||
| HostBinding, | ||||||||||||||
| HostListener, | ||||||||||||||
| Input, | ||||||||||||||
| Output, | ||||||||||||||
| booleanAttribute, | ||||||||||||||
| inject, | ||||||||||||||
| AfterViewInit, | ||||||||||||||
| OnDestroy | ||||||||||||||
| } from '@angular/core'; | ||||||||||||||
| import { PlatformUtil } from 'igniteui-angular/core'; | ||||||||||||||
| import { animationFrameScheduler, Subscription } from 'rxjs'; | ||||||||||||||
| import { Directive, ElementRef, Input, booleanAttribute, inject, AfterViewInit, signal, EventEmitter, Output} from '@angular/core'; | ||||||||||||||
| import { IgxFocusRingDirective } from '../focus-ring/focus-ring.directive'; | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| export const IgxBaseButtonType = { | ||||||||||||||
| Flat: 'flat', | ||||||||||||||
|
|
@@ -21,126 +9,53 @@ export const IgxBaseButtonType = { | |||||||||||||
| } as const; | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| @Directive() | ||||||||||||||
| export abstract class IgxButtonBaseDirective implements AfterViewInit, OnDestroy { | ||||||||||||||
| private _platformUtil = inject(PlatformUtil); | ||||||||||||||
| public element = inject(ElementRef); | ||||||||||||||
| private _viewInit = false; | ||||||||||||||
| private _animationScheduler: Subscription; | ||||||||||||||
| @Directive({ | ||||||||||||||
| host: { | ||||||||||||||
| 'role': 'button', | ||||||||||||||
| '[attr.disabled]': '_disabled() || null', | ||||||||||||||
| '[class.igx-button--focused]': '_hasKeyboardFocus()', | ||||||||||||||
| '[class.igx-button--disabled]': '_disabled()', | ||||||||||||||
| '[style.--_init-transition]': '_hasRendered() ? null : "0s"', | ||||||||||||||
| '[style.transition]': '_hasRendered() ? "var(--_button-transition)" : "none"', | ||||||||||||||
| '(click)': 'buttonClick.emit($event)', | ||||||||||||||
| }, | ||||||||||||||
| hostDirectives: [IgxFocusRingDirective] | ||||||||||||||
| }) | ||||||||||||||
| export abstract class IgxButtonBaseDirective implements AfterViewInit { | ||||||||||||||
| protected readonly _element = inject<ElementRef<HTMLElement>>(ElementRef); | ||||||||||||||
| protected readonly _hasKeyboardFocus = inject(IgxFocusRingDirective).hasKeyboardFocus; | ||||||||||||||
|
|
||||||||||||||
| /** | ||||||||||||||
| * Emitted when the button is clicked. | ||||||||||||||
| */ | ||||||||||||||
| @Output() | ||||||||||||||
| public buttonClick = new EventEmitter<any>(); | ||||||||||||||
| protected readonly _hasRendered = signal(false); | ||||||||||||||
| protected readonly _disabled = signal(false); | ||||||||||||||
|
|
||||||||||||||
| /** | ||||||||||||||
| * Sets/gets the `role` attribute. | ||||||||||||||
| * Gets or sets whether the button is disabled. | ||||||||||||||
| * | ||||||||||||||
| * @example | ||||||||||||||
| * ```typescript | ||||||||||||||
| * this.button.role = 'navbutton'; | ||||||||||||||
| * let buttonRole = this.button.role; | ||||||||||||||
| * ``` | ||||||||||||||
| */ | ||||||||||||||
| @HostBinding('attr.role') | ||||||||||||||
| public role = 'button'; | ||||||||||||||
|
|
||||||||||||||
| /** | ||||||||||||||
| * @hidden | ||||||||||||||
| * @internal | ||||||||||||||
| */ | ||||||||||||||
| @HostListener('click', ['$event']) | ||||||||||||||
| public onClick(ev: MouseEvent) { | ||||||||||||||
| this.buttonClick.emit(ev); | ||||||||||||||
| this.focused = false; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| /** | ||||||||||||||
| * @hidden | ||||||||||||||
| * @internal | ||||||||||||||
| */ | ||||||||||||||
| @HostListener('blur') | ||||||||||||||
| protected onBlur() { | ||||||||||||||
| this.focused = false; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| /** | ||||||||||||||
| * Sets/gets whether the button component is on focus. | ||||||||||||||
| * Default value is `false`. | ||||||||||||||
| * ```typescript | ||||||||||||||
| * this.button.focus = true; | ||||||||||||||
| * ``` | ||||||||||||||
| * ```typescript | ||||||||||||||
| * let isFocused = this.button.focused; | ||||||||||||||
| * ```html | ||||||||||||||
| * <button type="button" igxButton="flat" [disabled]="isDisabled"></button> | ||||||||||||||
| * ``` | ||||||||||||||
| */ | ||||||||||||||
| @HostBinding('class.igx-button--focused') | ||||||||||||||
| protected focused = false; | ||||||||||||||
|
|
||||||||||||||
| /** | ||||||||||||||
| * Enables/disables the button. | ||||||||||||||
| * | ||||||||||||||
| * @example | ||||||||||||||
| * ```html | ||||||||||||||
| * <button igxButton="fab" disabled></button> | ||||||||||||||
| * ``` | ||||||||||||||
| */ | ||||||||||||||
| @Input({ transform: booleanAttribute }) | ||||||||||||||
| @HostBinding('class.igx-button--disabled') | ||||||||||||||
| public disabled = false; | ||||||||||||||
|
|
||||||||||||||
| /** | ||||||||||||||
| * @hidden | ||||||||||||||
| * @internal | ||||||||||||||
| */ | ||||||||||||||
| @HostBinding('attr.disabled') | ||||||||||||||
| public get disabledAttribute() { | ||||||||||||||
| return this.disabled || null; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| protected constructor() { | ||||||||||||||
| // In browser, set via native API for immediate effect (no-op on server). | ||||||||||||||
| // In SSR there is no paint, so there’s no visual rendering or transitions to suppress. | ||||||||||||||
| // Fix style flickering https://github.com/IgniteUI/igniteui-angular/issues/14759 | ||||||||||||||
| if (this._platformUtil.isBrowser) { | ||||||||||||||
| this.element.nativeElement.style.setProperty('--_init-transition', '0s'); | ||||||||||||||
| this.element.nativeElement.style.setProperty('transition', 'none'); | ||||||||||||||
| } | ||||||||||||||
| public set disabled(value: boolean) { | ||||||||||||||
| this._disabled.set(value); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| public ngAfterViewInit(): void { | ||||||||||||||
| if (this._platformUtil.isBrowser && !this._viewInit) { | ||||||||||||||
| this._viewInit = true; | ||||||||||||||
|
|
||||||||||||||
| this._animationScheduler = animationFrameScheduler.schedule(() => { | ||||||||||||||
| this.element.nativeElement.style.removeProperty('--_init-transition'); | ||||||||||||||
| this.element.nativeElement.style.setProperty('transition', 'var(--_button-transition)'); | ||||||||||||||
| }); | ||||||||||||||
| } | ||||||||||||||
| public get disabled(): boolean { | ||||||||||||||
| return this._disabled(); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| public ngOnDestroy(): void { | ||||||||||||||
| if (this._animationScheduler) { | ||||||||||||||
| this._animationScheduler.unsubscribe(); | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| /** Emitted when the button is clicked. */ | ||||||||||||||
| @Output() | ||||||||||||||
| public readonly buttonClick = new EventEmitter<MouseEvent>(); | ||||||||||||||
|
|
||||||||||||||
| /** | ||||||||||||||
| * @hidden | ||||||||||||||
| * @internal | ||||||||||||||
| */ | ||||||||||||||
| @HostListener('keyup', ['$event']) | ||||||||||||||
| protected updateOnKeyUp(event: KeyboardEvent) { | ||||||||||||||
| if (event.key === "Tab") { | ||||||||||||||
| this.focused = true; | ||||||||||||||
| } | ||||||||||||||
| /** Returns the underlying DOM element. */ | ||||||||||||||
| public get nativeElement(): HTMLButtonElement { | ||||||||||||||
| return this._element.nativeElement as HTMLButtonElement; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| /** | ||||||||||||||
| * Returns the underlying DOM element. | ||||||||||||||
| */ | ||||||||||||||
| public get nativeElement() { | ||||||||||||||
| return this.element.nativeElement; | ||||||||||||||
| public ngAfterViewInit(): void { | ||||||||||||||
| // FUOC workaround - ensures that the transition is only applied after the initial render | ||||||||||||||
|
||||||||||||||
| // FUOC workaround - ensures that the transition is only applied after the initial render | |
| // FUOC workaround - ensures that the transition is only applied after the initial render | |
| if (typeof requestAnimationFrame === 'function') { | |
| requestAnimationFrame(() => this._hasRendered.set(true)); | |
| return; | |
| } |
Uh oh!
There was an error while loading. Please reload this page.