Skip to content

Commit

Permalink
add new component CustomSelect
Browse files Browse the repository at this point in the history
  • Loading branch information
itchief committed Sep 27, 2020
1 parent 74b2034 commit fa456d7
Show file tree
Hide file tree
Showing 3 changed files with 315 additions and 0 deletions.
101 changes: 101 additions & 0 deletions custom_select/custom_select.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
.select {
position: relative;
width: 100%;
z-index: 1000;
}

.select__trigger {
display: flex;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 0.3125rem;
cursor: pointer;
align-items: center;
width: 100%;
font-size: 1rem;
padding: 0.375rem 0.75rem;
line-height: 1.4;
user-select: none;
font-size: 1rem;
justify-content: space-between;
font-style: italic;
}

.select__trigger::after {
content: '';
width: 0.75rem;
height: 0.75rem;
background-size: cover;
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" height="100" width="100"%3E%3Cpath d="M97.625 25.3l-4.813-4.89c-1.668-1.606-3.616-2.41-5.84-2.41-2.27 0-4.194.804-5.777 2.41L50 52.087 18.806 20.412C17.223 18.805 15.298 18 13.03 18c-2.225 0-4.172.804-5.84 2.41l-4.75 4.89C.813 26.95 0 28.927 0 31.23c0 2.346.814 4.301 2.439 5.865l41.784 42.428C45.764 81.174 47.689 82 50 82c2.268 0 4.215-.826 5.84-2.476l41.784-42.428c1.584-1.608 2.376-3.563 2.376-5.865 0-2.26-.792-4.236-2.375-5.932z"/%3E%3C/svg%3E');
}

.select__trigger:focus {
outline: none;
}

.select_show .select__trigger::after {
transform: rotate(180deg);
}

.select__dropdown {
display: none;
position: absolute;
top: 2.5rem;
left: 0;
right: 0;
border: 1px solid #ccc;
max-height: 10rem;
overflow-y: auto;
border-radius: 0.3125rem;
}

.select_show .select__dropdown {
display: block;
}

.select_show .select__backdrop {
display: block;
}

.select__items {
margin: 0;
padding: 0;
list-style: none;
}

.select__item {
padding: 0.375rem 0.75rem;
}

.select__item_selected {
background-color: #e1f5fe;
display: flex;
align-items: center;
justify-content: space-between;
}

.select__item_selected::after {
content: '';
width: 0.75rem;
height: 0.75rem;
color: #0277bd;
background-size: cover;
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" height="100" width="100" class="svg-inline--fa fa-check fa-w-16" data-icon="check" data-prefix="fas" aria-hidden="true"%3E%3Cpath d="M33.964 85.547l-32.5-32.251a4.935 4.935 0 010-7.017l7.071-7.017a5.027 5.027 0 017.071 0L37.5 60.987l46.894-46.534a5.028 5.028 0 017.07 0l7.072 7.017a4.935 4.935 0 010 7.017l-57.5 57.06a5.027 5.027 0 01-7.072 0z" fill="%230277bd"/%3E%3C/svg%3E');
}

.select__item:hover {
background-color: #f5f5f5;
cursor: pointer;
transition: 0.2s background-color ease-in-out;
}

.select__backdrop {
position: fixed;
z-index: -1;
left: 0;
right: 0;
top: 0;
bottom: 0;
display: none;
background-color: transparent;
}
140 changes: 140 additions & 0 deletions custom_select/custom_select.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
(function () {
if (typeof window.CustomEvent === 'function') return false;
function CustomEvent(event, params) {
params = params || { bubbles: false, cancelable: false, detail: null };
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent(
event,
params.bubbles,
params.cancelable,
params.detail
);
return evt;
}
window.CustomEvent = CustomEvent;
})();

const templateSelect = (data = [], defaultText = 'Выберите из списка') => {
let items = [];
data.forEach((item) => {
let classItemSelected = '';
if (item === defaultText) {
classItemSelected = ' select__item_selected';
}
items.push(
`<li class="select__item${classItemSelected}" data-select="item">${item}</li>`
);
});
return `
<div class="select__backdrop" data-select="backdrop"></div>
<button type="button" class="select__trigger" data-select="trigger">
${defaultText}
</button>
<div class="select__dropdown">
<ul class="select__items">
${items.join('')}
</ul>
</div>`;
};

