From 44d9d4e1aeb5254cb19d5f66c49d2a20dcbb0fba Mon Sep 17 00:00:00 2001 From: Simeon Simeonoff Date: Thu, 27 Feb 2025 14:08:17 +0200 Subject: [PATCH 1/3] feat(list): add selected property to the list-item component Closes #15121 --- package-lock.json | 8 +- package.json | 2 +- projects/igniteui-angular/package.json | 2 +- .../components/list/_list-component.scss | 5 + .../styles/components/list/_list-theme.scss | 91 +++++++++++++++++++ .../src/lib/list/list-item.component.ts | 26 ++++++ .../src/lib/list/list.component.spec.ts | 40 +++++++- .../lib/test-utils/list-components.spec.ts | 15 +++ src/app/list/list.sample.html | 9 +- src/app/list/list.sample.ts | 1 + 10 files changed, 188 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4b3ad44fd3e..0efb41fd6c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@types/source-map": "0.5.2", "express": "^4.21.1", "fflate": "^0.8.1", - "igniteui-theming": "^15.0.0", + "igniteui-theming": "^15.1.1", "igniteui-trial-watermark": "^3.0.2", "lodash-es": "^4.17.21", "rxjs": "^7.8.0", @@ -12518,9 +12518,9 @@ } }, "node_modules/igniteui-theming": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/igniteui-theming/-/igniteui-theming-15.0.0.tgz", - "integrity": "sha512-4ANFe6t3t9fEDexmB8/H7hEO1eBBWohu1tbEUFdfQPBMgUT/qqXbSlxW3FGCmZV7Oxr5rn9ZdXyScPxzwRx1lg==", + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/igniteui-theming/-/igniteui-theming-15.1.1.tgz", + "integrity": "sha512-PvU7znjYOUAT3fSX94gRrAmgFvvM8z04x6JJNAnO/7/jYSJmSWFtW3G4NfAMm8Uross5IGZuYq4cjlQo1a/Hkw==", "license": "MIT", "peerDependencies": { "sass": "^1.69.5" diff --git a/package.json b/package.json index 2b77eeb5aad..3034c3cf77f 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "@types/source-map": "0.5.2", "express": "^4.21.1", "fflate": "^0.8.1", - "igniteui-theming": "^15.0.0", + "igniteui-theming": "^15.1.1", "igniteui-trial-watermark": "^3.0.2", "lodash-es": "^4.17.21", "rxjs": "^7.8.0", diff --git a/projects/igniteui-angular/package.json b/projects/igniteui-angular/package.json index fc8fd3df4f0..783505dac40 100644 --- a/projects/igniteui-angular/package.json +++ b/projects/igniteui-angular/package.json @@ -73,7 +73,7 @@ "tslib": "^2.3.0", "igniteui-trial-watermark": "^3.0.2", "lodash-es": "^4.17.21", - "igniteui-theming": "^15.0.0", + "igniteui-theming": "^15.1.1", "@igniteui/material-icons-extended": "^3.1.0" }, "peerDependencies": { diff --git a/projects/igniteui-angular/src/lib/core/styles/components/list/_list-component.scss b/projects/igniteui-angular/src/lib/core/styles/components/list/_list-component.scss index 7d3759eadf4..6f551506d24 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/list/_list-component.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/list/_list-component.scss @@ -28,6 +28,11 @@ @extend %igx-list-item-base--active !optional; } + // css class `igx-list__item-base--selected + @include e(item-base, $m: selected) { + @extend %igx-list-item-base--selected !optional; + } + // css class 'igx-list__item-right' applied to the panning container shown when the list item is panned left @include e(item-right) { @extend %igx-list-item-pan !optional; diff --git a/projects/igniteui-angular/src/lib/core/styles/components/list/_list-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/list/_list-theme.scss index e06678a80a1..7605e2504f9 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/list/_list-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/list/_list-theme.scss @@ -19,21 +19,27 @@ /// @param {Color} $item-background [null] - The list item background color. /// @param {Color} $item-background-hover [null] - The list item hover background color. /// @param {Color} $item-background-active [null] - The active list item background color. +/// @param {Color} $item-background-selected [null] - The selected list item background color. /// @param {Color} $item-text-color [null] - The list item text color. /// @param {Color} $item-text-color-hover [null] - The list item hover text color. /// @param {Color} $item-text-color-active [null] - The active list item text color. +/// @param {Color} $item-text-color-selected [null] - The selected list item text color. /// @param {Color} $item-title-color [null] - The list item title color. /// @param {Color} $item-title-color-hover [null] - The list item hover title color. /// @param {Color} $item-title-color-active [null] - The active list item title color. +/// @param {Color} $item-title-color-selected [null] - The selected list item title color. /// @param {Color} $item-subtitle-color [null] - The list item subtitle color. /// @param {Color} $item-subtitle-color-hover [null] - The list item hover subtitle color. /// @param {Color} $item-subtitle-color-active [null] - The active list item subtitle color. +/// @param {Color} $item-subtitle-color-selected [null] - The selected list item subtitle color. /// @param {Color} $item-action-color [null] - The list item action color. /// @param {Color} $item-action-color-hover [null] - The list item hover action color. /// @param {Color} $item-action-color-active [null] - The active list item action color. +/// @param {Color} $item-action-color-selected [null] - The selected list item action color. /// @param {Color} $item-thumbnail-color [null] - The list item thumbnail color. /// @param {Color} $item-thumbnail-color-hover [null] - The list item hover thumbnail color. /// @param {Color} $item-thumbnail-color-active [null] - The active list item thumbnail color. +/// @param {Color} $item-thumbnail-color-selected [null] - The selected list item thumbnail color. /// @param {List} $border-radius [null] - The border radius used for list component. /// @param {List} $item-border-radius [null] - The border radius used for list item. /// @param {Color} $border-width [null] - The list border width. @@ -54,21 +60,27 @@ $item-background: null, $item-background-hover: null, $item-background-active: null, + $item-background-selected: null, $item-text-color: null, $item-text-color-hover: null, $item-text-color-active: null, + $item-text-color-selected: null, $item-title-color: null, $item-title-color-hover: null, $item-title-color-active: null, + $item-title-color-selected: null, $item-subtitle-color: null, $item-subtitle-color-hover: null, $item-subtitle-color-active: null, + $item-subtitle-color-selected: null, $item-action-color: null, $item-action-color-hover: null, $item-action-color-active: null, + $item-action-color-selected: null, $item-thumbnail-color: null, $item-thumbnail-color-hover: null, $item-thumbnail-color-active: null, + $item-thumbnail-color-selected: null, $border-color: null, $border-width: null, ) { @@ -120,6 +132,16 @@ } } + @if not($item-background-selected) and $item-background { + @if meta.type-of($item-background) == 'color' { + @if luminance($item-background) < .5 { + $item-background-selected: color.scale($item-background, $lightness: 8%); + } @else { + $item-background-selected: color.scale($item-background, $lightness: -8%); + } + } + } + @if not($header-text-color) and $header-background { $header-text-color: text-contrast($header-background); } @@ -180,28 +202,54 @@ $item-text-color-active: text-contrast($item-background-active); } + @if not($item-text-color-selected) and $item-background-selected { + $item-text-color-selected: text-contrast($item-background-selected); + } + @if not($item-title-color-active) and $item-background-active { $item-title-color-active: text-contrast($item-background-active); } + @if not($item-title-color-selected) and $item-background-selected { + $item-title-color-selected: text-contrast($item-background-selected); + } + @if not($item-action-color-active) and $item-background-active { $item-action-color-active: text-contrast($item-background-active); } + @if not($item-action-color-selected) and $item-background-selected { + $item-action-color-selected: text-contrast($item-background-selected); + } + @if not($item-thumbnail-color-active) and $item-background-active { $item-thumbnail-color-active: text-contrast($item-background-active); } + @if not($item-thumbnail-color-selected) and $item-background-selected { + $item-thumbnail-color-selected: text-contrast($item-background-selected); + } + @if not($item-subtitle-color-active) and $item-background-active { @if meta.type-of($item-background-active) == 'color' { $item-subtitle-color-active: rgba(text-contrast($item-background-active), .74); } } + @if not($item-subtitle-color-selected) and $item-background-selected { + @if meta.type-of($item-background-selected) == 'color' { + $item-subtitle-color-selected: rgba(text-contrast($item-background-selected), .74); + } + } + @if not($item-subtitle-color-active) and $item-text-color-active { $item-subtitle-color-active: $item-text-color-active; } + @if not($item-subtitle-color-selected) and $item-text-color-selected { + $item-subtitle-color-selected: $item-text-color-selected; + } + @return extend($theme, ( name: $name, border-radius: $border-radius, @@ -212,21 +260,27 @@ item-background: $item-background, item-background-hover: $item-background-hover, item-background-active: $item-background-active, + item-background-selected: $item-background-selected, item-text-color: $item-text-color, item-text-color-hover: $item-text-color-hover, item-text-color-active: $item-text-color-active, + item-text-color-selected: $item-text-color-selected, item-title-color:$item-title-color, item-title-color-hover:$item-title-color-hover, item-title-color-active:$item-title-color-active, + item-title-color-selected:$item-title-color-selected, item-subtitle-color: $item-subtitle-color, item-subtitle-color-hover: $item-subtitle-color-hover, item-subtitle-color-active: $item-subtitle-color-active, + item-subtitle-color-selected: $item-subtitle-color-selected, item-action-color: $item-action-color, item-action-color-hover: $item-action-color-hover, item-action-color-active: $item-action-color-active, + item-action-color-selected: $item-action-color-selected, item-thumbnail-color: $item-thumbnail-color, item-thumbnail-color-hover: $item-thumbnail-color-hover, item-thumbnail-color-active: $item-thumbnail-color-active, + item-thumbnail-color-selected: $item-thumbnail-color-selected, border-color: $border-color, border-width: $border-width, theme: map.get($schema, '_meta', 'theme'), @@ -423,6 +477,12 @@ } } + %igx-list-item-base--selected { + %igx-list-item-content { + @extend %igx-list-item-content--selected; + } + } + %igx-list-item-pan { position: absolute; visibility: hidden; @@ -550,6 +610,37 @@ } } + %igx-list-item-content--selected { + color: var-get($theme, 'item-text-color-selected'); + background: var-get($theme, 'item-background-selected'); + z-index: 3; + + %igx-list__item-line-title { + color: var-get($theme, 'item-title-color-selected') + } + + %igx-list__item-line-subtitle { + color: var-get($theme, 'item-subtitle-color-selected') + } + + %igx-list__item-actions { + color: var-get($theme, 'item-action-color-selected'); + + igx-icon, + igc-icon { + color: var-get($theme, 'item-action-color-selected')} + } + + %igx-list__item-thumbnail { + color: var-get($theme, 'item-thumbnail-color-selected'); + + igx-icon, + igc-icon { + color: var-get($theme, 'item-thumbnail-color-selected') + } + } + } + %igx-list-item-content--inactive { transition: transform .3s $out-quad; } diff --git a/projects/igniteui-angular/src/lib/list/list-item.component.ts b/projects/igniteui-angular/src/lib/list/list-item.component.ts index 6d4eb22a51d..9b014df69ea 100644 --- a/projects/igniteui-angular/src/lib/list/list-item.component.ts +++ b/projects/igniteui-angular/src/lib/list/list-item.component.ts @@ -19,6 +19,7 @@ import { import { HammerGesturesManager } from '../core/touch'; import { rem } from '../core/utils'; import { NgTemplateOutlet } from '@angular/common'; +import { Host } from 'igniteui-dockmanager/dist/types/stencil-public-runtime'; /** * The Ignite UI List Item component is a container intended for row items in the Ignite UI for Angular List component. @@ -133,6 +134,7 @@ export class IgxListItemComponent implements IListChild { private lastPanDir = IgxListPanState.NONE; private _role: string = ''; + private _selected = false;; /** * Gets the `panState` of a `list item`. @@ -281,6 +283,30 @@ export class IgxListItemComponent implements IListChild { this._role = val; } + /** + * Sets/gets whether the `list item` is selected. + * Selection is only applied to non-header items. + * When selected, the CSS class 'igx-list__item-base--selected' is added to the item. + * ```html + * Selected Item + * ``` + * ```typescript + * let isSelected = this.listItem.selected; + * this.listItem.selected = true; + * ``` + * + * @memberof IgxListItemComponent + */ + @HostBinding('class.igx-list__item-base--selected') + @Input({ transform: booleanAttribute }) + public get selected() { + return this._selected && !this.isHeader; + } + + public set selected(value: boolean) { + this._selected = value; + } + /** * Indicates whether `list item` should have header style. * ```typescript diff --git a/projects/igniteui-angular/src/lib/list/list.component.spec.ts b/projects/igniteui-angular/src/lib/list/list.component.spec.ts index d4a13345d07..4b427f3c7d8 100644 --- a/projects/igniteui-angular/src/lib/list/list.component.spec.ts +++ b/projects/igniteui-angular/src/lib/list/list.component.spec.ts @@ -24,7 +24,8 @@ import { ListWithIgxForAndScrollingComponent, TwoHeadersListComponent, TwoHeadersListNoPanningComponent, - ListDirectivesComponent + ListDirectivesComponent, + ListWithSelectedItemComponent } from '../test-utils/list-components.spec'; import { configureTestSuite } from '../test-utils/configure-suite'; import { wait } from '../test-utils/ui-interactions.spec'; @@ -44,7 +45,8 @@ describe('List', () => { TwoHeadersListNoPanningComponent, ListWithPanningTemplatesComponent, ListWithIgxForAndScrollingComponent, - ListDirectivesComponent + ListDirectivesComponent, + ListWithSelectedItemComponent ] }); }); @@ -661,6 +663,40 @@ describe('List', () => { } })); + it('should properly set and get the selected property of list items', () => { + const fixture = TestBed.createComponent(ListWithSelectedItemComponent); + const list = fixture.componentInstance.list; + fixture.detectChanges(); + + // Get all list items + const items = list.children.toArray(); + const headerItem = items[0]; + const firstItem = items[1]; + const secondItem = items[2]; + + // Verify initial selected state + expect(headerItem.selected).toBe(false); // Headers should never be selected even if selected=true + expect(firstItem.selected).toBe(true); + expect(secondItem.selected).toBe(false); + + // Check if the selected class is applied correctly + expect(headerItem.element.classList.contains('igx-list__item-base--selected')).toBe(false); + expect(firstItem.element.classList.contains('igx-list__item-base--selected')).toBe(true); + expect(secondItem.element.classList.contains('igx-list__item-base--selected')).toBe(false); + + // Change selected state programmatically + secondItem.selected = true; + fixture.detectChanges(); + expect(secondItem.selected).toBe(true); + expect(secondItem.element.classList.contains('igx-list__item-base--selected')).toBe(true); + + // Try to select a header item (should not apply) + headerItem.selected = true; + fixture.detectChanges(); + expect(headerItem.selected).toBe(false); + expect(headerItem.element.classList.contains('igx-list__item-base--selected')).toBe(false); + }); + it('Initializes igxListThumbnail directive', () => { const fixture = TestBed.createComponent(ListDirectivesComponent); fixture.detectChanges(); diff --git a/projects/igniteui-angular/src/lib/test-utils/list-components.spec.ts b/projects/igniteui-angular/src/lib/test-utils/list-components.spec.ts index 6e1bd77a99d..ea1f27749e8 100644 --- a/projects/igniteui-angular/src/lib/test-utils/list-components.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/list-components.spec.ts @@ -63,6 +63,21 @@ export class BasicListComponent { export class ListWithHeaderComponent extends BasicListComponent { } +@Component({ + template: ` +
+ + Header + Item 1 + Item 2 + Item 3 + +
`, + imports: [IgxListComponent, IgxListItemComponent] +}) +export class ListWithSelectedItemComponent extends BasicListComponent { +} + @Component({ template: `
diff --git a/src/app/list/list.sample.html b/src/app/list/list.sample.html index fc87ee69955..c58c9218e77 100644 --- a/src/app/list/list.sample.html +++ b/src/app/list/list.sample.html @@ -3,9 +3,9 @@
Angular List
- Employees List + Employees List @for (employee of employeeItems; track employee) { - + @if(properties.addAvatarThumbnail) { } @@ -19,7 +19,10 @@

{{ employee.name }}

{{ employee.position }}

} @if(properties.addCheckboxAction) { - + + } @if(properties.addIconAction) { info diff --git a/src/app/list/list.sample.ts b/src/app/list/list.sample.ts index 5d7edc82ca0..7e1ca2ae378 100644 --- a/src/app/list/list.sample.ts +++ b/src/app/list/list.sample.ts @@ -61,6 +61,7 @@ interface Employee { name: string; position: string; description: string; + selected?: boolean; } @Component({ From 3dc3c64346cf72974935494427b162ba991a54ac Mon Sep 17 00:00:00 2001 From: Simeon Simeonoff Date: Thu, 27 Feb 2025 16:33:59 +0200 Subject: [PATCH 2/3] Update projects/igniteui-angular/src/lib/list/list-item.component.ts Co-authored-by: Silvia Ivanova <59446295+SisIvanova@users.noreply.github.com> --- projects/igniteui-angular/src/lib/list/list-item.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/list/list-item.component.ts b/projects/igniteui-angular/src/lib/list/list-item.component.ts index 9b014df69ea..ab0bd4e7b07 100644 --- a/projects/igniteui-angular/src/lib/list/list-item.component.ts +++ b/projects/igniteui-angular/src/lib/list/list-item.component.ts @@ -19,7 +19,6 @@ import { import { HammerGesturesManager } from '../core/touch'; import { rem } from '../core/utils'; import { NgTemplateOutlet } from '@angular/common'; -import { Host } from 'igniteui-dockmanager/dist/types/stencil-public-runtime'; /** * The Ignite UI List Item component is a container intended for row items in the Ignite UI for Angular List component. From 320493e1c306e84d489e6bfdc6d72f2a50c3acfe Mon Sep 17 00:00:00 2001 From: Simeon Simeonoff Date: Fri, 28 Feb 2025 10:06:18 +0200 Subject: [PATCH 3/3] chore: update changelog to reflect new feature --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cafd6fa1621..f250cdc6e4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Ignite UI for Angular Change Log All notable changes for each version of this project will be documented in this file. +## 19.1.1 +### New Features +- IgxListItem + - Added a new `selected` input property, making it easier to indicate when a list item is selected by applying styling responsible for that state. + ## 19.1.0 ### General - `IgxCarousel`