forked from Ruderverein-Donau-Linz/rowt
		
	[TASK] add multiselct js
This commit is contained in:
		
							
								
								
									
										224
									
								
								frontend/static/js/multiselect-dropdown.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								frontend/static/js/multiselect-dropdown.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,224 @@
 | 
			
		||||
var style = document.createElement('style');
 | 
			
		||||
style.setAttribute("id","multiselect_dropdown_styles");
 | 
			
		||||
style.innerHTML = `
 | 
			
		||||
.multiselect-dropdown{
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  padding: 2px 5px 0px 5px;
 | 
			
		||||
  border-radius: 4px;
 | 
			
		||||
  border: solid 1px #ced4da;
 | 
			
		||||
  background-color: white;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");
 | 
			
		||||
  background-repeat: no-repeat;
 | 
			
		||||
  background-position: right .75rem center;
 | 
			
		||||
  background-size: 16px 12px;
 | 
			
		||||
}
 | 
			
		||||
.multiselect-dropdown span.optext, .multiselect-dropdown span.placeholder{
 | 
			
		||||
  margin-right:0.5em; 
 | 
			
		||||
  margin-bottom:2px;
 | 
			
		||||
  padding:1px 0; 
 | 
			
		||||
  border-radius: 4px; 
 | 
			
		||||
  display:inline-block;
 | 
			
		||||
}
 | 
			
		||||
.multiselect-dropdown span.optext{
 | 
			
		||||
  background-color: rgba(226, 232, 240, 0.8);
 | 
			
		||||
  padding:1px 0.75em; 
 | 
			
		||||
}
 | 
			
		||||
.multiselect-dropdown span.optext .optdel {
 | 
			
		||||
  float: right;
 | 
			
		||||
  margin: 0 -6px 1px 5px;
 | 
			
		||||
  font-size: 0.7em;
 | 
			
		||||
  margin-top: 2px;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  color: #666;
 | 
			
		||||
}
 | 
			
		||||
.multiselect-dropdown span.optext .optdel:hover { color: #c66;}
 | 
			
		||||
.multiselect-dropdown span.placeholder{
 | 
			
		||||
  color:#ced4da;
 | 
			
		||||
}
 | 
			
		||||
.multiselect-dropdown-list-wrapper{
 | 
			
		||||
  box-shadow: gray 0 3px 8px;
 | 
			
		||||
  z-index: 100;
 | 
			
		||||
  padding:2px;
 | 
			
		||||
  border-radius: 4px;
 | 
			
		||||
  border: solid 1px #ced4da;
 | 
			
		||||
  display: none;
 | 
			
		||||
  margin: -1px;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top:0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  right: 0;
 | 
			
		||||
  background: white;
 | 
			
		||||
}
 | 
			
		||||
.multiselect-dropdown-list-wrapper .multiselect-dropdown-search{
 | 
			
		||||
  margin-bottom:5px;
 | 
			
		||||
}
 | 
			
		||||
.multiselect-dropdown-list{
 | 
			
		||||
  padding:2px;
 | 
			
		||||
  height: 15rem;
 | 
			
		||||
  overflow-y:auto;
 | 
			
		||||
  overflow-x: hidden;
 | 
			
		||||
}
 | 
			
		||||
.multiselect-dropdown-list::-webkit-scrollbar {
 | 
			
		||||
  width: 6px;
 | 
			
		||||
}
 | 
			
		||||
.multiselect-dropdown-list::-webkit-scrollbar-thumb {
 | 
			
		||||
  background-color: #bec4ca;
 | 
			
		||||
  border-radius:3px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.multiselect-dropdown-list div{
 | 
			
		||||
  padding: 5px;
 | 
			
		||||
}
 | 
			
		||||
.multiselect-dropdown-list input{
 | 
			
		||||
  height: 1.15em;
 | 
			
		||||
  width: 1.15em;
 | 
			
		||||
  margin-right: 0.35em;  
 | 
			
		||||
}
 | 
			
		||||
.multiselect-dropdown-list div.checked{
 | 
			
		||||
}
 | 
			
		||||
.multiselect-dropdown-list div:hover{
 | 
			
		||||
  background-color: #ced4da;
 | 
			
		||||
}
 | 
			
		||||
.multiselect-dropdown span.maxselected {width:100%;}
 | 
			
		||||
.multiselect-dropdown-all-selector {border-bottom:solid 1px #999;}
 | 
			
		||||
`;
 | 
			
		||||
document.head.appendChild(style);
 | 
			
		||||
 | 
			
		||||
