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 • 9.07 kB
JavaScript
import{t}from"./toast-DgCSlJPv.js";import{n as e,t as a}from"./modal-MTuCfURV.js";var n=["January","February","March","April","May","June","July","August","September","October","November","December"],l=[{value:"",label:"Teal",var:"var(--primary)"},{value:"blue",label:"Blue",var:"var(--blue)"},{value:"yellow",label:"Yellow",var:"var(--yellow)"},{value:"red",label:"Red",var:"var(--red)"},{value:"purple",label:"Purple",var:"var(--purple)"}],o={"2026-04-01":[{title:"Standup 9am"}],"2026-04-03":[{title:"Design review",color:"blue"}],"2026-04-06":[{title:"1:1 with Sara"}],"2026-04-07":[{title:"Q2 Planning",color:"yellow"},{title:"Lunch demo"}],"2026-04-09":[{title:"All-hands",color:"purple"}],"2026-04-11":[{title:"Launch deadline",color:"red"}],"2026-04-14":[{title:"Standup 9am"}],"2026-04-15":[{title:"Customer call",color:"blue"}],"2026-04-17":[{title:"Design review"}],"2026-04-18":[{title:"Vendor demo",color:"yellow"}],"2026-04-22":[{title:"Quarterly review",color:"purple"}],"2026-04-24":[{title:"Code freeze",color:"red"}],"2026-04-27":[{title:"Standup 9am"},{title:"Sprint planning",color:"blue"}],"2026-04-29":[{title:"1:1 with Aigars"}],"2026-04-30":[{title:"Release v4.0",color:"yellow"}]},r=new Map,i=1;function s(t){return String(t).padStart(2,"0")}function d(t,e,a){return`${t}-${s(e+1)}-${s(a)}`}function c(t){const[e,a,n]=t.split("-").map(Number);return{year:e,month:a-1,day:n}}function u(t,e,a){const n=new Date(t,e,a).getDay(),l=[];return 1===n&&l.push({title:"Standup 9am",color:"",procedural:!0}),5!==n||a>7||l.push({title:"Design review",color:"blue",procedural:!0}),l}function v(t,e,a){const n=d(t,e,a),l=r.get(n);return l&&l.length?l:u(t,e,a)}function p(t){return l.find(e=>e.value===t)?.var??"var(--primary)"}function m(t){return String(t).replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"}[t]))}function g(t){const{gridEl:e,monthEl:a,year:l,month:o}=t;a.textContent=`${n[o]} ${l}`;const r=new Date,i=d(r.getFullYear(),r.getMonth(),r.getDate()),s=(new Date(l,o,1).getDay()+6)%7,c=new Date(l,o+1,0).getDate(),u=new Date(l,o,0).getDate(),p=[];["Mon","Tue","Wed","Thu","Fri","Sat","Sun"].forEach(t=>{p.push(`<div class="dow">${t}</div>`)});for(let n=s;n>0;n--)p.push(`<div class="calendar-day muted"><span class="day-num">${u-n+1}</span></div>`);for(let n=1;c>=n;n++){const t=d(l,o,n)===i,e=v(l,o,n).map((t,e)=>`\n <span class="calendar-event${t.color?" "+t.color:""}" data-event-idx="${e}" data-day="${n}">${m(t.title)}</span>\n `).join("");p.push(`<div class="calendar-day${t?" today":""}" data-day="${n}"><span class="day-num">${n}</span>${e}</div>`)}const g=42-s-c;for(let n=1;g>=n;n++)p.push(`<div class="calendar-day muted"><span class="day-num">${n}</span></div>`);e.innerHTML=p.join("")}function h({title:t="",color:e="",dateValue:a}){return`\n <form class="modal-form" novalidate>\n <div class="modal-form-row">\n <label for="ev-title">Title</label>\n <input type="text" id="ev-title" name="title" value="${m(t)}" placeholder="Event title" autocomplete="off" required>\n </div>\n <div class="modal-form-row">\n <label for="ev-date">Date</label>\n <input type="date" id="ev-date" name="date" value="${a}" required>\n </div>\n <div class="modal-form-row">\n <label>Color</label>\n ${n="color",o=e,`\n <div class="color-swatches" role="radiogroup" aria-label="Event color">\n ${l.map((t,e)=>`\n <input type="radio" id="${n}-${e}" name="${n}" value="${t.value}" ${t.value===(o||"")?"checked":""}>\n <label for="${n}-${e}" style="background:${t.var}" title="${t.label}" aria-label="${t.label}"></label>\n `).join("")}\n </div>\n `}\n </div>\n </form>\n `;var n,o}function y(t){const e=t.querySelector("form");if(!e)return null;const a=new FormData(e);return{title:(a.get("title")||"").toString().trim(),color:(a.get("color")||"").toString(),date:(a.get("date")||"").toString()}}function b(a,n,l){const o=(r.get(n)||[])[l];if(!o)return;const{year:i,month:s,day:u}=c(n),v=d(i,s,u);e({title:"Edit event",body:h({title:o.title,color:o.color,dateValue:v}),actions:[{label:"Delete",variant:"outline",action:({close:e})=>{const i=r.get(n)||[];return i.splice(l,1),i.length?r.set(n,i):r.delete(n),g(a),t(`Deleted: ${o.title}`),e(),!1},closeOnAction:!1},{label:"Cancel",variant:"outline"},{label:"Save",variant:"primary",action:({body:e})=>{const i=y(e);if(!i||!i.title)return t("Title is required",{variant:"warning"}),!1;const s=r.get(n)||[];s.splice(l,1),s.length?r.set(n,s):r.delete(n);const d=r.get(i.date)||[];d.push({id:o.id,title:i.title,color:i.color}),r.set(i.date,d);const u=c(i.date);return u.year===a.year&&u.month===a.month||(a.year=u.year,a.month=u.month),g(a),t(`Saved: ${i.title}`,{variant:"success"}),!0}}]})}function f(a,n){e({title:"New event",body:h({dateValue:n||(()=>{const t=new Date;return t.getFullYear()===a.year&&t.getMonth()===a.month?d(t.getFullYear(),t.getMonth(),t.getDate()):d(a.year,a.month,1)})()}),actions:[{label:"Cancel",variant:"outline"},{label:"Create",variant:"primary",action:({body:e})=>{const n=y(e);if(!n||!n.title)return t("Title is required",{variant:"warning"}),!1;const l=r.get(n.date)||[];l.push({id:i++,title:n.title,color:n.color}),r.set(n.date,l);const o=c(n.date);return o.year===a.year&&o.month===a.month||(a.year=o.year,a.month=o.month),g(a),t(`Created: ${n.title}`,{variant:"success"}),!0}}]})}function $(l,o,i,s){const c=d(o,i,s),v=`${n[i]} ${s}, ${o}`,h=()=>{const t=r.get(c)||[],e=t.length?[]:u(o,i,s);return t.length||e.length?t.length?`\n <div class="modal-events-list">\n ${t.map((t,e)=>`\n <div class="modal-event-row">\n <span class="swatch" style="background:${p(t.color)}"></span>\n <span class="title">${m(t.title)}</span>\n <span class="row-actions">\n <button type="button" class="row-btn" data-edit="${e}" aria-label="Edit ${m(t.title)}">\n <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M11 2l3 3-9 9H2v-3l9-9z"/></svg>\n </button>\n <button type="button" class="row-btn" data-delete="${e}" aria-label="Delete ${m(t.title)}">\n <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 4h10M6 4V2h4v2M5 4l1 9h4l1-9"/></svg>\n </button>\n </span>\n </div>\n `).join("")}\n </div>\n `:`\n <div class="modal-events-list">\n ${e.map(t=>`\n <div class="modal-event-row" style="opacity:.75">\n <span class="swatch" style="background:${p(t.color)}"></span>\n <span class="title">${m(t.title)} <small style="color:var(--text-muted);font-weight:normal">· suggested</small></span>\n </div>\n `).join("")}\n </div>\n `:`<div class="modal-events-empty">No events yet for ${v}.</div>`},{dialog:y,body:$}=e({title:v,body:h(),actions:[{label:"Close",variant:"outline"},{label:"Add event",variant:"primary",action:()=>(f(l,c),!0)}]});return $.addEventListener("click",e=>{const n=e.target.closest("[data-edit]"),o=e.target.closest("[data-delete]");if(n){const t=parseInt(n.dataset.edit,10);a(),setTimeout(()=>b(l,c,t),200)}else if(o){const e=parseInt(o.dataset.delete,10),a=r.get(c)||[],n=a[e];a.splice(e,1),a.length?r.set(c,a):r.delete(c),g(l),$.innerHTML=h(),n&&t(`Deleted: ${n.title}`)}}),{dialog:y,body:$}}function w(){const t=document.querySelector(".calendar-grid"),e=document.querySelector(".calendar-month");if(!t||!e)return;r.size||Object.entries(o).forEach(([t,e])=>{r.set(t,e.map(t=>({id:i++,title:t.title,color:t.color||""})))});const a=e.textContent.trim().split(/\s+/),l=n.indexOf(a[0]),s=parseInt(a[1],10),c={gridEl:t,monthEl:e,year:Number.isFinite(s)?s:(new Date).getFullYear(),month:0>l?(new Date).getMonth():l};g(c);const u=document.querySelectorAll(".calendar-toolbar .nav-btns .card-opt-btn");u[0]&&u[0].addEventListener("click",t=>{t.stopPropagation(),t.preventDefault(),c.month-=1,0>c.month&&(c.month=11,c.year-=1),g(c)}),u[1]&&u[1].addEventListener("click",t=>{t.stopPropagation(),t.preventDefault(),c.month+=1,c.month>11&&(c.month=0,c.year+=1),g(c)}),document.querySelectorAll(".page-actions .btn").forEach(t=>{const e=t.textContent.trim().toLowerCase();"today"===e?t.addEventListener("click",t=>{t.stopPropagation(),t.preventDefault();const e=new Date;c.year=e.getFullYear(),c.month=e.getMonth(),g(c)}):e.includes("new event")&&t.addEventListener("click",t=>{t.stopPropagation(),t.preventDefault(),f(c)})}),t.addEventListener("click",t=>{const e=t.target.closest("[data-event-idx]");if(e){t.stopPropagation();const a=parseInt(e.dataset.day,10),n=parseInt(e.dataset.eventIdx,10),l=d(c.year,c.month,a),o=r.get(l);return void(o&&o[n]?b(c,l,n):$(c,c.year,c.month,a))}const a=t.target.closest("[data-day]");if(a&&!a.classList.contains("muted")){const t=parseInt(a.dataset.day,10);$(c,c.year,c.month,t)}})}export{w as initCalendar};