diff --git a/libs/feature/dataviz/src/lib/chart-view/chart-view.component.ts b/libs/feature/dataviz/src/lib/chart-view/chart-view.component.ts index c35fe4995f..2deda3803b 100644 --- a/libs/feature/dataviz/src/lib/chart-view/chart-view.component.ts +++ b/libs/feature/dataviz/src/lib/chart-view/chart-view.component.ts @@ -12,7 +12,7 @@ import { FieldAggregation, getJsonDataItemsProxy, } from '@geonetwork-ui/data-fetcher' -import { DDChoices } from '@geonetwork-ui/ui/inputs' +import { DropdownChoice } from '@geonetwork-ui/ui/inputs' import { BehaviorSubject, combineLatest, EMPTY, Observable } from 'rxjs' import { catchError, @@ -24,10 +24,7 @@ import { tap, } from 'rxjs/operators' import { DataService } from '../service/data.service' -import { - AggregationTypes, - InputChartType, -} from '@geonetwork-ui/common/domain/dataviz-configuration.model' +import { InputChartType } from '@geonetwork-ui/common/domain/dataviz-configuration.model' import { DatasetDistribution } from '@geonetwork-ui/common/domain/record' import { TranslateService } from '@ngx-translate/core' @@ -93,7 +90,7 @@ export class ChartViewComponent { error = null errorInfo = null - typeChoices: DDChoices = [ + typeChoices: DropdownChoice[] = [ { label: 'chart.type.bar', value: 'bar' }, { label: 'chart.type.barHorizontal', value: 'bar-horizontal' }, { label: 'chart.type.line', value: 'line' }, @@ -111,7 +108,7 @@ export class ChartViewComponent { { label: 'chart.aggregation.min', value: 'min' }, { label: 'chart.aggregation.average', value: 'average' }, { label: 'chart.aggregation.count', value: 'count' }, - ] as DDChoices + ] as DropdownChoice[] } dataset$: Observable<BaseReader> = this.currentLink$.pipe( diff --git a/libs/ui/inputs/src/index.ts b/libs/ui/inputs/src/index.ts index cd80f3653e..3328a2da52 100644 --- a/libs/ui/inputs/src/index.ts +++ b/libs/ui/inputs/src/index.ts @@ -1,4 +1,6 @@ export * from './lib/dropdown-selector/dropdown-selector.component' +export * from './lib/dropdown-selector/dropdown-selector.model' +export * from './lib/dropdown-multiselect/dropdown-multiselect.component' export * from './lib/dropdown-multiselect/dropdown-multiselect.model' export * from './lib/text-input/text-input.component' export * from './lib/chips-input/chips-input.component' diff --git a/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.stories.ts b/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.stories.ts index ff51631608..8064b038e6 100644 --- a/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.stories.ts +++ b/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.stories.ts @@ -9,13 +9,14 @@ import { OverlayModule } from '@angular/cdk/overlay' import { MatCheckboxModule } from '@angular/material/checkbox' import { TranslateModule } from '@ngx-translate/core' import { MatIcon } from '@angular/material/icon' +import { ButtonComponent } from '../button/button.component' export default { title: 'Inputs/DropdownMultiselectComponent', component: DropdownMultiselectComponent, decorators: [ moduleMetadata({ - declarations: [MatIcon], + declarations: [MatIcon, ButtonComponent], imports: [OverlayModule, MatCheckboxModule, TranslateModule.forRoot()], }), componentWrapperDecorator( diff --git a/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.html b/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.html index fd6374aaf7..9af90ae192 100644 --- a/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.html +++ b/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.html @@ -11,8 +11,9 @@ <gn-ui-button type="outline" class="grow min-w-0" - style="min-width: {{ minWidth }}" - extraClass="w-full !p-[8px] !pl-[16px] {{ extraBtnClass }}" + extraClass="!p-[8px] !pl-[16px] flex flex-row w-auto max-w-full {{ + extraBtnClass + }}" [title]="title" [attr.aria-owns]="id" (buttonClick)="openOverlay()" @@ -20,12 +21,10 @@ #overlayOrigin="cdkOverlayOrigin" (keydown)="handleTriggerKeydown($event)" > - <div class="grow flex items-center mr-2 gap-2 overflow-hidden"> - <div class="text-left font-medium truncate py-1"> - {{ getChoiceLabel() | translate }} - </div> + <div class="grow font-medium truncate mr-2"> + {{ getChoiceLabel() | translate }} </div> - <mat-icon class="shrink-0 opacity-40"> + <mat-icon class="material-symbols-outlined shrink-0 opacity-40"> <ng-container *ngIf="overlayOpen">expand_less</ng-container> <ng-container *ngIf="!overlayOpen">expand_more</ng-container> </mat-icon> @@ -46,7 +45,7 @@ <div class="bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container" [style.max-height]="overlayMaxHeight" - [style.width]="overlayWidth" + [style.min-width]="overlayWidth" role="listbox" tabindex="-1" [attr.id]="id" @@ -54,22 +53,23 @@ [attr.aria-label]="title" (keydown)="handleOverlayKeydown($event)" > - <label + <button + #choiceInputs + type="button" *ngFor="let choice of choices" [title]="choice.label" - class="flex px-5 py-1 w-full text-gray-900 cursor-pointer hover:text-primary-darkest hover:bg-gray-50 focus-within:text-primary-darkest focus-within:bg-gray-50 transition-colors" + class="flex px-5 py-1 w-full cursor-pointer transition-colors" + [ngClass]=" + isSelected(choice) + ? 'text-white bg-primary hover:text-white hover:bg-primary-darker focus:text-white focus:bg-primary-darker' + : 'text-gray-900 hover:text-primary-darkest hover:bg-gray-50 focus:text-primary-darkest focus:bg-gray-50' + " + (click)="onSelectValue(choice)" + (keydown)="selectIfEnter($event, choice)" > - <input - #choiceInputs - class="w-[0px] h-[18px] align-text-top shrink-0" - type="text" - [checked]="isSelected(choice)" - (click)="onSelectValue(choice)" - (keydown)="selectIfEnter($event, choice)" - /> <span class="text-[14px]"> {{ choice.label | translate }} </span> - </label> + </button> </div> </ng-template> diff --git a/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.stories.ts b/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.stories.ts index 4175ff67b2..5f4f3bbcea 100644 --- a/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.stories.ts +++ b/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.stories.ts @@ -1,5 +1,6 @@ import { applicationConfig, + componentWrapperDecorator, Meta, moduleMetadata, StoryObj, @@ -11,24 +12,27 @@ import { TRANSLATE_DEFAULT_CONFIG, UtilI18nModule, } from '@geonetwork-ui/util/i18n' -import { MatCheckboxModule } from '@angular/material/checkbox' +import { MatIcon } from '@angular/material/icon' +import { ButtonComponent } from '../button/button.component' +import { importProvidersFrom } from '@angular/core' export default { title: 'Inputs/DropdownSelectorComponent', component: DropdownSelectorComponent, decorators: [ moduleMetadata({ - declarations: [], - imports: [ - UtilI18nModule, - OverlayModule, - MatCheckboxModule, - TranslateModule.forRoot(TRANSLATE_DEFAULT_CONFIG), - ], + declarations: [MatIcon, ButtonComponent], + imports: [UtilI18nModule, OverlayModule, TranslateModule], }), applicationConfig({ - providers: [], + providers: [ + importProvidersFrom(TranslateModule.forRoot(TRANSLATE_DEFAULT_CONFIG)), + ], }), + componentWrapperDecorator( + (story) => + `<div class="border border-gray-300 h-[250px] w-[600px] p-[10px]" style="resize: both; overflow: auto">${story}</div>` + ), ], } as Meta<DropdownSelectorComponent> @@ -42,11 +46,11 @@ export const Primary: StoryObj<DropdownSelectorComponent> = { value: 'choice1', }, { - label: 'My Choice 2', + label: 'My Choice 2, second choice', value: 'choice2', }, { - label: 'My Choice 3', + label: 'My Choice 3, very very very very very very long text', value: 'choice3', }, ], diff --git a/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.ts b/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.ts index 3a9555532f..28364f421a 100644 --- a/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.ts +++ b/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.ts @@ -15,13 +15,8 @@ import { ViewChild, ViewChildren, } from '@angular/core' -import { Choice } from '../dropdown-multiselect/dropdown-multiselect.model' -import { take } from 'rxjs/operators' - -export type DDChoices = Array<{ - label: string - value: string -}> +import { firstValueFrom } from 'rxjs' +import { DropdownChoice } from './dropdown-selector.model' const DEFAULT_ROW_NUMBERS = 6 @@ -35,12 +30,12 @@ export class DropdownSelectorComponent implements OnInit { @Input() title: string @Input() showTitle = true @Input() ariaName: string - @Input() choices: DDChoices - @Input() selected: any + @Input() choices: Array<DropdownChoice> + @Input() selected: DropdownChoice['value'] @Input() maxRows: number @Input() extraBtnClass = '' @Input() minWidth = '' - @Output() selectValue = new EventEmitter<any>() + @Output() selectValue = new EventEmitter<DropdownChoice['value']>() @ViewChild('overlayOrigin') overlayOrigin: CdkOverlayOrigin @ViewChild(CdkConnectedOverlay) overlay: CdkConnectedOverlay overlayOpen = false @@ -65,7 +60,7 @@ export class DropdownSelectorComponent implements OnInit { @ViewChildren('choiceInputs', { read: ElementRef }) choiceInputs: QueryList<ElementRef> - get selectedChoice(): Choice { + get selectedChoice(): DropdownChoice { return ( this.choices.find((choice) => choice.value === this.selected) ?? this.choices[0] @@ -87,11 +82,11 @@ export class DropdownSelectorComponent implements OnInit { } } - isSelected(choice) { + isSelected(choice: DropdownChoice) { return choice === this.selectedChoice } - onSelectValue(choice: Choice): void { + onSelectValue(choice: DropdownChoice) { this.closeOverlay() this.selected = choice.value this.selectValue.emit(this.selected) @@ -106,8 +101,8 @@ export class DropdownSelectorComponent implements OnInit { : 'none' this.overlayOpen = true return Promise.all([ - this.overlay.attach.pipe(take(1)).toPromise(), - this.choiceInputs.changes.pipe(take(1)).toPromise(), + firstValueFrom(this.overlay.attach), + firstValueFrom(this.choiceInputs.changes), ]) } @@ -177,7 +172,7 @@ export class DropdownSelectorComponent implements OnInit { ) } - selectIfEnter(event: KeyboardEvent, choice: Choice) { + selectIfEnter(event: KeyboardEvent, choice: DropdownChoice) { if (event.code === 'Enter') { event.preventDefault() this.onSelectValue(choice) diff --git a/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.model.ts b/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.model.ts new file mode 100644 index 0000000000..1eb2cb10b8 --- /dev/null +++ b/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.model.ts @@ -0,0 +1,5 @@ +// FIXME: this should support more than string values, and match the multiselect choice model +export interface DropdownChoice { + value: unknown + label: string +}