diff --git a/projects/igniteui-angular/src/lib/combo/combo.component.html b/projects/igniteui-angular/src/lib/combo/combo.component.html
index 9e795a6f3d3..9fa321d2514 100644
--- a/projects/igniteui-angular/src/lib/combo/combo.component.html
+++ b/projects/igniteui-angular/src/lib/combo/combo.component.html
@@ -9,7 +9,7 @@
, set2: Set): any[] => {
IgxComboAddItemComponent,
IgxButtonDirective,
IgxRippleDirective,
+ IgxReadOnlyInputDirective,
IgxComboFilteringPipe,
IgxComboGroupingPipe
]
diff --git a/projects/igniteui-angular/src/lib/core/styles/components/input/_input-group-component.scss b/projects/igniteui-angular/src/lib/core/styles/components/input/_input-group-component.scss
index 00062ada101..bf077148fc2 100644
--- a/projects/igniteui-angular/src/lib/core/styles/components/input/_input-group-component.scss
+++ b/projects/igniteui-angular/src/lib/core/styles/components/input/_input-group-component.scss
@@ -42,11 +42,11 @@
}
@include e(notch) {
- @extend %igx-input-group__notch !optional;
+ @extend %igx-input-group__notch !optional;
}
@include e(filler) {
- @extend %igx-input-group__filler !optional;
+ @extend %igx-input-group__filler !optional;
}
@include e(input) {
@@ -111,20 +111,24 @@
@extend %suffixed !optional;
}
+ @include m(readonly) {
+ @extend %form-group-display--readonly !optional;
+ }
+
// Textarea modifier
@include m(textarea-group) {
@extend %textarea-group !optional;
@include e(bundle-main) {
- @extend %form-group-textarea-group-bundle-main !optional;
+ @extend %form-group-textarea-group-bundle-main !optional;
}
@include e(bundle) {
- @extend %form-group-textarea-group-bundle !optional;
+ @extend %form-group-textarea-group-bundle !optional;
}
@include e(label) {
- @extend %form-group-textarea-label !optional;
+ @extend %form-group-textarea-label !optional;
}
}
@@ -282,6 +286,8 @@
}
@include m(invalid) {
+ @extend %form-group-display--invalid !optional;
+
@include e(label) {
@extend %form-group-label--error !optional;
}
diff --git a/projects/igniteui-angular/src/lib/core/styles/components/input/_input-group-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/input/_input-group-theme.scss
index 66643c96e65..05b21fa297c 100644
--- a/projects/igniteui-angular/src/lib/core/styles/components/input/_input-group-theme.scss
+++ b/projects/igniteui-angular/src/lib/core/styles/components/input/_input-group-theme.scss
@@ -286,6 +286,91 @@
}
}
+ %form-group-display--readonly:not(%form-group-display--file) {
+ igx-prefix,
+ [igxPrefix],
+ igx-suffix,
+ [igxSuffix] {
+ color: var-get($theme, 'disabled-text-color');
+
+ @if $variant == 'fluent' {
+ background: transparent;
+ }
+
+ @if $variant == 'bootstrap' {
+ background: var-get($theme, 'border-disabled-background');
+ }
+ }
+
+ @if $variant == 'bootstrap' {
+ %form-group-input {
+ background: var-get($theme, 'border-disabled-background');
+ }
+ }
+
+ %form-group-bundle--hover::after {
+ @if $variant == 'material' {
+ border-block-end-color: var-get($theme, 'idle-bottom-line-color');
+ }
+ }
+
+ @if $variant == 'indigo' {
+ %form-group-bundle--hover:not(:focus-within) {
+ background: unset;
+
+ &::after {
+ border-block-end-color: var-get($theme, 'disabled-text-color');
+ }
+ }
+ }
+
+ &%igx-input-group-fluent:not(:focus-within) {
+ %form-group-bundle--hover::before {
+ box-shadow: inset 0 0 0 var(--_fluent-input-border-size) var-get($theme, 'border-color');
+ }
+ }
+
+ &%form-group-display--box:not(%form-group-display--disabled) {
+ %form-group-bundle {
+ background: var-get($theme, 'box-background-focus');
+ }
+ }
+
+ &%form-group-display--border:not(%form-group-display--disabled) {
+ %form-group-bundle:hover:not(:focus-within) {
+ %form-group-bundle-start,
+ %igx-input-group__filler,
+ %form-group-bundle-end {
+ border-color: var-get($theme, 'border-color');
+ }
+
+ %igx-input-group__notch {
+ border-block-start-color: var-get($theme, 'border-color');
+ border-block-end-color: var-get($theme, 'border-color');
+ }
+ }
+ }
+
+ &%form-group-display--search {
+ %form-group-bundle-search--hover:not(:focus-within) {
+ box-shadow: var-get($theme, 'search-resting-shadow');
+ }
+ }
+
+ &:hover {
+ %form-group-input--hover {
+ cursor: default;
+ color: var-get($theme, 'filled-text-color');
+
+ &:not(:focus-within) {
+ &::placeholder {
+ color: var-get($theme, 'placeholder-color');
+ }
+ }
+ }
+ }
+ }
+
%form-group-display--disabled {
pointer-events: none;
user-select: none;
@@ -361,8 +446,6 @@
}
%form-group-bundle--hover {
- //cursor: pointer;
-
&::after {
border-block-end-width: rem(1px);
border-block-end-color: var-get($theme, 'hover-bottom-line-color');
@@ -396,13 +479,6 @@
caret-color: initial;
}
- %form-group-bundle--error {
- &::after {
- border-block-end-color: var-get($theme, 'error-secondary-color');
- }
- caret-color: initial;
- }
-
%form-group-bundle--disabled {
cursor: default;
@@ -612,8 +688,7 @@
%bootstrap-file-focused,
%bootstrap-file-valid,
%bootstrap-file-warning,
- %bootstrap-file-invalid
- {
+ %bootstrap-file-invalid {
%form-group-bundle {
border-radius: var-get($theme, 'box-border-radius');
transition: box-shadow .15s ease-out, border .15s ease-out;
@@ -906,11 +981,9 @@
}
&:hover {
- %form-group-bundle-start {
- border-color: var-get($theme, 'hover-border-color');
- }
-
- %igx-input-group__filler {
+ %form-group-bundle-start,
+ %igx-input-group__filler,
+ %form-group-bundle-end {
border-color: var-get($theme, 'hover-border-color');
}
@@ -918,10 +991,6 @@
border-block-start-color: var-get($theme, 'hover-border-color');
border-block-end-color: var-get($theme, 'hover-border-color');
}
-
- %form-group-bundle-end {
- border-color: var-get($theme, 'hover-border-color');
- }
}
}
@@ -1242,10 +1311,6 @@
color: var-get($theme, 'success-secondary-color');
}
- %form-group-label--error {
- color: var-get($theme, 'error-secondary-color');
- }
-
%form-group-label--required {
&::after {
content: '#{$required-symbol}';
@@ -1517,11 +1582,8 @@
}
}
- %form-group-line--error {
- background: var-get($theme, 'error-secondary-color');
- }
-
- %form-group-border--error {
+ %form-group-border--error:not(%form-group-display--readonly),
+ %form-group-border--error%form-group-display--file {
%form-group-bundle-start {
border-inline-start-color: var-get($theme, 'error-secondary-color');
border-block-start-color: var-get($theme, 'error-secondary-color');
@@ -1611,10 +1673,6 @@
%form-group-helper--warning {
color: var-get($theme, 'warning-secondary-color');
}
-
- %form-group-helper--error {
- color: var-get($theme, 'error-secondary-color');
- }
}
%form-group-helper-item {
@@ -1861,14 +1919,6 @@
}
}
- %form-group-bundle-error--fluent,
- %form-group-bundle-error--fluent--hover,
- %form-group-bundle-error--fluent--focus {
- &::before {
- box-shadow: inset 0 0 0 var(--_fluent-input-border-size) var-get($theme, 'error-secondary-color');
- }
- }
-
%form-group-bundle-success--fluent,
%form-group-bundle-success--fluent--hover,
%form-group-bundle-success--fluent--focus {
@@ -1898,6 +1948,48 @@
}
}
+ %form-group-display--invalid:not(%form-group-display--readonly),
+ %form-group-display--invalid%form-group-display--file {
+ @if $variant != 'indigo' {
+ %form-group-label--error,
+ %form-group-helper--error {
+ color: var-get($theme, 'error-secondary-color');
+ }
+ }
+
+ %form-group-line--error {
+ background: var-get($theme, 'error-secondary-color');
+ }
+
+ %form-group-bundle--error {
+ &::after {
+ border-block-end-color: var-get($theme, 'error-secondary-color');
+ }
+
+ caret-color: initial;
+ }
+
+ &%form-group-display--bootstrap {
+ %bootstrap-input--error {
+ border: rem(1px) solid var-get($theme, 'error-secondary-color');
+
+ &:focus {
+ box-shadow: 0 0 0 rem(4px) var-get($theme, 'error-shadow-color');
+ }
+ }
+ }
+
+ &%igx-input-group-fluent {
+ %form-group-bundle-error--fluent,
+ %form-group-bundle-error--fluent--hover,
+ %form-group-bundle-error--fluent--focus {
+ &::before {
+ box-shadow: inset 0 0 0 var(--_fluent-input-border-size) var-get($theme, 'error-secondary-color');
+ }
+ }
+ }
+ }
+
// Native input
%fluent-input {
font-size: rem(14px);
@@ -2266,14 +2358,16 @@
box-shadow: 0 0 0 rem(4px) color($color: 'warn', $variant: '500', $opacity: .38);
}
- %bootstrap-input--error {
- border: rem(1px) solid var-get($theme, 'error-secondary-color');
+ %form-group-display:not(%form-group-display--file) {
+ %bootstrap-input--error {
+ border: rem(1px) solid var-get($theme, 'error-secondary-color');
- &:focus {
- box-shadow: 0 0 0 rem(4px) var-get($theme, 'error-shadow-color');
-
- + %bootstrap-file-input {
+ &:focus {
box-shadow: 0 0 0 rem(4px) var-get($theme, 'error-shadow-color');
+
+ + %bootstrap-file-input {
+ box-shadow: 0 0 0 rem(4px) var-get($theme, 'error-shadow-color');
+ }
}
}
}
diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.component.html b/projects/igniteui-angular/src/lib/date-picker/date-picker.component.html
index 81159a032bd..551816ddb1c 100644
--- a/projects/igniteui-angular/src/lib/date-picker/date-picker.component.html
+++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.component.html
@@ -14,7 +14,9 @@
}
-
-
diff --git a/projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.ts b/projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.ts
index e09b95b35ce..46347ae67c4 100644
--- a/projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.ts
+++ b/projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.ts
@@ -37,6 +37,7 @@ import { getCurrentResourceStrings } from '../core/i18n/resources';
import { fadeIn, fadeOut } from 'igniteui-angular/animations';
import { PickerCalendarOrientation } from '../date-common/types';
import { calendarRange, isDateInRanges } from '../calendar/common/helpers';
+import { IgxReadOnlyInputDirective } from '../directives/input/read-only-input.directive';
const SingleInputDatesConcatenationString = ' - ';
@@ -76,6 +77,7 @@ const SingleInputDatesConcatenationString = ' - ';
IgxInputDirective,
IgxPrefixDirective,
IgxSuffixDirective,
+ IgxReadOnlyInputDirective,
DateRangePickerFormatPipe
]
})
@@ -351,6 +353,10 @@ export class IgxDateRangePickerComponent extends PickerBaseDirective
@Input({ transform: booleanAttribute })
public showWeekNumbers = false;
+ /** @hidden @internal */
+ @Input({ transform: booleanAttribute })
+ public readOnly = false;
+
/**
* Emitted when the picker's value changes. Used for two-way binding.
*
@@ -643,7 +649,7 @@ export class IgxDateRangePickerComponent extends PickerBaseDirective
* ```
*/
public open(overlaySettings?: OverlaySettings): void {
- if (!this.collapsed || this.disabled) {
+ if (!this.collapsed || this.disabled || this.readOnly) {
return;
}
diff --git a/projects/igniteui-angular/src/lib/directives/input/read-only-input.directive.spec.ts b/projects/igniteui-angular/src/lib/directives/input/read-only-input.directive.spec.ts
new file mode 100644
index 00000000000..43f7b4edfc0
--- /dev/null
+++ b/projects/igniteui-angular/src/lib/directives/input/read-only-input.directive.spec.ts
@@ -0,0 +1,61 @@
+import { Component, ViewChild } from '@angular/core';
+import { TestBed, waitForAsync } from '@angular/core/testing';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { IgxReadOnlyInputDirective } from './read-only-input.directive';
+import { IgxDatePickerComponent, IgxInputGroupComponent } from 'igniteui-angular';
+import { By } from '@angular/platform-browser';
+
+describe('IgxReadOnlyInputDirective', () => {
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ NoopAnimationsModule,
+ TestComponent
+ ]
+ })
+ .compileComponents();
+ }));
+
+ it('should update readOnly property and `igx-input-group--readonly` class correctly', () => {
+ const fixture = TestBed.createComponent(TestComponent);
+ fixture.detectChanges();
+
+ const inputGroupDebug = fixture.debugElement.query(By.directive(IgxInputGroupComponent));
+ const inputGroupEl = inputGroupDebug.nativeElement as HTMLElement;
+ expect(inputGroupEl.classList.contains('igx-input-group--readonly')).toBeFalse();
+
+ const inputDebug = fixture.debugElement.query(By.css('input'));
+ const inputEl = inputDebug.nativeElement as HTMLInputElement;
+ expect(inputEl.readOnly).toBeFalse();
+
+ fixture.componentInstance.datePicker.readOnly = true;
+ fixture.detectChanges();
+ expect(inputGroupEl.classList.contains('igx-input-group--readonly')).toBeTrue();
+ expect(inputEl.readOnly).toBeTrue();
+
+ fixture.componentInstance.datePicker.readOnly = false;
+ fixture.detectChanges();
+ expect(inputGroupEl.classList.contains('igx-input-group--readonly')).toBeFalse();
+ expect(inputEl.readOnly).toBeFalse();
+
+ // When the date-picker component is in dialog mode, the native input is always readonly
+ fixture.componentInstance.datePicker.mode = 'dialog';
+ fixture.detectChanges();
+ expect(inputGroupEl.classList.contains('igx-input-group--readonly')).toBeFalse();
+ expect(inputEl.readOnly).toBeTrue();
+
+ fixture.componentInstance.datePicker.readOnly = true;
+ fixture.detectChanges();
+ expect(inputGroupEl.classList.contains('igx-input-group--readonly')).toBeTrue();
+ expect(inputEl.readOnly).toBeTrue();
+ });
+});
+
+@Component({
+ template: ``,
+ imports: [IgxDatePickerComponent, IgxReadOnlyInputDirective]
+})
+class TestComponent {
+ @ViewChild(IgxDatePickerComponent, { static: true })
+ public datePicker!: IgxDatePickerComponent;
+}
diff --git a/projects/igniteui-angular/src/lib/directives/input/read-only-input.directive.ts b/projects/igniteui-angular/src/lib/directives/input/read-only-input.directive.ts
new file mode 100644
index 00000000000..e3163465608
--- /dev/null
+++ b/projects/igniteui-angular/src/lib/directives/input/read-only-input.directive.ts
@@ -0,0 +1,26 @@
+import { Directive, effect, inject, input } from '@angular/core';
+import { IgxInputGroupComponent } from '../../input-group/input-group.component';
+
+@Directive({
+ selector: '[igxReadOnlyInput]',
+ exportAs: 'igxReadOnlyInput',
+ standalone: true
+})
+export class IgxReadOnlyInputDirective {
+ public igxReadOnlyInput = input(false);
+
+ private _inputGroup: IgxInputGroupComponent | null = inject(
+ IgxInputGroupComponent,
+ {
+ optional: true
+ }
+ );
+
+ constructor() {
+ effect(() => {
+ if (this._inputGroup) {
+ this._inputGroup.readOnly = this.igxReadOnlyInput();
+ }
+ });
+ }
+}
diff --git a/projects/igniteui-angular/src/lib/grids/filtering/base/grid-filtering-row.component.html b/projects/igniteui-angular/src/lib/grids/filtering/base/grid-filtering-row.component.html
index 48bf1c5c0a1..ce397fa1313 100644
--- a/projects/igniteui-angular/src/lib/grids/filtering/base/grid-filtering-row.component.html
+++ b/projects/igniteui-angular/src/lib/grids/filtering/base/grid-filtering-row.component.html
@@ -60,7 +60,6 @@
- Angular Date Picker