simple-calendar-js 3.0.13 → 3.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
No unreleased changes yet.
|
|
11
11
|
|
|
12
|
+
## [3.0.14] - 2026-06-29
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- Month navigation overflow bug where navigating from end-of-month dates (e.g. May 31st) would skip a month forward or appear to do nothing when going backward
|
|
16
|
+
|
|
12
17
|
## [3.0.13] - 2026-03-08
|
|
13
18
|
|
|
14
19
|
### Changed
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SimpleCalendarJs
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Lightweight JS calendar (~16KB gzipped). Zero dependencies. Works in React, Vue, Angular, or vanilla JS out of the box. Month, week, day, and list views. Built-in date & range picker. No FullCalendar bloat.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/simple-calendar-js)
|
|
6
6
|
[](https://github.com/pclslopes/SimpleCalendarJs/blob/main/LICENSE)
|
|
@@ -97,6 +97,8 @@ npm install simple-calendar-js
|
|
|
97
97
|
|
|
98
98
|
Download the files from the [dist/](https://github.com/pclslopes/SimpleCalendarJs/tree/main/dist) folder and include them in your project.
|
|
99
99
|
|
|
100
|
+
> **License:** Free for personal and open source use. Commercial projects require a license → [simplecalendarjs.com/#pricing](https://www.simplecalendarjs.com/#pricing)
|
|
101
|
+
|
|
100
102
|
## Quick Start
|
|
101
103
|
|
|
102
104
|
### Vanilla JavaScript
|
|
@@ -334,4 +336,4 @@ This project is available for personal, educational, and non-commercial use.
|
|
|
334
336
|
|
|
335
337
|
**Commercial use requires a separate license.** See [LICENSE](https://github.com/pclslopes/SimpleCalendarJs/blob/main/LICENSE) file for full terms.
|
|
336
338
|
|
|
337
|
-
For commercial licensing inquiries: simplecalendarjs@gmail.com or visit [www.simplecalendarjs.com](https://www.simplecalendarjs.com)
|
|
339
|
+
For commercial licensing inquiries: simplecalendarjs@gmail.com or visit [www.simplecalendarjs.com/#pricing](https://www.simplecalendarjs.com/#pricing)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SimpleCalendarJs v3.0.
|
|
2
|
+
* SimpleCalendarJs v3.0.14
|
|
3
3
|
* A clean, modern, and feature-rich JavaScript calendar component with zero dependencies
|
|
4
4
|
*
|
|
5
5
|
* @author Pedro Lopes <simplecalendarjs@gmail.com>
|
|
@@ -11,4 +11,4 @@
|
|
|
11
11
|
ES MODULE EXPORT
|
|
12
12
|
Also available as window.SimpleCalendarJs via the IIFE wrapper.
|
|
13
13
|
============================================================ */
|
|
14
|
-
return class{constructor(e,s={}){if("string"==typeof e){if(this._el=document.querySelector(e),!this._el)throw new Error(`SimpleCalendarJs: no element for "${e}"`)}else this._el=e;this._opts=Object.assign({mode:"calendar",defaultView:"month",defaultDate:null,weekStartsOn:0,locale:"default",weekdayFormat:"short",use24Hour:!1,showTimeInItems:!0,showGridLines:!0,monthTimedEventStyle:"list",monthDayNumberAlign:"left",showToolbar:!0,showTodayButton:!0,showNavigation:!0,showTitle:!0,showYearPicker:!0,showViewSwitcher:!0,showTooltips:!0,tooltipBgColor:null,tooltipTextColor:null,tooltipMaxWidth:250,tooltipDelay:400,tooltipAllowHtml:!0,showBorder:!0,listDaysForward:30,enabledViews:["month","week","day"],enableDragDrop:!1,enableResize:!1,autoContrastText:!1,contrastLevel:"high",allowHtmlInEvents:!0,showEventBorder:!1,fetchEvents:null,onEventClick:null,onSlotClick:null,onDateSelect:null,onRangeSelect:null,onViewChange:null,onNavigate:null,onEventDrop:null},s),this._view=this._opts.defaultView,this._date=t(this._opts.defaultDate||new Date),this._events=[],this._cachedRange=null,this._cachedEvents=[],this._nowInterval=null,this._yearPickerOpen=!1,this._yearPickerBase=0,this._selectedDate=null,this._selectedRange={start:null,end:null},this._yearOutsideHandler=null,this._dragState=null,this._clickSuppressed=!1,this._tooltipElement=null,this._tooltipTimeout=null,this._currentTooltipTarget=null,this._root=document.createElement("div"),this._root.className="uc-calendar",this._opts.showGridLines||this._root.classList.add("uc-no-grid"),this._opts.showBorder||this._root.classList.add("uc-no-border"),this._el.appendChild(this._root),this._onClick=this._handleClick.bind(this),this._root.addEventListener("click",this._onClick),this._onMouseOver=this._handleTooltipHover.bind(this),this._onMouseOut=this._handleTooltipMouseOut.bind(this),this._root.addEventListener("mouseover",this._onMouseOver),this._root.addEventListener("mouseout",this._onMouseOut),(this._opts.enableDragDrop||this._opts.enableResize)&&(this._onMouseDown=this._handleDragStart.bind(this),this._onMouseMoveGlobal=this._handleDragMove.bind(this),this._onMouseUp=this._handleDragEnd.bind(this),this._onTouchStart=this._handleDragStart.bind(this),this._onTouchMove=this._handleDragMove.bind(this),this._onTouchEnd=this._handleDragEnd.bind(this),this._onKeyDown=this._handleDragKeyDown.bind(this),this._root.addEventListener("mousedown",this._onMouseDown),document.addEventListener("mousemove",this._onMouseMoveGlobal),document.addEventListener("mouseup",this._onMouseUp),this._root.addEventListener("touchstart",this._onTouchStart,{passive:!0}),document.addEventListener("touchmove",this._onTouchMove,{passive:!1}),document.addEventListener("touchend",this._onTouchEnd),document.addEventListener("keydown",this._onKeyDown),this._opts.enableDragDrop&&this._root.setAttribute("data-drag-enabled","true"),this._opts.enableResize&&this._root.setAttribute("data-resize-enabled","true")),this._root.innerHTML=this._buildShell(),this._resizeObserver=new ResizeObserver(()=>{this._resizeTimeout&&clearTimeout(this._resizeTimeout),this._resizeTimeout=setTimeout(()=>{this._renderView()},100)}),this._resizeObserver.observe(this._root),this._fetchAndRender(),this._startNowUpdater()}setView(t){t!==this._view&&(this._view=t,this._opts.onViewChange&&this._opts.onViewChange(t),this._fetchAndRender())}navigate(t){const e=new Date(this._date);"month"===this._view?(e.setMonth(e.getMonth()+t),e.setDate(1)):"week"===this._view?e.setDate(e.getDate()+7*t):e.setDate(e.getDate()+t),this._date=e;const s=this._getRange();this._opts.onNavigate&&this._opts.onNavigate(s.start,s.end),this._fetchAndRender()}goToToday(){this._date=t(new Date);const e=this._getRange();this._opts.onNavigate&&this._opts.onNavigate(e.start,e.end),this._fetchAndRender()}goToDate(e){this._date=t(o(e)),this._fetchAndRender()}refresh(){this._cachedRange=null,this._cachedEvents=[],this._fetchAndRender()}getSelectedDate(){return this._selectedDate}setSelectedDate(e){"date-picker"===this._opts.mode&&(this._selectedDate=e?t(e):null,this._renderView())}getSelectedRange(){return{...this._selectedRange}}setSelectedRange(e,s=null){"range-picker"===this._opts.mode&&(this._selectedRange={start:e?t(e):null,end:s?t(s):null},this._renderView())}clearSelection(){this._selectedDate=null,this._selectedRange={start:null,end:null},this._renderView()}destroy(){this._nowInterval&&clearInterval(this._nowInterval),this._resizeTimeout&&clearTimeout(this._resizeTimeout),this._resizeObserver&&this._resizeObserver.disconnect(),this._yearOutsideHandler&&document.removeEventListener("click",this._yearOutsideHandler),this._root.removeEventListener("click",this._onClick),this._root.removeEventListener("mouseover",this._onMouseOver),this._root.removeEventListener("mouseout",this._onMouseOut),this._tooltipElement&&(this._tooltipElement.remove(),this._tooltipElement=null),this._tooltipTimeout&&(clearTimeout(this._tooltipTimeout),this._tooltipTimeout=null),(this._opts.enableDragDrop||this._opts.enableResize)&&(this._root.removeEventListener("mousedown",this._onMouseDown),document.removeEventListener("mousemove",this._onMouseMoveGlobal),document.removeEventListener("mouseup",this._onMouseUp),this._root.removeEventListener("touchstart",this._onTouchStart),document.removeEventListener("touchmove",this._onTouchMove),document.removeEventListener("touchend",this._onTouchEnd),document.removeEventListener("keydown",this._onKeyDown)),this._root.remove()}_getRange(){if("month"===this._view){const s=l(this._date.getFullYear(),this._date.getMonth(),this._opts.weekStartsOn);return{start:t(s[0]),end:e(s[s.length-1])}}if("week"===this._view){const s=r(this._date,this._opts.weekStartsOn);return{start:t(s[0]),end:e(s[6])}}if("list"===this._view){const t=new Date,e=new Date(t);return e.setDate(e.getDate()+this._opts.listDaysForward),{start:t,end:e}}return{start:t(this._date),end:e(this._date)}}_getMonthGridRange(){const s=l(this._date.getFullYear(),this._date.getMonth(),this._opts.weekStartsOn);return{start:t(s[0]),end:e(s[s.length-1])}}async _fetchAndRender(){this._updateToolbar();const t="date-picker"===this._opts.mode||"range-picker"===this._opts.mode;if(!this._opts.fetchEvents||t)return void this._renderView();const e=this._getRange();if(this._cachedRange&&this._cachedRange.start<=e.start&&this._cachedRange.end>=e.end)return this._events=this._cachedEvents.filter(t=>t.end>=e.start&&t.start<=e.end),void this._renderView();const s=this._root.querySelector(".uc-loading");s&&(s.style.display="flex");try{let t;t="list"===this._view?e:this._getMonthGridRange();const s=(await this._opts.fetchEvents(t.start,t.end)||[]).map(t=>({...t,start:o(t.start),end:o(t.end)}));this._cachedRange={start:t.start,end:t.end},this._cachedEvents=s,this._events=s.filter(t=>t.end>=e.start&&t.start<=e.end)}catch(t){this._events=[]}s&&(s.style.display="none"),this._renderView()}_renderView(){const t=this._root.querySelector(".uc-view-container");if(!t)return;const e=this._root.parentElement;if(e&&e.style.height&&"auto"!==e.style.height&&""!==e.style.height?this._root.setAttribute("data-height-constrained","true"):this._root.removeAttribute("data-height-constrained"),"month"===this._view)t.innerHTML=this._buildMonthView();else if("week"===this._view){const e=r(this._date,this._opts.weekStartsOn);t.innerHTML=this._buildWeekOrDayView(e),this._scrollToBusinessHours(t)}else"list"===this._view?t.innerHTML=this._buildListView():(t.innerHTML=this._buildWeekOrDayView([this._date]),this._scrollToBusinessHours(t))}_scrollToBusinessHours(t){requestAnimationFrame(()=>{const e=t.querySelector(".uc-time-body");if(!e)return;const s=parseFloat(getComputedStyle(this._root).getPropertyValue("--cal-hour-height"))||60;e.scrollTop=7*s})}_renderToolbar(){const t=this._root.querySelector(".uc-toolbar");if(!t)return;const e=document.createElement("div");e.innerHTML=this._buildToolbar(),this._root.replaceChild(e.firstElementChild,t)}_buildShell(){return`\n ${this._buildToolbar()}\n <div class="uc-loading" style="display:none">\n <div class="uc-spinner"></div>\n </div>\n <div class="uc-view-container"></div>\n `}_updateToolbar(){const t=this._root.querySelector(".uc-toolbar");if(t){const e=document.createElement("div");e.innerHTML=this._buildToolbar();const s=e.firstElementChild;s&&t.replaceWith(s)}}_buildToolbar(){if(!this._opts.showToolbar)return"";const t=this._date.getFullYear(),e=(new Date).getFullYear();let s,n=!0;if("month"===this._view)s=new Intl.DateTimeFormat(this._opts.locale,{month:"long"}).format(this._date);else if("week"===this._view){const t=r(this._date,this._opts.weekStartsOn);s=function(t,e,s){if(t.getMonth()===e.getMonth()&&t.getFullYear()===e.getFullYear())return`${new Intl.DateTimeFormat(s,{month:"long"}).format(t)} ${t.getDate()}–${e.getDate()}`;const i=t=>new Intl.DateTimeFormat(s,{month:"short",day:"numeric"}).format(t);return`${i(t)} – ${i(e)}`}(t[0],t[6],this._opts.locale)}else if("list"===this._view){const t=new Date,e=new Date(t);e.setDate(e.getDate()+this._opts.listDaysForward),s=function(t,e,s){if(t.getMonth()===e.getMonth()&&t.getFullYear()===e.getFullYear())return`${new Intl.DateTimeFormat(s,{month:"long"}).format(t)} ${t.getDate()}–${e.getDate()}, ${t.getFullYear()}`;const i=t=>new Intl.DateTimeFormat(s,{month:"short",day:"numeric"}).format(t);return`${i(t)} – ${i(e)}, ${e.getFullYear()}`}(t,e,this._opts.locale),n=!1}else s=new Intl.DateTimeFormat(this._opts.locale,{weekday:"long",month:"long",day:"numeric"}).format(this._date);let a="";if(this._opts.showYearPicker&&this._yearPickerOpen){const s=this._yearPickerBase,i=Array.from({length:12},(i,n)=>{const a=s+n,o=a===t;return`<button class="${"uc-year-item"+(o?" uc-active":"")+(a===e&&!o?" uc-today-year":"")}" data-action="select-year" data-year="${a}">${a}</button>`}).join("");a=`\n <div class="uc-year-picker">\n <div class="uc-year-picker-nav">\n <button class="uc-year-nav-btn" data-action="year-prev" aria-label="Previous years">‹</button>\n <span class="uc-year-range">${s} – ${s+11}</span>\n <button class="uc-year-nav-btn" data-action="year-next" aria-label="Next years">›</button>\n </div>\n <div class="uc-year-grid">${i}</div>\n </div>`}const o=this._opts.locale,l=b(o,"today"),c=b(o,"month"),d=b(o,"week"),u=b(o,"day"),p=b(o,"list");let _="";if(this._opts.showNavigation||this._opts.showTodayButton){const t="list"===this._view?" disabled":"",e=this._opts.showNavigation?`<button class="uc-btn uc-nav-btn" data-action="prev" aria-label="Previous"${t}>‹</button>`:"",s=new Date,n=i(this._date,s)?" uc-active":"";_=`\n <div class="uc-toolbar-section uc-toolbar-left">\n ${e}${this._opts.showTodayButton?`<button class="uc-btn uc-today-btn${n}" data-action="today"${t}>${h(l)}</button>`:""}${this._opts.showNavigation?`<button class="uc-btn uc-nav-btn" data-action="next" aria-label="Next"${t}>›</button>`:""}\n </div>`}let v="";if(this._opts.showTitle){const e=n?this._opts.showYearPicker?`<button class="uc-year-btn${this._yearPickerOpen?" uc-open":""}" data-action="year-pick" aria-label="Select year">${t}</button>`:t:"";v=`\n <div class="uc-toolbar-section uc-toolbar-center">\n <h2 class="uc-title">\n <span class="uc-title-main">${h(s)}</span>\n ${e}\n </h2>\n ${a}\n </div>`}let g="";const m="date-picker"===this._opts.mode||"range-picker"===this._opts.mode;if(this._opts.showViewSwitcher&&!m){const t=this._opts.enabledViews,e=[],s=[];if(t.includes("month")&&(e.push(`<button class="uc-btn uc-view-btn${"month"===this._view?" uc-active":""}" data-view="month">${h(c)}</button>`),s.push(`<button class="uc-dropdown-item${"month"===this._view?" uc-active":""}" data-view="month">${h(c)}</button>`)),t.includes("week")&&(e.push(`<button class="uc-btn uc-view-btn${"week"===this._view?" uc-active":""}" data-view="week">${h(d)}</button>`),s.push(`<button class="uc-dropdown-item${"week"===this._view?" uc-active":""}" data-view="week">${h(d)}</button>`)),t.includes("day")&&(e.push(`<button class="uc-btn uc-view-btn${"day"===this._view?" uc-active":""}" data-view="day">${h(u)}</button>`),s.push(`<button class="uc-dropdown-item${"day"===this._view?" uc-active":""}" data-view="day">${h(u)}</button>`)),t.includes("list")&&(e.push(`<button class="uc-btn uc-view-btn${"list"===this._view?" uc-active":""}" data-view="list">${h(p)}</button>`),s.push(`<button class="uc-dropdown-item${"list"===this._view?" uc-active":""}" data-view="list">${h(p)}</button>`)),e.length>0){const t=b(o,"today"),n=i(this._date,new Date)?" uc-active":"",a="list"===this._view?" disabled":"";s.push('<div class="uc-dropdown-separator"></div>'),s.push(`<button class="uc-dropdown-item${n}" data-action="today"${a}>${h(t)}</button>`),g=`\n <div class="uc-toolbar-section uc-toolbar-right">\n <div class="uc-view-switcher">\n ${e.join("")}\n </div>\n <div class="uc-view-dropdown">\n <button class="uc-btn uc-view-dropdown-btn" aria-label="View options">\n <span class="uc-view-icon">⊞</span>\n </button>\n <div class="uc-dropdown-menu">\n ${s.join("")}\n </div>\n </div>\n </div>`}}return`\n <div class="uc-toolbar">\n ${_}${v}${g}\n </div>\n `}_buildMonthView(){const{locale:s,weekStartsOn:i}=this._opts,n=l(this._date.getFullYear(),this._date.getMonth(),i),a=d(s,i,this._opts.weekdayFormat),o=this._events.map(s=>({...s,_origStart:s.start,start:D(s)?t(s.start):s.start,end:D(s)?e(s.end):s.end})),r=a.map(t=>`<div class="uc-month-day-name">${h(t)}</div>`).join(""),c=[];for(let t=0;t<n.length;t+=7)c.push(n.slice(t,t+7));let u;const p=this._root.parentElement,_=p&&p.style.height&&"auto"!==p.style.height&&""!==p.style.height;if(_){const t=p.clientHeight,e=this._root.querySelector(".uc-toolbar"),s=this._root.querySelector(".uc-month-header"),i=t-(e?e.offsetHeight:50)-(s?s.offsetHeight:36)-2,n=c.length,a=Math.floor(i/n),o=Math.floor((a-30-28)/24);u=Math.max(0,o)}else u=1/0;const v=_?Math.floor((p.clientHeight-(this._root.querySelector(".uc-toolbar")?.offsetHeight||50)-(this._root.querySelector(".uc-month-header")?.offsetHeight||36)-2)/c.length):null;return`\n <div class="uc-month-view">\n <div class="uc-month-header">${r}</div>\n <div class="uc-month-body">${c.map(t=>this._buildWeekRow(t,o,u,v)).join("")}</div>\n </div>\n `}_buildWeekRow(t,e,s,a=null){const o=this._date.getMonth(),r=e.filter(D),l=e.filter(t=>!D(t)),d=S(r,t),u=Array.from({length:7},()=>new Set);for(const{startCol:t,endCol:e,slot:s}of d)for(let i=t;i<=e;i++)u[i].add(s);const _=t.map(t=>l.filter(e=>i(e.start,t)).sort((t,e)=>t.start-e.start)),v="date-picker"===this._opts.mode||"range-picker"===this._opts.mode,g=v?" uc-day-align-center-picker":"center"===this._opts.monthDayNumberAlign?" uc-day-align-center":"right"===this._opts.monthDayNumberAlign?" uc-day-align-right":"",m=t.map((t,e)=>{const s=!v&&n(t)?" uc-today":"",a=t.getMonth()!==o?" uc-other-month":"";let r="";if("date-picker"===this._opts.mode&&this._selectedDate)r=i(t,this._selectedDate)?" uc-selected":"";else if("range-picker"===this._opts.mode&&this._selectedRange.start)if(this._selectedRange.end){r=t>=this._selectedRange.start&&t<=this._selectedRange.end?" uc-in-range":"",i(t,this._selectedRange.start)&&(r+=" uc-range-start"),i(t,this._selectedRange.end)&&(r+=" uc-range-end")}else r=i(t,this._selectedRange.start)?" uc-range-start":"";return`\n <div class="uc-day-cell${s}${a}${r}${g}" data-date="${t.toISOString()}" data-action="day-click">\n <span class="uc-day-number" data-action="day-number" data-date="${t.toISOString()}">${t.getDate()}</span>\n </div>`}).join("");let f="";for(const{event:t,startCol:e,endCol:i,slot:n,isStart:a,isEnd:o}of d){if(s!==1/0&&n>=s)continue;const r=100/7,l=e*r,c=(i-e+1)*r,d=`calc(var(--cal-header-day-height) + ${n} * (var(--cal-event-height) + var(--cal-event-gap)) + 4px)`,u=t.color||"var(--cal-event-bg)",_=t.textColor||(this._opts.autoContrastText?y(u,this._opts.contrastLevel):"var(--cal-event-text)"),v=t.borderColor||(this._opts.showEventBorder?w(u,25):"transparent"),g=this._opts.showEventBorder?`border:1px solid ${h(v)};`:"",m=a?"var(--cal-event-border-radius)":"0",b=o?"var(--cal-event-border-radius)":"0",D=a?"":" uc-continues-left",S=o?"":" uc-continues-right",$=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"",k=this._opts.enableResize&&o?'<div class="uc-resize-handle-right" data-action="resize-handle"></div>':"";f+=`\n <div class="uc-event-bar${D}${S}"\n style="left:calc(${l}% + 2px);width:calc(${c}% - 4px);top:${d};background:${h(u)};color:${h(_)};${g}border-radius:${m} ${b} ${b} ${m};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${$}>\n ${a?`<span class="uc-event-title">${p(t.title,this._opts.allowHtmlInEvents,t.allowHtml)}</span>`:" "}\n ${k}\n </div>`}let b=-1;for(let e=0;e<7;e++){const i=100/7,n=e*i,a=t[e],o=[...u[e]],r=o.length>0?Math.max(...o)+1:0,l=_[e];if(s===1/0)l.forEach((t,e)=>{const s=r+e;b=Math.max(b,s);const a=`calc(var(--cal-header-day-height) + ${s} * (var(--cal-event-height) + var(--cal-event-gap)) + 4px)`,o=t.color||"var(--cal-event-bg)",l=t.textColor||(this._opts.autoContrastText?y(o,this._opts.contrastLevel):"var(--cal-event-text)"),d=c(t.start,this._opts.locale,this._opts.use24Hour),u=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"";if("list"===this._opts.monthTimedEventStyle){const e=this._opts.showTimeInItems?`<span class="uc-event-time">${h(d)}</span>`:"";f+=`\n <div class="uc-event-bar uc-event-bar--list"\n style="left:calc(${n}% + 2px);width:calc(${i}% - 4px);top:${a};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${u}>\n <span class="uc-event-dot" style="background:${h(o)};"></span>\n ${e}\n <span class="uc-event-title">${p(t.title,this._opts.allowHtmlInEvents,t.allowHtml)}</span>\n </div>`}else{const e=t.borderColor||(this._opts.showEventBorder?w(o,25):"transparent"),s=this._opts.showEventBorder?`border:1px solid ${h(e)};`:"",r=this._opts.showTimeInItems?`<span class="uc-event-time">${h(d)}</span>`:"";f+=`\n <div class="uc-event-bar"\n style="left:calc(${n}% + 2px);width:calc(${i}% - 4px);top:${a};background:${h(o)};color:${h(l)};${s}"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${u}>\n ${r}\n <span class="uc-event-title">${p(t.title,this._opts.allowHtmlInEvents,t.allowHtml)}</span>\n </div>`}});else{const t=[...u[e]].filter(t=>t>=s).length,o=[];for(let t=r;t<s;t++)o.push(t);let d=t;if(l.forEach((t,e)=>{if(e<o.length){const s=o[e];b=Math.max(b,s);const a=`calc(var(--cal-header-day-height) + ${s} * (var(--cal-event-height) + var(--cal-event-gap)) + 4px)`,r=t.color||"var(--cal-event-bg)",l=t.textColor||(this._opts.autoContrastText?y(r,this._opts.contrastLevel):"var(--cal-event-text)"),d=c(t.start,this._opts.locale,this._opts.use24Hour),u=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"";if("list"===this._opts.monthTimedEventStyle){const e=this._opts.showTimeInItems?`<span class="uc-event-time">${h(d)}</span>`:"";f+=`\n <div class="uc-event-bar uc-event-bar--list"\n style="left:calc(${n}% + 2px);width:calc(${i}% - 4px);top:${a};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${u}>\n <span class="uc-event-dot" style="background:${h(r)};"></span>\n ${e}\n <span class="uc-event-title">${p(t.title,this._opts.allowHtmlInEvents,t.allowHtml)}</span>\n </div>`}else{const e=t.borderColor||(this._opts.showEventBorder?w(r,25):"transparent"),s=this._opts.showEventBorder?`border:1px solid ${h(e)};`:"",o=this._opts.showTimeInItems?`<span class="uc-event-time">${h(d)}</span>`:"";f+=`\n <div class="uc-event-bar"\n style="left:calc(${n}% + 2px);width:calc(${i}% - 4px);top:${a};background:${h(r)};color:${h(l)};${s}"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${u}>\n ${o}\n <span class="uc-event-title">${p(t.title,this._opts.allowHtmlInEvents,t.allowHtml)}</span>\n </div>`}}else d++}),d>0){f+=`\n <div class="uc-more-link"\n style="left:calc(${n}% + 2px);width:calc(${i}% - 4px);top:${`calc(var(--cal-header-day-height) + ${s} * (var(--cal-event-height) + var(--cal-event-gap)) + 2px)`};"\n data-date="${a.toISOString()}" data-action="more-click">\n +${d} more\n </div>`}}}for(const{slot:t}of d)(s===1/0||t<s)&&(b=Math.max(b,t));let $="";if(null!==a)$=` style="height: ${a}px;"`;else if(s===1/0){$=` style="min-height: ${b>=0?`calc(var(--cal-header-day-height) + ${b+1} * (var(--cal-event-height) + var(--cal-event-gap)) + 8px)`:"80px"};"`}return`\n <div class="uc-week-row"${$}>\n <div class="uc-week-cells">${m}</div>\n <div class="uc-week-events">${f}</div>\n </div>`}_buildWeekOrDayView(s){const{locale:a,weekStartsOn:o,use24Hour:r}=this._opts,l=1===s.length,u=l?" uc-day-header-single":"",_=l?[new Intl.DateTimeFormat(a,{weekday:this._opts.weekdayFormat}).format(s[0])]:d(a,o,this._opts.weekdayFormat),v=t(s[0]),g=e(s[s.length-1]),m=this._events.filter(t=>D(t)&&t.start<=g&&e(t.end)>=v).map(s=>({...s,start:t(s.start),end:e(s.end)})),f=this._events.filter(t=>!D(t)&&t.start>=v&&t.start<=g),$=s.map((t,e)=>{const s=n(t)?" uc-today":"",i=t.getDate();return`\n <div class="uc-week-day-header${s}">\n <span class="uc-week-day-name">${h(_[e])}</span>\n <span class="uc-week-day-num" data-action="day-number" data-date="${t.toISOString()}">${i}</span>\n </div>`}).join(""),k=l?m.map((t,e)=>({event:t,startCol:0,endCol:0,slot:e,isStart:!0,isEnd:!0})):S(m,s),E=k.length?Math.max(...k.map(t=>t.slot))+1:0;let T="";for(const{event:t,startCol:e,endCol:i,slot:n,isStart:a,isEnd:o}of k){const r=100/s.length,l=e*r,c=(i-e+1)*r,d=`calc(${n} * (var(--cal-event-height) + 3px) + 2px)`,u=t.color||"var(--cal-event-bg)",_=t.textColor||(this._opts.autoContrastText?y(u,this._opts.contrastLevel):"var(--cal-event-text)"),v=t.borderColor||(this._opts.showEventBorder?w(u,25):"transparent"),g=this._opts.showEventBorder?`border:1px solid ${h(v)};`:"",m=a?"var(--cal-event-border-radius)":"0",f=o?"var(--cal-event-border-radius)":"0",b=a?"":" uc-continues-left",D=o?"":" uc-continues-right",S=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"",$=this._opts.enableResize&&o?'<div class="uc-resize-handle-right" data-action="resize-handle"></div>':"";T+=`\n <div class="uc-event-bar${b}${D}"\n style="left:calc(${l}% + 2px);width:calc(${c}% - 4px);top:${d};background:${h(u)};color:${h(_)};${g}border-radius:${m} ${f} ${f} ${m};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${S}>\n <span class="uc-event-title">${p(t.title,this._opts.allowHtmlInEvents,t.allowHtml)}</span>\n ${$}\n </div>`}const M=`calc(${Math.max(1,E)} * (var(--cal-event-height) + 3px) + 6px)`,x=new Date,C=60*x.getHours()+x.getMinutes(),H=s.length,R=Array.from({length:24},(t,e)=>`<div class="uc-hour-cell"><span class="uc-hour-label">${h(0===e?"":c(new Date(2e3,0,1,e),"en-US",r))}</span></div>`).join(""),L=s.map(t=>{const e=n(t)?" uc-today":"",s=Array.from({length:24},()=>'<div class="uc-hour-row"><div class="uc-half-hour-line"></div></div>').join(""),o=function(t){if(!t.length)return[];const e=[...t].sort((t,e)=>t.start-e.start||e.end-t.end),s=[],i=[];for(const t of e){let e=s.findIndex(e=>e<=t.start);-1===e?(e=s.length,s.push(t.end)):s[e]=t.end,i.push({event:t,col:e})}return i.map(t=>{const e=i.filter(e=>e.event.start<t.event.end&&e.event.end>t.event.start),s=Math.max(...e.map(t=>t.col))+1;return{...t,totalCols:s}})}(f.filter(e=>i(e.start,t))).map(({event:t,col:e,totalCols:s})=>{const i=60*t.start.getHours()+t.start.getMinutes(),n=60*t.end.getHours()+t.end.getMinutes(),o=`calc(${i} / 60 * var(--cal-hour-height))`,l=`calc(${Math.max(n-i,30)} / 60 * var(--cal-hour-height))`,d=100/s,u=`calc(${e*d}% + 1px)`,_=`calc(${d}% - 2px)`,v=t.color||"var(--cal-event-bg)",g=t.textColor||(this._opts.autoContrastText?y(v,this._opts.contrastLevel):"var(--cal-event-text)"),m=t.borderColor||(this._opts.showEventBorder?w(v,25):"transparent"),f=this._opts.showEventBorder?`border:1px solid ${h(m)};`:"",b=c(t.start,a,r),D=n-i<=60?" uc-timed-event--short":"",S=this._opts.showTimeInItems?`<span class="uc-event-time">${h(b)}</span>`:"",$=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"",k=this._opts.enableResize?'<div class="uc-resize-handle" data-action="resize-handle"></div>':"";return`\n <div class="uc-timed-event${D}"\n style="top:${o};height:${l};left:${u};width:${_};background:${h(v)};color:${h(g)};${f}"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${$}>\n ${S}\n <span class="uc-event-title">${p(t.title,this._opts.allowHtmlInEvents,t.allowHtml)}</span>\n ${k}\n </div>`}).join(""),l=n(t)?`<div class="uc-now-indicator" style="top:calc(${C} / 60 * var(--cal-hour-height));">\n <span class="uc-now-dot"></span>\n <span class="uc-now-line"></span>\n </div>`:"";return`<div class="uc-time-col${e}" data-date="${t.toISOString()}" data-action="slot-col">\n ${s}${o}${l}\n </div>`}).join("");return`\n <div class="${l?"uc-day-view":"uc-week-view"}">\n <div class="uc-week-header">\n <div class="uc-time-gutter-spacer"></div>\n <div class="uc-week-day-headers${u}">${$}</div>\n </div>\n <div class="uc-all-day-section">\n <div class="uc-time-gutter-spacer uc-all-day-label">${b(this._opts.locale,"allDay")}</div>\n <div class="uc-all-day-events" style="min-height:${M}">${T}</div>\n </div>\n <div class="uc-time-body">\n <div class="uc-time-grid-inner">\n <div class="uc-time-gutter">${R}</div>\n <div class="uc-time-columns" style="--uc-col-count:${H}">${L}</div>\n </div>\n </div>\n </div>`}_buildListView(){const e=new Date,s=t(e),i=new Date(s);i.setDate(i.getDate()+this._opts.listDaysForward);const n=this._events.filter(t=>t.end>=e&&t.start<=i);n.sort((e,i)=>{const n=(e.start<s?s:t(e.start))-(i.start<s?s:t(i.start));return 0!==n?n:e.start-i.start});const a=new Map;if(n.forEach(e=>{const i=e.start<s?s:t(e.start),n=`${(o=i).getFullYear()}-${String(o.getMonth()+1).padStart(2,"0")}-${String(o.getDate()).padStart(2,"0")}`;var o;a.has(n)||a.set(n,[]),a.get(n).push(e)}),0===a.size)return'\n <div class="uc-list-view">\n <div class="uc-list-empty">\n <p>No upcoming events</p>\n </div>\n </div>';let r="";return a.forEach((t,e)=>{const s=o(e),i=new Intl.DateTimeFormat(this._opts.locale,{weekday:"long",year:"numeric",month:"long",day:"numeric"}).format(s),n=t.map(t=>{const e=D(t)?"All day":c(t.start,this._opts.locale,this._opts.use24Hour),s=t.color||"var(--cal-event-bg)",i=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||"")}"`:"";return`\n <div class="uc-list-event" data-event-id="${h(t.id)}" data-action="event-click" ${i}>\n <div class="uc-list-event-indicator" style="background: ${h(s)};"></div>\n <div class="uc-list-event-time">${h(e)}</div>\n <div class="uc-list-event-content">\n <div class="uc-list-event-title">${p(t.title,this._opts.allowHtmlInEvents,t.allowHtml)}</div>\n </div>\n </div>`}).join("");r+=`\n <div class="uc-list-date-group">\n <div class="uc-list-date-header">${h(i)}</div>\n <div class="uc-list-events">${n}</div>\n </div>`}),`<div class="uc-list-view">${r}</div>`}_closeYearPicker(){this._yearOutsideHandler&&(document.removeEventListener("click",this._yearOutsideHandler),this._yearOutsideHandler=null),this._yearPickerOpen=!1,this._renderToolbar()}_handleClick(e){if(this._clickSuppressed)return void(this._clickSuppressed=!1);const s=e.target.closest(".uc-view-dropdown-btn");if(s){e.stopPropagation();const t=s.parentElement,i=t.classList.contains("uc-open");return this._root.querySelectorAll(".uc-view-dropdown.uc-open").forEach(t=>t.classList.remove("uc-open")),void(i||(t.classList.add("uc-open"),this._dropdownOutsideHandler=e=>{e.target.closest(".uc-view-dropdown")||(t.classList.remove("uc-open"),document.removeEventListener("click",this._dropdownOutsideHandler))},setTimeout(()=>document.addEventListener("click",this._dropdownOutsideHandler),0)))}const i=e.target.closest("[data-view]");if(i)return this.setView(i.dataset.view),void this._root.querySelectorAll(".uc-view-dropdown.uc-open").forEach(t=>t.classList.remove("uc-open"));const n=e.target.closest("[data-action]");if(!n)return;switch(n.dataset.action){case"prev":this.navigate(-1);break;case"next":this.navigate(1);break;case"today":this.goToToday(),this._root.querySelectorAll(".uc-view-dropdown.uc-open").forEach(t=>t.classList.remove("uc-open"));break;case"year-pick":e.stopPropagation(),this._yearPickerOpen?this._closeYearPicker():(this._yearPickerOpen=!0,this._yearPickerBase=this._date.getFullYear()-4,this._renderToolbar(),this._yearOutsideHandler=t=>{t.target.closest(".uc-year-picker")||t.target.closest('[data-action="year-pick"]')||this._closeYearPicker()},setTimeout(()=>document.addEventListener("click",this._yearOutsideHandler),0));break;case"year-prev":e.stopPropagation(),this._yearPickerBase-=12,this._renderToolbar();break;case"year-next":e.stopPropagation(),this._yearPickerBase+=12,this._renderToolbar();break;case"select-year":{e.stopPropagation();const t=parseInt(n.dataset.year,10);this._date=new Date(this._date.getFullYear()!==t?new Date(this._date).setFullYear(t):this._date),this._closeYearPicker();const s=this._getRange();this._opts.onNavigate&&this._opts.onNavigate(s.start,s.end),this._fetchAndRender();break}case"event-click":{e.stopPropagation();const t=n.dataset.eventId,s=this._events.find(e=>String(e.id)===String(t));s&&this._opts.onEventClick&&this._opts.onEventClick(s,e);break}case"day-click":{if(e.target.closest('[data-action="day-number"]'))break;if(e.target.closest('[data-action="event-click"]'))break;const t=new Date(n.dataset.date);"date-picker"===this._opts.mode?this._handleDateSelection(t):"range-picker"===this._opts.mode?this._handleRangeSelection(t):this._opts.onSlotClick&&this._opts.onSlotClick(t,e);break}case"day-number":{e.stopPropagation();const s=new Date(n.dataset.date);if("date-picker"===this._opts.mode)this._handleDateSelection(s);else if("range-picker"===this._opts.mode)this._handleRangeSelection(s);else{if(!this._opts.enabledViews.includes("day"))break;this._date=t(s),this._view="day",this._opts.onViewChange&&this._opts.onViewChange("day"),this._fetchAndRender()}break}case"more-click":{if(e.stopPropagation(),!this._opts.enabledViews.includes("day"))break;const s=new Date(n.dataset.date);this._date=t(s),this._view="day",this._opts.onViewChange&&this._opts.onViewChange("day"),this._fetchAndRender();break}case"slot-col":if(e.target.closest('[data-action="event-click"]'))break;if(this._opts.onSlotClick){const t=n,s=t.getBoundingClientRect(),i=e.clientY-s.top,a=parseFloat(getComputedStyle(this._root).getPropertyValue("--cal-hour-height"))||60,o=Math.max(0,Math.floor(i/a*60)),r=Math.floor(o/60)%24,l=15*Math.round(o%60/15),c=new Date(t.dataset.date);c.setHours(r,l,0,0),this._opts.onSlotClick(c,e)}}}_handleDateSelection(e){const s=t(e);this._selectedDate=s,this._renderView(),this._opts.onDateSelect&&this._opts.onDateSelect(s)}_handleRangeSelection(e){const s=t(e);!this._selectedRange.start||this._selectedRange.start&&this._selectedRange.end?this._selectedRange={start:s,end:null}:(s<this._selectedRange.start?this._selectedRange={start:s,end:this._selectedRange.start}:this._selectedRange.end=s,this._opts.onRangeSelect&&this._opts.onRangeSelect(this._selectedRange.start,this._selectedRange.end)),this._renderView()}_handleTooltipHover(t){const e=t.target.closest("[data-tooltip]");if(!e)return;const s=e.dataset.tooltip;if(!s||!s.trim())return;const i="true"===e.dataset.tooltipHtml||this._opts.tooltipAllowHtml;this._showTooltip(e,s,i)}_handleTooltipMouseOut(t){const e=t.target.closest("[data-tooltip]");e&&(t.relatedTarget&&e.contains(t.relatedTarget)||this._hideTooltip())}_createTooltipElement(){if(this._tooltipElement)return this._tooltipElement;const t=document.createElement("div");return t.className="uc-tooltip-container",t.innerHTML='<div class="uc-tooltip-content"></div><div class="uc-tooltip-arrow"></div>',this._opts.tooltipBgColor&&t.style.setProperty("--tooltip-bg",this._opts.tooltipBgColor),this._opts.tooltipTextColor&&t.style.setProperty("--tooltip-text",this._opts.tooltipTextColor),this._opts.tooltipMaxWidth&&t.style.setProperty("--tooltip-max-width",this._opts.tooltipMaxWidth+"px"),document.body.appendChild(t),this._tooltipElement=t,t}_showTooltip(t,e,s){this._opts.showTooltips&&e&&(this._tooltipTimeout&&clearTimeout(this._tooltipTimeout),this._tooltipTimeout=setTimeout(()=>{this._tooltipElement||this._createTooltipElement();const i=this._tooltipElement.querySelector(".uc-tooltip-content");s?(i.innerHTML=u(e),i.querySelectorAll("a").forEach(t=>{t.setAttribute("target","_blank"),t.setAttribute("rel","noopener noreferrer")})):i.textContent=e,this._positionTooltipFixed(t),this._tooltipElement.style.opacity="1",this._tooltipElement.style.visibility="visible",this._currentTooltipTarget=t},this._opts.tooltipDelay))}_hideTooltip(){this._tooltipTimeout&&(clearTimeout(this._tooltipTimeout),this._tooltipTimeout=null),this._tooltipElement&&(this._tooltipElement.style.opacity="0",this._tooltipElement.style.visibility="hidden"),this._currentTooltipTarget=null}_positionTooltipFixed(t){if(!this._tooltipElement)return;const e=t.getBoundingClientRect(),s=this._tooltipElement.getBoundingClientRect();let i=e.top-s.height-8,n=e.left+e.width/2-s.width/2;n+s.width>window.innerWidth-10&&(n=window.innerWidth-s.width-10),n<10&&(n=10),i<10?(i=e.bottom+8,this._tooltipElement.classList.add("uc-tooltip-position-bottom")):this._tooltipElement.classList.remove("uc-tooltip-position-bottom"),this._tooltipElement.style.top=i+"px",this._tooltipElement.style.left=n+"px";const a=e.left+e.width/2-n,o=this._tooltipElement.querySelector(".uc-tooltip-arrow");o&&(o.style.left=a+"px")}_handleDragStart(t){if("list"===this._view)return;const e=t.target.closest('[data-action="resize-handle"]'),s=!!e,i=e&&e.classList.contains("uc-resize-handle-right"),n=e&&e.classList.contains("uc-resize-handle"),a=t.target.closest('[data-event-id][data-action="event-click"]');if(!a)return;if(t.target.closest('.uc-year-picker, [data-action="year-pick"], .uc-toolbar'))return;const o=a.dataset.eventId,r=this._events.find(t=>String(t.id)===String(o));if(!r)return;const l=r.allDay||!1;if(s&&!this._opts.enableResize)return;if(!s&&!this._opts.enableDragDrop)return;if(n&&(l||"week"!==this._view&&"day"!==this._view))return;if(i&&!l)return;const c=this._getEventPosition(t),d=a.getBoundingClientRect(),h=c.x-d.left,u=c.y-d.top;this._dragState={mode:s?"resize":"move",resizeDirection:i?"horizontal":n?"vertical":null,eventId:o,originalEvent:{...r,start:new Date(r.start),end:new Date(r.end)},dragElement:null,startX:c.x,startY:c.y,currentX:c.x,currentY:c.y,offsetX:h,offsetY:u,originalView:this._view,isAllDay:l,hasMoved:!1,startTime:Date.now(),originalElement:a}}_handleDragMove(t){if(!this._dragState)return;const e=this._getEventPosition(t);this._dragState.currentX=e.x,this._dragState.currentY=e.y;const s=e.x-this._dragState.startX,i=e.y-this._dragState.startY,n=Math.sqrt(s*s+i*i);if(!this._dragState.hasMoved){const t=Date.now()-this._dragState.startTime;if(!(n>5||t>150))return;this._dragState.hasMoved=!0,this._createDragElement(),this._root.classList.add("uc-dragging")}if(t.type.startsWith("touch")&&t.preventDefault(),this._dragState.dragElement)if("resize"===this._dragState.mode&&"vertical"===this._dragState.resizeDirection)this._updateResizePreview(e.x,e.y);else if("resize"===this._dragState.mode&&"horizontal"===this._dragState.resizeDirection)this._updateHorizontalResizePreview(e.x);else{const t=this._getSnappedPosition(e.x,e.y);t?(this._dragState.dragElement.style.left=t.x+"px",this._dragState.dragElement.style.top=t.y+"px"):(this._dragState.dragElement.style.left=e.x-this._dragState.offsetX+"px",this._dragState.dragElement.style.top=e.y-this._dragState.offsetY+"px")}}_getSnappedPosition(t,e){if("week"!==this._view&&"day"!==this._view)return null;const s=this._calculateDropPosition(t,e);if(!s)return null;const i=this._dragState.dragElement;if(!i)return null;if(!s.isAllDay){const e=this._root.querySelector(".uc-time-columns");if(!e)return null;const n=e.querySelectorAll(".uc-time-col");let a=null;for(const e of n){const s=e.getBoundingClientRect();if(t>=s.left&&t<s.right){a=e;break}}if(!a)return null;const o=a.getBoundingClientRect(),r=(60*s.date.getHours()+s.date.getMinutes())/60*(parseFloat(getComputedStyle(this._root).getPropertyValue("--cal-hour-height"))||60),l=i.querySelector(".uc-event-time");if(l){const t=c(s.date,this._opts.locale,this._opts.use24Hour);l.textContent=t}return{x:o.left+4,y:o.top+r}}if(s.isAllDay){const e=this._root.querySelector(".uc-all-day-events");if(!e)return null;const s=e.getBoundingClientRect(),i="day"===this._view?[this._date]:r(this._date,this._opts.weekStartsOn),n=s.width/i.length,a=t-s.left,o=Math.floor(a/n);return o<0||o>=i.length?null:{x:s.left+o*n+4,y:s.top+4}}return null}_updateResizePreview(t,e){const s=this._dragState.dragElement,i=this._dragState.originalEvent,n=this._root.querySelector(".uc-time-columns");if(!n)return;const a=n.querySelectorAll(".uc-time-col");let o=null;for(const e of a){const s=e.getBoundingClientRect();if(t>=s.left&&t<s.right){o=e;break}}if(!o)return;const r=o.getBoundingClientRect(),l=Math.max(0,e-r.top),d=parseFloat(getComputedStyle(this._root).getPropertyValue("--cal-hour-height"))||60,h=Math.floor(l/d*60),u=Math.floor(h/60)%24,p=15*Math.round(h%60/15),_=new Date(i.start);_.setHours(u,p,0,0),_<=i.start&&_.setTime(i.start.getTime()+9e5);const v=(_-i.start)/6e4/60*d;s.style.height=v+"px";const g=s.querySelector(".uc-event-time");if(g){const t=c(i.start,this._opts.locale,this._opts.use24Hour),e=c(_,this._opts.locale,this._opts.use24Hour);g.textContent=`${t} - ${e}`}this._dragState.newEndTime=_}_updateHorizontalResizePreview(t){const s=this._dragState.dragElement,i=this._dragState.originalEvent,n=this._dragState.originalElement;if(!n)return;const a=n.getBoundingClientRect(),o=t-a.left;let c;if("month"===this._view){const s=this._root.querySelector(".uc-month-body");if(!s)return;const i=s.getBoundingClientRect(),n=i.width/7,o=t-i.left,r=Math.floor(o/n),d=l(this._date.getFullYear(),this._date.getMonth(),this._opts.weekStartsOn),h=a.top-i.top,u=i.height/Math.ceil(d.length/7),p=7*Math.floor(h/u)+r;p>=0&&p<d.length&&(c=e(d[p]))}else{const s="day"===this._view?[this._date]:r(this._date,this._opts.weekStartsOn),i=this._root.querySelector(".uc-all-day-events");if(!i)return;const n=i.getBoundingClientRect(),a=n.width/s.length,o=t-n.left,l=Math.floor(o/a);l>=0&&l<s.length&&(c=e(s[l]))}if(!c)return;c<i.start&&(c=e(i.start));const d=o+20;s.style.width=Math.max(d,60)+"px",this._dragState.newEndDate=c}_handleDragEnd(t){if(!this._dragState)return;if(this._dragState.hasMoved){if(t.preventDefault(),t.stopPropagation(),this._clickSuppressed=!0,setTimeout(()=>{this._clickSuppressed=!1},10),"resize"===this._dragState.mode&&"vertical"===this._dragState.resizeDirection)this._dragState.newEndTime&&this._performResize(this._dragState.newEndTime);else if("resize"===this._dragState.mode&&"horizontal"===this._dragState.resizeDirection)this._dragState.newEndDate&&this._performHorizontalResize(this._dragState.newEndDate);else{const e=this._getEventPosition(t),s=this._calculateDropPosition(e.x,e.y);s&&this._performDrop(s)}this._cleanupDrag()}else this._dragState=null}_handleDragKeyDown(t){this._dragState&&this._dragState.hasMoved&&"Escape"===t.key&&this._cleanupDrag()}_getEventPosition(t){if(t.type.startsWith("touch")){const e=t.touches[0]||t.changedTouches[0];return{x:e.clientX,y:e.clientY}}return{x:t.clientX,y:t.clientY}}_createDragElement(){const t=this._root.querySelector(`[data-event-id="${this._dragState.eventId}"]`);if(!t)return;const e=t.cloneNode(!0);e.classList.add("uc-dragging-element");const s=t.getBoundingClientRect();e.style.position="fixed",e.style.pointerEvents="none",e.style.zIndex="10000",e.style.opacity="0.8",e.style.width=t.offsetWidth+"px","resize"===this._dragState.mode?(e.style.left=s.left+"px",e.style.top=s.top+"px",e.style.height=t.offsetHeight+"px"):(e.style.left=this._dragState.currentX-this._dragState.offsetX+"px",e.style.top=this._dragState.currentY-this._dragState.offsetY+"px"),document.body.appendChild(e),this._dragState.dragElement=e}_cleanupDrag(){this._dragState&&this._dragState.dragElement&&this._dragState.dragElement.remove(),this._root.classList.remove("uc-dragging"),this._dragState=null}_calculateDropPosition(t,e){if("week"===this._view||"day"===this._view){const s=this._calculateAllDayDropPosition(t,e);if(s)return s;const i=this._calculateTimedDropPosition(t,e);if(i)return i}return"month"===this._view?this._calculateMonthDropPosition(t,e):null}_calculateMonthDropPosition(t,e){const s=this._root.querySelector(".uc-month-body");if(!s)return null;const i=s.getBoundingClientRect(),n=t-i.left,a=e-i.top,o=i.width/7,r=s.querySelectorAll(".uc-week-row").length,c=i.height/r,d=Math.floor(n/o),h=Math.floor(a/c),u=l(this._date.getFullYear(),this._date.getMonth(),this._opts.weekStartsOn),p=7*h+d;return d<0||d>=7||p<0||p>=u.length?null:{date:u[p],isMonthView:!0}}_calculateTimedDropPosition(t,e){const s=this._root.querySelector(".uc-time-columns");if(!s)return null;const i=s.querySelectorAll(".uc-time-col");let n=null;for(const e of i){const s=e.getBoundingClientRect();if(t>=s.left&&t<s.right){n=e;break}}if(!n)return null;const a=n.getBoundingClientRect(),o=Math.max(0,e-a.top),r=parseFloat(getComputedStyle(this._root).getPropertyValue("--cal-hour-height"))||60,l=Math.floor(o/r*60),c=Math.floor(l/60)%24,d=15*Math.round(l%60/15),h=n.dataset.date;if(!h)return null;const u=new Date(h);return u.setHours(c,d,0,0),{date:u,isAllDay:!1}}_calculateAllDayDropPosition(e,s){const i=this._root.querySelector(".uc-all-day-events");if(!i)return null;const n=i.getBoundingClientRect();if(s<n.top||s>n.bottom)return null;const a="day"===this._view?[this._date]:r(this._date,this._opts.weekStartsOn),o=n.width/a.length,l=e-n.left,c=Math.floor(l/o);return c<0||c>=a.length?null:{date:t(a[c]),isAllDay:!0}}_performDrop(i){const n=this._dragState.originalEvent,o=new Date(n.start),r=new Date(n.end);let l,c,d;const h=n.allDay||this._dragState.isAllDay;if(i.isMonthView)if(h){const n=a(o,r);l=t(i.date),c=e(s(i.date,Math.max(0,n))),d=!0}else{const t=r-o;l=new Date(i.date),l.setHours(o.getHours(),o.getMinutes(),o.getSeconds(),o.getMilliseconds()),c=new Date(l.getTime()+t),d=!1}else if(i.isAllDay){const n=Math.max(0,Math.ceil((r-o)/864e5));l=t(i.date),c=e(s(i.date,n)),d=!0}else{if(l=i.date,h)c=new Date(l.getTime()+36e5);else{const t=r-o;c=new Date(l.getTime()+t)}d=!1}const u=this._events.findIndex(t=>String(t.id)===String(this._dragState.eventId));u>=0&&(this._events[u].start=l,this._events[u].end=c,this._events[u].allDay=d);const p=this._cachedEvents.findIndex(t=>String(t.id)===String(this._dragState.eventId));p>=0&&(this._cachedEvents[p].start=l,this._cachedEvents[p].end=c,this._cachedEvents[p].allDay=d),this._opts.onEventDrop&&this._opts.onEventDrop(u>=0?this._events[u]:null,o,r,l,c),this._renderView()}_performResize(t){const e=this._dragState.originalEvent,s=new Date(e.start),i=new Date(e.end),n=s,a=this._events.findIndex(t=>String(t.id)===String(this._dragState.eventId));a>=0&&(this._events[a].end=t);const o=this._cachedEvents.findIndex(t=>String(t.id)===String(this._dragState.eventId));o>=0&&(this._cachedEvents[o].end=t),this._opts.onEventDrop&&this._opts.onEventDrop(a>=0?this._events[a]:null,s,i,n,t),this._renderView()}_performHorizontalResize(t){const e=this._dragState.originalEvent,s=new Date(e.start),i=new Date(e.end),n=s,a=this._events.findIndex(t=>String(t.id)===String(this._dragState.eventId));a>=0&&(this._events[a].end=t);const o=this._cachedEvents.findIndex(t=>String(t.id)===String(this._dragState.eventId));o>=0&&(this._cachedEvents[o].end=t),this._opts.onEventDrop&&this._opts.onEventDrop(a>=0?this._events[a]:null,s,i,n,t),this._renderView()}_startNowUpdater(){this._nowInterval=setInterval(()=>{const t=this._root.querySelectorAll(".uc-now-indicator");if(!t.length)return;const e=new Date,s=`calc(${60*e.getHours()+e.getMinutes()} / 60 * var(--cal-hour-height))`;t.forEach(t=>t.style.top=s)},6e4)}}});
|
|
14
|
+
return class{constructor(e,s={}){if("string"==typeof e){if(this._el=document.querySelector(e),!this._el)throw new Error(`SimpleCalendarJs: no element for "${e}"`)}else this._el=e;this._opts=Object.assign({mode:"calendar",defaultView:"month",defaultDate:null,weekStartsOn:0,locale:"default",weekdayFormat:"short",use24Hour:!1,showTimeInItems:!0,showGridLines:!0,monthTimedEventStyle:"list",monthDayNumberAlign:"left",showToolbar:!0,showTodayButton:!0,showNavigation:!0,showTitle:!0,showYearPicker:!0,showViewSwitcher:!0,showTooltips:!0,tooltipBgColor:null,tooltipTextColor:null,tooltipMaxWidth:250,tooltipDelay:400,tooltipAllowHtml:!0,showBorder:!0,listDaysForward:30,enabledViews:["month","week","day"],enableDragDrop:!1,enableResize:!1,autoContrastText:!1,contrastLevel:"high",allowHtmlInEvents:!0,showEventBorder:!1,fetchEvents:null,onEventClick:null,onSlotClick:null,onDateSelect:null,onRangeSelect:null,onViewChange:null,onNavigate:null,onEventDrop:null},s),this._view=this._opts.defaultView,this._date=t(this._opts.defaultDate||new Date),this._events=[],this._cachedRange=null,this._cachedEvents=[],this._nowInterval=null,this._yearPickerOpen=!1,this._yearPickerBase=0,this._selectedDate=null,this._selectedRange={start:null,end:null},this._yearOutsideHandler=null,this._dragState=null,this._clickSuppressed=!1,this._tooltipElement=null,this._tooltipTimeout=null,this._currentTooltipTarget=null,this._root=document.createElement("div"),this._root.className="uc-calendar",this._opts.showGridLines||this._root.classList.add("uc-no-grid"),this._opts.showBorder||this._root.classList.add("uc-no-border"),this._el.appendChild(this._root),this._onClick=this._handleClick.bind(this),this._root.addEventListener("click",this._onClick),this._onMouseOver=this._handleTooltipHover.bind(this),this._onMouseOut=this._handleTooltipMouseOut.bind(this),this._root.addEventListener("mouseover",this._onMouseOver),this._root.addEventListener("mouseout",this._onMouseOut),(this._opts.enableDragDrop||this._opts.enableResize)&&(this._onMouseDown=this._handleDragStart.bind(this),this._onMouseMoveGlobal=this._handleDragMove.bind(this),this._onMouseUp=this._handleDragEnd.bind(this),this._onTouchStart=this._handleDragStart.bind(this),this._onTouchMove=this._handleDragMove.bind(this),this._onTouchEnd=this._handleDragEnd.bind(this),this._onKeyDown=this._handleDragKeyDown.bind(this),this._root.addEventListener("mousedown",this._onMouseDown),document.addEventListener("mousemove",this._onMouseMoveGlobal),document.addEventListener("mouseup",this._onMouseUp),this._root.addEventListener("touchstart",this._onTouchStart,{passive:!0}),document.addEventListener("touchmove",this._onTouchMove,{passive:!1}),document.addEventListener("touchend",this._onTouchEnd),document.addEventListener("keydown",this._onKeyDown),this._opts.enableDragDrop&&this._root.setAttribute("data-drag-enabled","true"),this._opts.enableResize&&this._root.setAttribute("data-resize-enabled","true")),this._root.innerHTML=this._buildShell(),this._resizeObserver=new ResizeObserver(()=>{this._resizeTimeout&&clearTimeout(this._resizeTimeout),this._resizeTimeout=setTimeout(()=>{this._renderView()},100)}),this._resizeObserver.observe(this._root),this._fetchAndRender(),this._startNowUpdater()}setView(t){t!==this._view&&(this._view=t,this._opts.onViewChange&&this._opts.onViewChange(t),this._fetchAndRender())}navigate(t){const e=new Date(this._date);"month"===this._view?(e.setDate(1),e.setMonth(e.getMonth()+t)):"week"===this._view?e.setDate(e.getDate()+7*t):e.setDate(e.getDate()+t),this._date=e;const s=this._getRange();this._opts.onNavigate&&this._opts.onNavigate(s.start,s.end),this._fetchAndRender()}goToToday(){this._date=t(new Date);const e=this._getRange();this._opts.onNavigate&&this._opts.onNavigate(e.start,e.end),this._fetchAndRender()}goToDate(e){this._date=t(o(e)),this._fetchAndRender()}refresh(){this._cachedRange=null,this._cachedEvents=[],this._fetchAndRender()}getSelectedDate(){return this._selectedDate}setSelectedDate(e){"date-picker"===this._opts.mode&&(this._selectedDate=e?t(e):null,this._renderView())}getSelectedRange(){return{...this._selectedRange}}setSelectedRange(e,s=null){"range-picker"===this._opts.mode&&(this._selectedRange={start:e?t(e):null,end:s?t(s):null},this._renderView())}clearSelection(){this._selectedDate=null,this._selectedRange={start:null,end:null},this._renderView()}destroy(){this._nowInterval&&clearInterval(this._nowInterval),this._resizeTimeout&&clearTimeout(this._resizeTimeout),this._resizeObserver&&this._resizeObserver.disconnect(),this._yearOutsideHandler&&document.removeEventListener("click",this._yearOutsideHandler),this._root.removeEventListener("click",this._onClick),this._root.removeEventListener("mouseover",this._onMouseOver),this._root.removeEventListener("mouseout",this._onMouseOut),this._tooltipElement&&(this._tooltipElement.remove(),this._tooltipElement=null),this._tooltipTimeout&&(clearTimeout(this._tooltipTimeout),this._tooltipTimeout=null),(this._opts.enableDragDrop||this._opts.enableResize)&&(this._root.removeEventListener("mousedown",this._onMouseDown),document.removeEventListener("mousemove",this._onMouseMoveGlobal),document.removeEventListener("mouseup",this._onMouseUp),this._root.removeEventListener("touchstart",this._onTouchStart),document.removeEventListener("touchmove",this._onTouchMove),document.removeEventListener("touchend",this._onTouchEnd),document.removeEventListener("keydown",this._onKeyDown)),this._root.remove()}_getRange(){if("month"===this._view){const s=l(this._date.getFullYear(),this._date.getMonth(),this._opts.weekStartsOn);return{start:t(s[0]),end:e(s[s.length-1])}}if("week"===this._view){const s=r(this._date,this._opts.weekStartsOn);return{start:t(s[0]),end:e(s[6])}}if("list"===this._view){const t=new Date,e=new Date(t);return e.setDate(e.getDate()+this._opts.listDaysForward),{start:t,end:e}}return{start:t(this._date),end:e(this._date)}}_getMonthGridRange(){const s=l(this._date.getFullYear(),this._date.getMonth(),this._opts.weekStartsOn);return{start:t(s[0]),end:e(s[s.length-1])}}async _fetchAndRender(){this._updateToolbar();const t="date-picker"===this._opts.mode||"range-picker"===this._opts.mode;if(!this._opts.fetchEvents||t)return void this._renderView();const e=this._getRange();if(this._cachedRange&&this._cachedRange.start<=e.start&&this._cachedRange.end>=e.end)return this._events=this._cachedEvents.filter(t=>t.end>=e.start&&t.start<=e.end),void this._renderView();const s=this._root.querySelector(".uc-loading");s&&(s.style.display="flex");try{let t;t="list"===this._view?e:this._getMonthGridRange();const s=(await this._opts.fetchEvents(t.start,t.end)||[]).map(t=>({...t,start:o(t.start),end:o(t.end)}));this._cachedRange={start:t.start,end:t.end},this._cachedEvents=s,this._events=s.filter(t=>t.end>=e.start&&t.start<=e.end)}catch(t){this._events=[]}s&&(s.style.display="none"),this._renderView()}_renderView(){const t=this._root.querySelector(".uc-view-container");if(!t)return;const e=this._root.parentElement;if(e&&e.style.height&&"auto"!==e.style.height&&""!==e.style.height?this._root.setAttribute("data-height-constrained","true"):this._root.removeAttribute("data-height-constrained"),"month"===this._view)t.innerHTML=this._buildMonthView();else if("week"===this._view){const e=r(this._date,this._opts.weekStartsOn);t.innerHTML=this._buildWeekOrDayView(e),this._scrollToBusinessHours(t)}else"list"===this._view?t.innerHTML=this._buildListView():(t.innerHTML=this._buildWeekOrDayView([this._date]),this._scrollToBusinessHours(t))}_scrollToBusinessHours(t){requestAnimationFrame(()=>{const e=t.querySelector(".uc-time-body");if(!e)return;const s=parseFloat(getComputedStyle(this._root).getPropertyValue("--cal-hour-height"))||60;e.scrollTop=7*s})}_renderToolbar(){const t=this._root.querySelector(".uc-toolbar");if(!t)return;const e=document.createElement("div");e.innerHTML=this._buildToolbar(),this._root.replaceChild(e.firstElementChild,t)}_buildShell(){return`\n ${this._buildToolbar()}\n <div class="uc-loading" style="display:none">\n <div class="uc-spinner"></div>\n </div>\n <div class="uc-view-container"></div>\n `}_updateToolbar(){const t=this._root.querySelector(".uc-toolbar");if(t){const e=document.createElement("div");e.innerHTML=this._buildToolbar();const s=e.firstElementChild;s&&t.replaceWith(s)}}_buildToolbar(){if(!this._opts.showToolbar)return"";const t=this._date.getFullYear(),e=(new Date).getFullYear();let s,n=!0;if("month"===this._view)s=new Intl.DateTimeFormat(this._opts.locale,{month:"long"}).format(this._date);else if("week"===this._view){const t=r(this._date,this._opts.weekStartsOn);s=function(t,e,s){if(t.getMonth()===e.getMonth()&&t.getFullYear()===e.getFullYear())return`${new Intl.DateTimeFormat(s,{month:"long"}).format(t)} ${t.getDate()}–${e.getDate()}`;const i=t=>new Intl.DateTimeFormat(s,{month:"short",day:"numeric"}).format(t);return`${i(t)} – ${i(e)}`}(t[0],t[6],this._opts.locale)}else if("list"===this._view){const t=new Date,e=new Date(t);e.setDate(e.getDate()+this._opts.listDaysForward),s=function(t,e,s){if(t.getMonth()===e.getMonth()&&t.getFullYear()===e.getFullYear())return`${new Intl.DateTimeFormat(s,{month:"long"}).format(t)} ${t.getDate()}–${e.getDate()}, ${t.getFullYear()}`;const i=t=>new Intl.DateTimeFormat(s,{month:"short",day:"numeric"}).format(t);return`${i(t)} – ${i(e)}, ${e.getFullYear()}`}(t,e,this._opts.locale),n=!1}else s=new Intl.DateTimeFormat(this._opts.locale,{weekday:"long",month:"long",day:"numeric"}).format(this._date);let a="";if(this._opts.showYearPicker&&this._yearPickerOpen){const s=this._yearPickerBase,i=Array.from({length:12},(i,n)=>{const a=s+n,o=a===t;return`<button class="${"uc-year-item"+(o?" uc-active":"")+(a===e&&!o?" uc-today-year":"")}" data-action="select-year" data-year="${a}">${a}</button>`}).join("");a=`\n <div class="uc-year-picker">\n <div class="uc-year-picker-nav">\n <button class="uc-year-nav-btn" data-action="year-prev" aria-label="Previous years">‹</button>\n <span class="uc-year-range">${s} – ${s+11}</span>\n <button class="uc-year-nav-btn" data-action="year-next" aria-label="Next years">›</button>\n </div>\n <div class="uc-year-grid">${i}</div>\n </div>`}const o=this._opts.locale,l=b(o,"today"),c=b(o,"month"),d=b(o,"week"),u=b(o,"day"),p=b(o,"list");let _="";if(this._opts.showNavigation||this._opts.showTodayButton){const t="list"===this._view?" disabled":"",e=this._opts.showNavigation?`<button class="uc-btn uc-nav-btn" data-action="prev" aria-label="Previous"${t}>‹</button>`:"",s=new Date,n=i(this._date,s)?" uc-active":"";_=`\n <div class="uc-toolbar-section uc-toolbar-left">\n ${e}${this._opts.showTodayButton?`<button class="uc-btn uc-today-btn${n}" data-action="today"${t}>${h(l)}</button>`:""}${this._opts.showNavigation?`<button class="uc-btn uc-nav-btn" data-action="next" aria-label="Next"${t}>›</button>`:""}\n </div>`}let v="";if(this._opts.showTitle){const e=n?this._opts.showYearPicker?`<button class="uc-year-btn${this._yearPickerOpen?" uc-open":""}" data-action="year-pick" aria-label="Select year">${t}</button>`:t:"";v=`\n <div class="uc-toolbar-section uc-toolbar-center">\n <h2 class="uc-title">\n <span class="uc-title-main">${h(s)}</span>\n ${e}\n </h2>\n ${a}\n </div>`}let g="";const m="date-picker"===this._opts.mode||"range-picker"===this._opts.mode;if(this._opts.showViewSwitcher&&!m){const t=this._opts.enabledViews,e=[],s=[];if(t.includes("month")&&(e.push(`<button class="uc-btn uc-view-btn${"month"===this._view?" uc-active":""}" data-view="month">${h(c)}</button>`),s.push(`<button class="uc-dropdown-item${"month"===this._view?" uc-active":""}" data-view="month">${h(c)}</button>`)),t.includes("week")&&(e.push(`<button class="uc-btn uc-view-btn${"week"===this._view?" uc-active":""}" data-view="week">${h(d)}</button>`),s.push(`<button class="uc-dropdown-item${"week"===this._view?" uc-active":""}" data-view="week">${h(d)}</button>`)),t.includes("day")&&(e.push(`<button class="uc-btn uc-view-btn${"day"===this._view?" uc-active":""}" data-view="day">${h(u)}</button>`),s.push(`<button class="uc-dropdown-item${"day"===this._view?" uc-active":""}" data-view="day">${h(u)}</button>`)),t.includes("list")&&(e.push(`<button class="uc-btn uc-view-btn${"list"===this._view?" uc-active":""}" data-view="list">${h(p)}</button>`),s.push(`<button class="uc-dropdown-item${"list"===this._view?" uc-active":""}" data-view="list">${h(p)}</button>`)),e.length>0){const t=b(o,"today"),n=i(this._date,new Date)?" uc-active":"",a="list"===this._view?" disabled":"";s.push('<div class="uc-dropdown-separator"></div>'),s.push(`<button class="uc-dropdown-item${n}" data-action="today"${a}>${h(t)}</button>`),g=`\n <div class="uc-toolbar-section uc-toolbar-right">\n <div class="uc-view-switcher">\n ${e.join("")}\n </div>\n <div class="uc-view-dropdown">\n <button class="uc-btn uc-view-dropdown-btn" aria-label="View options">\n <span class="uc-view-icon">⊞</span>\n </button>\n <div class="uc-dropdown-menu">\n ${s.join("")}\n </div>\n </div>\n </div>`}}return`\n <div class="uc-toolbar">\n ${_}${v}${g}\n </div>\n `}_buildMonthView(){const{locale:s,weekStartsOn:i}=this._opts,n=l(this._date.getFullYear(),this._date.getMonth(),i),a=d(s,i,this._opts.weekdayFormat),o=this._events.map(s=>({...s,_origStart:s.start,start:D(s)?t(s.start):s.start,end:D(s)?e(s.end):s.end})),r=a.map(t=>`<div class="uc-month-day-name">${h(t)}</div>`).join(""),c=[];for(let t=0;t<n.length;t+=7)c.push(n.slice(t,t+7));let u;const p=this._root.parentElement,_=p&&p.style.height&&"auto"!==p.style.height&&""!==p.style.height;if(_){const t=p.clientHeight,e=this._root.querySelector(".uc-toolbar"),s=this._root.querySelector(".uc-month-header"),i=t-(e?e.offsetHeight:50)-(s?s.offsetHeight:36)-2,n=c.length,a=Math.floor(i/n),o=Math.floor((a-30-28)/24);u=Math.max(0,o)}else u=1/0;const v=_?Math.floor((p.clientHeight-(this._root.querySelector(".uc-toolbar")?.offsetHeight||50)-(this._root.querySelector(".uc-month-header")?.offsetHeight||36)-2)/c.length):null;return`\n <div class="uc-month-view">\n <div class="uc-month-header">${r}</div>\n <div class="uc-month-body">${c.map(t=>this._buildWeekRow(t,o,u,v)).join("")}</div>\n </div>\n `}_buildWeekRow(t,e,s,a=null){const o=this._date.getMonth(),r=e.filter(D),l=e.filter(t=>!D(t)),d=S(r,t),u=Array.from({length:7},()=>new Set);for(const{startCol:t,endCol:e,slot:s}of d)for(let i=t;i<=e;i++)u[i].add(s);const _=t.map(t=>l.filter(e=>i(e.start,t)).sort((t,e)=>t.start-e.start)),v="date-picker"===this._opts.mode||"range-picker"===this._opts.mode,g=v?" uc-day-align-center-picker":"center"===this._opts.monthDayNumberAlign?" uc-day-align-center":"right"===this._opts.monthDayNumberAlign?" uc-day-align-right":"",m=t.map((t,e)=>{const s=!v&&n(t)?" uc-today":"",a=t.getMonth()!==o?" uc-other-month":"";let r="";if("date-picker"===this._opts.mode&&this._selectedDate)r=i(t,this._selectedDate)?" uc-selected":"";else if("range-picker"===this._opts.mode&&this._selectedRange.start)if(this._selectedRange.end){r=t>=this._selectedRange.start&&t<=this._selectedRange.end?" uc-in-range":"",i(t,this._selectedRange.start)&&(r+=" uc-range-start"),i(t,this._selectedRange.end)&&(r+=" uc-range-end")}else r=i(t,this._selectedRange.start)?" uc-range-start":"";return`\n <div class="uc-day-cell${s}${a}${r}${g}" data-date="${t.toISOString()}" data-action="day-click">\n <span class="uc-day-number" data-action="day-number" data-date="${t.toISOString()}">${t.getDate()}</span>\n </div>`}).join("");let f="";for(const{event:t,startCol:e,endCol:i,slot:n,isStart:a,isEnd:o}of d){if(s!==1/0&&n>=s)continue;const r=100/7,l=e*r,c=(i-e+1)*r,d=`calc(var(--cal-header-day-height) + ${n} * (var(--cal-event-height) + var(--cal-event-gap)) + 4px)`,u=t.color||"var(--cal-event-bg)",_=t.textColor||(this._opts.autoContrastText?y(u,this._opts.contrastLevel):"var(--cal-event-text)"),v=t.borderColor||(this._opts.showEventBorder?w(u,25):"transparent"),g=this._opts.showEventBorder?`border:1px solid ${h(v)};`:"",m=a?"var(--cal-event-border-radius)":"0",b=o?"var(--cal-event-border-radius)":"0",D=a?"":" uc-continues-left",S=o?"":" uc-continues-right",$=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"",k=this._opts.enableResize&&o?'<div class="uc-resize-handle-right" data-action="resize-handle"></div>':"";f+=`\n <div class="uc-event-bar${D}${S}"\n style="left:calc(${l}% + 2px);width:calc(${c}% - 4px);top:${d};background:${h(u)};color:${h(_)};${g}border-radius:${m} ${b} ${b} ${m};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${$}>\n ${a?`<span class="uc-event-title">${p(t.title,this._opts.allowHtmlInEvents,t.allowHtml)}</span>`:" "}\n ${k}\n </div>`}let b=-1;for(let e=0;e<7;e++){const i=100/7,n=e*i,a=t[e],o=[...u[e]],r=o.length>0?Math.max(...o)+1:0,l=_[e];if(s===1/0)l.forEach((t,e)=>{const s=r+e;b=Math.max(b,s);const a=`calc(var(--cal-header-day-height) + ${s} * (var(--cal-event-height) + var(--cal-event-gap)) + 4px)`,o=t.color||"var(--cal-event-bg)",l=t.textColor||(this._opts.autoContrastText?y(o,this._opts.contrastLevel):"var(--cal-event-text)"),d=c(t.start,this._opts.locale,this._opts.use24Hour),u=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"";if("list"===this._opts.monthTimedEventStyle){const e=this._opts.showTimeInItems?`<span class="uc-event-time">${h(d)}</span>`:"";f+=`\n <div class="uc-event-bar uc-event-bar--list"\n style="left:calc(${n}% + 2px);width:calc(${i}% - 4px);top:${a};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${u}>\n <span class="uc-event-dot" style="background:${h(o)};"></span>\n ${e}\n <span class="uc-event-title">${p(t.title,this._opts.allowHtmlInEvents,t.allowHtml)}</span>\n </div>`}else{const e=t.borderColor||(this._opts.showEventBorder?w(o,25):"transparent"),s=this._opts.showEventBorder?`border:1px solid ${h(e)};`:"",r=this._opts.showTimeInItems?`<span class="uc-event-time">${h(d)}</span>`:"";f+=`\n <div class="uc-event-bar"\n style="left:calc(${n}% + 2px);width:calc(${i}% - 4px);top:${a};background:${h(o)};color:${h(l)};${s}"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${u}>\n ${r}\n <span class="uc-event-title">${p(t.title,this._opts.allowHtmlInEvents,t.allowHtml)}</span>\n </div>`}});else{const t=[...u[e]].filter(t=>t>=s).length,o=[];for(let t=r;t<s;t++)o.push(t);let d=t;if(l.forEach((t,e)=>{if(e<o.length){const s=o[e];b=Math.max(b,s);const a=`calc(var(--cal-header-day-height) + ${s} * (var(--cal-event-height) + var(--cal-event-gap)) + 4px)`,r=t.color||"var(--cal-event-bg)",l=t.textColor||(this._opts.autoContrastText?y(r,this._opts.contrastLevel):"var(--cal-event-text)"),d=c(t.start,this._opts.locale,this._opts.use24Hour),u=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"";if("list"===this._opts.monthTimedEventStyle){const e=this._opts.showTimeInItems?`<span class="uc-event-time">${h(d)}</span>`:"";f+=`\n <div class="uc-event-bar uc-event-bar--list"\n style="left:calc(${n}% + 2px);width:calc(${i}% - 4px);top:${a};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${u}>\n <span class="uc-event-dot" style="background:${h(r)};"></span>\n ${e}\n <span class="uc-event-title">${p(t.title,this._opts.allowHtmlInEvents,t.allowHtml)}</span>\n </div>`}else{const e=t.borderColor||(this._opts.showEventBorder?w(r,25):"transparent"),s=this._opts.showEventBorder?`border:1px solid ${h(e)};`:"",o=this._opts.showTimeInItems?`<span class="uc-event-time">${h(d)}</span>`:"";f+=`\n <div class="uc-event-bar"\n style="left:calc(${n}% + 2px);width:calc(${i}% - 4px);top:${a};background:${h(r)};color:${h(l)};${s}"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${u}>\n ${o}\n <span class="uc-event-title">${p(t.title,this._opts.allowHtmlInEvents,t.allowHtml)}</span>\n </div>`}}else d++}),d>0){f+=`\n <div class="uc-more-link"\n style="left:calc(${n}% + 2px);width:calc(${i}% - 4px);top:${`calc(var(--cal-header-day-height) + ${s} * (var(--cal-event-height) + var(--cal-event-gap)) + 2px)`};"\n data-date="${a.toISOString()}" data-action="more-click">\n +${d} more\n </div>`}}}for(const{slot:t}of d)(s===1/0||t<s)&&(b=Math.max(b,t));let $="";if(null!==a)$=` style="height: ${a}px;"`;else if(s===1/0){$=` style="min-height: ${b>=0?`calc(var(--cal-header-day-height) + ${b+1} * (var(--cal-event-height) + var(--cal-event-gap)) + 8px)`:"80px"};"`}return`\n <div class="uc-week-row"${$}>\n <div class="uc-week-cells">${m}</div>\n <div class="uc-week-events">${f}</div>\n </div>`}_buildWeekOrDayView(s){const{locale:a,weekStartsOn:o,use24Hour:r}=this._opts,l=1===s.length,u=l?" uc-day-header-single":"",_=l?[new Intl.DateTimeFormat(a,{weekday:this._opts.weekdayFormat}).format(s[0])]:d(a,o,this._opts.weekdayFormat),v=t(s[0]),g=e(s[s.length-1]),m=this._events.filter(t=>D(t)&&t.start<=g&&e(t.end)>=v).map(s=>({...s,start:t(s.start),end:e(s.end)})),f=this._events.filter(t=>!D(t)&&t.start>=v&&t.start<=g),$=s.map((t,e)=>{const s=n(t)?" uc-today":"",i=t.getDate();return`\n <div class="uc-week-day-header${s}">\n <span class="uc-week-day-name">${h(_[e])}</span>\n <span class="uc-week-day-num" data-action="day-number" data-date="${t.toISOString()}">${i}</span>\n </div>`}).join(""),k=l?m.map((t,e)=>({event:t,startCol:0,endCol:0,slot:e,isStart:!0,isEnd:!0})):S(m,s),E=k.length?Math.max(...k.map(t=>t.slot))+1:0;let T="";for(const{event:t,startCol:e,endCol:i,slot:n,isStart:a,isEnd:o}of k){const r=100/s.length,l=e*r,c=(i-e+1)*r,d=`calc(${n} * (var(--cal-event-height) + 3px) + 2px)`,u=t.color||"var(--cal-event-bg)",_=t.textColor||(this._opts.autoContrastText?y(u,this._opts.contrastLevel):"var(--cal-event-text)"),v=t.borderColor||(this._opts.showEventBorder?w(u,25):"transparent"),g=this._opts.showEventBorder?`border:1px solid ${h(v)};`:"",m=a?"var(--cal-event-border-radius)":"0",f=o?"var(--cal-event-border-radius)":"0",b=a?"":" uc-continues-left",D=o?"":" uc-continues-right",S=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"",$=this._opts.enableResize&&o?'<div class="uc-resize-handle-right" data-action="resize-handle"></div>':"";T+=`\n <div class="uc-event-bar${b}${D}"\n style="left:calc(${l}% + 2px);width:calc(${c}% - 4px);top:${d};background:${h(u)};color:${h(_)};${g}border-radius:${m} ${f} ${f} ${m};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${S}>\n <span class="uc-event-title">${p(t.title,this._opts.allowHtmlInEvents,t.allowHtml)}</span>\n ${$}\n </div>`}const M=`calc(${Math.max(1,E)} * (var(--cal-event-height) + 3px) + 6px)`,x=new Date,C=60*x.getHours()+x.getMinutes(),H=s.length,R=Array.from({length:24},(t,e)=>`<div class="uc-hour-cell"><span class="uc-hour-label">${h(0===e?"":c(new Date(2e3,0,1,e),"en-US",r))}</span></div>`).join(""),L=s.map(t=>{const e=n(t)?" uc-today":"",s=Array.from({length:24},()=>'<div class="uc-hour-row"><div class="uc-half-hour-line"></div></div>').join(""),o=function(t){if(!t.length)return[];const e=[...t].sort((t,e)=>t.start-e.start||e.end-t.end),s=[],i=[];for(const t of e){let e=s.findIndex(e=>e<=t.start);-1===e?(e=s.length,s.push(t.end)):s[e]=t.end,i.push({event:t,col:e})}return i.map(t=>{const e=i.filter(e=>e.event.start<t.event.end&&e.event.end>t.event.start),s=Math.max(...e.map(t=>t.col))+1;return{...t,totalCols:s}})}(f.filter(e=>i(e.start,t))).map(({event:t,col:e,totalCols:s})=>{const i=60*t.start.getHours()+t.start.getMinutes(),n=60*t.end.getHours()+t.end.getMinutes(),o=`calc(${i} / 60 * var(--cal-hour-height))`,l=`calc(${Math.max(n-i,30)} / 60 * var(--cal-hour-height))`,d=100/s,u=`calc(${e*d}% + 1px)`,_=`calc(${d}% - 2px)`,v=t.color||"var(--cal-event-bg)",g=t.textColor||(this._opts.autoContrastText?y(v,this._opts.contrastLevel):"var(--cal-event-text)"),m=t.borderColor||(this._opts.showEventBorder?w(v,25):"transparent"),f=this._opts.showEventBorder?`border:1px solid ${h(m)};`:"",b=c(t.start,a,r),D=n-i<=60?" uc-timed-event--short":"",S=this._opts.showTimeInItems?`<span class="uc-event-time">${h(b)}</span>`:"",$=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"",k=this._opts.enableResize?'<div class="uc-resize-handle" data-action="resize-handle"></div>':"";return`\n <div class="uc-timed-event${D}"\n style="top:${o};height:${l};left:${u};width:${_};background:${h(v)};color:${h(g)};${f}"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${$}>\n ${S}\n <span class="uc-event-title">${p(t.title,this._opts.allowHtmlInEvents,t.allowHtml)}</span>\n ${k}\n </div>`}).join(""),l=n(t)?`<div class="uc-now-indicator" style="top:calc(${C} / 60 * var(--cal-hour-height));">\n <span class="uc-now-dot"></span>\n <span class="uc-now-line"></span>\n </div>`:"";return`<div class="uc-time-col${e}" data-date="${t.toISOString()}" data-action="slot-col">\n ${s}${o}${l}\n </div>`}).join("");return`\n <div class="${l?"uc-day-view":"uc-week-view"}">\n <div class="uc-week-header">\n <div class="uc-time-gutter-spacer"></div>\n <div class="uc-week-day-headers${u}">${$}</div>\n </div>\n <div class="uc-all-day-section">\n <div class="uc-time-gutter-spacer uc-all-day-label">${b(this._opts.locale,"allDay")}</div>\n <div class="uc-all-day-events" style="min-height:${M}">${T}</div>\n </div>\n <div class="uc-time-body">\n <div class="uc-time-grid-inner">\n <div class="uc-time-gutter">${R}</div>\n <div class="uc-time-columns" style="--uc-col-count:${H}">${L}</div>\n </div>\n </div>\n </div>`}_buildListView(){const e=new Date,s=t(e),i=new Date(s);i.setDate(i.getDate()+this._opts.listDaysForward);const n=this._events.filter(t=>t.end>=e&&t.start<=i);n.sort((e,i)=>{const n=(e.start<s?s:t(e.start))-(i.start<s?s:t(i.start));return 0!==n?n:e.start-i.start});const a=new Map;if(n.forEach(e=>{const i=e.start<s?s:t(e.start),n=`${(o=i).getFullYear()}-${String(o.getMonth()+1).padStart(2,"0")}-${String(o.getDate()).padStart(2,"0")}`;var o;a.has(n)||a.set(n,[]),a.get(n).push(e)}),0===a.size)return'\n <div class="uc-list-view">\n <div class="uc-list-empty">\n <p>No upcoming events</p>\n </div>\n </div>';let r="";return a.forEach((t,e)=>{const s=o(e),i=new Intl.DateTimeFormat(this._opts.locale,{weekday:"long",year:"numeric",month:"long",day:"numeric"}).format(s),n=t.map(t=>{const e=D(t)?"All day":c(t.start,this._opts.locale,this._opts.use24Hour),s=t.color||"var(--cal-event-bg)",i=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||"")}"`:"";return`\n <div class="uc-list-event" data-event-id="${h(t.id)}" data-action="event-click" ${i}>\n <div class="uc-list-event-indicator" style="background: ${h(s)};"></div>\n <div class="uc-list-event-time">${h(e)}</div>\n <div class="uc-list-event-content">\n <div class="uc-list-event-title">${p(t.title,this._opts.allowHtmlInEvents,t.allowHtml)}</div>\n </div>\n </div>`}).join("");r+=`\n <div class="uc-list-date-group">\n <div class="uc-list-date-header">${h(i)}</div>\n <div class="uc-list-events">${n}</div>\n </div>`}),`<div class="uc-list-view">${r}</div>`}_closeYearPicker(){this._yearOutsideHandler&&(document.removeEventListener("click",this._yearOutsideHandler),this._yearOutsideHandler=null),this._yearPickerOpen=!1,this._renderToolbar()}_handleClick(e){if(this._clickSuppressed)return void(this._clickSuppressed=!1);const s=e.target.closest(".uc-view-dropdown-btn");if(s){e.stopPropagation();const t=s.parentElement,i=t.classList.contains("uc-open");return this._root.querySelectorAll(".uc-view-dropdown.uc-open").forEach(t=>t.classList.remove("uc-open")),void(i||(t.classList.add("uc-open"),this._dropdownOutsideHandler=e=>{e.target.closest(".uc-view-dropdown")||(t.classList.remove("uc-open"),document.removeEventListener("click",this._dropdownOutsideHandler))},setTimeout(()=>document.addEventListener("click",this._dropdownOutsideHandler),0)))}const i=e.target.closest("[data-view]");if(i)return this.setView(i.dataset.view),void this._root.querySelectorAll(".uc-view-dropdown.uc-open").forEach(t=>t.classList.remove("uc-open"));const n=e.target.closest("[data-action]");if(!n)return;switch(n.dataset.action){case"prev":this.navigate(-1);break;case"next":this.navigate(1);break;case"today":this.goToToday(),this._root.querySelectorAll(".uc-view-dropdown.uc-open").forEach(t=>t.classList.remove("uc-open"));break;case"year-pick":e.stopPropagation(),this._yearPickerOpen?this._closeYearPicker():(this._yearPickerOpen=!0,this._yearPickerBase=this._date.getFullYear()-4,this._renderToolbar(),this._yearOutsideHandler=t=>{t.target.closest(".uc-year-picker")||t.target.closest('[data-action="year-pick"]')||this._closeYearPicker()},setTimeout(()=>document.addEventListener("click",this._yearOutsideHandler),0));break;case"year-prev":e.stopPropagation(),this._yearPickerBase-=12,this._renderToolbar();break;case"year-next":e.stopPropagation(),this._yearPickerBase+=12,this._renderToolbar();break;case"select-year":{e.stopPropagation();const t=parseInt(n.dataset.year,10);this._date=new Date(this._date.getFullYear()!==t?new Date(this._date).setFullYear(t):this._date),this._closeYearPicker();const s=this._getRange();this._opts.onNavigate&&this._opts.onNavigate(s.start,s.end),this._fetchAndRender();break}case"event-click":{e.stopPropagation();const t=n.dataset.eventId,s=this._events.find(e=>String(e.id)===String(t));s&&this._opts.onEventClick&&this._opts.onEventClick(s,e);break}case"day-click":{if(e.target.closest('[data-action="day-number"]'))break;if(e.target.closest('[data-action="event-click"]'))break;const t=new Date(n.dataset.date);"date-picker"===this._opts.mode?this._handleDateSelection(t):"range-picker"===this._opts.mode?this._handleRangeSelection(t):this._opts.onSlotClick&&this._opts.onSlotClick(t,e);break}case"day-number":{e.stopPropagation();const s=new Date(n.dataset.date);if("date-picker"===this._opts.mode)this._handleDateSelection(s);else if("range-picker"===this._opts.mode)this._handleRangeSelection(s);else{if(!this._opts.enabledViews.includes("day"))break;this._date=t(s),this._view="day",this._opts.onViewChange&&this._opts.onViewChange("day"),this._fetchAndRender()}break}case"more-click":{if(e.stopPropagation(),!this._opts.enabledViews.includes("day"))break;const s=new Date(n.dataset.date);this._date=t(s),this._view="day",this._opts.onViewChange&&this._opts.onViewChange("day"),this._fetchAndRender();break}case"slot-col":if(e.target.closest('[data-action="event-click"]'))break;if(this._opts.onSlotClick){const t=n,s=t.getBoundingClientRect(),i=e.clientY-s.top,a=parseFloat(getComputedStyle(this._root).getPropertyValue("--cal-hour-height"))||60,o=Math.max(0,Math.floor(i/a*60)),r=Math.floor(o/60)%24,l=15*Math.round(o%60/15),c=new Date(t.dataset.date);c.setHours(r,l,0,0),this._opts.onSlotClick(c,e)}}}_handleDateSelection(e){const s=t(e);this._selectedDate=s,this._renderView(),this._opts.onDateSelect&&this._opts.onDateSelect(s)}_handleRangeSelection(e){const s=t(e);!this._selectedRange.start||this._selectedRange.start&&this._selectedRange.end?this._selectedRange={start:s,end:null}:(s<this._selectedRange.start?this._selectedRange={start:s,end:this._selectedRange.start}:this._selectedRange.end=s,this._opts.onRangeSelect&&this._opts.onRangeSelect(this._selectedRange.start,this._selectedRange.end)),this._renderView()}_handleTooltipHover(t){const e=t.target.closest("[data-tooltip]");if(!e)return;const s=e.dataset.tooltip;if(!s||!s.trim())return;const i="true"===e.dataset.tooltipHtml||this._opts.tooltipAllowHtml;this._showTooltip(e,s,i)}_handleTooltipMouseOut(t){const e=t.target.closest("[data-tooltip]");e&&(t.relatedTarget&&e.contains(t.relatedTarget)||this._hideTooltip())}_createTooltipElement(){if(this._tooltipElement)return this._tooltipElement;const t=document.createElement("div");return t.className="uc-tooltip-container",t.innerHTML='<div class="uc-tooltip-content"></div><div class="uc-tooltip-arrow"></div>',this._opts.tooltipBgColor&&t.style.setProperty("--tooltip-bg",this._opts.tooltipBgColor),this._opts.tooltipTextColor&&t.style.setProperty("--tooltip-text",this._opts.tooltipTextColor),this._opts.tooltipMaxWidth&&t.style.setProperty("--tooltip-max-width",this._opts.tooltipMaxWidth+"px"),document.body.appendChild(t),this._tooltipElement=t,t}_showTooltip(t,e,s){this._opts.showTooltips&&e&&(this._tooltipTimeout&&clearTimeout(this._tooltipTimeout),this._tooltipTimeout=setTimeout(()=>{this._tooltipElement||this._createTooltipElement();const i=this._tooltipElement.querySelector(".uc-tooltip-content");s?(i.innerHTML=u(e),i.querySelectorAll("a").forEach(t=>{t.setAttribute("target","_blank"),t.setAttribute("rel","noopener noreferrer")})):i.textContent=e,this._positionTooltipFixed(t),this._tooltipElement.style.opacity="1",this._tooltipElement.style.visibility="visible",this._currentTooltipTarget=t},this._opts.tooltipDelay))}_hideTooltip(){this._tooltipTimeout&&(clearTimeout(this._tooltipTimeout),this._tooltipTimeout=null),this._tooltipElement&&(this._tooltipElement.style.opacity="0",this._tooltipElement.style.visibility="hidden"),this._currentTooltipTarget=null}_positionTooltipFixed(t){if(!this._tooltipElement)return;const e=t.getBoundingClientRect(),s=this._tooltipElement.getBoundingClientRect();let i=e.top-s.height-8,n=e.left+e.width/2-s.width/2;n+s.width>window.innerWidth-10&&(n=window.innerWidth-s.width-10),n<10&&(n=10),i<10?(i=e.bottom+8,this._tooltipElement.classList.add("uc-tooltip-position-bottom")):this._tooltipElement.classList.remove("uc-tooltip-position-bottom"),this._tooltipElement.style.top=i+"px",this._tooltipElement.style.left=n+"px";const a=e.left+e.width/2-n,o=this._tooltipElement.querySelector(".uc-tooltip-arrow");o&&(o.style.left=a+"px")}_handleDragStart(t){if("list"===this._view)return;const e=t.target.closest('[data-action="resize-handle"]'),s=!!e,i=e&&e.classList.contains("uc-resize-handle-right"),n=e&&e.classList.contains("uc-resize-handle"),a=t.target.closest('[data-event-id][data-action="event-click"]');if(!a)return;if(t.target.closest('.uc-year-picker, [data-action="year-pick"], .uc-toolbar'))return;const o=a.dataset.eventId,r=this._events.find(t=>String(t.id)===String(o));if(!r)return;const l=r.allDay||!1;if(s&&!this._opts.enableResize)return;if(!s&&!this._opts.enableDragDrop)return;if(n&&(l||"week"!==this._view&&"day"!==this._view))return;if(i&&!l)return;const c=this._getEventPosition(t),d=a.getBoundingClientRect(),h=c.x-d.left,u=c.y-d.top;this._dragState={mode:s?"resize":"move",resizeDirection:i?"horizontal":n?"vertical":null,eventId:o,originalEvent:{...r,start:new Date(r.start),end:new Date(r.end)},dragElement:null,startX:c.x,startY:c.y,currentX:c.x,currentY:c.y,offsetX:h,offsetY:u,originalView:this._view,isAllDay:l,hasMoved:!1,startTime:Date.now(),originalElement:a}}_handleDragMove(t){if(!this._dragState)return;const e=this._getEventPosition(t);this._dragState.currentX=e.x,this._dragState.currentY=e.y;const s=e.x-this._dragState.startX,i=e.y-this._dragState.startY,n=Math.sqrt(s*s+i*i);if(!this._dragState.hasMoved){const t=Date.now()-this._dragState.startTime;if(!(n>5||t>150))return;this._dragState.hasMoved=!0,this._createDragElement(),this._root.classList.add("uc-dragging")}if(t.type.startsWith("touch")&&t.preventDefault(),this._dragState.dragElement)if("resize"===this._dragState.mode&&"vertical"===this._dragState.resizeDirection)this._updateResizePreview(e.x,e.y);else if("resize"===this._dragState.mode&&"horizontal"===this._dragState.resizeDirection)this._updateHorizontalResizePreview(e.x);else{const t=this._getSnappedPosition(e.x,e.y);t?(this._dragState.dragElement.style.left=t.x+"px",this._dragState.dragElement.style.top=t.y+"px"):(this._dragState.dragElement.style.left=e.x-this._dragState.offsetX+"px",this._dragState.dragElement.style.top=e.y-this._dragState.offsetY+"px")}}_getSnappedPosition(t,e){if("week"!==this._view&&"day"!==this._view)return null;const s=this._calculateDropPosition(t,e);if(!s)return null;const i=this._dragState.dragElement;if(!i)return null;if(!s.isAllDay){const e=this._root.querySelector(".uc-time-columns");if(!e)return null;const n=e.querySelectorAll(".uc-time-col");let a=null;for(const e of n){const s=e.getBoundingClientRect();if(t>=s.left&&t<s.right){a=e;break}}if(!a)return null;const o=a.getBoundingClientRect(),r=(60*s.date.getHours()+s.date.getMinutes())/60*(parseFloat(getComputedStyle(this._root).getPropertyValue("--cal-hour-height"))||60),l=i.querySelector(".uc-event-time");if(l){const t=c(s.date,this._opts.locale,this._opts.use24Hour);l.textContent=t}return{x:o.left+4,y:o.top+r}}if(s.isAllDay){const e=this._root.querySelector(".uc-all-day-events");if(!e)return null;const s=e.getBoundingClientRect(),i="day"===this._view?[this._date]:r(this._date,this._opts.weekStartsOn),n=s.width/i.length,a=t-s.left,o=Math.floor(a/n);return o<0||o>=i.length?null:{x:s.left+o*n+4,y:s.top+4}}return null}_updateResizePreview(t,e){const s=this._dragState.dragElement,i=this._dragState.originalEvent,n=this._root.querySelector(".uc-time-columns");if(!n)return;const a=n.querySelectorAll(".uc-time-col");let o=null;for(const e of a){const s=e.getBoundingClientRect();if(t>=s.left&&t<s.right){o=e;break}}if(!o)return;const r=o.getBoundingClientRect(),l=Math.max(0,e-r.top),d=parseFloat(getComputedStyle(this._root).getPropertyValue("--cal-hour-height"))||60,h=Math.floor(l/d*60),u=Math.floor(h/60)%24,p=15*Math.round(h%60/15),_=new Date(i.start);_.setHours(u,p,0,0),_<=i.start&&_.setTime(i.start.getTime()+9e5);const v=(_-i.start)/6e4/60*d;s.style.height=v+"px";const g=s.querySelector(".uc-event-time");if(g){const t=c(i.start,this._opts.locale,this._opts.use24Hour),e=c(_,this._opts.locale,this._opts.use24Hour);g.textContent=`${t} - ${e}`}this._dragState.newEndTime=_}_updateHorizontalResizePreview(t){const s=this._dragState.dragElement,i=this._dragState.originalEvent,n=this._dragState.originalElement;if(!n)return;const a=n.getBoundingClientRect(),o=t-a.left;let c;if("month"===this._view){const s=this._root.querySelector(".uc-month-body");if(!s)return;const i=s.getBoundingClientRect(),n=i.width/7,o=t-i.left,r=Math.floor(o/n),d=l(this._date.getFullYear(),this._date.getMonth(),this._opts.weekStartsOn),h=a.top-i.top,u=i.height/Math.ceil(d.length/7),p=7*Math.floor(h/u)+r;p>=0&&p<d.length&&(c=e(d[p]))}else{const s="day"===this._view?[this._date]:r(this._date,this._opts.weekStartsOn),i=this._root.querySelector(".uc-all-day-events");if(!i)return;const n=i.getBoundingClientRect(),a=n.width/s.length,o=t-n.left,l=Math.floor(o/a);l>=0&&l<s.length&&(c=e(s[l]))}if(!c)return;c<i.start&&(c=e(i.start));const d=o+20;s.style.width=Math.max(d,60)+"px",this._dragState.newEndDate=c}_handleDragEnd(t){if(!this._dragState)return;if(this._dragState.hasMoved){if(t.preventDefault(),t.stopPropagation(),this._clickSuppressed=!0,setTimeout(()=>{this._clickSuppressed=!1},10),"resize"===this._dragState.mode&&"vertical"===this._dragState.resizeDirection)this._dragState.newEndTime&&this._performResize(this._dragState.newEndTime);else if("resize"===this._dragState.mode&&"horizontal"===this._dragState.resizeDirection)this._dragState.newEndDate&&this._performHorizontalResize(this._dragState.newEndDate);else{const e=this._getEventPosition(t),s=this._calculateDropPosition(e.x,e.y);s&&this._performDrop(s)}this._cleanupDrag()}else this._dragState=null}_handleDragKeyDown(t){this._dragState&&this._dragState.hasMoved&&"Escape"===t.key&&this._cleanupDrag()}_getEventPosition(t){if(t.type.startsWith("touch")){const e=t.touches[0]||t.changedTouches[0];return{x:e.clientX,y:e.clientY}}return{x:t.clientX,y:t.clientY}}_createDragElement(){const t=this._root.querySelector(`[data-event-id="${this._dragState.eventId}"]`);if(!t)return;const e=t.cloneNode(!0);e.classList.add("uc-dragging-element");const s=t.getBoundingClientRect();e.style.position="fixed",e.style.pointerEvents="none",e.style.zIndex="10000",e.style.opacity="0.8",e.style.width=t.offsetWidth+"px","resize"===this._dragState.mode?(e.style.left=s.left+"px",e.style.top=s.top+"px",e.style.height=t.offsetHeight+"px"):(e.style.left=this._dragState.currentX-this._dragState.offsetX+"px",e.style.top=this._dragState.currentY-this._dragState.offsetY+"px"),document.body.appendChild(e),this._dragState.dragElement=e}_cleanupDrag(){this._dragState&&this._dragState.dragElement&&this._dragState.dragElement.remove(),this._root.classList.remove("uc-dragging"),this._dragState=null}_calculateDropPosition(t,e){if("week"===this._view||"day"===this._view){const s=this._calculateAllDayDropPosition(t,e);if(s)return s;const i=this._calculateTimedDropPosition(t,e);if(i)return i}return"month"===this._view?this._calculateMonthDropPosition(t,e):null}_calculateMonthDropPosition(t,e){const s=this._root.querySelector(".uc-month-body");if(!s)return null;const i=s.getBoundingClientRect(),n=t-i.left,a=e-i.top,o=i.width/7,r=s.querySelectorAll(".uc-week-row").length,c=i.height/r,d=Math.floor(n/o),h=Math.floor(a/c),u=l(this._date.getFullYear(),this._date.getMonth(),this._opts.weekStartsOn),p=7*h+d;return d<0||d>=7||p<0||p>=u.length?null:{date:u[p],isMonthView:!0}}_calculateTimedDropPosition(t,e){const s=this._root.querySelector(".uc-time-columns");if(!s)return null;const i=s.querySelectorAll(".uc-time-col");let n=null;for(const e of i){const s=e.getBoundingClientRect();if(t>=s.left&&t<s.right){n=e;break}}if(!n)return null;const a=n.getBoundingClientRect(),o=Math.max(0,e-a.top),r=parseFloat(getComputedStyle(this._root).getPropertyValue("--cal-hour-height"))||60,l=Math.floor(o/r*60),c=Math.floor(l/60)%24,d=15*Math.round(l%60/15),h=n.dataset.date;if(!h)return null;const u=new Date(h);return u.setHours(c,d,0,0),{date:u,isAllDay:!1}}_calculateAllDayDropPosition(e,s){const i=this._root.querySelector(".uc-all-day-events");if(!i)return null;const n=i.getBoundingClientRect();if(s<n.top||s>n.bottom)return null;const a="day"===this._view?[this._date]:r(this._date,this._opts.weekStartsOn),o=n.width/a.length,l=e-n.left,c=Math.floor(l/o);return c<0||c>=a.length?null:{date:t(a[c]),isAllDay:!0}}_performDrop(i){const n=this._dragState.originalEvent,o=new Date(n.start),r=new Date(n.end);let l,c,d;const h=n.allDay||this._dragState.isAllDay;if(i.isMonthView)if(h){const n=a(o,r);l=t(i.date),c=e(s(i.date,Math.max(0,n))),d=!0}else{const t=r-o;l=new Date(i.date),l.setHours(o.getHours(),o.getMinutes(),o.getSeconds(),o.getMilliseconds()),c=new Date(l.getTime()+t),d=!1}else if(i.isAllDay){const n=Math.max(0,Math.ceil((r-o)/864e5));l=t(i.date),c=e(s(i.date,n)),d=!0}else{if(l=i.date,h)c=new Date(l.getTime()+36e5);else{const t=r-o;c=new Date(l.getTime()+t)}d=!1}const u=this._events.findIndex(t=>String(t.id)===String(this._dragState.eventId));u>=0&&(this._events[u].start=l,this._events[u].end=c,this._events[u].allDay=d);const p=this._cachedEvents.findIndex(t=>String(t.id)===String(this._dragState.eventId));p>=0&&(this._cachedEvents[p].start=l,this._cachedEvents[p].end=c,this._cachedEvents[p].allDay=d),this._opts.onEventDrop&&this._opts.onEventDrop(u>=0?this._events[u]:null,o,r,l,c),this._renderView()}_performResize(t){const e=this._dragState.originalEvent,s=new Date(e.start),i=new Date(e.end),n=s,a=this._events.findIndex(t=>String(t.id)===String(this._dragState.eventId));a>=0&&(this._events[a].end=t);const o=this._cachedEvents.findIndex(t=>String(t.id)===String(this._dragState.eventId));o>=0&&(this._cachedEvents[o].end=t),this._opts.onEventDrop&&this._opts.onEventDrop(a>=0?this._events[a]:null,s,i,n,t),this._renderView()}_performHorizontalResize(t){const e=this._dragState.originalEvent,s=new Date(e.start),i=new Date(e.end),n=s,a=this._events.findIndex(t=>String(t.id)===String(this._dragState.eventId));a>=0&&(this._events[a].end=t);const o=this._cachedEvents.findIndex(t=>String(t.id)===String(this._dragState.eventId));o>=0&&(this._cachedEvents[o].end=t),this._opts.onEventDrop&&this._opts.onEventDrop(a>=0?this._events[a]:null,s,i,n,t),this._renderView()}_startNowUpdater(){this._nowInterval=setInterval(()=>{const t=this._root.querySelectorAll(".uc-now-indicator");if(!t.length)return;const e=new Date,s=`calc(${60*e.getHours()+e.getMinutes()} / 60 * var(--cal-hour-height))`;t.forEach(t=>t.style.top=s)},6e4)}}});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "simple-calendar-js",
|
|
3
|
-
"version": "3.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.0.14",
|
|
4
|
+
"description": "Lightweight JavaScript calendar component (~16KB gzipped). Zero dependencies, framework-agnostic (React, Vue, Angular, vanilla JS). Month, week, day, and list views. Built-in date & range picker. Free for personal use — commercial license at simplecalendarjs.com.",
|
|
5
5
|
"main": "dist/simple-calendar-js.min.js",
|
|
6
6
|
"style": "dist/simple-calendar-js.min.css",
|
|
7
7
|
"files": [
|
|
@@ -38,7 +38,12 @@
|
|
|
38
38
|
"framework-agnostic",
|
|
39
39
|
"react",
|
|
40
40
|
"vue",
|
|
41
|
-
"angular"
|
|
41
|
+
"angular",
|
|
42
|
+
"fullcalendar-alternative",
|
|
43
|
+
"event-calendar",
|
|
44
|
+
"calendar-component",
|
|
45
|
+
"booking",
|
|
46
|
+
"scheduling"
|
|
42
47
|
],
|
|
43
48
|
"author": "Pedro Lopes <simplecalendarjs@gmail.com> (https://www.simplecalendarjs.com)",
|
|
44
49
|
"license": "SEE LICENSE IN LICENSE",
|