@taiga-ui/kit
Version:
Taiga UI Angular main components kit
455 lines (446 loc) • 20.1 kB
JavaScript
import { __decorate, __param } from 'tslib';
import { EventEmitter, Optional, Self, Inject, ChangeDetectorRef, Input, HostBinding, Output, ContentChild, TemplateRef, ViewChild, Component, ChangeDetectionStrategy, forwardRef, Pipe, Directive, ContentChildren, NgModule } from '@angular/core';
import { AbstractTuiMultipleControl, TUI_DEFAULT_STRINGIFY, TUI_DEFAULT_IDENTITY_MATCHER, ALWAYS_FALSE_HANDLER, EMPTY_ARRAY, isNativeFocused, setNativeFocused, tuiDefaultProp, tuiPure, TUI_FOCUSABLE_ITEM_ACCESSOR, EMPTY_QUERY, itemsQueryListObservable, getOriginalArrayFromQueryList, isPresent, tuiReplayedValueChangesFrom, EMPTY_FUNCTION, TuiPreventDefaultModule, TuiActiveZoneModule, TuiLetModule, TuiMapperPipeModule, TuiHoveredModule } from '@taiga-ui/cdk';
import { NgControl, NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
import { TuiSvgService, TuiTextfieldLabelOutsideDirective, TUI_TEXTFIELD_LABEL_OUTSIDE, TuiDataListDirective, TUI_DATA_LIST_ACCESSOR, TuiHostedDropdownComponent, TUI_DATA_LIST_HOST, TUI_OPTION_CONTENT, sizeBigger, TuiOptionComponent, TuiSvgModule, TuiHostedDropdownModule, TuiPrimitiveCheckboxModule, TuiDataListModule } from '@taiga-ui/core';
import { TuiStringifiableItem } from '@taiga-ui/kit/classes';
import { TuiInputTagComponent, TuiInputTagModule } from '@taiga-ui/kit/components/input-tag';
import { iconBlank } from '@taiga-ui/kit/constants';
import { FIXED_DROPDOWN_CONTROLLER_PROVIDER } from '@taiga-ui/kit/providers';
import { CommonModule } from '@angular/common';
import { TUI_MULTI_SELECT_OPTION, TuiMultiSelectOptionModule } from '@taiga-ui/kit/components/multi-select-option';
import { PolymorpheusModule } from '@tinkoff/ng-polymorpheus';
import { combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
var TuiMultiSelectComponent_1;
let TuiMultiSelectComponent = TuiMultiSelectComponent_1 = class TuiMultiSelectComponent extends AbstractTuiMultipleControl {
constructor(control, changeDetectorRef, svgService, textfieldLabelOutside) {
super(control, changeDetectorRef);
this.textfieldLabelOutside = textfieldLabelOutside;
this.stringify = TUI_DEFAULT_STRINGIFY;
this.identityMatcher = TUI_DEFAULT_IDENTITY_MATCHER;
this.expandable = true;
this.search = '';
this.editable = true;
this.disabledItemHandler = ALWAYS_FALSE_HANDLER;
this.valueContent = '';
this.searchChange = new EventEmitter();
this.open = false;
this.valueMapper = (value, stringify, group) => group
? EMPTY_ARRAY
: value.map(item => new TuiStringifiableItem(item, stringify));
this.disabledItemHandlerWrapper = handler => stringifiable => typeof stringifiable === 'string' || handler(stringifiable.item);
this.datalist = '';
svgService.define({ iconBlank });
}
get nativeFocusableElement() {
return this.input ? this.input.nativeFocusableElement : null;
}
get focused() {
return ((!!this.input && this.input.focused) ||
(!!this.hostedDropdown && this.hostedDropdown.focused));
}
get computedValue() {
return this.computedGroup ? EMPTY_ARRAY : this.value;
}
// @bad TODO: think of a better way
get searchOrSpace() {
return this.computedGroup ? ' ' : this.searchString;
}
get searchString() {
return this.search === null ? '' : this.search;
}
get tagIcon() {
return this.interactive ? 'iconBlank' : 'tuiIconChevronDownLarge';
}
get interactive() {
return !this.disabled && !this.readOnly;
}
get inputHidden() {
return !this.editable && !this.computedGroup;
}
get computedGroup() {
return (!!this.valueContent &&
this.value.length > 0 &&
(!this.focused || !this.editable));
}
get context() {
return this.getContext(this.value);
}
getStringifier(stringify) {
return ({ $implicit }) => stringify($implicit);
}
onHoveredChange(hovered) {
this.updateHovered(hovered);
}
onSpace(event) {
if (!this.editable) {
event.preventDefault();
}
if (!this.readOnly) {
this.open = true;
}
}
handleOption(option) {
const { value, identityMatcher } = this;
const index = value.findIndex(item => identityMatcher(item, option));
this.updateValue(index === -1
? [...value, option]
: [...value.slice(0, index), ...value.slice(index + 1)]);
this.updateSearch(null);
}
onEnter(event) {
const { value } = this;
const options = this.accessor ? this.accessor.getOptions() : [];
if (options.length !== 1) {
return;
}
const index = value.indexOf(options[0]);
event.preventDefault();
this.updateValue(index === -1
? [...value, options[0]]
: [...value.slice(0, index), ...value.slice(index + 1)]);
this.updateSearch(null);
}
onClick({ nativeFocusableElement }) {
if (this.interactive &&
nativeFocusableElement &&
isNativeFocused(nativeFocusableElement)) {
this.open = !this.open;
}
}
onArrowClick() {
this.open = !this.open;
this.focusInput();
}
onInput(value) {
this.updateValue(value.map(({ item }) => item));
}
onSearch(search) {
this.open = true;
this.updateSearch(search);
}
onActiveZone(active) {
this.updateFocused(active);
}
setDisabledState() {
super.setDisabledState();
this.open = false;
}
updateSearch(search) {
if (this.search === search) {
return;
}
this.search = search;
this.searchChange.emit(search);
}
focusInput(preventScroll = false) {
if (this.nativeFocusableElement) {
setNativeFocused(this.nativeFocusableElement, true, preventScroll);
}
}
getContext($implicit) {
return { $implicit };
}
};
TuiMultiSelectComponent.ctorParameters = () => [
{ type: NgControl, decorators: [{ type: Optional }, { type: Self }, { type: Inject, args: [NgControl,] }] },
{ type: ChangeDetectorRef, decorators: [{ type: Inject, args: [ChangeDetectorRef,] }] },
{ type: TuiSvgService, decorators: [{ type: Inject, args: [TuiSvgService,] }] },
{ type: TuiTextfieldLabelOutsideDirective, decorators: [{ type: Inject, args: [TUI_TEXTFIELD_LABEL_OUTSIDE,] }] }
];
__decorate([
Input(),
tuiDefaultProp()
], TuiMultiSelectComponent.prototype, "stringify", void 0);
__decorate([
Input(),
tuiDefaultProp()
], TuiMultiSelectComponent.prototype, "identityMatcher", void 0);
__decorate([
Input(),
tuiDefaultProp()
], TuiMultiSelectComponent.prototype, "expandable", void 0);
__decorate([
Input(),
tuiDefaultProp()
], TuiMultiSelectComponent.prototype, "search", void 0);
__decorate([
Input(),
HostBinding('class._editable'),
tuiDefaultProp()
], TuiMultiSelectComponent.prototype, "editable", void 0);
__decorate([
Input(),
tuiDefaultProp()
], TuiMultiSelectComponent.prototype, "disabledItemHandler", void 0);
__decorate([
Input(),
tuiDefaultProp()
], TuiMultiSelectComponent.prototype, "valueContent", void 0);
__decorate([
Output()
], TuiMultiSelectComponent.prototype, "searchChange", void 0);
__decorate([
ContentChild(TuiDataListDirective, { read: TemplateRef })
], TuiMultiSelectComponent.prototype, "datalist", void 0);
__decorate([
ContentChild(TUI_DATA_LIST_ACCESSOR)
], TuiMultiSelectComponent.prototype, "accessor", void 0);
__decorate([
ViewChild(TuiHostedDropdownComponent)
], TuiMultiSelectComponent.prototype, "hostedDropdown", void 0);
__decorate([
ViewChild(TuiInputTagComponent)
], TuiMultiSelectComponent.prototype, "input", void 0);
__decorate([
tuiPure
], TuiMultiSelectComponent.prototype, "getStringifier", null);
__decorate([
tuiPure
], TuiMultiSelectComponent.prototype, "getContext", null);
TuiMultiSelectComponent = TuiMultiSelectComponent_1 = __decorate([
Component({
selector: 'tui-multi-select',
template: "<tui-hosted-dropdown\n class=\"wrapper\"\n [canOpen]=\"interactive\"\n [content]=\"datalist || ''\"\n [(open)]=\"open\"\n (tuiHoveredChange)=\"onHoveredChange($event)\"\n (tuiActiveZoneChange)=\"onActiveZone($event)\"\n>\n <tui-input-tag\n tuiHostedDropdownHost\n #inputTag\n automation-id=\"tui-multi-select__input\"\n class=\"input\"\n [nativeId]=\"nativeId\"\n [icon]=\"tagIcon\"\n [disabled]=\"disabled\"\n [disabledItemHandler]=\"disabledItemHandler | tuiMapper : disabledItemHandlerWrapper\"\n [readOnly]=\"readOnly\"\n [inputHidden]=\"!editable\"\n [pseudoHovered]=\"hovered\"\n [pseudoFocused]=\"focused\"\n [pseudoInvalid]=\"computedInvalid\"\n [editable]=\"false\"\n [expandable]=\"expandable\"\n [search]=\"searchOrSpace\"\n [ngModel]=\"computedValue | tuiMapper: valueMapper: stringify\"\n (ngModelChange)=\"onInput($event)\"\n (searchChange)=\"onSearch($event)\"\n (keydown.space)=\"onSpace($event)\"\n (keydown.enter)=\"onEnter($event)\"\n (click.stop)=\"onClick(inputTag)\"\n >\n <ng-content></ng-content>\n </tui-input-tag>\n <div\n *ngIf=\"computedGroup\"\n polymorpheus-outlet\n class=\"group\"\n [class.group_fullsize]=\"textfieldLabelOutside.labelOutside\"\n [context]=\"context\"\n [content]=\"valueContent\"\n ></div>\n <tui-svg\n *ngIf=\"interactive\"\n automation-id=\"tui-multi-select__arrow\"\n class=\"arrow\"\n src=\"tuiIconChevronDownLarge\"\n tuiPreventDefault=\"mousedown\"\n [class.arrow_open]=\"open\"\n (click.prevent)=\"onArrowClick()\"\n ></tui-svg>\n</tui-hosted-dropdown>\n",
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{
provide: TUI_FOCUSABLE_ITEM_ACCESSOR,
useExisting: forwardRef(() => TuiMultiSelectComponent_1),
},
{
provide: TUI_DATA_LIST_HOST,
useExisting: forwardRef(() => TuiMultiSelectComponent_1),
},
FIXED_DROPDOWN_CONTROLLER_PROVIDER,
],
styles: [":host{position:relative;display:block}:host._disabled{pointer-events:none}.wrapper{display:block}:host:not(._editable):not(._readonly) .input{cursor:pointer}.arrow{transition-duration:.3s;transition-timing-function:ease-in-out;display:flex;width:24px;align-items:center;justify-content:center;color:var(--tui-text-03);box-sizing:border-box;transition-property:color,transform;position:absolute;top:50%;transform:translate(0,-50%);right:12px;height:24px;box-sizing:content-box;cursor:pointer}.arrow:hover{color:var(--tui-text-02)}:host._disabled .arrow,:host._readonly .arrow{pointer-events:none}:host[data-mode=onDark] .arrow{color:var(--tui-text-03-night)}:host[data-mode=onDark] .arrow:hover{color:var(--tui-text-01-night)}.arrow_open{transform:rotate(-180deg) translate(0,50%)}.group{position:absolute;top:0;left:0;bottom:0;display:flex;align-items:center;padding:27px 16px 9px;pointer-events:none}.group_fullsize{padding-top:1px;padding-bottom:0}:host[data-tui-host-size='m'] .group_fullsize.group_fullsize{padding-top:0}:host[data-tui-host-size='m'] .group{padding:19px 12px 0;font-size:13px}"]
}),
__param(0, Optional()),
__param(0, Self()),
__param(0, Inject(NgControl)),
__param(1, Inject(ChangeDetectorRef)),
__param(2, Inject(TuiSvgService)),
__param(3, Inject(TUI_TEXTFIELD_LABEL_OUTSIDE))
], TuiMultiSelectComponent);
let TuiHideSelectedPipe = class TuiHideSelectedPipe {
constructor(component) {
this.component = component;
}
transform(items, { value, identityMatcher } = this.component) {
return items && this.filter(items, value, identityMatcher);
}
filter(items, value, matcher) {
return items.filter(item => value.every(selected => !matcher(selected, item)));
}
};
TuiHideSelectedPipe.ctorParameters = () => [
{ type: TuiMultiSelectComponent, decorators: [{ type: Inject, args: [TuiMultiSelectComponent,] }] }
];
__decorate([
tuiPure
], TuiHideSelectedPipe.prototype, "filter", null);
TuiHideSelectedPipe = __decorate([
Pipe({
name: 'tuiHideSelected',
pure: false,
}),
__param(0, Inject(TuiMultiSelectComponent))
], TuiHideSelectedPipe);
// TODO: Remove and switch to viewProviders when Angular is updated to Ivy
/** @deprecated */
let TuiMultiSelectGroupResetDirective = class TuiMultiSelectGroupResetDirective {
};
TuiMultiSelectGroupResetDirective = __decorate([
Directive({
selector: '[tuiMultiSelectGroupReset]',
providers: [
{
provide: TUI_OPTION_CONTENT,
useValue: null,
},
],
})
], TuiMultiSelectGroupResetDirective);
let TuiMultiSelectGroupComponent = class TuiMultiSelectGroupComponent {
constructor(host, control) {
this.host = host;
this.control = control;
this.label = '';
this.options = EMPTY_QUERY;
}
get size() {
return (this.options.first && this.options.first.size) || 'm';
}
get checkboxSize() {
return this.options.first && sizeBigger(this.options.first.size) ? 'l' : 'm';
}
get empty$() {
return itemsQueryListObservable(this.options).pipe(map(({ length }) => !length));
}
get disabled$() {
return itemsQueryListObservable(this.options).pipe(map(items => items.every(({ disabled }) => disabled)));
}
get value$() {
return combineLatest(this.items$, this.valueChanges$).pipe(map(([items, current]) => {
let result = false;
for (let i = 0; i < items.length; i++) {
const selected = current.some(selected => this.matcher(selected, items[i]));
if ((!selected && result) || (selected && !result && i)) {
return null;
}
result = selected;
}
return result;
}));
}
onClick(checked) {
if (!this.control.control) {
return;
}
const controlValue = this.control.value || [];
const { values } = this;
const filtered = controlValue.filter(current => values.every(item => !this.matcher(current, item)));
this.control.control.setValue(checked ? filtered : [...filtered, ...values]);
}
get values() {
return this.filter(getOriginalArrayFromQueryList(this.options));
}
get matcher() {
return this.host.identityMatcher || TUI_DEFAULT_IDENTITY_MATCHER;
}
get items$() {
return itemsQueryListObservable(this.options).pipe(map(options => options.map(({ value }) => value).filter(isPresent)));
}
get valueChanges$() {
return tuiReplayedValueChangesFrom(this.control).pipe(map(value => value || []));
}
filter(items) {
return items.map(({ value }) => value).filter(isPresent);
}
};
TuiMultiSelectGroupComponent.ctorParameters = () => [
{ type: undefined, decorators: [{ type: Inject, args: [TUI_DATA_LIST_HOST,] }] },
{ type: NgControl, decorators: [{ type: Inject, args: [NgControl,] }] }
];
__decorate([
Input(),
tuiDefaultProp()
], TuiMultiSelectGroupComponent.prototype, "label", void 0);
__decorate([
ContentChildren(TuiOptionComponent)
], TuiMultiSelectGroupComponent.prototype, "options", void 0);
__decorate([
tuiPure
], TuiMultiSelectGroupComponent.prototype, "empty$", null);
__decorate([
tuiPure
], TuiMultiSelectGroupComponent.prototype, "disabled$", null);
__decorate([
tuiPure
], TuiMultiSelectGroupComponent.prototype, "value$", null);
__decorate([
tuiPure
], TuiMultiSelectGroupComponent.prototype, "items$", null);
__decorate([
tuiPure
], TuiMultiSelectGroupComponent.prototype, "valueChanges$", null);
__decorate([
tuiPure
], TuiMultiSelectGroupComponent.prototype, "filter", null);
TuiMultiSelectGroupComponent = __decorate([
Component({
selector: 'tui-opt-group[tuiMultiSelectGroup]',
template: "<ng-container *tuiLet=\"value$ | async as value\">\n <button\n *ngIf=\"label && !(empty$ | async)\"\n tuiMultiSelectGroupReset\n tuiOption\n [size]=\"size\"\n [disabled]=\"disabled$ | async\"\n (click)=\"onClick(value)\"\n >\n <tui-primitive-checkbox\n class=\"tui-space_right-3\"\n [size]=\"checkboxSize\"\n [value]=\"value\"\n ></tui-primitive-checkbox>\n <span class=\"label\">{{label}}</span>\n </button>\n</ng-container>\n<ng-content></ng-content>\n",
changeDetection: ChangeDetectionStrategy.OnPush,
styles: [":host{display:flex;flex-direction:column}:host:before{content:''}.label{font:var(--tui-font-text-xs);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;color:var(--tui-text-02)}"]
}),
__param(0, Inject(TUI_DATA_LIST_HOST)),
__param(1, Inject(NgControl))
], TuiMultiSelectGroupComponent);
function hostFallbackFactory(control, host) {
return (host || {
handleOption: option => {
if (!control.control) {
return;
}
const value = control.value || [];
const index = value.indexOf(option);
control.control.setValue(index === -1
? [...value, option]
: [...value.slice(0, index), ...value.slice(index + 1)]);
},
});
}
const ɵ0 = TUI_MULTI_SELECT_OPTION, ɵ1 = {
writeValue: EMPTY_FUNCTION,
registerOnChange: EMPTY_FUNCTION,
registerOnTouched: EMPTY_FUNCTION,
};
let TuiMultiSelectGroupDirective = class TuiMultiSelectGroupDirective {
};
TuiMultiSelectGroupDirective = __decorate([
Directive({
selector: '[tuiMultiSelectGroup]',
providers: [
{
provide: TUI_OPTION_CONTENT,
useValue: ɵ0,
},
{
provide: TUI_DATA_LIST_HOST,
deps: [
NgControl,
[new Optional(), forwardRef(() => TuiMultiSelectComponent)],
],
useFactory: hostFallbackFactory,
},
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useValue: ɵ1,
},
],
})
], TuiMultiSelectGroupDirective);
let TuiMultiSelectModule = class TuiMultiSelectModule {
};
TuiMultiSelectModule = __decorate([
NgModule({
imports: [
CommonModule,
FormsModule,
PolymorpheusModule,
TuiPreventDefaultModule,
TuiActiveZoneModule,
TuiLetModule,
TuiMapperPipeModule,
TuiHoveredModule,
TuiSvgModule,
TuiHostedDropdownModule,
TuiInputTagModule,
TuiMultiSelectOptionModule,
TuiPrimitiveCheckboxModule,
TuiDataListModule,
],
declarations: [
TuiMultiSelectComponent,
TuiMultiSelectGroupComponent,
TuiMultiSelectGroupDirective,
TuiMultiSelectGroupResetDirective,
TuiHideSelectedPipe,
],
exports: [
TuiMultiSelectComponent,
TuiMultiSelectGroupComponent,
TuiMultiSelectGroupDirective,
TuiHideSelectedPipe,
],
})
], TuiMultiSelectModule);
/**
* Generated bundle index. Do not edit.
*/
export { TuiHideSelectedPipe, TuiMultiSelectComponent, TuiMultiSelectGroupComponent, TuiMultiSelectGroupDirective, TuiMultiSelectGroupResetDirective, TuiMultiSelectModule, hostFallbackFactory, ɵ0, ɵ1 };
//# sourceMappingURL=taiga-ui-kit-components-multi-select.js.map