Skip to content

Commit

Permalink
feat: select all AIT-8411
Browse files Browse the repository at this point in the history
  • Loading branch information
Lihua Tang committed Sep 14, 2021
1 parent f1fe9c7 commit f202fa4
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 16 deletions.
1 change: 1 addition & 0 deletions src/i18n/language/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ export const en: I18NInterface = {
saturday: 'Sat',
sunday: 'Sun',
now: 'Now',
select_all: 'Select All',
},
};
1 change: 1 addition & 0 deletions src/i18n/language/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ export const zh: I18NInterface = {
saturday: '六',
sunday: '日',
now: '此刻',
select_all: '全选',
},
};
11 changes: 10 additions & 1 deletion src/select/multi-select/multi-select.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
[border]="true"
[size]="tagSize"
[closeable]="!disabled"
(close)="removeValue(option.value)"
(close)="removeTag(option.value)"
[ngClass]="tagClassFn && tagClassFn(option.label, option.value)"
>
<ng-container
Expand Down Expand Up @@ -78,6 +78,15 @@
>
{{ filterString }}
</aui-option>
<aui-option
#selectAllOption
*ngIf="allowSelectAll && !allowCreate"
[isSelectAllOption]="true"
[iconChecked]="selectAllIconChecked"
[indeterminate]="selectAllIndeterminate"
>
{{ 'select_all' | auiI18n }}
</aui-option>
<ng-container *ngIf="allowCreate">
<aui-option
*ngFor="let option of customCreatedOptions$ | async"
Expand Down
78 changes: 73 additions & 5 deletions src/select/multi-select/multi-select.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {

import { createWithMaxRowCount } from '../../input/tags-input/with-max-row-count';
import { ComponentSize } from '../../types';
import { Bem, buildBem, coerceString } from '../../utils';
import { Bem, buildBem, coerceAttrBoolean, coerceString } from '../../utils';
import { BaseSelect } from '../base-select';
import { OptionComponent } from '../option/option.component';
import {
Expand Down Expand Up @@ -63,6 +63,7 @@ export class MultiSelectComponent<T = SelectPrimitiveValue>
bem: Bem = buildBem('aui-multi-select');
selectedOptions$: Observable<Array<SelectFilterOption<T>>>;

private _allowSelectAll = false;
selectedValues: T[] = [];
values$ = this.value$$.asObservable();

Expand All @@ -75,12 +76,24 @@ export class MultiSelectComponent<T = SelectPrimitiveValue>
@Input()
customRowHeight = 0; // 0: use default style const value, > 1: for ```tagClassFn``` maybe affect lineHeight

@Input()
get allowSelectAll() {
return this._allowSelectAll;
}

set allowSelectAll(val: boolean | '') {
this._allowSelectAll = coerceAttrBoolean(val);
}

@ViewChild('inputRef', { static: true })
inputRef: ElementRef<HTMLInputElement>;

@ViewChild('inputValueMirror', { static: true })
inputValueMirror: ElementRef<HTMLElement>;

@ViewChild('selectAllOption', { static: false })
selectAllOption: OptionComponent<T>;

@HostBinding('style.position')
get hostPosition() {
return this.withMaxRowCount.hostPosition();
Expand All @@ -92,6 +105,8 @@ export class MultiSelectComponent<T = SelectPrimitiveValue>
}

inputValue = '';
selectAllIndeterminate = false;
selectAllIconChecked = false;

get rootClass() {
const size = this.size || ComponentSize.Medium;
Expand Down Expand Up @@ -190,6 +205,7 @@ export class MultiSelectComponent<T = SelectPrimitiveValue>
onShowOptions() {
super.onShowOptions();
this.inputRef.nativeElement.focus();
this.calcSelectAllStatus();
}

onHideOptions() {
Expand All @@ -202,6 +218,7 @@ export class MultiSelectComponent<T = SelectPrimitiveValue>
super.onInput(event);
this.setInputWidth();
this.tooltipRef.updatePosition();
this.calcSelectAllStatus();
}

onInputFocus() {
Expand Down Expand Up @@ -236,6 +253,7 @@ export class MultiSelectComponent<T = SelectPrimitiveValue>
} else {
super.onKeyDown(event);
}
this.calcSelectAllStatus();
}

writeValue(val: T[]) {
Expand All @@ -247,17 +265,48 @@ export class MultiSelectComponent<T = SelectPrimitiveValue>
}

selectOption(option: OptionComponent<T>) {
if (option.selected) {
this.removeValue(option.value);
} else {
this.addValue(option.value);
if (this.allowSelectAll && !this.allowCreate) {
const visibleOptions = this.contentOptions.filter(
({ visible }) => visible,
);
const checkedOptions = visibleOptions.filter(({ selected }) => selected);
if (option === this.selectAllOption) {
if (checkedOptions.length === visibleOptions.length) {
this.emitValueChange(
this.contentOptions
.filter(
({ selected, value }) =>
selected &&
!visibleOptions.map(({ value }) => value).includes(value),
)
.map(({ value }) => value),
);
} else {
this.emitValueChange([
...new Set([
...this.contentOptions
.filter(({ selected }) => selected)
.map(({ value }) => value),
...visibleOptions.map(({ value }) => value),
]),
]);
}
}
}
if (option !== this.selectAllOption) {
if (option.selected) {
this.removeValue(option.value);
} else {
this.addValue(option.value);
}
}
const isCustom = !this.contentOptions.some(
({ value }) => value === option.value,
);
if (isCustom) {
this.resetFocusedOption();
}
this.calcSelectAllStatus();
}

addValue(value: T) {
Expand Down Expand Up @@ -288,6 +337,25 @@ export class MultiSelectComponent<T = SelectPrimitiveValue>
this.emitValueChange([]);
event.stopPropagation();
event.preventDefault();
this.calcSelectAllStatus();
}

removeTag(value: T) {
this.removeValue(value);
this.calcSelectAllStatus();
}

private calcSelectAllStatus() {
if (this.allowSelectAll && !this.allowCreate) {
const visibleOptions = this.contentOptions.filter(
({ visible }) => visible,
);
const checkedOptions = visibleOptions.filter(({ selected }) => selected);
this.selectAllIndeterminate =
checkedOptions.length &&
checkedOptions.length !== visibleOptions.length;
this.selectAllIconChecked = !!checkedOptions.length;
}
}

private resetInput() {
Expand Down
15 changes: 13 additions & 2 deletions src/select/option/option.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,24 @@
[hidden]="!(visible$ | async)"
[class]="bem.block(size$ | async)"
[class.isDisabled]="disabled"
[class.isSelected]="selected$ | async"
[class.isSelected]="
(isSelectAllOption && iconChecked) ||
(!isSelectAllOption && (selected$ | async))
"
[class.isMulti]="isMulti"
[class.isFocused]="focused"
(click)="onClick()"
>
<i *ngIf="isMulti" class="aui-option__pointer">
<aui-icon [hidden]="!(selected$ | async)" icon="check_s"></aui-icon>
<aui-icon
icon="check_s"
[hidden]="
(isSelectAllOption && !iconChecked) ||
(!isSelectAllOption && !(selected$ | async)) ||
indeterminate
"
></aui-icon>
<aui-icon icon="minus_s" [hidden]="!indeterminate"></aui-icon>
</i>
<ng-content></ng-content>
</div>
39 changes: 37 additions & 2 deletions src/select/option/option.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ export class OptionComponent<T> {
bem: Bem = buildBem('aui-option');

private _disabled = false;
private _isSelectAllOption = false;
private _indeterminate = false;
private _iconChecked = false;
private _label: string | TemplateRef<unknown> = '';
private _labelContext: unknown = {};
private _value: T;
Expand Down Expand Up @@ -84,6 +87,33 @@ export class OptionComponent<T> {
this._disabled = coerceAttrBoolean(val);
}

@Input()
get isSelectAllOption() {
return this._isSelectAllOption;
}

set isSelectAllOption(val: boolean | '') {
this._isSelectAllOption = coerceAttrBoolean(val);
}

@Input()
get indeterminate() {
return this._indeterminate;
}

set indeterminate(val: boolean | '') {
this._indeterminate = coerceAttrBoolean(val);
}

@Input()
get iconChecked() {
return this._iconChecked;
}

set iconChecked(val: boolean | '') {
this._iconChecked = coerceAttrBoolean(val);
}

isMulti = false;

@ViewChild('elRef', { static: true })
Expand Down Expand Up @@ -134,8 +164,10 @@ export class OptionComponent<T> {
map(([label, value, labelContext]) => ({ label, value, labelContext })),
),
]).pipe(
map(([filterString, option]) =>
this.select.filterFn(filterString, option),
map(
([filterString, option]) =>
this.select.filterFn(filterString, option) ||
!!this.isSelectAllOption,
),
distinctUntilChanged(),
tap(visible => {
Expand All @@ -150,6 +182,9 @@ export class OptionComponent<T> {
if (this.disabled) {
return;
}
if (this.indeterminate) {
this.indeterminate = false;
}
this.select.onOptionClick(this);
}

Expand Down
18 changes: 13 additions & 5 deletions src/select/select.module.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';

import { IconModule } from '../icon/public-api';
import { InputModule } from '../input/public-api';
import { TagModule } from '../tag/public-api';
import { TooltipModule } from '../tooltip/public-api';
import { I18nModule } from '../i18n/i18n.module';
import { IconModule } from '../icon/icon.module';
import { InputModule } from '../input/input.module';
import { TagModule } from '../tag/tag.module';
import { TooltipModule } from '../tooltip/tooltip.module';

import {
OptionContentDirective,
Expand All @@ -18,7 +19,14 @@ import { SelectComponent } from './select.component';
import { IncludesDirective } from './validators';

@NgModule({
imports: [CommonModule, InputModule, IconModule, TooltipModule, TagModule],
imports: [
CommonModule,
InputModule,
I18nModule,
IconModule,
TooltipModule,
TagModule,
],
declarations: [
SelectComponent,
OptionComponent,
Expand Down
6 changes: 5 additions & 1 deletion stories/select/select.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,11 @@ import { SelectModule, IconModule } from '@alauda/ui';
<Story name="multi" height="240px">
{{
template: /* HTML */ `
<aui-multi-select [(ngModel)]="value" [maxRowCount]="3">
<aui-multi-select
[(ngModel)]="value"
[maxRowCount]="3"
[allowSelectAll]="true"
>
<aui-option *ngFor="let option of arr" [value]="option">
{{ option }}
</aui-option>
Expand Down

0 comments on commit f202fa4

Please sign in to comment.