Skip to content

Commit

Permalink
feat(kit): Select add ability to use native select on mobile (#2964)
Browse files Browse the repository at this point in the history
  • Loading branch information
vladimirpotekhin authored Oct 31, 2022
1 parent 32c8736 commit c909bec
Show file tree
Hide file tree
Showing 16 changed files with 277 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
(tuiAutofilledChange)="onAutofilled($event)"
>
<ng-content select="input"></ng-content>
<ng-content select="select"></ng-content>
<input
#focusableElement
tuiMaskAccessor
Expand Down
27 changes: 27 additions & 0 deletions projects/demo/src/modules/components/select/examples/11/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<form class="b-form">
<tui-select [formControl]="itemControl">
Character
<select
tuiSelect
[items]="items"
></select>
</tui-select>

<tui-select
class="tui-space_top-4"
[formControl]="itemGroupControl"
[tuiTextfieldCleaner]="true"
>
Food
<select
tuiSelect
[items]="groupItems"
[labels]="labels"
></select>

<input
tuiTextfield
placeholder="Make a choice"
/>
</tui-select>
</form>
31 changes: 31 additions & 0 deletions projects/demo/src/modules/components/select/examples/11/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {Component} from '@angular/core';
import {FormControl} from '@angular/forms';
import {changeDetection} from '@demo/emulate/change-detection';
import {encapsulation} from '@demo/emulate/encapsulation';

@Component({
selector: `tui-select-example-11`,
templateUrl: `./index.html`,
changeDetection,
encapsulation,
})
export class TuiSelectExample11 {
itemControl = new FormControl();
itemGroupControl = new FormControl();

items = [
`Luke Skywalker`,
`Leia Organa Solo`,
`Darth Vader`,
`Han Solo`,
`Obi-Wan Kenobi`,
`Yoda`,
];

groupItems = [
[`Caesar`, `Greek`, `Apple and Chicken`],
[`Broccoli Cheddar`, `Chicken and Rice`, `Chicken Noodle`],
];

labels = [`Salad`, `Soup`];
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ export class ExampleTuiSelectComponent extends AbstractExampleTuiControl {
HTML: import(`./examples/10/index.html?raw`),
};

readonly example11: TuiDocExample = {
TypeScript: import(`./examples/11/index.ts?raw`),
HTML: import(`./examples/11/index.html?raw`),
};

readonly items = [new Account(`Ruble`, 500), new Account(`Dollar`, 237)];

readonly valueTemplateVariants = [``, `Template`];
Expand Down
2 changes: 2 additions & 0 deletions projects/demo/src/modules/components/select/select.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {TuiSelectExample8} from './examples/8';
import {TuiSelectExample9} from './examples/9';
import {ExampleMyAccountComponent} from './examples/9/account/my-account.component';
import {TuiSelectExample10} from './examples/10';
import {TuiSelectExample11} from './examples/11';
import {ExampleTuiSelectComponent} from './select.component';

@NgModule({
Expand Down Expand Up @@ -91,6 +92,7 @@ import {ExampleTuiSelectComponent} from './select.component';
TuiSelectExample8,
TuiSelectExample9,
TuiSelectExample10,
TuiSelectExample11,
],
exports: [ExampleTuiSelectComponent],
})
Expand Down
15 changes: 15 additions & 0 deletions projects/demo/src/modules/components/select/select.template.html
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,21 @@
>
<tui-select-example-10></tui-select-example-10>
</tui-doc-example>

<tui-doc-example
id="native-select"
heading="Native select"
[content]="example11"
>
<tui-notification class="tui-space_bottom-4 b-form">
You can enable native select on mobile devices by putting
<code>select</code>
inside with
<code>tuiSelect</code>
directive as shown below
</tui-notification>
<tui-select-example-11></tui-select-example-11>
</tui-doc-example>
</ng-template>

<ng-template pageTab>
Expand Down
3 changes: 3 additions & 0 deletions projects/kit/components/select/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export * from './native-select/native-select';
export * from './native-select/native-select.component';
export * from './native-select/native-select-group.component';
export * from './select.component';
export * from './select.directive';
export * from './select.module';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {ChangeDetectionStrategy, Component, Input, TemplateRef} from '@angular/core';
import {TuiDataListDirective} from '@taiga-ui/core';

import {AbstractTuiNativeSelect} from './native-select';

@Component({
selector: `select[tuiSelect][labels]`,
templateUrl: `./native-select-group.template.html`,
providers: [
{
provide: TuiDataListDirective,
deps: [TuiNativeSelectGroupComponent],
useExisting: TuiNativeSelectGroupComponent,
},
{
provide: TemplateRef,
deps: [TuiNativeSelectGroupComponent],
useFactory: ({datalist}: TuiNativeSelectGroupComponent) => datalist,
},
{
provide: AbstractTuiNativeSelect,
useExisting: TuiNativeSelectGroupComponent,
},
],
host: {
'[attr.aria-invalid]': `host.invalid`,
'[disabled]': `host.disabled`,
'[tabIndex]': `host.focusable ? 0 : -1`,
'[readOnly]': `host.readOnly`,
'[value]': `host.value`,
'(change)': `host.onValueChange($event.target.value)`,
},
styleUrls: [`./native-select.style.less`],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TuiNativeSelectGroupComponent extends AbstractTuiNativeSelect {
@Input()
items: readonly string[][] | null = [];

@Input()
labels: readonly string[] = [];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<tui-data-list-wrapper
*tuiDataList
[items]="items"
[labels]="labels"
></tui-data-list-wrapper>
<optgroup
*ngFor="let group of items; let index = index"
[label]="labels[index]"
>
<option
*ngFor="let option of group"
[value]="option"
>
{{ option }}
</option>
</optgroup>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {ChangeDetectionStrategy, Component, Input, TemplateRef} from '@angular/core';
import {TuiDataListDirective} from '@taiga-ui/core';

import {AbstractTuiNativeSelect} from './native-select';

@Component({
selector: `select[tuiSelect]:not([labels])`,
templateUrl: `./native-select.template.html`,
providers: [
{
provide: TuiDataListDirective,
deps: [TuiNativeSelectComponent],
useExisting: TuiNativeSelectComponent,
},
{
provide: TemplateRef,
deps: [TuiNativeSelectComponent],
useFactory: ({datalist}: TuiNativeSelectComponent) => datalist,
},
{
provide: AbstractTuiNativeSelect,
useExisting: TuiNativeSelectComponent,
},
],
host: {
'[attr.aria-invalid]': `host.invalid`,
'[disabled]': `host.disabled`,
'[tabIndex]': `host.focusable ? 0 : -1`,
'[readOnly]': `host.readOnly`,
'[value]': `host.value`,
'(change)': `host.onValueChange($event.target.value)`,
},
styleUrls: [`./native-select.style.less`],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TuiNativeSelectComponent extends AbstractTuiNativeSelect {
@Input()
items: readonly string[] | null = [];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@import 'taiga-ui-local';

:host {
.fullsize();
opacity: 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<tui-data-list-wrapper
*tuiDataList
[items]="items"
></tui-data-list-wrapper>
<option
*ngFor="let option of items"
[value]="option"
>
{{ option }}
</option>
30 changes: 30 additions & 0 deletions projects/kit/components/select/native-select/native-select.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {
Directive,
ElementRef,
HostBinding,
Inject,
TemplateRef,
ViewChild,
} from '@angular/core';
import {TuiIdService} from '@taiga-ui/cdk';
import {TUI_TEXTFIELD_HOST, TuiDataListDirective, TuiTextfieldHost} from '@taiga-ui/core';

@Directive()
export abstract class AbstractTuiNativeSelect {
@ViewChild(TuiDataListDirective, {read: TemplateRef, static: true})
readonly datalist: TemplateRef<any> | null = null;

constructor(
@Inject(TUI_TEXTFIELD_HOST) readonly host: TuiTextfieldHost,
@Inject(ElementRef) private readonly elementRef: ElementRef<HTMLInputElement>,
@Inject(TuiIdService)
private readonly idService: TuiIdService,
) {
this.host.process(this.elementRef.nativeElement);
}

@HostBinding(`id`)
get id(): string {
return this.elementRef.nativeElement.id || this.idService.generate();
}
}
15 changes: 14 additions & 1 deletion projects/kit/components/select/select.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import {NgControl} from '@angular/forms';
import {
AbstractTuiNullableControl,
TUI_IS_MOBILE,
TuiActiveZoneDirective,
tuiAsControl,
tuiAsFocusableItemAccessor,
Expand Down Expand Up @@ -41,6 +42,7 @@ import {FIXED_DROPDOWN_CONTROLLER_PROVIDER} from '@taiga-ui/kit/providers';
import {TUI_ITEMS_HANDLERS, TuiItemsHandlers} from '@taiga-ui/kit/tokens';
import {PolymorpheusContent} from '@tinkoff/ng-polymorpheus';

import {AbstractTuiNativeSelect} from './native-select/native-select';
import {TUI_SELECT_OPTIONS, TuiSelectOptions} from './select-options';

@Component({
Expand All @@ -66,6 +68,9 @@ export class TuiSelectComponent<T>
@ViewChild(TuiHostedDropdownComponent)
private readonly hostedDropdown?: TuiHostedDropdownComponent;

@ContentChild(AbstractTuiNativeSelect, {static: true})
private readonly nativeSelect?: AbstractTuiNativeSelect;

@Input()
@tuiDefaultProp()
stringify: TuiItemsHandlers<T>['stringify'] = this.itemsHandlers.stringify;
Expand Down Expand Up @@ -98,6 +103,8 @@ export class TuiSelectComponent<T>
private readonly itemsHandlers: TuiItemsHandlers<T>,
@Inject(TUI_SELECT_OPTIONS)
private readonly options: TuiSelectOptions<T>,
@Inject(TUI_IS_MOBILE)
readonly isMobile: boolean,
) {
super(control, changeDetectorRef);
}
Expand All @@ -119,6 +126,10 @@ export class TuiSelectComponent<T>
);
}

get nativeDropdownMode(): boolean {
return !!this.nativeSelect && this.isMobile;
}

get computedValue(): string {
return this.value === null ? `` : this.stringify(this.value) || ` `;
}
Expand All @@ -127,9 +138,11 @@ export class TuiSelectComponent<T>
return this.valueContent || this.computedValue;
}

onValueChange(value: string): void {
onValueChange(value: T): void {
if (!value) {
this.updateValue(null);
} else {
this.updateValue(value || null);
}
}

Expand Down
21 changes: 19 additions & 2 deletions projects/kit/components/select/select.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
import {TuiActiveZoneModule} from '@taiga-ui/cdk';
import {
TuiDataListModule,
TuiHostedDropdownModule,
TuiPrimitiveTextfieldModule,
TuiTextfieldComponent,
TuiTextfieldControllerModule,
} from '@taiga-ui/core';
import {TuiArrowModule} from '@taiga-ui/kit/components/arrow';
import {TuiDataListWrapperModule} from '@taiga-ui/kit/components/data-list-wrapper';
import {TuiSelectOptionModule} from '@taiga-ui/kit/components/select-option';
import {PolymorpheusModule} from '@tinkoff/ng-polymorpheus';

import {TuiNativeSelectComponent} from './native-select/native-select.component';
import {TuiNativeSelectGroupComponent} from './native-select/native-select-group.component';
import {TuiSelectComponent} from './select.component';
import {TuiSelectDirective} from './select.directive';

Expand All @@ -24,8 +28,21 @@ import {TuiSelectDirective} from './select.directive';
TuiSelectOptionModule,
TuiArrowModule,
TuiTextfieldControllerModule,
TuiDataListWrapperModule,
TuiDataListModule,
],
declarations: [
TuiSelectComponent,
TuiSelectDirective,
TuiNativeSelectComponent,
TuiNativeSelectGroupComponent,
],
exports: [
TuiSelectComponent,
TuiSelectDirective,
TuiTextfieldComponent,
TuiNativeSelectComponent,
TuiNativeSelectGroupComponent,
],
declarations: [TuiSelectComponent, TuiSelectDirective],
exports: [TuiSelectComponent, TuiSelectDirective, TuiTextfieldComponent],
})
export class TuiSelectModule {}
Loading

0 comments on commit c909bec

Please sign in to comment.