MOBILE-3947 combobox: Improve combobox
parent
0975669042
commit
ab16380626
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 |
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue