UNPKG

vue3-calendar-heatmap

Version:

A lightweight calendar heatmap Vue 3 component built on SVG, inspired by julienr114's vue-calendar-heatmap ans github's contribution calendar graph

3 lines (2 loc) 11.8 kB
(function(i,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue"),require("tippy.js")):typeof define=="function"&&define.amd?define(["exports","vue","tippy.js"],e):(i=typeof globalThis<"u"?globalThis:i||self,e(i.CalendarHeatmap={},i.Vue,i.tippy))})(this,function(i,e,h){"use strict";var ue=Object.defineProperty;var De=(i,e,h)=>e in i?ue(i,e,{enumerable:!0,configurable:!0,writable:!0,value:h}):i[e]=h;var d=(i,e,h)=>(De(i,typeof e!="symbol"?e+"":e,h),h);const f=class{constructor(a,n,l){d(this,"startDate");d(this,"endDate");d(this,"max");d(this,"_values");d(this,"_firstFullWeekOfMonths");d(this,"_activities");d(this,"_calendar");this.endDate=this.parseDate(a),this.max=l||Math.ceil(Math.max(...n.map(E=>E.count))/5*4),this.startDate=this.shiftDate(a,-f.DAYS_IN_ONE_YEAR),this._values=n}set values(a){this.max=Math.ceil(Math.max(...a.map(n=>n.count))/5*4),this._values=a,this._firstFullWeekOfMonths=void 0,this._calendar=void 0,this._activities=void 0}get values(){return this._values}get activities(){if(!this._activities){this._activities=new Map;for(let a=0,n=this.values.length;a<n;a++)this._activities.set(this.keyDayParser(this.values[a].date),{count:this.values[a].count,colorIndex:this.getColorIndex(this.values[a].count)})}return this._activities}get weekCount(){return this.getDaysCount()/f.DAYS_IN_WEEK}get calendar(){if(!this._calendar){let a=this.shiftDate(this.startDate,-this.getCountEmptyDaysAtStart());a=new Date(a.getFullYear(),a.getMonth(),a.getDate()),this._calendar=new Array(this.weekCount);for(let n=0,l=this._calendar.length;n<l;n++){this._calendar[n]=new Array(f.DAYS_IN_WEEK);for(let E=0;E<f.DAYS_IN_WEEK;E++){const s=this.activities.get(this.keyDayParser(a));this._calendar[n][E]={date:new Date(a.valueOf()),count:s?s.count:void 0,colorIndex:s?s.colorIndex:0},a.setDate(a.getDate()+1)}}}return this._calendar}get firstFullWeekOfMonths(){if(!this._firstFullWeekOfMonths){const a=this.calendar;this._firstFullWeekOfMonths=[];for(let n=1,l=a.length;n<l;n++){const E=a[n-1][0].date,s=a[n][0].date;(E.getFullYear()<s.getFullYear()||E.getMonth()<s.getMonth())&&this._firstFullWeekOfMonths.push({value:s.getMonth(),index:n})}}return this._firstFullWeekOfMonths}getColorIndex(a){return a==null?0:a<=0?1:a>=this.max?5:Math.ceil(a*100/this.max*.03)+1}getCountEmptyDaysAtStart(){return this.startDate.getDay()}getCountEmptyDaysAtEnd(){return f.DAYS_IN_WEEK-1-this.endDate.getDay()}getDaysCount(){return f.DAYS_IN_ONE_YEAR+1+this.getCountEmptyDaysAtStart()+this.getCountEmptyDaysAtEnd()}shiftDate(a,n){const l=new Date(a);return l.setDate(l.getDate()+n),l}parseDate(a){return a instanceof Date?a:new Date(a)}keyDayParser(a){const n=this.parseDate(a);return String(n.getFullYear())+String(n.getMonth()).padStart(2,"0")+String(n.getDate()).padStart(2,"0")}};let o=f;d(o,"DEFAULT_RANGE_COLOR_LIGHT",["#ebedf0","#dae2ef","#c0ddf9","#73b3f3","#3886e1","#17459e"]),d(o,"DEFAULT_RANGE_COLOR_DARK",["#1f1f22","#1e334a","#1d466c","#1d5689","#1d69ac","#1B95D1"]),d(o,"DEFAULT_LOCALE",{months:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],days:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],on:"on",less:"Less",more:"More"}),d(o,"DEFAULT_TOOLTIP_UNIT","contributions"),d(o,"DAYS_IN_ONE_YEAR",365),d(o,"DAYS_IN_WEEK",7),d(o,"SQUARE_SIZE",10);const Ae="",Re="",M=e.defineComponent({name:"CalendarHeatmap",props:{endDate:{required:!0},max:{type:Number},rangeColor:{type:Array},values:{type:Array,required:!0},locale:{type:Object},tooltip:{type:Boolean,default:!0},tooltipUnit:{type:String,default:o.DEFAULT_TOOLTIP_UNIT},tooltipFormatter:{type:Function},vertical:{type:Boolean,default:!1},noDataText:{type:[Boolean,String],default:null},round:{type:Number,default:0},darkMode:Boolean},emits:["dayClick"],setup(t){const a=o.SQUARE_SIZE/5,n=o.SQUARE_SIZE+a,l=Math.ceil(o.SQUARE_SIZE*2.5),E=n*3,s=o.SQUARE_SIZE+o.SQUARE_SIZE/2,c=o.SQUARE_SIZE+o.SQUARE_SIZE/2,_=`translate(${l}, ${s})`,D=e.ref(null),A=e.ref(new Date),m=e.ref(new o(t.endDate,t.values,t.max)),R=e.ref(0),p=e.ref(0),B=e.ref("0 0 0 0"),C=e.ref("0 0 0 0"),T=e.ref(""),w=e.ref(""),L=e.ref(""),u=e.ref({}),I=e.ref(t.rangeColor||(t.darkMode?o.DEFAULT_RANGE_COLOR_DARK:o.DEFAULT_RANGE_COLOR_LIGHT)),{values:ie,tooltipUnit:de,tooltipFormatter:Ee,noDataText:ce,max:_e,vertical:Q,locale:he}=e.toRefs(t),S=new Map;let g;function Z(){S.clear(),g?g.setInstances(Array.from(S.values())):g=h.createSingleton(Array.from(S.values()),{overrides:[],moveTransition:"transform 0.1s ease-out",allowHTML:!0})}function fe(r){if(t.tooltip){if(r.count!==void 0)return t.tooltipFormatter?t.tooltipFormatter(r,t.tooltipUnit):`<b>${r.count} ${t.tooltipUnit}</b> ${u.value.on} ${u.value.months[r.date.getMonth()]} ${r.date.getDate()}, ${r.date.getFullYear()}`;if(t.noDataText)return`${t.noDataText}`;if(t.noDataText!==!1)return`<b>No ${t.tooltipUnit}</b> ${u.value.on} ${u.value.months[r.date.getMonth()]} ${r.date.getDate()}, ${r.date.getFullYear()}`}}function me(r){return t.vertical?`translate(0, ${n*m.value.weekCount-(r+1)*n})`:`translate(${r*n}, 0)`}function Se(r){return t.vertical?`translate(${r*n}, 0)`:`translate(0, ${r*n})`}function ge(r){return t.vertical?{x:3,y:n*m.value.weekCount-n*r.index-n/4}:{x:n*r.index,y:n-a}}e.watch([e.toRef(t,"rangeColor"),e.toRef(t,"darkMode")],([r,y])=>{I.value=r||(y?o.DEFAULT_RANGE_COLOR_DARK:o.DEFAULT_RANGE_COLOR_LIGHT)}),e.watch(Q,r=>{r?(R.value=l+n*o.DAYS_IN_WEEK+E,p.value=s+n*m.value.weekCount+a,T.value=`translate(${l}, 0)`,w.value=`translate(0, ${s})`):(R.value=l+n*m.value.weekCount+a,p.value=s+n*o.DAYS_IN_WEEK,T.value=`translate(0, ${s})`,w.value=`translate(${l}, 0)`)},{immediate:!0}),e.watch([R,p],([r,y])=>B.value=` 0 0 ${r} ${y}`,{immediate:!0}),e.watch([R,p,I],([r,y,k])=>{L.value=Q.value?`translate(${l+n*o.DAYS_IN_WEEK}, ${s})`:`translate(${r-n*k.length-30}, ${y-c})`},{immediate:!0}),e.watch(he,r=>u.value=r?{...o.DEFAULT_LOCALE,...r}:o.DEFAULT_LOCALE,{immediate:!0}),e.watch(I,r=>C.value=`0 0 ${o.SQUARE_SIZE*(r.length+1)} ${o.SQUARE_SIZE}`,{immediate:!0}),e.watch([ie,de,Ee,ce,_e,I],()=>{m.value=new o(t.endDate,t.values,t.max),S.forEach(r=>r.destroy()),e.nextTick(Z)}),e.onMounted(Z),e.onBeforeUnmount(()=>{g==null||g.destroy(),S.forEach(r=>r.destroy())});function ye(r){if(g&&r.target&&r.target.classList.contains("vch__day__square")&&r.target.dataset.weekIndex!==void 0&&r.target.dataset.dayIndex!==void 0){const y=Number(r.target.dataset.weekIndex),k=Number(r.target.dataset.dayIndex);if(!isNaN(y)&&!isNaN(k)){const O=fe(m.value.calendar[y][k]);if(O){const N=S.get(r.target);N?N.setContent(O):N||(S.set(r.target,h(r.target,{content:O})),g.setInstances(Array.from(S.values())))}}}}return{SQUARE_BORDER_SIZE:a,SQUARE_SIZE:n,LEFT_SECTION_WIDTH:l,RIGHT_SECTION_WIDTH:E,TOP_SECTION_HEIGHT:s,BOTTOM_SECTION_HEIGHT:c,svg:D,heatmap:m,now:A,width:R,height:p,viewbox:B,daysLabelWrapperTransform:T,monthsLabelWrapperTransform:w,yearWrapperTransform:_,legendWrapperTransform:L,lo:u,legendViewbox:C,curRangeColor:I,getWeekPosition:me,getDayPosition:Se,getMonthLabelPosition:ge,initTippyLazy:ye}}}),pe="",F=(t,a)=>{const n=t.__vccOpts||t;for(const[l,E]of a)n[l]=E;return n},$=["viewBox"],b=["transform"],W=["x","y"],v=["transform"],V=["x","y"],Y=["x","y"],P=["x","y"],G=["transform"],K=["x"],H=["rx","ry","width","height","x","y"],z=["x","y"],j=["transform"],q=["transform"],J=["rx","ry","transform","width","height","data-week-index","data-day-index","onClick"],X={class:"vch__legend"},x={class:"vch__legend-left"},ee={class:"vch__legend-right"},te={class:"vch__legend"},ae=["viewBox","height"],ne={class:"vch__legend__wrapper"},re=["rx","ry","width","height","x"];function oe(t,a,n,l,E,s){return e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass({vch__container:!0,"dark-mode":t.darkMode})},[(e.openBlock(),e.createElementBlock("svg",{class:"vch__wrapper",ref:"svg",viewBox:t.viewbox},[e.createElementVNode("g",{class:"vch__months__labels__wrapper",transform:t.monthsLabelWrapperTransform},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(t.heatmap.firstFullWeekOfMonths,(c,_)=>(e.openBlock(),e.createElementBlock("text",{class:"vch__month__label",key:_,x:t.getMonthLabelPosition(c).x,y:t.getMonthLabelPosition(c).y},e.toDisplayString(t.lo.months[c.value]),9,W))),128))],8,b),e.createElementVNode("g",{class:"vch__days__labels__wrapper",transform:t.daysLabelWrapperTransform},[e.createElementVNode("text",{class:"vch__day__label",x:t.vertical?t.SQUARE_SIZE:0,y:t.vertical?t.SQUARE_SIZE-t.SQUARE_BORDER_SIZE:20},e.toDisplayString(t.lo.days[1]),9,V),e.createElementVNode("text",{class:"vch__day__label",x:t.vertical?t.SQUARE_SIZE*3:0,y:t.vertical?t.SQUARE_SIZE-t.SQUARE_BORDER_SIZE:44},e.toDisplayString(t.lo.days[3]),9,Y),e.createElementVNode("text",{class:"vch__day__label",x:t.vertical?t.SQUARE_SIZE*5:0,y:t.vertical?t.SQUARE_SIZE-t.SQUARE_BORDER_SIZE:69},e.toDisplayString(t.lo.days[5]),9,P)],8,v),t.vertical?(e.openBlock(),e.createElementBlock("g",{key:0,class:"vch__legend__wrapper",transform:t.legendWrapperTransform},[e.createElementVNode("text",{x:t.SQUARE_SIZE*1.25,y:"8"},e.toDisplayString(t.lo.less),9,K),(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(t.curRangeColor,(c,_)=>(e.openBlock(),e.createElementBlock("rect",{key:_,rx:t.round,ry:t.round,style:e.normalizeStyle({fill:c}),width:t.SQUARE_SIZE-t.SQUARE_BORDER_SIZE,height:t.SQUARE_SIZE-t.SQUARE_BORDER_SIZE,x:t.SQUARE_SIZE*1.75,y:t.SQUARE_SIZE*(_+1)},null,12,H))),128)),e.createElementVNode("text",{x:t.SQUARE_SIZE*1.25,y:t.SQUARE_SIZE*(t.curRangeColor.length+2)-t.SQUARE_BORDER_SIZE},e.toDisplayString(t.lo.more),9,z)],8,G)):e.createCommentVNode("",!0),e.createElementVNode("g",{class:"vch__year__wrapper",transform:t.yearWrapperTransform,onMouseover:a[0]||(a[0]=(...c)=>t.initTippyLazy&&t.initTippyLazy(...c))},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(t.heatmap.calendar,(c,_)=>(e.openBlock(),e.createElementBlock("g",{class:"vch__month__wrapper",key:_,transform:t.getWeekPosition(_)},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(c,(D,A)=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:A},[D.date<t.now?(e.openBlock(),e.createElementBlock("rect",{key:0,class:"vch__day__square",rx:t.round,ry:t.round,transform:t.getDayPosition(A),width:t.SQUARE_SIZE-t.SQUARE_BORDER_SIZE,height:t.SQUARE_SIZE-t.SQUARE_BORDER_SIZE,style:e.normalizeStyle({fill:t.curRangeColor[D.colorIndex]}),"data-week-index":_,"data-day-index":A,onClick:m=>t.$emit("dayClick",D)},null,12,J)):e.createCommentVNode("",!0)],64))),128))],8,q))),128))],40,j)],8,$)),e.createElementVNode("div",X,[e.renderSlot(t.$slots,"legend",{},()=>[e.createElementVNode("div",x,[e.renderSlot(t.$slots,"vch__legend-left")]),e.createElementVNode("div",ee,[e.renderSlot(t.$slots,"legend-right",{},()=>[e.createElementVNode("div",te,[e.createElementVNode("div",null,e.toDisplayString(t.lo.less),1),t.vertical?e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock("svg",{key:0,class:"vch__external-legend-wrapper",viewBox:t.legendViewbox,height:t.SQUARE_SIZE-t.SQUARE_BORDER_SIZE},[e.createElementVNode("g",ne,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(t.curRangeColor,(c,_)=>(e.openBlock(),e.createElementBlock("rect",{key:_,rx:t.round,ry:t.round,style:e.normalizeStyle({fill:c}),width:t.SQUARE_SIZE-t.SQUARE_BORDER_SIZE,height:t.SQUARE_SIZE-t.SQUARE_BORDER_SIZE,x:t.SQUARE_SIZE*_},null,12,re))),128))])],8,ae)),e.createElementVNode("div",null,e.toDisplayString(t.lo.more),1)])])])])])],2)}const U=F(M,[["render",oe]]);function le(t){t.component(U.name,U)}const se={install:le};i.CalendarHeatmap=U,i.Heatmap=o,i.default=se,Object.defineProperties(i,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}); //# sourceMappingURL=vue3-calendar-heatmap.umd.js.map