From 338ffb9c191954d50255ba5ba80960f4c7fd47a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=BChl?= Date: Wed, 8 Apr 2026 17:14:48 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20layout=20polish=20=E2=80=94=20responsive?= =?UTF-8?q?=20filter,=20scroll,=20resize,=20min=20cell=20size?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- Resources/public/heatmap.css | 22 +++++++++++++++++++--- Resources/public/heatmap.js | 2 +- assets/src/heatmap.ts | 31 +++++++++++++++++++++++++------ assets/test/setup.ts | 8 ++++++++ vitest.config.ts | 1 + 5 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 assets/test/setup.ts diff --git a/Resources/public/heatmap.css b/Resources/public/heatmap.css index 628d548..1b5ddd6 100644 --- a/Resources/public/heatmap.css +++ b/Resources/public/heatmap.css @@ -42,8 +42,8 @@ } .heatmap-wrapper .heatmap-svg-area { - flex: 1; - min-width: 0; + width: 100%; + overflow-x: scroll; } .heatmap-wrapper .heatmap-filter { @@ -56,6 +56,22 @@ max-width: 200px; } +/* Small screens: filter above heatmap */ +@media (max-width: 1330px) { + .heatmap-wrapper { + flex-direction: column-reverse; + } + + .heatmap-wrapper .heatmap-filter { + padding-top: 0; + width: 100%; + } + + .heatmap-filter select { + max-width: none; + } +} + .heatmap-weekend { opacity: 0.8; } @@ -67,7 +83,7 @@ .heatmap-stats { display: flex; gap: 16px; - padding: 8px 0 0; + padding: 12px 0 0; font-size: 0.8125rem; color: var(--tblr-secondary, #6c757d); flex-wrap: wrap; diff --git a/Resources/public/heatmap.js b/Resources/public/heatmap.js index bb84665..7241bb7 100644 --- a/Resources/public/heatmap.js +++ b/Resources/public/heatmap.js @@ -1 +1 @@ -"use strict";var KimaiHeatmap=(()=>{var Wt=Object.defineProperty;var en=Object.getOwnPropertyDescriptor;var rn=Object.getOwnPropertyNames;var nn=Object.prototype.hasOwnProperty;var on=(t,e)=>{for(var r in e)Wt(t,r,{get:e[r],enumerable:!0})},an=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of rn(e))!nn.call(t,o)&&o!==r&&Wt(t,o,{get:()=>e[o],enumerable:!(n=en(e,o))||n.enumerable});return t};var un=t=>an(Wt({},"__esModule",{value:!0}),t);var za={};on(za,{calculateStats:()=>Br,calculateStreak:()=>Vr,init:()=>Ra,renderHeatmap:()=>ce});var St="http://www.w3.org/1999/xhtml",Pt={svg:"http://www.w3.org/2000/svg",xhtml:St,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function Tt(t){var e=t+="",r=e.indexOf(":");return r>=0&&(e=t.slice(0,r))!=="xmlns"&&(t=t.slice(r+1)),Pt.hasOwnProperty(e)?{space:Pt[e],local:t}:t}function sn(t){return function(){var e=this.ownerDocument,r=this.namespaceURI;return r===St&&e.documentElement.namespaceURI===St?e.createElement(t):e.createElementNS(r,t)}}function ln(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function Dt(t){var e=Tt(t);return(e.local?ln:sn)(e)}function fn(){}function Ct(t){return t==null?fn:function(){return this.querySelector(t)}}function pe(t){typeof t!="function"&&(t=Ct(t));for(var e=this._groups,r=e.length,n=new Array(r),o=0;o=A&&(A=H+1);!(N=W[A])&&++A=0;)(i=n[o])&&(a&&i.compareDocumentPosition(a)^4&&a.parentNode.insertBefore(i,a),a=i);return this}function be(t){t||(t=Sn);function e(p,g){return p&&g?t(p.__data__,g.__data__):!p-!g}for(var r=this._groups,n=r.length,o=new Array(n),a=0;ae?1:t>=e?0:NaN}function Ue(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this}function ke(){return Array.from(this)}function Ae(){for(var t=this._groups,e=0,r=t.length;e1?this.each((e==null?An:typeof e=="function"?Nn:Fn)(t,e,r??"")):Ln(this.node(),t)}function Ln(t,e){return t.style.getPropertyValue(e)||kt(t).getComputedStyle(t,null).getPropertyValue(e)}function Hn(t){return function(){delete this[t]}}function Yn(t,e){return function(){this[t]=e}}function En(t,e){return function(){var r=e.apply(this,arguments);r==null?delete this[t]:this[t]=r}}function Ee(t,e){return arguments.length>1?this.each((e==null?Hn:typeof e=="function"?En:Yn)(t,e)):this.node()[t]}function We(t){return t.trim().split(/^|\s+/)}function Ot(t){return t.classList||new Pe(t)}function Pe(t){this._node=t,this._names=We(t.getAttribute("class")||"")}Pe.prototype={add:function(t){var e=this._names.indexOf(t);e<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};function Ie(t,e){for(var r=Ot(t),n=-1,o=e.length;++n=0&&(r=e.slice(n+1),e=e.slice(0,n)),{type:e,name:r}})}function to(t){return function(){var e=this.__on;if(e){for(var r=0,n=-1,o=e.length,a;re?1:t>=e?0:NaN}function zt(t,e){return t==null||e==null?NaN:et?1:e>=t?0:NaN}function At(t){let e,r,n;t.length!==2?(e=tt,r=(u,s)=>tt(t(u),s),n=(u,s)=>t(u)-s):(e=t===tt||t===zt?t:io,r=t,n=t);function o(u,s,l=0,c=u.length){if(l>>1;r(u[p],s)<0?l=p+1:c=p}while(l>>1;r(u[p],s)<=0?l=p+1:c=p}while(ll&&n(u[p-1],s)>-n(u[p],s)?p-1:p}return{left:o,center:i,right:a}}function io(){return 0}function $t(t){return t===null?NaN:+t}var er=At(tt),rr=er.right,uo=er.left,so=At($t).center,Vt=rr;var lo=Math.sqrt(50),fo=Math.sqrt(10),co=Math.sqrt(2);function Ft(t,e,r){let n=(e-t)/Math.max(0,r),o=Math.floor(Math.log10(n)),a=n/Math.pow(10,o),i=a>=lo?10:a>=fo?5:a>=co?2:1,u,s,l;return o<0?(l=Math.pow(10,-o)/i,u=Math.round(t*l),s=Math.round(e*l),u/le&&--s,l=-l):(l=Math.pow(10,o)*i,u=Math.round(t/l),s=Math.round(e/l),u*le&&--s),s0))return[];if(t===e)return[t];let n=e=o))return[];let u=a-o+1,s=new Array(u);if(n)if(i<0)for(let l=0;l=n)&&(r=n);else{let n=-1;for(let o of t)(o=e(o,++n,t))!=null&&(r=o)&&(r=o)}return r}function nr(t,e){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(e).domain(t);break}return this}function or(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)}function et(t,e){if(!isFinite(t)||t===0)return null;var r=(t=e?t.toExponential(e-1):t.toExponential()).indexOf("e"),n=t.slice(0,r);return[n.length>1?n[0]+n.slice(2):n,+t.slice(r+1)]}function q(t){return t=et(Math.abs(t)),t?t[1]:NaN}function ar(t,e){return function(r,n){for(var o=r.length,a=[],i=0,u=t[0],s=0;o>0&&u>0&&(s+u+1>n&&(u=Math.max(1,n-s)),a.push(r.substring(o-=u,o+u)),!((s+=u+1)>n));)u=t[i=(i+1)%t.length];return a.reverse().join(e)}}function ir(t){return function(e){return e.replace(/[0-9]/g,function(r){return t[+r]})}}var mo=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function Q(t){if(!(e=mo.exec(t)))throw new Error("invalid format: "+t);var e;return new Lt({fill:e[1],align:e[2],sign:e[3],symbol:e[4],zero:e[5],width:e[6],comma:e[7],precision:e[8]&&e[8].slice(1),trim:e[9],type:e[10]})}Q.prototype=Lt.prototype;function Lt(t){this.fill=t.fill===void 0?" ":t.fill+"",this.align=t.align===void 0?">":t.align+"",this.sign=t.sign===void 0?"-":t.sign+"",this.symbol=t.symbol===void 0?"":t.symbol+"",this.zero=!!t.zero,this.width=t.width===void 0?void 0:+t.width,this.comma=!!t.comma,this.precision=t.precision===void 0?void 0:+t.precision,this.trim=!!t.trim,this.type=t.type===void 0?"":t.type+""}Lt.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(this.width===void 0?"":Math.max(1,this.width|0))+(this.comma?",":"")+(this.precision===void 0?"":"."+Math.max(0,this.precision|0))+(this.trim?"~":"")+this.type};function ur(t){t:for(var e=t.length,r=1,n=-1,o;r0&&(n=0);break}return n>0?t.slice(0,n)+t.slice(o+1):t}var dt;function sr(t,e){var r=et(t,e);if(!r)return dt=void 0,t.toPrecision(e);var n=r[0],o=r[1],a=o-(dt=Math.max(-8,Math.min(8,Math.floor(o/3)))*3)+1,i=n.length;return a===i?n:a>i?n+new Array(a-i+1).join("0"):a>0?n.slice(0,a)+"."+n.slice(a):"0."+new Array(1-a).join("0")+et(t,Math.max(0,e+a-1))[0]}function Zt(t,e){var r=et(t,e);if(!r)return t+"";var n=r[0],o=r[1];return o<0?"0."+new Array(-o).join("0")+n:n.length>o+1?n.slice(0,o+1)+"."+n.slice(o+1):n+new Array(o-n.length+2).join("0")}var Qt={"%":(t,e)=>(t*100).toFixed(e),b:t=>Math.round(t).toString(2),c:t=>t+"",d:or,e:(t,e)=>t.toExponential(e),f:(t,e)=>t.toFixed(e),g:(t,e)=>t.toPrecision(e),o:t=>Math.round(t).toString(8),p:(t,e)=>Zt(t*100,e),r:Zt,s:sr,X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function Xt(t){return t}var lr=Array.prototype.map,fr=["y","z","a","f","p","n","\xB5","m","","k","M","G","T","P","E","Z","Y"];function cr(t){var e=t.grouping===void 0||t.thousands===void 0?Xt:ar(lr.call(t.grouping,Number),t.thousands+""),r=t.currency===void 0?"":t.currency[0]+"",n=t.currency===void 0?"":t.currency[1]+"",o=t.decimal===void 0?".":t.decimal+"",a=t.numerals===void 0?Xt:ir(lr.call(t.numerals,String)),i=t.percent===void 0?"%":t.percent+"",u=t.minus===void 0?"\u2212":t.minus+"",s=t.nan===void 0?"NaN":t.nan+"";function l(p,g){p=Q(p);var x=p.fill,w=p.align,_=p.sign,W=p.symbol,P=p.zero,H=p.width,A=p.comma,T=p.precision,N=p.trim,D=p.type;D==="n"?(A=!0,D="g"):Qt[D]||(T===void 0&&(T=12),N=!0,D="g"),(P||x==="0"&&w==="=")&&(P=!0,x="0",w="=");var at=(g&&g.prefix!==void 0?g.prefix:"")+(W==="$"?r:W==="#"&&/[boxX]/.test(D)?"0"+D.toLowerCase():""),I=(W==="$"?n:/[%p]/.test(D)?i:"")+(g&&g.suffix!==void 0?g.suffix:""),B=Qt[D],it=/[defgprs%]/.test(D);T=T===void 0?6:/[gprs]/.test(D)?Math.max(1,Math.min(21,T)):Math.max(0,Math.min(20,T));function K(d){var U=at,h=I,C,ut,Z;if(D==="c")h=B(d)+h,d="";else{d=+d;var R=d<0||1/d<0;if(d=isNaN(d)?s:B(Math.abs(d),T),N&&(d=ur(d)),R&&+d==0&&_!=="+"&&(R=!1),U=(R?_==="("?_:u:_==="-"||_==="("?"":_)+U,h=(D==="s"&&!isNaN(d)&&dt!==void 0?fr[8+dt/3]:"")+h+(R&&_==="("?")":""),it){for(C=-1,ut=d.length;++CZ||Z>57){h=(Z===46?o+d.slice(C+1):d.slice(C))+h,d=d.slice(0,C);break}}}A&&!P&&(d=e(d,1/0));var st=U.length+d.length+h.length,O=st>1)+U+d+h+O.slice(st);break;default:d=O+U+d+h;break}return a(d)}return K.toString=function(){return p+""},K}function c(p,g){var x=Math.max(-8,Math.min(8,Math.floor(q(g)/3)))*3,w=Math.pow(10,-x),_=l((p=Q(p),p.type="f",p),{suffix:fr[8+x/3]});return function(W){return _(w*W)}}return{format:l,formatPrefix:c}}var Ht,Yt,Et;Gt({thousands:",",grouping:[3],currency:["$",""]});function Gt(t){return Ht=cr(t),Yt=Ht.format,Et=Ht.formatPrefix,Ht}function Jt(t){return Math.max(0,-q(Math.abs(t)))}function Kt(t,e){return Math.max(0,Math.max(-8,Math.min(8,Math.floor(q(e)/3)))*3-q(Math.abs(t)))}function jt(t,e){return t=Math.abs(t),e=Math.abs(e)-t,Math.max(0,q(e)-q(t))+1}function te(t,e,r,n){var o=Bt(t,e,r),a;switch(n=Q(n??",f"),n.type){case"s":{var i=Math.max(Math.abs(t),Math.abs(e));return n.precision==null&&!isNaN(a=Kt(o,i))&&(n.precision=a),Et(n,i)}case"":case"e":case"g":case"p":case"r":{n.precision==null&&!isNaN(a=jt(o,Math.max(Math.abs(t),Math.abs(e))))&&(n.precision=a-(n.type==="e"));break}case"f":case"%":{n.precision==null&&!isNaN(a=Jt(o))&&(n.precision=a-(n.type==="%")*2);break}}return Yt(n)}function mr(t){var e=t.domain;return t.ticks=function(r){var n=e();return Nt(n[0],n[n.length-1],r??10)},t.tickFormat=function(r,n){var o=e();return te(o[0],o[o.length-1],r??10,n)},t.nice=function(r){r==null&&(r=10);var n=e(),o=0,a=n.length-1,i=n[o],u=n[a],s,l,c=10;for(u0;){if(l=pt(i,u,r),l===s)return n[o]=i,n[a]=u,e(n);if(l>0)i=Math.floor(i/l)*l,u=Math.ceil(u/l)*l;else if(l<0)i=Math.ceil(i*l)/l,u=Math.floor(u*l)/l;else break;s=l}return t},t}function yt(){var t=0,e=1,r=1,n=[.5],o=[0,1],a;function i(s){return s!=null&&s<=s?o[Vt(n,s,0,r)]:a}function u(){var s=-1;for(n=new Array(r);++s=r?[n[r-1],e]:[n[l-1],n[l]]},i.unknown=function(s){return arguments.length&&(a=s),i},i.thresholds=function(){return n.slice()},i.copy=function(){return yt().domain([t,e]).range(o).unknown(a)},nr.apply(mr(i),arguments)}var ee=new Date,re=new Date;function F(t,e,r,n){function o(a){return t(a=arguments.length===0?new Date:new Date(+a)),a}return o.floor=a=>(t(a=new Date(+a)),a),o.ceil=a=>(t(a=new Date(a-1)),e(a,1),t(a),a),o.round=a=>{let i=o(a),u=o.ceil(a);return a-i(e(a=new Date(+a),i==null?1:Math.floor(i)),a),o.range=(a,i,u)=>{let s=[];if(a=o.ceil(a),u=u==null?1:Math.floor(u),!(a0))return s;let l;do s.push(l=new Date(+a)),e(a,u),t(a);while(lF(i=>{if(i>=i)for(;t(i),!a(i);)i.setTime(i-1)},(i,u)=>{if(i>=i)if(u<0)for(;++u<=0;)for(;e(i,-1),!a(i););else for(;--u>=0;)for(;e(i,1),!a(i););}),r&&(o.count=(a,i)=>(ee.setTime(+a),re.setTime(+i),t(ee),t(re),Math.floor(r(ee,re))),o.every=a=>(a=Math.floor(a),!isFinite(a)||!(a>0)?null:a>1?o.filter(n?i=>n(i)%a===0:i=>o.count(0,i)%a===0):o)),o}var z=F(t=>t.setHours(0,0,0,0),(t,e)=>t.setDate(t.getDate()+e),(t,e)=>(e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*6e4)/864e5,t=>t.getDate()-1),ho=z.range,gt=F(t=>{t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCDate(t.getUTCDate()+e)},(t,e)=>(e-t)/864e5,t=>t.getUTCDate()-1),yo=gt.range,pr=F(t=>{t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCDate(t.getUTCDate()+e)},(t,e)=>(e-t)/864e5,t=>Math.floor(t/864e5)),go=pr.range;function rt(t){return F(e=>{e.setDate(e.getDate()-(e.getDay()+7-t)%7),e.setHours(0,0,0,0)},(e,r)=>{e.setDate(e.getDate()+r*7)},(e,r)=>(r-e-(r.getTimezoneOffset()-e.getTimezoneOffset())*6e4)/6048e5)}var nt=rt(0),X=rt(1),hr=rt(2),dr=rt(3),G=rt(4),yr=rt(5),gr=rt(6),xr=nt.range,vo=X.range,Mo=hr.range,wo=dr.range,_o=G.range,So=yr.range,To=gr.range;function ot(t){return F(e=>{e.setUTCDate(e.getUTCDate()-(e.getUTCDay()+7-t)%7),e.setUTCHours(0,0,0,0)},(e,r)=>{e.setUTCDate(e.getUTCDate()+r*7)},(e,r)=>(r-e)/6048e5)}var xt=ot(0),lt=ot(1),vr=ot(2),Mr=ot(3),J=ot(4),wr=ot(5),_r=ot(6),Sr=xt.range,Do=lt.range,Co=vr.range,bo=Mr.range,Uo=J.range,ko=wr.range,Ao=_r.range;var vt=F(t=>{t.setDate(1),t.setHours(0,0,0,0)},(t,e)=>{t.setMonth(t.getMonth()+e)},(t,e)=>e.getMonth()-t.getMonth()+(e.getFullYear()-t.getFullYear())*12,t=>t.getMonth()),Fo=vt.range,Tr=F(t=>{t.setUTCDate(1),t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCMonth(t.getUTCMonth()+e)},(t,e)=>e.getUTCMonth()-t.getUTCMonth()+(e.getUTCFullYear()-t.getUTCFullYear())*12,t=>t.getUTCMonth()),No=Tr.range;var $=F(t=>{t.setMonth(0,1),t.setHours(0,0,0,0)},(t,e)=>{t.setFullYear(t.getFullYear()+e)},(t,e)=>e.getFullYear()-t.getFullYear(),t=>t.getFullYear());$.every=t=>!isFinite(t=Math.floor(t))||!(t>0)?null:F(e=>{e.setFullYear(Math.floor(e.getFullYear()/t)*t),e.setMonth(0,1),e.setHours(0,0,0,0)},(e,r)=>{e.setFullYear(e.getFullYear()+r*t)});var Lo=$.range,V=F(t=>{t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCFullYear(t.getUTCFullYear()+e)},(t,e)=>e.getUTCFullYear()-t.getUTCFullYear(),t=>t.getUTCFullYear());V.every=t=>!isFinite(t=Math.floor(t))||!(t>0)?null:F(e=>{e.setUTCFullYear(Math.floor(e.getUTCFullYear()/t)*t),e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)},(e,r)=>{e.setUTCFullYear(e.getUTCFullYear()+r*t)});var Ho=V.range;function oe(t){if(0<=t.y&&t.y<100){var e=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return e.setFullYear(t.y),e}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function ae(t){if(0<=t.y&&t.y<100){var e=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return e.setUTCFullYear(t.y),e}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function Mt(t,e,r){return{y:t,m:e,d:r,H:0,M:0,S:0,L:0}}function ie(t){var e=t.dateTime,r=t.date,n=t.time,o=t.periods,a=t.days,i=t.shortDays,u=t.months,s=t.shortMonths,l=wt(o),c=_t(o),p=wt(a),g=_t(a),x=wt(i),w=_t(i),_=wt(u),W=_t(u),P=wt(s),H=_t(s),A={a:Z,A:R,b:st,B:O,c:null,d:Ar,e:Ar,f:na,g:pa,G:da,H:ta,I:ea,j:ra,L:Yr,m:oa,M:aa,p:Zr,q:Qr,Q:Lr,s:Hr,S:ia,u:ua,U:sa,V:la,w:fa,W:ca,x:null,X:null,y:ma,Y:ha,Z:ya,"%":Nr},T={a:Xr,A:Gr,b:Jr,B:Kr,c:null,d:Fr,e:Fr,f:Ma,g:Aa,G:Na,H:ga,I:xa,j:va,L:Wr,m:wa,M:_a,p:jr,q:tn,Q:Lr,s:Hr,S:Sa,u:Ta,U:Da,V:Ca,w:ba,W:Ua,x:null,X:null,y:ka,Y:Fa,Z:La,"%":Nr},N={a:it,A:K,b:d,B:U,c:h,d:Ur,e:Ur,f:Go,g:br,G:Cr,H:kr,I:kr,j:Bo,L:Xo,m:Vo,M:Zo,p:B,q:$o,Q:Ko,s:jo,S:Qo,u:Io,U:Oo,V:qo,w:Po,W:Ro,x:C,X:ut,y:br,Y:Cr,Z:zo,"%":Jo};A.x=D(r,A),A.X=D(n,A),A.c=D(e,A),T.x=D(r,T),T.X=D(n,T),T.c=D(e,T);function D(m,y){return function(v){var f=[],L=-1,S=0,Y=m.length,E,j,me;for(v instanceof Date||(v=new Date(+v));++L53)return null;"w"in f||(f.w=1),"Z"in f?(S=ae(Mt(f.y,0,1)),Y=S.getUTCDay(),S=Y>4||Y===0?lt.ceil(S):lt(S),S=gt.offset(S,(f.V-1)*7),f.y=S.getUTCFullYear(),f.m=S.getUTCMonth(),f.d=S.getUTCDate()+(f.w+6)%7):(S=oe(Mt(f.y,0,1)),Y=S.getDay(),S=Y>4||Y===0?X.ceil(S):X(S),S=z.offset(S,(f.V-1)*7),f.y=S.getFullYear(),f.m=S.getMonth(),f.d=S.getDate()+(f.w+6)%7)}else("W"in f||"U"in f)&&("w"in f||(f.w="u"in f?f.u%7:"W"in f?1:0),Y="Z"in f?ae(Mt(f.y,0,1)).getUTCDay():oe(Mt(f.y,0,1)).getDay(),f.m=0,f.d="W"in f?(f.w+6)%7+f.W*7-(Y+5)%7:f.w+f.U*7-(Y+6)%7);return"Z"in f?(f.H+=f.Z/100|0,f.M+=f.Z%100,ae(f)):oe(f)}}function I(m,y,v,f){for(var L=0,S=y.length,Y=v.length,E,j;L=Y)return-1;if(E=y.charCodeAt(L++),E===37){if(E=y.charAt(L++),j=N[E in Dr?y.charAt(L++):E],!j||(f=j(m,v,f))<0)return-1}else if(E!=v.charCodeAt(f++))return-1}return f}function B(m,y,v){var f=l.exec(y.slice(v));return f?(m.p=c.get(f[0].toLowerCase()),v+f[0].length):-1}function it(m,y,v){var f=x.exec(y.slice(v));return f?(m.w=w.get(f[0].toLowerCase()),v+f[0].length):-1}function K(m,y,v){var f=p.exec(y.slice(v));return f?(m.w=g.get(f[0].toLowerCase()),v+f[0].length):-1}function d(m,y,v){var f=P.exec(y.slice(v));return f?(m.m=H.get(f[0].toLowerCase()),v+f[0].length):-1}function U(m,y,v){var f=_.exec(y.slice(v));return f?(m.m=W.get(f[0].toLowerCase()),v+f[0].length):-1}function h(m,y,v){return I(m,e,y,v)}function C(m,y,v){return I(m,r,y,v)}function ut(m,y,v){return I(m,n,y,v)}function Z(m){return i[m.getDay()]}function R(m){return a[m.getDay()]}function st(m){return s[m.getMonth()]}function O(m){return u[m.getMonth()]}function Zr(m){return o[+(m.getHours()>=12)]}function Qr(m){return 1+~~(m.getMonth()/3)}function Xr(m){return i[m.getUTCDay()]}function Gr(m){return a[m.getUTCDay()]}function Jr(m){return s[m.getUTCMonth()]}function Kr(m){return u[m.getUTCMonth()]}function jr(m){return o[+(m.getUTCHours()>=12)]}function tn(m){return 1+~~(m.getUTCMonth()/3)}return{format:function(m){var y=D(m+="",A);return y.toString=function(){return m},y},parse:function(m){var y=at(m+="",!1);return y.toString=function(){return m},y},utcFormat:function(m){var y=D(m+="",T);return y.toString=function(){return m},y},utcParse:function(m){var y=at(m+="",!0);return y.toString=function(){return m},y}}}var Dr={"-":"",_:" ",0:"0"},k=/^\s*\d+/,Yo=/^%/,Eo=/[\\^$*+?|[\]().{}]/g;function M(t,e,r){var n=t<0?"-":"",o=(n?-t:t)+"",a=o.length;return n+(a[e.toLowerCase(),r]))}function Po(t,e,r){var n=k.exec(e.slice(r,r+1));return n?(t.w=+n[0],r+n[0].length):-1}function Io(t,e,r){var n=k.exec(e.slice(r,r+1));return n?(t.u=+n[0],r+n[0].length):-1}function Oo(t,e,r){var n=k.exec(e.slice(r,r+2));return n?(t.U=+n[0],r+n[0].length):-1}function qo(t,e,r){var n=k.exec(e.slice(r,r+2));return n?(t.V=+n[0],r+n[0].length):-1}function Ro(t,e,r){var n=k.exec(e.slice(r,r+2));return n?(t.W=+n[0],r+n[0].length):-1}function Cr(t,e,r){var n=k.exec(e.slice(r,r+4));return n?(t.y=+n[0],r+n[0].length):-1}function br(t,e,r){var n=k.exec(e.slice(r,r+2));return n?(t.y=+n[0]+(+n[0]>68?1900:2e3),r+n[0].length):-1}function zo(t,e,r){var n=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(e.slice(r,r+6));return n?(t.Z=n[1]?0:-(n[2]+(n[3]||"00")),r+n[0].length):-1}function $o(t,e,r){var n=k.exec(e.slice(r,r+1));return n?(t.q=n[0]*3-3,r+n[0].length):-1}function Vo(t,e,r){var n=k.exec(e.slice(r,r+2));return n?(t.m=n[0]-1,r+n[0].length):-1}function Ur(t,e,r){var n=k.exec(e.slice(r,r+2));return n?(t.d=+n[0],r+n[0].length):-1}function Bo(t,e,r){var n=k.exec(e.slice(r,r+3));return n?(t.m=0,t.d=+n[0],r+n[0].length):-1}function kr(t,e,r){var n=k.exec(e.slice(r,r+2));return n?(t.H=+n[0],r+n[0].length):-1}function Zo(t,e,r){var n=k.exec(e.slice(r,r+2));return n?(t.M=+n[0],r+n[0].length):-1}function Qo(t,e,r){var n=k.exec(e.slice(r,r+2));return n?(t.S=+n[0],r+n[0].length):-1}function Xo(t,e,r){var n=k.exec(e.slice(r,r+3));return n?(t.L=+n[0],r+n[0].length):-1}function Go(t,e,r){var n=k.exec(e.slice(r,r+6));return n?(t.L=Math.floor(n[0]/1e3),r+n[0].length):-1}function Jo(t,e,r){var n=Yo.exec(e.slice(r,r+1));return n?r+n[0].length:-1}function Ko(t,e,r){var n=k.exec(e.slice(r));return n?(t.Q=+n[0],r+n[0].length):-1}function jo(t,e,r){var n=k.exec(e.slice(r));return n?(t.s=+n[0],r+n[0].length):-1}function Ar(t,e){return M(t.getDate(),e,2)}function ta(t,e){return M(t.getHours(),e,2)}function ea(t,e){return M(t.getHours()%12||12,e,2)}function ra(t,e){return M(1+z.count($(t),t),e,3)}function Yr(t,e){return M(t.getMilliseconds(),e,3)}function na(t,e){return Yr(t,e)+"000"}function oa(t,e){return M(t.getMonth()+1,e,2)}function aa(t,e){return M(t.getMinutes(),e,2)}function ia(t,e){return M(t.getSeconds(),e,2)}function ua(t){var e=t.getDay();return e===0?7:e}function sa(t,e){return M(nt.count($(t)-1,t),e,2)}function Er(t){var e=t.getDay();return e>=4||e===0?G(t):G.ceil(t)}function la(t,e){return t=Er(t),M(G.count($(t),t)+($(t).getDay()===4),e,2)}function fa(t){return t.getDay()}function ca(t,e){return M(X.count($(t)-1,t),e,2)}function ma(t,e){return M(t.getFullYear()%100,e,2)}function pa(t,e){return t=Er(t),M(t.getFullYear()%100,e,2)}function ha(t,e){return M(t.getFullYear()%1e4,e,4)}function da(t,e){var r=t.getDay();return t=r>=4||r===0?G(t):G.ceil(t),M(t.getFullYear()%1e4,e,4)}function ya(t){var e=t.getTimezoneOffset();return(e>0?"-":(e*=-1,"+"))+M(e/60|0,"0",2)+M(e%60,"0",2)}function Fr(t,e){return M(t.getUTCDate(),e,2)}function ga(t,e){return M(t.getUTCHours(),e,2)}function xa(t,e){return M(t.getUTCHours()%12||12,e,2)}function va(t,e){return M(1+gt.count(V(t),t),e,3)}function Wr(t,e){return M(t.getUTCMilliseconds(),e,3)}function Ma(t,e){return Wr(t,e)+"000"}function wa(t,e){return M(t.getUTCMonth()+1,e,2)}function _a(t,e){return M(t.getUTCMinutes(),e,2)}function Sa(t,e){return M(t.getUTCSeconds(),e,2)}function Ta(t){var e=t.getUTCDay();return e===0?7:e}function Da(t,e){return M(xt.count(V(t)-1,t),e,2)}function Pr(t){var e=t.getUTCDay();return e>=4||e===0?J(t):J.ceil(t)}function Ca(t,e){return t=Pr(t),M(J.count(V(t),t)+(V(t).getUTCDay()===4),e,2)}function ba(t){return t.getUTCDay()}function Ua(t,e){return M(lt.count(V(t)-1,t),e,2)}function ka(t,e){return M(t.getUTCFullYear()%100,e,2)}function Aa(t,e){return t=Pr(t),M(t.getUTCFullYear()%100,e,2)}function Fa(t,e){return M(t.getUTCFullYear()%1e4,e,4)}function Na(t,e){var r=t.getUTCDay();return t=r>=4||r===0?J(t):J.ceil(t),M(t.getUTCFullYear()%1e4,e,4)}function La(){return"+0000"}function Nr(){return"%"}function Lr(t){return+t}function Hr(t){return Math.floor(+t/1e3)}var ft,ct,Ir,Or,qr;ue({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});function ue(t){return ft=ie(t),ct=ft.format,Ir=ft.parse,Or=ft.utcFormat,qr=ft.utcParse,ft}var le={cellSize:13,cellGap:2,marginTop:20,marginLeft:30,marginBottom:4},se=["#9be9a8","#40c463","#30a14e","#216e39"],Ha=["Mon","","Wed","","Fri","",""],Ya=["Sun","","Tue","","Thu","","Sat"];function Ea(t){return t==="sunday"?Ya:Ha}var Wa=ct("%b"),fe=ct("%Y-%m-%d"),zr=ct("%a, %b %-d, %Y");function Pa(t){try{return getComputedStyle(t).getPropertyValue("--tblr-bg-surface"),se}catch{return se}}function Ia(t){let e=new Map;for(let r of t)e.set(r.date,r);return e}function $r(t){return t==="sunday"?nt:X}function Oa(t,e,r,n="monday"){let o=$r(n),a=o.floor(t),i=[],u=new Date(t);for(;u<=e;){let s=fe(u),l=o.count(a,u),c=u.getDay(),p=n==="sunday"?c:(c+6)%7,g=c===0||c===6;i.push({date:new Date(u),dateStr:s,entry:r.get(s)||null,week:l,day:p,isWeekend:g}),u=z.offset(u,1)}return i}function qa(){let t=document.createElement("div");return t.className="heatmap-tooltip",t.style.display="none",t}function Vr(t){if(t.length===0)return 0;let e=new Set(t.filter(a=>a.hours>0).map(a=>a.date));if(e.size===0)return 0;let r=new Date;r.setHours(0,0,0,0);let n=new Date(r);e.has(fe(n))||(n=z.offset(n,-1));let o=0;for(;e.has(fe(n));)o++,n=z.offset(n,-1);return o}function Br(t){let e=t.filter(a=>a.hours>0);if(e.length===0)return{totalHours:0,avgHours:0,busiestDay:null};let r=Math.round(e.reduce((a,i)=>a+i.hours,0)*10)/10,n=Math.round(r/e.length*10)/10,o=e.reduce((a,i)=>i.hours>a.hours?i:a);return{totalHours:r,avgHours:n,busiestDay:{date:o.date,hours:o.hours}}}function Rr(t,e){let r=t.querySelector(".heatmap-stats");r&&r.remove();let n=Vr(e),o=Br(e),a=document.createElement("div");a.className="heatmap-stats";let i=[];if(i.push(`\u{1F525} ${n} days`),i.push(`Total: ${o.totalHours}h`),i.push(`Avg: ${o.avgHours}h/day`),o.busiestDay){let u=new Date(o.busiestDay.date+"T00:00:00"),s=zr(u);i.push(`Busiest: ${s} \u2014 ${o.busiestDay.hours.toFixed(1)}h`)}a.innerHTML=i.join(""),t.appendChild(a)}function ce(t,e,r=le,n,o,a="monday"){if(t.innerHTML="",!e.days||e.days.length===0){let h=document.createElement("div");h.textContent=o||"No tracking data available",h.style.padding="1rem",h.style.color="var(--tblr-secondary, #6c757d)",t.appendChild(h);return}let i=Ia(e.days),u=new Date(e.range.begin),s=new Date(e.range.end),l=Oa(u,s,i,a),c=ht(e.days,h=>h.hours)||1,p=Pa(t),g=yt().domain([0,c]).range(p),{cellGap:x,marginTop:w,marginLeft:_,marginBottom:W}=r,P=(ht(l,h=>h.week)??0)+1,H=t.clientWidth||800,T=Math.min(18,Math.max(2,Math.floor((H-_)/P)-x)),N=T+x,D=_+P*N,at=w+7*N+W,I=document.createElement("div");I.style.maxWidth=`${D}px`,I.style.margin="0 auto",t.appendChild(I);let B=Rt(I).append("svg").attr("width",D).attr("height",at).attr("class","heatmap-svg"),it=$r(a),K=[],d=it.floor(u);vt.range(vt.ceil(u),s).forEach(h=>{K.push({date:h,week:it.count(d,h)})}),B.selectAll(".month-label").data(K).join("text").attr("class","heatmap-label month-label").attr("x",h=>_+h.week*N).attr("y",w-6).text(h=>Wa(h.date)),B.selectAll(".day-label").data(Ea(a)).join("text").attr("class","heatmap-label day-label").attr("x",_-6).attr("y",(h,C)=>w+C*N+T-2).attr("text-anchor","end").text(h=>h),document.querySelectorAll(".heatmap-tooltip").forEach(h=>h.remove());let U=qa();U.style.position="fixed",document.body.appendChild(U),B.selectAll(".heatmap-cell").data(l).join("rect").attr("class",h=>{let C="heatmap-cell";return h.entry||(C+=" heatmap-empty"),h.isWeekend&&(C+=" heatmap-weekend"),C}).attr("x",h=>_+h.week*N).attr("y",h=>w+h.day*N).attr("width",T).attr("height",T).attr("fill",h=>h.entry?g(h.entry.hours):"").on("mouseenter",function(h,C){let ut=C.entry?C.entry.hours.toFixed(1):"0.0",Z=C.entry?C.entry.count:0;U.innerHTML=`${zr(C.date)}
${ut}h (${Z} entries)`,U.style.display="block";let R=h.target.getBoundingClientRect();U.style.left=`${R.left+T/2}px`,U.style.top=`${R.top-U.offsetHeight-8}px`}).on("mouseleave",function(){U.style.display="none"}).on("click",function(h,C){n&&n(C.dateStr)})}function Ra(t){let e=t.getAttribute("data-url");if(!e){console.error("KimaiHeatmap: missing data-url attribute");return}let r=t.getAttribute("data-timesheet-url")||"/en/timesheet/",n=t.getAttribute("data-week-start")||"monday",o=t.getAttribute("data-projects"),a=o?JSON.parse(o):[],i=null,u=c=>{let p=`${c} - ${c}`,g=`${r}?daterange=${encodeURIComponent(p)}`;i&&(g+=`&projects[]=${i}`),window.location.href=g};t.innerHTML="";let s=document.createElement("div");s.className="heatmap-wrapper";let l=document.createElement("div");if(l.className="heatmap-svg-area",s.appendChild(l),a.length>0){let c=document.createElement("div");c.className="heatmap-filter";let p=document.createElement("select");p.className="form-select form-select-sm",p.setAttribute("aria-label","Filter by project");let g=document.createElement("option");g.value="",g.textContent="All Projects",p.appendChild(g);for(let x of a){let w=document.createElement("option");w.value=String(x.id),w.textContent=x.name,p.appendChild(w)}p.addEventListener("change",()=>{let x=p.value;i=x?parseInt(x,10):null;let w=x?`${e}?project=${x}`:e;fetch(w).then(_=>{if(!_.ok)throw new Error(`HTTP ${_.status}`);return _.json()}).then(_=>{ce(l,_,le,u,"No tracking data for this project",n),Rr(s,_.days)}).catch(_=>{console.error("KimaiHeatmap: failed to load filtered data",_)})}),c.appendChild(p),s.appendChild(c)}t.appendChild(s),fetch(e).then(c=>{if(!c.ok)throw new Error(`HTTP ${c.status}`);return c.json()}).then(c=>{ce(l,c,le,u,void 0,n),Rr(s,c.days)}).catch(c=>{console.error("KimaiHeatmap: failed to load data",c),l.textContent="Failed to load heatmap data"})}return un(za);})(); +"use strict";var KimaiHeatmap=(()=>{var Wt=Object.defineProperty;var tn=Object.getOwnPropertyDescriptor;var en=Object.getOwnPropertyNames;var rn=Object.prototype.hasOwnProperty;var nn=(t,e)=>{for(var r in e)Wt(t,r,{get:e[r],enumerable:!0})},on=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of en(e))!rn.call(t,o)&&o!==r&&Wt(t,o,{get:()=>e[o],enumerable:!(n=tn(e,o))||n.enumerable});return t};var an=t=>on(Wt({},"__esModule",{value:!0}),t);var za={};nn(za,{calculateStats:()=>$r,calculateStreak:()=>zr,init:()=>Ra,renderHeatmap:()=>Vr});var St="http://www.w3.org/1999/xhtml",Pt={svg:"http://www.w3.org/2000/svg",xhtml:St,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function Tt(t){var e=t+="",r=e.indexOf(":");return r>=0&&(e=t.slice(0,r))!=="xmlns"&&(t=t.slice(r+1)),Pt.hasOwnProperty(e)?{space:Pt[e],local:t}:t}function un(t){return function(){var e=this.ownerDocument,r=this.namespaceURI;return r===St&&e.documentElement.namespaceURI===St?e.createElement(t):e.createElementNS(r,t)}}function sn(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function Dt(t){var e=Tt(t);return(e.local?sn:un)(e)}function ln(){}function Ct(t){return t==null?ln:function(){return this.querySelector(t)}}function ce(t){typeof t!="function"&&(t=Ct(t));for(var e=this._groups,r=e.length,n=new Array(r),o=0;o=N&&(N=b+1);!(E=U[N])&&++N=0;)(i=n[o])&&(a&&i.compareDocumentPosition(a)^4&&a.parentNode.insertBefore(i,a),a=i);return this}function De(t){t||(t=_n);function e(p,w){return p&&w?t(p.__data__,w.__data__):!p-!w}for(var r=this._groups,n=r.length,o=new Array(n),a=0;ae?1:t>=e?0:NaN}function Ce(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this}function be(){return Array.from(this)}function Ue(){for(var t=this._groups,e=0,r=t.length;e1?this.each((e==null?kn:typeof e=="function"?Fn:An)(t,e,r??"")):Ln(this.node(),t)}function Ln(t,e){return t.style.getPropertyValue(e)||kt(t).getComputedStyle(t,null).getPropertyValue(e)}function Nn(t){return function(){delete this[t]}}function Hn(t,e){return function(){this[t]=e}}function Yn(t,e){return function(){var r=e.apply(this,arguments);r==null?delete this[t]:this[t]=r}}function He(t,e){return arguments.length>1?this.each((e==null?Nn:typeof e=="function"?Yn:Hn)(t,e)):this.node()[t]}function Ye(t){return t.trim().split(/^|\s+/)}function Ot(t){return t.classList||new Ee(t)}function Ee(t){this._node=t,this._names=Ye(t.getAttribute("class")||"")}Ee.prototype={add:function(t){var e=this._names.indexOf(t);e<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};function We(t,e){for(var r=Ot(t),n=-1,o=e.length;++n=0&&(r=e.slice(n+1),e=e.slice(0,n)),{type:e,name:r}})}function jn(t){return function(){var e=this.__on;if(e){for(var r=0,n=-1,o=e.length,a;re?1:t>=e?0:NaN}function zt(t,e){return t==null||e==null?NaN:et?1:e>=t?0:NaN}function At(t){let e,r,n;t.length!==2?(e=ot,r=(u,l)=>ot(t(u),l),n=(u,l)=>t(u)-l):(e=t===ot||t===zt?t:ao,r=t,n=t);function o(u,l,s=0,m=u.length){if(s>>1;r(u[p],l)<0?s=p+1:m=p}while(s>>1;r(u[p],l)<=0?s=p+1:m=p}while(ss&&n(u[p-1],l)>-n(u[p],l)?p-1:p}return{left:o,center:i,right:a}}function ao(){return 0}function $t(t){return t===null?NaN:+t}var je=At(ot),tr=je.right,io=je.left,uo=At($t).center,Vt=tr;var so=Math.sqrt(50),lo=Math.sqrt(10),fo=Math.sqrt(2);function Ft(t,e,r){let n=(e-t)/Math.max(0,r),o=Math.floor(Math.log10(n)),a=n/Math.pow(10,o),i=a>=so?10:a>=lo?5:a>=fo?2:1,u,l,s;return o<0?(s=Math.pow(10,-o)/i,u=Math.round(t*s),l=Math.round(e*s),u/se&&--l,s=-s):(s=Math.pow(10,o)*i,u=Math.round(t/s),l=Math.round(e/s),u*se&&--l),l0))return[];if(t===e)return[t];let n=e=o))return[];let u=a-o+1,l=new Array(u);if(n)if(i<0)for(let s=0;s=n)&&(r=n);else{let n=-1;for(let o of t)(o=e(o,++n,t))!=null&&(r=o)&&(r=o)}return r}function er(t,e){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(e).domain(t);break}return this}function rr(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)}function at(t,e){if(!isFinite(t)||t===0)return null;var r=(t=e?t.toExponential(e-1):t.toExponential()).indexOf("e"),n=t.slice(0,r);return[n.length>1?n[0]+n.slice(2):n,+t.slice(r+1)]}function q(t){return t=at(Math.abs(t)),t?t[1]:NaN}function nr(t,e){return function(r,n){for(var o=r.length,a=[],i=0,u=t[0],l=0;o>0&&u>0&&(l+u+1>n&&(u=Math.max(1,n-l)),a.push(r.substring(o-=u,o+u)),!((l+=u+1)>n));)u=t[i=(i+1)%t.length];return a.reverse().join(e)}}function or(t){return function(e){return e.replace(/[0-9]/g,function(r){return t[+r]})}}var co=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function G(t){if(!(e=co.exec(t)))throw new Error("invalid format: "+t);var e;return new Nt({fill:e[1],align:e[2],sign:e[3],symbol:e[4],zero:e[5],width:e[6],comma:e[7],precision:e[8]&&e[8].slice(1),trim:e[9],type:e[10]})}G.prototype=Nt.prototype;function Nt(t){this.fill=t.fill===void 0?" ":t.fill+"",this.align=t.align===void 0?">":t.align+"",this.sign=t.sign===void 0?"-":t.sign+"",this.symbol=t.symbol===void 0?"":t.symbol+"",this.zero=!!t.zero,this.width=t.width===void 0?void 0:+t.width,this.comma=!!t.comma,this.precision=t.precision===void 0?void 0:+t.precision,this.trim=!!t.trim,this.type=t.type===void 0?"":t.type+""}Nt.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(this.width===void 0?"":Math.max(1,this.width|0))+(this.comma?",":"")+(this.precision===void 0?"":"."+Math.max(0,this.precision|0))+(this.trim?"~":"")+this.type};function ar(t){t:for(var e=t.length,r=1,n=-1,o;r0&&(n=0);break}return n>0?t.slice(0,n)+t.slice(o+1):t}var dt;function ir(t,e){var r=at(t,e);if(!r)return dt=void 0,t.toPrecision(e);var n=r[0],o=r[1],a=o-(dt=Math.max(-8,Math.min(8,Math.floor(o/3)))*3)+1,i=n.length;return a===i?n:a>i?n+new Array(a-i+1).join("0"):a>0?n.slice(0,a)+"."+n.slice(a):"0."+new Array(1-a).join("0")+at(t,Math.max(0,e+a-1))[0]}function Zt(t,e){var r=at(t,e);if(!r)return t+"";var n=r[0],o=r[1];return o<0?"0."+new Array(-o).join("0")+n:n.length>o+1?n.slice(0,o+1)+"."+n.slice(o+1):n+new Array(o-n.length+2).join("0")}var Qt={"%":(t,e)=>(t*100).toFixed(e),b:t=>Math.round(t).toString(2),c:t=>t+"",d:rr,e:(t,e)=>t.toExponential(e),f:(t,e)=>t.toFixed(e),g:(t,e)=>t.toPrecision(e),o:t=>Math.round(t).toString(8),p:(t,e)=>Zt(t*100,e),r:Zt,s:ir,X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function Xt(t){return t}var ur=Array.prototype.map,sr=["y","z","a","f","p","n","\xB5","m","","k","M","G","T","P","E","Z","Y"];function lr(t){var e=t.grouping===void 0||t.thousands===void 0?Xt:nr(ur.call(t.grouping,Number),t.thousands+""),r=t.currency===void 0?"":t.currency[0]+"",n=t.currency===void 0?"":t.currency[1]+"",o=t.decimal===void 0?".":t.decimal+"",a=t.numerals===void 0?Xt:or(ur.call(t.numerals,String)),i=t.percent===void 0?"%":t.percent+"",u=t.minus===void 0?"\u2212":t.minus+"",l=t.nan===void 0?"NaN":t.nan+"";function s(p,w){p=G(p);var h=p.fill,x=p.align,S=p.sign,U=p.symbol,L=p.zero,b=p.width,N=p.comma,k=p.precision,E=p.trim,_=p.type;_==="n"?(N=!0,_="g"):Qt[_]||(k===void 0&&(k=12),E=!0,_="g"),(L||h==="0"&&x==="=")&&(L=!0,h="0",x="=");var tt=(w&&w.prefix!==void 0?w.prefix:"")+(U==="$"?r:U==="#"&&/[boxX]/.test(_)?"0"+_.toLowerCase():""),Z=(U==="$"?n:/[%p]/.test(_)?i:"")+(w&&w.suffix!==void 0?w.suffix:""),R=Qt[_],et=/[defgprs%]/.test(_);k=k===void 0?6:/[gprs]/.test(_)?Math.max(1,Math.min(21,k)):Math.max(0,Math.min(20,k));function rt(y){var I=tt,D=Z,d,A,Q;if(_==="c")D=R(y)+D,y="";else{y=+y;var X=y<0||1/y<0;if(y=isNaN(y)?l:R(Math.abs(y),k),E&&(y=ar(y)),X&&+y==0&&S!=="+"&&(X=!1),I=(X?S==="("?S:u:S==="-"||S==="("?"":S)+I,D=(_==="s"&&!isNaN(y)&&dt!==void 0?sr[8+dt/3]:"")+D+(X&&S==="("?")":""),et){for(d=-1,A=y.length;++dQ||Q>57){D=(Q===46?o+y.slice(d+1):y.slice(d))+D,y=y.slice(0,d);break}}}N&&!L&&(y=e(y,1/0));var z=I.length+y.length+D.length,O=z>1)+I+y+D+O.slice(z);break;default:y=O+I+y+D;break}return a(y)}return rt.toString=function(){return p+""},rt}function m(p,w){var h=Math.max(-8,Math.min(8,Math.floor(q(w)/3)))*3,x=Math.pow(10,-h),S=s((p=G(p),p.type="f",p),{suffix:sr[8+h/3]});return function(U){return S(x*U)}}return{format:s,formatPrefix:m}}var Ht,Yt,Et;Gt({thousands:",",grouping:[3],currency:["$",""]});function Gt(t){return Ht=lr(t),Yt=Ht.format,Et=Ht.formatPrefix,Ht}function Jt(t){return Math.max(0,-q(Math.abs(t)))}function Kt(t,e){return Math.max(0,Math.max(-8,Math.min(8,Math.floor(q(e)/3)))*3-q(Math.abs(t)))}function jt(t,e){return t=Math.abs(t),e=Math.abs(e)-t,Math.max(0,q(e)-q(t))+1}function te(t,e,r,n){var o=Bt(t,e,r),a;switch(n=G(n??",f"),n.type){case"s":{var i=Math.max(Math.abs(t),Math.abs(e));return n.precision==null&&!isNaN(a=Kt(o,i))&&(n.precision=a),Et(n,i)}case"":case"e":case"g":case"p":case"r":{n.precision==null&&!isNaN(a=jt(o,Math.max(Math.abs(t),Math.abs(e))))&&(n.precision=a-(n.type==="e"));break}case"f":case"%":{n.precision==null&&!isNaN(a=Jt(o))&&(n.precision=a-(n.type==="%")*2);break}}return Yt(n)}function fr(t){var e=t.domain;return t.ticks=function(r){var n=e();return Lt(n[0],n[n.length-1],r??10)},t.tickFormat=function(r,n){var o=e();return te(o[0],o[o.length-1],r??10,n)},t.nice=function(r){r==null&&(r=10);var n=e(),o=0,a=n.length-1,i=n[o],u=n[a],l,s,m=10;for(u0;){if(s=pt(i,u,r),s===l)return n[o]=i,n[a]=u,e(n);if(s>0)i=Math.floor(i/s)*s,u=Math.ceil(u/s)*s;else if(s<0)i=Math.ceil(i*s)/s,u=Math.floor(u*s)/s;else break;l=s}return t},t}function yt(){var t=0,e=1,r=1,n=[.5],o=[0,1],a;function i(l){return l!=null&&l<=l?o[Vt(n,l,0,r)]:a}function u(){var l=-1;for(n=new Array(r);++l=r?[n[r-1],e]:[n[s-1],n[s]]},i.unknown=function(l){return arguments.length&&(a=l),i},i.thresholds=function(){return n.slice()},i.copy=function(){return yt().domain([t,e]).range(o).unknown(a)},er.apply(fr(i),arguments)}var ee=new Date,re=new Date;function H(t,e,r,n){function o(a){return t(a=arguments.length===0?new Date:new Date(+a)),a}return o.floor=a=>(t(a=new Date(+a)),a),o.ceil=a=>(t(a=new Date(a-1)),e(a,1),t(a),a),o.round=a=>{let i=o(a),u=o.ceil(a);return a-i(e(a=new Date(+a),i==null?1:Math.floor(i)),a),o.range=(a,i,u)=>{let l=[];if(a=o.ceil(a),u=u==null?1:Math.floor(u),!(a0))return l;let s;do l.push(s=new Date(+a)),e(a,u),t(a);while(sH(i=>{if(i>=i)for(;t(i),!a(i);)i.setTime(i-1)},(i,u)=>{if(i>=i)if(u<0)for(;++u<=0;)for(;e(i,-1),!a(i););else for(;--u>=0;)for(;e(i,1),!a(i););}),r&&(o.count=(a,i)=>(ee.setTime(+a),re.setTime(+i),t(ee),t(re),Math.floor(r(ee,re))),o.every=a=>(a=Math.floor(a),!isFinite(a)||!(a>0)?null:a>1?o.filter(n?i=>n(i)%a===0:i=>o.count(0,i)%a===0):o)),o}var $=H(t=>t.setHours(0,0,0,0),(t,e)=>t.setDate(t.getDate()+e),(t,e)=>(e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*6e4)/864e5,t=>t.getDate()-1),po=$.range,gt=H(t=>{t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCDate(t.getUTCDate()+e)},(t,e)=>(e-t)/864e5,t=>t.getUTCDate()-1),ho=gt.range,cr=H(t=>{t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCDate(t.getUTCDate()+e)},(t,e)=>(e-t)/864e5,t=>Math.floor(t/864e5)),yo=cr.range;function it(t){return H(e=>{e.setDate(e.getDate()-(e.getDay()+7-t)%7),e.setHours(0,0,0,0)},(e,r)=>{e.setDate(e.getDate()+r*7)},(e,r)=>(r-e-(r.getTimezoneOffset()-e.getTimezoneOffset())*6e4)/6048e5)}var ut=it(0),J=it(1),mr=it(2),pr=it(3),K=it(4),hr=it(5),dr=it(6),yr=ut.range,xo=J.range,vo=mr.range,Mo=pr.range,wo=K.range,_o=hr.range,So=dr.range;function st(t){return H(e=>{e.setUTCDate(e.getUTCDate()-(e.getUTCDay()+7-t)%7),e.setUTCHours(0,0,0,0)},(e,r)=>{e.setUTCDate(e.getUTCDate()+r*7)},(e,r)=>(r-e)/6048e5)}var xt=st(0),lt=st(1),gr=st(2),xr=st(3),j=st(4),vr=st(5),Mr=st(6),wr=xt.range,To=lt.range,Do=gr.range,Co=xr.range,bo=j.range,Uo=vr.range,ko=Mr.range;var vt=H(t=>{t.setDate(1),t.setHours(0,0,0,0)},(t,e)=>{t.setMonth(t.getMonth()+e)},(t,e)=>e.getMonth()-t.getMonth()+(e.getFullYear()-t.getFullYear())*12,t=>t.getMonth()),Ao=vt.range,_r=H(t=>{t.setUTCDate(1),t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCMonth(t.getUTCMonth()+e)},(t,e)=>e.getUTCMonth()-t.getUTCMonth()+(e.getUTCFullYear()-t.getUTCFullYear())*12,t=>t.getUTCMonth()),Fo=_r.range;var V=H(t=>{t.setMonth(0,1),t.setHours(0,0,0,0)},(t,e)=>{t.setFullYear(t.getFullYear()+e)},(t,e)=>e.getFullYear()-t.getFullYear(),t=>t.getFullYear());V.every=t=>!isFinite(t=Math.floor(t))||!(t>0)?null:H(e=>{e.setFullYear(Math.floor(e.getFullYear()/t)*t),e.setMonth(0,1),e.setHours(0,0,0,0)},(e,r)=>{e.setFullYear(e.getFullYear()+r*t)});var Lo=V.range,B=H(t=>{t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCFullYear(t.getUTCFullYear()+e)},(t,e)=>e.getUTCFullYear()-t.getUTCFullYear(),t=>t.getUTCFullYear());B.every=t=>!isFinite(t=Math.floor(t))||!(t>0)?null:H(e=>{e.setUTCFullYear(Math.floor(e.getUTCFullYear()/t)*t),e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)},(e,r)=>{e.setUTCFullYear(e.getUTCFullYear()+r*t)});var No=B.range;function oe(t){if(0<=t.y&&t.y<100){var e=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return e.setFullYear(t.y),e}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function ae(t){if(0<=t.y&&t.y<100){var e=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return e.setUTCFullYear(t.y),e}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function Mt(t,e,r){return{y:t,m:e,d:r,H:0,M:0,S:0,L:0}}function ie(t){var e=t.dateTime,r=t.date,n=t.time,o=t.periods,a=t.days,i=t.shortDays,u=t.months,l=t.shortMonths,s=wt(o),m=_t(o),p=wt(a),w=_t(a),h=wt(i),x=_t(i),S=wt(u),U=_t(u),L=wt(l),b=_t(l),N={a:Q,A:X,b:z,B:O,c:null,d:Ur,e:Ur,f:ra,g:ma,G:ha,H:jo,I:ta,j:ea,L:Nr,m:na,M:oa,p:Br,q:Zr,Q:Fr,s:Lr,S:aa,u:ia,U:ua,V:sa,w:la,W:fa,x:null,X:null,y:ca,Y:pa,Z:da,"%":Ar},k={a:Qr,A:Xr,b:Gr,B:Jr,c:null,d:kr,e:kr,f:va,g:ka,G:Fa,H:ya,I:ga,j:xa,L:Yr,m:Ma,M:wa,p:Kr,q:jr,Q:Fr,s:Lr,S:_a,u:Sa,U:Ta,V:Da,w:Ca,W:ba,x:null,X:null,y:Ua,Y:Aa,Z:La,"%":Ar},E={a:et,A:rt,b:y,B:I,c:D,d:Cr,e:Cr,f:Xo,g:Dr,G:Tr,H:br,I:br,j:Vo,L:Qo,m:$o,M:Bo,p:R,q:zo,Q:Jo,s:Ko,S:Zo,u:Po,U:Io,V:Oo,w:Wo,W:qo,x:d,X:A,y:Dr,Y:Tr,Z:Ro,"%":Go};N.x=_(r,N),N.X=_(n,N),N.c=_(e,N),k.x=_(r,k),k.X=_(n,k),k.c=_(e,k);function _(c,g){return function(v){var f=[],Y=-1,T=0,W=c.length,P,nt,fe;for(v instanceof Date||(v=new Date(+v));++Y53)return null;"w"in f||(f.w=1),"Z"in f?(T=ae(Mt(f.y,0,1)),W=T.getUTCDay(),T=W>4||W===0?lt.ceil(T):lt(T),T=gt.offset(T,(f.V-1)*7),f.y=T.getUTCFullYear(),f.m=T.getUTCMonth(),f.d=T.getUTCDate()+(f.w+6)%7):(T=oe(Mt(f.y,0,1)),W=T.getDay(),T=W>4||W===0?J.ceil(T):J(T),T=$.offset(T,(f.V-1)*7),f.y=T.getFullYear(),f.m=T.getMonth(),f.d=T.getDate()+(f.w+6)%7)}else("W"in f||"U"in f)&&("w"in f||(f.w="u"in f?f.u%7:"W"in f?1:0),W="Z"in f?ae(Mt(f.y,0,1)).getUTCDay():oe(Mt(f.y,0,1)).getDay(),f.m=0,f.d="W"in f?(f.w+6)%7+f.W*7-(W+5)%7:f.w+f.U*7-(W+6)%7);return"Z"in f?(f.H+=f.Z/100|0,f.M+=f.Z%100,ae(f)):oe(f)}}function Z(c,g,v,f){for(var Y=0,T=g.length,W=v.length,P,nt;Y=W)return-1;if(P=g.charCodeAt(Y++),P===37){if(P=g.charAt(Y++),nt=E[P in Sr?g.charAt(Y++):P],!nt||(f=nt(c,v,f))<0)return-1}else if(P!=v.charCodeAt(f++))return-1}return f}function R(c,g,v){var f=s.exec(g.slice(v));return f?(c.p=m.get(f[0].toLowerCase()),v+f[0].length):-1}function et(c,g,v){var f=h.exec(g.slice(v));return f?(c.w=x.get(f[0].toLowerCase()),v+f[0].length):-1}function rt(c,g,v){var f=p.exec(g.slice(v));return f?(c.w=w.get(f[0].toLowerCase()),v+f[0].length):-1}function y(c,g,v){var f=L.exec(g.slice(v));return f?(c.m=b.get(f[0].toLowerCase()),v+f[0].length):-1}function I(c,g,v){var f=S.exec(g.slice(v));return f?(c.m=U.get(f[0].toLowerCase()),v+f[0].length):-1}function D(c,g,v){return Z(c,e,g,v)}function d(c,g,v){return Z(c,r,g,v)}function A(c,g,v){return Z(c,n,g,v)}function Q(c){return i[c.getDay()]}function X(c){return a[c.getDay()]}function z(c){return l[c.getMonth()]}function O(c){return u[c.getMonth()]}function Br(c){return o[+(c.getHours()>=12)]}function Zr(c){return 1+~~(c.getMonth()/3)}function Qr(c){return i[c.getUTCDay()]}function Xr(c){return a[c.getUTCDay()]}function Gr(c){return l[c.getUTCMonth()]}function Jr(c){return u[c.getUTCMonth()]}function Kr(c){return o[+(c.getUTCHours()>=12)]}function jr(c){return 1+~~(c.getUTCMonth()/3)}return{format:function(c){var g=_(c+="",N);return g.toString=function(){return c},g},parse:function(c){var g=tt(c+="",!1);return g.toString=function(){return c},g},utcFormat:function(c){var g=_(c+="",k);return g.toString=function(){return c},g},utcParse:function(c){var g=tt(c+="",!0);return g.toString=function(){return c},g}}}var Sr={"-":"",_:" ",0:"0"},F=/^\s*\d+/,Ho=/^%/,Yo=/[\\^$*+?|[\]().{}]/g;function M(t,e,r){var n=t<0?"-":"",o=(n?-t:t)+"",a=o.length;return n+(a[e.toLowerCase(),r]))}function Wo(t,e,r){var n=F.exec(e.slice(r,r+1));return n?(t.w=+n[0],r+n[0].length):-1}function Po(t,e,r){var n=F.exec(e.slice(r,r+1));return n?(t.u=+n[0],r+n[0].length):-1}function Io(t,e,r){var n=F.exec(e.slice(r,r+2));return n?(t.U=+n[0],r+n[0].length):-1}function Oo(t,e,r){var n=F.exec(e.slice(r,r+2));return n?(t.V=+n[0],r+n[0].length):-1}function qo(t,e,r){var n=F.exec(e.slice(r,r+2));return n?(t.W=+n[0],r+n[0].length):-1}function Tr(t,e,r){var n=F.exec(e.slice(r,r+4));return n?(t.y=+n[0],r+n[0].length):-1}function Dr(t,e,r){var n=F.exec(e.slice(r,r+2));return n?(t.y=+n[0]+(+n[0]>68?1900:2e3),r+n[0].length):-1}function Ro(t,e,r){var n=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(e.slice(r,r+6));return n?(t.Z=n[1]?0:-(n[2]+(n[3]||"00")),r+n[0].length):-1}function zo(t,e,r){var n=F.exec(e.slice(r,r+1));return n?(t.q=n[0]*3-3,r+n[0].length):-1}function $o(t,e,r){var n=F.exec(e.slice(r,r+2));return n?(t.m=n[0]-1,r+n[0].length):-1}function Cr(t,e,r){var n=F.exec(e.slice(r,r+2));return n?(t.d=+n[0],r+n[0].length):-1}function Vo(t,e,r){var n=F.exec(e.slice(r,r+3));return n?(t.m=0,t.d=+n[0],r+n[0].length):-1}function br(t,e,r){var n=F.exec(e.slice(r,r+2));return n?(t.H=+n[0],r+n[0].length):-1}function Bo(t,e,r){var n=F.exec(e.slice(r,r+2));return n?(t.M=+n[0],r+n[0].length):-1}function Zo(t,e,r){var n=F.exec(e.slice(r,r+2));return n?(t.S=+n[0],r+n[0].length):-1}function Qo(t,e,r){var n=F.exec(e.slice(r,r+3));return n?(t.L=+n[0],r+n[0].length):-1}function Xo(t,e,r){var n=F.exec(e.slice(r,r+6));return n?(t.L=Math.floor(n[0]/1e3),r+n[0].length):-1}function Go(t,e,r){var n=Ho.exec(e.slice(r,r+1));return n?r+n[0].length:-1}function Jo(t,e,r){var n=F.exec(e.slice(r));return n?(t.Q=+n[0],r+n[0].length):-1}function Ko(t,e,r){var n=F.exec(e.slice(r));return n?(t.s=+n[0],r+n[0].length):-1}function Ur(t,e){return M(t.getDate(),e,2)}function jo(t,e){return M(t.getHours(),e,2)}function ta(t,e){return M(t.getHours()%12||12,e,2)}function ea(t,e){return M(1+$.count(V(t),t),e,3)}function Nr(t,e){return M(t.getMilliseconds(),e,3)}function ra(t,e){return Nr(t,e)+"000"}function na(t,e){return M(t.getMonth()+1,e,2)}function oa(t,e){return M(t.getMinutes(),e,2)}function aa(t,e){return M(t.getSeconds(),e,2)}function ia(t){var e=t.getDay();return e===0?7:e}function ua(t,e){return M(ut.count(V(t)-1,t),e,2)}function Hr(t){var e=t.getDay();return e>=4||e===0?K(t):K.ceil(t)}function sa(t,e){return t=Hr(t),M(K.count(V(t),t)+(V(t).getDay()===4),e,2)}function la(t){return t.getDay()}function fa(t,e){return M(J.count(V(t)-1,t),e,2)}function ca(t,e){return M(t.getFullYear()%100,e,2)}function ma(t,e){return t=Hr(t),M(t.getFullYear()%100,e,2)}function pa(t,e){return M(t.getFullYear()%1e4,e,4)}function ha(t,e){var r=t.getDay();return t=r>=4||r===0?K(t):K.ceil(t),M(t.getFullYear()%1e4,e,4)}function da(t){var e=t.getTimezoneOffset();return(e>0?"-":(e*=-1,"+"))+M(e/60|0,"0",2)+M(e%60,"0",2)}function kr(t,e){return M(t.getUTCDate(),e,2)}function ya(t,e){return M(t.getUTCHours(),e,2)}function ga(t,e){return M(t.getUTCHours()%12||12,e,2)}function xa(t,e){return M(1+gt.count(B(t),t),e,3)}function Yr(t,e){return M(t.getUTCMilliseconds(),e,3)}function va(t,e){return Yr(t,e)+"000"}function Ma(t,e){return M(t.getUTCMonth()+1,e,2)}function wa(t,e){return M(t.getUTCMinutes(),e,2)}function _a(t,e){return M(t.getUTCSeconds(),e,2)}function Sa(t){var e=t.getUTCDay();return e===0?7:e}function Ta(t,e){return M(xt.count(B(t)-1,t),e,2)}function Er(t){var e=t.getUTCDay();return e>=4||e===0?j(t):j.ceil(t)}function Da(t,e){return t=Er(t),M(j.count(B(t),t)+(B(t).getUTCDay()===4),e,2)}function Ca(t){return t.getUTCDay()}function ba(t,e){return M(lt.count(B(t)-1,t),e,2)}function Ua(t,e){return M(t.getUTCFullYear()%100,e,2)}function ka(t,e){return t=Er(t),M(t.getUTCFullYear()%100,e,2)}function Aa(t,e){return M(t.getUTCFullYear()%1e4,e,4)}function Fa(t,e){var r=t.getUTCDay();return t=r>=4||r===0?j(t):j.ceil(t),M(t.getUTCFullYear()%1e4,e,4)}function La(){return"+0000"}function Ar(){return"%"}function Fr(t){return+t}function Lr(t){return Math.floor(+t/1e3)}var ft,ct,Wr,Pr,Ir;ue({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});function ue(t){return ft=ie(t),ct=ft.format,Wr=ft.parse,Pr=ft.utcFormat,Ir=ft.utcParse,ft}var Or={cellSize:13,cellGap:2,marginTop:20,marginLeft:30,marginBottom:4},se=["#9be9a8","#40c463","#30a14e","#216e39"],Na=["Mon","","Wed","","Fri","",""],Ha=["Sun","","Tue","","Thu","","Sat"];function Ya(t){return t==="sunday"?Ha:Na}var Ea=ct("%b"),le=ct("%Y-%m-%d"),qr=ct("%a, %b %-d, %Y");function Wa(t){try{return getComputedStyle(t).getPropertyValue("--tblr-bg-surface"),se}catch{return se}}function Pa(t){let e=new Map;for(let r of t)e.set(r.date,r);return e}function Rr(t){return t==="sunday"?ut:J}function Ia(t,e,r,n="monday"){let o=Rr(n),a=o.floor(t),i=[],u=new Date(t);for(;u<=e;){let l=le(u),s=o.count(a,u),m=u.getDay(),p=n==="sunday"?m:(m+6)%7,w=m===0||m===6;i.push({date:new Date(u),dateStr:l,entry:r.get(l)||null,week:s,day:p,isWeekend:w}),u=$.offset(u,1)}return i}function Oa(){let t=document.createElement("div");return t.className="heatmap-tooltip",t.style.display="none",t}function zr(t){if(t.length===0)return 0;let e=new Set(t.filter(a=>a.hours>0).map(a=>a.date));if(e.size===0)return 0;let r=new Date;r.setHours(0,0,0,0);let n=new Date(r);e.has(le(n))||(n=$.offset(n,-1));let o=0;for(;e.has(le(n));)o++,n=$.offset(n,-1);return o}function $r(t){let e=t.filter(a=>a.hours>0);if(e.length===0)return{totalHours:0,avgHours:0,busiestDay:null};let r=Math.round(e.reduce((a,i)=>a+i.hours,0)*10)/10,n=Math.round(r/e.length*10)/10,o=e.reduce((a,i)=>i.hours>a.hours?i:a);return{totalHours:r,avgHours:n,busiestDay:{date:o.date,hours:o.hours}}}function qa(t,e){let r=t.querySelector(".heatmap-stats");r&&r.remove();let n=zr(e),o=$r(e),a=document.createElement("div");a.className="heatmap-stats";let i=[];if(i.push(`\u{1F525} ${n} days`),i.push(`Total: ${o.totalHours}h`),i.push(`Avg: ${o.avgHours}h/day`),o.busiestDay){let u=new Date(o.busiestDay.date+"T00:00:00"),l=qr(u);i.push(`Busiest: ${l} \u2014 ${o.busiestDay.hours.toFixed(1)}h`)}a.innerHTML=i.join(""),t.appendChild(a)}function Vr(t,e,r=Or,n,o,a="monday"){if(t.innerHTML="",!e.days||e.days.length===0){let d=document.createElement("div");d.textContent=o||"No tracking data available",d.style.padding="1rem",d.style.color="var(--tblr-secondary, #6c757d)",t.appendChild(d);return}let i=Pa(e.days),u=new Date(e.range.begin),l=new Date(e.range.end),s=Ia(u,l,i,a),m=ht(e.days,d=>d.hours)||1,p=Wa(t),w=yt().domain([0,m]).range(p),{cellGap:h,marginTop:x,marginLeft:S,marginBottom:U}=r,L=(ht(s,d=>d.week)??0)+1,b=t.clientWidth||800,E=Math.min(22,Math.max(10,Math.floor((b-S)/L)-h)),_=E+h,tt=S+L*_,Z=x+7*_+U,R=document.createElement("div");R.style.maxWidth=`${tt}px`,R.style.margin="0 auto",t.appendChild(R);let et=Rt(R).append("svg").attr("width",tt).attr("height",Z).attr("class","heatmap-svg"),rt=Rr(a),y=[],I=rt.floor(u);vt.range(vt.ceil(u),l).forEach(d=>{y.push({date:d,week:rt.count(I,d)})}),et.selectAll(".month-label").data(y).join("text").attr("class","heatmap-label month-label").attr("x",d=>S+d.week*_).attr("y",x-6).text(d=>Ea(d.date)),et.selectAll(".day-label").data(Ya(a)).join("text").attr("class","heatmap-label day-label").attr("x",S-6).attr("y",(d,A)=>x+A*_+E-2).attr("text-anchor","end").text(d=>d),document.querySelectorAll(".heatmap-tooltip").forEach(d=>d.remove());let D=Oa();D.style.position="fixed",document.body.appendChild(D),et.selectAll(".heatmap-cell").data(s).join("rect").attr("class",d=>{let A="heatmap-cell";return d.entry||(A+=" heatmap-empty"),d.isWeekend&&(A+=" heatmap-weekend"),A}).attr("x",d=>S+d.week*_).attr("y",d=>x+d.day*_).attr("width",E).attr("height",E).attr("fill",d=>d.entry?w(d.entry.hours):"").on("mouseenter",function(d,A){let Q=A.entry?A.entry.hours.toFixed(1):"0.0",X=A.entry?A.entry.count:0;D.innerHTML=`${qr(A.date)}
${Q}h (${X} entries)`,D.style.display="block";let z=d.target.getBoundingClientRect();D.style.left=`${z.left+E/2}px`,D.style.top=`${z.top-D.offsetHeight-8}px`}).on("mouseleave",function(){D.style.display="none"}).on("click",function(d,A){n&&n(A.dateStr)})}function Ra(t){let e=t.getAttribute("data-url");if(!e){console.error("KimaiHeatmap: missing data-url attribute");return}let r=t.getAttribute("data-timesheet-url")||"/en/timesheet/",n=t.getAttribute("data-week-start")||"monday",o=t.getAttribute("data-projects"),a=o?JSON.parse(o):[],i=null,u=h=>{let x=`${h} - ${h}`,S=`${r}?daterange=${encodeURIComponent(x)}`;i&&(S+=`&projects[]=${i}`),window.location.href=S};t.innerHTML="";let l=document.createElement("div");l.className="heatmap-wrapper";let s=document.createElement("div");s.className="heatmap-svg-area",l.appendChild(s);let m=null,p=(h,x)=>{m=h,Vr(s,h,Or,u,x,n),qa(t,h.days),s.scrollLeft=s.scrollWidth};if(a.length>0){let h=document.createElement("div");h.className="heatmap-filter";let x=document.createElement("select");x.className="form-select form-select-sm",x.setAttribute("aria-label","Filter by project");let S=document.createElement("option");S.value="",S.textContent="All Projects",x.appendChild(S);for(let U of a){let L=document.createElement("option");L.value=String(U.id),L.textContent=U.name,x.appendChild(L)}x.addEventListener("change",()=>{let U=x.value;i=U?parseInt(U,10):null;let L=U?`${e}?project=${U}`:e;fetch(L).then(b=>{if(!b.ok)throw new Error(`HTTP ${b.status}`);return b.json()}).then(b=>{p(b,"No tracking data for this project")}).catch(b=>{console.error("KimaiHeatmap: failed to load filtered data",b)})}),h.appendChild(x),l.appendChild(h)}t.appendChild(l);let w;window.addEventListener("resize",()=>{clearTimeout(w),w=setTimeout(()=>{m&&p(m)},200)}),fetch(e).then(h=>{if(!h.ok)throw new Error(`HTTP ${h.status}`);return h.json()}).then(h=>{p(h)}).catch(h=>{console.error("KimaiHeatmap: failed to load data",h),s.textContent="Failed to load heatmap data"})}return an(za);})(); diff --git a/assets/src/heatmap.ts b/assets/src/heatmap.ts index 6d44ab8..8ba5141 100644 --- a/assets/src/heatmap.ts +++ b/assets/src/heatmap.ts @@ -209,8 +209,9 @@ export function renderHeatmap( // Compute cell size to fill available width, capped at 16px const containerWidth = container.clientWidth || 800; - const maxCellSize = 18; - const cellSize = Math.min(maxCellSize, Math.max(2, Math.floor((containerWidth - marginLeft) / numWeeks) - cellGap)); + const maxCellSize = 22; + const minCellSize = 10; + const cellSize = Math.min(maxCellSize, Math.max(minCellSize, Math.floor((containerWidth - marginLeft) / numWeeks) - cellGap)); const step = cellSize + cellGap; const svgWidth = marginLeft + numWeeks * step; const svgHeight = marginTop + 7 * step + marginBottom; @@ -297,6 +298,7 @@ export function renderHeatmap( if (!onCellClick) return; onCellClick(d.dateStr); }); + } export function init(container: HTMLElement): void { @@ -331,6 +333,16 @@ export function init(container: HTMLElement): void { svgArea.className = 'heatmap-svg-area'; wrapper.appendChild(svgArea); + // Shared state for current data (used by resize re-render and filter) + let currentData: HeatmapData | null = null; + + const doRender = (data: HeatmapData, emptyMsg?: string) => { + currentData = data; + renderHeatmap(svgArea, data, DEFAULT_CONFIG, onCellClick, emptyMsg, weekStart); + renderStats(container, data.days); + svgArea.scrollLeft = svgArea.scrollWidth; + }; + // Build filter dropdown (only if projects exist) if (projects.length > 0) { const filterDiv = document.createElement('div'); @@ -363,8 +375,7 @@ export function init(container: HTMLElement): void { return res.json() as Promise; }) .then(data => { - renderHeatmap(svgArea, data, DEFAULT_CONFIG, onCellClick, 'No tracking data for this project', weekStart); - renderStats(wrapper, data.days); + doRender(data, 'No tracking data for this project'); }) .catch(err => { console.error('KimaiHeatmap: failed to load filtered data', err); @@ -377,6 +388,15 @@ export function init(container: HTMLElement): void { container.appendChild(wrapper); + // Re-render on window resize (debounced) + let resizeTimer: ReturnType; + window.addEventListener('resize', () => { + clearTimeout(resizeTimer); + resizeTimer = setTimeout(() => { + if (currentData) doRender(currentData); + }, 200); + }); + // Initial data fetch fetch(baseUrl) .then(res => { @@ -384,8 +404,7 @@ export function init(container: HTMLElement): void { return res.json() as Promise; }) .then(data => { - renderHeatmap(svgArea, data, DEFAULT_CONFIG, onCellClick, undefined, weekStart); - renderStats(wrapper, data.days); + doRender(data); }) .catch(err => { console.error('KimaiHeatmap: failed to load data', err); diff --git a/assets/test/setup.ts b/assets/test/setup.ts new file mode 100644 index 0000000..810e49e --- /dev/null +++ b/assets/test/setup.ts @@ -0,0 +1,8 @@ +// jsdom doesn't provide ResizeObserver +if (typeof globalThis.ResizeObserver === 'undefined') { + globalThis.ResizeObserver = class ResizeObserver { + observe() {} + unobserve() {} + disconnect() {} + } as unknown as typeof globalThis.ResizeObserver; +} diff --git a/vitest.config.ts b/vitest.config.ts index c4588ab..66b0c9b 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -4,5 +4,6 @@ export default defineConfig({ test: { environment: 'jsdom', globals: true, + setupFiles: ['./assets/test/setup.ts'], }, });