function MultiselectDropdown(options){
 | 
			
		||||
  var config={
 | 
			
		||||
    search:true,
 | 
			
		||||
    height:'15rem',
 | 
			
		||||
    placeholder:'Auswählen',
 | 
			
		||||
    txtSelected:'ausgewählt',
 | 
			
		||||
    txtAll:'All',
 | 
			
		||||
    txtRemove: 'Löschen',
 | 
			
		||||
    txtSearch:'Suchen',
 | 
			
		||||
    ...options
 | 
			
		||||
  };
 | 
			
		||||
  function newEl(tag,attrs){
 | 
			
		||||
    var e=document.createElement(tag);
 | 
			
		||||
    if(attrs!==undefined) Object.keys(attrs).forEach(k=>{
 | 
			
		||||
      if(k==='class') { Array.isArray(attrs[k]) ? attrs[k].forEach(o=>o!==''?e.classList.add(o):0) : (attrs[k]!==''?e.classList.add(attrs[k]):0)}
 | 
			
		||||
      else if(k==='style'){  
 | 
			
		||||
        Object.keys(attrs[k]).forEach(ks=>{
 | 
			
		||||
          e.style[ks]=attrs[k][ks];
 | 
			
		||||
        });
 | 
			
		||||
       }
 | 
			
		||||
      else if(k==='text'){attrs[k]===''?e.innerHTML=' ':e.innerText=attrs[k]}
 | 
			
		||||
      else e[k]=attrs[k];
 | 
			
		||||
    });
 | 
			
		||||
    return e;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  
 | 
			
		||||
  document.querySelectorAll("select[multiple]").forEach((el,k)=>{
 | 
			
		||||
    
 | 
			
		||||
    var div=newEl('div',{class:'multiselect-dropdown',style:{width:config.style?.width??el.clientWidth+'px',padding:config.style?.padding??''}});
 | 
			
		||||
    el.style.display='none';
 | 
			
		||||
    el.parentNode.insertBefore(div,el.nextSibling);
 | 
			
		||||
    var listWrap=newEl('div',{class:'multiselect-dropdown-list-wrapper'});
 | 
			
		||||
    var list=newEl('div',{class:'multiselect-dropdown-list',style:{height:config.height}});
 | 
			
		||||
    var search=newEl('input',{class:['multiselect-dropdown-search'].concat([config.searchInput?.class??'form-control']),style:{width:'100%',display:el.attributes['multiselect-search']?.value==='true'?'block':'none'},placeholder:config.txtSearch});
 | 
			
		||||
    listWrap.appendChild(search);
 | 
			
		||||
    div.appendChild(listWrap);
 | 
			
		||||
    listWrap.appendChild(list);
 | 
			
		||||
 | 
			
		||||
    el.loadOptions=()=>{
 | 
			
		||||
      list.innerHTML='';
 | 
			
		||||
      
 | 
			
		||||
      if(el.attributes['multiselect-select-all']?.value=='true'){
 | 
			
		||||
        var op=newEl('div',{class:'multiselect-dropdown-all-selector'})
 | 
			
		||||
        var ic=newEl('input',{type:'checkbox'});
 | 
			
		||||
        op.appendChild(ic);
 | 
			
		||||
        op.appendChild(newEl('label',{text:config.txtAll}));
 | 
			
		||||
  
 | 
			
		||||
        op.addEventListener('click',()=>{
 | 
			
		||||
          op.classList.toggle('checked');
 | 
			
		||||
          op.querySelector("input").checked=!op.querySelector("input").checked;
 | 
			
		||||
          
 | 
			
		||||
          var ch=op.querySelector("input").checked;
 | 
			
		||||
          list.querySelectorAll(":scope > div:not(.multiselect-dropdown-all-selector)")
 | 
			
		||||
            .forEach(i=>{if(i.style.display!=='none'){i.querySelector("input").checked=ch; i.optEl.selected=ch}});
 | 
			
		||||
  
 | 
			
		||||
          el.dispatchEvent(new Event('change'));
 | 
			
		||||
        });
 | 
			
		||||
        ic.addEventListener('click',(ev)=>{
 | 
			
		||||
          ic.checked=!ic.checked;
 | 
			
		||||
        });
 | 
			
		||||
        el.addEventListener('change', (ev)=>{
 | 
			
		||||
          let itms=Array.from(list.querySelectorAll(":scope > div:not(.multiselect-dropdown-all-selector)")).filter(e=>e.style.display!=='none')
 | 
			
		||||
          let existsNotSelected=itms.find(i=>!i.querySelector("input").checked);
 | 
			
		||||
          if(ic.checked && existsNotSelected) ic.checked=false;
 | 
			
		||||
          else if(ic.checked==false && existsNotSelected===undefined) ic.checked=true;
 | 
			
		||||
        });
 | 
			
		||||
  
 | 
			
		||||
        list.appendChild(op);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      Array.from(el.options).map(o=>{
 | 
			
		||||
        var op=newEl('div',{class:o.selected?'checked':'',optEl:o})
 | 
			
		||||
        var ic=newEl('input',{type:'checkbox',checked:o.selected});
 | 
			
		||||
        op.appendChild(ic);
 | 
			
		||||
        op.appendChild(newEl('label',{text:o.text}));
 | 
			
		||||
 | 
			
		||||
        op.addEventListener('click',()=>{
 | 
			
		||||
          op.classList.toggle('checked');
 | 
			
		||||
          op.querySelector("input").checked=!op.querySelector("input").checked;
 | 
			
		||||
          op.optEl.selected=!!!op.optEl.selected;
 | 
			
		||||
          el.dispatchEvent(new Event('change'));
 | 
			
		||||
        });
 | 
			
		||||
        ic.addEventListener('click',(ev)=>{
 | 
			
		||||
          ic.checked=!ic.checked;
 | 
			
		||||
        });
 | 
			
		||||
        o.listitemEl=op;
 | 
			
		||||
        list.appendChild(op);
 | 
			
		||||
      });
 | 
			
		||||
      div.listEl=listWrap;
 | 
			
		||||
 | 
			
		||||
      div.refresh=()=>{
 | 
			
		||||
        div.querySelectorAll('span.optext, span.placeholder').forEach(t=>div.removeChild(t));
 | 
			
		||||
        var sels=Array.from(el.selectedOptions);
 | 
			
		||||
        if(sels.length>(el.attributes['multiselect-max-items']?.value??5)){
 | 
			
		||||
          div.appendChild(newEl('span',{class:['optext','maxselected'],text:sels.length+' '+config.txtSelected}));          
 | 
			
		||||
        }
 | 
			
		||||
        else{
 | 
			
		||||
          sels.map(x=>{
 | 
			
		||||
            var c=newEl('span',{class:'optext',text:x.text, srcOption: x});
 | 
			
		||||
            if((el.attributes['multiselect-hide-x']?.value !== 'true'))
 | 
			
		||||
              c.appendChild(newEl('span',{class:'optdel',text:'🗙',title:config.txtRemove, onclick:(ev)=>{c.srcOption.listitemEl.dispatchEvent(new Event('click'));div.refresh();ev.stopPropagation();}}));
 | 
			
		||||
 | 
			
		||||
            div.appendChild(c);
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
        if(0==el.selectedOptions.length) div.appendChild(newEl('span',{class:'placeholder',text:el.attributes['placeholder']?.value??config.placeholder}));
 | 
			
		||||
      };
 | 
			
		||||
      div.refresh();
 | 
			
		||||
    }
 | 
			
		||||
    el.loadOptions();
 | 
			
		||||
    
 | 
			
		||||
    search.addEventListener('input',()=>{
 | 
			
		||||
      list.querySelectorAll(":scope div:not(.multiselect-dropdown-all-selector)").forEach(d=>{
 | 
			
		||||
        var txt=d.querySelector("label").innerText.toUpperCase();
 | 
			
		||||
        d.style.display=txt.includes(search.value.toUpperCase())?'block':'none';
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    div.addEventListener('click',()=>{
 | 
			
		||||
      div.listEl.style.display='block';
 | 
			
		||||
      search.focus();
 | 
			
		||||
      search.select();
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    document.addEventListener('click', function(event) {
 | 
			
		||||
      if (!div.contains(event.target)) {
 | 
			
		||||
        listWrap.style.display='none';
 | 
			
		||||
        div.refresh();
 | 
			
		||||
      }
 | 
			
		||||
    });    
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
window.addEventListener('load',()=>{
 | 
			
		||||
  MultiselectDropdown(window.MultiselectDropdownOptions);
 | 
			
		||||
});
 | 
			
		||||
@@ -71,6 +71,8 @@
 | 
			
		||||
  document.getElementById("departure").value = formattedDate;
 | 
			
		||||
	</script>
 | 
			
		||||
  </form>
 | 
			
		||||
 | 
			
		||||
  <script src="/public/js/multiselect-dropdown.js" ></script>
 | 
			
		||||
  <script>
 | 
			
		||||
   document.getElementById('form').addEventListener('submit', function(e) {
 | 
			
		||||
     e.preventDefault();
 | 
			
		||||
@@ -100,6 +102,7 @@
 | 
			
		||||
            document.getElementById('shipmaster_only_steering').checked = shipmaster_only_steering;
 | 
			
		||||
            document.getElementById('newrower-max_rower_allowed').innerHTML = selectedOption.getAttribute("extra-amount_seats");
 | 
			
		||||
 | 
			
		||||
            console.log(selectElement.selectedOptions[0]);
 | 
			
		||||
          }
 | 
			
		||||
          
 | 
			
		||||
          document.addEventListener('DOMContentLoaded', function() {
 | 
			
		||||
@@ -224,9 +227,9 @@
 | 
			
		||||
{% macro home(log, only_ones) %}
 | 
			
		||||
<form class="grid grid-cols-1 gap-3" action="/log/{{log.id}}" method="post">
 | 
			
		||||
    {% if not only_ones %}
 | 
			
		||||
      {{ log::rower_select(id="rowers"~log.id, selected=log.rowers, amount_seats=log.boat.amount_seats) }}
 | 
			
		||||
      
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    <div>
 | 
			
		||||
    <div class="relative">
 | 
			
		||||
        <label for="destination" class="small text-gray-600">Ziel</label>
 | 
			
		||||
        <input class="input rounded-md" type="search" list="destinations" placeholder="Destination" required="required", id="destination-home" name="destination" value="{{log.destination}}" oninput="var inputElement = document.getElementById('destination-home');
 | 
			
		||||
          var dataList = document.getElementById('destinations');
 | 
			
		||||
@@ -241,8 +244,6 @@
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="relative">
 | 
			
		||||
      {{ macros::input(label="Distanz", name="distance_in_km", id="distance_in_km_home", type="number", min=0, value=log.distance_in_km, required=true) }}
 | 
			
		||||
      <span class="absolute right-0 bottom-0 py-1.5 px-2 bg-white border-0 text-gray-600 ring-1 ring-inset ring-gray-300 rounded-br-md rounded-tr-md">km</span>
 | 
			
		||||
    </div>
 | 
			
		||||
  
 | 
			
		||||
    {{ macros::input(label="Kommentar", name="comments", type="text", value=log.comments) }}
 | 
			
		||||
@@ -257,23 +258,26 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
{% macro rower_select(id, selected, amount_seats='', class='') %}
 | 
			
		||||
  <select multiple="multiple" name="rower[]" id="{{id}}" onclick="updateSelectedRowersCount{{id}}()" onblur="updateSelectedRowersCount{{id}}()" class="{{ class }}">
 | 
			
		||||
    {% for user in users %}
 | 
			
		||||
      {% set_global sel = false %}
 | 
			
		||||
      {% for rower in selected %}
 | 
			
		||||
        {% if rower.id == user.id %}
 | 
			
		||||
          {% set_global sel = true %}
 | 
			
		||||
        {% endif %}
 | 
			
		||||
  <div class="{{ class }}">
 | 
			
		||||
    <select multiple="multiple" name="rower[]" multiselect-search="true" multiselect-max-items="8" id="{{id}}" onclick="updateSelectedRowersCount{{id}}()" onblur="updateSelectedRowersCount{{id}}()" class="w-full">
 | 
			
		||||
      {% for user in users %}
 | 
			
		||||
        {% set_global sel = false %}
 | 
			
		||||
        {% for rower in selected %}
 | 
			
		||||
          {% if rower.id == user.id %}
 | 
			
		||||
            {% set_global sel = true %}
 | 
			
		||||
          {% endif %}
 | 
			
		||||
        {% endfor %}
 | 
			
		||||
        <option value="{{ user.id }}" {% if sel %}selected{% endif %} onmousedown="event.preventDefault();this.selected = !this.selected; return false;">{{user.name}}</option>
 | 
			
		||||
      {% endfor %}
 | 
			
		||||
      <option value="{{ user.id }}" {% if sel %}selected{% endif %} onmousedown="event.preventDefault();this.selected = !this.selected; return false;">{{user.name}}</option>
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
  </select>
 | 
			
		||||
  <script>
 | 
			
		||||
    function updateSelectedRowersCount{{id}}() {
 | 
			
		||||
    	document.getElementById('{{id}}-amount_rower_selected').textContent = document.getElementById('{{id}}').selectedOptions.length+1; 
 | 
			
		||||
    }
 | 
			
		||||
    document.addEventListener('DOMContentLoaded', updateSelectedRowersCount{{id}});
 | 
			
		||||
  </script>
 | 
			
		||||
    </select>
 | 
			
		||||
    <script>
 | 
			
		||||
      function updateSelectedRowersCount{{id}}() {
 | 
			
		||||
        document.getElementById('{{id}}-amount_rower_selected').textContent = document.getElementById('{{id}}').selectedOptions.length+1; 
 | 
			
		||||
      }
 | 
			
		||||
      document.addEventListener('DOMContentLoaded', updateSelectedRowersCount{{id}});
 | 
			
		||||
    </script>
 | 
			
		||||
  </div>
 | 
			
		||||
  
 | 
			
		||||
  <div>
 | 
			
		||||
    <span id="{{id}}-amount_rower_selected"></span>/<span id="{{id}}-max_rower_allowed">{{amount_seats}}</span> Ruderer ausgewählt
 | 
			
		||||
  </div>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user