Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 64 additions & 43 deletions projects/widgets/select/select.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,45 @@ import { ActiveDescendantKeyManager, LiveAnnouncer } from '@angular/cdk/a11y';
import { SelectionModel } from '@angular/cdk/collections';
import { hasModifierKey } from '@angular/cdk/keycodes';
import { CdkConnectedOverlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, computed, contentChild, contentChildren, effect, ElementRef, forwardRef, inject, input, isDevMode, OnDestroy, OnInit, signal, untracked, viewChild, ViewEncapsulation } from '@angular/core';
import {
AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
computed,
contentChild,
contentChildren,
effect,
ElementRef,
forwardRef,
inject,
input,
isDevMode,
OnDestroy,
OnInit,
signal,
untracked,
viewChild,
ViewEncapsulation,
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { ControlValueAccessor, NG_VALUE_ACCESSOR, ValueChangeEvent } from '@angular/forms';
import { coerceNumberAttribute, createClassList } from '@i-cell/ids-angular/core';
import { _countGroupLabelsBeforeOption, _getOptionScrollPosition, AbstractErrorStateMatcher, AbstractSuccessStateMatcher, formFieldControlClass, IDS_FORM_FIELD_CONTROL, IDS_OPTION_GROUP, IDS_OPTION_PARENT_COMPONENT, IdsFormFieldComponent, IdsFormFieldControl, IdsOptionComponent, IdsOptionGroupComponent, IdsOptionSelectionChange } from '@i-cell/ids-angular/forms';
import {
_countGroupLabelsBeforeOption,
_getOptionScrollPosition,
AbstractErrorStateMatcher,
AbstractSuccessStateMatcher,
formFieldControlClass,
IDS_FORM_FIELD_CONTROL,
IDS_OPTION_GROUP,
IDS_OPTION_PARENT_COMPONENT,
IdsFormFieldComponent,
IdsFormFieldControl,
IdsOptionComponent,
IdsOptionGroupComponent,
IdsOptionSelectionChange,
} from '@i-cell/ids-angular/forms';
import { IdsIconComponent } from '@i-cell/ids-angular/icon';
import { filter, first } from 'rxjs';

Expand Down Expand Up @@ -82,22 +116,27 @@ export class IdsSelectComponent
public valueCompareFn = input<(o1: unknown, o2: unknown) => boolean>((o1: unknown, o2: unknown) => o1 === o2);
public sortCompareFn = input<(a: IdsOptionComponent, b: IdsOptionComponent, options: Readonly<IdsOptionComponent[]>) => number>();
public tabIndex = input<number, unknown>(0, { transform: coerceNumberAttribute });
public typeaheadDebounceInterval = input<number, unknown>(
this._defaultConfig.typeaheadDebounceInterval, { transform: coerceNumberAttribute },
);
public typeaheadDebounceInterval = input<number, unknown>(this._defaultConfig.typeaheadDebounceInterval, {
transform: coerceNumberAttribute,
});

public isPanelOpen = signal<boolean>(false);
public parentSize = computed(() => this._parentFormField.parentOrSelfSize());
public parentVariant = computed(() => this._parentFormField.parentOrSelfVariant());
private _focused = signal<boolean>(false);

private _canOpen = computed(() => !this.isPanelOpen() && !this.disabled() && !this.readonly() && this.options().length > 0);
protected _hostClasses = computed(() => this._getHostClasses([
this.parentSize(),
this.parentVariant(),
this.disabled() ? 'disabled' : null,
this.readonly() ? 'readonly' : null,
], [formFieldControlClass]));
protected _hostClasses = computed(() =>
this._getHostClasses(
[
this.parentSize(),
this.parentVariant(),
this.disabled() ? 'disabled' : null,
this.readonly() ? 'readonly' : null,
],
[formFieldControlClass],
),
);

protected _panelClasses = computed(() => createClassList(`${this._hostClassName}-panel`, [
this.parentSize(),
Expand All @@ -106,6 +145,7 @@ export class IdsSelectComponent

private _panel = viewChild<ElementRef<HTMLElement>>('panel');
private _overlayDir = viewChild(CdkConnectedOverlay);

public options = contentChildren<IdsOptionComponent>(IdsOptionComponent, { descendants: true });
public optionGroups = contentChildren<IdsOptionGroupComponent>(IDS_OPTION_GROUP, { descendants: true });
protected _customTrigger = contentChild(IdsSelectTriggerDirective);
Expand All @@ -115,7 +155,7 @@ export class IdsSelectComponent
private _selectionModel?: SelectionModel<IdsOptionComponent>;
private readonly _subscribedOptions = new WeakSet<IdsOptionComponent>();
private _onChange: (value: unknown) => void = () => {};
private _onTouched: () => unknown = () => { };
private _onTouched: () => unknown = () => {};

protected readonly _empty = computed(() => {
if (this.options().some((option) => option.selected())) {
Expand Down Expand Up @@ -294,11 +334,7 @@ export class IdsSelectComponent
private _handleClosedPanelKeydown(event: KeyboardEvent): void {
const manager = this._keyManager;
const key = event.key;
const isArrowKey =
key === 'ArrowDown' ||
key === 'ArrowUp' ||
key === 'ArrowLeft' ||
key === 'ArrowRight';
const isArrowKey = key === 'ArrowDown' || key === 'ArrowUp' || key === 'ArrowLeft' || key === 'ArrowRight';
const isOpenKey = key === 'Enter' || key === ' ';

if ((!manager?.isTyping() && isOpenKey && !hasModifierKey(event)) || isArrowKey) {
Expand All @@ -325,12 +361,7 @@ export class IdsSelectComponent
if (isArrowKey && event.altKey) {
event.preventDefault();
this.close();
} else if (
!isTyping &&
(key === 'Enter' || key === ' ') &&
manager?.activeItem &&
!hasModifierKey(event)
) {
} else if (!isTyping && (key === 'Enter' || key === ' ') && manager?.activeItem && !hasModifierKey(event)) {
event.preventDefault();
manager.activeItem.selectViaInteraction();
} else if (!isTyping && this.multiSelect() && key === 'a' && event.ctrlKey) {
Expand Down Expand Up @@ -374,12 +405,7 @@ export class IdsSelectComponent
if (index === 0 && labelCount === 1) {
panel.scrollTop = 0;
} else {
panel.scrollTop = _getOptionScrollPosition(
element.offsetTop,
element.offsetHeight,
panel.scrollTop,
panel.offsetHeight,
);
panel.scrollTop = _getOptionScrollPosition(element.offsetTop, element.offsetHeight, panel.scrollTop, panel.offsetHeight);
}
}
}
Expand All @@ -389,27 +415,22 @@ export class IdsSelectComponent
const options = this.options();

const sortComparator = this.sortCompareFn();
this._selectionModel?.sort((a, b) => (sortComparator
? sortComparator(a, b, options)
: options.indexOf(a) - options.indexOf(b)));
this._selectionModel?.sort((a, b) => (sortComparator ? sortComparator(a, b, options) : options.indexOf(a) - options.indexOf(b)));
}
}

private _getOverlayWidth(
preferredOrigin: ElementRef<ElementRef> | CdkOverlayOrigin | undefined,
): string | number {
const refToMeasure
= preferredOrigin instanceof CdkOverlayOrigin
? preferredOrigin.elementRef
: preferredOrigin || this._elementRef;
private _getOverlayWidth(preferredOrigin: ElementRef<ElementRef> | CdkOverlayOrigin | undefined): string | number {
const refToMeasure = preferredOrigin instanceof CdkOverlayOrigin ? preferredOrigin.elementRef : preferredOrigin || this._elementRef;
return refToMeasure.nativeElement.getBoundingClientRect().width;
}

protected _panelAttached(): void {
this._overlayDir()?.positionChange.pipe(first()).subscribe(() => {
this._changeDetectorRef.detectChanges();
this._positioningSettled();
});
this._overlayDir()
?.positionChange.pipe(first())
.subscribe(() => {
this._changeDetectorRef.detectChanges();
this._positioningSettled();
});
}

public toggle(): void {
Expand Down
4 changes: 2 additions & 2 deletions projects/widgets/side-sheet/side-sheet.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
<ng-container *ngTemplateOutlet="customHeader"/>
}
</app-side-sheet-header>
<div class="ids-side-sheet__content" [ngClass]="{ 'ids-side-sheet__content--scrollable': isScrollable() }">
<div class="ids-side-sheet__content" cdkScrollable [ngClass]="{ 'ids-side-sheet__content--scrollable': isScrollable() }">
<ng-container *ngTemplateOutlet="content"/>
</div>
<div class="ids-side-sheet__footer">
Expand Down Expand Up @@ -105,7 +105,7 @@
<ng-container *ngTemplateOutlet="customHeader"/>
}
</app-side-sheet-header>
<div class="ids-side-sheet__content" [ngClass]="{ 'ids-side-sheet__content--scrollable': isScrollable() }">
<div class="ids-side-sheet__content" cdkScrollable [ngClass]="{ 'ids-side-sheet__content--scrollable': isScrollable() }">
<ng-container *ngTemplateOutlet="content"/>
</div>
<div class="ids-side-sheet__footer">
Expand Down
2 changes: 2 additions & 0 deletions projects/widgets/side-sheet/side-sheet.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {

import { trigger, transition, style, animate } from '@angular/animations';
import { A11yModule, CdkTrapFocus } from '@angular/cdk/a11y';
import { CdkScrollable } from '@angular/cdk/scrolling';
import { NgClass, NgTemplateOutlet } from '@angular/common';
import {
Component,
Expand Down Expand Up @@ -57,6 +58,7 @@ const defaultConfig = IDS_SIDE_SHEET_DEFAULT_CONFIG_FACTORY();
NgClass,
A11yModule,
CdkTrapFocus,
CdkScrollable,
],
})
export class IdsSideSheetComponent extends ComponentBaseWithDefaults<IdsSideSheetDefaultConfig> {
Expand Down
Loading