@protomaze/time 1.5.4 → 1.6.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.
@@ -0,0 +1,13 @@
1
+ /** A font for the slider's labels and tooltips. Any part left out keeps its default. */
2
+ export type TimeSliderFont = {
3
+ /** CSS font-family, e.g. '"IP Klartext", sans-serif'. Default 'monospace'. */
4
+ family?: string;
5
+ /** CSS font-weight, e.g. 500 or 'bold'. Default 'normal'. */
6
+ weight?: string | number;
7
+ /** Size in pixels. Default 12. */
8
+ size?: number;
9
+ };
10
+ export declare const DEFAULT_FONT: Required<TimeSliderFont>;
11
+ export declare function resolveFont(font?: TimeSliderFont, fallback?: Required<TimeSliderFont>): Required<TimeSliderFont>;
12
+ /** The CSS / canvas font shorthand, e.g. '500 12px "IP Klartext", sans-serif'. */
13
+ export declare function toCssFont(font: Required<TimeSliderFont>): string;
package/common/Font.js ADDED
@@ -0,0 +1 @@
1
+ export const DEFAULT_FONT={family:"monospace",weight:"normal",size:12};export function resolveFont(e,i=DEFAULT_FONT){return{family:e?.family??i.family,weight:e?.weight??i.weight,size:e?.size??i.size}}export function toCssFont(e){return`${e.weight} ${e.size}px ${e.family}`}
@@ -46,9 +46,34 @@ export declare function formatDuration(durationOrRange: number | TimeSliderTimeR
46
46
  export declare function formatDate(time: number, padding?: boolean, locale?: string): string;
47
47
  export declare function formatDateTime(time: number, locale?: string): string;
48
48
  export declare function formatDateWithWeekDay(time: number, locale?: string): string;
49
+ /** The units a span is told in, largest first. Calendar units, so a month is a calendar month long. */
50
+ declare const SPAN_UNITS: readonly ["year", "month", "week", "day", "hour", "minute", "second"];
51
+ export type TimeSpanUnit = (typeof SPAN_UNITS)[number];
52
+ export type TimeSpanDescription = {
53
+ /** At most two units, the larger first, with a ~ when rounded: "3 months 2 weeks", "~2 years 5 months". */
54
+ text: string;
55
+ /** False when the text is rounded (it then starts with ~). */
56
+ exact: boolean;
57
+ /** For exactly one of a unit, the unit's name alone ("month"), for phrases like "Next month". */
58
+ unitName?: string;
59
+ };
60
+ /**
61
+ * A span of time as people say it: in its largest calendar unit and the one below, the last one
62
+ * rounded on what is left over and carried into the larger one where it makes a whole one
63
+ * ("12 months" becomes "1 year"), with a ~ wherever it isn't exact. Localized with
64
+ * Intl.NumberFormat unit names.
65
+ */
66
+ export declare function describeTimeSpan(startTime: number, endTime: number, locale?: string): TimeSpanDescription;
67
+ /**
68
+ * The dates a range covers, e.g. "Sep 4, 2012 – Sep 30, 2025". An end at local midnight is
69
+ * exclusive, so the last day shown is the day before it. Ranges that aren't whole days and
70
+ * last less than a week also show the time of day.
71
+ */
72
+ export declare function formatTimeSpanDates(startTime: number, endTime: number, locale?: string): string;
49
73
  export declare function formatDateRange(range: TimeSliderTimeRangeArray, locale?: string): string;
50
74
  export declare function formatTime(time: number): string;
51
75
  export declare function getTimePartsInHours(time: number): [hoursSinceYear0: number, msSinceHourStart: number];
52
76
  export declare function splitTime3(time: number): [t1: number, t2: number, t3: number];
53
77
  export declare function splitTime2(time: number): [t1: number, t2: number];
54
78
  export declare function getDurationParts(duration: number): [days: number, ms: number];
79
+ export {};
@@ -1 +1 @@
1
- import{CENTURY as e,DECADE as t,MILLENIUM as r,ONE_DAY as a,ONE_HOUR as n,ONE_MINUTE as o,ONE_MONTH as s,ONE_SECOND as u,ONE_WEEK as i,ONE_YEAR as c}from"../ticks/TimeSliderConstants.js";const l=new Date;l.setFullYear(0,0,1),l.setHours(0,0,0,0);export const year0Ms=l.getTime();const g=new Date;g.setFullYear(1e3,0,1),g.setHours(0,0,0,0);export const year1000Ms=g.getTime();export const SPLIT_PART_2=Math.pow(2,22);export const SPLIT_TIME_3=Math.pow(2,14);export const SPLIT_TIME_3_POW2=SPLIT_TIME_3*SPLIT_TIME_3;export const MONTH_NAMES=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];let m;export function setDefaultLocale(e){m=e}export function getDefaultLocale(){return m??("undefined"!=typeof navigator?navigator.language:"en")}const f=new Map;function h(e,t){const r=e??getDefaultLocale(),a=r+JSON.stringify(t);let n=f.get(a);return n||(n=new Intl.DateTimeFormat(r,t),f.set(a,n)),n}const T={en:{year:["year","years"],week:["week","weeks"],day:["day","days"]},de:{year:["Jahr","Jahre"],week:["Woche","Wochen"],day:["Tag","Tage"]},fr:{year:["an","ans"],week:["semaine","semaines"],day:["jour","jours"]},nl:{year:["jaar","jaar"],week:["week","weken"],day:["dag","dagen"]}};export function getStartOfSecond(e){return new Date(e).setMilliseconds(0)}export function getStartOfMinute(e){return new Date(e).setSeconds(0,0)}export function getStartOfHour(e){return new Date(e).setMinutes(0,0,0)}export function getStartOfDay(e){return new Date(e).setHours(0,0,0,0)}export function getStartOfWeek(e){const t=new Date(e),r=t.getDay(),a=t.getDate()-r+(0===r?-6:1);return new Date(t.setDate(a)).setHours(0,0,0,0)}function M(e){const t=new Date(e.valueOf()),r=(e.getDay()+6)%7+1;return t.setDate(t.getDate()-r+4),t}export function getISOWeekNumber(e){const t=M(e),r=new Date(t.getFullYear(),0,1),n=new Date(t.getFullYear(),t.getMonth(),t.getDate()),o=Math.round((n.getTime()-r.getTime())/a);return Math.floor(o/7)+1}export function getISOWeekYear(e){return M(e).getFullYear()}const D={en:"W",de:"KW",fr:"S",nl:"Wk"};export function getWeekLabelPrefix(e){const t=e.split("-")[0].toLowerCase();return D[t]??D.en}export function getStartOfMonth(e){const t=new Date(e);return t.setDate(1),t.setHours(0,0,0,0)}export function getStartOfXYears(e,t){const r=new Date(e),a=t*Math.floor(r.getFullYear()/t),n=new Date(a,0,1);n.setFullYear(a);return n.setHours(0,0,0,0)}export function getTimeUnitInMs(l){switch(l){case"year":return c;case"month":return s;case"week":return i;case"day":return a;case"hour":return n;case"minute":return o;case"second":return u;case"decade":return t;case"century":return e;case"millennium":return r}}export function getTimeUnitRange(e,t,r=0){const a=new Date(e),n=new Date(a),o=new Date(a);switch(t){case"year":{const e=a.getFullYear()+r;n.setFullYear(e,0,1),n.setHours(0,0,0,0),o.setFullYear(e+1,0,1),o.setHours(0,0,0,0);break}case"month":{const e=a.getFullYear(),t=a.getMonth()+r;n.setFullYear(e,t,1),n.setHours(0,0,0,0),o.setFullYear(e,t+1,1),o.setHours(0,0,0,0);break}case"week":{const e=(a.getDay()+6)%7;n.setDate(a.getDate()-e+7*r),n.setHours(0,0,0,0),o.setTime(n.getTime()),o.setDate(n.getDate()+7);break}case"day":n.setDate(a.getDate()+r),n.setHours(0,0,0,0),o.setTime(n.getTime()),o.setDate(n.getDate()+1);break;case"hour":n.setTime(a.getTime()),n.setHours(a.getHours()+r,0,0,0),o.setTime(n.getTime()),o.setHours(n.getHours()+1);break;case"minute":n.setTime(a.getTime()),n.setMinutes(a.getMinutes()+r,0,0),o.setTime(n.getTime()),o.setMinutes(n.getMinutes()+1);break;case"second":n.setTime(a.getTime()),n.setSeconds(a.getSeconds()+r,0),o.setTime(n.getTime()),o.setSeconds(n.getSeconds()+1);break;case"decade":{const e=10*Math.floor(a.getFullYear()/10)+10*r;n.setFullYear(e,0,1),n.setHours(0,0,0,0),o.setFullYear(e+10,0,1),o.setHours(0,0,0,0);break}case"century":{const e=100*Math.floor(a.getFullYear()/100)+100*r;n.setFullYear(e,0,1),n.setHours(0,0,0,0),o.setFullYear(e+100,0,1),o.setHours(0,0,0,0);break}case"millennium":{const e=1e3*Math.floor(a.getFullYear()/1e3)+1e3*r;n.setFullYear(e,0,1),n.setHours(0,0,0,0),o.setFullYear(e+1e3,0,1),o.setHours(0,0,0,0);break}default:throw new Error("Invalid category")}return{startTime:n.getTime(),endTime:o.getTime()}}export function getTimeOffset(e,t,r){const a=new Date(e),n=new Date(t);switch(r){case"year":return n.getFullYear()-a.getFullYear();case"month":return 12*(n.getFullYear()-a.getFullYear())+(n.getMonth()-a.getMonth());case"week":const e=6048e5;return Math.floor((n.getTime()-a.getTime())/e);case"day":const t=864e5;return Math.floor((n.getTime()-a.getTime())/t);case"hour":const r=36e5;return Math.floor((n.getTime()-a.getTime())/r);case"minute":const o=6e4;return Math.floor((n.getTime()-a.getTime())/o);case"second":const s=1e3;return Math.floor((n.getTime()-a.getTime())/s);case"decade":return Math.floor(n.getFullYear()/10)-Math.floor(a.getFullYear()/10);case"century":return Math.floor(n.getFullYear()/100)-Math.floor(a.getFullYear()/100);case"millennium":return Math.floor(n.getFullYear()/1e3)-Math.floor(a.getFullYear()/1e3);default:throw new Error("Invalid category")}}export function getMatchingTimeUnit(e,t){const r=["millennium","century","decade","year","month","week","day","hour","minute","second"];for(const a of r){const r=getTimeUnitRange(e,a),n=r.endTime-r.startTime;if(0===n)continue;const o=.1*n,s=Math.abs(e-r.startTime)<=o,u=Math.abs(t-r.endTime)<=o;if(s&&u)return a}}export function getLargeTimeUnitRange(e,t,r=0){const a=new Date(e),n=new Date(a),o=new Date(a),s=Math.floor(n.getFullYear()/t)*t+r*t;return n.setFullYear(s,0,1),n.setHours(0,0,0,0),o.setFullYear(s+t-1,11,31),o.setHours(23,59,59,999),{startTime:n.getTime(),endTime:o.getTime()}}export function formatDuration(t,r){const a=function(e){const t=e.split("-")[0].toLowerCase();return T[t]??T.en}(r??getDefaultLocale()),o="number"==typeof t?t:Array.isArray(t)?t[1]-t[0]:t.endTime-t.startTime,s=Math.floor(o/1e3),l=Math.floor(s/60),g=Math.floor(l/60),m=Math.floor(g/24),f=Math.floor(m/365),h=Math.floor(o%1e3),M=s%60,D=l%60,d=g%24,w=m%365,y=[];if(f>0&&y.push(`${f} ${f>1?a.year[1]:a.year[0]}`),f<1||w>Math.ceil(f/4))if(w%7==0&&0===d&&0===D&&0===M&&w>0&&o<e){const e=Math.floor(w/7);y.push(`${e} ${e>1?a.week[1]:a.week[0]}`)}else w>0&&o<e&&y.push(`${w} ${w>1?a.day[1]:a.day[0]}`),d>0&&o<c&&y.push(`${d}h`),D>0&&o<i&&y.push(`${D}m`),M>0&&o<n&&y.push(`${M}s`),o<5*u&&o>=1&&y.push(`${h}ms`),o<1&&y.push("<1ms");return y.join(" ")}export function formatDate(e,t,r){const a=new Date(e);return h(r,{day:t?"2-digit":"numeric",month:"short",year:"numeric"}).format(a)}export function formatDateTime(e,t){const r="number"==typeof e?new Date(e):e;return`${h(t,{weekday:"short",day:"2-digit",month:"short",year:"numeric"}).format(r)} ${formatTime(r.getTime())}`}export function formatDateWithWeekDay(e,t){const r="number"==typeof e?new Date(e):e;return h(t,{weekday:"short",day:"2-digit",month:"short",year:"numeric"}).format(r)}export function formatDateRange(e,t){const[r,a]=e,n=new Date(r),o=new Date(a),s=new Date(a-1),u=1===n.getDate()&&1===o.getDate(),i=u&&0===n.getMonth()&&0===o.getMonth(),c=h(t,{day:"2-digit",month:"short",year:"numeric"}),l=h(t,{month:"short",year:"numeric"}),g=h(t,{year:"numeric"});return n.getFullYear()===s.getFullYear()?n.getMonth()===s.getMonth()?n.getDate()===s.getDate()?c.format(s):u?l.format(s):c.formatRange(n,s):i?g.format(s):c.formatRange(n,s):i?g.formatRange(n,s):c.formatRange(n,s)}export function formatTime(e){const t=new Date(e),r=t.getHours(),a=t.getMinutes(),n=t.getSeconds();return(r<10?"0":"")+r+":"+(a<10?"0":"")+a+":"+(n<10?"0":"")+n}export function getTimePartsInHours(e){return[Math.floor(e/n),e%n]}export function splitTime3(e){const t=Math.floor(e/SPLIT_TIME_3_POW2),r=e%SPLIT_TIME_3_POW2;return[t,Math.floor(r/SPLIT_TIME_3),r%SPLIT_TIME_3]}export function splitTime2(e){return[Math.floor(e/SPLIT_PART_2),e%SPLIT_PART_2]}export function getDurationParts(e){return[Math.floor(e/a),e%a]}
1
+ import{CENTURY as e,DECADE as t,MILLENIUM as r,ONE_DAY as n,ONE_HOUR as a,ONE_MINUTE as o,ONE_MONTH as s,ONE_SECOND as u,ONE_WEEK as i,ONE_YEAR as c}from"../ticks/TimeSliderConstants.js";const l=new Date;l.setFullYear(0,0,1),l.setHours(0,0,0,0);export const year0Ms=l.getTime();const g=new Date;g.setFullYear(1e3,0,1),g.setHours(0,0,0,0);export const year1000Ms=g.getTime();export const SPLIT_PART_2=Math.pow(2,22);export const SPLIT_TIME_3=Math.pow(2,14);export const SPLIT_TIME_3_POW2=SPLIT_TIME_3*SPLIT_TIME_3;export const MONTH_NAMES=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];let m;export function setDefaultLocale(e){m=e}export function getDefaultLocale(){return m??("undefined"!=typeof navigator?navigator.language:"en")}const f=new Map;function h(e,t){const r=e??getDefaultLocale(),n=r+JSON.stringify(t);let a=f.get(n);return a||(a=new Intl.DateTimeFormat(r,t),f.set(n,a)),a}const T={en:{year:["year","years"],week:["week","weeks"],day:["day","days"]},de:{year:["Jahr","Jahre"],week:["Woche","Wochen"],day:["Tag","Tage"]},fr:{year:["an","ans"],week:["semaine","semaines"],day:["jour","jours"]},nl:{year:["jaar","jaar"],week:["week","weken"],day:["dag","dagen"]}};export function getStartOfSecond(e){return new Date(e).setMilliseconds(0)}export function getStartOfMinute(e){return new Date(e).setSeconds(0,0)}export function getStartOfHour(e){return new Date(e).setMinutes(0,0,0)}export function getStartOfDay(e){return new Date(e).setHours(0,0,0,0)}export function getStartOfWeek(e){const t=new Date(e),r=t.getDay(),n=t.getDate()-r+(0===r?-6:1);return new Date(t.setDate(n)).setHours(0,0,0,0)}function M(e){const t=new Date(e.valueOf()),r=(e.getDay()+6)%7+1;return t.setDate(t.getDate()-r+4),t}export function getISOWeekNumber(e){const t=M(e),r=new Date(t.getFullYear(),0,1),a=new Date(t.getFullYear(),t.getMonth(),t.getDate()),o=Math.round((a.getTime()-r.getTime())/n);return Math.floor(o/7)+1}export function getISOWeekYear(e){return M(e).getFullYear()}const D={en:"W",de:"KW",fr:"S",nl:"Wk"};export function getWeekLabelPrefix(e){const t=e.split("-")[0].toLowerCase();return D[t]??D.en}export function getStartOfMonth(e){const t=new Date(e);return t.setDate(1),t.setHours(0,0,0,0)}export function getStartOfXYears(e,t){const r=new Date(e),n=t*Math.floor(r.getFullYear()/t),a=new Date(n,0,1);a.setFullYear(n);return a.setHours(0,0,0,0)}export function getTimeUnitInMs(l){switch(l){case"year":return c;case"month":return s;case"week":return i;case"day":return n;case"hour":return a;case"minute":return o;case"second":return u;case"decade":return t;case"century":return e;case"millennium":return r}}export function getTimeUnitRange(e,t,r=0){const n=new Date(e),a=new Date(n),o=new Date(n);switch(t){case"year":{const e=n.getFullYear()+r;a.setFullYear(e,0,1),a.setHours(0,0,0,0),o.setFullYear(e+1,0,1),o.setHours(0,0,0,0);break}case"month":{const e=n.getFullYear(),t=n.getMonth()+r;a.setFullYear(e,t,1),a.setHours(0,0,0,0),o.setFullYear(e,t+1,1),o.setHours(0,0,0,0);break}case"week":{const e=(n.getDay()+6)%7;a.setDate(n.getDate()-e+7*r),a.setHours(0,0,0,0),o.setTime(a.getTime()),o.setDate(a.getDate()+7);break}case"day":a.setDate(n.getDate()+r),a.setHours(0,0,0,0),o.setTime(a.getTime()),o.setDate(a.getDate()+1);break;case"hour":a.setTime(n.getTime()),a.setHours(n.getHours()+r,0,0,0),o.setTime(a.getTime()),o.setHours(a.getHours()+1);break;case"minute":a.setTime(n.getTime()),a.setMinutes(n.getMinutes()+r,0,0),o.setTime(a.getTime()),o.setMinutes(a.getMinutes()+1);break;case"second":a.setTime(n.getTime()),a.setSeconds(n.getSeconds()+r,0),o.setTime(a.getTime()),o.setSeconds(a.getSeconds()+1);break;case"decade":{const e=10*Math.floor(n.getFullYear()/10)+10*r;a.setFullYear(e,0,1),a.setHours(0,0,0,0),o.setFullYear(e+10,0,1),o.setHours(0,0,0,0);break}case"century":{const e=100*Math.floor(n.getFullYear()/100)+100*r;a.setFullYear(e,0,1),a.setHours(0,0,0,0),o.setFullYear(e+100,0,1),o.setHours(0,0,0,0);break}case"millennium":{const e=1e3*Math.floor(n.getFullYear()/1e3)+1e3*r;a.setFullYear(e,0,1),a.setHours(0,0,0,0),o.setFullYear(e+1e3,0,1),o.setHours(0,0,0,0);break}default:throw new Error("Invalid category")}return{startTime:a.getTime(),endTime:o.getTime()}}export function getTimeOffset(e,t,r){const n=new Date(e),a=new Date(t);switch(r){case"year":return a.getFullYear()-n.getFullYear();case"month":return 12*(a.getFullYear()-n.getFullYear())+(a.getMonth()-n.getMonth());case"week":const e=6048e5;return Math.floor((a.getTime()-n.getTime())/e);case"day":const t=864e5;return Math.floor((a.getTime()-n.getTime())/t);case"hour":const r=36e5;return Math.floor((a.getTime()-n.getTime())/r);case"minute":const o=6e4;return Math.floor((a.getTime()-n.getTime())/o);case"second":const s=1e3;return Math.floor((a.getTime()-n.getTime())/s);case"decade":return Math.floor(a.getFullYear()/10)-Math.floor(n.getFullYear()/10);case"century":return Math.floor(a.getFullYear()/100)-Math.floor(n.getFullYear()/100);case"millennium":return Math.floor(a.getFullYear()/1e3)-Math.floor(n.getFullYear()/1e3);default:throw new Error("Invalid category")}}export function getMatchingTimeUnit(e,t){const r=["millennium","century","decade","year","month","week","day","hour","minute","second"];for(const n of r){const r=getTimeUnitRange(e,n),a=r.endTime-r.startTime;if(0===a)continue;const o=.1*a,s=Math.abs(e-r.startTime)<=o,u=Math.abs(t-r.endTime)<=o;if(s&&u)return n}}export function getLargeTimeUnitRange(e,t,r=0){const n=new Date(e),a=new Date(n),o=new Date(n),s=Math.floor(a.getFullYear()/t)*t+r*t;return a.setFullYear(s,0,1),a.setHours(0,0,0,0),o.setFullYear(s+t-1,11,31),o.setHours(23,59,59,999),{startTime:a.getTime(),endTime:o.getTime()}}export function formatDuration(t,r){const n=function(e){const t=e.split("-")[0].toLowerCase();return T[t]??T.en}(r??getDefaultLocale()),o="number"==typeof t?t:Array.isArray(t)?t[1]-t[0]:t.endTime-t.startTime,s=Math.floor(o/1e3),l=Math.floor(s/60),g=Math.floor(l/60),m=Math.floor(g/24),f=Math.floor(m/365),h=Math.floor(o%1e3),M=s%60,D=l%60,d=g%24,p=m%365,y=[];if(f>0&&y.push(`${f} ${f>1?n.year[1]:n.year[0]}`),f<1||p>Math.ceil(f/4))if(p%7==0&&0===d&&0===D&&0===M&&p>0&&o<e){const e=Math.floor(p/7);y.push(`${e} ${e>1?n.week[1]:n.week[0]}`)}else p>0&&o<e&&y.push(`${p} ${p>1?n.day[1]:n.day[0]}`),d>0&&o<c&&y.push(`${d}h`),D>0&&o<i&&y.push(`${D}m`),M>0&&o<a&&y.push(`${M}s`),o<5*u&&o>=1&&y.push(`${h}ms`),o<1&&y.push("<1ms");return y.join(" ")}export function formatDate(e,t,r){const n=new Date(e);return h(r,{day:t?"2-digit":"numeric",month:"short",year:"numeric"}).format(n)}export function formatDateTime(e,t){const r="number"==typeof e?new Date(e):e;return`${h(t,{weekday:"short",day:"2-digit",month:"short",year:"numeric"}).format(r)} ${formatTime(r.getTime())}`}export function formatDateWithWeekDay(e,t){const r="number"==typeof e?new Date(e):e;return h(t,{weekday:"short",day:"2-digit",month:"short",year:"numeric"}).format(r)}const d=["year","month","week","day","hour","minute","second"],p=new Map;function y(e,t){const r=e+":"+t;let n=p.get(r);return n||(n=new Intl.NumberFormat(e,{style:"unit",unit:t,unitDisplay:"long"}),p.set(r,n)),n}function w(e,t,r){const n=new Date(e);switch(t){case"year":n.setFullYear(n.getFullYear()+r);break;case"month":n.setMonth(n.getMonth()+r);break;case"week":n.setDate(n.getDate()+7*r);break;case"day":n.setDate(n.getDate()+r);break;case"hour":return e+r*a;case"minute":return e+r*o;case"second":return e+r*u}return n.getTime()}function x(e){return"second"===e?0:1e3}function F(e,t,r){let n=0;for(;w(e,r,n+1)<=t+x(r);)n++;return n}export function describeTimeSpan(e,t,r){const n=r??getDefaultLocale(),a=[];let o=e;for(const e of d){const r=F(o,t,e);if((0!==r||0!==a.length)&&(a.push([e,r]),o=w(o,e,r),2===a.length))break}0===a.length&&a.push(["second",0]);const s=t-o,u=a[a.length-1],i=Math.abs(s)<=x(u[0]);if(s>=(w(o,u[0],1)-o)/2&&u[1]++,2===a.length){const[[t,r],[o,s]]=a,u=w(e,t,r),i=w(u,t,1),c=.5*(w(u,o,1)-u);if(w(u,o,s)>=i-c){return{text:`~${describeTimeSpan(e,i,n).text.replace(/^~/,"")}`,exact:!1}}}const c=a.filter(([,e])=>e>0);0===c.length&&c.push(a[0]);const l=c.map(([e,t])=>y(n,e).format(t)).join(" ");return{text:i?l:`~${l}`,exact:i,unitName:i&&1===c.length&&1===c[0][1]?y(n,c[0][0]).formatToParts(1).filter(e=>"unit"===e.type).map(e=>e.value).join(""):void 0}}export function formatTimeSpanDates(e,t,r){const n=e=>{const t=new Date(e);return 0===t.getHours()&&0===t.getMinutes()&&0===t.getSeconds()&&0===t.getMilliseconds()},a=n(e)&&n(t),o=a&&t>e?t-1:t;return h(r,!a&&t-e<i?{day:"numeric",month:"short",year:"numeric",hour:"2-digit",minute:"2-digit"}:{day:"numeric",month:"short",year:"numeric"}).formatRange(new Date(e),new Date(Math.max(e,o)))}export function formatDateRange(e,t){const[r,n]=e,a=new Date(r),o=new Date(n),s=new Date(n-1),u=1===a.getDate()&&1===o.getDate(),i=u&&0===a.getMonth()&&0===o.getMonth(),c=h(t,{day:"2-digit",month:"short",year:"numeric"}),l=h(t,{month:"short",year:"numeric"}),g=h(t,{year:"numeric"});return a.getFullYear()===s.getFullYear()?a.getMonth()===s.getMonth()?a.getDate()===s.getDate()?c.format(s):u?l.format(s):c.formatRange(a,s):i?g.format(s):c.formatRange(a,s):i?g.formatRange(a,s):c.formatRange(a,s)}export function formatTime(e){const t=new Date(e),r=t.getHours(),n=t.getMinutes(),a=t.getSeconds();return(r<10?"0":"")+r+":"+(n<10?"0":"")+n+":"+(a<10?"0":"")+a}export function getTimePartsInHours(e){return[Math.floor(e/a),e%a]}export function splitTime3(e){const t=Math.floor(e/SPLIT_TIME_3_POW2),r=e%SPLIT_TIME_3_POW2;return[t,Math.floor(r/SPLIT_TIME_3),r%SPLIT_TIME_3]}export function splitTime2(e){return[Math.floor(e/SPLIT_PART_2),e%SPLIT_PART_2]}export function getDurationParts(e){return[Math.floor(e/n),e%n]}
@@ -1,10 +1,11 @@
1
- import { TimeUnit } from '../common/TimeTools';
1
+ import { TimeSpanDescription, TimeUnit } from '../common/TimeTools';
2
2
  import { TimeSliderElement } from "../data/TimeSliderElement";
3
3
  import { TimeSliderRange, TimeSliderTimeRange, TimeSliderTimeRangeWithDuration, TimeSliderValueRange } from "../data/TimeSliderRange";
4
4
  import { TypedEventEmitter } from "../event/TypedEventEmitter";
5
5
  import { TimeSliderLayer } from "../layer/TimeSliderLayer";
6
6
  import { TimeSliderLayerLoader } from "../layer/TimeSliderLayerLoader";
7
7
  import { TimeTicksBar } from "../ticks/TimeTicksBar";
8
+ import { TimeSliderFont } from "../common/Font";
8
9
  import { TimeSliderEngine } from "./TimeSliderEngine";
9
10
  export type TimeSliderHoverOptions = {
10
11
  time?: boolean;
@@ -68,6 +69,53 @@ export type TimeSliderSelectionRange = {
68
69
  };
69
70
  export type TimeSliderSelectionChangePhase = 'start' | 'drag' | 'end';
70
71
  export type TimeSliderSelectRangeMode = 'zoom' | 'select' | 'createSelection';
72
+ /** A tick box on the ticks bar, as passed to TimeSliderOptions.tickTooltip and the 'tickhover' event. */
73
+ export type TimeSliderTickInfo = {
74
+ startTime: number;
75
+ endTime: number;
76
+ /** The tick's time unit, when it matches one (see getMatchingTimeUnit). */
77
+ unit?: TimeUnit;
78
+ /** The label the box shows, possibly shortened to fit. */
79
+ label: string;
80
+ /** The tick's full label, e.g. "March 2024" where the box shows "Mar". Localized. */
81
+ fullLabel: string;
82
+ };
83
+ /** The 'tickhover' event: the hovered tick box and its rectangle in client (viewport) coordinates. */
84
+ export type TimeSliderTickHoverInfo = TimeSliderTickInfo & {
85
+ rect: DOMRect;
86
+ };
87
+ /** true (default): tooltip with the tick's full label. false: no built-in tooltip (use the 'tickhover' event). A function: its text, or no tooltip for undefined. */
88
+ export type TimeSliderTickTooltip = boolean | ((tick: TimeSliderTickInfo) => string | undefined);
89
+ /** Style of the slider's tooltips (hover, tick and selection label). CSS values; anything left out keeps its default. */
90
+ export type TimeSliderTooltipStyle = {
91
+ background?: string;
92
+ border?: string;
93
+ borderRadius?: string;
94
+ color?: string;
95
+ padding?: string;
96
+ maxWidth?: string;
97
+ /** Defaults to the slider's font (TimeSliderOptions.font). */
98
+ font?: TimeSliderFont;
99
+ };
100
+ /** See TimeSlider.describeStep(). */
101
+ export type TimeSliderStepDescription = {
102
+ /** The range next()/previous() goes to, limits applied. */
103
+ range: TimeSliderTimeRange;
104
+ /** False when the step wouldn't move the range (at the limits). */
105
+ canStep: boolean;
106
+ /** The step's distance, e.g. { text: "~2 years 5 months", exact: false }. */
107
+ span: TimeSpanDescription;
108
+ /** e.g. "Next month", "Previous ~2 years 5 months", "Weiter: 1 Monat". */
109
+ label: string;
110
+ };
111
+ /** See TimeSlider.describeSpan(). */
112
+ export type TimeSliderSpanDescription = {
113
+ /** e.g. "~13 years 3 months". */
114
+ text: string;
115
+ exact: boolean;
116
+ /** e.g. "Sep 4, 2012 – Sep 30, 2025". */
117
+ dates: string;
118
+ };
71
119
  export interface TimeSliderOptions {
72
120
  showCurrent?: boolean;
73
121
  showTicks?: boolean;
@@ -87,6 +135,14 @@ export interface TimeSliderOptions {
87
135
  tickLabelStyler?: TimeSliderTickLabelStyler;
88
136
  /** Outer bounds of the visible time range: zooming out never shows more than this and panning stops at its edges. Applied in setTimeRange(), so wheel, pinch, up(), fit() and animations all respect it. Either side may be -Infinity/Infinity. See also setLimits(). */
89
137
  limits?: TimeSliderTimeRange;
138
+ /** Font of the tick labels, the y-axis labels (YAxisSettings.style.labels.font overrides it per axis) and the tooltips. Defaults to 12px monospace (y-axis: 12px sans-serif). Labels are measured in it so they always fit their tick boxes, and redrawn once a web font has loaded. See also setFont(). */
139
+ font?: TimeSliderFont;
140
+ /** Background colour behind the layers and the ticks bar, e.g. '#1A1A1A'. Defaults to the theme's (#111 dark, #eee light) and is kept across theme changes. See also setBackground(). */
141
+ background?: string;
142
+ /** Tooltip on hovering a tick box, see TimeSliderTickTooltip. See also setTickTooltip() and the 'tickhover' event. */
143
+ tickTooltip?: TimeSliderTickTooltip;
144
+ /** Style of the hover, tick and selection-label tooltips. See also setTooltipStyle(). */
145
+ tooltipStyle?: TimeSliderTooltipStyle;
90
146
  }
91
147
  export declare class TimeSlider extends TypedEventEmitter<{
92
148
  live: boolean;
@@ -111,6 +167,8 @@ export declare class TimeSlider extends TypedEventEmitter<{
111
167
  hover: TimeSliderHoverInfo | null;
112
168
  /** Fires when a tick label on the ticks bar is clicked. Also still zooms/fits to that tick's range, same as before this event existed. */
113
169
  tickclick: TimeSliderTickClickInfo;
170
+ /** Fires when the pointer moves onto another tick box on the ticks bar, and with null when it leaves them. Carries the box's client rect, to render your own tooltip. */
171
+ tickhover: TimeSliderTickHoverInfo | null;
114
172
  }> {
115
173
  valueRanges: Map<string, {
116
174
  min: number;
@@ -165,6 +223,12 @@ export declare class TimeSlider extends TypedEventEmitter<{
165
223
  selectingRangeVisible: boolean;
166
224
  whenDoneSelectingRange?: () => void;
167
225
  private _limits?;
226
+ private _font?;
227
+ private _background?;
228
+ private tickTooltip;
229
+ private tooltipStyle;
230
+ private tickToolTip;
231
+ private stopWatchingFonts?;
168
232
  getLayers(): TimeSliderLayer<TimeSliderElement, import("..").TimeSliderStyle>[];
169
233
  getVisibleLayers(): TimeSliderLayer<TimeSliderElement, import("..").TimeSliderStyle>[];
170
234
  constructor(options?: TimeSliderOptions);
@@ -175,6 +239,22 @@ export declare class TimeSlider extends TypedEventEmitter<{
175
239
  setWeekLabel(weekLabel: 'default' | 'calendarWeek', weekLabelPrefix?: string): void;
176
240
  /** Overrides per-tick label color/opacity, e.g. to fade ticks outside a selection. Pass undefined to clear. */
177
241
  setTickLabelStyler(styler: TimeSliderTickLabelStyler | undefined): void;
242
+ get font(): TimeSliderFont | undefined;
243
+ /** Sets the font of the tick labels, y-axis labels and tooltips (see TimeSliderOptions.font); undefined goes back to the defaults. */
244
+ setFont(font: TimeSliderFont | undefined): void;
245
+ /** Re-measures and redraws the labels, e.g. after a web font finished loading. Done automatically on document.fonts 'loadingdone'. */
246
+ refreshFonts(): void;
247
+ private watchFontLoading;
248
+ get background(): string | undefined;
249
+ /** Sets the background colour behind the layers and the ticks bar (see TimeSliderOptions.background); undefined goes back to the theme's. */
250
+ setBackground(background: string | undefined): void;
251
+ /** Sets the tooltip shown on hovering a tick box, see TimeSliderTickTooltip. */
252
+ setTickTooltip(tickTooltip: TimeSliderTickTooltip): void;
253
+ /** Sets the style of the hover, tick and selection-label tooltips. */
254
+ setTooltipStyle(style: TimeSliderTooltipStyle): void;
255
+ private applyTooltipStyles;
256
+ private applyTooltipStyle;
257
+ private onTickHover;
178
258
  get limits(): TimeSliderTimeRange | undefined;
179
259
  /** Sets (or with undefined clears) the outer bounds of the visible time range, see TimeSliderOptions.limits. The current range is clamped right away. */
180
260
  setLimits(limits: TimeSliderTimeRange | undefined): void;
@@ -285,6 +365,7 @@ export declare class TimeSlider extends TypedEventEmitter<{
285
365
  y: number;
286
366
  } | null): void;
287
367
  initHoverTooltip(): void;
368
+ private initTickTooltip;
288
369
  initHoverListener(): () => void;
289
370
  private syncSize;
290
371
  getTimeRange(): TimeSliderTimeRangeWithDuration;
@@ -308,6 +389,27 @@ export declare class TimeSlider extends TypedEventEmitter<{
308
389
  skip(amount: number, options?: {
309
390
  animate?: number;
310
391
  }): void;
392
+ /** The range skip(amount) goes to, before the limits are applied: the next/previous matching time unit (e.g. month), or the range shifted by its own duration. */
393
+ private getSkipRange;
394
+ private clampToLimits;
395
+ /**
396
+ * Describes what next() (direction 1) or previous() (-1) would do, for the app's own buttons:
397
+ * the exact range skip() goes to (snapped to a matching time unit and clamped to the limits),
398
+ * whether that moves at all, and a localized label such as "Next month",
399
+ * "Previous 3 months 2 weeks" or "Next ~2 years 5 months" (de: "Weiter: 1 Monat").
400
+ * At the limits the label still names the regular step, and canStep is false.
401
+ */
402
+ describeStep(direction: 1 | -1): TimeSliderStepDescription;
403
+ /** Whether next() (1) or previous() (-1) would move the range, false at the limits. */
404
+ canStep(direction: 1 | -1): boolean;
405
+ /**
406
+ * Describes a range (default: the current one) for tooltips: `text` is its length as people
407
+ * say it ("~13 years 3 months"), `dates` the dates it covers ("Sep 4, 2012 – Sep 30, 2025").
408
+ * Both localized.
409
+ */
410
+ describeSpan(range?: TimeSliderTimeRange): TimeSliderSpanDescription;
411
+ /** Whether the current range already shows `range` (default: what fit() fits to, the visible layers' range), after the limits, so a fit button can be disabled. */
412
+ isFitted(range?: TimeSliderTimeRange): boolean;
311
413
  getTimeRangeLabel(): string | undefined;
312
414
  private stopFit?;
313
415
  private cancelFit;
@@ -1 +1 @@
1
- import{v4 as e}from"uuid";import{Disposables as t}from"../common/Disposables.js";import{animateRangeTransition as i}from"../common/Easing.js";import{formatDate as s,formatDateRange as n,formatDuration as l,getDefaultLocale as a,getLargeTimeUnitRange as o,getMatchingTimeUnit as r,getTimeUnitRange as h,setDefaultLocale as d}from"../common/TimeTools.js";import{TimeSliderDynamicHistogram as m}from"../data/histogram/TimeSliderDynamicHistogram.js";import{getTimeArray as c}from"../data/TimeSliderElement.js";import{TimeSliderIntervalsModel as g}from"../data/TimeSliderIntervalsModel.js";import{addDuration as u,addPadding as p,applyTimeRangeUpdate as y,clampTimeRange as v,mergeTimeRanges as T,timeRangesOverlap as f,timeRangesUnion as E}from"../data/TimeSliderRange.js";import{TypedEventEmitter as L}from"../event/TypedEventEmitter.js";import{TimeSliderHistogramLayer as x}from"../layer/TimeSliderHistogramLayer.js";import{TimeSliderIntervalsLayer as R}from"../layer/TimeSliderIntervalsLayer.js";import{CENTURY as b,DECADE as w,MILLENIUM as S,ONE_DAY as C,ONE_HOUR as V,ONE_MINUTE as k,ONE_MONTH as M,ONE_SECOND as B,ONE_WEEK as D,ONE_YEAR as z,TODAY as I}from"../ticks/TimeSliderConstants.js";import{TimeTicksBar as O}from"../ticks/TimeTicksBar.js";import{YAxisTicks as A}from"../ticks/YAxisTicks.js";const H=B,_=10*S;export class TimeSlider extends L{getLayers(){return[...this.layers]}getVisibleLayers(){return this.getLayers().filter(e=>e.visible)}constructor(i){super(),this.valueRanges=new Map,this.layers=[],this.onDestroy=[],this.ticksBar=new O,this.loaders=new Set,this.layerUnloaders=new Map,this.layerListenerStoppers=new Map,this.layerLoaders=new Map,this.pressedKeys=new Set,this.container=null,this.d=new t,this.id=e(),this.playing=!1,this.live=!1,this._showCurrent=!1,this.current=Date.now(),this.range={time:I,value:{minValue:0,maxValue:100}},this.size={width:100,height:100},this.sliderElement=document.createElement("div"),this.yAxisTicks=new A,this.hoverLine=document.createElement("div"),this.toolTip=document.createElement("div"),this.selectedLabel=document.createElement("div"),this.selectingRangeElement=document.createElement("div"),this.selectionOverlayContainer=document.createElement("div"),this._selectionRanges=[],this.selectionRangeElements=[],this.shadingElements=[],this.columnBandOverlayContainer=document.createElement("div"),this.columnBandElements=[],this.selectingRange=!1,this.selectingRangeVisible=!1,this.updateValueRangesQueue=new Map,this.showingStats=!1,this._engineReady=!1,this.mouse=null,this.updatingLabel=!1,this.hoverCleared=!0,this.dragging=!1,this.pinching=!1,this.hoverOptions=i?.hover||{},this.selectionOptions={highlight:"boolean"!=typeof i?.selection?.highlight||i.selection.highlight,label:"boolean"!=typeof i?.selection?.label||i.selection.label,shading:"boolean"!=typeof i?.selection?.shading||i.selection.shading,invertShading:"boolean"==typeof i?.selection?.invertShading&&i.selection.invertShading,pick:i?.selection?.pick||"element",columnBand:i?.selection?.columnBand},this.showCurrent=i?.showCurrent||!1,this.showTicks=i?.showTicks||!1,this.selectRangeMode=i?.selectRangeMode||"select",this.playSpeed=i?.playSpeed||"auto",this.liveRange=i?.liveRange||10*B,this.theme=i?.theme||"dark",this._locale=i?.locale||a(),d(this._locale),this.ticksBar.setLocale(this._locale),this.ticksBar.setWeekLabel(i?.weekLabel||"default",i?.weekLabelPrefix),this.ticksBar.setTickLabelStyler(i?.tickLabelStyler),this.setLimits(i?.limits),this.syncMaxValues(),this.on("time",()=>this.renderSelectionOverlay()),this.on("size",()=>this.renderSelectionOverlay())}get locale(){return this._locale}setLocale(e){this._locale=e,d(e),this.ticksBar.setLocale(e),this.emit("locale",e)}setWeekLabel(e,t){this.ticksBar.setWeekLabel(e,t)}setTickLabelStyler(e){this.ticksBar.setTickLabelStyler(e)}get limits(){return this._limits?{...this._limits}:void 0}setLimits(e){!e||e.endTime>e.startTime||(console.warn("Ignoring time slider limits whose endTime is not after startTime",e),e=void 0),this._limits=e?{startTime:e.startTime,endTime:e.endTime}:void 0,this._limits&&this.setTimeRange(this.getTimeRange())}setSelection(e){this._selectionRanges=e.map(e=>({...e})),this.renderSelectionOverlay(!0),this.emit("selectionchange",{ranges:this.getSelection(),phase:"end"})}getSelection(){return this._selectionRanges.map(e=>({...e}))}clearSelection(){this.setSelection([])}setPickMode(e){this.selectionOptions.pick=e,this.updateHover()}selectionTimeToX(e,t,i,s){return e===-1/0?0:e===1/0?s:Math.min(s,Math.max(0,(e-t)/(i-t)*s))}renderSelectionOverlay(e=!1){if(!this.container)return;const t=this.getSliderBounds();if(t.width<=0)return;const{startTime:i,endTime:s}=this.getTimeRange(),n=e=>this.selectionTimeToX(e,i,s,t.width);if((e||this.selectionRangeElements.length!==this._selectionRanges.length||this.selectionRangeElements.some((e,t)=>e.range!==this._selectionRanges[t]))&&(this.selectionRangeElements.forEach(({body:e,startHandle:t,endHandle:i})=>{e.remove(),t?.remove(),i?.remove()}),this.selectionRangeElements=this._selectionRanges.map(e=>this.createSelectionRangeElements(e))),this.selectionRangeElements.forEach(({range:e,body:t,startHandle:i,endHandle:s})=>{const l=n(e.startTime),a=n(e.endTime);t.style.left=l+"px",t.style.width=Math.max(0,a-l)+"px",i&&(i.style.left=l-4+"px"),s&&(s.style.left=a-4+"px")}),this.shadingElements.forEach(e=>e.remove()),this.shadingElements=[],this.selectionOptions.shading&&this._selectionRanges.length>0){const e=T(this._selectionRanges).map(e=>[n(e.startTime),n(e.endTime)]);(this.selectionOptions.invertShading?e:function(e,t){const i=[];let s=0;for(const[t,n]of e)t>s&&i.push([s,t]),s=Math.max(s,n);s<t&&i.push([s,t]);return i}(e,t.width)).forEach(([e,t])=>{if(t<=e)return;const i=document.createElement("div");i.style.position="absolute",i.style.top="0px",i.style.height="100%",i.style.left=e+"px",i.style.width=t-e+"px",i.style.background="rgba(0, 0, 0, 0.5)",i.style.pointerEvents="none",this.selectionOverlayContainer.appendChild(i),this.shadingElements.push(i)})}}renderColumnHoverBand(e){if(this.columnBandElements.forEach(e=>e.remove()),this.columnBandElements=[],"column"!==this.selectionOptions.pick||0===e.length||!this.container)return;const t=this.getSliderBounds();if(t.width<=0)return;const{startTime:i,endTime:s}=this.getTimeRange(),n=e=>Math.min(t.width,Math.max(0,(e-i)/(s-i)*t.width)),l=this.selectionOptions.columnBand?.color??"#fff",a=this.selectionOptions.columnBand?.opacity??.15;e.forEach(({layer:e,elements:t})=>{const i=t.map(c),s=Math.min(...i.map(e=>e[0])),o=Math.max(...i.map(e=>e[1])),r=document.createElement("div");r.style.position="absolute",r.style.left=n(s)+"px",r.style.width=Math.max(1,n(o)-n(s))+"px",r.style.top=100*(1-e.max)+"%",r.style.height=100*(e.max-e.min)+"%",r.style.background=l,r.style.opacity=String(a),r.style.pointerEvents="none",this.columnBandOverlayContainer.appendChild(r),this.columnBandElements.push(r)})}createSelectionRangeElements(e){const t=document.createElement("div");t.style.position="absolute",t.style.top="0px",t.style.height="100%",t.style.background="rgba(100, 180, 255, 0.15)",t.style.borderTop="2px solid rgba(100, 180, 255, 0.8)",t.style.borderBottom="2px solid rgba(100, 180, 255, 0.8)",t.style.boxSizing="border-box",t.style.cursor="grab",t.style.pointerEvents="auto",t.style.touchAction="none",this.selectionOverlayContainer.appendChild(t);const i=()=>{const e=document.createElement("div");return e.style.position="absolute",e.style.top="0px",e.style.height="100%",e.style.width="8px",e.style.cursor="ew-resize",e.style.pointerEvents="auto",e.style.touchAction="none",e.style.background="rgba(100, 180, 255, 0.9)",this.selectionOverlayContainer.appendChild(e),e},s=e.startTime===-1/0?null:i(),n=e.endTime===1/0?null:i();return this.wireSelectionDrag(e,t,s,n),{range:e,body:t,startHandle:s,endHandle:n}}wireSelectionDrag(e,t,i,s){const n=(e,t)=>{e.addEventListener("pointerdown",i=>{i.stopPropagation(),i.preventDefault(),e.setPointerCapture(i.pointerId);const s=t(i),n=e=>{s(e),this.renderSelectionOverlay(),this.emit("selectionchange",{ranges:this.getSelection(),phase:"drag"})};let l=!1;const a=t=>{l||(l=!0,e.hasPointerCapture(t.pointerId)&&e.releasePointerCapture(t.pointerId),e.removeEventListener("pointermove",n),e.removeEventListener("pointerup",a),e.removeEventListener("pointercancel",a),e.removeEventListener("lostpointercapture",a),this.emit("selectionchange",{ranges:this.getSelection(),phase:"end"}))};this.emit("selectionchange",{ranges:this.getSelection(),phase:"start"}),e.addEventListener("pointermove",n),e.addEventListener("pointerup",a),e.addEventListener("pointercancel",a),e.addEventListener("lostpointercapture",a)})};i&&n(i,()=>t=>{const{t:i}=this.mouseInSlider(t);e.startTime=e.endTime===1/0?i:Math.min(i,e.endTime)}),s&&n(s,()=>t=>{const{t:i}=this.mouseInSlider(t);e.endTime=e.startTime===-1/0?i:Math.max(i,e.startTime)}),n(t,t=>{const i=this.mouseInSlider(t).t,s=e.startTime,n=e.endTime;return t=>{const l=this.mouseInSlider(t).t-i;s===-1/0&&n!==1/0?e.endTime=n+l:n===1/0&&s!==-1/0?e.startTime=s+l:s!==-1/0&&n!==1/0&&(e.startTime=s+l,e.endTime=n+l)}})}startSelectingRange(e){this.selectingRange=!0,this.whenDoneSelectingRange=e}stopSelectingRange(){this.selectingRange=!1,this.whenDoneSelectingRange?.(),this.whenDoneSelectingRange=void 0,this.selectingRangeVisible&&(this.container.removeChild(this.selectingRangeElement),this.selectingRangeVisible=!1)}syncMaxValues(){return this.updateAllValueRanges(),this.on("time",()=>this.updateAllValueRanges())}syncMaxValuesForLayer(e){return this.updateValueRanges(e.yAxisGroup),e.model.on(()=>this.updateValueRanges(e.yAxisGroup))}updateAllValueRanges(){this.layers.filter(e=>e.visible).forEach(e=>{this.updateValueRanges(e.yAxisGroup,!0)}),this.renderYAxis()}updateValueRanges(e,t){this.updateValueRangesQueue.has(e)||this.updateValueRangesQueue.set(e,{lastUpdate:0,pending:void 0});const i=this.updateValueRangesQueue.get(e);if(Date.now()-i.lastUpdate<100)return i.pending&&clearTimeout(i.pending),void(i.pending=setTimeout(()=>{i.pending=void 0,this.updateValueRanges(e)},200));i.lastUpdate=Date.now();const s=new Map,n=new Map;this.layers.filter(e=>e.visible).forEach(e=>{const t=e.yAxis.group||e.id;s.has(t)||(s.set(t,-Number.MAX_VALUE),n.set(t,Number.MAX_VALUE));const i=e.model.getValueRange(this.getTimeRange());i.minValue<n.get(t)&&n.set(t,i.minValue),i.maxValue>s.get(t)&&s.set(t,i.maxValue)}),this.valueRanges.clear(),s.forEach((e,t)=>{this.valueRanges.set(t,{max:e,min:n.get(t)})}),t||this.renderYAxis(),this.emit("valueRanges",e)}renderYAxis(){const e=new Map;this.layers.filter(e=>e.visible).forEach(t=>{const i=t.yAxis.group||t.id;e.has(i)||e.set(i,{min:t.min,max:t.max,group:i}),e.set(i,{...e.get(i),...t.yAxis,min:t.min,max:t.max})});const t=[];e.forEach(e=>{if(e.visible){const i=this.valueRanges.get(e.group);i&&t.push({...e,minValue:i.min,maxValue:i.max})}}),this.yAxisTicks.render(t)}setTheme(e){this.theme=e,this.ticksBar.setDarkMode("dark"===e),this.engine?.setTheme(e)}addLayerLoader(e){this.loaders.add(e)}showStats(e){this.showingStats=e,this.engine?.showStats(e)}get engineReady(){return this._engineReady}whenReady(){return this._engineReady?Promise.resolve():new Promise(e=>{const t=this.on("ready",()=>{t(),e()})})}async setEngine(e){this._engineReady=!1,this.engine&&(this.layerUnloaders.forEach(e=>e()),this.layerUnloaders.clear(),this.engine.dispose()),this.initSliderContainer(),this.engine=e,this.engine.setTheme(this.theme),await this.engine.init(this.sliderElement),this.engine.setTheme(this.theme),this._engineReady=!0,this.emit("ready"),this.getVisibleLayers().forEach(e=>this.loadLayer(e)),this.engine.showStats(this.showingStats),this.engine.setTimeRange(this.getTimeRange()),this.engine.setValueRange(this.getValueRange()),this.engine.setSize(this.size.width,this.size.height)}initSliderContainer(){this.stopControls&&this.stopControls(),this.container?.removeChild(this.sliderElement);let e=0;this.showTicks&&(e+=24),this.showCurrent&&(e+=4),this.sliderElement=document.createElement("div"),this.sliderElement.oncontextmenu=()=>!1,this.sliderElement.tabIndex=0,this.sliderElement.style.top="0px",this.sliderElement.style.width="100%",this.sliderElement.style.outline="none",this.sliderElement.style.touchAction="none",this.sliderElement.style.height=`calc(100% - ${0+e}px)`,this.container?.appendChild(this.sliderElement),this.initSelectLabel(),this.stopControls=this.initControls(),this.onDestroy.push(()=>{this.container?.removeChild(this.sliderElement)})}get showCurrent(){return this._showCurrent}set showCurrent(e){if(e!==this._showCurrent){this._showCurrent=e,this.ticksBar.showLine=!this.showCurrent;let t=0,i=0;this.showTicks&&(i+=24),this.showCurrent&&(i+=4),this.sliderElement.style.height=`calc(100% - ${t+i}px)`}}async init(e,t){const i="string"==typeof e?document.getElementById(e):e;if(this.container=i,i){i.oncontextmenu=()=>!1;let e=0,s=0;if(this.showTicks){s+=24;const e=document.createElement("div");e.style.width="100%",e.style.height="24px",e.style.position="absolute",e.style.bottom="0px",e.style.touchAction="none",i.appendChild(e),this.ticksBar.init(e,e=>{const t=r(e.startTime,e.endTime);this.emit("tickclick",{startTime:e.startTime,endTime:e.endTime,unit:t}),this.fit({range:e,animate:1e3})}),this.ticksBar.showLine=!this.showCurrent}this.showCurrent&&(s+=4),this.sliderElement.oncontextmenu=()=>!1,this.sliderElement.tabIndex=0,this.sliderElement.style.top=`${e}px`,this.sliderElement.style.width="100%",this.sliderElement.style.outline="none",this.sliderElement.style.touchAction="none",this.sliderElement.style.height=`calc(100% - ${e+s}px)`,i.appendChild(this.sliderElement),this.yAxisTicks.canvas.style.top=`${e}px`,this.yAxisTicks.canvas.style.height=`calc(100% - ${e+s}px)`,i.appendChild(this.yAxisTicks.canvas),this.selectionOverlayContainer.style.position="absolute",this.selectionOverlayContainer.style.width="100%",this.selectionOverlayContainer.style.overflow="hidden",this.selectionOverlayContainer.style.pointerEvents="none",this.selectionOverlayContainer.style.top=`${e}px`,this.selectionOverlayContainer.style.height=`calc(100% - ${e+s}px)`,i.appendChild(this.selectionOverlayContainer),this.onDestroy.push(()=>i.removeChild(this.selectionOverlayContainer)),this.columnBandOverlayContainer.style.position="absolute",this.columnBandOverlayContainer.style.width="100%",this.columnBandOverlayContainer.style.overflow="hidden",this.columnBandOverlayContainer.style.pointerEvents="none",this.columnBandOverlayContainer.style.top=`${e}px`,this.columnBandOverlayContainer.style.height=`calc(100% - ${e+s}px)`,i.appendChild(this.columnBandOverlayContainer),this.onDestroy.push(()=>i.removeChild(this.columnBandOverlayContainer)),this.initHoverTooltip(),this.initSelectLabel(),this.setSize(this.sliderElement.clientWidth,this.sliderElement.clientHeight),this.syncSize(),this.setTimeRange($(t)),this.setValueRange($(t))}else console.warn(`Element ${e} not found`)}initSelectLabel(){this.selectedLabel.style.position="absolute",this.selectedLabel.style.padding="4px 8px",this.selectedLabel.style.background="rgba(0, 0, 0, 0.75)",this.selectedLabel.style.color="white",this.selectedLabel.style.fontSize="12px",this.selectedLabel.style.maxWidth="200px",this.selectedLabel.style.fontFamily="monospace",this.selectedLabel.style.borderRadius="4px",this.selectedLabel.style.pointerEvents="none",this.selectedLabel.style.display="none",this.selectedLabel.style.zIndex="10",this.sliderElement.appendChild(this.selectedLabel)}getLabel(e,t,i,s){if(t.labeler){const n=t.labeler(e,i,s);return null===n?null:"string"==typeof n?`<div>${n}</div>`:n?n.outerHTML:`<div>${e.name||e.id}</div>`}return`<div>${e.name||e.id}</div>`}getLayerById(e){return this.layers.find(t=>t.id===e)}select(e,t){this.stopSelectingRange(),this.stopUpdatingLabel?.();const i=()=>{this.updatingLabel=!1,this.stopUpdatingLabel?.(),this.stopUpdatingLabel=void 0,this.selectedLabel.style.display="none",this.layers.forEach(e=>e.select([])),this.selectedLayerId=void 0,this.emit("select",[])},s=e=>{this.layers.filter(t=>!e.some(e=>t.id===e.layer.id)).forEach(e=>e.select([])),e.forEach(e=>{e.layer.select(e.elements)}),this.emit("select",e)};if(t){s(this.getVisibleLayers().map(e=>{const i=e.model.dataInTimeRange(t);return{layer:e,elements:i}}).filter(e=>e.elements.length>0))}else if(e){const t=this.findElements(e,24);if(s(t.map(e=>({layer:e.layer,elements:e.elements})).flat()),1===t.length&&t[0].elements.length>0&&this.selectionOptions.label){const e=t[0].elements[0],s=t[0].layer;this.selectedLabel.innerHTML=this.getLabel(e,s,t[0].t,t[0].v)||"",this.selectedLabel.style.display="block";const n=this.selectedLabel.offsetWidth,l=this.selectedLabel.offsetHeight;this.selectedLayerId=s.id;const a=Array.isArray(e.t);this.updatingLabel=!0;const o=()=>{if(!this.updatingLabel)return;const{startTime:i,duration:s,endTime:o}=this.getTimeRange(),r=t[0].layer.model.get(e.id);if(r){const e=f(this.getTimeRange(),r.t);if(e){const o=this.getSliderBounds();let h=(.5*(e.startTime+e.endTime)-i)/s*o.width;const d="number"==typeof r.v?r.v:.5*(r.v[1]+r.v[0]);let m=this.modelValueToPixels(d,t[0].layer);a?m-=.5*l:m+=8;const c=.5*n,g=.5*l,u=o.width-.5*n,p=o.height-.5*l,y=Math.min(u,Math.max(c,h)),v=Math.min(p,Math.max(g,m));this.selectedLabel.style.display="block",this.selectedLabel.style.left=y-.5*n+"px",this.selectedLabel.style.top=`${v}px`}else this.selectedLabel.style.display="none"}else this.selectedLabel.style.display="none"};o();const r=s instanceof x&&s.histogram instanceof m;this.stopUpdatingLabel=this.on("time",r?i:o)}}else i()}listenToClicks(){let e=null;const t=t=>{e={x:t.clientX,y:t.clientY}},i=t=>{if(!e)return;const i=t.clientX-e.x,s=t.clientY-e.y;i*i+s*s<=25&&(this.stopSelectingRange(),this.select(e)),e=null},s=()=>{e=null};return this.sliderElement.addEventListener("pointerdown",t),this.sliderElement.addEventListener("pointerup",i),this.sliderElement.addEventListener("pointercancel",s),()=>{this.sliderElement.removeEventListener("pointerdown",t),this.sliderElement.removeEventListener("pointerup",i),this.sliderElement.removeEventListener("pointercancel",s)}}getSliderBounds(){return this.sliderElement.getBoundingClientRect()}mouseInSlider(e){const t=this.getSliderBounds(),i="x"in e?e:{x:e.clientX,y:e.clientY},s={x:i.x-t.left,y:i.y-t.top},n={x:s.x/t.width,y:-s.y/t.height+1},{startTime:l,duration:a}=this.getTimeRange(),o=l+n.x*a,{minValue:r,maxValue:h}=this.getValueRange();return{absolute:s,normalized:n,t:o,v:r+n.x*(h-r)}}pixelsToLayerValue(e,t){if(!e)return{yInLayer:0,toLayerHeight:1};const i="normalized"in e?e.normalized:this.mouseInSlider(e).normalized;let s=t.min;const n=t.max-s;return{yInLayer:(i.y-s)/n,toLayerHeight:n/this.getSliderBounds().height}}pixelsToModelValue(e,t){const{yInLayer:i,toLayerHeight:s}=this.pixelsToLayerValue(e,t);let n=0,l=100;if(t.model instanceof g){const e=this.valueRanges.get(t.yAxisGroup)||{min:0,max:1e3};n=e.min,l=e.max-n}return{yInModel:n+i*l,toModelHeight:s*l}}layerValueToPixels(e,t){let i=t.min;return(1-(i+(t.max-i)*e))*this.getSliderBounds().height}modelValueToPixels(e,t){let i=0,s=100;if(t.model instanceof g){const e=t.model.getValueRange(this.getTimeRange());i=e.minValue,s=e.maxValue-i}const n=(e-i)/s;return this.layerValueToPixels(n,t)}findElements(e,t){if("column"===this.selectionOptions.pick)return this.findColumnElements(e);const{width:i,height:s}=this.getSliderBounds(),{normalized:n}=this.mouseInSlider(e),{startTime:l,endTime:a,duration:o}=this.getTimeRange(),r=l+n.x*o,h=t||12,d=h/i*o,m={startTime:r-1,endTime:r+1},c={startTime:r-.5*d,endTime:r+.5*d};return this.getVisibleLayers().map(e=>{const{yInModel:t,toModelHeight:i}=this.pixelsToModelValue({normalized:n},e),s=e instanceof R?0:h*i,l=e instanceof R?m:c,a={minValue:t-.5*s,maxValue:t+.5*s},o=e.model.dataInRange({...l,...a});return{layer:e,elements:o,t:r,v:t}}).filter(e=>e.elements.length>0)}findColumnElements(e,t){const{width:i}=this.getSliderBounds(),{normalized:s}=this.mouseInSlider(e),{startTime:n,duration:l}=this.getTimeRange(),a=n+s.x*l,o=(t||12)/i*l,r={startTime:a-1,endTime:a+1},h={startTime:a-.5*o,endTime:a+.5*o};return this.getVisibleLayers().map(e=>{const{yInModel:t}=this.pixelsToModelValue({normalized:s},e),i=e instanceof R?r:h,n=e.model.dataInTimeRange(i);return{layer:e,elements:n,t:a,v:t}}).filter(e=>e.elements.length>0)}clearHoverEvent(){this.hoverCleared||(this.hoverCleared=!0,this.renderColumnHoverBand([]),this.emit("hover",null))}updateHover(e){if(this.dragging)return this.hoverLine.style.display="none",this.toolTip.style.display="none",void this.clearHoverEvent();if(void 0!==e&&(this.mouse=e),this.mouse){const e=this.getSliderBounds(),{normalized:t}=this.mouseInSlider(this.mouse),{startTime:i,endTime:n,duration:l}=this.getTimeRange(),a=i+t.x*l,o=this.findElements(this.mouse);this.renderColumnHoverBand(o),this.hoverCleared=!1,this.emit("hover",{elements:o.map(e=>({layer:e.layer,elements:e.elements})),t:a,clientX:this.mouse.x,clientY:this.mouse.y});const r=o.map(e=>e.elements.map(t=>({e:t,l:e.layer,t:e.t,v:e.v}))).flat();let h;if(r.length>0)if(r.length>3){h=r.slice(0,2).map(e=>this.getLabel(e.e,e.l,e.t,e.v)).flat().filter(e=>null!==e).join(""),h+=`<div>And ${r.length-2} more...</div>`}else{h=o.map(e=>e.elements.map(t=>this.getLabel(t,e.layer,e.t,e.v))).flat().filter(e=>null!==e).join("")}if(!1===this.hoverOptions.tooltip)this.toolTip.style.display="none";else{const t=new Date(a),i=t.getHours(),n=t.getMinutes(),l=t.getSeconds(),o=s(a,!0,this._locale),r=`${(i<10?"0":"")+i}:${(n<10?"0":"")+n}:${(l<10?"0":"")+l}`;let d="";const m=this.ticksBar.cache.high.spacing;(void 0===this.hoverOptions.date?m>=V:this.hoverOptions.date)&&(d+=`<div>${o}</div>`),(void 0===this.hoverOptions.time?m<=C:this.hoverOptions.time)&&(d+=`<div>${r}</div>`),h&&(d+=h),this.toolTip.innerHTML=d,this.toolTip.style.display=d.length>0?"block":"none";const c=88,g=36;let u=this.mouse.x+10,p=this.mouse.y+10;const y=e.left+e.width-c-5,v=e.top+e.height-g-5;u>y&&(u=y),p>v&&(p=v),this.toolTip.style.left=`${u}px`,this.toolTip.style.top=`${p}px`}}else this.hoverLine.style.display="none",this.toolTip.style.display="none",this.clearHoverEvent()}initHoverTooltip(){this.toolTip.style.position="absolute",this.toolTip.style.padding="4px 8px",this.toolTip.style.background="rgba(0, 0, 0, 0.75)",this.toolTip.style.color="white",this.toolTip.style.fontSize="12px",this.toolTip.style.fontFamily="monospace",this.toolTip.style.borderRadius="4px",this.toolTip.style.pointerEvents="none",this.toolTip.style.display="none",this.toolTip.style.zIndex="10",this.toolTip.style.overflow="hidden",document.body.appendChild(this.toolTip)}initHoverListener(){const e=()=>{this.updateHover(null)},t=e=>{this.updateHover({x:e.clientX,y:e.clientY})};this.sliderElement.addEventListener("mouseleave",e),this.sliderElement.addEventListener("mousemove",t);const i=this.listenToClicks();return()=>{this.sliderElement.removeEventListener("mouseleave",e),this.sliderElement.removeEventListener("mousemove",t),i()}}syncSize(){const e=()=>{const{offsetWidth:e,offsetHeight:t}=this.sliderElement;this.setSize(e,t)},t=new ResizeObserver(()=>{e()});this.container&&(t.observe(this.container),e()),this.onDestroy.push(()=>{t.disconnect()})}getTimeRange(){return u(this.range.time)}getValueRange(){return this.range.value}setTimeRange(e,t){const i=this.getTimeRange(),s=y(i,e),n=this._limits?v(s,this._limits):s;n.startTime===i.startTime&&n.endTime===i.endTime||(this.range.time=n,this.engine?.setTimeRange(n),this.showTicks&&this.ticksBar.setTimeRange(n),this.emit("time",n),this.updateHover())}setValueRange(e){const t={...this.getValueRange(),...e};this.range.value=t,this.engine?.setValueRange(t),this.emit("value",this.getValueRange()),this.updateHover()}setSize(e,t){this.size={width:e,height:t},this.showTicks&&this.ticksBar.setWidth(e);const i=this.getTimeRange(),s=this.getValueRange();this.engine?.setSize(e,t),this.setTimeRange(i),this.setValueRange(s),this.updateHover(),this.emit("size",{width:e,height:t})}up(e){const{startTime:t,endTime:i,duration:s}=this.getTimeRange();let n={startTime:0,endTime:Date.now()};const l=.5*(t+i);if(s>=.9*S){const e=U(10*s);n=o(l,e)}else{const e=[{threshold:b,unit:"millennium"},{threshold:w,unit:"century"},{threshold:z,unit:"decade"},{threshold:.9*M,unit:"year"},{threshold:D,unit:"month"},{threshold:C,unit:"week"},{threshold:V,unit:"day"},{threshold:k,unit:"hour"},{threshold:B,unit:"minute"},{threshold:-1,unit:"second"}];let t=0;for(;s<.9*e[t].threshold;)t++;const i=e[t].unit;n=h(l,i)}this.fit({...e,range:n})}down(e){const{startTime:t,endTime:i,duration:s}=this.getTimeRange();let n={startTime:0,endTime:Date.now()};const l=t+("number"==typeof e?.xRatio?e.xRatio:.5)*s;if(s>=.9*S){const e=U(s/10);n=o(l,e)}else{let e=0;const t=[{threshold:S,unit:"century"},{threshold:b,unit:"decade"},{threshold:w,unit:"year"},{threshold:z,unit:"month"},{threshold:1.1*M,unit:"week"},{threshold:D,unit:"day"},{threshold:C,unit:"hour"},{threshold:V,unit:"minute"},{threshold:k,unit:"second"}].reverse();for(;s>1.1*t[e].threshold;)e++;const i=t[e].unit;n=h(l,i)}this.fit({...e,range:n})}next(e){this.skip(1,e)}previous(e){this.skip(-1,e)}skip(e,t){let i={startTime:0,endTime:Date.now()};const{startTime:s,endTime:n,duration:l}=this.getTimeRange(),a=.5*(s+n);if(l>=9*S){const e=U(l);i=o(a,e)}else{const t=r(s,n);i=t?h(a,t,Math.round(e)):{startTime:s+e*l,endTime:n+e*l}}this.fit({...t,range:i})}getTimeRangeLabel(){const{startTime:e,endTime:t}=this.getTimeRange(),i=new Date(e);return 0===i.getHours()&&0===i.getMinutes()&&0===i.getSeconds()&&0===i.getMilliseconds()?n([e,t],this._locale):void 0}cancelFit(){this.stopFit?.(),this.stopFit=void 0}fit(e){this.cancelFit();const t=e?.range||this.getLayersTimeRange(),s=e?.padding?p(t,e.padding):t;if(e?.animate){const t=this.getTimeRange();this.stopFit=i(t.startTime,t.endTime,s.startTime,s.endTime,e.animate,(e,t)=>this.setTimeRange({startTime:e,endTime:t}))}else this.setTimeRange(s)}getLayersTimeRange(){if(0===this.getVisibleLayers().length)return I;{let e=this.getVisibleLayers()[0].model.getTimeRange();return this.getVisibleLayers().slice(1).forEach(t=>e=E(e,t.model.getTimeRange())),e}}loadLayer(e){if(e.timeSlider&&e.timeSlider.id!==this.id)console.warn("Layer already has been loaded by another time slider, create a new layer with the same model and style if you want to add it to another time slider");else if(this.engine&&this._engineReady&&!this.layerUnloaders.has(e.id)){const t=[...this.loaders].filter(e=>this.engine instanceof e.engineClass).find(t=>t.canLoad(e));if(t){const i=Date.now(),s=t.load(e,this.engine.scene,this.engine,this);e.timeSlider=this,e.loaded=!0;const n=e.name||e.constructor.name+"#"+e.id.slice(e.id.length-4).toUpperCase();console.log(`Loaded ${n} in ${l(Date.now()-i)}`);const a=this.syncMaxValuesForLayer(e);this.layerLoaders.set(e.id,t),this.layerUnloaders.set(e.id,()=>{a(),s(),this.layerLoaders.delete(e.id),e.loaded=!1})}else console.warn("No loader found for",{layer:e})}}unloadLayer(e){this.selectedLayerId===e.id&&this.select(null),this.layerUnloaders.has(e.id)&&(this.layerUnloaders.get(e.id)(),this.layerUnloaders.delete(e.id))}addLayer(e){if(this.layers.push(e),e.visible){this.loadLayer(e);const t=e.model.getTimeRange();if(1===this.getVisibleLayers().length)this.setTimeRange(t);else{const e=E(this.getTimeRange(),t);this.setTimeRange(e)}}const t=[e.on("visible",()=>{e.visible?this.loadLayer(e):this.unloadLayer(e),this.updateAllValueRanges()}),e.on("min",()=>this.onLayerBandChange(e)),e.on("max",()=>this.onLayerBandChange(e))];this.layerListenerStoppers.set(e.id,()=>t.forEach(e=>e()))}onLayerBandChange(e){const t=this.layerLoaders.get(e.id);t&&!t.followsLayerBand&&(this.layerUnloaders.get(e.id)?.(),this.layerUnloaders.delete(e.id),this.loadLayer(e)),this.updateAllValueRanges()}removeLayer(e){const t="string"==typeof e?e:e.id,i=this.layers.find(e=>e.id===t);i&&(this.unloadLayer(i),this.layerUnloaders.delete(t),this.layerListenerStoppers.get(t)?.(),this.layerListenerStoppers.delete(t),this.layers=this.layers.filter(e=>e.id!==t))}destroy(){this.layers.map(e=>e.id).forEach(e=>this.removeLayer(e)),this.onDestroy.forEach(e=>e())}addHorizontalPinchZoom(){let e=null,t=null;const i=()=>{this.pinching=!1,e=null,t=null},s=i=>{if(2===i.touches.length){i.preventDefault(),this.dragging=!1,this.pinching=!0;const s=this.mouseInSlider(i.touches[0]).t,n=this.mouseInSlider(i.touches[1]).t;e=Math.min(s,n),t=Math.max(s,n)}},n=s=>{if(2===s.touches.length){if(null===e||null===t)return;s.preventDefault(),this.dragging=!1;const i=this.mouseInSlider(s.touches[0]).normalized.x,n=this.mouseInSlider(s.touches[1]).normalized.x,l=Math.min(i,n),a=Math.max(i,n),o=(t-e)/(a-l);let r=e-l*o,h=t+(1-a)*o;if(o>_){const e=.5*(r+h);r=e-.5*_,h=e+.5*_}if(o<H){const e=.5*(r+h);r=e-.5*H,h=e+.5*H}this.setTimeRange({startTime:r,endTime:h})}else i()},l=()=>{i()};this.sliderElement.addEventListener("touchstart",s,{passive:!1}),this.sliderElement.addEventListener("touchmove",n,{passive:!1}),this.sliderElement.addEventListener("touchend",l),this.sliderElement.addEventListener("touchcancel",l),this.onDestroy.push(()=>{this.sliderElement.removeEventListener("touchstart",s),this.sliderElement.removeEventListener("touchmove",n),this.sliderElement.removeEventListener("touchend",l),this.sliderElement.removeEventListener("touchcancel",l)})}initControls(){const e=new t;this.dragging=!1;let i,s=0,n=0,l=0;e.add(this.initHoverListener());const a=e=>{this.sliderElement.focus(),e.preventDefault();const t=this.mouseInSlider(e);this.zoom(.001*e.deltaY,{x:t.normalized.x})};let o=0,r=-1;const h=e=>{const t=Date.now(),i=t-o;r>=0&&i<300&&i>0&&(this.select(null),e.preventDefault(),this.zoom(-.75,{animate:1e3,x:r})),o=t},d=e=>{e.preventDefault(),this.select(null);const t=this.mouseInSlider(e);this.zoom(-.75,{animate:1e3,x:t.normalized.x})};this.selectingRangeElement.style.height="calc(100% - 6px)",this.selectingRangeElement.style.pointerEvents="none",this.selectingRangeElement.style.position="absolute",this.selectingRangeElement.style.border="3px solid #df0",this.selectingRangeElement.style.background="#df04",this.selectingRangeElement.style.top="0px";const m=e=>{if(this.sliderElement.focus(),2===e.button||0===e.button){e.preventDefault();const t=this.mouseInSlider(e),i=t.normalized.x,{startTime:a,duration:o}=this.getTimeRange();s=a+i*o,n=Date.now(),l=t.absolute.x,(e.ctrlKey||2===e.button)&&(this.selectingRange=!0),this.selectingRange?(this.selectingRangeElement.style.left=t.absolute.x+"px",this.selectingRangeElement.style.width="0px",this.container.appendChild(this.selectingRangeElement),this.selectingRangeVisible=!0):this.dragging=!0}},c=e=>{const t=this.mouseInSlider(e);return i=t.normalized.x,t},g=e=>{const t=c(e);if(this.selectingRange){const e=Math.abs(l-t.absolute.x),i=Math.min(l,t.absolute.x);this.selectingRangeElement.style.left=i+"px",this.selectingRangeElement.style.width=e-6+"px"}if(!this.dragging||Date.now()-n<100&&Math.abs(l-t.absolute.x)<10)return;e.preventDefault();const i=t.normalized.x,{startTime:a,endTime:o}=this.getTimeRange(),r=a+i*(o-a)-s;this.setTimeRange({startTime:a-r,endTime:o-r})},u=e=>{const t=this.getSliderBounds(),n=t.height-(e.clientY-t.top);if(!this.dragging&&!this.pinching&&n<8)this.ticksBar.click(e);else if(i=void 0,this.dragging=!1,this.selectingRange){const t=this.mouseInSlider(e).normalized.x,{startTime:i,endTime:n}=this.getTimeRange(),l=i+t*(n-i);"zoom"===this.selectRangeMode&&this.fit({range:{startTime:Math.min(s,l),endTime:Math.max(s,l)},animate:1e3}),"select"===this.selectRangeMode&&this.select(null,{startTime:Math.min(s,l),endTime:Math.max(s,l)}),"createSelection"===this.selectRangeMode&&this.setSelection([...this.getSelection(),{startTime:Math.min(s,l),endTime:Math.max(s,l)}])}this.stopSelectingRange()},p=e=>{this.pressedKeys.add(e.code)},y=e=>{this.pressedKeys.delete(e.code),"Space"===e.code&&(e.preventDefault(),this.togglePlaying()),"KeyF"===e.code&&(e.preventDefault(),this.fit({animate:3e3}));const t=void 0;"ArrowRight"===e.code&&(e.preventDefault(),this.skip(1,{animate:t})),"ArrowLeft"===e.code&&(e.preventDefault(),this.skip(-1,{animate:t})),"ArrowUp"===e.code&&(e.preventDefault(),this.up({animate:t})),"ArrowDown"===e.code&&(e.preventDefault(),this.down({xRatio:i,animate:t}))};let v=!0;const T=()=>{if(v){const e=this.pressedKeys.has("ShiftLeft")?.05:.01,t=2*e,s=this.pressedKeys.has("KeyD"),n=this.pressedKeys.has("KeyA");s&&this.shift(-e),n&&this.shift(e),this.pressedKeys.has("KeyW")&&this.zoom(-t,{x:s||n?void 0:i}),this.pressedKeys.has("KeyS")&&this.zoom(t,{x:s||n?void 0:i}),requestAnimationFrame(T)}};T(),e.add(()=>v=!1);const f=e=>e.preventDefault();this.sliderElement.addEventListener("contextmenu",f),this.sliderElement.addEventListener("wheel",a),this.sliderElement.addEventListener("pointerdown",m),this.sliderElement.addEventListener("pointermove",g),this.sliderElement.addEventListener("pointerup",u),this.sliderElement.addEventListener("dblclick",d),this.sliderElement.addEventListener("touchend",h),this.sliderElement.addEventListener("touchstart",e=>{if(1===e.touches.length){const t=this.mouseInSlider(e.touches[0]);r=t.normalized.x}else r=-1}),this.sliderElement.addEventListener("keydown",p),this.sliderElement.addEventListener("keyup",y),this.addHorizontalPinchZoom();const E=()=>{this.sliderElement.removeEventListener("contextmenu",f),this.sliderElement.removeEventListener("wheel",a),this.sliderElement.removeEventListener("pointerdown",m),this.sliderElement.removeEventListener("pointermove",g),this.sliderElement.removeEventListener("pointerup",u),this.sliderElement.removeEventListener("dblclick",d),this.sliderElement.removeEventListener("touchend",h),this.sliderElement.removeEventListener("keydown",p),this.sliderElement.removeEventListener("keyup",y)};return this.onDestroy.push(E),e.add(E),e.disposeLater()}shift(e,t){const{startTime:i,endTime:s,duration:n}=this.getTimeRange(),l=t?.absolute?e:e*n;this.fit({...t,range:{startTime:i-l,endTime:s-l}})}zoom(e,t){const i="number"==typeof t?.x?t.x:.5,{startTime:s,endTime:n,duration:l}=this.getTimeRange(),a={startTime:s-e*l*i,endTime:n+e*l*(1-i)},o=a.endTime-a.startTime,r=a.startTime+.5*o;o>_&&(a.startTime=r-.5*_,a.endTime=r+.5*_),o<H&&(a.startTime=r-.5*H,a.endTime=r+.5*H),this.fit({range:a,animate:t?.animate})}togglePlaying(){this.playing?this.pause():this.play()}isPlaying(){return this.playing}play(){if(this.playing)return;this.playing=!0,this.emit("playing",!0);let e=Date.now();const t=()=>{if(this.playing){if(this.live){const{duration:e}=this.getTimeRange(),t=Date.now(),i=10/this.size.width;this.setTimeRange({startTime:t-(1-i)*e,endTime:t+i*e}),this.setCurrent(t)}else if("auto"!==this.playSpeed||this.showCurrent){const t=Date.now(),i=t-e,s=this.current-this.getTimeRange().startTime,n="auto"===this.playSpeed?1:this.playSpeed;this.shift(-n*i,{absolute:!0}),this.setCurrent(this.getTimeRange().startTime+s),e=t}else{const t=Date.now();let i=5e-5*(t-e);this.pressedKeys.has("ShiftLeft")&&(i*=5);const s=this.current-this.getTimeRange().startTime;this.shift(-i),this.setCurrent(this.getTimeRange().startTime+s),e=t}requestAnimationFrame(t)}};t()}setCurrent(e){this.current!==e&&(Math.abs(e-Date.now())>100&&this.live&&(this.pause(),this.setLive(!1)),this.current=e,this.emit("current",e))}pause(){this.playing&&(this.playing=!1,this.emit("playing",!1),this.setLive(!1))}setLive(e){if(this.live!==e){if(this.live=e,e){const e=this.getTimeRange();this.fit({range:{startTime:e.endTime-this.liveRange,endTime:e.endTime},animate:B}),this.play()}this.emit("live",e)}}}function $(e){return{startTime:"number"==typeof e?.startTime?e.startTime:"number"==typeof e?.endTime?e.duration?e.endTime-e.duration:e.endTime-C:e?.duration?Date.now()-.5*e.duration:Date.now()-.5*C,endTime:"number"==typeof e?.endTime?e.endTime:"number"==typeof e?.startTime?e.duration?e.startTime+e.duration:e.startTime+C:e?.duration?Date.now()+.5*e.duration:Date.now()+.5*C,duration:e?.duration?e.duration:"number"==typeof e?.startTime&&"number"==typeof e?.endTime?e.endTime+e.startTime:C,minValue:"number"==typeof e?.minValue?e.minValue:"number"==typeof e?.maxValue?e.maxValue>0?0:e.maxValue-100:0,maxValue:"number"==typeof e?.maxValue?e.maxValue:"number"==typeof e?.minValue?e.minValue<0?0:e.minValue+100:100}}function U(e){const t=e/315576e5,i=Math.log10(t),s=Math.ceil(i);return Math.pow(10,s)}
1
+ import{v4 as e}from"uuid";import{Disposables as t}from"../common/Disposables.js";import{animateRangeTransition as i}from"../common/Easing.js";import{describeTimeSpan as s,formatDate as n,formatDateRange as l,formatDuration as o,formatTimeSpanDates as a,getDefaultLocale as r,getLargeTimeUnitRange as h,getMatchingTimeUnit as d,getTimeUnitRange as m,setDefaultLocale as c}from"../common/TimeTools.js";import{TimeSliderDynamicHistogram as u}from"../data/histogram/TimeSliderDynamicHistogram.js";import{getTimeArray as g}from"../data/TimeSliderElement.js";import{TimeSliderIntervalsModel as p}from"../data/TimeSliderIntervalsModel.js";import{addDuration as y,addPadding as T,applyTimeRangeUpdate as v,clampTimeRange as f,mergeTimeRanges as E,timeRangesOverlap as x,timeRangesUnion as L}from"../data/TimeSliderRange.js";import{TypedEventEmitter as b}from"../event/TypedEventEmitter.js";import{TimeSliderHistogramLayer as R}from"../layer/TimeSliderHistogramLayer.js";import{TimeSliderIntervalsLayer as w}from"../layer/TimeSliderIntervalsLayer.js";import{CENTURY as S,DECADE as k,MILLENIUM as C,ONE_DAY as V,ONE_HOUR as M,ONE_MINUTE as B,ONE_MONTH as D,ONE_SECOND as z,ONE_WEEK as _,ONE_YEAR as H,TODAY as I}from"../ticks/TimeSliderConstants.js";import{TimeTicksBar as O}from"../ticks/TimeTicksBar.js";import{YAxisTicks as A}from"../ticks/YAxisTicks.js";import{resolveFont as $,toCssFont as F}from"../common/Font.js";const U=z,P=10*C;export class TimeSlider extends b{getLayers(){return[...this.layers]}getVisibleLayers(){return this.getLayers().filter(e=>e.visible)}constructor(i){super(),this.valueRanges=new Map,this.layers=[],this.onDestroy=[],this.ticksBar=new O,this.loaders=new Set,this.layerUnloaders=new Map,this.layerListenerStoppers=new Map,this.layerLoaders=new Map,this.pressedKeys=new Set,this.container=null,this.d=new t,this.id=e(),this.playing=!1,this.live=!1,this._showCurrent=!1,this.current=Date.now(),this.range={time:I,value:{minValue:0,maxValue:100}},this.size={width:100,height:100},this.sliderElement=document.createElement("div"),this.yAxisTicks=new A,this.hoverLine=document.createElement("div"),this.toolTip=document.createElement("div"),this.selectedLabel=document.createElement("div"),this.selectingRangeElement=document.createElement("div"),this.selectionOverlayContainer=document.createElement("div"),this._selectionRanges=[],this.selectionRangeElements=[],this.shadingElements=[],this.columnBandOverlayContainer=document.createElement("div"),this.columnBandElements=[],this.selectingRange=!1,this.selectingRangeVisible=!1,this.tickTooltip=!0,this.tooltipStyle={},this.tickToolTip=document.createElement("div"),this.updateValueRangesQueue=new Map,this.showingStats=!1,this._engineReady=!1,this.mouse=null,this.updatingLabel=!1,this.hoverCleared=!0,this.dragging=!1,this.pinching=!1,this.hoverOptions=i?.hover||{},this.selectionOptions={highlight:"boolean"!=typeof i?.selection?.highlight||i.selection.highlight,label:"boolean"!=typeof i?.selection?.label||i.selection.label,shading:"boolean"!=typeof i?.selection?.shading||i.selection.shading,invertShading:"boolean"==typeof i?.selection?.invertShading&&i.selection.invertShading,pick:i?.selection?.pick||"element",columnBand:i?.selection?.columnBand},this.showCurrent=i?.showCurrent||!1,this.showTicks=i?.showTicks||!1,this.selectRangeMode=i?.selectRangeMode||"select",this.playSpeed=i?.playSpeed||"auto",this.liveRange=i?.liveRange||10*z,this.theme=i?.theme||"dark",this._locale=i?.locale||r(),c(this._locale),this.ticksBar.setLocale(this._locale),this.ticksBar.setWeekLabel(i?.weekLabel||"default",i?.weekLabelPrefix),this.ticksBar.setTickLabelStyler(i?.tickLabelStyler),this.setLimits(i?.limits),this.tickTooltip=i?.tickTooltip??!0,this.tooltipStyle=i?.tooltipStyle??{},this.ticksBar.onHoverTick=e=>this.onTickHover(e),this.setBackground(i?.background),this.setFont(i?.font),this.syncMaxValues(),this.on("time",()=>this.renderSelectionOverlay()),this.on("size",()=>this.renderSelectionOverlay())}get locale(){return this._locale}setLocale(e){this._locale=e,c(e),this.ticksBar.setLocale(e),this.emit("locale",e)}setWeekLabel(e,t){this.ticksBar.setWeekLabel(e,t)}setTickLabelStyler(e){this.ticksBar.setTickLabelStyler(e)}get font(){return this._font?{...this._font}:void 0}setFont(e){this._font=e?{...e}:void 0;const t=$(e);this.ticksBar.setFont(F(t),t.size),this.yAxisTicks.setFont(e),this.applyTooltipStyles(),this.renderYAxis(),e&&"undefined"!=typeof document&&document.fonts&&document.fonts.load(F(t)).then(()=>this.refreshFonts(),()=>{})}refreshFonts(){this.ticksBar.refreshFont(),this.renderYAxis()}watchFontLoading(){if(this.stopWatchingFonts||"undefined"==typeof document||!document.fonts)return;const e=document.fonts,t=()=>this.refreshFonts();e.addEventListener("loadingdone",t),e.ready.then(t,()=>{}),this.stopWatchingFonts=()=>e.removeEventListener("loadingdone",t),this.onDestroy.push(()=>this.stopWatchingFonts?.())}get background(){return this._background}setBackground(e){this._background=e,this.engine?.setBackground(e),this.ticksBar.setBackground(e)}setTickTooltip(e){this.tickTooltip=e,!1===e&&(this.tickToolTip.style.display="none")}setTooltipStyle(e){this.tooltipStyle={...e},this.applyTooltipStyles()}applyTooltipStyles(){this.applyTooltipStyle(this.toolTip),this.applyTooltipStyle(this.tickToolTip),this.applyTooltipStyle(this.selectedLabel,"200px")}applyTooltipStyle(e,t=""){const i=this.tooltipStyle;e.style.background=i.background??"rgba(0, 0, 0, 0.75)",e.style.color=i.color??"white",e.style.border=i.border??"none",e.style.borderRadius=i.borderRadius??"4px",e.style.padding=i.padding??"4px 8px",e.style.maxWidth=i.maxWidth??t,e.style.font=F($(i.font??this._font))}onTickHover(e){if(!e)return this.tickToolTip.style.display="none",void this.emit("tickhover",null);const t=this.ticksBar.getCanvasRect(),i=new DOMRect(t.left+e.x,t.top,e.width,t.height),s={startTime:e.startTime,endTime:e.endTime,unit:d(e.startTime,e.endTime),label:e.label,fullLabel:this.ticksBar.getFullLabel(e)};this.emit("tickhover",{...s,rect:i});const n=!1===this.tickTooltip?void 0:"function"==typeof this.tickTooltip?this.tickTooltip(s):s.fullLabel;if(!n)return void(this.tickToolTip.style.display="none");this.tickToolTip.textContent=n,this.tickToolTip.style.display="block";const l=this.tickToolTip.offsetWidth,o=this.tickToolTip.offsetHeight,a=Math.min(Math.max(4,i.left+.5*i.width-.5*l),window.innerWidth-l-4),r=i.top-o-6;this.tickToolTip.style.left=`${a}px`,this.tickToolTip.style.top=`${r>=4?r:i.bottom+6}px`}get limits(){return this._limits?{...this._limits}:void 0}setLimits(e){!e||e.endTime>e.startTime||(console.warn("Ignoring time slider limits whose endTime is not after startTime",e),e=void 0),this._limits=e?{startTime:e.startTime,endTime:e.endTime}:void 0,this._limits&&this.setTimeRange(this.getTimeRange())}setSelection(e){this._selectionRanges=e.map(e=>({...e})),this.renderSelectionOverlay(!0),this.emit("selectionchange",{ranges:this.getSelection(),phase:"end"})}getSelection(){return this._selectionRanges.map(e=>({...e}))}clearSelection(){this.setSelection([])}setPickMode(e){this.selectionOptions.pick=e,this.updateHover()}selectionTimeToX(e,t,i,s){return e===-1/0?0:e===1/0?s:Math.min(s,Math.max(0,(e-t)/(i-t)*s))}renderSelectionOverlay(e=!1){if(!this.container)return;const t=this.getSliderBounds();if(t.width<=0)return;const{startTime:i,endTime:s}=this.getTimeRange(),n=e=>this.selectionTimeToX(e,i,s,t.width);if((e||this.selectionRangeElements.length!==this._selectionRanges.length||this.selectionRangeElements.some((e,t)=>e.range!==this._selectionRanges[t]))&&(this.selectionRangeElements.forEach(({body:e,startHandle:t,endHandle:i})=>{e.remove(),t?.remove(),i?.remove()}),this.selectionRangeElements=this._selectionRanges.map(e=>this.createSelectionRangeElements(e))),this.selectionRangeElements.forEach(({range:e,body:t,startHandle:i,endHandle:s})=>{const l=n(e.startTime),o=n(e.endTime);t.style.left=l+"px",t.style.width=Math.max(0,o-l)+"px",i&&(i.style.left=l-4+"px"),s&&(s.style.left=o-4+"px")}),this.shadingElements.forEach(e=>e.remove()),this.shadingElements=[],this.selectionOptions.shading&&this._selectionRanges.length>0){const e=E(this._selectionRanges).map(e=>[n(e.startTime),n(e.endTime)]);(this.selectionOptions.invertShading?e:function(e,t){const i=[];let s=0;for(const[t,n]of e)t>s&&i.push([s,t]),s=Math.max(s,n);s<t&&i.push([s,t]);return i}(e,t.width)).forEach(([e,t])=>{if(t<=e)return;const i=document.createElement("div");i.style.position="absolute",i.style.top="0px",i.style.height="100%",i.style.left=e+"px",i.style.width=t-e+"px",i.style.background="rgba(0, 0, 0, 0.5)",i.style.pointerEvents="none",this.selectionOverlayContainer.appendChild(i),this.shadingElements.push(i)})}}renderColumnHoverBand(e){if(this.columnBandElements.forEach(e=>e.remove()),this.columnBandElements=[],"column"!==this.selectionOptions.pick||0===e.length||!this.container)return;const t=this.getSliderBounds();if(t.width<=0)return;const{startTime:i,endTime:s}=this.getTimeRange(),n=e=>Math.min(t.width,Math.max(0,(e-i)/(s-i)*t.width)),l=this.selectionOptions.columnBand?.color??"#fff",o=this.selectionOptions.columnBand?.opacity??.15;e.forEach(({layer:e,elements:t})=>{const i=t.map(g),s=Math.min(...i.map(e=>e[0])),a=Math.max(...i.map(e=>e[1])),r=document.createElement("div");r.style.position="absolute",r.style.left=n(s)+"px",r.style.width=Math.max(1,n(a)-n(s))+"px",r.style.top=100*(1-e.max)+"%",r.style.height=100*(e.max-e.min)+"%",r.style.background=l,r.style.opacity=String(o),r.style.pointerEvents="none",this.columnBandOverlayContainer.appendChild(r),this.columnBandElements.push(r)})}createSelectionRangeElements(e){const t=document.createElement("div");t.style.position="absolute",t.style.top="0px",t.style.height="100%",t.style.background="rgba(100, 180, 255, 0.15)",t.style.borderTop="2px solid rgba(100, 180, 255, 0.8)",t.style.borderBottom="2px solid rgba(100, 180, 255, 0.8)",t.style.boxSizing="border-box",t.style.cursor="grab",t.style.pointerEvents="auto",t.style.touchAction="none",this.selectionOverlayContainer.appendChild(t);const i=()=>{const e=document.createElement("div");return e.style.position="absolute",e.style.top="0px",e.style.height="100%",e.style.width="8px",e.style.cursor="ew-resize",e.style.pointerEvents="auto",e.style.touchAction="none",e.style.background="rgba(100, 180, 255, 0.9)",this.selectionOverlayContainer.appendChild(e),e},s=e.startTime===-1/0?null:i(),n=e.endTime===1/0?null:i();return this.wireSelectionDrag(e,t,s,n),{range:e,body:t,startHandle:s,endHandle:n}}wireSelectionDrag(e,t,i,s){const n=(e,t)=>{e.addEventListener("pointerdown",i=>{i.stopPropagation(),i.preventDefault(),e.setPointerCapture(i.pointerId);const s=t(i),n=e=>{s(e),this.renderSelectionOverlay(),this.emit("selectionchange",{ranges:this.getSelection(),phase:"drag"})};let l=!1;const o=t=>{l||(l=!0,e.hasPointerCapture(t.pointerId)&&e.releasePointerCapture(t.pointerId),e.removeEventListener("pointermove",n),e.removeEventListener("pointerup",o),e.removeEventListener("pointercancel",o),e.removeEventListener("lostpointercapture",o),this.emit("selectionchange",{ranges:this.getSelection(),phase:"end"}))};this.emit("selectionchange",{ranges:this.getSelection(),phase:"start"}),e.addEventListener("pointermove",n),e.addEventListener("pointerup",o),e.addEventListener("pointercancel",o),e.addEventListener("lostpointercapture",o)})};i&&n(i,()=>t=>{const{t:i}=this.mouseInSlider(t);e.startTime=e.endTime===1/0?i:Math.min(i,e.endTime)}),s&&n(s,()=>t=>{const{t:i}=this.mouseInSlider(t);e.endTime=e.startTime===-1/0?i:Math.max(i,e.startTime)}),n(t,t=>{const i=this.mouseInSlider(t).t,s=e.startTime,n=e.endTime;return t=>{const l=this.mouseInSlider(t).t-i;s===-1/0&&n!==1/0?e.endTime=n+l:n===1/0&&s!==-1/0?e.startTime=s+l:s!==-1/0&&n!==1/0&&(e.startTime=s+l,e.endTime=n+l)}})}startSelectingRange(e){this.selectingRange=!0,this.whenDoneSelectingRange=e}stopSelectingRange(){this.selectingRange=!1,this.whenDoneSelectingRange?.(),this.whenDoneSelectingRange=void 0,this.selectingRangeVisible&&(this.container.removeChild(this.selectingRangeElement),this.selectingRangeVisible=!1)}syncMaxValues(){return this.updateAllValueRanges(),this.on("time",()=>this.updateAllValueRanges())}syncMaxValuesForLayer(e){return this.updateValueRanges(e.yAxisGroup),e.model.on(()=>this.updateValueRanges(e.yAxisGroup))}updateAllValueRanges(){this.layers.filter(e=>e.visible).forEach(e=>{this.updateValueRanges(e.yAxisGroup,!0)}),this.renderYAxis()}updateValueRanges(e,t){this.updateValueRangesQueue.has(e)||this.updateValueRangesQueue.set(e,{lastUpdate:0,pending:void 0});const i=this.updateValueRangesQueue.get(e);if(Date.now()-i.lastUpdate<100)return i.pending&&clearTimeout(i.pending),void(i.pending=setTimeout(()=>{i.pending=void 0,this.updateValueRanges(e)},200));i.lastUpdate=Date.now();const s=new Map,n=new Map;this.layers.filter(e=>e.visible).forEach(e=>{const t=e.yAxis.group||e.id;s.has(t)||(s.set(t,-Number.MAX_VALUE),n.set(t,Number.MAX_VALUE));const i=e.model.getValueRange(this.getTimeRange());i.minValue<n.get(t)&&n.set(t,i.minValue),i.maxValue>s.get(t)&&s.set(t,i.maxValue)}),this.valueRanges.clear(),s.forEach((e,t)=>{this.valueRanges.set(t,{max:e,min:n.get(t)})}),t||this.renderYAxis(),this.emit("valueRanges",e)}renderYAxis(){const e=new Map;this.layers.filter(e=>e.visible).forEach(t=>{const i=t.yAxis.group||t.id;e.has(i)||e.set(i,{min:t.min,max:t.max,group:i}),e.set(i,{...e.get(i),...t.yAxis,min:t.min,max:t.max})});const t=[];e.forEach(e=>{if(e.visible){const i=this.valueRanges.get(e.group);i&&t.push({...e,minValue:i.min,maxValue:i.max})}}),this.yAxisTicks.render(t)}setTheme(e){this.theme=e,this.ticksBar.setDarkMode("dark"===e),this.engine?.setTheme(e)}addLayerLoader(e){this.loaders.add(e)}showStats(e){this.showingStats=e,this.engine?.showStats(e)}get engineReady(){return this._engineReady}whenReady(){return this._engineReady?Promise.resolve():new Promise(e=>{const t=this.on("ready",()=>{t(),e()})})}async setEngine(e){this._engineReady=!1,this.engine&&(this.layerUnloaders.forEach(e=>e()),this.layerUnloaders.clear(),this.engine.dispose()),this.initSliderContainer(),this.engine=e,this.engine.setTheme(this.theme),this.engine.setBackground(this._background),await this.engine.init(this.sliderElement),this.engine.setTheme(this.theme),this._engineReady=!0,this.emit("ready"),this.getVisibleLayers().forEach(e=>this.loadLayer(e)),this.engine.showStats(this.showingStats),this.engine.setTimeRange(this.getTimeRange()),this.engine.setValueRange(this.getValueRange()),this.engine.setSize(this.size.width,this.size.height)}initSliderContainer(){this.stopControls&&this.stopControls(),this.container?.removeChild(this.sliderElement);let e=0;this.showTicks&&(e+=24),this.showCurrent&&(e+=4),this.sliderElement=document.createElement("div"),this.sliderElement.oncontextmenu=()=>!1,this.sliderElement.tabIndex=0,this.sliderElement.style.top="0px",this.sliderElement.style.width="100%",this.sliderElement.style.outline="none",this.sliderElement.style.touchAction="none",this.sliderElement.style.height=`calc(100% - ${0+e}px)`,this.container?.appendChild(this.sliderElement),this.initSelectLabel(),this.stopControls=this.initControls(),this.onDestroy.push(()=>{this.container?.removeChild(this.sliderElement)})}get showCurrent(){return this._showCurrent}set showCurrent(e){if(e!==this._showCurrent){this._showCurrent=e,this.ticksBar.showLine=!this.showCurrent;let t=0,i=0;this.showTicks&&(i+=24),this.showCurrent&&(i+=4),this.sliderElement.style.height=`calc(100% - ${t+i}px)`}}async init(e,t){const i="string"==typeof e?document.getElementById(e):e;if(this.container=i,i){i.oncontextmenu=()=>!1;let e=0,s=0;if(this.showTicks){s+=24;const e=document.createElement("div");e.style.width="100%",e.style.height="24px",e.style.position="absolute",e.style.bottom="0px",e.style.touchAction="none",i.appendChild(e),this.ticksBar.init(e,e=>{const t=d(e.startTime,e.endTime);this.emit("tickclick",{startTime:e.startTime,endTime:e.endTime,unit:t}),this.fit({range:e,animate:1e3})}),this.ticksBar.showLine=!this.showCurrent}this.showCurrent&&(s+=4),this.sliderElement.oncontextmenu=()=>!1,this.sliderElement.tabIndex=0,this.sliderElement.style.top=`${e}px`,this.sliderElement.style.width="100%",this.sliderElement.style.outline="none",this.sliderElement.style.touchAction="none",this.sliderElement.style.height=`calc(100% - ${e+s}px)`,i.appendChild(this.sliderElement),this.yAxisTicks.canvas.style.top=`${e}px`,this.yAxisTicks.canvas.style.height=`calc(100% - ${e+s}px)`,i.appendChild(this.yAxisTicks.canvas),this.selectionOverlayContainer.style.position="absolute",this.selectionOverlayContainer.style.width="100%",this.selectionOverlayContainer.style.overflow="hidden",this.selectionOverlayContainer.style.pointerEvents="none",this.selectionOverlayContainer.style.top=`${e}px`,this.selectionOverlayContainer.style.height=`calc(100% - ${e+s}px)`,i.appendChild(this.selectionOverlayContainer),this.onDestroy.push(()=>i.removeChild(this.selectionOverlayContainer)),this.columnBandOverlayContainer.style.position="absolute",this.columnBandOverlayContainer.style.width="100%",this.columnBandOverlayContainer.style.overflow="hidden",this.columnBandOverlayContainer.style.pointerEvents="none",this.columnBandOverlayContainer.style.top=`${e}px`,this.columnBandOverlayContainer.style.height=`calc(100% - ${e+s}px)`,i.appendChild(this.columnBandOverlayContainer),this.onDestroy.push(()=>i.removeChild(this.columnBandOverlayContainer)),this.initHoverTooltip(),this.initTickTooltip(),this.watchFontLoading(),this.initSelectLabel(),this.setSize(this.sliderElement.clientWidth,this.sliderElement.clientHeight),this.syncSize(),this.setTimeRange(j(t)),this.setValueRange(j(t))}else console.warn(`Element ${e} not found`)}initSelectLabel(){this.selectedLabel.style.position="absolute",this.applyTooltipStyle(this.selectedLabel,"200px"),this.selectedLabel.style.pointerEvents="none",this.selectedLabel.style.display="none",this.selectedLabel.style.zIndex="10",this.sliderElement.appendChild(this.selectedLabel)}getLabel(e,t,i,s){if(t.labeler){const n=t.labeler(e,i,s);return null===n?null:"string"==typeof n?`<div>${n}</div>`:n?n.outerHTML:`<div>${e.name||e.id}</div>`}return`<div>${e.name||e.id}</div>`}getLayerById(e){return this.layers.find(t=>t.id===e)}select(e,t){this.stopSelectingRange(),this.stopUpdatingLabel?.();const i=()=>{this.updatingLabel=!1,this.stopUpdatingLabel?.(),this.stopUpdatingLabel=void 0,this.selectedLabel.style.display="none",this.layers.forEach(e=>e.select([])),this.selectedLayerId=void 0,this.emit("select",[])},s=e=>{this.layers.filter(t=>!e.some(e=>t.id===e.layer.id)).forEach(e=>e.select([])),e.forEach(e=>{e.layer.select(e.elements)}),this.emit("select",e)};if(t){s(this.getVisibleLayers().map(e=>{const i=e.model.dataInTimeRange(t);return{layer:e,elements:i}}).filter(e=>e.elements.length>0))}else if(e){const t=this.findElements(e,24);if(s(t.map(e=>({layer:e.layer,elements:e.elements})).flat()),1===t.length&&t[0].elements.length>0&&this.selectionOptions.label){const e=t[0].elements[0],s=t[0].layer;this.selectedLabel.innerHTML=this.getLabel(e,s,t[0].t,t[0].v)||"",this.selectedLabel.style.display="block";const n=this.selectedLabel.offsetWidth,l=this.selectedLabel.offsetHeight;this.selectedLayerId=s.id;const o=Array.isArray(e.t);this.updatingLabel=!0;const a=()=>{if(!this.updatingLabel)return;const{startTime:i,duration:s,endTime:a}=this.getTimeRange(),r=t[0].layer.model.get(e.id);if(r){const e=x(this.getTimeRange(),r.t);if(e){const a=this.getSliderBounds();let h=(.5*(e.startTime+e.endTime)-i)/s*a.width;const d="number"==typeof r.v?r.v:.5*(r.v[1]+r.v[0]);let m=this.modelValueToPixels(d,t[0].layer);o?m-=.5*l:m+=8;const c=.5*n,u=.5*l,g=a.width-.5*n,p=a.height-.5*l,y=Math.min(g,Math.max(c,h)),T=Math.min(p,Math.max(u,m));this.selectedLabel.style.display="block",this.selectedLabel.style.left=y-.5*n+"px",this.selectedLabel.style.top=`${T}px`}else this.selectedLabel.style.display="none"}else this.selectedLabel.style.display="none"};a();const r=s instanceof R&&s.histogram instanceof u;this.stopUpdatingLabel=this.on("time",r?i:a)}}else i()}listenToClicks(){let e=null;const t=t=>{e={x:t.clientX,y:t.clientY}},i=t=>{if(!e)return;const i=t.clientX-e.x,s=t.clientY-e.y;i*i+s*s<=25&&(this.stopSelectingRange(),this.select(e)),e=null},s=()=>{e=null};return this.sliderElement.addEventListener("pointerdown",t),this.sliderElement.addEventListener("pointerup",i),this.sliderElement.addEventListener("pointercancel",s),()=>{this.sliderElement.removeEventListener("pointerdown",t),this.sliderElement.removeEventListener("pointerup",i),this.sliderElement.removeEventListener("pointercancel",s)}}getSliderBounds(){return this.sliderElement.getBoundingClientRect()}mouseInSlider(e){const t=this.getSliderBounds(),i="x"in e?e:{x:e.clientX,y:e.clientY},s={x:i.x-t.left,y:i.y-t.top},n={x:s.x/t.width,y:-s.y/t.height+1},{startTime:l,duration:o}=this.getTimeRange(),a=l+n.x*o,{minValue:r,maxValue:h}=this.getValueRange();return{absolute:s,normalized:n,t:a,v:r+n.x*(h-r)}}pixelsToLayerValue(e,t){if(!e)return{yInLayer:0,toLayerHeight:1};const i="normalized"in e?e.normalized:this.mouseInSlider(e).normalized;let s=t.min;const n=t.max-s;return{yInLayer:(i.y-s)/n,toLayerHeight:n/this.getSliderBounds().height}}pixelsToModelValue(e,t){const{yInLayer:i,toLayerHeight:s}=this.pixelsToLayerValue(e,t);let n=0,l=100;if(t.model instanceof p){const e=this.valueRanges.get(t.yAxisGroup)||{min:0,max:1e3};n=e.min,l=e.max-n}return{yInModel:n+i*l,toModelHeight:s*l}}layerValueToPixels(e,t){let i=t.min;return(1-(i+(t.max-i)*e))*this.getSliderBounds().height}modelValueToPixels(e,t){let i=0,s=100;if(t.model instanceof p){const e=t.model.getValueRange(this.getTimeRange());i=e.minValue,s=e.maxValue-i}const n=(e-i)/s;return this.layerValueToPixels(n,t)}findElements(e,t){if("column"===this.selectionOptions.pick)return this.findColumnElements(e);const{width:i,height:s}=this.getSliderBounds(),{normalized:n}=this.mouseInSlider(e),{startTime:l,endTime:o,duration:a}=this.getTimeRange(),r=l+n.x*a,h=t||12,d=h/i*a,m={startTime:r-1,endTime:r+1},c={startTime:r-.5*d,endTime:r+.5*d};return this.getVisibleLayers().map(e=>{const{yInModel:t,toModelHeight:i}=this.pixelsToModelValue({normalized:n},e),s=e instanceof w?0:h*i,l=e instanceof w?m:c,o={minValue:t-.5*s,maxValue:t+.5*s},a=e.model.dataInRange({...l,...o});return{layer:e,elements:a,t:r,v:t}}).filter(e=>e.elements.length>0)}findColumnElements(e,t){const{width:i}=this.getSliderBounds(),{normalized:s}=this.mouseInSlider(e),{startTime:n,duration:l}=this.getTimeRange(),o=n+s.x*l,a=(t||12)/i*l,r={startTime:o-1,endTime:o+1},h={startTime:o-.5*a,endTime:o+.5*a};return this.getVisibleLayers().map(e=>{const{yInModel:t}=this.pixelsToModelValue({normalized:s},e),i=e instanceof w?r:h,n=e.model.dataInTimeRange(i);return{layer:e,elements:n,t:o,v:t}}).filter(e=>e.elements.length>0)}clearHoverEvent(){this.hoverCleared||(this.hoverCleared=!0,this.renderColumnHoverBand([]),this.emit("hover",null))}updateHover(e){if(this.dragging)return this.hoverLine.style.display="none",this.toolTip.style.display="none",void this.clearHoverEvent();if(void 0!==e&&(this.mouse=e),this.mouse){const e=this.getSliderBounds(),{normalized:t}=this.mouseInSlider(this.mouse),{startTime:i,endTime:s,duration:l}=this.getTimeRange(),o=i+t.x*l,a=this.findElements(this.mouse);this.renderColumnHoverBand(a),this.hoverCleared=!1,this.emit("hover",{elements:a.map(e=>({layer:e.layer,elements:e.elements})),t:o,clientX:this.mouse.x,clientY:this.mouse.y});const r=a.map(e=>e.elements.map(t=>({e:t,l:e.layer,t:e.t,v:e.v}))).flat();let h;if(r.length>0)if(r.length>3){h=r.slice(0,2).map(e=>this.getLabel(e.e,e.l,e.t,e.v)).flat().filter(e=>null!==e).join(""),h+=`<div>And ${r.length-2} more...</div>`}else{h=a.map(e=>e.elements.map(t=>this.getLabel(t,e.layer,e.t,e.v))).flat().filter(e=>null!==e).join("")}if(!1===this.hoverOptions.tooltip)this.toolTip.style.display="none";else{const t=new Date(o),i=t.getHours(),s=t.getMinutes(),l=t.getSeconds(),a=n(o,!0,this._locale),r=`${(i<10?"0":"")+i}:${(s<10?"0":"")+s}:${(l<10?"0":"")+l}`;let d="";const m=this.ticksBar.cache.high.spacing;(void 0===this.hoverOptions.date?m>=M:this.hoverOptions.date)&&(d+=`<div>${a}</div>`),(void 0===this.hoverOptions.time?m<=V:this.hoverOptions.time)&&(d+=`<div>${r}</div>`),h&&(d+=h),this.toolTip.innerHTML=d,this.toolTip.style.display=d.length>0?"block":"none";const c=88,u=36;let g=this.mouse.x+10,p=this.mouse.y+10;const y=e.left+e.width-c-5,T=e.top+e.height-u-5;g>y&&(g=y),p>T&&(p=T),this.toolTip.style.left=`${g}px`,this.toolTip.style.top=`${p}px`}}else this.hoverLine.style.display="none",this.toolTip.style.display="none",this.clearHoverEvent()}initHoverTooltip(){this.toolTip.style.position="fixed",this.applyTooltipStyle(this.toolTip),this.toolTip.style.pointerEvents="none",this.toolTip.style.display="none",this.toolTip.style.zIndex="10",this.toolTip.style.overflow="hidden",document.body.appendChild(this.toolTip),this.onDestroy.push(()=>this.toolTip.remove())}initTickTooltip(){this.tickToolTip.style.position="fixed",this.applyTooltipStyle(this.tickToolTip),this.tickToolTip.style.pointerEvents="none",this.tickToolTip.style.display="none",this.tickToolTip.style.zIndex="10",this.tickToolTip.style.whiteSpace="pre-line",document.body.appendChild(this.tickToolTip),this.onDestroy.push(()=>this.tickToolTip.remove())}initHoverListener(){const e=()=>{this.updateHover(null)},t=e=>{this.updateHover({x:e.clientX,y:e.clientY})};this.sliderElement.addEventListener("mouseleave",e),this.sliderElement.addEventListener("mousemove",t);const i=this.listenToClicks();return()=>{this.sliderElement.removeEventListener("mouseleave",e),this.sliderElement.removeEventListener("mousemove",t),i()}}syncSize(){const e=()=>{const{offsetWidth:e,offsetHeight:t}=this.sliderElement;this.setSize(e,t)},t=new ResizeObserver(()=>{e()});this.container&&(t.observe(this.container),e()),this.onDestroy.push(()=>{t.disconnect()})}getTimeRange(){return y(this.range.time)}getValueRange(){return this.range.value}setTimeRange(e,t){const i=this.getTimeRange(),s=v(i,e),n=this._limits?f(s,this._limits):s;n.startTime===i.startTime&&n.endTime===i.endTime||(this.range.time=n,this.engine?.setTimeRange(n),this.showTicks&&this.ticksBar.setTimeRange(n),this.emit("time",n),this.updateHover())}setValueRange(e){const t={...this.getValueRange(),...e};this.range.value=t,this.engine?.setValueRange(t),this.emit("value",this.getValueRange()),this.updateHover()}setSize(e,t){this.size={width:e,height:t},this.showTicks&&this.ticksBar.setWidth(e);const i=this.getTimeRange(),s=this.getValueRange();this.engine?.setSize(e,t),this.setTimeRange(i),this.setValueRange(s),this.updateHover(),this.emit("size",{width:e,height:t})}up(e){const{startTime:t,endTime:i,duration:s}=this.getTimeRange();let n={startTime:0,endTime:Date.now()};const l=.5*(t+i);if(s>=.9*C){const e=X(10*s);n=h(l,e)}else{const e=[{threshold:S,unit:"millennium"},{threshold:k,unit:"century"},{threshold:H,unit:"decade"},{threshold:.9*D,unit:"year"},{threshold:_,unit:"month"},{threshold:V,unit:"week"},{threshold:M,unit:"day"},{threshold:B,unit:"hour"},{threshold:z,unit:"minute"},{threshold:-1,unit:"second"}];let t=0;for(;s<.9*e[t].threshold;)t++;const i=e[t].unit;n=m(l,i)}this.fit({...e,range:n})}down(e){const{startTime:t,endTime:i,duration:s}=this.getTimeRange();let n={startTime:0,endTime:Date.now()};const l=t+("number"==typeof e?.xRatio?e.xRatio:.5)*s;if(s>=.9*C){const e=X(s/10);n=h(l,e)}else{let e=0;const t=[{threshold:C,unit:"century"},{threshold:S,unit:"decade"},{threshold:k,unit:"year"},{threshold:H,unit:"month"},{threshold:1.1*D,unit:"week"},{threshold:_,unit:"day"},{threshold:V,unit:"hour"},{threshold:M,unit:"minute"},{threshold:B,unit:"second"}].reverse();for(;s>1.1*t[e].threshold;)e++;const i=t[e].unit;n=m(l,i)}this.fit({...e,range:n})}next(e){this.skip(1,e)}previous(e){this.skip(-1,e)}skip(e,t){this.fit({...t,range:this.getSkipRange(e)})}getSkipRange(e){const{startTime:t,endTime:i,duration:s}=this.getTimeRange(),n=.5*(t+i);if(s>=9*C){const e=X(s);return h(n,e)}const l=d(t,i);return l?m(n,l,Math.round(e)):{startTime:t+e*s,endTime:i+e*s}}clampToLimits(e){return this._limits?f(e,this._limits):e}describeStep(e){const t=this.getTimeRange(),i=this.getSkipRange(e),n=this.clampToLimits(i),l=Math.abs(n.startTime-t.startTime)>W||Math.abs(n.endTime-t.endTime)>W,o=l?n:i,a=e>0?s(t.startTime,o.startTime,this._locale):s(o.startTime,t.startTime,this._locale);return{range:n,canStep:l,span:a,label:Y(e,a,this._locale)}}canStep(e){return this.describeStep(e).canStep}describeSpan(e){const{startTime:t,endTime:i}=e??this.getTimeRange(),n=s(t,i,this._locale);return{text:n.text,exact:n.exact,dates:a(t,i,this._locale)}}isFitted(e){const t=this.clampToLimits(e??this.getLayersTimeRange()),i=this.getTimeRange(),s=Math.max(W,.001*(t.endTime-t.startTime));return Math.abs(i.startTime-t.startTime)<=s&&Math.abs(i.endTime-t.endTime)<=s}getTimeRangeLabel(){const{startTime:e,endTime:t}=this.getTimeRange(),i=new Date(e);return 0===i.getHours()&&0===i.getMinutes()&&0===i.getSeconds()&&0===i.getMilliseconds()?l([e,t],this._locale):void 0}cancelFit(){this.stopFit?.(),this.stopFit=void 0}fit(e){this.cancelFit();const t=e?.range||this.getLayersTimeRange(),s=e?.padding?T(t,e.padding):t;if(e?.animate){const t=this.getTimeRange();this.stopFit=i(t.startTime,t.endTime,s.startTime,s.endTime,e.animate,(e,t)=>this.setTimeRange({startTime:e,endTime:t}))}else this.setTimeRange(s)}getLayersTimeRange(){if(0===this.getVisibleLayers().length)return I;{let e=this.getVisibleLayers()[0].model.getTimeRange();return this.getVisibleLayers().slice(1).forEach(t=>e=L(e,t.model.getTimeRange())),e}}loadLayer(e){if(e.timeSlider&&e.timeSlider.id!==this.id)console.warn("Layer already has been loaded by another time slider, create a new layer with the same model and style if you want to add it to another time slider");else if(this.engine&&this._engineReady&&!this.layerUnloaders.has(e.id)){const t=[...this.loaders].filter(e=>this.engine instanceof e.engineClass).find(t=>t.canLoad(e));if(t){const i=Date.now(),s=t.load(e,this.engine.scene,this.engine,this);e.timeSlider=this,e.loaded=!0;const n=e.name||e.constructor.name+"#"+e.id.slice(e.id.length-4).toUpperCase();console.log(`Loaded ${n} in ${o(Date.now()-i)}`);const l=this.syncMaxValuesForLayer(e);this.layerLoaders.set(e.id,t),this.layerUnloaders.set(e.id,()=>{l(),s(),this.layerLoaders.delete(e.id),e.loaded=!1})}else console.warn("No loader found for",{layer:e})}}unloadLayer(e){this.selectedLayerId===e.id&&this.select(null),this.layerUnloaders.has(e.id)&&(this.layerUnloaders.get(e.id)(),this.layerUnloaders.delete(e.id))}addLayer(e){if(this.layers.push(e),e.visible){this.loadLayer(e);const t=e.model.getTimeRange();if(1===this.getVisibleLayers().length)this.setTimeRange(t);else{const e=L(this.getTimeRange(),t);this.setTimeRange(e)}}const t=[e.on("visible",()=>{e.visible?this.loadLayer(e):this.unloadLayer(e),this.updateAllValueRanges()}),e.on("min",()=>this.onLayerBandChange(e)),e.on("max",()=>this.onLayerBandChange(e))];this.layerListenerStoppers.set(e.id,()=>t.forEach(e=>e()))}onLayerBandChange(e){const t=this.layerLoaders.get(e.id);t&&!t.followsLayerBand&&(this.layerUnloaders.get(e.id)?.(),this.layerUnloaders.delete(e.id),this.loadLayer(e)),this.updateAllValueRanges()}removeLayer(e){const t="string"==typeof e?e:e.id,i=this.layers.find(e=>e.id===t);i&&(this.unloadLayer(i),this.layerUnloaders.delete(t),this.layerListenerStoppers.get(t)?.(),this.layerListenerStoppers.delete(t),this.layers=this.layers.filter(e=>e.id!==t))}destroy(){this.layers.map(e=>e.id).forEach(e=>this.removeLayer(e)),this.onDestroy.forEach(e=>e())}addHorizontalPinchZoom(){let e=null,t=null;const i=()=>{this.pinching=!1,e=null,t=null},s=i=>{if(2===i.touches.length){i.preventDefault(),this.dragging=!1,this.pinching=!0;const s=this.mouseInSlider(i.touches[0]).t,n=this.mouseInSlider(i.touches[1]).t;e=Math.min(s,n),t=Math.max(s,n)}},n=s=>{if(2===s.touches.length){if(null===e||null===t)return;s.preventDefault(),this.dragging=!1;const i=this.mouseInSlider(s.touches[0]).normalized.x,n=this.mouseInSlider(s.touches[1]).normalized.x,l=Math.min(i,n),o=Math.max(i,n),a=(t-e)/(o-l);let r=e-l*a,h=t+(1-o)*a;if(a>P){const e=.5*(r+h);r=e-.5*P,h=e+.5*P}if(a<U){const e=.5*(r+h);r=e-.5*U,h=e+.5*U}this.setTimeRange({startTime:r,endTime:h})}else i()},l=()=>{i()};this.sliderElement.addEventListener("touchstart",s,{passive:!1}),this.sliderElement.addEventListener("touchmove",n,{passive:!1}),this.sliderElement.addEventListener("touchend",l),this.sliderElement.addEventListener("touchcancel",l),this.onDestroy.push(()=>{this.sliderElement.removeEventListener("touchstart",s),this.sliderElement.removeEventListener("touchmove",n),this.sliderElement.removeEventListener("touchend",l),this.sliderElement.removeEventListener("touchcancel",l)})}initControls(){const e=new t;this.dragging=!1;let i,s=0,n=0,l=0;e.add(this.initHoverListener());const o=e=>{this.sliderElement.focus(),e.preventDefault();const t=this.mouseInSlider(e);this.zoom(.001*e.deltaY,{x:t.normalized.x})};let a=0,r=-1;const h=e=>{const t=Date.now(),i=t-a;r>=0&&i<300&&i>0&&(this.select(null),e.preventDefault(),this.zoom(-.75,{animate:1e3,x:r})),a=t},d=e=>{e.preventDefault(),this.select(null);const t=this.mouseInSlider(e);this.zoom(-.75,{animate:1e3,x:t.normalized.x})};this.selectingRangeElement.style.height="calc(100% - 6px)",this.selectingRangeElement.style.pointerEvents="none",this.selectingRangeElement.style.position="absolute",this.selectingRangeElement.style.border="3px solid #df0",this.selectingRangeElement.style.background="#df04",this.selectingRangeElement.style.top="0px";const m=e=>{if(this.sliderElement.focus(),2===e.button||0===e.button){e.preventDefault();const t=this.mouseInSlider(e),i=t.normalized.x,{startTime:o,duration:a}=this.getTimeRange();s=o+i*a,n=Date.now(),l=t.absolute.x,(e.ctrlKey||2===e.button)&&(this.selectingRange=!0),this.selectingRange?(this.selectingRangeElement.style.left=t.absolute.x+"px",this.selectingRangeElement.style.width="0px",this.container.appendChild(this.selectingRangeElement),this.selectingRangeVisible=!0):this.dragging=!0}},c=e=>{const t=this.mouseInSlider(e);return i=t.normalized.x,t},u=e=>{const t=c(e);if(this.selectingRange){const e=Math.abs(l-t.absolute.x),i=Math.min(l,t.absolute.x);this.selectingRangeElement.style.left=i+"px",this.selectingRangeElement.style.width=e-6+"px"}if(!this.dragging||Date.now()-n<100&&Math.abs(l-t.absolute.x)<10)return;e.preventDefault();const i=t.normalized.x,{startTime:o,endTime:a}=this.getTimeRange(),r=o+i*(a-o)-s;this.setTimeRange({startTime:o-r,endTime:a-r})},g=e=>{const t=this.getSliderBounds(),n=t.height-(e.clientY-t.top);if(!this.dragging&&!this.pinching&&n<8)this.ticksBar.click(e);else if(i=void 0,this.dragging=!1,this.selectingRange){const t=this.mouseInSlider(e).normalized.x,{startTime:i,endTime:n}=this.getTimeRange(),l=i+t*(n-i);"zoom"===this.selectRangeMode&&this.fit({range:{startTime:Math.min(s,l),endTime:Math.max(s,l)},animate:1e3}),"select"===this.selectRangeMode&&this.select(null,{startTime:Math.min(s,l),endTime:Math.max(s,l)}),"createSelection"===this.selectRangeMode&&this.setSelection([...this.getSelection(),{startTime:Math.min(s,l),endTime:Math.max(s,l)}])}this.stopSelectingRange()},p=e=>{this.pressedKeys.add(e.code)},y=e=>{this.pressedKeys.delete(e.code),"Space"===e.code&&(e.preventDefault(),this.togglePlaying()),"KeyF"===e.code&&(e.preventDefault(),this.fit({animate:3e3}));const t=void 0;"ArrowRight"===e.code&&(e.preventDefault(),this.skip(1,{animate:t})),"ArrowLeft"===e.code&&(e.preventDefault(),this.skip(-1,{animate:t})),"ArrowUp"===e.code&&(e.preventDefault(),this.up({animate:t})),"ArrowDown"===e.code&&(e.preventDefault(),this.down({xRatio:i,animate:t}))};let T=!0;const v=()=>{if(T){const e=this.pressedKeys.has("ShiftLeft")?.05:.01,t=2*e,s=this.pressedKeys.has("KeyD"),n=this.pressedKeys.has("KeyA");s&&this.shift(-e),n&&this.shift(e),this.pressedKeys.has("KeyW")&&this.zoom(-t,{x:s||n?void 0:i}),this.pressedKeys.has("KeyS")&&this.zoom(t,{x:s||n?void 0:i}),requestAnimationFrame(v)}};v(),e.add(()=>T=!1);const f=e=>e.preventDefault();this.sliderElement.addEventListener("contextmenu",f),this.sliderElement.addEventListener("wheel",o),this.sliderElement.addEventListener("pointerdown",m),this.sliderElement.addEventListener("pointermove",u),this.sliderElement.addEventListener("pointerup",g),this.sliderElement.addEventListener("dblclick",d),this.sliderElement.addEventListener("touchend",h),this.sliderElement.addEventListener("touchstart",e=>{if(1===e.touches.length){const t=this.mouseInSlider(e.touches[0]);r=t.normalized.x}else r=-1}),this.sliderElement.addEventListener("keydown",p),this.sliderElement.addEventListener("keyup",y),this.addHorizontalPinchZoom();const E=()=>{this.sliderElement.removeEventListener("contextmenu",f),this.sliderElement.removeEventListener("wheel",o),this.sliderElement.removeEventListener("pointerdown",m),this.sliderElement.removeEventListener("pointermove",u),this.sliderElement.removeEventListener("pointerup",g),this.sliderElement.removeEventListener("dblclick",d),this.sliderElement.removeEventListener("touchend",h),this.sliderElement.removeEventListener("keydown",p),this.sliderElement.removeEventListener("keyup",y)};return this.onDestroy.push(E),e.add(E),e.disposeLater()}shift(e,t){const{startTime:i,endTime:s,duration:n}=this.getTimeRange(),l=t?.absolute?e:e*n;this.fit({...t,range:{startTime:i-l,endTime:s-l}})}zoom(e,t){const i="number"==typeof t?.x?t.x:.5,{startTime:s,endTime:n,duration:l}=this.getTimeRange(),o={startTime:s-e*l*i,endTime:n+e*l*(1-i)},a=o.endTime-o.startTime,r=o.startTime+.5*a;a>P&&(o.startTime=r-.5*P,o.endTime=r+.5*P),a<U&&(o.startTime=r-.5*U,o.endTime=r+.5*U),this.fit({range:o,animate:t?.animate})}togglePlaying(){this.playing?this.pause():this.play()}isPlaying(){return this.playing}play(){if(this.playing)return;this.playing=!0,this.emit("playing",!0);let e=Date.now();const t=()=>{if(this.playing){if(this.live){const{duration:e}=this.getTimeRange(),t=Date.now(),i=10/this.size.width;this.setTimeRange({startTime:t-(1-i)*e,endTime:t+i*e}),this.setCurrent(t)}else if("auto"!==this.playSpeed||this.showCurrent){const t=Date.now(),i=t-e,s=this.current-this.getTimeRange().startTime,n="auto"===this.playSpeed?1:this.playSpeed;this.shift(-n*i,{absolute:!0}),this.setCurrent(this.getTimeRange().startTime+s),e=t}else{const t=Date.now();let i=5e-5*(t-e);this.pressedKeys.has("ShiftLeft")&&(i*=5);const s=this.current-this.getTimeRange().startTime;this.shift(-i),this.setCurrent(this.getTimeRange().startTime+s),e=t}requestAnimationFrame(t)}};t()}setCurrent(e){this.current!==e&&(Math.abs(e-Date.now())>100&&this.live&&(this.pause(),this.setLive(!1)),this.current=e,this.emit("current",e))}pause(){this.playing&&(this.playing=!1,this.emit("playing",!1),this.setLive(!1))}setLive(e){if(this.live!==e){if(this.live=e,e){const e=this.getTimeRange();this.fit({range:{startTime:e.endTime-this.liveRange,endTime:e.endTime},animate:z}),this.play()}this.emit("live",e)}}}function j(e){return{startTime:"number"==typeof e?.startTime?e.startTime:"number"==typeof e?.endTime?e.duration?e.endTime-e.duration:e.endTime-V:e?.duration?Date.now()-.5*e.duration:Date.now()-.5*V,endTime:"number"==typeof e?.endTime?e.endTime:"number"==typeof e?.startTime?e.duration?e.startTime+e.duration:e.startTime+V:e?.duration?Date.now()+.5*e.duration:Date.now()+.5*V,duration:e?.duration?e.duration:"number"==typeof e?.startTime&&"number"==typeof e?.endTime?e.endTime+e.startTime:V,minValue:"number"==typeof e?.minValue?e.minValue:"number"==typeof e?.maxValue?e.maxValue>0?0:e.maxValue-100:0,maxValue:"number"==typeof e?.maxValue?e.maxValue:"number"==typeof e?.minValue?e.minValue<0?0:e.minValue+100:100}}const W=z,K={en:{next:"Next {span}",previous:"Previous {span}",nextUnit:"Next {unit}",previousUnit:"Previous {unit}"},de:{next:"Weiter: {span}",previous:"Zurück: {span}"},fr:{next:"Suivant : {span}",previous:"Précédent : {span}"},nl:{next:"Volgende: {span}",previous:"Vorige: {span}"}};function Y(e,t,i){const s=K[i.split("-")[0].toLowerCase()];if(!s)return(e>0?"→ ":"← ")+t.text;const n=e>0?s.nextUnit:s.previousUnit;return n&&t.unitName?n.replace("{unit}",t.unitName):(e>0?s.next:s.previous).replace("{span}",t.text)}function X(e){const t=e/315576e5,i=Math.log10(t),s=Math.ceil(i);return Math.pow(10,s)}
@@ -14,8 +14,13 @@ export declare abstract class TimeSliderEngine<T> extends TypedEventEmitter<{
14
14
  private valueRange;
15
15
  private size;
16
16
  theme: 'dark' | 'light';
17
+ /** Clear colour behind the layers; undefined for the theme's default (#111 dark, #eee light). */
18
+ background?: string;
17
19
  constructor(scene: T);
18
20
  setTheme(theme: 'dark' | 'light'): void;
21
+ /** Sets the clear colour behind the layers, kept across theme changes; undefined goes back to the theme's default. */
22
+ setBackground(background: string | undefined): void;
23
+ protected getClearColor(): string;
19
24
  getTimeRange(): {
20
25
  duration: number;
21
26
  startTime: number;
@@ -1 +1 @@
1
- import{TypedEventEmitter as e}from"../event/TypedEventEmitter.js";import{TODAY as t}from"../ticks/TimeSliderConstants.js";import{addDuration as i}from"../data/TimeSliderRange.js";export class TimeSliderEngine extends e{constructor(e){super(),this.scene=e,this.timeRange=t,this.valueRange={minValue:0,maxValue:100},this.size={width:1e3,height:100},this.theme="dark"}setTheme(e){this.theme=e,this.setTimeRange(this.timeRange)}getTimeRange(){return{...this.timeRange,duration:this.timeRange.endTime-this.timeRange.startTime}}getValueRange(){return this.valueRange}getSize(){return this.size}showStats(e){}dispose(){this.destroy(),this.size={width:1e3,height:100}}setTimeRange(e){if(e.startTime!==this.timeRange.startTime||e.endTime!==this.timeRange.endTime){this.timeRange=e;const t=i(e);this.updateTimeRange(t),this.emit("time",t)}}setValueRange(e){e.minValue===this.valueRange.minValue&&e.maxValue===this.valueRange.maxValue||(this.valueRange=e,this.updateValueRange(e),this.emit("value",e))}setSize(e,t){this.size.width===e&&this.size.height===t||(this.size={width:e,height:t},this.updateSize(e,t),this.emit("size",this.size))}}
1
+ import{TypedEventEmitter as e}from"../event/TypedEventEmitter.js";import{TODAY as t}from"../ticks/TimeSliderConstants.js";import{addDuration as i}from"../data/TimeSliderRange.js";export class TimeSliderEngine extends e{constructor(e){super(),this.scene=e,this.timeRange=t,this.valueRange={minValue:0,maxValue:100},this.size={width:1e3,height:100},this.theme="dark"}setTheme(e){this.theme=e,this.setTimeRange(this.timeRange)}setBackground(e){this.background=e,this.setTheme(this.theme)}getClearColor(){return this.background??("dark"===this.theme?"#111":"#eee")}getTimeRange(){return{...this.timeRange,duration:this.timeRange.endTime-this.timeRange.startTime}}getValueRange(){return this.valueRange}getSize(){return this.size}showStats(e){}dispose(){this.destroy(),this.size={width:1e3,height:100}}setTimeRange(e){if(e.startTime!==this.timeRange.startTime||e.endTime!==this.timeRange.endTime){this.timeRange=e;const t=i(e);this.updateTimeRange(t),this.emit("time",t)}}setValueRange(e){e.minValue===this.valueRange.minValue&&e.maxValue===this.valueRange.maxValue||(this.valueRange=e,this.updateValueRange(e),this.emit("value",e))}setSize(e,t){this.size.width===e&&this.size.height===t||(this.size={width:e,height:t},this.updateSize(e,t),this.emit("size",this.size))}}
package/index.d.ts CHANGED
@@ -54,6 +54,7 @@ export * from './ticks/TimeSliderConstants';
54
54
  export * from './ticks/TimeTicksBar';
55
55
  export * from './ticks/YAxisTicks';
56
56
  export * from './common/TimeTools';
57
+ export * from './common/Font';
57
58
  export * from './common/Easing';
58
59
  export * from './common/Clamper';
59
60
  export * from './common/Disposables';
package/index.js CHANGED
@@ -1 +1 @@
1
- export*from"./engine/TimeSlider.js";export*from"./engine/TimeSliderEngine.js";export*from"./engine/PinchZoom.js";export*from"./engine/HorizontalPinchZoom.js";export*from"./three/ThreeTimeSlider.js";export*from"./three/ThreeTimeSliderEngine.js";export*from"./three/Three3DTimeSliderEngine.js";export*from"./three/react/ReactThreeTimeSlider.js";export*from"./three/layer/ThreeTimeLayerLoader.js";export*from"./three/layer/ThreePointsTimeLayerLoader.js";export*from"./three/layer/ThreeIntervalsTimeLayerLoader.js";export*from"./three/layer/Three3DPointsTimeLayerLoader.js";export*from"./three/layer/Three3DIntervalsTimeLayerLoader.js";export*from"./react/ReactTimeSlider.js";export*from"./react/ColoredSlider.js";export{ColoredSliderExtendsInput}from"./react/ColoredSliderExtendsInput.js";export*from"./react/FullscreenButton.js";export*from"./react/MaterialIconButton.js";export*from"./react/MaterialTextButton.js";export*from"./react/DateLabelWithPicker.js";export*from"./react/TimeLabelWithPicker.js";export*from"./react/DateRangeLabelWithPicker.js";export*from"./react/YearScroller.js";export*from"./react/PointerClick.js";export*from"./react/PinchZoomHook.js";export*from"./react/DeepMemoHook.js";export*from"./data/TimeSliderElement.js";export*from"./data/TimeSliderModel.js";export*from"./data/TimeSliderPointsModel.js";export*from"./data/TimeSliderIntervalsModel.js";export*from"./data/CompositeTimeSliderModel.js";export*from"./data/TimeSliderRange.js";export*from"./data/TimeSliderBush.js";export*from"./data/IntervalLaneManager.js";export*from"./data/TimeSliderLanesModel.js";export*from"./data/histogram/TimeSliderHistogram.js";export*from"./data/histogram/TimeSliderFixedHistogram.js";export*from"./data/histogram/TimeSliderDynamicHistogram.js";export*from"./data/histogram/TimeSliderSplittingFixedHistogram.js";export*from"./layer/TimeSliderLayer.js";export*from"./layer/TimeSliderLayerLoader.js";export*from"./layer/TimeSliderPointsLayer.js";export*from"./layer/TimeSliderIntervalsLayer.js";export*from"./layer/TimeSliderHistogramLayer.js";export*from"./style/TimeSliderStyle.js";export*from"./style/TimeSliderStyler.js";export*from"./style/TimeSliderColors.js";export*from"./style/TimeSliderLabeler.js";export*from"./style/TimeSliderPointsStyle.js";export*from"./style/TimeSliderIntervalsStyle.js";export*from"./style/TimeSliderHistogramStyle.js";export*from"./ticks/TimeSliderConstants.js";export*from"./ticks/TimeTicksBar.js";export*from"./ticks/YAxisTicks.js";export*from"./common/TimeTools.js";export*from"./common/Easing.js";export*from"./common/Clamper.js";export*from"./common/Disposables.js";export*from"./common/Expand.js";export*from"./common/GlslTools.js";export*from"./event/TypedEventEmitter.js";
1
+ export*from"./engine/TimeSlider.js";export*from"./engine/TimeSliderEngine.js";export*from"./engine/PinchZoom.js";export*from"./engine/HorizontalPinchZoom.js";export*from"./three/ThreeTimeSlider.js";export*from"./three/ThreeTimeSliderEngine.js";export*from"./three/Three3DTimeSliderEngine.js";export*from"./three/react/ReactThreeTimeSlider.js";export*from"./three/layer/ThreeTimeLayerLoader.js";export*from"./three/layer/ThreePointsTimeLayerLoader.js";export*from"./three/layer/ThreeIntervalsTimeLayerLoader.js";export*from"./three/layer/Three3DPointsTimeLayerLoader.js";export*from"./three/layer/Three3DIntervalsTimeLayerLoader.js";export*from"./react/ReactTimeSlider.js";export*from"./react/ColoredSlider.js";export{ColoredSliderExtendsInput}from"./react/ColoredSliderExtendsInput.js";export*from"./react/FullscreenButton.js";export*from"./react/MaterialIconButton.js";export*from"./react/MaterialTextButton.js";export*from"./react/DateLabelWithPicker.js";export*from"./react/TimeLabelWithPicker.js";export*from"./react/DateRangeLabelWithPicker.js";export*from"./react/YearScroller.js";export*from"./react/PointerClick.js";export*from"./react/PinchZoomHook.js";export*from"./react/DeepMemoHook.js";export*from"./data/TimeSliderElement.js";export*from"./data/TimeSliderModel.js";export*from"./data/TimeSliderPointsModel.js";export*from"./data/TimeSliderIntervalsModel.js";export*from"./data/CompositeTimeSliderModel.js";export*from"./data/TimeSliderRange.js";export*from"./data/TimeSliderBush.js";export*from"./data/IntervalLaneManager.js";export*from"./data/TimeSliderLanesModel.js";export*from"./data/histogram/TimeSliderHistogram.js";export*from"./data/histogram/TimeSliderFixedHistogram.js";export*from"./data/histogram/TimeSliderDynamicHistogram.js";export*from"./data/histogram/TimeSliderSplittingFixedHistogram.js";export*from"./layer/TimeSliderLayer.js";export*from"./layer/TimeSliderLayerLoader.js";export*from"./layer/TimeSliderPointsLayer.js";export*from"./layer/TimeSliderIntervalsLayer.js";export*from"./layer/TimeSliderHistogramLayer.js";export*from"./style/TimeSliderStyle.js";export*from"./style/TimeSliderStyler.js";export*from"./style/TimeSliderColors.js";export*from"./style/TimeSliderLabeler.js";export*from"./style/TimeSliderPointsStyle.js";export*from"./style/TimeSliderIntervalsStyle.js";export*from"./style/TimeSliderHistogramStyle.js";export*from"./ticks/TimeSliderConstants.js";export*from"./ticks/TimeTicksBar.js";export*from"./ticks/YAxisTicks.js";export*from"./common/TimeTools.js";export*from"./common/Font.js";export*from"./common/Easing.js";export*from"./common/Clamper.js";export*from"./common/Disposables.js";export*from"./common/Expand.js";export*from"./common/GlslTools.js";export*from"./event/TypedEventEmitter.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protomaze/time",
3
- "version": "1.5.4",
3
+ "version": "1.6.0",
4
4
  "description": "Protomaze Time sliders",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -1 +1 @@
1
- import e,{forwardRef as t,useCallback as n,useEffect as r,useImperativeHandle as i,useLayoutEffect as a,useMemo as o,useRef as l,useState as s}from"react";import{Disposables as c}from"../common/Disposables.js";import{formatDuration as m}from"../common/TimeTools.js";import{TimeSlider as d}from"../engine/TimeSlider.js";import{ONE_YEAR as p}from"../ticks/TimeSliderConstants.js";import{ColoredSlider as u}from"./ColoredSlider.js";import{DateLabelWithPicker as h}from"./DateLabelWithPicker.js";import{DateRangeLabelWithPicker as g}from"./DateRangeLabelWithPicker.js";import{MaterialIconButton as f}from"./MaterialIconButton.js";import{MaterialTextButton as x}from"./MaterialTextButton.js";import{TimeLabelWithPicker as w}from"./TimeLabelWithPicker.js";const y={padding:"4px 8px",background:"#0008",borderRadius:"4px",color:"#fff",fontFamily:"monospace",fontSize:"12px"},b=[Date.now()-p,Date.now()];function v(e,t){return e[0]===t[0]&&e[1]===t[1]}function E(e){return JSON.stringify(e)}export const ReactTimeSlider=t(({timeRange:t,onTimeRange:n,selectRangeButton:a,noPlayButton:p,noTimeLabels:k,noRangeLabel:C,noDateLabels:L,theme:R,canHideLabels:S,verticalGap:N,engine:I,layers:_,loaders:D,showStats:z,currentSlider:j,...M},P)=>{const W=o(()=>new d(M),[]);r(()=>{M?.showCurrent&&(W.showCurrent=M?.showCurrent)},[M.showCurrent]),r(()=>{M?.showTicks&&(W.showTicks=M?.showTicks)},[M.showTicks]),r(()=>{M?.playSpeed&&(W.playSpeed=M?.playSpeed)},[M.playSpeed]),r(()=>{M?.liveRange&&(W.liveRange=M?.liveRange)},[M.liveRange]),r(()=>{M?.locale&&W.setLocale(M.locale)},[M.locale]),r(()=>{M?.weekLabel&&W.setWeekLabel(M.weekLabel,M.weekLabelPrefix)},[M.weekLabel,M.weekLabelPrefix]),r(()=>{W.setTickLabelStyler(M?.tickLabelStyler)},[M.tickLabelStyler]),r(()=>{W.setLimits(M?.limits)},[M.limits?.startTime,M.limits?.endTime]);const[,B]=s(0);r(()=>W.on("locale",()=>B(e=>e+1)),[W]);const F=N||4,G=22+F+8+"px",H=22+F+22+F+8+"px",O=l(null),X=l(null),A=l(null),[J,U]=s(!1),[V,Y]=s(!1),[$,q]=s(!1),[K,Q]=s(!1),[Z,ee]=s(!1),[te,ne]=s(Date.now()),re=l(null);useMaterialIcons();const[ie,ae]=s(t||b),oe=l(t||b),le=void 0!==t,se=l(new Set),[ce,me]=s(!1);r(()=>{if(le){const e=E(t);if(J&&!v(t,oe.current)&&!se.current.has(e)){oe.current=t;const[e,n]=t;W.setTimeRange({startTime:e,endTime:n})}se.current.delete(e)}},[J,t,le]),r(()=>{const e=new c;return e.add(W.on("time",e=>{const{startTime:t,endTime:r}=e,i=[t,r];v(i,oe.current)||(oe.current=i,se.current.add(E(i)),ae(i),n?.(i))})),e.disposeLater()},[W]),r(()=>{U(!1);const e=new c;return W.init(X.current).then(()=>U(!0)),e.add(W.on("time",e=>{if(e.startTime!==oe.current[0]||e.endTime!==oe.current[1]){const{startTime:t,endTime:r}=e,i=[t,r];oe.current=i,ae(i),n?.(i)}})),e.add(W.on("playing",Y)),e.add(W.on("live",q)),e.add(W.on("current",ne)),e.disposeLater()},[W]),r(()=>{D?.forEach(e=>W.addLayerLoader(e))},[D]),r(()=>{"boolean"==typeof M.showCurrent&&(W.showCurrent=M.showCurrent)},[M.showCurrent]),i(P,()=>({get timeSlider(){return W}})),r(()=>{if(M.showCurrent){const e=setInterval(()=>{A.current&&(A.current.available=Date.now())},10);return()=>clearInterval(e)}},[M.showCurrent]),r(()=>{J&&W.showStats(!0===z)},[J,z]),r(()=>{},[J]),r(()=>{J&&W.setEngine(I)},[J,I]),r(()=>{if(J){const e=new Set,t=new Set;W.getLayers().forEach(e=>t.add(e.id)),_.forEach(n=>{t.delete(n.id),e.add(n.id)}),W.getLayers().forEach(t=>e.delete(t.id));const n=_.filter(t=>e.has(t.id)),r=W.getLayers().filter(e=>t.has(e.id));r.length===W.getLayers().length&&n.length>0&&W.setTimeRange(n[0].model.getTimeRange()),r.forEach(e=>W.removeLayer(e)),n.forEach(e=>W.addLayer(e))}},[J,_]);r(()=>{const e=e=>{re.current&&e.target&&e.target.parentElement?.getAttribute("layerpanelbutton")!==W.id&&!re.current.contains(e.target)&&ee(!1)};if(Z)return document.addEventListener("mousedown",e),()=>{document.removeEventListener("mousedown",e)}},[Z]);const de=e=>{const t=W.current,{duration:n,startTime:r}=W.getTimeRange(),i=(t-r)/n;i>1||i<0?W.setTimeRange({startTime:e-.5*n,endTime:e+.5*n}):W.setTimeRange({startTime:e-i*n,endTime:e+(1-i)*n}),W.setCurrent(e)};r(()=>{const e=()=>{if(O.current){let e=560;M.showCurrent&&(e+=160),p&&(e-=30),C&&(e-=160),k&&(e-=140),L&&(e-=185);const t=O.current.getBoundingClientRect().width;Q(t<e)}};e();const t=new ResizeObserver(e);return O.current&&t.observe(O.current),window.addEventListener("resize",e),()=>{t.disconnect(),window.removeEventListener("resize",e)}},[M.showCurrent,p,C,k,L]);let pe=42;M.showCurrent&&(pe+=4),K&&!S&&(pe+=26);const ue="dark"===R;return r(()=>{R&&W.setTheme(R)},[R]),e.createElement("div",{ref:O,style:{display:"block",position:"relative",borderRadius:"inherit",background:ue?"#222":"#ddd",width:"100%",height:"100%",padding:"8px",boxSizing:"border-box"}},Z&&e.createElement("div",{ref:re,style:{position:"absolute",top:G,left:"8px",maxWidth:"200px",display:"flex",gap:"8px",alignItems:"center",paddingRight:"8px",paddingBottom:"8px",paddingTop:"8px",flexDirection:"column",zIndex:1e3,background:ue?"#222":"#ddd"}},W.getLayers().map(t=>e.createElement(T,{key:t.id,layer:t,timeSlider:W,isDarkMode:ue}))),e.createElement("div",{style:{position:"absolute",top:"8px",left:"8px",display:"flex",gap:"8px",alignItems:"center"}},e.createElement(f,{layerpanelbutton:W.id,iconName:"layers",onClick:e=>ee(e=>!e)}),e.createElement(f,{iconName:"skip_previous",onClick:()=>W.up({animate:1e3}),rotate:90}),!K&&!C&&e.createElement("span",{style:{...y}},m(ie)),e.createElement(f,{iconName:"arrow_range",onClick:()=>W.fit({animate:3e3})}),a&&e.createElement(f,{iconName:"ink_selection",active:ce,onClick:()=>{me(!0),W.startSelectingRange(()=>me(!1))}})),K&&!S&&M.showCurrent&&!C&&e.createElement("div",{style:{position:"absolute",top:G,left:"8px",display:"flex",gap:"8px",height:"22px"}},e.createElement("span",{style:{...y}},m(ie))),K&&!S&&!M.showCurrent&&!C&&e.createElement("div",{style:{position:"absolute",top:"8px",left:"50%",transform:"translateX(-50%)",display:"flex",gap:"8px",height:"22px"}},e.createElement("span",{style:{...y}},m(ie))),(!K||!S)&&e.createElement("div",{style:{position:"absolute",top:K?G:"8px",right:K?"8px":"50%",transform:K?void 0:"translateX(50%)",padding:0,display:"flex",gap:"8px",height:"22px",width:!K||M.showCurrent?void 0:"calc(100% - 16px)"}},!M.showCurrent&&(!k||!k)&&e.createElement(e.Fragment,null,!k&&e.createElement(w,{theme:R,time:ie[0],setTime:e=>W.setTimeRange({startTime:e})}),K&&!k&&!L&&!M.showCurrent&&e.createElement("div",{style:{flexGrow:1}}),!L&&e.createElement(g,{theme:R,range:[ie[0],ie[1]],setRange:e=>W.setTimeRange({startTime:e[0],endTime:e[1]})}),K&&!k&&!L&&!M.showCurrent&&e.createElement("div",{style:{flexGrow:1}}),!k&&e.createElement(w,{theme:R,time:ie[1]-1,setTime:e=>W.setTimeRange({endTime:e+1})})),M.showCurrent&&e.createElement(e.Fragment,null,!L&&e.createElement(h,{theme:R,time:te,setTime:de}),!k&&e.createElement(w,{theme:R,time:te,setTime:de}))),e.createElement("div",{style:{position:"absolute",top:"8px",right:"8px",display:"flex",gap:"8px",alignItems:"center"}},M.showCurrent&&e.createElement(e.Fragment,null,e.createElement(x,{text:"Live",onClick:()=>{W.live?W.pause():W.setLive(!0)},color:$?"#fff":ue?"#888":"#bbb"},e.createElement("span",{className:"material-symbols-outlined",style:{fontSize:"18px",zIndex:1,lineHeight:1}},"radio_button_unchecked"),$&&e.createElement("span",{className:"material-symbols-outlined",style:{fontSize:"18px",lineHeight:1,marginLeft:"-24px",color:"red"}},"radio_button_checked"))),e.createElement(f,{iconName:"skip_previous",onClick:()=>W.previous({animate:1e3})}),!p&&!V&&e.createElement(f,{iconName:"play_arrow",onClick:()=>W.play()}),!p&&V&&e.createElement(f,{iconName:"pause",onClick:()=>W.pause()}),e.createElement(f,{iconName:"skip_next",onClick:()=>W.next({animate:1e3})})),M.showCurrent&&e.createElement(u,{ref:A,value:te,min:ie[0],max:ie[1],onValue:e=>{W.pause(),W.setCurrent(e)},onPointerMove:e=>W.updateHover({x:e.clientX,y:e.clientY}),onPointerLeave:()=>W.updateHover(null),matchWidth:!0,style:{width:"calc(100% - 16px)",left:"8px",position:"absolute",bottom:"26px",zIndex:1},...j}),e.createElement("div",{ref:X,style:{position:"absolute",top:K&&!S?H:G,left:"8px",borderRadius:"inherit",width:"calc(100% - 16px)",height:`calc(100% - ${pe}px)`,background:ue?"#111":"#eee",overflow:"hidden"}}))});function T({layer:t,timeSlider:n,isDarkMode:i}){const[a,o]=s(t.visible);return r(()=>{t.on("visible",e=>o(e))},[]),e.createElement("div",{style:{display:"flex",gap:"8px",alignItems:"center",background:i?"#222":"#ddd",borderRadius:"4px",width:"100%"}},a&&e.createElement(f,{iconName:"visibility",onClick:()=>t.visible=!1}),!a&&e.createElement(f,{iconName:"visibility_off",color:"#888",onClick:()=>t.visible=!0}),e.createElement("span",{style:{...y,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",flexGrow:1,minWidth:0}},t.name||t.constructor.name+"#"+t.id.slice(t.id.length-4).toUpperCase()),e.createElement(f,{iconName:"center_focus_weak",onClick:()=>n.fit({animate:3e3,range:t.model.getTimeRange()})}))}export function useMaterialIcons(e="outlined"){r(()=>{const e=document.createElement("link");return e.rel="stylesheet",e.href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200",document.head.appendChild(e),()=>{document.head.removeChild(e)}},[e])}
1
+ import e,{forwardRef as t,useCallback as n,useEffect as r,useImperativeHandle as i,useLayoutEffect as o,useMemo as a,useRef as l,useState as s}from"react";import{Disposables as c}from"../common/Disposables.js";import{formatDuration as m}from"../common/TimeTools.js";import{TimeSlider as d}from"../engine/TimeSlider.js";import{ONE_YEAR as p}from"../ticks/TimeSliderConstants.js";import{ColoredSlider as u}from"./ColoredSlider.js";import{DateLabelWithPicker as g}from"./DateLabelWithPicker.js";import{DateRangeLabelWithPicker as h}from"./DateRangeLabelWithPicker.js";import{MaterialIconButton as f}from"./MaterialIconButton.js";import{MaterialTextButton as w}from"./MaterialTextButton.js";import{TimeLabelWithPicker as x}from"./TimeLabelWithPicker.js";const y={padding:"4px 8px",background:"#0008",borderRadius:"4px",color:"#fff",fontFamily:"monospace",fontSize:"12px"},b=[Date.now()-p,Date.now()];function v(e,t){return e[0]===t[0]&&e[1]===t[1]}function k(e){return JSON.stringify(e)}export const ReactTimeSlider=t(({timeRange:t,onTimeRange:n,selectRangeButton:o,noPlayButton:p,noTimeLabels:E,noRangeLabel:C,noDateLabels:L,theme:S,canHideLabels:R,verticalGap:N,engine:I,layers:_,loaders:z,showStats:D,currentSlider:j,...M},P)=>{const B=a(()=>new d(M),[]);r(()=>{M?.showCurrent&&(B.showCurrent=M?.showCurrent)},[M.showCurrent]),r(()=>{M?.showTicks&&(B.showTicks=M?.showTicks)},[M.showTicks]),r(()=>{M?.playSpeed&&(B.playSpeed=M?.playSpeed)},[M.playSpeed]),r(()=>{M?.liveRange&&(B.liveRange=M?.liveRange)},[M.liveRange]),r(()=>{M?.locale&&B.setLocale(M.locale)},[M.locale]),r(()=>{M?.weekLabel&&B.setWeekLabel(M.weekLabel,M.weekLabelPrefix)},[M.weekLabel,M.weekLabelPrefix]),r(()=>{B.setTickLabelStyler(M?.tickLabelStyler)},[M.tickLabelStyler]),r(()=>{B.setLimits(M?.limits)},[M.limits?.startTime,M.limits?.endTime]),r(()=>{B.setFont(M?.font)},[M.font?.family,M.font?.weight,M.font?.size]),r(()=>{B.setBackground(M?.background)},[M.background]),r(()=>{B.setTickTooltip(M?.tickTooltip??!0)},[M.tickTooltip]),r(()=>{B.setTooltipStyle(M?.tooltipStyle??{})},[JSON.stringify(M.tooltipStyle??{})]);const[,W]=s(0);r(()=>B.on("locale",()=>W(e=>e+1)),[B]);const F=N||4,G=22+F+8+"px",H=22+F+22+F+8+"px",O=l(null),X=l(null),A=l(null),[J,U]=s(!1),[V,Y]=s(!1),[$,q]=s(!1),[K,Q]=s(!1),[Z,ee]=s(!1),[te,ne]=s(Date.now()),re=l(null);useMaterialIcons();const[ie,oe]=s(t||b),ae=l(t||b),le=void 0!==t,se=l(new Set),[ce,me]=s(!1);r(()=>{if(le){const e=k(t);if(J&&!v(t,ae.current)&&!se.current.has(e)){ae.current=t;const[e,n]=t;B.setTimeRange({startTime:e,endTime:n})}se.current.delete(e)}},[J,t,le]),r(()=>{const e=new c;return e.add(B.on("time",e=>{const{startTime:t,endTime:r}=e,i=[t,r];v(i,ae.current)||(ae.current=i,se.current.add(k(i)),oe(i),n?.(i))})),e.disposeLater()},[B]),r(()=>{U(!1);const e=new c;return B.init(X.current).then(()=>U(!0)),e.add(B.on("time",e=>{if(e.startTime!==ae.current[0]||e.endTime!==ae.current[1]){const{startTime:t,endTime:r}=e,i=[t,r];ae.current=i,oe(i),n?.(i)}})),e.add(B.on("playing",Y)),e.add(B.on("live",q)),e.add(B.on("current",ne)),e.disposeLater()},[B]),r(()=>{z?.forEach(e=>B.addLayerLoader(e))},[z]),r(()=>{"boolean"==typeof M.showCurrent&&(B.showCurrent=M.showCurrent)},[M.showCurrent]),i(P,()=>({get timeSlider(){return B}})),r(()=>{if(M.showCurrent){const e=setInterval(()=>{A.current&&(A.current.available=Date.now())},10);return()=>clearInterval(e)}},[M.showCurrent]),r(()=>{J&&B.showStats(!0===D)},[J,D]),r(()=>{},[J]),r(()=>{J&&B.setEngine(I)},[J,I]),r(()=>{if(J){const e=new Set,t=new Set;B.getLayers().forEach(e=>t.add(e.id)),_.forEach(n=>{t.delete(n.id),e.add(n.id)}),B.getLayers().forEach(t=>e.delete(t.id));const n=_.filter(t=>e.has(t.id)),r=B.getLayers().filter(e=>t.has(e.id));r.length===B.getLayers().length&&n.length>0&&B.setTimeRange(n[0].model.getTimeRange()),r.forEach(e=>B.removeLayer(e)),n.forEach(e=>B.addLayer(e))}},[J,_]);r(()=>{const e=e=>{re.current&&e.target&&e.target.parentElement?.getAttribute("layerpanelbutton")!==B.id&&!re.current.contains(e.target)&&ee(!1)};if(Z)return document.addEventListener("mousedown",e),()=>{document.removeEventListener("mousedown",e)}},[Z]);const de=e=>{const t=B.current,{duration:n,startTime:r}=B.getTimeRange(),i=(t-r)/n;i>1||i<0?B.setTimeRange({startTime:e-.5*n,endTime:e+.5*n}):B.setTimeRange({startTime:e-i*n,endTime:e+(1-i)*n}),B.setCurrent(e)};r(()=>{const e=()=>{if(O.current){let e=560;M.showCurrent&&(e+=160),p&&(e-=30),C&&(e-=160),E&&(e-=140),L&&(e-=185);const t=O.current.getBoundingClientRect().width;Q(t<e)}};e();const t=new ResizeObserver(e);return O.current&&t.observe(O.current),window.addEventListener("resize",e),()=>{t.disconnect(),window.removeEventListener("resize",e)}},[M.showCurrent,p,C,E,L]);let pe=42;M.showCurrent&&(pe+=4),K&&!R&&(pe+=26);const ue="dark"===S;return r(()=>{S&&B.setTheme(S)},[S]),e.createElement("div",{ref:O,style:{display:"block",position:"relative",borderRadius:"inherit",background:ue?"#222":"#ddd",width:"100%",height:"100%",padding:"8px",boxSizing:"border-box"}},Z&&e.createElement("div",{ref:re,style:{position:"absolute",top:G,left:"8px",maxWidth:"200px",display:"flex",gap:"8px",alignItems:"center",paddingRight:"8px",paddingBottom:"8px",paddingTop:"8px",flexDirection:"column",zIndex:1e3,background:ue?"#222":"#ddd"}},B.getLayers().map(t=>e.createElement(T,{key:t.id,layer:t,timeSlider:B,isDarkMode:ue}))),e.createElement("div",{style:{position:"absolute",top:"8px",left:"8px",display:"flex",gap:"8px",alignItems:"center"}},e.createElement(f,{layerpanelbutton:B.id,iconName:"layers",onClick:e=>ee(e=>!e)}),e.createElement(f,{iconName:"skip_previous",onClick:()=>B.up({animate:1e3}),rotate:90}),!K&&!C&&e.createElement("span",{style:{...y}},m(ie)),e.createElement(f,{iconName:"arrow_range",onClick:()=>B.fit({animate:3e3})}),o&&e.createElement(f,{iconName:"ink_selection",active:ce,onClick:()=>{me(!0),B.startSelectingRange(()=>me(!1))}})),K&&!R&&M.showCurrent&&!C&&e.createElement("div",{style:{position:"absolute",top:G,left:"8px",display:"flex",gap:"8px",height:"22px"}},e.createElement("span",{style:{...y}},m(ie))),K&&!R&&!M.showCurrent&&!C&&e.createElement("div",{style:{position:"absolute",top:"8px",left:"50%",transform:"translateX(-50%)",display:"flex",gap:"8px",height:"22px"}},e.createElement("span",{style:{...y}},m(ie))),(!K||!R)&&e.createElement("div",{style:{position:"absolute",top:K?G:"8px",right:K?"8px":"50%",transform:K?void 0:"translateX(50%)",padding:0,display:"flex",gap:"8px",height:"22px",width:!K||M.showCurrent?void 0:"calc(100% - 16px)"}},!M.showCurrent&&(!E||!E)&&e.createElement(e.Fragment,null,!E&&e.createElement(x,{theme:S,time:ie[0],setTime:e=>B.setTimeRange({startTime:e})}),K&&!E&&!L&&!M.showCurrent&&e.createElement("div",{style:{flexGrow:1}}),!L&&e.createElement(h,{theme:S,range:[ie[0],ie[1]],setRange:e=>B.setTimeRange({startTime:e[0],endTime:e[1]})}),K&&!E&&!L&&!M.showCurrent&&e.createElement("div",{style:{flexGrow:1}}),!E&&e.createElement(x,{theme:S,time:ie[1]-1,setTime:e=>B.setTimeRange({endTime:e+1})})),M.showCurrent&&e.createElement(e.Fragment,null,!L&&e.createElement(g,{theme:S,time:te,setTime:de}),!E&&e.createElement(x,{theme:S,time:te,setTime:de}))),e.createElement("div",{style:{position:"absolute",top:"8px",right:"8px",display:"flex",gap:"8px",alignItems:"center"}},M.showCurrent&&e.createElement(e.Fragment,null,e.createElement(w,{text:"Live",onClick:()=>{B.live?B.pause():B.setLive(!0)},color:$?"#fff":ue?"#888":"#bbb"},e.createElement("span",{className:"material-symbols-outlined",style:{fontSize:"18px",zIndex:1,lineHeight:1}},"radio_button_unchecked"),$&&e.createElement("span",{className:"material-symbols-outlined",style:{fontSize:"18px",lineHeight:1,marginLeft:"-24px",color:"red"}},"radio_button_checked"))),e.createElement(f,{iconName:"skip_previous",onClick:()=>B.previous({animate:1e3})}),!p&&!V&&e.createElement(f,{iconName:"play_arrow",onClick:()=>B.play()}),!p&&V&&e.createElement(f,{iconName:"pause",onClick:()=>B.pause()}),e.createElement(f,{iconName:"skip_next",onClick:()=>B.next({animate:1e3})})),M.showCurrent&&e.createElement(u,{ref:A,value:te,min:ie[0],max:ie[1],onValue:e=>{B.pause(),B.setCurrent(e)},onPointerMove:e=>B.updateHover({x:e.clientX,y:e.clientY}),onPointerLeave:()=>B.updateHover(null),matchWidth:!0,style:{width:"calc(100% - 16px)",left:"8px",position:"absolute",bottom:"26px",zIndex:1},...j}),e.createElement("div",{ref:X,style:{position:"absolute",top:K&&!R?H:G,left:"8px",borderRadius:"inherit",width:"calc(100% - 16px)",height:`calc(100% - ${pe}px)`,background:M.background??(ue?"#111":"#eee"),overflow:"hidden"}}))});function T({layer:t,timeSlider:n,isDarkMode:i}){const[o,a]=s(t.visible);return r(()=>{t.on("visible",e=>a(e))},[]),e.createElement("div",{style:{display:"flex",gap:"8px",alignItems:"center",background:i?"#222":"#ddd",borderRadius:"4px",width:"100%"}},o&&e.createElement(f,{iconName:"visibility",onClick:()=>t.visible=!1}),!o&&e.createElement(f,{iconName:"visibility_off",color:"#888",onClick:()=>t.visible=!0}),e.createElement("span",{style:{...y,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",flexGrow:1,minWidth:0}},t.name||t.constructor.name+"#"+t.id.slice(t.id.length-4).toUpperCase()),e.createElement(f,{iconName:"center_focus_weak",onClick:()=>n.fit({animate:3e3,range:t.model.getTimeRange()})}))}export function useMaterialIcons(e="outlined"){r(()=>{const e=document.createElement("link");return e.rel="stylesheet",e.href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200",document.head.appendChild(e),()=>{document.head.removeChild(e)}},[e])}
@@ -1 +1 @@
1
- import{BufferGeometry as e,GridHelper as t,Group as s,Line as i,LineBasicMaterial as o,Mesh as a,MeshBasicMaterial as n,PerspectiveCamera as r,PlaneGeometry as d,Scene as l,Vector2 as h,Vector3 as m,WebGLRenderer as c}from"three";import p from"three/examples/jsm/libs/stats.module.js";import{Disposables as w}from"../common/Disposables.js";import{getStartOfWeek as x}from"../common/TimeTools.js";import{TimeSliderEngine as u}from"../engine/TimeSliderEngine.js";import{ONE_MONTH as v,ONE_SECOND as f,ONE_WEEK as b}from"../ticks/TimeSliderConstants.js";import{TimeTicksBar as y}from"../ticks/TimeTicksBar.js";export class ScaledPerspectiveCamera extends r{updateMatrixWorld(e){super.updateMatrixWorld(e),this.matrixWorldInverse.copy(this.matrixWorld).invert()}updateWorldMatrix(e,t,...s){super.updateWorldMatrix(e,t,...s),this.matrixWorldInverse.copy(this.matrixWorld).invert()}}export class Three3DTimeSliderEngine extends u{constructor(){super(new l),this.camera=new ScaledPerspectiveCamera}showStats(e){if(this.container)if(e){this.stats=new p;const e=this.stats.dom;e.style.position="absolute",e.style.top="auto",e.style.bottom="32px",e.style.left="8px",this.container.appendChild(e),this.statsDom=e}else this.statsDom&&(this.container.removeChild(this.statsDom),this.statsDom=void 0,this.stats=void 0)}setTheme(e){super.setTheme(e),this.onThemeChange?.(e)}async init(r){const l=new c({antialias:!0});this.renderer=l,this.disposables=new w,this.container=r,this.renderer.setClearColor("#111"),r.appendChild(l.domElement),this.disposables.add(()=>r.removeChild(l.domElement)),this.camera.position.z=2.073,this.container.addEventListener("keydown",e=>{const t=1/this.camera.scale.x;"KeyQ"===e.code&&(e.preventDefault(),this.camera.rotateY(t)),"KeyE"===e.code&&(e.preventDefault(),this.camera.rotateY(-t))});const h=new s,p=new s;h.scale.x=f,h.position.z=.9,this.scene.add(h);const u=new d(100,1),g=new n({color:"#444"}),T=new a(u,g);T.position.z=.5,T.position.y=-.001,T.rotateX(-.5*Math.PI),h.add(T);const C=new a(u,g);C.position.z=.5,C.position.y=-.001,C.rotateX(-.5*Math.PI),p.add(C);const z=new o({color:"#444"}),S=(new e).setFromPoints([new m(0,0,0),new m(0,1,0),new m(0,1,1)]),P=(new e).setFromPoints([new m(-1,0,0),new m(1,0,0)]),j=new i(P,z),D=new i(P,z);D.position.y=1,p.add(j),p.add(D),p.position.z=.9,this.scene.add(p);const M=[],V=new o({color:"#000"}),k=(new e).setFromPoints([new m(0,0,1),new m(0,0,-1e3)]),W=new s;p.add(W);const E=[];let I,F,R,X;const K=()=>{Date.now();const e=this.getSize().width,{startTime:t,duration:s,endTime:o}=this.getTimeRange(),a=s,[n]=y.getSpacing(e,a),r=.5*(o+t);if(n<v){p.visible=!1,h.scale.x=n;let e=r%n;n>=b&&(e=r-x(r)),h.position.x=-e,h.visible=!0}else{h.visible=!1;let e=0,a=0;y.generateTicks(n,t-3*s,o+3*s,(n,r)=>{t-s<=n&&n<=o+s&&(M[e]||(M[e]=new i(S,z),p.add(M[e])),M[e].position.x=n,M[e].visible=!0,e++),E[a]||(E[a]=new i(k,V),p.add(E[a])),E[a].position.x=n,E[a].visible=!0,a++});for(let e=a;e<E.length;e++)E[e].visible=!1;for(let t=e;t<M.length;t++)M[t].visible=!1;X&&(X.scale.x=1e3*s),C.scale.x=s,C.position.x=r,X&&(X.position.x=-500*s),j.scale.x=1e3*s,D.scale.x=1e3*s,p.position.x=-r,p.visible=!0}};this.onThemeChange=e=>{const s="dark"===e?"#000":"#fff",i="dark"===e?"#444":"#bbb";this.renderer?.setClearColor("dark"===e?"#111":"#eee"),I&&h.remove(I),I?.dispose(),I=new t(1e3,1e3,s,s),h.add(I),R&&h.remove(R),R?.dispose(),R=new t(1e3,1e3,i,i),R.position.z=500,R.position.y=1,h.add(R),F&&h.remove(F),F?.dispose(),F=new t(1e3,1e3,i,i),F.position.y=-499,F.rotateX(-.5*Math.PI),h.add(F),X&&p.remove(X),X?.dispose(),X=new t(200,200,s,s),X.position.z=-101,p.add(X),g.color.set(i),z.color.set(i),V.color.set(s),K()},this.onThemeChange(this.theme),this.disposables.add(this.on("time",K)),this.disposables.add(this.on("size",K)),this.updateValueRange({minValue:0,maxValue:1});let O=!0;this.disposables.add(()=>O=!1),this.disposables.add(()=>this.scene.clear()),this.disposables.add(()=>l.dispose());const Y=()=>{O&&(l.render(this.scene,this.camera),this.stats&&this.stats.update(),requestAnimationFrame(Y))};Y()}updateTimeRange(e){const t=this.camera.scale.x,{duration:s}=e;this.camera.scale.x=s,t!==s&&(this.camera.rotation.y=this.camera.rotation.y*t/s),this.camera.updateProjectionMatrix()}updateValueRange(e){const t=e.maxValue-e.minValue;this.camera.scale.y=t,this.camera.position.y=e.minValue+.5*t,this.camera.updateProjectionMatrix()}updateSize(e,t){this.renderer?.setSize(e,t,!0)}destroy(){this.disposables?.dispose()}setHorizontalCompression(e){if(!this.renderer)return;if(e<=1)return void this.camera.clearViewOffset();const{width:t,height:s}=this.renderer.getSize(new h),i=t*e,o=s,a=(i-t)/2,n=t,r=s;this.camera.setViewOffset(i,o,a,0,n,r)}}
1
+ import{BufferGeometry as e,GridHelper as t,Group as s,Line as i,LineBasicMaterial as o,Mesh as a,MeshBasicMaterial as n,PerspectiveCamera as r,PlaneGeometry as d,Scene as l,Vector2 as h,Vector3 as m,WebGLRenderer as c}from"three";import p from"three/examples/jsm/libs/stats.module.js";import{Disposables as w}from"../common/Disposables.js";import{getStartOfWeek as x}from"../common/TimeTools.js";import{TimeSliderEngine as u}from"../engine/TimeSliderEngine.js";import{ONE_MONTH as v,ONE_SECOND as f,ONE_WEEK as b}from"../ticks/TimeSliderConstants.js";import{TimeTicksBar as g}from"../ticks/TimeTicksBar.js";export class ScaledPerspectiveCamera extends r{updateMatrixWorld(e){super.updateMatrixWorld(e),this.matrixWorldInverse.copy(this.matrixWorld).invert()}updateWorldMatrix(e,t,...s){super.updateWorldMatrix(e,t,...s),this.matrixWorldInverse.copy(this.matrixWorld).invert()}}export class Three3DTimeSliderEngine extends u{constructor(){super(new l),this.camera=new ScaledPerspectiveCamera}showStats(e){if(this.container)if(e){this.stats=new p;const e=this.stats.dom;e.style.position="absolute",e.style.top="auto",e.style.bottom="32px",e.style.left="8px",this.container.appendChild(e),this.statsDom=e}else this.statsDom&&(this.container.removeChild(this.statsDom),this.statsDom=void 0,this.stats=void 0)}setTheme(e){super.setTheme(e),this.onThemeChange?.(e)}async init(r){const l=new c({antialias:!0});this.renderer=l,this.disposables=new w,this.container=r,this.renderer.setClearColor(this.getClearColor()),r.appendChild(l.domElement),this.disposables.add(()=>r.removeChild(l.domElement)),this.camera.position.z=2.073,this.container.addEventListener("keydown",e=>{const t=1/this.camera.scale.x;"KeyQ"===e.code&&(e.preventDefault(),this.camera.rotateY(t)),"KeyE"===e.code&&(e.preventDefault(),this.camera.rotateY(-t))});const h=new s,p=new s;h.scale.x=f,h.position.z=.9,this.scene.add(h);const u=new d(100,1),y=new n({color:"#444"}),C=new a(u,y);C.position.z=.5,C.position.y=-.001,C.rotateX(-.5*Math.PI),h.add(C);const T=new a(u,y);T.position.z=.5,T.position.y=-.001,T.rotateX(-.5*Math.PI),p.add(T);const z=new o({color:"#444"}),S=(new e).setFromPoints([new m(0,0,0),new m(0,1,0),new m(0,1,1)]),P=(new e).setFromPoints([new m(-1,0,0),new m(1,0,0)]),j=new i(P,z),D=new i(P,z);D.position.y=1,p.add(j),p.add(D),p.position.z=.9,this.scene.add(p);const M=[],V=new o({color:"#000"}),W=(new e).setFromPoints([new m(0,0,1),new m(0,0,-1e3)]),k=new s;p.add(k);const E=[];let I,F,R,X;const K=()=>{Date.now();const e=this.getSize().width,{startTime:t,duration:s,endTime:o}=this.getTimeRange(),a=s,[n]=g.getSpacing(e,a),r=.5*(o+t);if(n<v){p.visible=!1,h.scale.x=n;let e=r%n;n>=b&&(e=r-x(r)),h.position.x=-e,h.visible=!0}else{h.visible=!1;let e=0,a=0;g.generateTicks(n,t-3*s,o+3*s,(n,r)=>{t-s<=n&&n<=o+s&&(M[e]||(M[e]=new i(S,z),p.add(M[e])),M[e].position.x=n,M[e].visible=!0,e++),E[a]||(E[a]=new i(W,V),p.add(E[a])),E[a].position.x=n,E[a].visible=!0,a++});for(let e=a;e<E.length;e++)E[e].visible=!1;for(let t=e;t<M.length;t++)M[t].visible=!1;X&&(X.scale.x=1e3*s),T.scale.x=s,T.position.x=r,X&&(X.position.x=-500*s),j.scale.x=1e3*s,D.scale.x=1e3*s,p.position.x=-r,p.visible=!0}};this.onThemeChange=e=>{const s="dark"===e?"#000":"#fff",i="dark"===e?"#444":"#bbb";this.renderer?.setClearColor(this.getClearColor()),I&&h.remove(I),I?.dispose(),I=new t(1e3,1e3,s,s),h.add(I),R&&h.remove(R),R?.dispose(),R=new t(1e3,1e3,i,i),R.position.z=500,R.position.y=1,h.add(R),F&&h.remove(F),F?.dispose(),F=new t(1e3,1e3,i,i),F.position.y=-499,F.rotateX(-.5*Math.PI),h.add(F),X&&p.remove(X),X?.dispose(),X=new t(200,200,s,s),X.position.z=-101,p.add(X),y.color.set(i),z.color.set(i),V.color.set(s),K()},this.onThemeChange(this.theme),this.disposables.add(this.on("time",K)),this.disposables.add(this.on("size",K)),this.updateValueRange({minValue:0,maxValue:1});let O=!0;this.disposables.add(()=>O=!1),this.disposables.add(()=>this.scene.clear()),this.disposables.add(()=>l.dispose());const Y=()=>{O&&(l.render(this.scene,this.camera),this.stats&&this.stats.update(),requestAnimationFrame(Y))};Y()}updateTimeRange(e){const t=this.camera.scale.x,{duration:s}=e;this.camera.scale.x=s,t!==s&&(this.camera.rotation.y=this.camera.rotation.y*t/s),this.camera.updateProjectionMatrix()}updateValueRange(e){const t=e.maxValue-e.minValue;this.camera.scale.y=t,this.camera.position.y=e.minValue+.5*t,this.camera.updateProjectionMatrix()}updateSize(e,t){this.renderer?.setSize(e,t,!0)}destroy(){this.disposables?.dispose()}setHorizontalCompression(e){if(!this.renderer)return;if(e<=1)return void this.camera.clearViewOffset();const{width:t,height:s}=this.renderer.getSize(new h),i=t*e,o=s,a=(i-t)/2,n=t,r=s;this.camera.setViewOffset(i,o,a,0,n,r)}}
@@ -1 +1 @@
1
- import{BufferGeometry as e,GridHelper as s,Group as t,Line as i,LineBasicMaterial as o,OrthographicCamera as a,Scene as n,Vector3 as r,WebGLRenderer as h}from"three";import m from"three/examples/jsm/libs/stats.module.js";import{Disposables as d}from"../common/Disposables.js";import{TimeSliderEngine as l}from"../engine/TimeSliderEngine.js";import{TimeTicksBar as c}from"../ticks/TimeTicksBar.js";import{getStartOfWeek as p}from"../common/TimeTools.js";import{ONE_MONTH as b,ONE_SECOND as T,ONE_WEEK as u}from"../ticks/TimeSliderConstants.js";export class ThreeTimeSliderEngine extends l{constructor(){super(new n),this.camera=new a(0,1,1,0)}showStats(e){if(this.container)if(e){this.stats=new m;const e=this.stats.dom;e.style.position="absolute",e.style.top="auto",e.style.bottom="32px",e.style.left="8px",this.container.appendChild(e),this.statsDom=e}else this.statsDom&&(this.container.removeChild(this.statsDom),this.statsDom=void 0,this.stats=void 0)}setTheme(e){super.setTheme(e),this.onThemeChange?.(e)}async init(a){const n="dark"===this.theme;this.container=a;const m=new h({antialias:!0});this.renderer=m,this.disposables=new d,this.renderer.setClearColor(n?"#111":"#eee"),a.appendChild(this.renderer.domElement),this.disposables.add(()=>a.removeChild(m.domElement)),this.camera.position.z=10;const l=new t,g=new t;l.scale.x=T,this.scene.add(l);const w=new o({color:n?"#444":"#bbb"}),f=(new e).setFromPoints([new r(0,0,0),new r(0,1,0),new r(0,1,1)]);this.scene.add(g);const x=[];let C;const v=()=>{const e=this.getSize().width,{startTime:s,duration:t,endTime:o}=this.getTimeRange(),a=t,[n]=c.getSpacing(e,a),r=.5*(o+s);if(n<b){g.visible=!1,l.scale.x=n;let e=r%n;n>=u&&(e=r-p(r)),l.position.x=-e,l.visible=!0}else{l.visible=!1;let e=0;c.generateTicks(n,s-3*t,o+3*t,(a,n)=>{s-t<=a&&a<=o+t&&(x[e]||(x[e]=new i(f,w),g.add(x[e])),x[e].position.x=a,x[e].visible=!0,e++)});for(let s=e;s<x.length;s++)x[s].visible=!1;g.position.x=-r,g.visible=!0}};this.onThemeChange=e=>{const t="dark"===e?"#444":"#bbb";this.renderer?.setClearColor("dark"===e?"#111":"#eee"),C&&l.remove(C),C=new s(1e3,1e3,t,t),C.position.y=-499,C.rotateX(-.5*Math.PI),l.add(C),w.color.set(t),v()},this.onThemeChange(this.theme),this.disposables.add(this.on("time",v)),this.disposables.add(this.on("size",v));let j=!0;this.disposables.add(()=>j=!1),this.disposables.add(()=>this.scene.clear()),this.disposables.add(()=>m.dispose());const S=()=>{j&&(m.render(this.scene,this.camera),this.stats&&this.stats.update(),requestAnimationFrame(S))};S()}destroy(){this.disposables?.dispose()}updateTimeRange(e){const{startTime:s,duration:t}=e;this.camera.left=-.5*t,this.camera.right=.5*t,this.camera.updateProjectionMatrix()}updateValueRange(e){this.camera.top=e.maxValue,this.camera.bottom=e.minValue,this.camera.updateProjectionMatrix()}updateSize(e,s){this.renderer?.setSize(e,s)}}
1
+ import{BufferGeometry as e,GridHelper as s,Group as t,Line as i,LineBasicMaterial as o,OrthographicCamera as a,Scene as n,Vector3 as r,WebGLRenderer as h}from"three";import m from"three/examples/jsm/libs/stats.module.js";import{Disposables as d}from"../common/Disposables.js";import{TimeSliderEngine as l}from"../engine/TimeSliderEngine.js";import{TimeTicksBar as c}from"../ticks/TimeTicksBar.js";import{getStartOfWeek as p}from"../common/TimeTools.js";import{ONE_MONTH as b,ONE_SECOND as T,ONE_WEEK as g}from"../ticks/TimeSliderConstants.js";export class ThreeTimeSliderEngine extends l{constructor(){super(new n),this.camera=new a(0,1,1,0)}showStats(e){if(this.container)if(e){this.stats=new m;const e=this.stats.dom;e.style.position="absolute",e.style.top="auto",e.style.bottom="32px",e.style.left="8px",this.container.appendChild(e),this.statsDom=e}else this.statsDom&&(this.container.removeChild(this.statsDom),this.statsDom=void 0,this.stats=void 0)}setTheme(e){super.setTheme(e),this.onThemeChange?.(e)}async init(a){const n="dark"===this.theme;this.container=a;const m=new h({antialias:!0});this.renderer=m,this.disposables=new d,this.renderer.setClearColor(this.getClearColor()),a.appendChild(this.renderer.domElement),this.disposables.add(()=>a.removeChild(m.domElement)),this.camera.position.z=10;const l=new t,u=new t;l.scale.x=T,this.scene.add(l);const w=new o({color:n?"#444":"#bbb"}),C=(new e).setFromPoints([new r(0,0,0),new r(0,1,0),new r(0,1,1)]);this.scene.add(u);const f=[];let x;const v=()=>{const e=this.getSize().width,{startTime:s,duration:t,endTime:o}=this.getTimeRange(),a=t,[n]=c.getSpacing(e,a),r=.5*(o+s);if(n<b){u.visible=!1,l.scale.x=n;let e=r%n;n>=g&&(e=r-p(r)),l.position.x=-e,l.visible=!0}else{l.visible=!1;let e=0;c.generateTicks(n,s-3*t,o+3*t,(a,n)=>{s-t<=a&&a<=o+t&&(f[e]||(f[e]=new i(C,w),u.add(f[e])),f[e].position.x=a,f[e].visible=!0,e++)});for(let s=e;s<f.length;s++)f[s].visible=!1;u.position.x=-r,u.visible=!0}};this.onThemeChange=e=>{const t="dark"===e?"#444":"#bbb";this.renderer?.setClearColor(this.getClearColor()),x&&l.remove(x),x=new s(1e3,1e3,t,t),x.position.y=-499,x.rotateX(-.5*Math.PI),l.add(x),w.color.set(t),v()},this.onThemeChange(this.theme),this.disposables.add(this.on("time",v)),this.disposables.add(this.on("size",v));let j=!0;this.disposables.add(()=>j=!1),this.disposables.add(()=>this.scene.clear()),this.disposables.add(()=>m.dispose());const S=()=>{j&&(m.render(this.scene,this.camera),this.stats&&this.stats.update(),requestAnimationFrame(S))};S()}destroy(){this.disposables?.dispose()}updateTimeRange(e){const{startTime:s,duration:t}=e;this.camera.left=-.5*t,this.camera.right=.5*t,this.camera.updateProjectionMatrix()}updateValueRange(e){this.camera.top=e.maxValue,this.camera.bottom=e.minValue,this.camera.updateProjectionMatrix()}updateSize(e,s){this.renderer?.setSize(e,s)}}
@@ -9,6 +9,15 @@ export type TimeTicksCache = {
9
9
  segments: TimeTicksSegment[];
10
10
  spacing: number;
11
11
  };
12
+ /** The clickable tick box under the pointer: its range, the label it shows and its box in canvas pixels. */
13
+ export type TimeTicksHover = {
14
+ startTime: number;
15
+ endTime: number;
16
+ spacing: number;
17
+ label: string;
18
+ x: number;
19
+ width: number;
20
+ };
12
21
  export declare class TimeTicksBar {
13
22
  private canvas;
14
23
  private current;
@@ -21,6 +30,15 @@ export declare class TimeTicksBar {
21
30
  private weekLabel;
22
31
  private weekLabelPrefix?;
23
32
  private tickLabelStyler?;
33
+ private font;
34
+ private fontSize;
35
+ /** How much more text fits per pixel in this font than in the reference monospace one. */
36
+ private spaceScale;
37
+ private container?;
38
+ private background?;
39
+ private hoverTick?;
40
+ /** Called when the tick box under the pointer changes, with undefined when there is none. */
41
+ onHoverTick?: (hover: TimeTicksHover | undefined) => void;
24
42
  needsUpdate: boolean;
25
43
  private animationFrameId?;
26
44
  cache: {
@@ -38,6 +56,20 @@ export declare class TimeTicksBar {
38
56
  color?: string;
39
57
  opacity?: number;
40
58
  } | undefined) | undefined): void;
59
+ /** Sets the label font (CSS shorthand, e.g. '500 12px "IP Klartext", sans-serif'). Labels are measured in it, so they always fit their tick boxes. */
60
+ setFont(font: string, size: number): void;
61
+ /** Re-measures the font, e.g. once a web font has finished loading. */
62
+ refreshFont(): void;
63
+ private updateSpaceScale;
64
+ /** Background of the ticks bar; undefined leaves it transparent, showing the container. */
65
+ setBackground(background: string | undefined): void;
66
+ /** A tick's label with all the room it wants, e.g. "March 2024"; ticks under a day get their date and time range. Localized. */
67
+ getFullLabel(tick: {
68
+ startTime: number;
69
+ endTime: number;
70
+ spacing: number;
71
+ }): string;
72
+ getCanvasRect(): DOMRect;
41
73
  init(container: HTMLDivElement, updateTime: (newRange: TimeSliderTimeRange) => void): void;
42
74
  dispose(): void;
43
75
  click(e: MouseEvent): void;
@@ -49,6 +81,12 @@ export declare class TimeTicksBar {
49
81
  static getSpacing(widthPx: number, durationMs: number): number[];
50
82
  draw(): void;
51
83
  private actualDraw;
84
+ /**
85
+ * Labels for a row of ticks, measured in the current font: the longest label variant that fits
86
+ * every tick's box, the same variant for the whole row (so months don't alternate between
87
+ * "Jan23" and "F23"). A label that still doesn't fit its box is left out ('').
88
+ */
89
+ private fitRowLabels;
52
90
  static splitInTimeUnits(timeSlider: TimeSlider, unit: TimeUnit): {
53
91
  start: number;
54
92
  end: number;
@@ -1 +1 @@
1
- import{getDefaultLocale as t,getISOWeekNumber as e,getISOWeekYear as i,getStartOfDay as a,getStartOfMonth as s,getStartOfWeek as n,getStartOfXYears as r,getTimeUnitInMs as o,getWeekLabelPrefix as l}from"../common/TimeTools.js";import{CENTURY as h,DECADE as c,MILLENIUM as $,ONE_DAY as g,ONE_HOUR as d,ONE_MINUTE as m,ONE_MONTH as u,ONE_SECOND as T,ONE_WEEK as p,ONE_YEAR as w}from"./TimeSliderConstants.js";export class TimeTicksBar{constructor(){this.canvas=document.createElement("canvas"),this.current={startTimeMs:Date.now(),endTimeMs:Date.now()+g,widthPx:100,heightPx:24},this.xHover=void 0,this.hoverRange=void 0,this.updateTime=()=>{},this.showLine=!0,this.darkMode=!0,this.locale=t(),this.weekLabel="default",this.needsUpdate=!0,this.cache={high:{segments:[],spacing:1},medium:{segments:[],spacing:1}}}setDarkMode(t){this.darkMode=t,this.draw()}setLocale(t){this.locale=t,this.draw()}setWeekLabel(t,e){this.weekLabel=t,this.weekLabelPrefix=e,this.draw()}setTickLabelStyler(t){this.tickLabelStyler=t,this.draw()}init(t,e){this.updateTime=e,this.canvas.style.position="absolute",this.canvas.style.top="0px",this.canvas.style.left="0px",this.canvas.style.width="100%",this.canvas.style.height="100%",this.canvas.style.touchAction="none",this.canvas.style.cursor="pointer",t.appendChild(this.canvas),this.canvas.addEventListener("pointermove",t=>{this.xHover=(t.clientX-this.canvas.getBoundingClientRect().left)/this.canvas.width,this.draw()}),this.canvas.addEventListener("click",t=>{this.click(t)},{capture:!0}),this.canvas.addEventListener("pointerleave",t=>{this.xHover=void 0,this.hoverRange=void 0,this.draw()});const i=()=>{this.needsUpdate&&(this.actualDraw(),this.needsUpdate=!1),this.animationFrameId=requestAnimationFrame(i)};this.animationFrameId=requestAnimationFrame(i)}dispose(){this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=void 0)}click(t){this.xHover=(t.clientX-this.canvas.getBoundingClientRect().left)/this.canvas.width,this.draw(),this.hoverRange&&this.updateTime(this.hoverRange),this.xHover=void 0}setTimeRange(t){this.current.startTimeMs=t.startTime,this.current.endTimeMs=t.endTime,this.draw()}setWidth(t){this.current.widthPx=t,this.draw()}static getSpacing(t,e){const i=e/t,a=[T,5*T,10*T,15*T,30*T,m,5*m,10*m,15*m,30*m,d,3*d,6*d,g,p,u,w,c,h,$,1e4*w];let s=a[0],n=a[0],r=a[0];for(const t of a){if(t/i>=(t<m?56:t<g?36:30)){s=t;break}r=n,n=t}return[s,n,r]}draw(){this.needsUpdate=!0}actualDraw(){const{startTimeMs:t,endTimeMs:e,widthPx:i,heightPx:a}=this.current;if(i<=0)return;const s=this.canvas;s.width=i,s.height=a;const n=s.getContext("2d");n.clearRect(0,0,i,a);const r=e-t,[o,l,h]=TimeTicksBar.getSpacing(i,r);this.cache.low=l<g?{segments:this.drawTicks(h,4,!1,t,e,r,i,n),spacing:h}:void 0,this.cache.medium={segments:this.drawTicks(l,6,!1,t,e,r,i,n),spacing:l},this.cache.high={segments:this.drawTicks(o,8,!0,t,e,r,i,n),spacing:o}}static splitInTimeUnits(t,e){const{startTime:i,endTime:a}=t.getTimeRange(),s=o(e),n=[];return TimeTicksBar.generateTicks(s,i,a,(t,e)=>{n.push({start:t,end:e})}),n}static generateTicks(t,e,i,o){let l=Math.floor(e/t)*t;t>d&&t<p?l=a(l):t===p?l=n(l):t===u?l=s(l):t>=w&&(l=r(l,t/w));let h=e=>e+t;t===u?h=e=>s(e+1.1*t):t>=w&&(h=e=>{const i=new Date(e),a=new Date(i.getFullYear()+t/w,0,1);return a.setFullYear(i.getFullYear()+t/w),a.setHours(0,0,0,0)});let c=l;for(;c<=i+t;){const t=h(c);o(c,t),c=t}}drawTicks(t,e,i,a,s,n,r,o){const l=[];return o.strokeStyle="#888",o.fillStyle=this.darkMode?"white":"black",o.font="12px monospace",o.textAlign="center",o.textBaseline="top",this.showLine&&(o.beginPath(),o.moveTo(0,0),o.lineWidth=1,o.lineTo(r,0),o.stroke()),TimeTicksBar.generateTicks(t,a,s,(h,c)=>{const $=(h-a)/n*r;l.push({start:h,end:c});const d=t>=g&&i?24:e,m=(c-h)/n*r,u=formatTimeLabel(h,t,m,this.locale,this.weekLabel,this.weekLabelPrefix);if(i){const e="number"==typeof this.xHover?a+this.xHover*(s-a):void 0,i="number"==typeof e&&h<e&&e<c;i&&(o.fillStyle=this.darkMode?"rgba(225,255,0,0.2)":"rgba(225,255,0,0.4)",o.fillRect($,0,m,24),o.fillStyle=t>=g?this.darkMode?"#df0":"black":this.darkMode?"white":"black",i&&(this.hoverRange={startTime:h,endTime:c}))}if(o.beginPath(),o.moveTo($,0),o.lineWidth=e/4,o.lineTo($,d),o.stroke(),i){const e=this.tickLabelStyler?.({startTime:h,endTime:c}),i=o.globalAlpha;if(e?.color&&(o.fillStyle=e.color),"number"==typeof e?.opacity&&(o.globalAlpha=e.opacity),t>=g){const t=.5*m;o.fillText(u,$+t,8)}else o.fillText(u,$,8);o.globalAlpha=i}o.fillStyle=this.darkMode?"white":"black"}),l}}export function formatTimeLabel(a,s,n,r,o,h){const c="number"==typeof a?new Date(a):a,$=r??t(),T=c.getFullYear().toString().slice(2),f=((c.getMonth()+1).toString().padStart(2,"0"),c.getDate()),k=c.getHours().toString().padStart(2,"0"),v=c.getMinutes().toString().padStart(2,"0"),S=c.getSeconds().toString().padStart(2,"0"),L=c.toLocaleString($,{month:"short"});if(s>w){const t=new Date(c.getTime()+s),e=c.getFullYear(),i=t.getFullYear();if(n<75){if(n<60){const t=(i-e).toString();return`${e.toString().padStart(4,"0").slice(0,4-t.length)}${"X".repeat(t.length)}`}return`${e}-${i}`}return`${e} - ${i}`}if(s===w)return`${c.getFullYear()}`;if(s===u){if(n<64){if(n<52){if(n<40){return`${c.toLocaleString($,{month:"narrow"})}${T}`}return`${L}${T}`}return`${L} ${T}`}return n<120?`${L} ${c.getFullYear()}`:`${c.toLocaleString($,{month:"long"})} ${c.getFullYear()}`}if(s===p){const t=new Date(c.getTime()+1e5);if("calendarWeek"===o){const a=e(t),s=h??l($);return n<28?`${a}`:n<60?`${s}${a}`:`${s}${a} ${i(t)}`}const a=t.toLocaleString($,{month:"short"}),s=t.getDate(),r=(t.getFullYear().toString().slice(2),new Date(c.getTime()+6*g)),d=r.toLocaleString($,{month:"short"}),m=r.getDate();r.getFullYear().toString().slice(2);return a===d?n<130?n<110?n<95?n<70?n<60?n<48?formatTimeLabel(t.getTime(),g,n,$):`${s}-${m}${d.slice(0,2)}`:`${s}-${m}${d}`:`${s} - ${m} ${d}`:`${s} - ${m} ${d} ${T}`:`${s} - ${m} ${d} ${r.getFullYear()}`:`${s} - ${m} ${r.toLocaleString($,{month:"long"})} ${r.getFullYear()}`:n<185?n<130?n<115?n<95?n<85?n<75?n<65?n<56?n<48?formatTimeLabel(t.getTime(),g,n,$):`${s}${a.slice(0,1)}${m}${d.slice(0,1)}`:`${s}${a.slice(0,1)}-${m}${d.slice(0,1)}`:`${s}${a.slice(0,2)}-${m}${d.slice(0,2)}`:`${s}${a}-${m}${d}`:`${s}${a} - ${m}${d}`:`${s} ${a} - ${m} ${d}`:`${s} ${a} - ${m} ${d} ${T}`:`${s} ${a} - ${m} ${d} ${r.getFullYear()}`:`${s} ${t.toLocaleString($,{month:"long"})} - ${m} ${r.toLocaleString($,{month:"long"})} ${r.getFullYear()}`}return s>=g||s>=d&&"12"===k?n<170?n<140?n<100?n<90?n<75?n<56&&s>=g?n<48?`${f}${L.slice(0,2)}`:`${f}${L}`:`${f} ${L}`:`${c.toLocaleString($,{weekday:"short"})} ${f} ${L}`:`${c.toLocaleString($,{weekday:"short"})} ${f} ${L} ${T}`:`${c.toLocaleString($,{weekday:"short"})} ${f} ${L} ${c.getFullYear()}`:`${c.toLocaleString($,{weekday:"long"})} ${f} ${L} ${c.getFullYear()}`:`${c.toLocaleString($,{weekday:"long"})} ${f} ${c.toLocaleString($,{month:"long"})} ${c.getFullYear()}`:s>=d?`${c.getHours()}h`:s>=m?"12"===k&&"00"===v?`${f} ${L}`:`${k}:${v}`:"12"===k&&"00"===v&&"00"===S?`${f} ${L}`:`${k}:${v}:${S}`}
1
+ import{formatTimeSpanDates as t,getDefaultLocale as e,getISOWeekNumber as i,getISOWeekYear as a,getStartOfDay as s,getStartOfMonth as n,getStartOfWeek as r,getStartOfXYears as o,getTimeUnitInMs as l,getWeekLabelPrefix as h}from"../common/TimeTools.js";import{CENTURY as c,DECADE as d,MILLENIUM as g,ONE_DAY as m,ONE_HOUR as $,ONE_MINUTE as u,ONE_MONTH as T,ONE_SECOND as p,ONE_WEEK as f,ONE_YEAR as k}from"./TimeSliderConstants.js";const w="12px monospace",v="0123456789 JanFebMarAprMayJunJulAugSepOctNovDec";export class TimeTicksBar{constructor(){this.canvas=document.createElement("canvas"),this.current={startTimeMs:Date.now(),endTimeMs:Date.now()+m,widthPx:100,heightPx:24},this.xHover=void 0,this.hoverRange=void 0,this.updateTime=()=>{},this.showLine=!0,this.darkMode=!0,this.locale=e(),this.weekLabel="default",this.font=w,this.fontSize=12,this.spaceScale=1,this.needsUpdate=!0,this.cache={high:{segments:[],spacing:1},medium:{segments:[],spacing:1}}}setDarkMode(t){this.darkMode=t,this.draw()}setLocale(t){this.locale=t,this.draw()}setWeekLabel(t,e){this.weekLabel=t,this.weekLabelPrefix=e,this.draw()}setTickLabelStyler(t){this.tickLabelStyler=t,this.draw()}setFont(t,e){this.font=t,this.fontSize=e,this.updateSpaceScale(),this.draw()}refreshFont(){this.updateSpaceScale(),this.draw()}updateSpaceScale(){const t=this.canvas.getContext("2d");if(!t)return;t.font=w;const e=t.measureText(v).width;t.font=this.font;const i=t.measureText(v).width;this.spaceScale=i>0?Math.min(3,Math.max(.25,e/i)):1}setBackground(t){this.background=t,this.container&&(this.container.style.background=t??"")}getFullLabel(e){return e.spacing>=m?formatTimeLabel(e.startTime,e.spacing,1e5,this.locale,this.weekLabel,this.weekLabelPrefix):t(e.startTime,e.endTime,this.locale)}getCanvasRect(){return this.canvas.getBoundingClientRect()}init(t,e){this.updateTime=e,this.container=t,this.setBackground(this.background),this.canvas.style.position="absolute",this.canvas.style.top="0px",this.canvas.style.left="0px",this.canvas.style.width="100%",this.canvas.style.height="100%",this.canvas.style.touchAction="none",this.canvas.style.cursor="pointer",t.appendChild(this.canvas),this.canvas.addEventListener("pointermove",t=>{this.xHover=(t.clientX-this.canvas.getBoundingClientRect().left)/this.canvas.width,this.draw()}),this.canvas.addEventListener("click",t=>{this.click(t)},{capture:!0}),this.canvas.addEventListener("pointerleave",t=>{this.xHover=void 0,this.hoverRange=void 0,this.draw()});const i=()=>{this.needsUpdate&&(this.actualDraw(),this.needsUpdate=!1),this.animationFrameId=requestAnimationFrame(i)};this.animationFrameId=requestAnimationFrame(i)}dispose(){this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=void 0)}click(t){this.xHover=(t.clientX-this.canvas.getBoundingClientRect().left)/this.canvas.width,this.draw(),this.hoverRange&&this.updateTime(this.hoverRange),this.xHover=void 0}setTimeRange(t){this.current.startTimeMs=t.startTime,this.current.endTimeMs=t.endTime,this.draw()}setWidth(t){this.current.widthPx=t,this.draw()}static getSpacing(t,e){const i=e/t,a=[p,5*p,10*p,15*p,30*p,u,5*u,10*u,15*u,30*u,$,3*$,6*$,m,f,T,k,d,c,g,1e4*k];let s=a[0],n=a[0],r=a[0];for(const t of a){if(t/i>=(t<u?56:t<m?36:30)){s=t;break}r=n,n=t}return[s,n,r]}draw(){this.needsUpdate=!0}actualDraw(){const{startTimeMs:t,endTimeMs:e,widthPx:i,heightPx:a}=this.current;if(i<=0)return;const s=this.canvas;s.width=i,s.height=a;const n=s.getContext("2d");n.clearRect(0,0,i,a);const r=this.hoverTick;this.hoverTick=void 0;const o=e-t,[l,h,c]=TimeTicksBar.getSpacing(i,o);this.cache.low=h<m?{segments:this.drawTicks(c,4,!1,t,e,o,i,n),spacing:c}:void 0,this.cache.medium={segments:this.drawTicks(h,6,!1,t,e,o,i,n),spacing:h},this.cache.high={segments:this.drawTicks(l,8,!0,t,e,o,i,n),spacing:l};const d=this.hoverTick;d?.startTime===r?.startTime&&d?.endTime===r?.endTime&&d?.x===r?.x||this.onHoverTick?.(d)}fitRowLabels(t,e,i){if(0===e.length)return[];const a=(t,e)=>formatTimeLabel(t,i,e,this.locale,this.weekLabel,this.weekLabelPrefix);let s=Math.min(...e.map(t=>t.box))*this.spaceScale,n=[],r=[];for(let i=0;i<12;i++){n=e.map(t=>a(t.time,s)),r=n.map(e=>t.measureText(e).width);const i=Math.min(...e.map((t,e)=>r[e]>0?(t.box-2)/r[e]:1/0));if(i>=1||s<=1)break;s=Math.min(s-1,s*i)}return n.map((t,i)=>r[i]>e[i].box?"":t)}static splitInTimeUnits(t,e){const{startTime:i,endTime:a}=t.getTimeRange(),s=l(e),n=[];return TimeTicksBar.generateTicks(s,i,a,(t,e)=>{n.push({start:t,end:e})}),n}static generateTicks(t,e,i,a){let l=Math.floor(e/t)*t;t>$&&t<f?l=s(l):t===f?l=r(l):t===T?l=n(l):t>=k&&(l=o(l,t/k));let h=e=>e+t;t===T?h=e=>n(e+1.1*t):t>=k&&(h=e=>{const i=new Date(e),a=new Date(i.getFullYear()+t/k,0,1);return a.setFullYear(i.getFullYear()+t/k),a.setHours(0,0,0,0)});let c=l;for(;c<=i+t;){const t=h(c);a(c,t),c=t}}drawTicks(t,e,i,a,s,n,r,o){const l=[];o.strokeStyle="#888",o.fillStyle=this.darkMode?"white":"black",o.font=this.font,o.textAlign="center",o.textBaseline="top",this.showLine&&(o.beginPath(),o.moveTo(0,0),o.lineWidth=1,o.lineTo(r,0),o.stroke());const h=new Map;if(i){const e=[];TimeTicksBar.generateTicks(t,a,s,(t,i)=>{e.push({time:t,box:(i-t)/n*r})}),this.fitRowLabels(o,e,t).forEach((t,i)=>h.set(e[i].time,t))}return TimeTicksBar.generateTicks(t,a,s,(c,d)=>{const g=(c-a)/n*r;l.push({start:c,end:d});const $=t>=m&&i?24:e,u=(d-c)/n*r,T=h.get(c)??"";if(i){const e="number"==typeof this.xHover?a+this.xHover*(s-a):void 0,i="number"==typeof e&&c<e&&e<d;i&&(o.fillStyle=this.darkMode?"rgba(225,255,0,0.2)":"rgba(225,255,0,0.4)",o.fillRect(g,0,u,24),o.fillStyle=t>=m?this.darkMode?"#df0":"black":this.darkMode?"white":"black",i&&(this.hoverRange={startTime:c,endTime:d}),this.hoverTick={startTime:c,endTime:d,spacing:t,label:T,x:g,width:u})}if(o.beginPath(),o.moveTo(g,0),o.lineWidth=e/4,o.lineTo(g,$),o.stroke(),i){const e=this.tickLabelStyler?.({startTime:c,endTime:d}),i=o.globalAlpha;e?.color&&(o.fillStyle=e.color),"number"==typeof e?.opacity&&(o.globalAlpha=e.opacity);const a=Math.max(1,.5*(24-this.fontSize)+2);if(t>=m){const t=.5*u;o.fillText(T,g+t,a)}else o.fillText(T,g,a);o.globalAlpha=i}o.fillStyle=this.darkMode?"white":"black"}),l}}export function formatTimeLabel(t,s,n,r,o,l){const c="number"==typeof t?new Date(t):t,d=r??e(),g=c.getFullYear().toString().slice(2),p=((c.getMonth()+1).toString().padStart(2,"0"),c.getDate()),w=c.getHours().toString().padStart(2,"0"),v=c.getMinutes().toString().padStart(2,"0"),S=c.getSeconds().toString().padStart(2,"0"),b=c.toLocaleString(d,{month:"short"});if(s>k){const t=new Date(c.getTime()+s),e=c.getFullYear(),i=t.getFullYear();if(n<75){if(n<60){const t=(i-e).toString();return`${e.toString().padStart(4,"0").slice(0,4-t.length)}${"X".repeat(t.length)}`}return`${e}-${i}`}return`${e} - ${i}`}if(s===k)return`${c.getFullYear()}`;if(s===T){if(n<64){if(n<52){if(n<40){return`${c.toLocaleString(d,{month:"narrow"})}${g}`}return`${b}${g}`}return`${b} ${g}`}return n<120?`${b} ${c.getFullYear()}`:`${c.toLocaleString(d,{month:"long"})} ${c.getFullYear()}`}if(s===f){const t=new Date(c.getTime()+1e5);if("calendarWeek"===o){const e=i(t),s=l??h(d);return n<28?`${e}`:n<60?`${s}${e}`:`${s}${e} ${a(t)}`}const e=t.toLocaleString(d,{month:"short"}),s=t.getDate(),r=(t.getFullYear().toString().slice(2),new Date(c.getTime()+6*m)),$=r.toLocaleString(d,{month:"short"}),u=r.getDate();r.getFullYear().toString().slice(2);return e===$?n<130?n<110?n<95?n<70?n<60?n<48?formatTimeLabel(t.getTime(),m,n,d):`${s}-${u}${$.slice(0,2)}`:`${s}-${u}${$}`:`${s} - ${u} ${$}`:`${s} - ${u} ${$} ${g}`:`${s} - ${u} ${$} ${r.getFullYear()}`:`${s} - ${u} ${r.toLocaleString(d,{month:"long"})} ${r.getFullYear()}`:n<185?n<130?n<115?n<95?n<85?n<75?n<65?n<56?n<48?formatTimeLabel(t.getTime(),m,n,d):`${s}${e.slice(0,1)}${u}${$.slice(0,1)}`:`${s}${e.slice(0,1)}-${u}${$.slice(0,1)}`:`${s}${e.slice(0,2)}-${u}${$.slice(0,2)}`:`${s}${e}-${u}${$}`:`${s}${e} - ${u}${$}`:`${s} ${e} - ${u} ${$}`:`${s} ${e} - ${u} ${$} ${g}`:`${s} ${e} - ${u} ${$} ${r.getFullYear()}`:`${s} ${t.toLocaleString(d,{month:"long"})} - ${u} ${r.toLocaleString(d,{month:"long"})} ${r.getFullYear()}`}return s>=m||s>=$&&"12"===w?n<170?n<140?n<100?n<90?n<75?n<56&&s>=m?n<48?`${p}${b.slice(0,2)}`:`${p}${b}`:`${p} ${b}`:`${c.toLocaleString(d,{weekday:"short"})} ${p} ${b}`:`${c.toLocaleString(d,{weekday:"short"})} ${p} ${b} ${g}`:`${c.toLocaleString(d,{weekday:"short"})} ${p} ${b} ${c.getFullYear()}`:`${c.toLocaleString(d,{weekday:"long"})} ${p} ${b} ${c.getFullYear()}`:`${c.toLocaleString(d,{weekday:"long"})} ${p} ${c.toLocaleString(d,{month:"long"})} ${c.getFullYear()}`:s>=$?`${c.getHours()}h`:s>=u?"12"===w&&"00"===v?`${p} ${b}`:`${w}:${v}`:"12"===w&&"00"===v&&"00"===S?`${p} ${b}`:`${w}:${v}:${S}`}
@@ -1,3 +1,4 @@
1
+ import { TimeSliderFont } from "../common/Font";
1
2
  export type YAxisSettings = {
2
3
  visible?: boolean;
3
4
  group?: string;
@@ -6,6 +7,8 @@ export type YAxisSettings = {
6
7
  labels?: {
7
8
  position?: 'left' | 'right' | 'center' | 'none';
8
9
  color?: string;
10
+ /** Overrides the slider's font (TimeSliderOptions.font) for this axis' labels. */
11
+ font?: TimeSliderFont;
9
12
  stroke?: {
10
13
  color?: string;
11
14
  width?: number;
@@ -19,6 +22,9 @@ export type YAxisSettings = {
19
22
  };
20
23
  export declare class YAxisTicks {
21
24
  canvas: HTMLCanvasElement;
25
+ private font;
26
+ /** The labels' font for axes that don't set their own; undefined for the default 12px sans-serif. */
27
+ setFont(font: TimeSliderFont | undefined): void;
22
28
  constructor();
23
29
  render(currents: (YAxisSettings & {
24
30
  min: number;
@@ -1 +1 @@
1
- export class YAxisTicks{constructor(){this.canvas=document.createElement("canvas"),this.canvas.style.top="0px",this.canvas.style.width="100%",this.canvas.style.outline="none",this.canvas.style.touchAction="none",this.canvas.style.pointerEvents="none",this.canvas.style.position="absolute",this.canvas.style.height="100%"}render(t){const e=this.canvas,i=e.parentElement?.getBoundingClientRect();if(!i)return;e.width===i.width&&e.height===i.height||(e.width=i.width,e.height=i.height);const s=e.getContext("2d");if(s){s.clearRect(0,0,e.width,e.height);for(const i of t){if(!1===i.visible)continue;const{min:t,max:n,minValue:o,maxValue:l}=i,h=e.height*(1-n),a=e.height*(1-t),c=a-h,r=getNiceTicks(o,l),d=i.format??(t=>t.toString()),f=i.style?.labels?.position??"right",g=i.style?.labels?.color??"#777",u=void 0!==i.style?.labels?.stroke,m=i.style?.labels?.stroke?.color??"#000",x=i.style?.labels?.stroke?.width??1,w=i.style?.lines?.color??"#7773",y=parseFloat(i.style?.lines?.width??"1");s.save(),s.lineWidth=y,s.fillStyle=g,s.font="12px sans-serif",s.textBaseline="middle",s.textAlign="left"===f?"right":"right"===f?"left":"center";for(const t of r){const i=(t-o)/(l-o);if(i<0||i>1)continue;const n=a-i*c;if(!(n<h+4||n>a-4)&&(s.beginPath(),s.moveTo(0,n),s.lineTo(e.width,n),s.strokeStyle=w,s.stroke(),"none"!==f)){const i=d(t),o=s.measureText(i).width;let l,h="center";"left"===f?(h="right",l=Math.max(4+o,4+o),l=Math.min(l,e.width/2-4)):"right"===f?(h="left",l=Math.min(e.width-4-o,e.width-4-o),l=Math.max(l,e.width/2+4)):(h="center",l=e.width/2),u&&(s.lineWidth=2*x,s.strokeStyle=m,s.strokeText(i,l,n)),s.textAlign=h,s.fillText(i,l,n)}}s.restore()}}}}export function getNiceTicks(t,e,i=6){const s=niceNum(e-t,!1),n=niceNum(s/(i-1),!0),o=Math.floor(t/n)*n,l=Math.ceil(e/n)*n,h=[];for(let i=o;i<=l+n/2;i+=n)i>=t&&i<=e&&h.push(parseFloat(i.toFixed(10)));return h}export function niceNum(t,e){const i=Math.floor(Math.log10(t)),s=t/Math.pow(10,i);let n=0;return n=e?s<1.5?1:s<3?2:s<7?5:10:s<=1?1:s<=2?2:s<=5?5:10,n*Math.pow(10,i)}
1
+ import{resolveFont as t,toCssFont as e}from"../common/Font.js";const i={family:"sans-serif",weight:"normal",size:12};export class YAxisTicks{setFont(e){this.font=e?t(e,i):i}constructor(){this.canvas=document.createElement("canvas"),this.font=i,this.canvas.style.top="0px",this.canvas.style.width="100%",this.canvas.style.outline="none",this.canvas.style.touchAction="none",this.canvas.style.pointerEvents="none",this.canvas.style.position="absolute",this.canvas.style.height="100%"}render(i){const n=this.canvas,s=n.parentElement?.getBoundingClientRect();if(!s)return;n.width===s.width&&n.height===s.height||(n.width=s.width,n.height=s.height);const o=n.getContext("2d");if(o){o.clearRect(0,0,n.width,n.height);for(const s of i){if(!1===s.visible)continue;const{min:i,max:l,minValue:h,maxValue:a}=s,r=n.height*(1-l),c=n.height*(1-i),f=c-r,d=getNiceTicks(h,a),m=s.format??(t=>t.toString()),g=s.style?.labels?.position??"right",u=s.style?.labels?.color??"#777",y=void 0!==s.style?.labels?.stroke,w=s.style?.labels?.stroke?.color??"#000",x=s.style?.labels?.stroke?.width??1,p=s.style?.lines?.color??"#7773",v=parseFloat(s.style?.lines?.width??"1");o.save(),o.fillStyle=u,o.font=e(t(s.style?.labels?.font,this.font)),o.textBaseline="middle",o.lineJoin="round",o.miterLimit=2,o.textAlign="left"===g?"right":"right"===g?"left":"center";for(const t of d){const e=(t-h)/(a-h);if(e<0||e>1)continue;const i=c-e*f;if(!(i<r+4||i>c-4)&&(o.beginPath(),o.moveTo(0,i),o.lineTo(n.width,i),o.lineWidth=v,o.strokeStyle=p,o.stroke(),"none"!==g)){const e=m(t),s=o.measureText(e).width;let l,h="center";"left"===g?(h="right",l=Math.max(4+s,4+s),l=Math.min(l,n.width/2-4)):"right"===g?(h="left",l=Math.min(n.width-4-s,n.width-4-s),l=Math.max(l,n.width/2+4)):(h="center",l=n.width/2),o.textAlign=h,y&&(o.lineWidth=2*x,o.strokeStyle=w,o.strokeText(e,l,i)),o.fillText(e,l,i)}}o.restore()}}}}export function getNiceTicks(t,e,i=6){const n=niceNum(e-t,!1),s=niceNum(n/(i-1),!0),o=Math.floor(t/s)*s,l=Math.ceil(e/s)*s,h=[];for(let i=o;i<=l+s/2;i+=s)i>=t&&i<=e&&h.push(parseFloat(i.toFixed(10)));return h}export function niceNum(t,e){const i=Math.floor(Math.log10(t)),n=t/Math.pow(10,i);let s=0;return s=e?n<1.5?1:n<3?2:n<7?5:10:n<=1?1:n<=2?2:n<=5?5:10,s*Math.pow(10,i)}