2023-01-06 13:40:18 +03:00
|
|
|
|
import {
|
2023-01-09 18:01:31 +03:00
|
|
|
|
createBreadCrumb,
|
2023-01-06 13:40:18 +03:00
|
|
|
|
createNativeSelect,
|
|
|
|
|
createNativeSelectOption,
|
|
|
|
|
} from './components/create-element/create-elementTs';
|
2023-01-09 18:01:31 +03:00
|
|
|
|
import { ICreateBreadCrumb } from './components/create-element/create-element.interface';
|
2023-01-09 17:57:56 +03:00
|
|
|
|
|
2023-01-09 16:10:43 +03:00
|
|
|
|
import {
|
|
|
|
|
createSelected,
|
|
|
|
|
getFormatItem,
|
|
|
|
|
getSelectText,
|
|
|
|
|
nativeOptionMultiple,
|
|
|
|
|
nativeOptionOrdinary,
|
|
|
|
|
} from './components/utils/utilsTs';
|
2023-01-09 18:01:31 +03:00
|
|
|
|
import { IDataItem, ITextSelect } from './components/utils/urils.interface';
|
2023-01-09 17:57:56 +03:00
|
|
|
|
|
2023-01-06 14:57:14 +03:00
|
|
|
|
import { ICgSelect } from './interfaces/cg-select.interface';
|
2023-01-05 18:21:10 +03:00
|
|
|
|
import { IItems } from './interfaces/items.interface';
|
2023-01-09 18:01:31 +03:00
|
|
|
|
|
2023-01-05 18:21:10 +03:00
|
|
|
|
import './main.scss';
|
|
|
|
|
|
2023-01-06 14:57:14 +03:00
|
|
|
|
export class CGSelect implements ICgSelect {
|
2023-01-05 18:21:10 +03:00
|
|
|
|
selector: string;
|
|
|
|
|
selected?: string;
|
|
|
|
|
placeholder?: string;
|
|
|
|
|
items?: IItems[] | string[] | any;
|
|
|
|
|
darkTheme?: boolean;
|
|
|
|
|
searchMode?: boolean;
|
|
|
|
|
closeOnSelect?: boolean;
|
|
|
|
|
nativeSelectMode?: boolean;
|
|
|
|
|
listDisplayMode?: boolean;
|
|
|
|
|
language?: string;
|
|
|
|
|
lable?: string;
|
|
|
|
|
styles?: object;
|
|
|
|
|
event?: string;
|
|
|
|
|
url?: string;
|
|
|
|
|
multiselect?: boolean;
|
|
|
|
|
multiselectTag?: boolean;
|
|
|
|
|
|
|
|
|
|
private element: Element | null;
|
2023-01-06 13:40:18 +03:00
|
|
|
|
private list: Element | null | undefined;
|
2023-01-06 14:57:14 +03:00
|
|
|
|
private options: ICgSelect;
|
2023-01-06 13:40:18 +03:00
|
|
|
|
private randomId: string;
|
|
|
|
|
private caret: Element | null | undefined;
|
2023-01-05 18:21:10 +03:00
|
|
|
|
private category: string;
|
2023-01-06 21:14:16 +03:00
|
|
|
|
private selectedItems: string[] | string;
|
2023-01-05 18:21:10 +03:00
|
|
|
|
private itemsSelect: IItems[] | string[] | any;
|
|
|
|
|
private indexes: number[] = [];
|
|
|
|
|
|
2023-01-06 14:57:14 +03:00
|
|
|
|
constructor(setting: ICgSelect) {
|
2023-01-05 18:21:10 +03:00
|
|
|
|
this.init(setting);
|
2023-01-05 19:56:07 +03:00
|
|
|
|
this.render();
|
2023-01-06 14:07:37 +03:00
|
|
|
|
this.closeSelectClick();
|
2023-01-05 18:21:10 +03:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-06 13:40:18 +03:00
|
|
|
|
/**
|
|
|
|
|
* Приватный метод инициализации экземпляра класса DropDown
|
2023-01-06 14:07:37 +03:00
|
|
|
|
* @method init
|
2023-01-06 13:40:18 +03:00
|
|
|
|
* @member
|
|
|
|
|
* @protected
|
|
|
|
|
* @param {ISgSelect} setting передаваемые настройки селекта
|
|
|
|
|
* @description Приватный метод. Общая инициализация селекта. Получение настоек и преобразвание элементов селекта.
|
|
|
|
|
* @example
|
|
|
|
|
* {
|
|
|
|
|
selector: '.cg-dropdown_one',
|
|
|
|
|
placeholder: 'Выберите авто',
|
|
|
|
|
items: [
|
|
|
|
|
'BMW',
|
|
|
|
|
{
|
|
|
|
|
id: '213sade',
|
|
|
|
|
title: 'Opel',
|
|
|
|
|
value: 1,
|
|
|
|
|
},
|
|
|
|
|
'Mersedes',
|
|
|
|
|
'MAN',
|
|
|
|
|
'max',
|
|
|
|
|
],
|
|
|
|
|
darkTheme: true,
|
|
|
|
|
multiselect: true,
|
|
|
|
|
multiselectTag: true,
|
|
|
|
|
}
|
|
|
|
|
*/
|
2023-01-06 14:57:14 +03:00
|
|
|
|
private init(setting: ICgSelect): void {
|
2023-01-06 13:40:18 +03:00
|
|
|
|
const { items, multiselect, url, selector } = setting;
|
2023-01-05 19:56:07 +03:00
|
|
|
|
|
2023-01-05 18:21:10 +03:00
|
|
|
|
this.options = setting;
|
|
|
|
|
|
|
|
|
|
const elem = document.querySelector(selector);
|
|
|
|
|
this.element = elem;
|
|
|
|
|
|
|
|
|
|
this.element?.addEventListener('click', (e) => {
|
|
|
|
|
e.preventDefault();
|
2023-01-06 13:40:18 +03:00
|
|
|
|
this.open();
|
2023-01-05 18:21:10 +03:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.itemsSelect = [];
|
2023-01-06 13:40:18 +03:00
|
|
|
|
|
2023-01-05 18:21:10 +03:00
|
|
|
|
if (!items && url) {
|
2023-01-06 13:40:18 +03:00
|
|
|
|
this.renderUrl();
|
|
|
|
|
return;
|
2023-01-05 18:21:10 +03:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-06 13:40:18 +03:00
|
|
|
|
items.forEach((dataItem: any, index: number) => {
|
|
|
|
|
let itemInputs: IDataItem = {
|
|
|
|
|
ItemValue: dataItem,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.itemsSelect.push(getFormatItem(itemInputs.ItemValue, index));
|
|
|
|
|
});
|
2023-01-05 18:21:10 +03:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-06 13:40:18 +03:00
|
|
|
|
/**
|
|
|
|
|
* Приватный метод рендера экземпляра класса DropDown
|
|
|
|
|
*@protected
|
2023-01-06 14:07:37 +03:00
|
|
|
|
* @method render
|
2023-01-06 13:40:18 +03:00
|
|
|
|
* @param {string} select необязательный елемент. Передаеться в метод initSelected
|
|
|
|
|
* @description Рендер елементов в селекте.
|
|
|
|
|
*/
|
|
|
|
|
private render(select?: string): void {
|
2023-01-05 19:56:07 +03:00
|
|
|
|
const {
|
|
|
|
|
styles,
|
|
|
|
|
multiselect,
|
|
|
|
|
searchMode,
|
|
|
|
|
multiselectTag,
|
|
|
|
|
darkTheme,
|
|
|
|
|
language,
|
|
|
|
|
nativeSelectMode,
|
|
|
|
|
listDisplayMode,
|
|
|
|
|
} = this.options;
|
|
|
|
|
|
|
|
|
|
const random = Math.random().toString(36).substring(2, 10);
|
|
|
|
|
|
2023-01-06 13:40:18 +03:00
|
|
|
|
this.initSelected();
|
|
|
|
|
|
|
|
|
|
const ulList = document.createElement('ul');
|
|
|
|
|
const nativeSelect = createNativeSelect();
|
|
|
|
|
|
|
|
|
|
let inputSearch: string = '';
|
|
|
|
|
let textNode: Text;
|
|
|
|
|
|
|
|
|
|
this.randomId = random;
|
|
|
|
|
|
|
|
|
|
ulList.classList.add('list');
|
|
|
|
|
|
|
|
|
|
this.element?.appendChild(ulList);
|
|
|
|
|
|
|
|
|
|
this.itemsSelect.forEach((dataItem: IItems | any) => {
|
|
|
|
|
this.element?.appendChild(nativeSelect);
|
|
|
|
|
|
|
|
|
|
const liItem = document.createElement('li');
|
|
|
|
|
const nativeOption = createNativeSelectOption();
|
|
|
|
|
const strongItem = document.createElement('strong');
|
|
|
|
|
|
|
|
|
|
liItem.classList.add('list__item');
|
|
|
|
|
strongItem.classList.add('category');
|
|
|
|
|
|
2023-01-06 21:14:16 +03:00
|
|
|
|
if (multiselect) {
|
2023-01-06 13:40:18 +03:00
|
|
|
|
const checkBox = document.createElement('input');
|
|
|
|
|
checkBox.type = 'checkbox';
|
|
|
|
|
checkBox.setAttribute('id', `chbox-${dataItem.id}`);
|
|
|
|
|
liItem.appendChild(checkBox);
|
2023-01-05 19:56:07 +03:00
|
|
|
|
|
2023-01-06 21:14:16 +03:00
|
|
|
|
if (multiselectTag) {
|
2023-01-06 13:40:18 +03:00
|
|
|
|
checkBox.classList.add('displayHide');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nativeSelect.setAttribute('multiple', 'multiple');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dataItem.title) {
|
|
|
|
|
nativeOption.text = dataItem.title;
|
|
|
|
|
nativeOption.value = dataItem.title;
|
|
|
|
|
textNode = document.createTextNode(dataItem.title);
|
|
|
|
|
|
|
|
|
|
nativeSelect.appendChild(nativeOption);
|
|
|
|
|
liItem.appendChild(textNode);
|
|
|
|
|
ulList.appendChild(liItem);
|
|
|
|
|
} else {
|
|
|
|
|
// Для отрисовки категорий
|
|
|
|
|
textNode = document.createTextNode(dataItem);
|
|
|
|
|
strongItem.appendChild(textNode);
|
|
|
|
|
ulList.appendChild(strongItem);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.list = this.element?.querySelector('.list');
|
|
|
|
|
this.caret = this.element?.querySelector('.caret');
|
|
|
|
|
|
2023-01-06 14:07:37 +03:00
|
|
|
|
this.addOptionsBehaviour();
|
2023-01-05 19:56:07 +03:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-05 18:21:10 +03:00
|
|
|
|
private renderUrl() {}
|
2023-01-05 19:56:07 +03:00
|
|
|
|
|
2023-01-06 13:40:18 +03:00
|
|
|
|
/**
|
|
|
|
|
* Привaтный метод экземпляра класса DropDown
|
|
|
|
|
*
|
2023-01-06 14:07:37 +03:00
|
|
|
|
* @method initSelected
|
2023-01-06 13:40:18 +03:00
|
|
|
|
* @param {string} select необязательный елемент. Используется в методе selectIndex
|
|
|
|
|
* @description Отрисовывает и стилизует селект
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
private initSelected(select?: string): void {
|
2023-01-05 19:56:07 +03:00
|
|
|
|
const { styles, selected, placeholder, lable, language } = this.options;
|
|
|
|
|
|
|
|
|
|
if (selected) {
|
|
|
|
|
createSelected(this.element, selected);
|
|
|
|
|
} else if (placeholder) {
|
|
|
|
|
createSelected(this.element, placeholder);
|
|
|
|
|
} else {
|
|
|
|
|
// if (language && language === 'ru') {
|
|
|
|
|
// createSelected(this.#element, ru.selectPlaceholder);
|
|
|
|
|
// } else {
|
|
|
|
|
// createSelected(this.#element, en.selectPlaceholder);
|
|
|
|
|
// }
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-06 13:40:18 +03:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Приватный метод экземпляра класса DropDown
|
|
|
|
|
* @protected
|
|
|
|
|
* @param {boolean} oneClick необязательный параметр передаваемый из функции buttonControl
|
|
|
|
|
* @description Открывает список для выбора элемента
|
2023-01-06 14:07:37 +03:00
|
|
|
|
* @method open
|
2023-01-06 13:40:18 +03:00
|
|
|
|
*/
|
|
|
|
|
private open(oneClick?: boolean): void {
|
|
|
|
|
if (oneClick === true) {
|
|
|
|
|
this.list?.classList.add('open');
|
|
|
|
|
this.caret?.classList.add('caret_rotate');
|
|
|
|
|
} else {
|
|
|
|
|
this.list?.classList.toggle('open');
|
|
|
|
|
this.caret?.classList.toggle('caret_rotate');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-06 14:07:37 +03:00
|
|
|
|
/**
|
|
|
|
|
* Приватный метод экземпляра класса DropDown
|
|
|
|
|
* @protected
|
|
|
|
|
* @description Закрывает список
|
|
|
|
|
* @method #close
|
|
|
|
|
*/
|
2023-01-09 16:10:43 +03:00
|
|
|
|
private close(): void {
|
2023-01-06 14:07:37 +03:00
|
|
|
|
this.list?.classList.remove('open');
|
|
|
|
|
this.caret?.classList.remove('caret_rotate');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Приватный метод экземпляра класса DropDown
|
|
|
|
|
* @protected
|
|
|
|
|
* @description Закрывает список по клику вне элемента
|
2023-01-06 14:57:14 +03:00
|
|
|
|
* @method closeSelectClick
|
2023-01-06 14:07:37 +03:00
|
|
|
|
*/
|
2023-01-09 16:10:43 +03:00
|
|
|
|
private closeSelectClick(): void {
|
2023-01-06 14:07:37 +03:00
|
|
|
|
const dropdown = document.querySelector(`${this.options.selector}`);
|
|
|
|
|
|
|
|
|
|
document.addEventListener('click', (e) => {
|
|
|
|
|
const withinBoundaries = e.composedPath().includes(dropdown!);
|
|
|
|
|
if (!withinBoundaries) {
|
|
|
|
|
// if (this.btn) {
|
|
|
|
|
// return;
|
|
|
|
|
// } else {
|
|
|
|
|
// this.#close();
|
|
|
|
|
// }
|
|
|
|
|
this.close();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Приватный метод экземпляра класса DropDown
|
|
|
|
|
* @protected
|
|
|
|
|
* @description Метод реализовывающий выбор элементов в разных режимах. Обычный/Мультиселект/Мультиселект + Мультиселект Таг.
|
|
|
|
|
* @method addOptionsBehaviour
|
|
|
|
|
*/
|
|
|
|
|
private addOptionsBehaviour() {
|
|
|
|
|
const {
|
|
|
|
|
multiselect,
|
|
|
|
|
placeholder,
|
|
|
|
|
selected,
|
|
|
|
|
multiselectTag,
|
|
|
|
|
searchMode,
|
|
|
|
|
closeOnSelect,
|
|
|
|
|
darkTheme,
|
|
|
|
|
} = this.options;
|
|
|
|
|
|
|
|
|
|
const options = this.element?.querySelectorAll('.list__item');
|
2023-01-06 21:14:16 +03:00
|
|
|
|
const select: HTMLElement | null | undefined = this.element?.querySelector('.selected');
|
2023-01-06 14:07:37 +03:00
|
|
|
|
const nativeOption = this.element?.querySelectorAll('.nativeSelect__nativeOption');
|
|
|
|
|
|
2023-01-09 16:10:43 +03:00
|
|
|
|
const placeholderTextSelect: ITextSelect = {
|
|
|
|
|
placeholder: placeholder,
|
|
|
|
|
selected: selected,
|
|
|
|
|
};
|
|
|
|
|
|
2023-01-06 14:07:37 +03:00
|
|
|
|
const ulMultipul = document.createElement('ul');
|
|
|
|
|
|
2023-01-06 21:14:16 +03:00
|
|
|
|
if (multiselect) {
|
2023-01-09 16:10:43 +03:00
|
|
|
|
this.selectedItems = [];
|
2023-01-06 14:07:37 +03:00
|
|
|
|
ulMultipul.classList.add('multiselect-tag');
|
|
|
|
|
select?.classList.add('overflow-hidden');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
options?.forEach((option: Element, index: number) => {
|
|
|
|
|
option.addEventListener('click', (event) => {
|
2023-01-06 21:14:16 +03:00
|
|
|
|
const item: IItems = this.itemsSelect[index];
|
2023-01-09 16:10:43 +03:00
|
|
|
|
|
2023-01-06 14:07:37 +03:00
|
|
|
|
const checkIndex = this.indexes.indexOf(index);
|
|
|
|
|
|
2023-01-06 21:14:16 +03:00
|
|
|
|
if (closeOnSelect == false || multiselect) {
|
|
|
|
|
event.stopPropagation();
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-09 16:10:43 +03:00
|
|
|
|
if (multiselect) {
|
|
|
|
|
option.classList.toggle('active');
|
|
|
|
|
|
|
|
|
|
const checkBox: HTMLInputElement | null = option.querySelector('input[type="checkbox"]');
|
|
|
|
|
|
|
|
|
|
if (checkBox) {
|
|
|
|
|
if (!(event.target instanceof HTMLInputElement)) {
|
|
|
|
|
checkBox.checked = !checkBox.checked;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (checkIndex == -1) {
|
|
|
|
|
this.indexes.push(index);
|
|
|
|
|
nativeOptionMultiple(nativeOption, item.title, true);
|
|
|
|
|
select!.textContent = '';
|
|
|
|
|
|
2023-01-09 17:57:56 +03:00
|
|
|
|
if (multiselectTag) {
|
|
|
|
|
if (Array.isArray(this.selectedItems)) {
|
|
|
|
|
const dataBreadCrumb: ICreateBreadCrumb = {
|
|
|
|
|
option: this.options,
|
|
|
|
|
element: this.element,
|
|
|
|
|
indexes: this.indexes,
|
|
|
|
|
selectedItems: this.selectedItems,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.selectedItems.push(item.title);
|
|
|
|
|
select!.appendChild(ulMultipul);
|
|
|
|
|
ulMultipul.appendChild(
|
2023-01-09 18:01:31 +03:00
|
|
|
|
createBreadCrumb(dataBreadCrumb, item.title, index, item.id),
|
2023-01-09 17:57:56 +03:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (Array.isArray(this.selectedItems)) {
|
|
|
|
|
this.selectedItems.push(item.title);
|
|
|
|
|
select!.innerText = this.selectedItems.join(',');
|
|
|
|
|
}
|
2023-01-09 16:10:43 +03:00
|
|
|
|
}
|
|
|
|
|
} else {
|
2023-01-09 17:57:56 +03:00
|
|
|
|
if (multiselectTag) {
|
|
|
|
|
const tagItem = document.getElementById(`tag-${index}-${item.id}`);
|
|
|
|
|
ulMultipul.removeChild<Element>(tagItem!);
|
|
|
|
|
}
|
2023-01-09 16:10:43 +03:00
|
|
|
|
|
|
|
|
|
if (Array.isArray(this.selectedItems)) {
|
|
|
|
|
this.selectedItems.splice(checkIndex, 1);
|
2023-01-09 17:57:56 +03:00
|
|
|
|
this.indexes.splice(checkIndex, 1);
|
|
|
|
|
nativeOptionMultiple(nativeOption, item.title, false);
|
2023-01-09 16:10:43 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-09 17:57:56 +03:00
|
|
|
|
if (!this.selectedItems.length) {
|
2023-01-09 16:10:43 +03:00
|
|
|
|
getSelectText(placeholderTextSelect, select);
|
2023-01-09 17:57:56 +03:00
|
|
|
|
} else {
|
|
|
|
|
if (multiselectTag) {
|
|
|
|
|
select!.appendChild(ulMultipul);
|
|
|
|
|
} else {
|
|
|
|
|
if (Array.isArray(this.selectedItems)) {
|
|
|
|
|
select!.innerText = this.selectedItems.join(',');
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-09 16:10:43 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
select!.textContent = item.title;
|
|
|
|
|
this.selectedItems = item.title;
|
|
|
|
|
|
|
|
|
|
nativeOptionOrdinary(nativeOption, item.title);
|
|
|
|
|
|
|
|
|
|
options.forEach((option) => {
|
|
|
|
|
option.classList.remove('active');
|
|
|
|
|
});
|
|
|
|
|
option.classList.add('active');
|
|
|
|
|
}
|
2023-01-06 14:07:37 +03:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
2023-01-05 18:21:10 +03:00
|
|
|
|
}
|