@leoncik/p14-hrnet-plugin
Version:
A custom select menu component made for my 14th OpenClassrooms project
21 lines (20 loc) • 12.5 kB
JavaScript
import React,{useState,useEffect,useRef}from"react";import styled from"styled-components";function styleInject(e,t){var n,o,t=(t=void 0===t?{}:t).insertAt;e&&"undefined"!=typeof document&&(n=document.head||document.getElementsByTagName("head")[0],(o=document.createElement("style")).type="text/css","top"===t&&n.firstChild?n.insertBefore(o,n.firstChild):n.appendChild(o),o.styleSheet?o.styleSheet.cssText=e:o.appendChild(document.createTextNode(e)))}var css_248z="*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n.select-menu {\n position: relative;\n}\n\n/* CUSTOM BUTTON */\n\n.custom-button {\n /* original JQuery CSS */\n text-align: left;\n white-space: nowrap;\n /* width: 14em; */\n border-radius: 3px;\n text-decoration: none;\n border: 1px solid #c5c5c5;\n /* background: #f6f6f6; */\n font-weight: normal;\n font-size: 1rem;\n padding: 0.4em 1em;\n display: inline-block;\n position: relative;\n line-height: normal;\n margin-right: 0.1em;\n cursor: pointer;\n vertical-align: middle;\n text-align: center;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n overflow: visible;\n /* Added CSS */\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n/* .custom-button:hover {\n border: 1px solid #cccccc;\n font-weight: normal;\n}\n.custom-button:active {\n border: 1px solid #003eff;\n background: #006cd8;\n font-weight: normal;\n} */\n\n.button-disabled {\n opacity: 0.45;\n pointer-events: none;\n}\n\n/* Prevent possible conflicts with the function that is closing the select menu when clicking outside. */\n.custom-button > * {\n pointer-events: none;\n}\n\n.custom-button-icon_rotated {\n transform: rotate(180deg);\n}\n\n.custom-button-text {\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n/* MENU */\n\n.menu-wrapper {\n /* original JQuery CSS */\n display: block;\n padding: 0;\n margin: 0;\n position: absolute;\n z-index: 100;\n}\n.menu {\n /* Original CSS style from JQuery */\n border: 1px solid #c5c5c5;\n overflow: auto;\n overflow-x: auto;\n overflow-x: hidden;\n padding-bottom: 1px;\n border-radius: 0 0 3px 3px;\n background: #ffffff;\n color: #333333;\n list-style: none;\n padding: 0;\n padding-bottom: 0px;\n margin: 0;\n outline: 0;\n /* Added CSS */\n display: none;\n}\n\n.menu-close {\n display: none;\n}\n\n.menu-open {\n display: block;\n}\n\n/* MENU ITEMS */\n\n.menu-item {\n cursor: pointer;\n padding: 3px 1em 3px 0.4em;\n}\n/* .menu-item:hover,\n.menu-item:focus {\n font-weight: normal;\n color: #ffffff;\n} */\n";styleInject(css_248z);const Button=styled.span`
color: ${({textColor:e})=>e};
&:hover {
color: ${({textHoverColor:e})=>e};
background: ${({hoverBackgroundColor:e})=>e};
}
&:active {
background: ${({activeBackgroundColor:e})=>e};
color: ${({textActiveColor:e})=>e};
}
`,CustomButton=({options:t,optionsValues:o,label:e,customButtonRef:n,customMenuRef:i,selectedOptionRef:r,width:l,id:a,selectedOption:s,optionIndex:c,incrementOptionIndex:d,updateNextOptionWithIndex:u,updateLastOptionWithIndex:p,setToLastOptionIndex:m,setToLFirstOptionIndex:x,updateFirstOptionWithIndex:f,decrementOptionIndex:h,updatePreviousOptionWithIndex:g,disabled:b,buttonIconPath:C,showButtonIcon:v,hiddenSelectRef:S,isIconRotated:w,openSelectMenu:E,closeSelectMenu:R,mainColor:k,buttonTextColor:y,buttonTextHoverColor:O,buttonBackgroundHoverColor:I,buttonTextActiveColor:T})=>{const B=(null==t?void 0:t[0])||"",A=()=>{var e;null!=(e=null==n?void 0:n.current)&&e.classList.contains("menu-expanded")?null!=(e=null==n?void 0:n.current)&&e.classList.contains("menu-expanded")&&R():E(),(s||i.current.firstChild).focus()},N=e=>{switch(e.code){case"Enter":case"Space":A();break;case"ArrowUp":case"ArrowLeft":(n=e).target.nextElementSibling.firstChild.children[c-1]&&r.current&&(r.current.textContent=n.target.nextElementSibling.firstChild.children[c-1].textContent,g(n),h(),S.current.firstChild.textContent=n.target.nextElementSibling.firstChild.children[c-1].textContent,S.current.firstChild.value=o?n.target.nextElementSibling.firstChild.children[c-1].getAttribute("data-option-value"):n.target.nextElementSibling.firstChild.children[c-1].textContent);break;case"ArrowDown":case"ArrowRight":(n=e).target.nextElementSibling.firstChild.children[c+1]&&r.current&&(r.current.textContent=n.target.nextElementSibling.firstChild.children[c+1].textContent,u(n),d(),S.current.firstChild.textContent=n.target.nextElementSibling.firstChild.children[c+1].textContent,S.current.firstChild.value=o?n.target.nextElementSibling.firstChild.children[c+1].getAttribute("data-option-value"):n.target.nextElementSibling.firstChild.children[c+1].textContent);break;case"PageDown":case"End":t=e,r.current&&(r.current.textContent=t.target.nextElementSibling.firstChild.lastChild.textContent,p(t),m(),S.current.firstChild.textContent=t.target.nextElementSibling.firstChild.lastChild.textContent,S.current.firstChild.value=o?t.target.nextElementSibling.firstChild.lastChild.textContent.getAttribute("data-option-value"):t.target.nextElementSibling.firstChild.lastChild.textContent);break;case"PageUp":case"Home":t=e,r.current&&(r.current.textContent=t.target.nextElementSibling.firstChild.firstChild.textContent,f(t),x(),S.current.firstChild.textContent=t.target.nextElementSibling.firstChild.firstChild.textContent,S.current.firstChild.value=o?t.target.nextElementSibling.firstChild.firstChild.textContent.getAttribute("data-option-value"):t.target.nextElementSibling.firstChild.firstChild.textContent)}var t,n};return React.createElement(Button,{activeBackgroundColor:k,hoverBackgroundColor:I,textColor:y,textHoverColor:O,textActiveColor:T,tabIndex:b?-1:0,onKeyDown:e=>{0<t.length&&N(e)},ref:n,onClick:()=>0<t.length&&A(),className:b?"button-disabled custom-button menu-unexpanded ui-selectmenu-button":"custom-button menu-unexpanded ui-selectmenu-button",style:{width:l+"px"},role:"combobox","aria-expanded":"false","aria-autocomplete":"list","aria-haspopup":"true","aria-owns":a?a+"-menu":"","aria-live":"polite","aria-label":null!=(k=null==r?void 0:r.current)&&k.textContent?"Option sélectionnée : "+r.current.textContent:e||B,"data-testid":"custom-button"},React.createElement("span",{ref:r,className:"custom-button-text","data-testid":"custom-button-text"},e||B),React.createElement("img",{alt:"","aria-hidden":"true",src:v?C:"",className:"custom-button-icon "+(w?"custom-button-icon_rotated":"")}))},Option=styled.li`
color: ${({initialTextColor:e})=>e};
width: ${({optionWidth:e})=>e+"px"};
font-size: ${({optionFontSize:e})=>e?""+e:""};
&:hover,
&:focus {
color: ${({hoverTextColor:e})=>e};
background: ${({activeBackgroundColor:e})=>e};
}
`,CustomMenu=({options:e,customMenuRef:t,handleSelectOption:n,width:o,customButtonRef:i,saveOption:r,saveOptionIndex:l,maxHeight:a,scrollable:s,offsetX:c,offsetY:d,optionsValues:u,closeSelectMenu:p,optionsFontSize:m,mainColor:x,optionTextColor:f,optionTextFocus:h,id:g})=>{const[b,C]=useState(0),v=(useEffect(()=>{if(i.current){const e=new ResizeObserver(()=>{i.current&&C(i.current.offsetHeight)});return e.observe(i.current),()=>{i.current?e.unobserve(i.current):e.disconnect()}}},[]),e=>{switch(e.code){case"Escape":case"Tab":null!=(t=null==i?void 0:i.current)&&t.classList.contains("menu-expanded")&&(p(),i.current.focus()),e.preventDefault();break;case"Enter":case"Space":n(e),r(e),l(e);break;case"ArrowUp":case"ArrowLeft":e.target.previousElementSibling&&e.target.previousElementSibling.focus();break;case"ArrowDown":case"ArrowRight":e.target.nextElementSibling&&e.target.nextElementSibling.focus();break;case"PageDown":case"End":e.target.parentNode.lastChild.focus();break;case"PageUp":case"Home":e.target.parentNode.firstChild.focus()}var t});return React.createElement("div",{className:"menu-wrapper",style:{width:o+"px",top:b+d+"px",left:0+c+"px"}},React.createElement("ul",{ref:t,"data-testid":"menu",className:"menu",id:g?g+"-menu":"",style:{width:o+"px",maxHeight:s?a+"px":"auto"}},null==e?void 0:e.map((e,t)=>React.createElement(Option,{initialTextColor:f,activeBackgroundColor:x,hoverTextColor:h,optionWidth:o,optionFontSize:m,tabIndex:0,onKeyDown:e=>{v(e)},onClick:e=>{n(e),r(e),l(e)},onMouseOver:e=>e.target.focus(),className:"menu-item",key:t,"data-option-value":u?u[t]:e,"data-testid":"menu-item"},e))))},HiddenSelect=({hiddenSelectRef:e,id:t,hiddenOptionText:n,hiddenOptionValue:o,inputRef:i,optionsValues:r})=>React.createElement("select",{ref:e,name:"hidden-select",id:t,style:{display:"none"}},React.createElement("option",{ref:i,value:r?o:n},n));var img="data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-chevron-down'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e";const SelectMenu=({label:e,id:t,options:n=[],inputRef:o,width:i=210,className:r,disabled:l=!1,maxHeight:a=200,scrollable:s=!0,offsetX:c=0,offsetY:d=0,buttonIconPath:u=img,showButtonIcon:p=!0,optionsValues:m,rotateButtonIcon:x=!0,optionsFontSize:f,mainColor:h="#007fff",optionTextColor:g="black",optionTextFocus:b="white",buttonBackgroundHoverColor:C="#ededed",buttonTextColor:v="#454545",buttonTextHoverColor:S="#2b2b2b",buttonTextActiveColor:w="white"})=>{const E=useRef(null),R=useRef(null),k=useRef(null);var y=useRef(null),O=(null==n?void 0:n[0])||"",I=m?null==m?void 0:m[0]:(null==n?void 0:n[0])||"";const[T,B]=useState(O),[z,F]=useState(I),[P,A]=useState(),[N,L]=useState(0),[W,M]=useState(!1),H=()=>{var e;null!=(e=null===E||void 0===E?void 0:E.current)&&e.classList.remove("menu-expanded"),null!=(e=null===E||void 0===E?void 0:E.current)&&e.classList.add("menu-unexpanded"),k.current.className="menu menu-close",x&&M(!1),null!=(e=null===E||void 0===E?void 0:E.current)&&e.setAttribute("aria-expanded","false")};useEffect(()=>{const e=e=>{var t;e.path?e.path[0]!==E.current&&null!=(t=null===E||void 0===E?void 0:E.current)&&t.classList.contains("menu-expanded")&&H():e.composedPath()[0]!==E.current&&null!=(t=null===E||void 0===E?void 0:E.current)&&t.classList.contains("menu-expanded")&&H()};return document.body.addEventListener("click",e),()=>document.body.removeEventListener("click",e)},[]);return React.createElement("div",{className:r?"select-menu "+r:"select-menu",style:{width:i+"px"}},React.createElement(CustomButton,{options:n,label:e||O,customButtonRef:E,customMenuRef:k,selectedOptionRef:R,hiddenSelectRef:y,width:i,id:t,disabled:l,selectedOption:P,optionIndex:N,buttonIconPath:u,incrementOptionIndex:()=>{L(N+1)},updateNextOptionWithIndex:e=>{A(e.target.nextElementSibling.firstChild.children[N+1])},updateLastOptionWithIndex:e=>{A(e.target.nextElementSibling.firstChild.lastChild)},setToLastOptionIndex:()=>{L(n.length-1)},setToLFirstOptionIndex:()=>{L(0)},updateFirstOptionWithIndex:e=>{A(e.target.nextElementSibling.firstChild.firstChild)},decrementOptionIndex:()=>{L(N-1)},updatePreviousOptionWithIndex:e=>{A(e.target.nextElementSibling.firstChild.children[N-1])},showButtonIcon:p,optionsValues:m,isIconRotated:W,closeSelectMenu:H,openSelectMenu:()=>{var e;null!=(e=null===E||void 0===E?void 0:E.current)&&e.classList.add("menu-expanded"),k.current.className="menu menu-open",x&&M(!0),null!=(e=null===E||void 0===E?void 0:E.current)&&e.setAttribute("aria-expanded","true")},mainColor:h,buttonTextColor:v,buttonTextActiveColor:w,buttonTextHoverColor:S,buttonBackgroundHoverColor:C}),React.createElement(CustomMenu,{options:n,handleSelectOption:e=>{R.current.textContent=e.target.textContent,B(e.target.textContent),m&&F(e.target.getAttribute("data-option-value")),null!=(e=null===E||void 0===E?void 0:E.current)&&e.classList.contains("menu-expanded")&&H(),null!=(e=null===E||void 0===E?void 0:E.current)&&e.focus()},customMenuRef:k,width:i,id:t,customButtonRef:E,saveOption:e=>{A(e.target)},saveOptionIndex:e=>{L(Array.from(e.target.parentNode.children).indexOf(e.target))},maxHeight:a,scrollable:s,offsetX:c,offsetY:d,optionsValues:m,closeSelectMenu:H,optionsFontSize:f,mainColor:h,optionTextColor:g,optionTextFocus:b}),React.createElement(HiddenSelect,{id:t,hiddenOptionText:T,hiddenOptionValue:z,hiddenSelectRef:y,inputRef:o,optionsValues:m}))};export{SelectMenu};