UNPKG

gentelella

Version:

Gentelella v4 — free admin template. 60 pages, 20 chart variants, fully interactive inbox & kanban, live theme generator, component playground, PWA-ready. Vite 8, vanilla JS, no Bootstrap, no jQuery.

1 lines 11.8 kB
var t=["January","February","March","April","May","June","July","August","September","October","November","December"],e=["M","T","W","T","F","S","S"];function n(t){return t?t.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"}):""}function a(t){return new Date(t.getFullYear(),t.getMonth(),t.getDate()).getTime()}function o(n,o,l,r,s){const i=(new Date(n,o,1).getDay()+6)%7,d=new Date(n,o+1,0).getDate(),c=[];for(let t=0;i>t;t+=1)c.push(null);for(let t=1;d>=t;t+=1)c.push(new Date(n,o,t));for(;c.length%7!=0;)c.push(null);const u=c.map(t=>{if(!t)return'<div class="dr-cell empty"></div>';const e=a(t),n=["dr-cell"];return l&&e===l&&n.push("selected","range-start"),r&&e===r&&n.push("selected","range-end"),l&&r&&e>l&&r>e&&n.push("in-range"),l&&!r&&s&&e>l&&s>=e&&n.push("in-range","preview"),e===a(new Date)&&n.push("today"),`<button type="button" class="${n.join(" ")}" data-ts="${e}">${t.getDate()}</button>`}).join("");return`\n <div class="dr-month">\n <div class="dr-month-head">${t[o]} ${n}</div>\n <div class="dr-dow">${e.map(t=>`<span>${t}</span>`).join("")}</div>\n <div class="dr-grid">${u}</div>\n </div>\n `}function l(t){if(t.dataset.drInit)return;t.dataset.drInit="1";const e=t.querySelector("input");if(!e)return;e.readOnly=!0;const l={from:null,to:null,hover:null,pivot:new Date},r=document.createElement("div");r.className="dr-popover",r.hidden=!0,document.body.appendChild(r);const s=[{label:"Today",get:()=>{const t=new Date;return[t,t]}},{label:"Last 7 days",get:()=>{const t=new Date,e=new Date;return t.setDate(t.getDate()-6),[t,e]}},{label:"Last 30 days",get:()=>{const t=new Date,e=new Date;return t.setDate(t.getDate()-29),[t,e]}},{label:"This month",get:()=>{const t=new Date;return[new Date(t.getFullYear(),t.getMonth(),1),t]}},{label:"Last month",get:()=>{const t=new Date;return[new Date(t.getFullYear(),t.getMonth()-1,1),new Date(t.getFullYear(),t.getMonth(),0)]}},{label:"This year",get:()=>{const t=new Date;return[new Date(t.getFullYear(),0,1),t]}}],i=()=>{const t=l.pivot,e=new Date(t.getFullYear(),t.getMonth()+1,1),i=l.from?a(l.from):null,d=l.to?a(l.to):null,c=l.hover?a(l.hover):null;r.innerHTML=`\n <div class="dr-presets">\n ${s.map(t=>`<button type="button" class="dr-preset" data-preset="${t.label}">${t.label}</button>`).join("")}\n </div>\n <div class="dr-cal">\n <div class="dr-nav">\n <button type="button" class="dr-prev" aria-label="Previous month"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M10 4L6 8l4 4"/></svg></button>\n <div class="dr-spacer"></div>\n <button type="button" class="dr-next" aria-label="Next month"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M6 4l4 4-4 4"/></svg></button>\n </div>\n <div class="dr-months">\n ${o(t.getFullYear(),t.getMonth(),i,d,c)}\n ${o(e.getFullYear(),e.getMonth(),i,d,c)}\n </div>\n <div class="dr-footer">\n <div class="dr-summary">${l.from?n(l.from):"—"}${l.to?n(l.to):"—"}</div>\n <div style="display:flex;gap:6px">\n <button type="button" class="btn btn-ghost btn-sm" data-action="clear">Clear</button>\n <button type="button" class="btn btn-primary btn-sm" data-action="apply" ${l.from?"":"disabled"}>Apply</button>\n </div>\n </div>\n </div>\n `},d=()=>{const t=e.getBoundingClientRect();r.style.position="fixed",r.style.top=`${t.bottom+6}px`,r.style.left=`${Math.max(8,Math.min(t.left,window.innerWidth-r.offsetWidth-8))}px`},c=()=>{r.hidden=!1,i(),requestAnimationFrame(d),t.classList.add("open")},u=()=>{r.hidden=!0,t.classList.remove("open")};e.addEventListener("focus",c),e.addEventListener("click",c),r.addEventListener("click",o=>{const r=o.target.closest(".dr-cell:not(.empty)");if(r){const t=parseInt(r.dataset.ts,10),e=new Date(t);return!l.from||l.from&&l.to?(l.from=e,l.to=null,l.hover=null):t<a(l.from)?(l.to=l.from,l.from=e):l.to=e,void i()}if(o.target.closest(".dr-prev"))l.pivot=new Date(l.pivot.getFullYear(),l.pivot.getMonth()-1,1),i();else if(o.target.closest(".dr-next"))l.pivot=new Date(l.pivot.getFullYear(),l.pivot.getMonth()+1,1),i();else if(o.target.closest("[data-preset]")){const t=s.find(t=>t.label===o.target.closest("[data-preset]").dataset.preset);if(t){const[e,n]=t.get();l.from=e,l.to=n,l.pivot=new Date(e.getFullYear(),e.getMonth(),1),i()}}else o.target.closest('[data-action="clear"]')?(l.from=l.to=null,i()):o.target.closest('[data-action="apply"]')&&(e.value=l.from?`${n(l.from)}${n(l.to||l.from)}`:"",t.dispatchEvent(new CustomEvent("change",{detail:{from:l.from,to:l.to||l.from}})),u())}),r.addEventListener("mouseover",t=>{if(l.from&&!l.to){const e=t.target.closest(".dr-cell:not(.empty)");e&&(l.hover=new Date(parseInt(e.dataset.ts,10)),i())}}),document.addEventListener("click",e=>{r.hidden||r.contains(e.target)||t.contains(e.target)||u()},!0),document.addEventListener("keydown",t=>{"Escape"!==t.key||r.hidden||u()}),window.addEventListener("resize",()=>{r.hidden||d()})}function r(t){if(t.dataset.rtInit)return;t.dataset.rtInit="1";const e=t.querySelector("textarea");t.innerHTML=`\n <div class="rt-toolbar" role="toolbar" aria-label="Formatting">\n <button type="button" data-cmd="bold" aria-label="Bold" title="Bold (⌘B)"><strong>B</strong></button>\n <button type="button" data-cmd="italic" aria-label="Italic" title="Italic (⌘I)"><em>I</em></button>\n <button type="button" data-cmd="underline" aria-label="Underline" title="Underline (⌘U)"><span style="text-decoration:underline">U</span></button>\n <span class="rt-sep"></span>\n <button type="button" data-block="h2" aria-label="Heading 2" title="Heading">H</button>\n <button type="button" data-block="blockquote" aria-label="Quote" title="Quote">"</button>\n <button type="button" data-cmd="insertUnorderedList" aria-label="Bulleted list" title="Bulleted list">\n <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="3" cy="4" r="1" fill="currentColor"/><circle cx="3" cy="8" r="1" fill="currentColor"/><circle cx="3" cy="12" r="1" fill="currentColor"/><line x1="6" y1="4" x2="14" y2="4"/><line x1="6" y1="8" x2="14" y2="8"/><line x1="6" y1="12" x2="14" y2="12"/></svg>\n </button>\n <button type="button" data-cmd="insertOrderedList" aria-label="Numbered list" title="Numbered list">\n <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><text x="2" y="6" font-size="6" font-family="monospace" fill="currentColor">1</text><text x="2" y="11" font-size="6" font-family="monospace" fill="currentColor">2</text><text x="2" y="15" font-size="6" font-family="monospace" fill="currentColor">3</text><line x1="7" y1="4" x2="14" y2="4"/><line x1="7" y1="9" x2="14" y2="9"/><line x1="7" y1="14" x2="14" y2="14"/></svg>\n </button>\n <span class="rt-sep"></span>\n <button type="button" data-cmd="link" aria-label="Insert link" title="Link (⌘K)">\n <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M7 9l2-2M5 5H4a3 3 0 100 6h2M11 11h1a3 3 0 100-6h-2"/></svg>\n </button>\n <button type="button" data-cmd="formatBlock" data-arg="<pre>" aria-label="Code block" title="Code"><code>&lt;/&gt;</code></button>\n <span class="rt-sep"></span>\n <button type="button" data-cmd="removeFormat" aria-label="Clear formatting" title="Clear">×</button>\n </div>\n <div class="rt-editor" contenteditable="true" role="textbox" aria-multiline="true">${e?.value||t.innerHTML}</div>\n `,e&&t.appendChild(e);const n=t.querySelector(".rt-editor"),a=t.querySelector(".rt-toolbar"),o=()=>{e&&(e.value=n.innerHTML),t.dispatchEvent(new CustomEvent("input",{detail:n.innerHTML}))},l=(t,e=null)=>{if("link"===t){const t=prompt("Link URL:","https://");t&&document.execCommand("createLink",!1,t)}else document.execCommand(t,!1,e);n.focus(),o()};a.addEventListener("click",t=>{const e=t.target.closest("button[data-cmd], button[data-block]");e&&(t.preventDefault(),e.dataset.block?l("formatBlock",`<${e.dataset.block}>`):l(e.dataset.cmd,e.dataset.arg))}),n.addEventListener("input",o),n.addEventListener("keydown",t=>{if(!t.metaKey&&!t.ctrlKey)return;const e={b:"bold",i:"italic",u:"underline",k:"link"}[t.key.toLowerCase()];e&&(t.preventDefault(),l(e))})}function s(t){if(t.dataset.msInit)return;let e;t.dataset.msInit="1";const n=t.querySelector("select");e=n?[...n.options].map(t=>({value:t.value,label:t.textContent})):t.dataset.options?t.dataset.options.split(",").map(t=>t.trim()).filter(Boolean).map(t=>({value:t,label:t})):[];const a=new Set(n?[...n.selectedOptions].map(t=>t.value):[]);t.innerHTML='\n <div class="ms-input" tabindex="0">\n <div class="ms-chips"></div>\n <input type="text" class="ms-search" placeholder="Type to search…" aria-label="Search options" autocomplete="off">\n <svg class="ms-chev" width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 6l4 4 4-4"/></svg>\n </div>\n <div class="ms-menu" hidden role="listbox"></div>\n ',n&&t.appendChild(n);const o=t.querySelector(".ms-input"),l=t.querySelector(".ms-chips"),r=t.querySelector(".ms-search"),s=t.querySelector(".ms-menu"),c=()=>{l.innerHTML=[...a].map(t=>{const n=e.find(e=>e.value===t);return`<span class="ms-chip">${i(n?n.label:t)}<button type="button" data-remove="${d(t)}" aria-label="Remove">×</button></span>`}).join("")},u=()=>{const t=r.value.trim().toLowerCase(),n=e.filter(e=>!a.has(e.value)&&(!t||e.label.toLowerCase().includes(t)));s.innerHTML=n.length?n.map((t,e)=>`\n <button type="button" class="ms-option${0===e?" active":""}" data-value="${d(t.value)}">${i(t.label)}</button>\n `).join(""):`<div class="ms-empty">${t?"No matches":"All selected"}</div>`},p=()=>{n&&([...n.options].forEach(t=>{t.selected=a.has(t.value)}),n.dispatchEvent(new Event("change",{bubbles:!0}))),t.dispatchEvent(new CustomEvent("change",{detail:{values:[...a]}}))},v=t=>{a.add(t),c(),u(),p(),r.value="",r.focus()},m=t=>{a.delete(t),c(),u(),p()},b=()=>{s.hidden=!1,t.classList.add("open"),u()},h=()=>{s.hidden=!0,t.classList.remove("open")};o.addEventListener("click",()=>{r.focus(),b()}),r.addEventListener("focus",b),r.addEventListener("input",u),r.addEventListener("keydown",t=>{if("Backspace"===t.key&&!r.value&&a.size){const t=[...a].pop();m(t)}else if("ArrowDown"===t.key||"ArrowUp"===t.key){t.preventDefault();const e=[...s.querySelectorAll(".ms-option")];if(!e.length)return;const n=e.findIndex(t=>t.classList.contains("active")),a="ArrowDown"===t.key?(n+1)%e.length:(n-1+e.length)%e.length;e.forEach(t=>t.classList.remove("active")),e[a].classList.add("active"),e[a].scrollIntoView({block:"nearest"})}else if("Enter"===t.key){t.preventDefault();const e=s.querySelector(".ms-option.active");e&&v(e.dataset.value)}else"Escape"===t.key&&h()}),s.addEventListener("click",t=>{const e=t.target.closest(".ms-option");e&&v(e.dataset.value)}),l.addEventListener("click",t=>{const e=t.target.closest("[data-remove]");e&&m(e.dataset.remove)}),document.addEventListener("click",e=>{t.contains(e.target)||h()}),c(),u()}function i(t){return String(t).replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}[t]))}function d(t){return i(t)}function c(){document.querySelectorAll("[data-date-range]").forEach(l),document.querySelectorAll("[data-rich-text]").forEach(r),document.querySelectorAll("[data-multi-select]").forEach(s)}export{c as initFormControls};