@forcecalendar/interface 0.5.0 → 0.7.0

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.
@@ -1,4 +1,4 @@
1
- var Y=Object.defineProperty;var N=(u,e,t)=>e in u?Y(u,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):u[e]=t;var A=(u,e,t)=>N(u,typeof e!="symbol"?e+"":e,t);(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))s(i);new MutationObserver(i=>{for(const a of i)if(a.type==="childList")for(const n of a.addedNodes)n.tagName==="LINK"&&n.rel==="modulepreload"&&s(n)}).observe(document,{childList:!0,subtree:!0});function t(i){const a={};return i.integrity&&(a.integrity=i.integrity),i.referrerPolicy&&(a.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?a.credentials="include":i.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function s(i){if(i.ep)return;i.ep=!0;const a=t(i);fetch(i.href,a)}})();class C extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}),this._listeners=new Map,this._state=null,this._props=new Map,this._initialized=!1}connectedCallback(){this._initialized||(this.initialize(),this._initialized=!0),this.mount()}disconnectedCallback(){this.unmount(),this.cleanup()}initialize(){}mount(){this.render()}unmount(){}cleanup(){this._listeners.forEach((e,t)=>{t.removeEventListener(e.event,e.handler)}),this._listeners.clear()}setState(e){const t=this._state;this._state={...this._state,...e},this.stateChanged(t,this._state),this.render()}getState(){return this._state}stateChanged(e,t){}setProp(e,t){const s=this._props.get(e);this._props.set(e,t),this.propChanged(e,s,t)}getProp(e){return this._props.get(e)}propChanged(e,t,s){}addEventListener(e,t,s){if(!e||!t||!s){console.warn("addEventListener called with invalid parameters",{element:e,event:t,handler:s});return}const i=s.bind(this);e.addEventListener(t,i),this._listeners.set(e,{event:t,handler:i})}emit(e,t={}){this.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,composed:!0}))}getStyles(){return""}getBaseStyles(){return`
1
+ var Y=Object.defineProperty;var N=(u,e,t)=>e in u?Y(u,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):u[e]=t;var A=(u,e,t)=>N(u,typeof e!="symbol"?e+"":e,t);(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))s(i);new MutationObserver(i=>{for(const a of i)if(a.type==="childList")for(const n of a.addedNodes)n.tagName==="LINK"&&n.rel==="modulepreload"&&s(n)}).observe(document,{childList:!0,subtree:!0});function t(i){const a={};return i.integrity&&(a.integrity=i.integrity),i.referrerPolicy&&(a.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?a.credentials="include":i.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function s(i){if(i.ep)return;i.ep=!0;const a=t(i);fetch(i.href,a)}})();class C extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}),this._listeners=new Map,this._state=null,this._props=new Map,this._initialized=!1}connectedCallback(){this._initialized||(this.initialize(),this._initialized=!0),this.mount()}disconnectedCallback(){this.unmount(),this.cleanup()}initialize(){}mount(){this.render()}unmount(){}cleanup(){this._listeners.forEach((e,t)=>{t.removeEventListener(e.event,e.handler)}),this._listeners.clear()}setState(e){const t=this._state;this._state={...this._state,...e},this.stateChanged(t,this._state),this.render()}getState(){return this._state}stateChanged(e,t){}setProp(e,t){const s=this._props.get(e);this._props.set(e,t),this.propChanged(e,s,t)}getProp(e){return this._props.get(e)}propChanged(e,t,s){}addListener(e,t,s){if(!e||!t||!s){console.warn("addListener called with invalid parameters",{element:e,event:t,handler:s});return}const i=s.bind(this);e.addEventListener(t,i),this._listeners.set(e,{event:t,handler:i})}emit(e,t={}){this.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,composed:!0}))}getStyles(){return""}getBaseStyles(){return`
2
2
  :host {
3
3
  display: block;
4
4
  box-sizing: border-box;
@@ -501,7 +501,7 @@ var Y=Object.defineProperty;var N=(u,e,t)=>e in u?Y(u,e,{enumerable:!0,configura
501
501
  ${o?`<span class="event-time">${o}</span>`:""}
502
502
  <span class="event-title">${T.escapeHTML(t)}</span>
503
503
  </div>
504
- `}afterRender(){this.$$(".month-day").forEach(e=>{e.addEventListener("click",this.handleDayClick.bind(this))}),this.$$(".event-item").forEach(e=>{e.addEventListener("click",this.handleEventClick.bind(this))}),this.$$(".more-events").forEach(e=>{e.addEventListener("click",this.handleMoreClick.bind(this))})}handleDayClick(e){e.stopPropagation();const t=e.currentTarget,s=new Date(t.dataset.date);this.stateManager.selectDate(s),this.emit("day-click",{date:s})}handleEventClick(e){e.stopPropagation();const s=e.currentTarget.dataset.eventId,i=this.stateManager.getEvents().find(a=>a.id===s);i&&(this.stateManager.selectEvent(i),this.emit("event-click",{event:i}))}handleMoreClick(e){e.stopPropagation();const t=e.currentTarget.closest(".month-day"),s=new Date(t.dataset.date),i=this.stateManager.getEventsForDate(s);this.emit("more-events-click",{date:s,events:i})}unmount(){this.unsubscribe&&this.unsubscribe()}}class se extends C{constructor(){super(),this._stateManager=null,this.viewData=null,this.hours=Array.from({length:24},(e,t)=>t)}set stateManager(e){this._stateManager=e,e&&(this.unsubscribe=e.subscribe(this.handleStateUpdate.bind(this)),this.loadViewData())}get stateManager(){return this._stateManager}handleStateUpdate(e,t){if(e.currentDate!==(t==null?void 0:t.currentDate)||e.view!==(t==null?void 0:t.view)){this.loadViewData();return}e.events!==(t==null?void 0:t.events)&&this.loadViewData(),e.selectedDate!==(t==null?void 0:t.selectedDate)&&this.updateSelection(e.selectedDate,t==null?void 0:t.selectedDate)}updateSelection(e,t){if(t){const s=this.shadowRoot.querySelector(`[data-date^="${t.toISOString().split("T")[0]}"]`);s&&s.classList.remove("selected")}if(e){const s=this.shadowRoot.querySelector(`[data-date^="${e.toISOString().split("T")[0]}"]`);s&&s.classList.add("selected")}}loadViewData(){if(!this.stateManager)return;const e=this.stateManager.getViewData();this.viewData=this.processViewData(e),this.render()}processViewData(e){if(!e)return null;let t=[];return e.weeks&&e.weeks.length>0?t=e.weeks[0].days:e.days&&(t=e.days),!t||t.length===0?null:{...e,days:t.map(s=>{const i=new Date(s.date);return{...s,date:i,isToday:p.isToday(i),timedEvents:(s.events||[]).filter(a=>!a.allDay),allDayEvents:(s.events||[]).filter(a=>a.allDay)}})}}getStyles(){return`
504
+ `}afterRender(){this.$$(".month-day").forEach(e=>{this.addListener(e,"click",this.handleDayClick)}),this.$$(".event-item").forEach(e=>{this.addListener(e,"click",this.handleEventClick)}),this.$$(".more-events").forEach(e=>{this.addListener(e,"click",this.handleMoreClick)})}handleDayClick(e){e.stopPropagation();const t=e.currentTarget,s=new Date(t.dataset.date);this.stateManager.selectDate(s),this.emit("day-click",{date:s})}handleEventClick(e){e.stopPropagation();const s=e.currentTarget.dataset.eventId,i=this.stateManager.getEvents().find(a=>a.id===s);i&&(this.stateManager.selectEvent(i),this.emit("event-click",{event:i}))}handleMoreClick(e){e.stopPropagation();const t=e.currentTarget.closest(".month-day"),s=new Date(t.dataset.date),i=this.stateManager.getEventsForDate(s);this.emit("more-events-click",{date:s,events:i})}unmount(){this.unsubscribe&&this.unsubscribe()}}class se extends C{constructor(){super(),this._stateManager=null,this.viewData=null,this.hours=Array.from({length:24},(e,t)=>t)}set stateManager(e){this._stateManager=e,e&&(this.unsubscribe=e.subscribe(this.handleStateUpdate.bind(this)),this.loadViewData())}get stateManager(){return this._stateManager}handleStateUpdate(e,t){if(e.currentDate!==(t==null?void 0:t.currentDate)||e.view!==(t==null?void 0:t.view)){this.loadViewData();return}e.events!==(t==null?void 0:t.events)&&this.loadViewData(),e.selectedDate!==(t==null?void 0:t.selectedDate)&&this.updateSelection(e.selectedDate,t==null?void 0:t.selectedDate)}updateSelection(e,t){if(t){const s=this.shadowRoot.querySelector(`[data-date^="${t.toISOString().split("T")[0]}"]`);s&&s.classList.remove("selected")}if(e){const s=this.shadowRoot.querySelector(`[data-date^="${e.toISOString().split("T")[0]}"]`);s&&s.classList.add("selected")}}loadViewData(){if(!this.stateManager)return;const e=this.stateManager.getViewData();this.viewData=this.processViewData(e),this.render()}processViewData(e){if(!e)return null;let t=[];return e.weeks&&e.weeks.length>0?t=e.weeks[0].days:e.days&&(t=e.days),!t||t.length===0?null:{...e,days:t.map(s=>{const i=new Date(s.date);return{...s,date:i,isToday:p.isToday(i),timedEvents:(s.events||[]).filter(a=>!a.allDay),allDayEvents:(s.events||[]).filter(a=>a.allDay)}})}}getStyles(){return`
505
505
  :host {
506
506
  display: flex;
507
507
  flex-direction: column;
@@ -736,7 +736,7 @@ var Y=Object.defineProperty;var N=(u,e,t)=>e in u?Y(u,e,{enumerable:!0,configura
736
736
  data-event-id="${e.id}">
737
737
  ${T.escapeHTML(e.title)}
738
738
  </div>
739
- `}renderNowIndicator(){const e=new Date;return`<div class="now-indicator" style="top: ${e.getHours()*60+e.getMinutes()}px"></div>`}afterRender(){const e=this.$("#scroll-container");e&&!this._scrolled&&(e.scrollTop=8*60-50,this._scrolled=!0),this.$$("[data-event-id]").forEach(t=>{t.addEventListener("click",s=>{s.stopPropagation();const i=t.dataset.eventId,a=this.stateManager.getEvents().find(n=>n.id===i);a&&this.emit("event-click",{event:a})})}),this.$$(".day-column").forEach(t=>{t.addEventListener("click",s=>{const i=this.$("#scroll-container"),a=t.getBoundingClientRect(),n=s.clientY-a.top+(i?i.scrollTop:0),r=new Date(t.dataset.date);r.setHours(Math.floor(n/60),Math.floor(n%60),0,0),this.stateManager.selectDate(r),this.emit("day-click",{date:r})})})}unmount(){this.unsubscribe&&this.unsubscribe()}}class ie extends C{constructor(){super(),this._stateManager=null,this.viewData=null,this.hours=Array.from({length:24},(e,t)=>t)}set stateManager(e){this._stateManager=e,e&&(this.unsubscribe=e.subscribe(this.handleStateUpdate.bind(this)),this.loadViewData())}get stateManager(){return this._stateManager}handleStateUpdate(e,t){if(e.currentDate!==(t==null?void 0:t.currentDate)||e.view!==(t==null?void 0:t.view)){this.loadViewData();return}e.events!==(t==null?void 0:t.events)&&this.loadViewData(),e.selectedDate!==(t==null?void 0:t.selectedDate)&&this.updateSelection(e.selectedDate,t==null?void 0:t.selectedDate)}updateSelection(e,t){const s=this.shadowRoot.querySelector(".day-column");if(!s)return;(a=>a&&p.isSameDay(a,new Date(s.dataset.date)))(e)?s.classList.add("selected"):s.classList.remove("selected")}loadViewData(){if(!this.stateManager)return;const e=this.stateManager.getViewData();this.viewData=this.processViewData(e),this.render()}processViewData(e){var n;if(!e)return null;let t=null;const s=(n=this.stateManager)==null?void 0:n.getState(),i=(s==null?void 0:s.currentDate)||new Date;if(e.days&&Array.isArray(e.days)&&e.days.length>0)t=e.days.find(r=>p.isSameDay(new Date(r.date),i))||e.days[0];else if(e.weeks&&Array.isArray(e.weeks)&&e.weeks.length>0){const r=e.weeks.flatMap(o=>o.days||[]);t=r.find(o=>p.isSameDay(new Date(o.date),i))||r[0]}else e.date&&(t=e);if(!t)return null;const a=new Date(t.date);return{...e,day:{...t,date:a,isToday:p.isToday(a),timedEvents:(t.events||[]).filter(r=>!r.allDay),allDayEvents:(t.events||[]).filter(r=>r.allDay)}}}getStyles(){return`
739
+ `}renderNowIndicator(){const e=new Date;return`<div class="now-indicator" style="top: ${e.getHours()*60+e.getMinutes()}px"></div>`}afterRender(){const e=this.$("#scroll-container");e&&!this._scrolled&&(e.scrollTop=8*60-50,this._scrolled=!0),this.$$("[data-event-id]").forEach(t=>{this.addListener(t,"click",s=>{s.stopPropagation();const i=s.currentTarget.dataset.eventId,a=this.stateManager.getEvents().find(n=>n.id===i);a&&this.emit("event-click",{event:a})})}),this.$$(".day-column").forEach(t=>{this.addListener(t,"click",s=>{const i=s.currentTarget,a=this.$("#scroll-container"),n=i.getBoundingClientRect(),r=s.clientY-n.top+(a?a.scrollTop:0),o=new Date(i.dataset.date);o.setHours(Math.floor(r/60),Math.floor(r%60),0,0),this.stateManager.selectDate(o),this.emit("day-click",{date:o})})})}unmount(){this.unsubscribe&&this.unsubscribe()}}class ie extends C{constructor(){super(),this._stateManager=null,this.viewData=null,this.hours=Array.from({length:24},(e,t)=>t)}set stateManager(e){this._stateManager=e,e&&(this.unsubscribe=e.subscribe(this.handleStateUpdate.bind(this)),this.loadViewData())}get stateManager(){return this._stateManager}handleStateUpdate(e,t){if(e.currentDate!==(t==null?void 0:t.currentDate)||e.view!==(t==null?void 0:t.view)){this.loadViewData();return}e.events!==(t==null?void 0:t.events)&&this.loadViewData(),e.selectedDate!==(t==null?void 0:t.selectedDate)&&this.updateSelection(e.selectedDate,t==null?void 0:t.selectedDate)}updateSelection(e,t){const s=this.shadowRoot.querySelector(".day-column");if(!s)return;(a=>a&&p.isSameDay(a,new Date(s.dataset.date)))(e)?s.classList.add("selected"):s.classList.remove("selected")}loadViewData(){if(!this.stateManager)return;const e=this.stateManager.getViewData();this.viewData=this.processViewData(e),this.render()}processViewData(e){var n;if(!e)return null;let t=null;const s=(n=this.stateManager)==null?void 0:n.getState(),i=(s==null?void 0:s.currentDate)||new Date;if(e.days&&Array.isArray(e.days)&&e.days.length>0)t=e.days.find(r=>p.isSameDay(new Date(r.date),i))||e.days[0];else if(e.weeks&&Array.isArray(e.weeks)&&e.weeks.length>0){const r=e.weeks.flatMap(o=>o.days||[]);t=r.find(o=>p.isSameDay(new Date(o.date),i))||r[0]}else e.date&&(t=e);if(!t)return null;const a=new Date(t.date);return{...e,day:{...t,date:a,isToday:p.isToday(a),timedEvents:(t.events||[]).filter(r=>!r.allDay),allDayEvents:(t.events||[]).filter(r=>r.allDay)}}}getStyles(){return`
740
740
  :host {
741
741
  display: flex;
742
742
  flex-direction: column;
@@ -950,7 +950,7 @@ var Y=Object.defineProperty;var N=(u,e,t)=>e in u?Y(u,e,{enumerable:!0,configura
950
950
  data-event-id="${e.id}">
951
951
  ${T.escapeHTML(e.title)}
952
952
  </div>
953
- `}renderNowIndicator(){const e=new Date;return`<div class="now-indicator" style="top: ${e.getHours()*60+e.getMinutes()}px"></div>`}afterRender(){const e=this.$("#scroll-container");e&&!this._scrolled&&(e.scrollTop=8*60-50,this._scrolled=!0),this.$$("[data-event-id]").forEach(s=>{s.addEventListener("click",i=>{i.stopPropagation();const a=s.dataset.eventId,n=this.stateManager.getEvents().find(r=>r.id===a);n&&this.emit("event-click",{event:n})})});const t=this.$(".day-column");t&&t.addEventListener("click",s=>{const i=this.$("#scroll-container"),a=t.getBoundingClientRect(),n=s.clientY-a.top+(i?i.scrollTop:0),r=new Date(t.dataset.date);r.setHours(Math.floor(n/60),Math.floor(n%60),0,0),this.stateManager.selectDate(r),this.emit("day-click",{date:r})})}unmount(){this.unsubscribe&&this.unsubscribe()}}class ae extends C{constructor(){super(),this._isVisible=!1,this._cleanupFocusTrap=null,this.config={title:"New Event",defaultDuration:60,colors:[{color:"#2563EB",label:"Blue"},{color:"#10B981",label:"Green"},{color:"#F59E0B",label:"Amber"},{color:"#EF4444",label:"Red"},{color:"#8B5CF6",label:"Purple"},{color:"#6B7280",label:"Gray"}]},this._formData={title:"",start:new Date,end:new Date,allDay:!1,color:this.config.colors[0].color}}static get observedAttributes(){return["open"]}attributeChangedCallback(e,t,s){e==="open"&&(s!==null?this.open():this.close())}getStyles(){return`
953
+ `}renderNowIndicator(){const e=new Date;return`<div class="now-indicator" style="top: ${e.getHours()*60+e.getMinutes()}px"></div>`}afterRender(){const e=this.$("#scroll-container");e&&!this._scrolled&&(e.scrollTop=8*60-50,this._scrolled=!0),this.$$("[data-event-id]").forEach(s=>{this.addListener(s,"click",i=>{i.stopPropagation();const a=i.currentTarget.dataset.eventId,n=this.stateManager.getEvents().find(r=>r.id===a);n&&this.emit("event-click",{event:n})})});const t=this.$(".day-column");t&&this.addListener(t,"click",s=>{const i=s.currentTarget,a=this.$("#scroll-container"),n=i.getBoundingClientRect(),r=s.clientY-n.top+(a?a.scrollTop:0),o=new Date(i.dataset.date);o.setHours(Math.floor(r/60),Math.floor(r%60),0,0),this.stateManager.selectDate(o),this.emit("day-click",{date:o})})}unmount(){this.unsubscribe&&this.unsubscribe()}}class ae extends C{constructor(){super(),this._isVisible=!1,this._cleanupFocusTrap=null,this.config={title:"New Event",defaultDuration:60,colors:[{color:"#2563EB",label:"Blue"},{color:"#10B981",label:"Green"},{color:"#F59E0B",label:"Amber"},{color:"#EF4444",label:"Red"},{color:"#8B5CF6",label:"Purple"},{color:"#6B7280",label:"Gray"}]},this._formData={title:"",start:new Date,end:new Date,allDay:!1,color:this.config.colors[0].color}}static get observedAttributes(){return["open"]}attributeChangedCallback(e,t,s){e==="open"&&(s!==null?this.open():this.close())}getStyles(){return`
954
954
  ${D.getBaseStyles()}
955
955
  ${D.getButtonStyles()}
956
956
 
@@ -1172,7 +1172,7 @@ var Y=Object.defineProperty;var N=(u,e,t)=>e in u?Y(u,e,{enumerable:!0,configura
1172
1172
  <button class="fc-btn fc-btn-primary" id="save-btn">Save Event</button>
1173
1173
  </footer>
1174
1174
  </div>
1175
- `}afterRender(){this.modalContent=this.$(".modal-content"),this.titleInput=this.$("#event-title"),this.startInput=this.$("#event-start"),this.endInput=this.$("#event-end"),this.colorContainer=this.$("#color-picker"),this.titleGroup=this.$("#title-group"),this.endGroup=this.$("#end-group"),this.$("#close-x").addEventListener("click",()=>this.close()),this.$("#cancel-btn").addEventListener("click",()=>this.close()),this.$("#save-btn").addEventListener("click",()=>this.save()),this.colorContainer.querySelectorAll(".color-btn").forEach(e=>{e.addEventListener("click",t=>{this._formData.color=t.target.dataset.color,this.updateColorSelection()})}),this.addEventListener("click",e=>{e.target===this&&this.close()}),this._handleKeyDown=e=>{e.key==="Escape"&&this.hasAttribute("open")&&this.close()},window.addEventListener("keydown",this._handleKeyDown)}updateColorSelection(){this.colorContainer.querySelectorAll(".color-btn").forEach(t=>{const s=t.dataset.color===this._formData.color;t.classList.toggle("selected",s),t.setAttribute("aria-checked",s?"true":"false")})}open(e=new Date){this.setAttribute("open",""),this.titleGroup.classList.remove("has-error"),this.endGroup.classList.remove("has-error"),this._formData.start=e,this._formData.end=new Date(e.getTime()+this.config.defaultDuration*60*1e3),this._formData.title="",this._formData.color=this.config.colors[0].color,this.startInput&&(this.titleInput.value="",this.startInput.value=this.formatDateForInput(this._formData.start),this.endInput.value=this.formatDateForInput(this._formData.end),this.updateColorSelection(),this._cleanupFocusTrap=T.trapFocus(this.modalContent))}close(){this.removeAttribute("open"),this._cleanupFocusTrap&&(this._cleanupFocusTrap(),this._cleanupFocusTrap=null)}validate(){let e=!0;this.titleGroup.classList.remove("has-error"),this.endGroup.classList.remove("has-error"),this.titleInput.value.trim()||(this.titleGroup.classList.add("has-error"),e=!1);const t=new Date(this.startInput.value);return new Date(this.endInput.value)<=t&&(this.endGroup.classList.add("has-error"),e=!1),e}save(){if(!this.validate())return;const e={title:this.titleInput.value.trim(),start:new Date(this.startInput.value),end:new Date(this.endInput.value),backgroundColor:this._formData.color};this.emit("save",e),this.close()}formatDateForInput(e){const t=o=>String(o).padStart(2,"0"),s=e.getFullYear(),i=t(e.getMonth()+1),a=t(e.getDate()),n=t(e.getHours()),r=t(e.getMinutes());return`${s}-${i}-${a}T${n}:${r}`}unmount(){this._cleanupFocusTrap&&this._cleanupFocusTrap(),window.removeEventListener("keydown",this._handleKeyDown)}}customElements.get("force-calendar-event-form")||customElements.define("force-calendar-event-form",ae);customElements.get("force-calendar-month")||customElements.define("force-calendar-month",te);customElements.get("force-calendar-week")||customElements.define("force-calendar-week",se);customElements.get("force-calendar-day")||customElements.define("force-calendar-day",ie);class ne extends C{static get observedAttributes(){return["view","date","locale","timezone","week-starts-on","height"]}constructor(){super(),this.stateManager=null,this.currentView=null}initialize(){const e={view:this.getAttribute("view")||"month",date:this.getAttribute("date")?new Date(this.getAttribute("date")):new Date,locale:this.getAttribute("locale")||"en-US",timeZone:this.getAttribute("timezone")||Intl.DateTimeFormat().resolvedOptions().timeZone,weekStartsOn:parseInt(this.getAttribute("week-starts-on")||"0")};this.stateManager=new ee(e),this.stateManager.subscribe(this.handleStateChange.bind(this)),this.setupEventListeners()}setupEventListeners(){g.on("navigation:*",(e,t)=>{this.emit("calendar-navigate",{action:t.split(":")[1],...e})}),g.on("view:changed",e=>{this.emit("calendar-view-change",e)}),g.on("event:*",(e,t)=>{this.emit(`calendar-event-${t.split(":")[1]}`,e)}),g.on("date:selected",e=>{this.emit("calendar-date-select",e)})}handleStateChange(e,t){e.view!==(t==null?void 0:t.view)&&(this.currentView=e.view),this.render()}mount(){super.mount(),this.loadView(this.stateManager.getView())}loadView(e){this.currentView=e,this.render()}getStyles(){const e=this.getAttribute("height")||"800px";return`
1175
+ `}afterRender(){this.modalContent=this.$(".modal-content"),this.titleInput=this.$("#event-title"),this.startInput=this.$("#event-start"),this.endInput=this.$("#event-end"),this.colorContainer=this.$("#color-picker"),this.titleGroup=this.$("#title-group"),this.endGroup=this.$("#end-group"),this.addListener(this.$("#close-x"),"click",()=>this.close()),this.addListener(this.$("#cancel-btn"),"click",()=>this.close()),this.addListener(this.$("#save-btn"),"click",()=>this.save()),this.colorContainer.querySelectorAll(".color-btn").forEach(e=>{this.addListener(e,"click",t=>{this._formData.color=t.currentTarget.dataset.color,this.updateColorSelection()})}),this.addListener(this,"click",e=>{e.target===this&&this.close()}),this._handleKeyDown=e=>{e.key==="Escape"&&this.hasAttribute("open")&&this.close()},window.addEventListener("keydown",this._handleKeyDown)}updateColorSelection(){this.colorContainer.querySelectorAll(".color-btn").forEach(t=>{const s=t.dataset.color===this._formData.color;t.classList.toggle("selected",s),t.setAttribute("aria-checked",s?"true":"false")})}open(e=new Date){this.hasAttribute("open")||this.setAttribute("open",""),this.titleGroup.classList.remove("has-error"),this.endGroup.classList.remove("has-error"),this._formData.start=e,this._formData.end=new Date(e.getTime()+this.config.defaultDuration*60*1e3),this._formData.title="",this._formData.color=this.config.colors[0].color,this.startInput&&(this.titleInput.value="",this.startInput.value=this.formatDateForInput(this._formData.start),this.endInput.value=this.formatDateForInput(this._formData.end),this.updateColorSelection(),this._cleanupFocusTrap=T.trapFocus(this.modalContent))}close(){this.removeAttribute("open"),this._cleanupFocusTrap&&(this._cleanupFocusTrap(),this._cleanupFocusTrap=null)}validate(){let e=!0;this.titleGroup.classList.remove("has-error"),this.endGroup.classList.remove("has-error"),this.titleInput.value.trim()||(this.titleGroup.classList.add("has-error"),e=!1);const t=new Date(this.startInput.value);return new Date(this.endInput.value)<=t&&(this.endGroup.classList.add("has-error"),e=!1),e}save(){if(!this.validate())return;const e={title:this.titleInput.value.trim(),start:new Date(this.startInput.value),end:new Date(this.endInput.value),backgroundColor:this._formData.color};this.emit("save",e),this.close()}formatDateForInput(e){const t=o=>String(o).padStart(2,"0"),s=e.getFullYear(),i=t(e.getMonth()+1),a=t(e.getDate()),n=t(e.getHours()),r=t(e.getMinutes());return`${s}-${i}-${a}T${n}:${r}`}unmount(){this._cleanupFocusTrap&&this._cleanupFocusTrap(),window.removeEventListener("keydown",this._handleKeyDown)}}customElements.get("force-calendar-event-form")||customElements.define("force-calendar-event-form",ae);customElements.get("force-calendar-month")||customElements.define("force-calendar-month",te);customElements.get("force-calendar-week")||customElements.define("force-calendar-week",se);customElements.get("force-calendar-day")||customElements.define("force-calendar-day",ie);class ne extends C{static get observedAttributes(){return["view","date","locale","timezone","week-starts-on","height"]}constructor(){super(),this.stateManager=null,this.currentView=null}initialize(){const e={view:this.getAttribute("view")||"month",date:this.getAttribute("date")?new Date(this.getAttribute("date")):new Date,locale:this.getAttribute("locale")||"en-US",timeZone:this.getAttribute("timezone")||Intl.DateTimeFormat().resolvedOptions().timeZone,weekStartsOn:parseInt(this.getAttribute("week-starts-on")||"0")};this.stateManager=new ee(e),this.stateManager.subscribe(this.handleStateChange.bind(this)),this.setupEventListeners()}setupEventListeners(){g.on("navigation:*",(e,t)=>{this.emit("calendar-navigate",{action:t.split(":")[1],...e})}),g.on("view:changed",e=>{this.emit("calendar-view-change",e)}),g.on("event:*",(e,t)=>{this.emit(`calendar-event-${t.split(":")[1]}`,e)}),g.on("date:selected",e=>{this.emit("calendar-date-select",e)})}handleStateChange(e,t){e.view!==(t==null?void 0:t.view)&&(this.currentView=e.view),this.render()}mount(){super.mount(),this.loadView(this.stateManager.getView())}loadView(e){this.currentView=e,this.render()}getStyles(){const e=this.getAttribute("height")||"800px";return`
1176
1176
  ${D.getBaseStyles()}
1177
1177
  ${D.getButtonStyles()}
1178
1178
  ${D.getGridStyles()}
package/dist/index.html CHANGED
@@ -198,7 +198,7 @@
198
198
  }
199
199
  }
200
200
  </style>
201
- <script type="module" crossorigin src="/assets/index-CPUFXutP.js"></script>
201
+ <script type="module" crossorigin src="/assets/index-CDzXWV3y.js"></script>
202
202
  </head>
203
203
  <body>
204
204
  <div class="dashboard">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forcecalendar/interface",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "type": "module",
5
5
  "description": "Official interface layer for forceCalendar Core - Enterprise calendar components",
6
6
  "main": "dist/force-calendar-interface.js",
@@ -14,7 +14,7 @@
14
14
  "dev": "vite",
15
15
  "build": "vite build",
16
16
  "preview": "vite preview",
17
- "test": "echo \"No tests yet\""
17
+ "test": "jest --testTimeout=10000"
18
18
  },
19
19
  "repository": {
20
20
  "type": "git",
@@ -40,6 +40,11 @@
40
40
  "@forcecalendar/core": "^0.3.0"
41
41
  },
42
42
  "devDependencies": {
43
+ "@babel/core": "^7.28.5",
44
+ "@babel/preset-env": "^7.28.5",
45
+ "babel-jest": "^30.2.0",
46
+ "jest": "^30.2.0",
47
+ "jest-environment-jsdom": "^30.2.0",
43
48
  "vite": "^5.0.0"
44
49
  }
45
50
  }
@@ -283,20 +283,20 @@ export class EventForm extends BaseComponent {
283
283
  this.titleGroup = this.$('#title-group');
284
284
  this.endGroup = this.$('#end-group');
285
285
 
286
- // Event Listeners
287
- this.$('#close-x').addEventListener('click', () => this.close());
288
- this.$('#cancel-btn').addEventListener('click', () => this.close());
289
- this.$('#save-btn').addEventListener('click', () => this.save());
286
+ // Event Listeners using addListener for automatic cleanup
287
+ this.addListener(this.$('#close-x'), 'click', () => this.close());
288
+ this.addListener(this.$('#cancel-btn'), 'click', () => this.close());
289
+ this.addListener(this.$('#save-btn'), 'click', () => this.save());
290
290
 
291
291
  this.colorContainer.querySelectorAll('.color-btn').forEach(btn => {
292
- btn.addEventListener('click', (e) => {
293
- this._formData.color = e.target.dataset.color;
292
+ this.addListener(btn, 'click', (e) => {
293
+ this._formData.color = e.currentTarget.dataset.color;
294
294
  this.updateColorSelection();
295
295
  });
296
296
  });
297
297
 
298
298
  // Close on backdrop click
299
- this.addEventListener('click', (e) => {
299
+ this.addListener(this, 'click', (e) => {
300
300
  if (e.target === this) this.close();
301
301
  });
302
302
 
@@ -319,7 +319,9 @@ export class EventForm extends BaseComponent {
319
319
  }
320
320
 
321
321
  open(initialDate = new Date()) {
322
- this.setAttribute('open', '');
322
+ if (!this.hasAttribute('open')) {
323
+ this.setAttribute('open', '');
324
+ }
323
325
 
324
326
  // Reset errors
325
327
  this.titleGroup.classList.remove('has-error');
@@ -365,9 +365,9 @@ export class DayView extends BaseComponent {
365
365
  }
366
366
 
367
367
  this.$$('[data-event-id]').forEach(el => {
368
- el.addEventListener('click', (e) => {
368
+ this.addListener(el, 'click', (e) => {
369
369
  e.stopPropagation();
370
- const eventId = el.dataset.eventId;
370
+ const eventId = e.currentTarget.dataset.eventId;
371
371
  const event = this.stateManager.getEvents().find(ev => ev.id === eventId);
372
372
  if (event) this.emit('event-click', { event });
373
373
  });
@@ -375,12 +375,13 @@ export class DayView extends BaseComponent {
375
375
 
376
376
  const dayCol = this.$('.day-column');
377
377
  if (dayCol) {
378
- dayCol.addEventListener('click', (e) => {
378
+ this.addListener(dayCol, 'click', (e) => {
379
+ const col = e.currentTarget;
379
380
  const container = this.$('#scroll-container');
380
- const rect = dayCol.getBoundingClientRect();
381
+ const rect = col.getBoundingClientRect();
381
382
  const y = e.clientY - rect.top + (container ? container.scrollTop : 0);
382
383
 
383
- const date = new Date(dayCol.dataset.date);
384
+ const date = new Date(col.dataset.date);
384
385
  date.setHours(Math.floor(y / 60), Math.floor(y % 60), 0, 0);
385
386
 
386
387
  this.stateManager.selectDate(date);
@@ -480,17 +480,17 @@ export class MonthView extends BaseComponent {
480
480
  afterRender() {
481
481
  // Add click handlers for days
482
482
  this.$$('.month-day').forEach(dayEl => {
483
- dayEl.addEventListener('click', this.handleDayClick.bind(this));
483
+ this.addListener(dayEl, 'click', this.handleDayClick);
484
484
  });
485
485
 
486
486
  // Add click handlers for events
487
487
  this.$$('.event-item').forEach(eventEl => {
488
- eventEl.addEventListener('click', this.handleEventClick.bind(this));
488
+ this.addListener(eventEl, 'click', this.handleEventClick);
489
489
  });
490
490
 
491
491
  // Add click handlers for "more events"
492
492
  this.$$('.more-events').forEach(moreEl => {
493
- moreEl.addEventListener('click', this.handleMoreClick.bind(this));
493
+ this.addListener(moreEl, 'click', this.handleMoreClick);
494
494
  });
495
495
  }
496
496
 
@@ -373,21 +373,22 @@ export class WeekView extends BaseComponent {
373
373
  }
374
374
 
375
375
  this.$$('[data-event-id]').forEach(el => {
376
- el.addEventListener('click', (e) => {
376
+ this.addListener(el, 'click', (e) => {
377
377
  e.stopPropagation();
378
- const eventId = el.dataset.eventId;
378
+ const eventId = e.currentTarget.dataset.eventId;
379
379
  const event = this.stateManager.getEvents().find(ev => ev.id === eventId);
380
380
  if (event) this.emit('event-click', { event });
381
381
  });
382
382
  });
383
383
 
384
384
  this.$$('.day-column').forEach(el => {
385
- el.addEventListener('click', (e) => {
385
+ this.addListener(el, 'click', (e) => {
386
+ const col = e.currentTarget;
386
387
  const container = this.$('#scroll-container');
387
- const rect = el.getBoundingClientRect();
388
+ const rect = col.getBoundingClientRect();
388
389
  const y = e.clientY - rect.top + (container ? container.scrollTop : 0);
389
390
 
390
- const date = new Date(el.dataset.date);
391
+ const date = new Date(col.dataset.date);
391
392
  date.setHours(Math.floor(y / 60), Math.floor(y % 60), 0, 0);
392
393
 
393
394
  this.stateManager.selectDate(date);
@@ -87,9 +87,9 @@ export class BaseComponent extends HTMLElement {
87
87
  }
88
88
 
89
89
  // Event handling
90
- addEventListener(element, event, handler) {
90
+ addListener(element, event, handler) {
91
91
  if (!element || !event || !handler) {
92
- console.warn('addEventListener called with invalid parameters', { element, event, handler });
92
+ console.warn('addListener called with invalid parameters', { element, event, handler });
93
93
  return;
94
94
  }
95
95
  const boundHandler = handler.bind(this);