From d5d4a0eb9ac45c444432e4816ee21fa17c8d8c2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87etin?= <69278826+cetincakiroglu@users.noreply.github.com> Date: Wed, 17 Jul 2024 17:03:58 +0300 Subject: [PATCH] Theming | Add radioButton, remove label and labelStyleClass, fixes #16043 --- .../components/radiobutton/radiobutton.css | 38 ----- src/app/components/radiobutton/radiobutton.ts | 108 +++++-------- .../radiobutton/style/radiobuttonstyle.ts | 145 ++++++++++++++++++ .../showcase/doc/radiobutton/invaliddoc.ts | 4 +- src/assets/showcase/styles/primeng.css | 1 - 5 files changed, 185 insertions(+), 111 deletions(-) delete mode 100755 src/app/components/radiobutton/radiobutton.css create mode 100644 src/app/components/radiobutton/style/radiobuttonstyle.ts diff --git a/src/app/components/radiobutton/radiobutton.css b/src/app/components/radiobutton/radiobutton.css deleted file mode 100755 index 7bd8a90360e..00000000000 --- a/src/app/components/radiobutton/radiobutton.css +++ /dev/null @@ -1,38 +0,0 @@ -@layer primeng { - .p-radiobutton { - display: inline-flex; - cursor: pointer; - user-select: none; - vertical-align: bottom; - position: relative; - } - - .p-radiobutton-box { - display: flex; - justify-content: center; - align-items: center; - } - - .p-radiobutton-icon { - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - transform: translateZ(0) scale(0.1); - border-radius: 50%; - visibility: hidden; - } - - .p-radiobutton-box.p-highlight .p-radiobutton-icon { - transform: translateZ(0) scale(1, 1); - visibility: visible; - } - - p-radiobutton { - display: inline-flex; - vertical-align: bottom; - align-items: center; - } - - .p-radiobutton-label { - line-height: 1; - } -} diff --git a/src/app/components/radiobutton/radiobutton.ts b/src/app/components/radiobutton/radiobutton.ts index 14cd436f516..56ad36afca1 100755 --- a/src/app/components/radiobutton/radiobutton.ts +++ b/src/app/components/radiobutton/radiobutton.ts @@ -1,9 +1,10 @@ import { CommonModule } from '@angular/common'; -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, Injectable, Injector, Input, NgModule, OnDestroy, OnInit, Output, ViewChild, booleanAttribute, forwardRef, numberAttribute } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, Injectable, Injector, Input, NgModule, OnDestroy, OnInit, Output, ViewChild, booleanAttribute, forwardRef, inject, numberAttribute } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms'; import { Nullable } from 'primeng/ts-helpers'; import { AutoFocusModule } from 'primeng/autofocus'; - +import { BaseComponent } from 'primeng/basecomponent'; +import { RadioButtonStyle } from './style/radiobuttonstyle'; import { RadioButtonClickEvent } from './radiobutton.interface'; import { PrimeNGConfig } from 'primeng/api'; @@ -57,56 +58,41 @@ export class RadioControlRegistry { [ngClass]="{ 'p-radiobutton p-component': true, 'p-radiobutton-checked': checked, - 'p-radiobutton-disabled': disabled, - 'p-radiobutton-focused': focused, + 'p-disabled': disabled, 'p-variant-filled': variant === 'filled' || config.inputStyle() === 'filled' }" [class]="styleClass" [attr.data-pc-name]="'radiobutton'" [attr.data-pc-section]="'root'" - (click)="handleClick($event, input, true)" > -
- -
-
- + +
+
- `, - providers: [RADIO_VALUE_ACCESSOR], - changeDetection: ChangeDetectionStrategy.OnPush, - host: { - class: 'p-element' - } + providers: [RADIO_VALUE_ACCESSOR, RadioButtonStyle], + changeDetection: ChangeDetectionStrategy.OnPush }) -export class RadioButton implements ControlValueAccessor, OnInit, OnDestroy { +export class RadioButton extends BaseComponent implements ControlValueAccessor, OnInit, OnDestroy { /** * Value of the radiobutton. * @group Props @@ -127,11 +113,6 @@ export class RadioButton implements ControlValueAccessor, OnInit, OnDestroy { * @group Props */ @Input({ transform: booleanAttribute }) disabled: boolean | undefined; - /** - * Label of the radiobutton. - * @group Props - */ - @Input() label: string | undefined; /** * Specifies the input variant of the component. * @group Props @@ -167,11 +148,6 @@ export class RadioButton implements ControlValueAccessor, OnInit, OnDestroy { * @group Props */ @Input() styleClass: string | undefined; - /** - * Style class of the label. - * @group Props - */ - @Input() labelStyleClass: string | undefined; /** * When present, it specifies that the component should automatically get focus on load. * @group Props @@ -208,36 +184,27 @@ export class RadioButton implements ControlValueAccessor, OnInit, OnDestroy { control: Nullable; - constructor( - public cd: ChangeDetectorRef, - private injector: Injector, - private registry: RadioControlRegistry, - public config: PrimeNGConfig - ) {} + _componentStyle = inject(RadioButtonStyle); + + injector = inject(Injector); + + registry = inject(RadioControlRegistry); ngOnInit() { + super.ngOnInit(); this.control = this.injector.get(NgControl); this.checkName(); this.registry.add(this.control, this); } - handleClick(event: Event, radioButton: HTMLElement, focus: boolean) { - event.preventDefault(); - - if (this.disabled) { - return; - } - - this.select(event); - - if (focus) { - radioButton.focus(); + onChange(event) { + if (!this.disabled) { + this.select(event); } } select(event: Event) { if (!this.disabled) { - this.inputViewChild.nativeElement.checked = true; this.checked = true; this.onModelChange(this.value); this.registry.select(this); @@ -289,6 +256,7 @@ export class RadioButton implements ControlValueAccessor, OnInit, OnDestroy { ngOnDestroy() { this.registry.remove(this); + super.ngOnDestroy(); } private checkName() { diff --git a/src/app/components/radiobutton/style/radiobuttonstyle.ts b/src/app/components/radiobutton/style/radiobuttonstyle.ts new file mode 100644 index 00000000000..d2b24f06165 --- /dev/null +++ b/src/app/components/radiobutton/style/radiobuttonstyle.ts @@ -0,0 +1,145 @@ +import { Injectable } from '@angular/core'; +import { BaseStyle } from 'primeng/base'; + +const theme = ({ dt }) => ` +.p-radiobutton { + position: relative; + display: inline-flex; + user-select: none; + vertical-align: bottom; + width: ${dt('radiobutton.width')}; + height: ${dt('radiobutton.height')}; +} + +.p-radiobutton-input { + cursor: pointer; + appearance: none; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + padding: 0; + margin: 0; + opacity: 0; + z-index: 1; + outline: 0 none; + border: 1px solid transparent; + border-radius: 50%; +} + +.p-radiobutton-box { + display: flex; + justify-content: center; + align-items: center; + border-radius: 50%; + border: 1px solid ${dt('radiobutton.border.color')}; + background: ${dt('radiobutton.background')}; + width: ${dt('radiobutton.width')}; + height: ${dt('radiobutton.height')}; + transition: background ${dt('radiobutton.transition.duration')}, color ${dt('radiobutton.transition.duration')}, border-color ${dt('radiobutton.transition.duration')}, box-shadow ${dt('radiobutton.transition.duration')}, outline-color ${dt( + 'radiobutton.transition.duration' +)}; + outline-color: transparent; + box-shadow: ${dt('radiobutton.shadow')}; +} + +.p-radiobutton-icon { + transition-duration: ${dt('radiobutton.transition.duration')}; + background: transparent; + font-size: ${dt('radiobutton.icon.size')}; + width: ${dt('radiobutton.icon.size')}; + height: ${dt('radiobutton.icon.size')}; + border-radius: 50%; + backface-visibility: hidden; + transform: translateZ(0) scale(0.1); +} + +.p-radiobutton:not(.p-disabled):has(.p-radiobutton-input:hover) .p-radiobutton-box { + border-color: ${dt('radiobutton.hover.border.color')}; +} + +.p-radiobutton-checked .p-radiobutton-box { + border-color: ${dt('radiobutton.checked.border.color')}; + background: ${dt('radiobutton.checked.background')}; +} + +.p-radiobutton-checked .p-radiobutton-box .p-radiobutton-icon { + background: ${dt('radiobutton.icon.checked.color')}; + transform: translateZ(0) scale(1, 1); + visibility: visible; +} + +.p-radiobutton-checked:not(.p-disabled):has(.p-radiobutton-input:hover) .p-radiobutton-box { + border-color: ${dt('radiobutton.checked.hover.border.color')}; + background: ${dt('radiobutton.checked.hover.background')}; +} + +.p-radiobutton:not(.p-disabled):has(.p-radiobutton-input:hover).p-radiobutton-checked .p-radiobutton-box .p-radiobutton-icon { + background: ${dt('radiobutton.icon.checked.hover.color')}; +} + +.p-radiobutton:not(.p-disabled):has(.p-radiobutton-input:focus-visible) .p-radiobutton-box { + border-color: ${dt('radiobutton.focus.border.color')}; + box-shadow: ${dt('radiobutton.focus.ring.shadow')}; + outline: ${dt('radiobutton.focus.ring.width')} ${dt('radiobutton.focus.ring.style')} ${dt('radiobutton.focus.ring.color')}; + outline-offset: ${dt('radiobutton.focus.ring.offset')}; +} + +.p-radiobutton-checked:not(.p-disabled):has(.p-radiobutton-input:focus-visible) .p-radiobutton-box { + border-color: ${dt('radiobutton.checked.focus.border.color')}; +} + +.p-radiobutton.ng-invalid.ng-dirty > .p-radiobutton-box { + border-color: ${dt('radiobutton.invalid.border.color')}; +} + +.p-radiobutton.p-variant-filled .p-radiobutton-box { + background: ${dt('radiobutton.filled.background')}; +} + +.p-radiobutton.p-variant-filled.p-radiobutton-checked .p-radiobutton-box { + background: ${dt('radiobutton.checked.background')}; +} + +.p-radiobutton.p-variant-filled:not(.p-disabled):has(.p-radiobutton-input:hover).p-radiobutton-checked .p-radiobutton-box { + background: ${dt('radiobutton.checked.hover.background')}; +} + +.p-radiobutton.p-disabled { + opacity: 1; +} + +.p-radiobutton.p-disabled .p-radiobutton-box { + background: ${dt('radiobutton.disabled.background')}; + border-color: ${dt('radiobutton.checked.disabled.border.color')}; +} + +.p-radiobutton-checked.p-disabled .p-radiobutton-box .p-radiobutton-icon { + background: ${dt('radiobutton.icon.disabled.color')}; +} +`; + +const classes = { + root: ({ instance, props }) => [ + 'p-radiobutton p-component', + { + 'p-radiobutton-checked': instance.checked, + 'p-disabled': props.disabled, + 'p-invalid': props.invalid, + 'p-variant-filled': props.variant ? props.variant === 'filled' : instance.$primevue.config.inputStyle === 'filled' || instance.$primevue.config.inputVariant === 'filled' + } + ], + box: 'p-radiobutton-box', + input: 'p-radiobutton-input', + icon: 'p-radiobutton-icon' +}; + +@Injectable() +export class RadioButtonStyle extends BaseStyle { + name = 'radiobutton'; + + theme = theme; + + classes = classes; +} diff --git a/src/app/showcase/doc/radiobutton/invaliddoc.ts b/src/app/showcase/doc/radiobutton/invaliddoc.ts index 7b95c0b4e68..eea99b63a23 100644 --- a/src/app/showcase/doc/radiobutton/invaliddoc.ts +++ b/src/app/showcase/doc/radiobutton/invaliddoc.ts @@ -8,13 +8,13 @@ import { Code } from '@domain/code';

Invalid state style is added using the ng-invalid and ng-dirty class to indicate a failed validation.

- +
` }) export class InvalidDoc { - checked: any; + checked: any = false; code: Code = { basic: ``, diff --git a/src/assets/showcase/styles/primeng.css b/src/assets/showcase/styles/primeng.css index ec8404b80f0..48beb6050ea 100755 --- a/src/assets/showcase/styles/primeng.css +++ b/src/assets/showcase/styles/primeng.css @@ -1,5 +1,4 @@ @import '../../../app/components/common/common.css'; @import '../../../app/components/password/password.css'; -@import '../../../app/components/radiobutton/radiobutton.css'; @import '../../../app/components/ripple/ripple.css'; @import '../../../app/components/tooltip/tooltip.css'; \ No newline at end of file