From 5452195723338ebc372dc87d8a4749f903022d90 Mon Sep 17 00:00:00 2001 From: PietroBossolasco Date: Wed, 1 Nov 2023 23:26:17 +0100 Subject: [PATCH] Release v.1.0.0 --- README.md | 81 +++++++---- source/text-selector.css | 13 +- source/text-selector.js | 267 ++++++++++++++++++++++++----------- styles.css | 6 - text-selector-v0-1-1.min.css | 3 - text-selector-v0-1-1.min.js | 3 - text-selector-v1-0-0.min.css | 3 + text-selector-v1-0-0.min.js | 3 + 8 files changed, 248 insertions(+), 131 deletions(-) delete mode 100644 styles.css delete mode 100644 text-selector-v0-1-1.min.css delete mode 100644 text-selector-v0-1-1.min.js create mode 100644 text-selector-v1-0-0.min.css create mode 100644 text-selector-v1-0-0.min.js diff --git a/README.md b/README.md index ac4098b..d1daa6c 100644 --- a/README.md +++ b/README.md @@ -2,64 +2,83 @@ ![GitHub License](https://img.shields.io/badge/license-MIT-blue.svg) -The Text-Selector library is a JavaScript library that allows you to create custom select elements with a text label that, when clicked, displays a list of options. It provides an alternative and visually appealing way to present and interact with select options. +The Text-Selector library is a JavaScript library that enables you to enhance the user experience with select elements. It allows you to replace standard select elements with custom-designed text and provides the ability to open and close the selection dynamically. This library also supports animations when jQuery is included. ## Features -* Show a list of selectable options when the text label is clicked. -* Easy to initialize with a simple function call. +* Easily transform standard select elements into custom-designed text elements. +* Customize the text with specific CSS classes and text element tags. +* Animations for opening and closing the selector (requires jQuery). +* Simplified initialization with a single function call. ## Installation -To use the Text-Selector library, you can include the JavaScript file in your HTML: +To use the Text-Selector library, follow these steps: + +1. Include the text-selector.min.js JavaScript file and the text-selector.min.css CSS file in your HTML. +1. Optionally, include jQuery if you want to enable animations for opening and closing the selector. Make sure to include jQuery before the Text-Selector library. + ```html - + + + ``` -Make sure to include the library after the document is loaded. + ## Usage -* Include the text-selector.js file in your HTML. -* Create a standard select element and an associated text element. Assign id attributes to both elements. +1. In your HTML document, insert a select element with the class text-selector. You can further customize the text by using the text-class attribute to add specific CSS classes and the text-element attribute to specify the HTML tag for the text (default is p). ```html - + + + + - -
Click me to select an option
``` -* Initialize the Text-Selector library by calling the textselectorinit function, passing an array of objects that define the text and select element pairs: -```js -textselectorinit([ - { text: 'myText', data: 'mySelect' } -]); +1. Initialize the Text-Selector library by calling the textSelector.init() function. This function should be called after the document has loaded. + +```html + ``` -Your custom text-selector element is now ready to use. When you click on the text element, it will display the select options. + +1. Your custom Text-Selector elements are now ready to use. When you click on the custom text, the selector will dynamically open and display the available options. ## Example ```html - + + + + Text-Selector Example + + + + - + + + + + + -
Click me to select an option
- - + + ``` ## License diff --git a/source/text-selector.css b/source/text-selector.css index 87434fe..0a1125d 100644 --- a/source/text-selector.css +++ b/source/text-selector.css @@ -1,10 +1,16 @@ -@import url(https://fonts.googleapis.com/css2&family=Roboto&display=swap); -.text-selector { +/* text-selector V0.1.1 - Made by Pietro Bossolasco */ +/* For docs -> https://github.com/PietroBossolasco/text-selector */ +@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@100;400&display=swap'); +.text-selector{ + display: none; +} +.text-selector-text { user-select: none; cursor: pointer; } .text-selector-selector { position: absolute; + z-index: 1001; display: none; padding: 10px; width: 200px; @@ -47,3 +53,6 @@ .text-selector-selected { background-color: #e2e2e2; } +.text-selector-text-disabled{ + cursor: default !important; +} \ No newline at end of file diff --git a/source/text-selector.js b/source/text-selector.js index b6ade66..51245db 100644 --- a/source/text-selector.js +++ b/source/text-selector.js @@ -1,93 +1,188 @@ -// text-selector V0.1.1 - Made by Pietro Bossolasco +// text-selector V1.0.0 - Made by Pietro Bossolasco // For docs -> https://github.com/PietroBossolasco/text-selector -function textselectorinit(e) { - (s = document.createElement("div")).classList.add("text-selector-selector"), - s.addEventListener("mouseleave", function () { - (canc = !1), - s.addEventListener("mouseover", () => { - canc = !0; - }), - setTimeout(() => { - if (!canc) - try { - $(".text-selector-selector").eq(0).fadeOut(100); - } catch (e) { - this.style.display = "none"; +const textSelector = { + s: null, // div with option + elements: [], // created + d: null, // select with text-selector class + init: function () { + this.d = document.getElementsByClassName("text-selector"); + for (let key in this.d) { + if (this.d[key].type == "select-one") { + try { + if (!this.d[key].classList.contains("text-selector-initialized")) { + let ccl = this.d[key].getAttribute("text-class"); + let ce = this.d[key].getAttribute("text-element"); + let id = this.generateId(); + ce = ce == null ? "p" : ce; + ccl = ccl === null ? "" : ccl; + let a = document.createElement(ce); + a.classList = ccl + " text-selector-text"; + a.innerText = this.d[key].options[this.d[key].selectedIndex].text; + a.setAttribute("text-selector-id", id); + this.d[key].insertAdjacentElement("afterend", a); + a.addEventListener("click", this.clickHandler); + let c = document.createElement("div"); + c.classList = "text-selector-selector"; + c.setAttribute("text-selector-id", id); + this.d[key].addEventListener("change", function () { + console.log(this) + a.innerText = this.options[this.selectedIndex].text; + }); + a.insertAdjacentElement("afterend", c); + this.elements.push({ + id: id, + select: this.d[key], + text: a, + selector: c, + }); + this.d[key].classList.add("text-selector-initialized"); + console.log(this.d[key].disabled) + let ausKey = key; + Object.freeze(ausKey); + const observercallback = () => { + if(this.d[ausKey].disabled && !a.classList.contains("text-selector-text-disabled")){ + a.classList.add("text-selector-text-disabled"); + } + else if(!this.d[ausKey].disabled && a.classList.contains("text-selector-text-disabled")){ + a.classList.remove("text-selector-text-disabled"); + } + console.log(ausKey) } - canc = !1; - }, 500); - }), - document.getElementsByTagName("body")[0].appendChild(s); - for (let l = 0; l < e.length; l++) { - let i = l; - Object.freeze(i), - (t = document.getElementById(e[l].text)).classList.add( - "text-selector-text" - ), - -1 !== (d = document.getElementById(e[l].data)).selectedIndex && - (t.innerText = d.options[d.selectedIndex].text), - (d.style.display = "none"); - try { - t.addEventListener("click", function () { - if (!(d = document.getElementById(e[i].data)).disabled) { - (t = document.getElementById(e[i].text)), - (c = d.childNodes), - (aus = Array.from(c)), - (s.innerHTML = ""); - var l = document.createElement("div"); - l.classList.add("text-selector-content"), - c.forEach(function (e) { - e.innerText && - ((sel = document.createElement("div")), - e.disabled - ? (sel.classList = - "text-selector-selection text-selector-disabled") - : e.selected - ? (sel.classList = - "text-selector-selection text-selector-selected") - : (sel.classList = - "text-selector-selection text-selector-normal"), - (sel.innerText = e.innerText), - e.disabled || - sel.addEventListener("click", function () { - (ex = document.getElementsByClassName( - "text-selector-selected" - )), - null != ex[0] && - (ex[0].classList = - "text-selector-selection text-selector-normal"), - (v = this.innerText), - (this.classList = - "text-selector-selection text-selector-selected"), - (d.selectedIndex = aus.indexOf(e) - 1), - (t.innerText = v); - }), - l.appendChild(sel)); - }), - (s.innerHTML = ""), - s.appendChild(l); + const configObserver = { attributes: true, childList: true, subtree: true }; + const observer = new MutationObserver(observercallback); + observer.observe(this.d[key], configObserver); + } + } catch (ex) { + console.error("Error during the initialization\n", ex); + } + } + } + }, + generateId: function () { + const characters = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + let result = ""; + let ok = false; + let length = 10; + while (!ok) { + for (let i = 0; i < length; i++) { + const randomIndex = Math.floor(Math.random() * characters.length); + result += characters.charAt(randomIndex); + } + if (!this.isIdInArray(result)) { + ok = true; + } + } + return result; + }, + isIdInArray: function (id) { + return this.elements.some((item) => item.id === id); + }, + findElementById: function (id) { + return this.elements.find((item) => item.id === id); + }, + removeAllContainers: function () { + this.elements.forEach((item) => { + if (item.selector.style.display != "none") { + try { + $(item.selector).fadeOut(100); + } catch (ex) { + item.selector.style.display = "none"; + } + } + }); + }, + mouseLeaveHandler: function (item) { + let isMouseInside = false; + + item.addEventListener("mouseenter", () => { + isMouseInside = true; + }); + + item.addEventListener("mouseleave", () => { + isMouseInside = false; + setTimeout(() => { + if (!isMouseInside) { try { - $(".text-selector-selector").eq(0).fadeIn(100); - } catch (n) { - s.style.display = "block"; + $(item).fadeOut(100); + } catch (ex) { + item.style.display = "none"; } - let o = this.offsetLeft, - r = this.offsetTop; - r > s.offsetHeight - ? ((s.style.paddingBottom = this.offsetHeight + 10 + "px"), - (s.style.top = r - s.offsetHeight + this.offsetHeight + "px"), - (s.style.paddingTop = "10px")) - : ((s.style.paddingBottom = "10px"), - (s.style.top = this.offsetHeight + "px"), - (s.style.paddingTop = this.offsetHeight + 10 + "px")), - o > s.offsetWidth / 2 + 10 - ? (s.style.left = - o + this.offsetWidth / 2 - s.offsetWidth / 2 + "px") - : (s.style.left = "10px"); + } + }, 500); + }); + }, + clickHandler: function (e) { + textSelector.removeAllContainers(); + let data = textSelector.findElementById( + e.target.getAttribute("text-selector-id") + ); + if (!data.select.disabled) { + data.selector.addEventListener( + "mouseleave", + textSelector.mouseLeaveHandler(data.selector) + ); + data.selector.innerHTML = ""; + let cn = Array.from(data.select.childNodes); + let content = document.createElement("div"); + content.classList.add("text-selector-content"); + cn.forEach((item) => { + if (item.innerText) { + let opt = document.createElement("div"); + if (item.disabled) { + opt.classList = "text-selector-selection text-selector-disabled"; + } else if (item.selected) { + opt.classList = "text-selector-selection text-selector-selected"; + } else { + opt.classList = "text-selector-selection text-selector-normal"; + } + opt.innerText = item.innerText; + if (!item.disabled) { + opt.addEventListener("click", function () { + ex = data.selector.getElementsByClassName( + "text-selector-selected" + ); + if (ex[0] != null) { + ex[0].classList = + "text-selector-selection text-selector-normal"; + } + v = this.innerText; + this.classList = "text-selector-selection text-selector-selected"; + item.selected = true; + data.text.innerText = v; + }); + } + content.appendChild(opt); } }); - } catch (n) { - alert("Error in the text-selector configuration"), console.error(n); + + data.selector.appendChild(content); + try { + $(data.selector).fadeIn(100); + } catch (ex) { + data.selector.style.display = "block"; + } + textSelector.calculatePosition(data, this); + } + }, + calculatePosition: function (data, element) { + const x = element.offsetLeft; + const y = element.offsetTop; + if (y > data.selector.offsetHeight) { + data.selector.style.paddingBottom = element.offsetHeight + 10 + "px"; + data.selector.style.top = + y - data.selector.offsetHeight + element.offsetHeight + "px"; + data.selector.style.paddingTop = "10px"; + } else { + data.selector.style.paddingBottom = "10px"; + data.selector.style.top = y + "px"; + data.selector.style.paddingTop = element.offsetHeight + 10 + "px"; + } + if (x > data.selector.offsetWidth / 2 + 10) { + data.selector.style.left = + x + element.offsetWidth / 2 - data.selector.offsetWidth / 2 + "px"; + } else { + data.selector.style.left = x + "px"; } - } -} + }, +}; diff --git a/styles.css b/styles.css deleted file mode 100644 index 1a16728..0000000 --- a/styles.css +++ /dev/null @@ -1,6 +0,0 @@ -body{ - display: flex; - align-items: center; - justify-content: center; - height: 100vh; -} \ No newline at end of file diff --git a/text-selector-v0-1-1.min.css b/text-selector-v0-1-1.min.css deleted file mode 100644 index f8dd2c1..0000000 --- a/text-selector-v0-1-1.min.css +++ /dev/null @@ -1,3 +0,0 @@ -/* text-selector V0.1.1 - Made by Pietro Bossolasco */ -/* For docs -> https://github.com/PietroBossolasco/text-selector */ -@import url(https://fonts.googleapis.com/css2&family=Roboto&display=swap);.text-selector-text{user-select:none;cursor:pointer}.text-selector-selector{position:absolute;display:none;padding:10px;width:200px}.text-selector-content{background-color:#fff;border-radius:5px;box-shadow:rgba(0,0,0,.05) 0 6px 24px 0,rgba(0,0,0,.08) 0 0 0 1px}.text-selector-selection{padding:5px 20px;min-height:25px;line-height:25px;user-select:none;transition-duration:.2s;font-family:Roboto,sans-serif;border-bottom:1px solid #efefef}.text-selector-selection:first-child{border-top-left-radius:5px;border-top-right-radius:5px}.text-selector-selection:last-child{border-bottom-left-radius:5px;border-bottom-right-radius:5px;border-bottom:none}.text-selector-disabled{color:gray}.text-selector-disabled:hover{background-color:#fff!important;cursor:default!important}.text-selector-selection:hover{cursor:pointer;transition-duration:.2s;background-color:#e2e2e2}.text-selector-selected{background-color:#e2e2e2} \ No newline at end of file diff --git a/text-selector-v0-1-1.min.js b/text-selector-v0-1-1.min.js deleted file mode 100644 index 1824bc2..0000000 --- a/text-selector-v0-1-1.min.js +++ /dev/null @@ -1,3 +0,0 @@ -// text-selector V0.1.1 - Made by Pietro Bossolasco -// For docs -> https://github.com/PietroBossolasco/text-selector -function textselectorinit(e){(s=document.createElement("div")).classList.add("text-selector-selector"),s.addEventListener("mouseleave",function(){canc=!1,s.addEventListener("mouseover",()=>{canc=!0}),setTimeout(()=>{if(!canc)try{$(".text-selector-selector").eq(0).fadeOut(100)}catch(e){this.style.display="none"}canc=!1},500)}),document.getElementsByTagName("body")[0].appendChild(s);for(let l=0;ls.offsetHeight?(s.style.paddingBottom=this.offsetHeight+10+"px",s.style.top=r-s.offsetHeight+this.offsetHeight+"px",s.style.paddingTop="10px"):(s.style.paddingBottom="10px",s.style.top=this.offsetHeight+"px",s.style.paddingTop=this.offsetHeight+10+"px"),o>s.offsetWidth/2+10?s.style.left=o+this.offsetWidth/2-s.offsetWidth/2+"px":s.style.left="10px"}})}catch(n){alert("Error in the text-selector configuration"),console.error(n)}}} \ No newline at end of file diff --git a/text-selector-v1-0-0.min.css b/text-selector-v1-0-0.min.css new file mode 100644 index 0000000..f47ec9b --- /dev/null +++ b/text-selector-v1-0-0.min.css @@ -0,0 +1,3 @@ +/* text-selector V1.0.0 - Made by Pietro Bossolasco */ +/* For docs -> https://github.com/PietroBossolasco/text-selector */ +@import url(https://fonts.googleapis.com/css2?family=Roboto:wght@100;400&display=swap);.text-selector{display:none}.text-selector-text{user-select:none;cursor:pointer}.text-selector-selector{position:absolute;z-index:1001;display:none;padding:10px;width:200px}.text-selector-content{background-color:#fff;border-radius:5px;box-shadow:rgba(0,0,0,.05) 0 6px 24px 0,rgba(0,0,0,.08) 0 0 0 1px}.text-selector-selection{padding:5px 20px;min-height:25px;line-height:25px;user-select:none;transition-duration:.2s;font-family:Roboto,sans-serif;border-bottom:1px solid #efefef}.text-selector-selection:first-child{border-top-left-radius:5px;border-top-right-radius:5px}.text-selector-selection:last-child{border-bottom-left-radius:5px;border-bottom-right-radius:5px;border-bottom:none}.text-selector-disabled{color:gray}.text-selector-disabled:hover{background-color:#fff!important;cursor:default!important}.text-selector-selection:hover{cursor:pointer;transition-duration:.2s;background-color:#e2e2e2}.text-selector-selected{background-color:#e2e2e2}.text-selector-text-disabled{cursor:default!important} \ No newline at end of file diff --git a/text-selector-v1-0-0.min.js b/text-selector-v1-0-0.min.js new file mode 100644 index 0000000..88069b9 --- /dev/null +++ b/text-selector-v1-0-0.min.js @@ -0,0 +1,3 @@ +// text-selector V1.0.0 - Made by Pietro Bossolasco +// For docs -> https://github.com/PietroBossolasco/text-selector +const textSelector={s:null,elements:[],d:null,init:function(){for(let e in this.d=document.getElementsByClassName("text-selector"),this.d)if("select-one"==this.d[e].type)try{if(!this.d[e].classList.contains("text-selector-initialized")){let t=this.d[e].getAttribute("text-class"),s=this.d[e].getAttribute("text-element"),l=this.generateId();s=null==s?"p":s,t=null===t?"":t;let i=document.createElement(s);i.classList=t+" text-selector-text",i.innerText=this.d[e].options[this.d[e].selectedIndex].text,i.setAttribute("text-selector-id",l),this.d[e].insertAdjacentElement("afterend",i),i.addEventListener("click",this.clickHandler);let n=document.createElement("div");n.classList="text-selector-selector",n.setAttribute("text-selector-id",l),this.d[e].addEventListener("change",function(){console.log(this),i.innerText=this.options[this.selectedIndex].text}),i.insertAdjacentElement("afterend",n),this.elements.push({id:l,select:this.d[e],text:i,selector:n}),this.d[e].classList.add("text-selector-initialized"),console.log(this.d[e].disabled);let o=e;Object.freeze(o);let c=()=>{this.d[o].disabled&&!i.classList.contains("text-selector-text-disabled")?i.classList.add("text-selector-text-disabled"):!this.d[o].disabled&&i.classList.contains("text-selector-text-disabled")&&i.classList.remove("text-selector-text-disabled"),console.log(o)},r={attributes:!0,childList:!0,subtree:!0},d=new MutationObserver(c);d.observe(this.d[e],r)}}catch(a){console.error("Error during the initialization\n",a)}},generateId:function(){let e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",t="",s=!1;for(;!s;){for(let l=0;l<10;l++){let i=Math.floor(Math.random()*e.length);t+=e.charAt(i)}this.isIdInArray(t)||(s=!0)}return t},isIdInArray:function(e){return this.elements.some(t=>t.id===e)},findElementById:function(e){return this.elements.find(t=>t.id===e)},removeAllContainers:function(){this.elements.forEach(e=>{if("none"!=e.selector.style.display)try{$(e.selector).fadeOut(100)}catch(t){e.selector.style.display="none"}})},mouseLeaveHandler:function(e){let t=!1;e.addEventListener("mouseenter",()=>{t=!0}),e.addEventListener("mouseleave",()=>{t=!1,setTimeout(()=>{if(!t)try{$(e).fadeOut(100)}catch(s){e.style.display="none"}},500)})},clickHandler:function(e){textSelector.removeAllContainers();let t=textSelector.findElementById(e.target.getAttribute("text-selector-id"));if(!t.select.disabled){t.selector.addEventListener("mouseleave",textSelector.mouseLeaveHandler(t.selector)),t.selector.innerHTML="";let s=Array.from(t.select.childNodes),l=document.createElement("div");l.classList.add("text-selector-content"),s.forEach(e=>{if(e.innerText){let s=document.createElement("div");e.disabled?s.classList="text-selector-selection text-selector-disabled":e.selected?s.classList="text-selector-selection text-selector-selected":s.classList="text-selector-selection text-selector-normal",s.innerText=e.innerText,e.disabled||s.addEventListener("click",function(){null!=(ex=t.selector.getElementsByClassName("text-selector-selected"))[0]&&(ex[0].classList="text-selector-selection text-selector-normal"),v=this.innerText,this.classList="text-selector-selection text-selector-selected",e.selected=!0,t.text.innerText=v}),l.appendChild(s)}}),t.selector.appendChild(l);try{$(t.selector).fadeIn(100)}catch(i){t.selector.style.display="block"}textSelector.calculatePosition(t,this)}},calculatePosition:function(e,t){let s=t.offsetLeft,l=t.offsetTop;l>e.selector.offsetHeight?(e.selector.style.paddingBottom=t.offsetHeight+10+"px",e.selector.style.top=l-e.selector.offsetHeight+t.offsetHeight+"px",e.selector.style.paddingTop="10px"):(e.selector.style.paddingBottom="10px",e.selector.style.top=l+"px",e.selector.style.paddingTop=t.offsetHeight+10+"px"),s>e.selector.offsetWidth/2+10?e.selector.style.left=s+t.offsetWidth/2-e.selector.offsetWidth/2+"px":e.selector.style.left=s+"px"}}; \ No newline at end of file