MOBILE-3947 combobox: Improve combobox

main
Pau Ferrer Ocaña 2024-01-11 12:56:17 +01:00
parent 0975669042
commit ab16380626
8 changed files with 146 additions and 150 deletions

View File

@ -71,7 +71,7 @@
</ion-col>
<ion-col size="auto" *ngIf="sort.enabled">
<core-combobox [label]="'core.sortby' | translate" [selection]="sort.selected" (onChange)="sortCourses($event)"
icon="fas-arrow-down-short-wide">
icon="fas-arrow-down-short-wide" class="no-border">
<ion-select-option class="ion-text-wrap" value="fullname">
{{'addon.block_myoverview.title' | translate}}
</ion-select-option>

View File

@ -10,24 +10,19 @@
}
ion-button,
core-combobox ::ng-deep ion-button {
--border-width: 0;
core-combobox ::ng-deep ion-select {
--a11y-min-target-size: 40px;
margin: 0;
}
ion-button {
--border-width: 0;
.select-icon {
display: none;
}
ion-icon {
font-size: 20px;
}
}
core-combobox ::ng-deep ion-select {
margin: 0;
--a11y-min-target-size: 40px;
}
ion-searchbar {
padding: 0;
--height: 40px;

View File

@ -36,7 +36,7 @@
</ion-col>
<ion-col size="auto">
<core-combobox [label]="'core.sortby' | translate" [formControl]="sort" (onChange)="sortChanged($event)"
icon="fas-arrow-down-short-wide">
icon="fas-arrow-down-short-wide" class="no-border">
<ion-select-option *ngFor="let option of sortOptions" class="ion-text-wrap" [value]="option.value">
{{ option.name | translate }}
</ion-select-option>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -5,30 +5,94 @@
display: block;
@include margin-horizontal(var(--ion-safe-area-left), var(--ion-safe-area-right));
&.no-border {
--core-combobox-border-width: 0px;
ion-select.combobox-icon-only {
--padding-start: 8px;
&::part(icon) {
display: none;
}
&::part(label) {
position: static;
}
ion-icon {
margin: var(--icon-margin);
font-size: 20px;
}
}
}
ion-select,
ion-button {
--icon-margin: 0 4px;
--background: var(--core-combobox-background);
--background-hover: black;
--background-activated: black;
--background-focused: black;
--background-hover-opacity: .04;
--background: var(--core-combobox-background);
--border-color: var(--core-combobox-border-color);
--border-style: solid;
--border-width: var(--core-combobox-border-width);
--border-radius: var(--core-combobox-radius);
--box-shadow: var(--core-combobox-box-shadow);
--padding-start: 16px;
--padding-end: 8px;
--padding-top: 8px;
--padding-bottom: 8px;
background: var(--background);
color: var(--color);
text-overflow: ellipsis;
white-space: nowrap;
min-height: var(--a11y-min-target-size);
overflow: hidden;
box-shadow: var(--box-shadow);
&:focus,
&:focus-within {
@include core-focus-style();
}
}
ion-select {
border-color: var(--border-color);
border-style: var(--border-style);
border-width: var(--border-width);
border-radius: var(--core-combobox-radius);
margin: 8px;
&.combobox-icon-only {
&::part(text) {
display: none;
}
}
&::part(label) {
position: absolute;
margin-inline: 0px;
}
&::part(icon) {
margin: var(--icon-margin);
opacity: 1;
}
}
ion-button {
--color: var(--core-combobox-color);
--color-activated: var(--core-combobox-color);
--color-focused: currentcolor;
--color-hover: currentcolor;
--border-color: var(--core-combobox-border-color);
--border-style: solid;
--border-width: var(--core-combobox-border-width);
--border-radius: var(--radius-xs);
--box-shadow: var(--core-combobox-box-shadow);
--padding-top: 8px;
--padding-end: 8px;
--padding-bottom: 8px;
--background-hover: black;
--background-activated: black;
--background-focused: black;
--background-hover-opacity: .04;
&.md {
--background-activated-opacity: 0;
@ -39,97 +103,36 @@
--background-activated-opacity: .12;
--background-focused-opacity: .15;
}
}
ion-button {
--padding-start: 8px;
}
ion-select {
--padding-start: 16px;
}
}
ion-select,
ion-button {
background: var(--background);
color: var(--color);
text-overflow: ellipsis;
white-space: nowrap;
min-height: var(--a11y-min-target-size);
overflow: hidden;
box-shadow: var(--box-shadow);
&:focus,
&:focus-within {
@include core-focus-style();
}
}
ion-select {
border-color: var(--border-color);
border-style: var(--border-style);
border-width: var(--border-width);
border-radius: var(--core-combobox-radius);
margin: 8px;
&::part(icon) {
margin: var(--icon-margin);
opacity: 1;
}
&::after {
@include button-state();
transition: var(--transition);
z-index: -1;
}
&:hover::after {
color: var(--color-hover);
background: var(--background-hover);
opacity: var(--background-hover-opacity);
}
&:focus::after,
&:focus-within::after {
color: var(--color-focused);
background: var(--background-focused);
opacity: var(--background-focused-opacity);
}
&[hidden] {
display: none !important;
}
}
ion-button {
border-radius: var(--core-combobox-radius);
margin: 4px 8px;
flex: 1;
&::part(native) {
text-transform: none;
font-weight: 400;
font-size: 16px;
line-height: 20px;
border-radius: var(--core-combobox-radius);
margin: 4px 8px;
flex: 1;
&::part(native) {
text-transform: none;
font-weight: 400;
font-size: 16px;
line-height: 20px;
border-radius: var(--core-combobox-radius);
}
.select-text {
@include margin-horizontal(null, auto);
overflow: hidden;
text-overflow: ellipsis;
}
.sr-only {
@include sr-only();
}
&.ion-activated {
--color: var(--color-activated);
}
ion-icon {
margin: var(--icon-margin);
}
}
.select-text {
@include margin-horizontal(null, auto);
overflow: hidden;
text-overflow: ellipsis;
}
.sr-only {
@include sr-only();
}
&.ion-activated {
--color: var(--color-activated);
}
ion-icon {
margin: var(--icon-margin);
}
}

View File

@ -12,11 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Translate } from '@singletons';
import { ModalOptions } from '@ionic/core';
import { CoreDomUtils } from '@services/utils/dom';
import { IonSelect } from '@ionic/angular';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
/**
@ -51,8 +50,6 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
})
export class CoreComboboxComponent implements ControlValueAccessor {
@ViewChild(IonSelect) select?: IonSelect;
@Input() interface: 'popover' | 'modal' = 'popover';
@Input() label = Translate.instant('core.show'); // Aria label.
@Input() disabled = false;
@ -112,30 +109,25 @@ export class CoreComboboxComponent implements ControlValueAccessor {
/**
* Shows combobox modal.
*
* @param event Event.
* @returns Promise resolved when done.
*/
async openSelect(event?: UIEvent): Promise<void> {
async openModal(): Promise<void> {
this.touch();
if (this.interface === 'modal') {
if (this.expanded || !this.modalOptions) {
return;
}
this.expanded = true;
if (this.expanded || !this.modalOptions) {
return;
}
this.expanded = true;
if (this.listboxId) {
this.modalOptions.id = this.listboxId;
}
if (this.listboxId) {
this.modalOptions.id = this.listboxId;
}
const data = await CoreDomUtils.openModal(this.modalOptions);
this.expanded = false;
const data = await CoreDomUtils.openModal(this.modalOptions);
this.expanded = false;
if (data) {
this.onValueChanged(data);
}
} else if (this.select) {
this.select.open(event);
if (data) {
this.onValueChanged(data);
}
}

View File

@ -1,18 +1,17 @@
<ion-button (click)="openSelect($event)" *ngIf="interface !== 'modal' && icon" [disabled]="disabled">
<ion-icon [name]="icon" [attr.aria-label]="label" slot="start" />
<div class="select-icon" role="presentation" aria-hidden="true">
<div class="select-icon-inner"></div>
</div>
</ion-button>
<ion-select *ngIf="interface !== 'modal'" class="ion-text-start" [(ngModel)]="selection" (ngModelChange)="onValueChanged(selection)"
[interface]="interface" [attr.aria-label]="label" [disabled]="disabled" [hidden]="!!icon">
[interface]="interface" [disabled]="disabled" [class.combobox-icon-only]="icon" [interfaceOptions]="{alignment: 'start', arrow: false}">
<div slot="label">
<span class="sr-only" *ngIf="label">{{ label }}</span>
<ion-icon *ngIf="icon" [name]="icon" aria-hidden="true" />
</div>
<ng-content></ng-content>
</ion-select>
<ion-button *ngIf="interface === 'modal'" aria-haspopup="listbox" [attr.aria-controls]="listboxId" [attr.aria-owns]="listboxId"
[attr.aria-expanded]="expanded" (click)="openSelect()" [disabled]="disabled" expand="block" role="combobox">
[attr.aria-expanded]="expanded" (click)="openModal()" [disabled]="disabled" expand="block" role="combobox">
<ion-icon *ngIf="icon" [name]="icon" slot="start" aria-hidden="true" />
<span class="sr-only" *ngIf="label">{{ label }}:</span>
<span class="sr-only" *ngIf="label">{{ label }},</span>
<div class="select-text">
<slot name="text">{{selection}}</slot>
</div>

View File

@ -1027,7 +1027,6 @@ input[type=radio],
--border-width: 2px;
--outer-border-width: 2px;
--border-style: solid;
--inner-border-radius: 50%;
--size: 20px;
&:not(.ion-color) {
@ -1045,10 +1044,10 @@ input[type=radio],
}
.ios ion-radio {
width: var(--size);
height: var(--size);
&::part(container) {
width: var(--size);
height: var(--size);
margin: 0px;
border-radius: var(--border-radius);
border-width: var(--outer-border-width);
@ -1155,6 +1154,13 @@ ion-select {
}
ion-select-popover {
ion-list ion-radio-group ion-item.select-interface-option ion-radio.hydrated::part(container) {
opacity: 1;
}
ion-item {
font-size: 14px;
}
ion-item.core-select-option-border-bottom {
border-bottom: 1px solid var(--stroke);
}
@ -1645,6 +1651,7 @@ ion-item.item {
// Change default outline.
:focus-visible {
@include core-focus-style();
border-radius: inherit;
outline: none;
}