diff --git a/README.md b/README.md index 1224262..1f3932b 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# my-select \ No newline at end of file +# my-select - Плагин \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..a1e608a --- /dev/null +++ b/index.html @@ -0,0 +1,24 @@ + + + + + + + Document + + + +
+
+ +
+
+ + + \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..d14b977 --- /dev/null +++ b/index.js @@ -0,0 +1,10 @@ +import { Select } from './select/select'; +import './select/index'; + +const select = new Select('#select', { + placeholder: "Выберите дату", + selectedId: 5, + data: ["Выбор 1", "Выбор 2", "Выбор 3"] +}); + +window.s = select; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..910b5f8 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,146 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "picomatch": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", + "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", + "dev": true + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "sass": { + "version": "1.32.11", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.11.tgz", + "integrity": "sha512-O9tRcob/fegUVSIV1ihLLZcftIOh0AF1VpKgusUfLqnb2jQ0GLDwI5ivv1FYWivGv8eZ/AwntTyTzjcHu0c/qw==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0" + } + }, + "select": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", + "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..a8550a2 --- /dev/null +++ b/package.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "select": "^1.1.2" + }, + "devDependencies": { + "sass": "^1.32.11" + } +} diff --git a/select/index.scss b/select/index.scss new file mode 100644 index 0000000..6c2a837 --- /dev/null +++ b/select/index.scss @@ -0,0 +1,123 @@ +:root { + --my-select-border: #ccc; + --my-select-value-height: 48px; + --my-select-active: red; + --my-select-hover: #eee; +} +@import url('https://fonts.googleapis.com/css2?family=Open+Sans&display=swap'); + +body { + font-family: 'Open Sans', sans-serif; + padding: 0; +} + +* { + box-sizing: border-box; +} + +.app { + padding-top: 5px; + display: flex; + justify-content: center; +} + +.wrapper { + max-width: 500px; + width: 100%; +} + +.my-select { + width: 100%; + position: relative; + z-index: 100; + + &__control { + border: 1px solid var(--my-select-border); + height: var(--my-select-value-height); + border-radius: 5px; + display: flex; + align-items: center; + padding: 0 10px; + cursor: pointer; + } + + &__backdrop { + position: fixed; + left: 0; + top: 0; + right: 0; + bottom: 0; + z-index: -1; + display: none; + } + + &__dropdown { + position: absolute; + max-height: calc(var(--my-select-value-height) * 5); + overflow-y: auto; + width: 100%; + display: none; + border: 1px solid var(--my-select-border); + + &::-webkit-scrollbar { + width: 2px; + } + + &::-webkit-scrollbar-button { + display: none; + } + + &::-webkit-scrollbar-corner { + background-color: transparent; + width: 0; + height: 0; + } + + &::-webkit-scrollbar-thumb { + background-clip: content-box; + background-color: #a0a4a8; + border: 2px solid red; + border-radius: 0px; + } + } + + &__list { + list-style: none; + padding: 0; + margin: 0; + } + + &__item { + height: var(--my-select-value-height); + display: flex; + align-items: center; + padding: 0 10px; + transition: 0.3s; + + &:not(:last-child) { + border-bottom: 1px solid var(--my-select-border); + } + + &:hover { + background-color: var(--my-select-hover); + cursor: pointer; + } + + &--active { + background-color: var(--my-select-active);; + } + } + + &--open &__control { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + border-bottom-color: transparent; + } + + &--open &__dropdown { + display: block; + } + &--open &__backdrop { + display: block; + } +} \ No newline at end of file diff --git a/select/select.js b/select/select.js new file mode 100644 index 0000000..ff590c9 --- /dev/null +++ b/select/select.js @@ -0,0 +1,134 @@ +const getTemplate = (data = [], placeholder, selectedId) => { + let textPlaceholder = placeholder ?? 'Выберите значение'; + + const items = data.map(item => { + let cls = ''; + + if(item.id === selectedId) { + textPlaceholder = item.value; + cls = 'my-select__item--active' + } + return `
  • ${item.value}
  • ` + }); + + + return ` +
    +
    +
    + ${textPlaceholder} +
    +
    + +
    +
    + ` +} + + +export class Select { + + constructor(selector, options) { + this.$el = document.querySelector(selector); + this.options = options; + this.isOption = options.isOption ?? true; + this.selectedId = options.selectedId; + this.optionsList = []; + + this.#render(); + this.#setup() + } + + #render() { + const {placeholder} = this.options; + const {data} = this.options; + const selectOptions = this.$el.options; + + if(this.isOption) { + for(var i = 0; i < selectOptions.length; i++) { + this.optionsList[i] = {id: i , value: selectOptions.item(i).text} + } + } + + if(data) { + var counter = this.optionsList.length; + for(var i = 0; i < data.length; i++) { + this.optionsList[counter] = {id: counter, value: data[i]}; + + counter++ + } + } + + this.$el.insertAdjacentHTML('afterend', getTemplate(this.optionsList , placeholder, this.selectedId)); + } + + #setup() { + this.clickHandler = this.clickHandler.bind(this); + this.$select = this.$el.nextElementSibling; + this.$item = this.$select.querySelectorAll('[data-type="item"]'); + this.$value = this.$select.querySelector('[data-type="value"]'); + this.$select.addEventListener('click', this.clickHandler); + this.$el.setAttribute('style' ,"display: none" ) + + } + + get isOpen() { + return this.$select.classList.contains('my-select--open') + } + + get current() { + return this.optionsList + } + + + clickHandler(event) { + const {type} = event.target.dataset; + + if(type === 'input') { + this.toogle() + } + + else if (type === 'item') { + const id = event.target.dataset.id + this.select(id) + } + + else if (type === 'backdrop') { + this.close() + } + + } + + + select(id) { + this.selectedId = id; + this.$value.textContent = this.current[id].value; + + this.$select.querySelectorAll(`[data-type='item']`).forEach(element => { + element.classList.remove('my-select__item--active') + }); + this.$select.querySelector(`[data-id='${id}']`).classList.add('my-select__item--active'); + this.close(); + } + + + toogle() { + this.isOpen ? this.close() : this.open() + } + + open() { + this.$select.classList.add('my-select--open') + } + + close() { + this.$select.classList.remove('my-select--open') + } + + destroy() { + this.$select.removeEventListener('click', this.clickHandler); + this.$el.removeAttribute('style') + this.$select.remove() + } +} \ No newline at end of file