import { Component, ElementRef, Input, Output, EventEmitter, NgZone, ChangeDetectorRef, AfterViewChecked } from '@angular/core'; import {NG_VALUE_ACCESSOR, ControlValueAccessor} from '@angular/forms'; import {AbstractComponent} from 'gp-admin-abstract'; @Component({ selector: 'gp-select', template: `
{{title}}
{{error}}
{{ placeholder }} {{valueTitle ? valueTitle : ' '}}
{{item.value}}
`, styles: [ `@charset "UTF-8"; /** * Переменные */ :root { --color-white: white; --color-success: #00afec; --color-warning: #ffb827; --color-error: #f35252; --color-disabled: #dbdbdb; --color-black: black; --color-span: #b5b5b5; --color-span-second: #212121; --color-bg: #f8f8f8; --color-bg-second: #f8f8f8; --color-bg-success: rgba(0, 175, 236, 0.1); --color-bg-warning: var(--color-warning); --color-bg-error: var(--color-error); --color-placeholder: #b5b5b5; --color-border: #dbdbdb; --color-radio-border: #d3d3d3; --font-roboto: 'Roboto', sans-serif; --font-helvetica: 'Helvetica', sans-serif; } /** * */ /** * Стили для компонента "gp-select" */ .gp-select { min-width: 18rem; } .gp-select--open .gp-select__arrow-icon { -moz-transform: scale(-1); -o-transform: scale(-1); -ms-transform: scale(-1); -webkit-transform: scale(-1); transform: scale(-1); } .gp-select--open .gp-select__dropdown { max-height: 12.5rem; padding: 0 0.5rem 0.5rem; pointer-events: auto; opacity: 1; -moz-transition-delay: 0ms, 0ms; -o-transition-delay: 0ms, 0ms; -webkit-transition-delay: 0ms, 0ms; transition-delay: 0ms, 0ms; -moz-transition-property: max-height, padding; -o-transition-property: max-height, padding; -webkit-transition-property: max-height, padding; transition-property: max-height, padding; } .gp-select--success input.gp-select__area { border-color: #00afec; } .gp-select--success .gp-select__title { color: #00afec; } .gp-select--success .gp-select__arrow-icon { border-top-color: #00afec; } .gp-select--success .gp-select__dropdown { border-right-color: #00afec; border-bottom-color: #00afec; border-left-color: #00afec; } .gp-select--error .gp-select__area { border-color: #f35252; } .gp-select--error .gp-select__title { color: #f35252; } .gp-select--error .gp-select__error { display: block; } .gp-select--error .gp-select__arrow-icon { border-top-color: #f35252; } .gp-select--error .gp-select__dropdown { border-right-color: #f35252; border-bottom-color: #f35252; border-left-color: #f35252; } .gp-select--disabled .gp-select__area { cursor: not-allowed; color: #b5b5b5; border-color: #dbdbdb; background-color: #f8f8f8; } .gp-select--disabled .gp-select__arrow-icon { border-top-color: #dbdbdb; } .gp-select--disabled .gp-select__dropdown { border-right-color: #dbdbdb; border-bottom-color: #dbdbdb; border-left-color: #dbdbdb; background-color: #f8f8f8; cursor: not-allowed; } .gp-select--disabled .gp-select__dropdown-item { color: #b5b5b5; background-color: #f8f8f8; cursor: not-allowed; } .gp-select__title { font: 400 1.2rem/1.4rem "Roboto", sans-serif; margin-bottom: 0.5rem; color: #b5b5b5; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .gp-select__error { font: 400 1.2rem/1.4rem "Roboto", sans-serif; display: none; color: #f35252; } .gp-select__area { font: 1.3rem/1.5rem "Roboto", sans-serif; width: 100%; height: 3.7rem; padding: 1rem 2.6rem 1rem 1.5rem; cursor: pointer; color: #212121; border: 0.1rem solid #dbdbdb; border-radius: 0.2rem; outline: none; background-color: var(--color-white); } .gp-select input::-webkit-select-placeholder, .gp-select input:-ms-select-placeholder, .gp-select input::placeholder { color: #b5b5b5; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .gp-select__arrow { top: 0.1rem; right: 0.1rem; height: 3.7rem; width: 2.6rem; border-radius: 0.1rem; } .gp-select__arrow-icon { -moz-transition: transform 150ms ease; -o-transition: transform 150ms ease; -webkit-transition: transform 150ms ease; transition: transform 150ms ease; border-top: 0.4rem solid #c4c4c4; border-right: 0.3rem solid transparent; border-bottom: 0 solid transparent; border-left: 0.3rem solid transparent; } .gp-select__dropdown { position: absolute; top: 54px; right: 0; left: 0; overflow: scroll; pointer-events: none; max-height: 0; margin: 0; padding: 0.1rem; list-style: none; opacity: 0; -moz-transition-property: max-height, padding, opacity; -o-transition-property: max-height, padding, opacity; -webkit-transition-property: max-height, padding, opacity; transition-property: max-height, padding, opacity; -moz-transition-delay: 0ms, 300ms, 300ms; -o-transition-delay: 0ms, 300ms, 300ms; -webkit-transition-delay: 0ms, 300ms, 300ms; transition-delay: 0ms, 300ms, 300ms; -moz-transition-property: 300ms, 0ms, 0ms; -o-transition-property: 300ms, 0ms, 0ms; -webkit-transition-property: 300ms, 0ms, 0ms; transition-property: 300ms, 0ms, 0ms; -moz-transition-timing-function: ease-in-out; -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; border-right: 0.1rem solid #dbdbdb; border-bottom: 0.1rem solid #dbdbdb; border-left: 0.1rem solid #dbdbdb; border-radius: 0 0 0.3rem 0.3rem; background-color: var(--color-white); z-index: 5; } .gp-select__dropdown-item { font: 1.3rem/1.5rem "Roboto", sans-serif; padding: 0.8rem 0.2rem 0.7rem 1rem; cursor: pointer; -moz-transition: all 150ms ease-in-out; -o-transition: all 150ms ease-in-out; -webkit-transition: all 150ms ease-in-out; transition: all 150ms ease-in-out; letter-spacing: 0.03rem; color: #212121; border-radius: 0.3rem; background-color: var(--color-white); -webkit-box-flex: 1 100%; -moz-box-flex: 1 100%; -webkit-flex: 1 100%; -ms-flex: 1 100%; flex: 1 100%; } .gp-select__dropdown-item:hover { background-color: #f8f8f8; } .gp-select__dropdown-item--disabled, .gp-select__dropdown-item[disabled] { color: #b5b5b5; pointer-events: none; } .gp-select__dropdown-item--active { color: #b5b5b5; pointer-events: none; } `], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: SelectComponent, multi: true } ], host: { '(document:click)': 'onDocumentClick($event)', }, }) export class SelectComponent extends AbstractComponent implements ControlValueAccessor, AfterViewChecked { @Input() title: string = ''; @Input() error: string = ''; @Input() disabled: boolean = false; @Input() placeholder: string = ''; @Input() value: string|number|any; @Output() onScrollUp: EventEmitter = new EventEmitter(); @Output() onScrollDown: EventEmitter = new EventEmitter(); public options: any[] = []; public opened: boolean = false; public valueTitle: string = ''; private isObject: boolean = false; constructor(private elRef: ElementRef, private changeDetector: ChangeDetectorRef, private zone: NgZone) { super(elRef); } public onDocumentClick(e: Event) { if (!this.getElementRef().nativeElement.contains(e.target)) { this.opened = false; } } ngAfterViewChecked(): void { this.zone.runOutsideAngular(() => { const opts = this.getElementRef().nativeElement.querySelectorAll('option'); if (opts.length > 0) { let newOpts = [], replaceOptions: boolean = (this.options.length != opts.length); for (let opt of opts) { newOpts.push({key: opt.value, value: opt.text}); if (!replaceOptions) { replaceOptions = (this.getOptionByKey(opt.value) === null); } } if (replaceOptions) { this.options = newOpts; this.valueTitle = this.getTitleByValue(this.value); this.changeDetector.detectChanges(); } } }); } showErrorText(): boolean { return ((typeof this.error === 'string') && !!this.error); } propagateChange = (_: any) => { }; writeValue(_value: string|number|any): void { if (_value && (typeof _value === 'object')) { this.isObject = true; this.value = _value.key; } else { this.isObject = false; this.value = _value; } this.valueTitle = this.getTitleByValue(_value); } registerOnChange(fn: any): void { this.propagateChange = fn; } registerOnTouched(fn: any): void { } setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; } setValue(_value: string|number): void { if (!this.disabled) { let title = this.getTitleByValue(_value); this.value = this.isObject ? {key: _value, value: title} : _value; this.valueTitle = title; this.propagateChange(this.value); } } scrollDown(): void { this.onScrollDown.emit(); } scrollUp(): void { this.onScrollUp.emit(); } private getTitleByValue(_value: string|number|any): string { let result = null; if (_value && (typeof _value === 'object')) { result = _value.value; } else { let opt = this.getOptionByKey(_value); if (opt) { result = opt.value; } } if (result === null) { result = this.valueTitle; } return result; } private getOptionByKey(_key: string|number): any { let result: any = null; for (let opt of this.options) { if (opt.hasOwnProperty('key') && opt.hasOwnProperty('value')) { if (opt.key == _key) { result = opt; break; } } } return result; } }