class CustomSelect {
constructor(selector, config) {
this._$main = document.querySelector(selector);
this._config = config;
if (this._config.data) {
this._render();
}
this._$trigger = this._$main.querySelector('[data-select="trigger"]');
this._addEventListener();
}
_isShow() {
return this._$main.classList.contains('select_show');
}
_changeItem(item) {
if (!item.classList.contains('select__item_selected')) {
const itemSelected = this._$main.querySelector('.select__item_selected');
if (itemSelected) {
itemSelected.classList.remove('select__item_selected');
}
item.classList.add('select__item_selected');
this._$trigger.textContent = item.textContent;
this._$main.dispatchEvent(this._changeValue);
this._config.onSelected ? this._config.onSelected(item) : null;
}
}
_eventHandler(e) {
const target = e.target;
const type = target.dataset.select;
if (type === 'trigger') {
this.toggle();
} else if (type === 'item') {
this._changeItem(target);
this.hide();
} else if (type === 'backdrop') {
// закрываем селект, если кликнули вне его
this.hide();
}
}
_addEventListener() {
// привяжем функцию _eventHandler к контексту this
this._eventHandler = this._eventHandler.bind(this);
// добавим слушатель
this._$main.addEventListener('click', this._eventHandler);
this._changeValue = new CustomEvent('select.change');
}
_render() {
// добавляем класс select, если его нет у базового элемента
if (!this._$main.classList.contains('select')) {
this._$main.classList.add('select');
}
this._$main.innerHTML = templateSelect(
this._config['data'],
this._config['defaultValue']
);
}
show() {
this._$main.classList.add('select_show');
}
hide() {
this._$main.classList.remove('select_show');
}
toggle() {
this._isShow() ? this.hide() : this.show();
}
// удаляем слушатели и данный селект из DOM
destroy() {
this._$main.removeEventListener('click', this._eventHandler);
this._$main.innerHTML = '';
}
selectedItem(value) {
if (typeof value === 'object') {
if (value['value']) {
this._$main
.querySelectorAll('[data-select="item"]')
.forEach(($item) => {
if ($item.textContent.trim() === value['value'].toString()) {
this._changeItem($item);
return;
}
});
} else if (value['index'] >= 0) {
const $item = this._$main.querySelectorAll('[data-select="item"]')[
value['index']
];
this._changeItem($item);
}
return this.selectedItem();
}
let indexSelected = -1;
let valueSelected = '';
this._$main
.querySelectorAll('[data-select="item"]')
.forEach(($element, index) => {
if ($element.classList.contains('select__item_selected')) {
indexSelected = index;
valueSelected = $element.textContent;
}
});
return { index: indexSelected, value: valueSelected };
}
}
74 changes: 74 additions & 0 deletions custom_select/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<!doctype html>
<html lang="ru">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Custom select</title>
<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet">
<!-- Подключаем CSS файл CustomSelect -->
<link rel="stylesheet" href="custom_select.css">
<!-- Стили для страницы -->
<style>
body {
font-family: 'Roboto', sans-serif;
}

.container {
max-width: 250px;
margin-left: auto;
margin-right: auto;
}
</style>
</head>

<body>

<div class="container">

<div class="select">
<div class="select__backdrop" data-select="backdrop"></div>
<button type="button" class="select__trigger" data-select="trigger">
Выберите из списка
</button>
<div class="select__dropdown">
<ul class="select__items">
<li class="select__item" data-select="item">1</li>
<li class="select__item select__item_selected" data-select="item">2</li>
<li class="select__item" data-select="item">3</li>
<li class="select__item" data-select="item">4</li>
<li class="select__item" data-select="item">5</li>
<li class="select__item" data-select="item">6</li>
<li class="select__item" data-select="item">7</li>
<li class="select__item" data-select="item">8</li>
<li class="select__item" data-select="item">9</li>
</ul>
</div>
</div>

</div>

<!-- Подключаем js-файл CustomSelect -->
<script src="custom_select.js"></script>

<!-- JS код в котором инициализируем экземпляр CustomSelect для .select -->
<script>
const select1 = new CustomSelect('.select', {
defaultValue: 'Ford',
data: ['Volkswagen', 'Ford', 'Toyota', 'Nissan'],
onSelected(item) {
console.log(`Выбранное значение: ${item.textContent}`);
},
});

document.querySelector('.select').addEventListener('select.change', (e) => {
console.log(
`Выбранное значение: ${
e.target.querySelector('.select__item_selected').textContent
}`
);
});
</script>
</body>

</html>

0 comments on commit fa456d7

Please sign in to comment.