@protomaze/time 1.7.0 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/demo/Common.d.ts CHANGED
@@ -18,7 +18,7 @@ export declare function createDemoIntervalsLayer(model: TimeSliderIntervalsModel
18
18
  color?: string;
19
19
  opacity?: number;
20
20
  width?: number;
21
- sides?: "all" | "vertical";
21
+ sides?: "all" | "vertical" | "vertical+base";
22
22
  smoothing?: number;
23
23
  };
24
24
  }>;
package/demo/Random.d.ts CHANGED
@@ -10,7 +10,7 @@ export declare function createRandomDemo(): {
10
10
  color?: string;
11
11
  opacity?: number;
12
12
  width?: number;
13
- sides?: "all" | "vertical";
13
+ sides?: "all" | "vertical" | "vertical+base";
14
14
  smoothing?: number;
15
15
  };
16
16
  }> | import("..").TimeSliderHistogramLayer<import("..").CompositeTimeSliderModel | import("..").TimeSliderModel<any>>)[];
@@ -24,7 +24,7 @@ export declare function createRandomDemo(): {
24
24
  color?: string;
25
25
  opacity?: number;
26
26
  width?: number;
27
- sides?: "all" | "vertical";
27
+ sides?: "all" | "vertical" | "vertical+base";
28
28
  smoothing?: number;
29
29
  };
30
30
  }>)[] | (import("..").TimeSliderIntervalsLayer<import("..").TimeSliderInterval<any>, {
@@ -37,7 +37,7 @@ export declare function createRandomDemo(): {
37
37
  color?: string;
38
38
  opacity?: number;
39
39
  width?: number;
40
- sides?: "all" | "vertical";
40
+ sides?: "all" | "vertical" | "vertical+base";
41
41
  smoothing?: number;
42
42
  };
43
43
  }> | import("..").TimeSliderHistogramLayer<import("..").CompositeTimeSliderModel | import("..").TimeSliderModel<any>>)[])[];
@@ -143,7 +143,10 @@ export interface TimeSliderOptions {
143
143
  tickTooltip?: TimeSliderTickTooltip;
144
144
  /** Style of the hover, tick and selection-label tooltips. See also setTooltipStyle(). */
145
145
  tooltipStyle?: TimeSliderTooltipStyle;
146
+ /** 'front' (default): the y-axis grid lines are drawn over the layers. 'behind': under them, like chart.js; the labels stay in front. See also setGridOrder(). */
147
+ gridOrder?: TimeSliderGridOrder;
146
148
  }
149
+ export type TimeSliderGridOrder = 'front' | 'behind';
147
150
  export declare class TimeSlider extends TypedEventEmitter<{
148
151
  live: boolean;
149
152
  playing: boolean;
@@ -229,6 +232,7 @@ export declare class TimeSlider extends TypedEventEmitter<{
229
232
  private tooltipStyle;
230
233
  private tickToolTip;
231
234
  private stopWatchingFonts?;
235
+ private _gridOrder;
232
236
  getLayers(): TimeSliderLayer<TimeSliderElement, import("..").TimeSliderStyle>[];
233
237
  getVisibleLayers(): TimeSliderLayer<TimeSliderElement, import("..").TimeSliderStyle>[];
234
238
  constructor(options?: TimeSliderOptions);
@@ -250,6 +254,17 @@ export declare class TimeSlider extends TypedEventEmitter<{
250
254
  get background(): string | undefined;
251
255
  /** Sets the background colour behind the layers and the ticks bar (see TimeSliderOptions.background); undefined goes back to the theme's. */
252
256
  setBackground(background: string | undefined): void;
257
+ get gridOrder(): TimeSliderGridOrder;
258
+ /** Draws the y-axis grid lines in front of the layers ('front', default) or behind them ('behind'). */
259
+ setGridOrder(gridOrder: TimeSliderGridOrder): void;
260
+ /**
261
+ * Grid lines (gridOrder 'behind') and baselines are drawn on a canvas behind the engine's. Only
262
+ * while there is something to show there, the engine clears to transparent and the slider paints
263
+ * the background itself; otherwise the engine draws it as before.
264
+ */
265
+ private updateBehindLayers;
266
+ /** Puts the canvas of lines behind the layers into the slider element, under the engine's canvas. */
267
+ private attachBehindCanvas;
253
268
  /** Sets the tooltip shown on hovering a tick box, see TimeSliderTickTooltip. */
254
269
  setTickTooltip(tickTooltip: TimeSliderTickTooltip): void;
255
270
  /** Sets the style of the hover, tick and selection-label tooltips. */
@@ -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{describeTimeSpan as s,formatDate as n,formatDateRange as o,formatDuration as l,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 H,ONE_YEAR as _,TODAY as A}from"../ticks/TimeSliderConstants.js";import{TimeTicksBar as I}from"../ticks/TimeTicksBar.js";import{YAxisTicks as O}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 I,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:A,value:{minValue:0,maxValue:100}},this.size={width:100,height:100},this.sliderElement=document.createElement("div"),this.yAxisTicks=new O,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.warnedDirection=new Set,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?.())}watchPixelRatio(){if("undefined"==typeof window||!window.matchMedia)return;let e=()=>{};const t=()=>{const i=window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`),s=()=>{e(),this.ticksBar.draw(),this.renderYAxis(),t()};i.addEventListener("change",s),e=()=>i.removeEventListener("change",s)};t(),this.onDestroy.push(()=>e())}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 o=this.tickToolTip.offsetWidth,l=this.tickToolTip.offsetHeight,a=Math.min(Math.max(4,i.left+.5*i.width-.5*o),window.innerWidth-o-4),r=i.top-l-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 o=n(e.startTime),l=n(e.endTime);t.style.left=o+"px",t.style.width=Math.max(0,l-o)+"px",i&&(i.style.left=o-4+"px"),s&&(s.style.left=l-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)),o=this.selectionOptions.columnBand?.color??"#fff",l=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=o,r.style.opacity=String(l),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 o=!1;const l=t=>{o||(o=!0,e.hasPointerCapture(t.pointerId)&&e.releasePointerCapture(t.pointerId),e.removeEventListener("pointermove",n),e.removeEventListener("pointerup",l),e.removeEventListener("pointercancel",l),e.removeEventListener("lostpointercapture",l),this.emit("selectionchange",{ranges:this.getSelection(),phase:"end"}))};this.emit("selectionchange",{ranges:this.getSelection(),phase:"start"}),e.addEventListener("pointermove",n),e.addEventListener("pointerup",l),e.addEventListener("pointercancel",l),e.addEventListener("lostpointercapture",l)})};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 o=this.mouseInSlider(t).t-i;s===-1/0&&n!==1/0?e.endTime=n+o:n===1/0&&s!==-1/0?e.startTime=s+o:s!==-1/0&&n!==1/0&&(e.startTime=s+o,e.endTime=n+o)}})}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,direction:this.isHangingDown(t)?"down":"up"})});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.watchPixelRatio(),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,o=this.selectedLabel.offsetHeight;this.selectedLayerId=s.id;const l=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);l?m-=.5*o:m+=8;const c=.5*n,u=.5*o,g=a.width-.5*n,p=a.height-.5*o,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:o,duration:l}=this.getTimeRange(),a=o+n.x*l,{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,n=t.max;const o=n-s;return{yInLayer:this.isHangingDown(t)?(n-i.y)/o:(i.y-s)/o,toLayerHeight:o/this.getSliderBounds().height}}isHangingDown(e){return"down"===e.direction&&(e instanceof w||(this.warnedDirection.has(e.id)||(this.warnedDirection.add(e.id),console.warn("Layer direction 'down' is only supported for bar (intervals / histogram) layers, ignoring it for",e.name)),!1))}pixelsToModelValue(e,t){const{yInLayer:i,toLayerHeight:s}=this.pixelsToLayerValue(e,t);let n=0,o=100;if(t.model instanceof p){const e=this.valueRanges.get(t.yAxisGroup)||{min:0,max:1e3};n=e.min,o=e.max-n}return{yInModel:n+i*o,toModelHeight:s*o}}layerValueToPixels(e,t){let i=t.min,s=t.max;const n=s-i,o=this.getSliderBounds().height;return(1-(this.isHangingDown(t)?s-n*e:i+n*e))*o}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:o,endTime:l,duration:a}=this.getTimeRange(),r=o+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,o=e instanceof w?m:c,l={minValue:t-.5*s,maxValue:t+.5*s},a=e.model.dataInRange({...o,...l});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:o}=this.getTimeRange(),l=n+s.x*o,a=(t||12)/i*o,r={startTime:l-1,endTime:l+1},h={startTime:l-.5*a,endTime:l+.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:l,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:o}=this.getTimeRange(),l=i+t.x*o,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:l,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(l),i=t.getHours(),s=t.getMinutes(),o=t.getSeconds(),a=n(l,!0,this._locale),r=`${(i<10?"0":"")+i}:${(s<10?"0":"")+s}:${(o<10?"0":"")+o}`;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.renderYAxis(),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 o=.5*(t+i);if(s>=.9*C){const e=X(10*s);n=h(o,e)}else{const e=[{threshold:S,unit:"millennium"},{threshold:k,unit:"century"},{threshold:_,unit:"decade"},{threshold:.9*D,unit:"year"},{threshold:H,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(o,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 o=t+("number"==typeof e?.xRatio?e.xRatio:.5)*s;if(s>=.9*C){const e=X(s/10);n=h(o,e)}else{let e=0;const t=[{threshold:C,unit:"century"},{threshold:S,unit:"decade"},{threshold:k,unit:"year"},{threshold:_,unit:"month"},{threshold:1.1*D,unit:"week"},{threshold:H,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(o,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 o=d(t,i);return o?m(n,o,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),o=Math.abs(n.startTime-t.startTime)>W||Math.abs(n.endTime-t.endTime)>W,l=o?n:i,a=e>0?s(t.startTime,l.startTime,this._locale):s(l.startTime,t.startTime,this._locale);return{range:n,canStep:o,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()?o([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 A;{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 ${l(Date.now()-i)}`);const o=this.syncMaxValuesForLayer(e);this.layerLoaders.set(e.id,t),this.layerUnloaders.set(e.id,()=>{o(),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)),e.on("direction",()=>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,o=Math.min(i,n),l=Math.max(i,n),a=(t-e)/(l-o);let r=e-o*a,h=t+(1-l)*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()},o=()=>{i()};this.sliderElement.addEventListener("touchstart",s,{passive:!1}),this.sliderElement.addEventListener("touchmove",n,{passive:!1}),this.sliderElement.addEventListener("touchend",o),this.sliderElement.addEventListener("touchcancel",o),this.onDestroy.push(()=>{this.sliderElement.removeEventListener("touchstart",s),this.sliderElement.removeEventListener("touchmove",n),this.sliderElement.removeEventListener("touchend",o),this.sliderElement.removeEventListener("touchcancel",o)})}initControls(){const e=new t;this.dragging=!1;let i,s=0,n=0,o=0;e.add(this.initHoverListener());const l=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:l,duration:a}=this.getTimeRange();s=l+i*a,n=Date.now(),o=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(o-t.absolute.x),i=Math.min(o,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(o-t.absolute.x)<10)return;e.preventDefault();const i=t.normalized.x,{startTime:l,endTime:a}=this.getTimeRange(),r=l+i*(a-l)-s;this.setTimeRange({startTime:l-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(),o=i+t*(n-i);"zoom"===this.selectRangeMode&&this.fit({range:{startTime:Math.min(s,o),endTime:Math.max(s,o)},animate:1e3}),"select"===this.selectRangeMode&&this.select(null,{startTime:Math.min(s,o),endTime:Math.max(s,o)}),"createSelection"===this.selectRangeMode&&this.setSelection([...this.getSelection(),{startTime:Math.min(s,o),endTime:Math.max(s,o)}])}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",l),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",l),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(),o=t?.absolute?e:e*n;this.fit({...t,range:{startTime:i-o,endTime:s-o}})}zoom(e,t){const i="number"==typeof t?.x?t.x:.5,{startTime:s,endTime:n,duration:o}=this.getTimeRange(),l={startTime:s-e*o*i,endTime:n+e*o*(1-i)},a=l.endTime-l.startTime,r=l.startTime+.5*a;a>P&&(l.startTime=r-.5*P,l.endTime=r+.5*P),a<U&&(l.startTime=r-.5*U,l.endTime=r+.5*U),this.fit({range:l,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)}
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 o,formatDuration as l,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 g}from"../data/histogram/TimeSliderDynamicHistogram.js";import{getTimeArray as u}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 B,ONE_MINUTE as M,ONE_MONTH as D,ONE_SECOND as _,ONE_WEEK as O,ONE_YEAR as A,TODAY as H}from"../ticks/TimeSliderConstants.js";import{TimeTicksBar as z}from"../ticks/TimeTicksBar.js";import{YAxisTicks as I}from"../ticks/YAxisTicks.js";import{resolveFont as $,toCssFont as F}from"../common/Font.js";const U=_,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 z,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:H,value:{minValue:0,maxValue:100}},this.size={width:100,height:100},this.sliderElement=document.createElement("div"),this.yAxisTicks=new I,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._gridOrder="front",this.updateValueRangesQueue=new Map,this.showingStats=!1,this._engineReady=!1,this.mouse=null,this.updatingLabel=!1,this.warnedDirection=new Set,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*_,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._gridOrder=i?.gridOrder??"front",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?.())}watchPixelRatio(){if("undefined"==typeof window||!window.matchMedia)return;let e=()=>{};const t=()=>{const i=window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`),s=()=>{e(),this.ticksBar.draw(),this.renderYAxis(),t()};i.addEventListener("change",s),e=()=>i.removeEventListener("change",s)};t(),this.onDestroy.push(()=>e())}get background(){return this._background}setBackground(e){this._background=e,this.engine?.setBackground(e),this.ticksBar.setBackground(e),this.updateBehindLayers()}get gridOrder(){return this._gridOrder}setGridOrder(e){this._gridOrder=e,this.updateBehindLayers(),this.renderYAxis()}updateBehindLayers(){const e="behind"===this._gridOrder||this.getVisibleLayers().some(e=>e.baseline);this.engine?.setTransparent(e),this.sliderElement.style.background=e?this._background??("dark"===this.theme?"#111":"#eee"):""}attachBehindCanvas(){const e=this.yAxisTicks.behindCanvas;e.style.height=this.sliderElement.style.height,e.parentElement!==this.sliderElement&&this.sliderElement.insertBefore(e,this.sliderElement.firstChild)}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 o=this.tickToolTip.offsetWidth,l=this.tickToolTip.offsetHeight,a=Math.min(Math.max(4,i.left+.5*i.width-.5*o),window.innerWidth-o-4),r=i.top-l-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 o=n(e.startTime),l=n(e.endTime);t.style.left=o+"px",t.style.width=Math.max(0,l-o)+"px",i&&(i.style.left=o-4+"px"),s&&(s.style.left=l-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)),o=this.selectionOptions.columnBand?.color??"#fff",l=this.selectionOptions.columnBand?.opacity??.15;e.forEach(({layer:e,elements:t})=>{const i=t.map(u),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=o,r.style.opacity=String(l),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 o=!1;const l=t=>{o||(o=!0,e.hasPointerCapture(t.pointerId)&&e.releasePointerCapture(t.pointerId),e.removeEventListener("pointermove",n),e.removeEventListener("pointerup",l),e.removeEventListener("pointercancel",l),e.removeEventListener("lostpointercapture",l),this.emit("selectionchange",{ranges:this.getSelection(),phase:"end"}))};this.emit("selectionchange",{ranges:this.getSelection(),phase:"start"}),e.addEventListener("pointermove",n),e.addEventListener("pointerup",l),e.addEventListener("pointercancel",l),e.addEventListener("lostpointercapture",l)})};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 o=this.mouseInSlider(t).t-i;s===-1/0&&n!==1/0?e.endTime=n+o:n===1/0&&s!==-1/0?e.startTime=s+o:s!==-1/0&&n!==1/0&&(e.startTime=s+o,e.endTime=n+o)}})}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,direction:this.isHangingDown(t)?"down":"up"})});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})}});const i=this.getVisibleLayers().filter(e=>e.baseline).map(e=>({y:this.isHangingDown(e)?e.max:e.min,color:e.baseline.color,width:e.baseline.width??1}));this.yAxisTicks.render(t,{gridBehind:"behind"===this._gridOrder,baselines:i})}setTheme(e){this.theme=e,this.updateBehindLayers(),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),this.attachBehindCanvas(),this.updateBehindLayers(),this.renderYAxis()}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)`,this.yAxisTicks.behindCanvas.style.height=this.sliderElement.style.height}}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.watchPixelRatio(),this.initSelectLabel(),this.setSize(this.sliderElement.clientWidth,this.sliderElement.clientHeight),this.syncSize(),this.setTimeRange(Y(t)),this.setValueRange(Y(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,o=this.selectedLabel.offsetHeight;this.selectedLayerId=s.id;const l=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);l?m-=.5*o:m+=8;const c=.5*n,g=.5*o,u=a.width-.5*n,p=a.height-.5*o,y=Math.min(u,Math.max(c,h)),T=Math.min(p,Math.max(g,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 g;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:o,duration:l}=this.getTimeRange(),a=o+n.x*l,{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,n=t.max;const o=n-s;return{yInLayer:this.isHangingDown(t)?(n-i.y)/o:(i.y-s)/o,toLayerHeight:o/this.getSliderBounds().height}}isHangingDown(e){return"down"===e.direction&&(e instanceof w||(this.warnedDirection.has(e.id)||(this.warnedDirection.add(e.id),console.warn("Layer direction 'down' is only supported for bar (intervals / histogram) layers, ignoring it for",e.name)),!1))}pixelsToModelValue(e,t){const{yInLayer:i,toLayerHeight:s}=this.pixelsToLayerValue(e,t);let n=0,o=100;if(t.model instanceof p){const e=this.valueRanges.get(t.yAxisGroup)||{min:0,max:1e3};n=e.min,o=e.max-n}return{yInModel:n+i*o,toModelHeight:s*o}}layerValueToPixels(e,t){let i=t.min,s=t.max;const n=s-i,o=this.getSliderBounds().height;return(1-(this.isHangingDown(t)?s-n*e:i+n*e))*o}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:o,endTime:l,duration:a}=this.getTimeRange(),r=o+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,o=e instanceof w?m:c,l={minValue:t-.5*s,maxValue:t+.5*s},a=e.model.dataInRange({...o,...l});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:o}=this.getTimeRange(),l=n+s.x*o,a=(t||12)/i*o,r={startTime:l-1,endTime:l+1},h={startTime:l-.5*a,endTime:l+.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:l,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:o}=this.getTimeRange(),l=i+t.x*o,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:l,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(l),i=t.getHours(),s=t.getMinutes(),o=t.getSeconds(),a=n(l,!0,this._locale),r=`${(i<10?"0":"")+i}:${(s<10?"0":"")+s}:${(o<10?"0":"")+o}`;let d="";const m=this.ticksBar.cache.high.spacing;(void 0===this.hoverOptions.date?m>=B: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,g=36;let u=this.mouse.x+10,p=this.mouse.y+10;const y=e.left+e.width-c-5,T=e.top+e.height-g-5;u>y&&(u=y),p>T&&(p=T),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="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.renderYAxis(),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 o=.5*(t+i);if(s>=.9*C){const e=X(10*s);n=h(o,e)}else{const e=[{threshold:S,unit:"millennium"},{threshold:k,unit:"century"},{threshold:A,unit:"decade"},{threshold:.9*D,unit:"year"},{threshold:O,unit:"month"},{threshold:V,unit:"week"},{threshold:B,unit:"day"},{threshold:M,unit:"hour"},{threshold:_,unit:"minute"},{threshold:-1,unit:"second"}];let t=0;for(;s<.9*e[t].threshold;)t++;const i=e[t].unit;n=m(o,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 o=t+("number"==typeof e?.xRatio?e.xRatio:.5)*s;if(s>=.9*C){const e=X(s/10);n=h(o,e)}else{let e=0;const t=[{threshold:C,unit:"century"},{threshold:S,unit:"decade"},{threshold:k,unit:"year"},{threshold:A,unit:"month"},{threshold:1.1*D,unit:"week"},{threshold:O,unit:"day"},{threshold:V,unit:"hour"},{threshold:B,unit:"minute"},{threshold:M,unit:"second"}].reverse();for(;s>1.1*t[e].threshold;)e++;const i=t[e].unit;n=m(o,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 o=d(t,i);return o?m(n,o,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),o=Math.abs(n.startTime-t.startTime)>j||Math.abs(n.endTime-t.endTime)>j,l=o?n:i,a=e>0?s(t.startTime,l.startTime,this._locale):s(l.startTime,t.startTime,this._locale);return{range:n,canStep:o,span:a,label:K(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(j,.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()?o([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 H;{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 ${l(Date.now()-i)}`);const o=this.syncMaxValuesForLayer(e);this.layerLoaders.set(e.id,t),this.layerUnloaders.set(e.id,()=>{o(),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(),this.updateBehindLayers()}),e.on("min",()=>this.onLayerBandChange(e)),e.on("max",()=>this.onLayerBandChange(e)),e.on("direction",()=>this.onLayerBandChange(e)),e.on("baseline",()=>{this.updateBehindLayers(),this.renderYAxis()})];this.layerListenerStoppers.set(e.id,()=>t.forEach(e=>e())),this.updateBehindLayers(),this.renderYAxis()}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),this.updateBehindLayers(),this.renderYAxis())}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,o=Math.min(i,n),l=Math.max(i,n),a=(t-e)/(l-o);let r=e-o*a,h=t+(1-l)*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()},o=()=>{i()};this.sliderElement.addEventListener("touchstart",s,{passive:!1}),this.sliderElement.addEventListener("touchmove",n,{passive:!1}),this.sliderElement.addEventListener("touchend",o),this.sliderElement.addEventListener("touchcancel",o),this.onDestroy.push(()=>{this.sliderElement.removeEventListener("touchstart",s),this.sliderElement.removeEventListener("touchmove",n),this.sliderElement.removeEventListener("touchend",o),this.sliderElement.removeEventListener("touchcancel",o)})}initControls(){const e=new t;this.dragging=!1;let i,s=0,n=0,o=0;e.add(this.initHoverListener());const l=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:l,duration:a}=this.getTimeRange();s=l+i*a,n=Date.now(),o=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(o-t.absolute.x),i=Math.min(o,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(o-t.absolute.x)<10)return;e.preventDefault();const i=t.normalized.x,{startTime:l,endTime:a}=this.getTimeRange(),r=l+i*(a-l)-s;this.setTimeRange({startTime:l-r,endTime:a-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(),o=i+t*(n-i);"zoom"===this.selectRangeMode&&this.fit({range:{startTime:Math.min(s,o),endTime:Math.max(s,o)},animate:1e3}),"select"===this.selectRangeMode&&this.select(null,{startTime:Math.min(s,o),endTime:Math.max(s,o)}),"createSelection"===this.selectRangeMode&&this.setSelection([...this.getSelection(),{startTime:Math.min(s,o),endTime:Math.max(s,o)}])}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",l),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",l),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(),o=t?.absolute?e:e*n;this.fit({...t,range:{startTime:i-o,endTime:s-o}})}zoom(e,t){const i="number"==typeof t?.x?t.x:.5,{startTime:s,endTime:n,duration:o}=this.getTimeRange(),l={startTime:s-e*o*i,endTime:n+e*o*(1-i)},a=l.endTime-l.startTime,r=l.startTime+.5*a;a>P&&(l.startTime=r-.5*P,l.endTime=r+.5*P),a<U&&(l.startTime=r-.5*U,l.endTime=r+.5*U),this.fit({range:l,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:_}),this.play()}this.emit("live",e)}}}function Y(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 j=_,W={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 K(e,t,i){const s=W[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)}
@@ -20,6 +20,10 @@ export declare abstract class TimeSliderEngine<T> extends TypedEventEmitter<{
20
20
  setTheme(theme: 'dark' | 'light'): void;
21
21
  /** Sets the clear colour behind the layers, kept across theme changes; undefined goes back to the theme's default. */
22
22
  setBackground(background: string | undefined): void;
23
+ /** When true the engine clears to transparent, so content behind its canvas (grid lines, baselines, the background painted by the slider) shows between the layers. */
24
+ transparent: boolean;
25
+ setTransparent(transparent: boolean): void;
26
+ protected getClearAlpha(): number;
23
27
  protected getClearColor(): string;
24
28
  getTimeRange(): {
25
29
  duration: 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)}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))}}
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",this.transparent=!1}setTheme(e){this.theme=e,this.setTimeRange(this.timeRange)}setBackground(e){this.background=e,this.setTheme(this.theme)}setTransparent(e){e!==this.transparent&&(this.transparent=e,this.setTheme(this.theme))}getClearAlpha(){return this.transparent?0:1}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))}}
@@ -14,6 +14,8 @@ export type TimeSliderLayerOptions<E extends TimeSliderElement, S extends TimeSl
14
14
  max?: number;
15
15
  /** 'up' (default): value 0 at the band's min (bottom), bars grow upwards. 'down': value 0 at the band's max (top), bars hang down. The layer's y-axis, hover values and picking follow it. Bar layers (intervals, histograms) only for now. */
16
16
  direction?: TimeSliderLayerDirection;
17
+ /** A horizontal line across the whole slider at the layer's value-0 edge (band min for 'up', max for 'down'), drawn behind the bars and on whole device pixels. */
18
+ baseline?: TimeSliderLayerBaseline;
17
19
  visible?: boolean;
18
20
  style?: S;
19
21
  styler?: TimeSliderStyler<E, S>;
@@ -21,12 +23,18 @@ export type TimeSliderLayerOptions<E extends TimeSliderElement, S extends TimeSl
21
23
  yAxis?: YAxisSettings;
22
24
  };
23
25
  export type TimeSliderLayerDirection = 'up' | 'down';
26
+ /** See TimeSliderLayerOptions.baseline. */
27
+ export type TimeSliderLayerBaseline = {
28
+ color: string;
29
+ width?: number;
30
+ };
24
31
  export declare abstract class TimeSliderLayer<E extends TimeSliderElement = TimeSliderElement, S extends TimeSliderStyle = TimeSliderStyle> extends TypedEventEmitter<{
25
32
  selection: E[];
26
33
  name: string;
27
34
  min: number;
28
35
  max: number;
29
36
  direction: TimeSliderLayerDirection;
37
+ baseline: TimeSliderLayerBaseline | undefined;
30
38
  visible: boolean;
31
39
  loaded?: boolean;
32
40
  invalidate: string;
@@ -37,6 +45,7 @@ export declare abstract class TimeSliderLayer<E extends TimeSliderElement = Time
37
45
  private _min;
38
46
  private _max;
39
47
  private _direction;
48
+ private _baseline?;
40
49
  private _name;
41
50
  private _visible;
42
51
  private _loaded;
@@ -62,6 +71,9 @@ export declare abstract class TimeSliderLayer<E extends TimeSliderElement = Time
62
71
  get direction(): TimeSliderLayerDirection;
63
72
  /** See TimeSliderLayerOptions.direction. */
64
73
  set direction(newDirection: TimeSliderLayerDirection);
74
+ get baseline(): TimeSliderLayerBaseline | undefined;
75
+ /** See TimeSliderLayerOptions.baseline; undefined removes it. */
76
+ set baseline(newBaseline: TimeSliderLayerBaseline | undefined);
65
77
  get timeSlider(): TimeSlider | undefined;
66
78
  set timeSlider(timeSlider: TimeSlider | undefined);
67
79
  get name(): string;
@@ -1 +1 @@
1
- import{v4 as i}from"uuid";import{TypedEventEmitter as t}from"../event/TypedEventEmitter.js";export class TimeSliderLayer extends t{constructor(t,e){super(),this.model=t,this._visible=!0,this._loaded=!1,this.id=e?.id||i(),this.style=e?.style||this.getDefaultStyle(),this._styler=e?.styler,this.labeler=e?.labeler,this.yAxis=e?.yAxis||{},this._name=e?.name||t.name||"Unknown",this._min=e?.min||0,this._max=e?.max||1,this._direction=e?.direction||"up",this._visible="boolean"!=typeof e?.visible||e.visible}get yAxisGroup(){return this.yAxis.group||this.id}get styler(){return this._styler}set styler(i){this._styler=i,this.invalidateAll()}invalidate(i){const t="string"==typeof i?i:i.id;this.emit("invalidate",t)}invalidateAll(){this.emit("invalidateAll")}select(i){this.emit("selection",i)}get min(){return this._min}set min(i){i!==this._min&&(this._min=i,this.emit("min",i))}get max(){return this._max}set max(i){i!==this._max&&(this._max=i,this.emit("max",i))}get direction(){return this._direction}set direction(i){i!==this._direction&&(this._direction=i,this.emit("direction",i))}get timeSlider(){return this._timeSlider}set timeSlider(i){this._timeSlider=i}get name(){return this._name}set name(i){i!==this._name&&(this._name=i,this.emit("name",i))}get visible(){return this._visible}set visible(i){i!==this._visible&&(this._visible=i,this.emit("visible",i),this.shouldModelLoad())}get loaded(){return this._loaded}set loaded(i){i!==this._loaded&&(this._loaded=i,this.shouldModelLoad(),this.emit("loaded",i))}shouldModelLoad(){const i=this.loaded&&this.visible;this.model.loaded(this.id,i)}}
1
+ import{v4 as i}from"uuid";import{TypedEventEmitter as e}from"../event/TypedEventEmitter.js";export class TimeSliderLayer extends e{constructor(e,t){super(),this.model=e,this._visible=!0,this._loaded=!1,this.id=t?.id||i(),this.style=t?.style||this.getDefaultStyle(),this._styler=t?.styler,this.labeler=t?.labeler,this.yAxis=t?.yAxis||{},this._name=t?.name||e.name||"Unknown",this._min=t?.min||0,this._max=t?.max||1,this._direction=t?.direction||"up",this._baseline=t?.baseline,this._visible="boolean"!=typeof t?.visible||t.visible}get yAxisGroup(){return this.yAxis.group||this.id}get styler(){return this._styler}set styler(i){this._styler=i,this.invalidateAll()}invalidate(i){const e="string"==typeof i?i:i.id;this.emit("invalidate",e)}invalidateAll(){this.emit("invalidateAll")}select(i){this.emit("selection",i)}get min(){return this._min}set min(i){i!==this._min&&(this._min=i,this.emit("min",i))}get max(){return this._max}set max(i){i!==this._max&&(this._max=i,this.emit("max",i))}get direction(){return this._direction}set direction(i){i!==this._direction&&(this._direction=i,this.emit("direction",i))}get baseline(){return this._baseline}set baseline(i){this._baseline=i,this.emit("baseline",i)}get timeSlider(){return this._timeSlider}set timeSlider(i){this._timeSlider=i}get name(){return this._name}set name(i){i!==this._name&&(this._name=i,this.emit("name",i))}get visible(){return this._visible}set visible(i){i!==this._visible&&(this._visible=i,this.emit("visible",i),this.shouldModelLoad())}get loaded(){return this._loaded}set loaded(i){i!==this._loaded&&(this._loaded=i,this.shouldModelLoad(),this.emit("loaded",i))}shouldModelLoad(){const i=this.loaded&&this.visible;this.model.loaded(this.id,i)}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protomaze/time",
3
- "version": "1.7.0",
3
+ "version": "1.8.1",
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 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
+ 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},O)=>{const P=a(()=>new d(M),[]);r(()=>{M?.showCurrent&&(P.showCurrent=M?.showCurrent)},[M.showCurrent]),r(()=>{M?.showTicks&&(P.showTicks=M?.showTicks)},[M.showTicks]),r(()=>{M?.playSpeed&&(P.playSpeed=M?.playSpeed)},[M.playSpeed]),r(()=>{M?.liveRange&&(P.liveRange=M?.liveRange)},[M.liveRange]),r(()=>{M?.locale&&P.setLocale(M.locale)},[M.locale]),r(()=>{M?.weekLabel&&P.setWeekLabel(M.weekLabel,M.weekLabelPrefix)},[M.weekLabel,M.weekLabelPrefix]),r(()=>{P.setTickLabelStyler(M?.tickLabelStyler)},[M.tickLabelStyler]),r(()=>{P.setLimits(M?.limits)},[M.limits?.startTime,M.limits?.endTime]),r(()=>{P.setFont(M?.font)},[M.font?.family,M.font?.weight,M.font?.size]),r(()=>{P.setBackground(M?.background)},[M.background]),r(()=>{P.setTickTooltip(M?.tickTooltip??!0)},[M.tickTooltip]),r(()=>{P.setTooltipStyle(M?.tooltipStyle??{})},[JSON.stringify(M.tooltipStyle??{})]),r(()=>{P.setGridOrder(M?.gridOrder??"front")},[M.gridOrder]);const[,B]=s(0);r(()=>P.on("locale",()=>B(e=>e+1)),[P]);const W=N||4,F=22+W+8+"px",G=22+W+22+W+8+"px",H=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;P.setTimeRange({startTime:e,endTime:n})}se.current.delete(e)}},[J,t,le]),r(()=>{const e=new c;return e.add(P.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()},[P]),r(()=>{U(!1);const e=new c;return P.init(X.current).then(()=>U(!0)),e.add(P.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(P.on("playing",Y)),e.add(P.on("live",q)),e.add(P.on("current",ne)),e.disposeLater()},[P]),r(()=>{z?.forEach(e=>P.addLayerLoader(e))},[z]),r(()=>{"boolean"==typeof M.showCurrent&&(P.showCurrent=M.showCurrent)},[M.showCurrent]),i(O,()=>({get timeSlider(){return P}})),r(()=>{if(M.showCurrent){const e=setInterval(()=>{A.current&&(A.current.available=Date.now())},10);return()=>clearInterval(e)}},[M.showCurrent]),r(()=>{J&&P.showStats(!0===D)},[J,D]),r(()=>{},[J]),r(()=>{J&&P.setEngine(I)},[J,I]),r(()=>{if(J){const e=new Set,t=new Set;P.getLayers().forEach(e=>t.add(e.id)),_.forEach(n=>{t.delete(n.id),e.add(n.id)}),P.getLayers().forEach(t=>e.delete(t.id));const n=_.filter(t=>e.has(t.id)),r=P.getLayers().filter(e=>t.has(e.id));r.length===P.getLayers().length&&n.length>0&&P.setTimeRange(n[0].model.getTimeRange()),r.forEach(e=>P.removeLayer(e)),n.forEach(e=>P.addLayer(e))}},[J,_]);r(()=>{const e=e=>{re.current&&e.target&&e.target.parentElement?.getAttribute("layerpanelbutton")!==P.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=P.current,{duration:n,startTime:r}=P.getTimeRange(),i=(t-r)/n;i>1||i<0?P.setTimeRange({startTime:e-.5*n,endTime:e+.5*n}):P.setTimeRange({startTime:e-i*n,endTime:e+(1-i)*n}),P.setCurrent(e)};r(()=>{const e=()=>{if(H.current){let e=560;M.showCurrent&&(e+=160),p&&(e-=30),C&&(e-=160),E&&(e-=140),L&&(e-=185);const t=H.current.getBoundingClientRect().width;Q(t<e)}};e();const t=new ResizeObserver(e);return H.current&&t.observe(H.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&&P.setTheme(S)},[S]),e.createElement("div",{ref:H,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:F,left:"8px",maxWidth:"200px",display:"flex",gap:"8px",alignItems:"center",paddingRight:"8px",paddingBottom:"8px",paddingTop:"8px",flexDirection:"column",zIndex:1e3,background:ue?"#222":"#ddd"}},P.getLayers().map(t=>e.createElement(T,{key:t.id,layer:t,timeSlider:P,isDarkMode:ue}))),e.createElement("div",{style:{position:"absolute",top:"8px",left:"8px",display:"flex",gap:"8px",alignItems:"center"}},e.createElement(f,{layerpanelbutton:P.id,iconName:"layers",onClick:e=>ee(e=>!e)}),e.createElement(f,{iconName:"skip_previous",onClick:()=>P.up({animate:1e3}),rotate:90}),!K&&!C&&e.createElement("span",{style:{...y}},m(ie)),e.createElement(f,{iconName:"arrow_range",onClick:()=>P.fit({animate:3e3})}),o&&e.createElement(f,{iconName:"ink_selection",active:ce,onClick:()=>{me(!0),P.startSelectingRange(()=>me(!1))}})),K&&!R&&M.showCurrent&&!C&&e.createElement("div",{style:{position:"absolute",top:F,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?F:"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=>P.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=>P.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=>P.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:()=>{P.live?P.pause():P.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:()=>P.previous({animate:1e3})}),!p&&!V&&e.createElement(f,{iconName:"play_arrow",onClick:()=>P.play()}),!p&&V&&e.createElement(f,{iconName:"pause",onClick:()=>P.pause()}),e.createElement(f,{iconName:"skip_next",onClick:()=>P.next({animate:1e3})})),M.showCurrent&&e.createElement(u,{ref:A,value:te,min:ie[0],max:ie[1],onValue:e=>{P.pause(),P.setCurrent(e)},onPointerMove:e=>P.updateHover({x:e.clientX,y:e.clientY}),onPointerLeave:()=>P.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?G:F,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])}
@@ -9,8 +9,13 @@ export type TimeSliderIntervalsStyle = TimeSliderStyle & {
9
9
  color?: string;
10
10
  opacity?: number;
11
11
  width?: number;
12
- /** 'all' (default) draws a border on every side, including between stacked segments. 'vertical' only draws left/right, between neighbouring bars. */
13
- sides?: 'all' | 'vertical';
12
+ /**
13
+ * 'all' (default) draws a border on every side, including between stacked segments. 'vertical' only draws
14
+ * left/right, between neighbouring bars. 'vertical+base' also draws the value-0 edge (the bottom, or the top
15
+ * for a layer hanging down) at the same width, so two bars meeting base to base are divided exactly like two
16
+ * neighbours: by one border width when centred (the bars overlap there), two when not. In whole device pixels.
17
+ */
18
+ sides?: 'all' | 'vertical' | 'vertical+base';
14
19
  /** Anti-aliasing ramp between border and fill, in pixels. 0 gives a hard edge (crisp borders between neighbouring bars). Defaults to 1.5 (2D) / 1 (3D). */
15
20
  smoothing?: number;
16
21
  };
@@ -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{getDevicePixelRatio as x,snapLinesToPixels as u}from"./PixelSnap.js";import{getStartOfWeek as v}from"../common/TimeTools.js";import{TimeSliderEngine as f}from"../engine/TimeSliderEngine.js";import{ONE_MONTH as b,ONE_SECOND as g,ONE_WEEK as y}from"../ticks/TimeSliderConstants.js";import{TimeTicksBar as C}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 f{constructor(e){super(new l),this.camera=new ScaledPerspectiveCamera,this.antialias=e?.antialias??!0}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:this.antialias});l.setPixelRatio(x());const p={value:new h};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 f=new s,T=new s;f.scale.x=g,f.position.z=.9,this.scene.add(f);const P=new d(100,1),z=new n({color:"#444"}),S=new a(P,z);S.position.z=.5,S.position.y=-.001,S.rotateX(-.5*Math.PI),f.add(S);const j=new a(P,z);j.position.z=.5,j.position.y=-.001,j.rotateX(-.5*Math.PI),T.add(j);const D=new o({color:"#444"}),M=(new e).setFromPoints([new m(0,0,0),new m(0,1,0),new m(0,1,1)]),V=(new e).setFromPoints([new m(-1,0,0),new m(1,0,0)]),R=new i(V,D),W=new i(V,D);W.position.y=1,T.add(R),T.add(W),T.position.z=.9,this.scene.add(T);const k=[],E=new o({color:"#000"}),I=(new e).setFromPoints([new m(0,0,1),new m(0,0,-1e3)]),F=new s;T.add(F);const X=[];let K,O,Y,q;const A=()=>{Date.now();const e=this.getSize().width,{startTime:t,duration:s,endTime:o}=this.getTimeRange(),a=s,[n]=C.getSpacing(e,a),r=.5*(o+t);if(n<b){T.visible=!1,f.scale.x=n;let e=r%n;n>=y&&(e=r-v(r)),f.position.x=-e,f.visible=!0}else{f.visible=!1;let e=0,a=0;C.generateTicks(n,t-3*s,o+3*s,(n,r)=>{t-s<=n&&n<=o+s&&(k[e]||(k[e]=new i(M,D),T.add(k[e])),k[e].position.x=n,k[e].visible=!0,e++),X[a]||(X[a]=new i(I,E),T.add(X[a])),X[a].position.x=n,X[a].visible=!0,a++});for(let e=a;e<X.length;e++)X[e].visible=!1;for(let t=e;t<k.length;t++)k[t].visible=!1;q&&(q.scale.x=1e3*s),j.scale.x=s,j.position.x=r,q&&(q.position.x=-500*s),R.scale.x=1e3*s,W.scale.x=1e3*s,T.position.x=-r,T.visible=!0}};this.onThemeChange=e=>{const s="dark"===e?"#000":"#fff",i="dark"===e?"#444":"#bbb";this.renderer?.setClearColor(this.getClearColor()),K&&f.remove(K),K?.dispose(),K=new t(1e3,1e3,s,s),f.add(K),Y&&f.remove(Y),Y?.dispose(),Y=new t(1e3,1e3,i,i),Y.position.z=500,Y.position.y=1,f.add(Y),O&&f.remove(O),O?.dispose(),O=new t(1e3,1e3,i,i),O.position.y=-499,O.rotateX(-.5*Math.PI),f.add(O),q&&T.remove(q),q?.dispose(),q=new t(200,200,s,s),q.position.z=-101,T.add(q),z.color.set(i),D.color.set(i),E.color.set(s),A()},this.onThemeChange(this.theme),this.disposables.add(this.on("time",A)),this.disposables.add(this.on("size",A)),this.updateValueRange({minValue:0,maxValue:1});let B=!0;this.disposables.add(()=>B=!1),this.disposables.add(()=>this.scene.clear()),this.disposables.add(()=>l.dispose());const H=()=>{B&&(l.getPixelRatio()!==x()&&l.setPixelRatio(x()),u(this.scene,l,p),l.render(this.scene,this.camera),this.stats&&this.stats.update(),requestAnimationFrame(H))};H()}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?.setPixelRatio(x()),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{getDevicePixelRatio as x,snapLinesToPixels as u}from"./PixelSnap.js";import{getStartOfWeek as v}from"../common/TimeTools.js";import{TimeSliderEngine as f}from"../engine/TimeSliderEngine.js";import{ONE_MONTH as g,ONE_SECOND as b,ONE_WEEK as y}from"../ticks/TimeSliderConstants.js";import{TimeTicksBar as C}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 f{constructor(e){super(new l),this.camera=new ScaledPerspectiveCamera,this.antialias=e?.antialias??!0}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:this.antialias,alpha:!0});l.domElement.style.position="relative",l.setPixelRatio(x());const p={value:new h};this.renderer=l,this.disposables=new w,this.container=r,this.renderer.setClearColor(this.getClearColor(),this.getClearAlpha()),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 f=new s,T=new s;f.scale.x=b,f.position.z=.9,this.scene.add(f);const P=new d(100,1),z=new n({color:"#444"}),S=new a(P,z);S.position.z=.5,S.position.y=-.001,S.rotateX(-.5*Math.PI),f.add(S);const j=new a(P,z);j.position.z=.5,j.position.y=-.001,j.rotateX(-.5*Math.PI),T.add(j);const D=new o({color:"#444"}),M=(new e).setFromPoints([new m(0,0,0),new m(0,1,0),new m(0,1,1)]),V=(new e).setFromPoints([new m(-1,0,0),new m(1,0,0)]),R=new i(V,D),W=new i(V,D);W.position.y=1,T.add(R),T.add(W),T.position.z=.9,this.scene.add(T);const k=[],E=new o({color:"#000"}),I=(new e).setFromPoints([new m(0,0,1),new m(0,0,-1e3)]),F=new s;T.add(F);const A=[];let X,K,O,Y;const q=()=>{Date.now();const e=this.getSize().width,{startTime:t,duration:s,endTime:o}=this.getTimeRange(),a=s,[n]=C.getSpacing(e,a),r=.5*(o+t);if(n<g){T.visible=!1,f.scale.x=n;let e=r%n;n>=y&&(e=r-v(r)),f.position.x=-e,f.visible=!0}else{f.visible=!1;let e=0,a=0;C.generateTicks(n,t-3*s,o+3*s,(n,r)=>{t-s<=n&&n<=o+s&&(k[e]||(k[e]=new i(M,D),T.add(k[e])),k[e].position.x=n,k[e].visible=!0,e++),A[a]||(A[a]=new i(I,E),T.add(A[a])),A[a].position.x=n,A[a].visible=!0,a++});for(let e=a;e<A.length;e++)A[e].visible=!1;for(let t=e;t<k.length;t++)k[t].visible=!1;Y&&(Y.scale.x=1e3*s),j.scale.x=s,j.position.x=r,Y&&(Y.position.x=-500*s),R.scale.x=1e3*s,W.scale.x=1e3*s,T.position.x=-r,T.visible=!0}};this.onThemeChange=e=>{const s="dark"===e?"#000":"#fff",i="dark"===e?"#444":"#bbb";this.renderer?.setClearColor(this.getClearColor(),this.getClearAlpha()),X&&f.remove(X),X?.dispose(),X=new t(1e3,1e3,s,s),f.add(X),O&&f.remove(O),O?.dispose(),O=new t(1e3,1e3,i,i),O.position.z=500,O.position.y=1,f.add(O),K&&f.remove(K),K?.dispose(),K=new t(1e3,1e3,i,i),K.position.y=-499,K.rotateX(-.5*Math.PI),f.add(K),Y&&T.remove(Y),Y?.dispose(),Y=new t(200,200,s,s),Y.position.z=-101,T.add(Y),z.color.set(i),D.color.set(i),E.color.set(s),q()},this.onThemeChange(this.theme),this.disposables.add(this.on("time",q)),this.disposables.add(this.on("size",q)),this.updateValueRange({minValue:0,maxValue:1});let B=!0;this.disposables.add(()=>B=!1),this.disposables.add(()=>this.scene.clear()),this.disposables.add(()=>l.dispose());const H=()=>{B&&(l.getPixelRatio()!==x()&&l.setPixelRatio(x()),u(this.scene,l,p),l.render(this.scene,this.camera),this.stats&&this.stats.update(),requestAnimationFrame(H))};H()}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?.setPixelRatio(x()),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 t,Group as s,Line as i,LineBasicMaterial as o,OrthographicCamera as a,Scene as n,Vector2 as r,Vector3 as h,WebGLRenderer as l}from"three";import m from"three/examples/jsm/libs/stats.module.js";import{Disposables as d}from"../common/Disposables.js";import{getDevicePixelRatio as c,snapLinesToPixels as p}from"./PixelSnap.js";import{TimeSliderEngine as b}from"../engine/TimeSliderEngine.js";import{TimeTicksBar as g}from"../ticks/TimeTicksBar.js";import{getStartOfWeek as u}from"../common/TimeTools.js";import{ONE_MONTH as T,ONE_SECOND as x,ONE_WEEK as w}from"../ticks/TimeSliderConstants.js";export class ThreeTimeSliderEngine extends b{constructor(e){super(new n),this.camera=new a(0,1,1,0),this.antialias=e?.antialias??!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 l({antialias:this.antialias});m.setPixelRatio(c());const b={value:new r};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 C=new s,f=new s;C.scale.x=x,this.scene.add(C);const v=new o({color:n?"#444":"#bbb"}),j=(new e).setFromPoints([new h(0,0,0),new h(0,1,0),new h(0,1,1)]);this.scene.add(f);const P=[];let S;const y=()=>{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<T){f.visible=!1,C.scale.x=n;let e=r%n;n>=w&&(e=r-u(r)),C.position.x=-e,C.visible=!0}else{C.visible=!1;let e=0;g.generateTicks(n,t-3*s,o+3*s,(a,n)=>{t-s<=a&&a<=o+s&&(P[e]||(P[e]=new i(j,v),f.add(P[e])),P[e].position.x=a,P[e].visible=!0,e++)});for(let t=e;t<P.length;t++)P[t].visible=!1;f.position.x=-r,f.visible=!0}};this.onThemeChange=e=>{const s="dark"===e?"#444":"#bbb";this.renderer?.setClearColor(this.getClearColor()),S&&C.remove(S),S=new t(1e3,1e3,s,s),S.position.y=-499,S.rotateX(-.5*Math.PI),C.add(S),v.color.set(s),y()},this.onThemeChange(this.theme),this.disposables.add(this.on("time",y)),this.disposables.add(this.on("size",y));let R=!0;this.disposables.add(()=>R=!1),this.disposables.add(()=>this.scene.clear()),this.disposables.add(()=>m.dispose());const k=()=>{R&&(m.getPixelRatio()!==c()&&m.setPixelRatio(c()),p(this.scene,m,b),m.render(this.scene,this.camera),this.stats&&this.stats.update(),requestAnimationFrame(k))};k()}destroy(){this.disposables?.dispose()}updateTimeRange(e){const{startTime:t,duration:s}=e;this.camera.left=-.5*s,this.camera.right=.5*s,this.camera.updateProjectionMatrix()}updateValueRange(e){this.camera.top=e.maxValue,this.camera.bottom=e.minValue,this.camera.updateProjectionMatrix()}updateSize(e,t){this.renderer?.setPixelRatio(c()),this.renderer?.setSize(e,t)}}
1
+ import{BufferGeometry as e,GridHelper as t,Group as s,Line as i,LineBasicMaterial as a,OrthographicCamera as o,Scene as n,Vector2 as r,Vector3 as h,WebGLRenderer as l}from"three";import m from"three/examples/jsm/libs/stats.module.js";import{Disposables as d}from"../common/Disposables.js";import{getDevicePixelRatio as p,snapLinesToPixels as c}from"./PixelSnap.js";import{TimeSliderEngine as b}from"../engine/TimeSliderEngine.js";import{TimeTicksBar as g}from"../ticks/TimeTicksBar.js";import{getStartOfWeek as u}from"../common/TimeTools.js";import{ONE_MONTH as T,ONE_SECOND as x,ONE_WEEK as C}from"../ticks/TimeSliderConstants.js";export class ThreeTimeSliderEngine extends b{constructor(e){super(new n),this.camera=new o(0,1,1,0),this.antialias=e?.antialias??!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(o){const n="dark"===this.theme;this.container=o;const m=new l({antialias:this.antialias,alpha:!0});m.domElement.style.position="relative",m.setPixelRatio(p());const b={value:new r};this.renderer=m,this.disposables=new d,this.renderer.setClearColor(this.getClearColor(),this.getClearAlpha()),o.appendChild(this.renderer.domElement),this.disposables.add(()=>o.removeChild(m.domElement)),this.camera.position.z=10;const w=new s,f=new s;w.scale.x=x,this.scene.add(w);const v=new a({color:n?"#444":"#bbb"}),j=(new e).setFromPoints([new h(0,0,0),new h(0,1,0),new h(0,1,1)]);this.scene.add(f);const P=[];let S;const y=()=>{const e=this.getSize().width,{startTime:t,duration:s,endTime:a}=this.getTimeRange(),o=s,[n]=g.getSpacing(e,o),r=.5*(a+t);if(n<T){f.visible=!1,w.scale.x=n;let e=r%n;n>=C&&(e=r-u(r)),w.position.x=-e,w.visible=!0}else{w.visible=!1;let e=0;g.generateTicks(n,t-3*s,a+3*s,(o,n)=>{t-s<=o&&o<=a+s&&(P[e]||(P[e]=new i(j,v),f.add(P[e])),P[e].position.x=o,P[e].visible=!0,e++)});for(let t=e;t<P.length;t++)P[t].visible=!1;f.position.x=-r,f.visible=!0}};this.onThemeChange=e=>{const s="dark"===e?"#444":"#bbb";this.renderer?.setClearColor(this.getClearColor(),this.getClearAlpha()),S&&w.remove(S),S=new t(1e3,1e3,s,s),S.position.y=-499,S.rotateX(-.5*Math.PI),w.add(S),v.color.set(s),y()},this.onThemeChange(this.theme),this.disposables.add(this.on("time",y)),this.disposables.add(this.on("size",y));let R=!0;this.disposables.add(()=>R=!1),this.disposables.add(()=>this.scene.clear()),this.disposables.add(()=>m.dispose());const k=()=>{R&&(m.getPixelRatio()!==p()&&m.setPixelRatio(p()),c(this.scene,m,b),m.render(this.scene,this.camera),this.stats&&this.stats.update(),requestAnimationFrame(k))};k()}destroy(){this.disposables?.dispose()}updateTimeRange(e){const{startTime:t,duration:s}=e;this.camera.left=-.5*s,this.camera.right=.5*s,this.camera.updateProjectionMatrix()}updateValueRange(e){this.camera.top=e.maxValue,this.camera.bottom=e.minValue,this.camera.updateProjectionMatrix()}updateSize(e,t){this.renderer?.setPixelRatio(p()),this.renderer?.setSize(e,t)}}
@@ -1 +1 @@
1
- import{BoxGeometry as e,Color as n,DoubleSide as t,InstancedMesh as o,Object3D as i,ShaderMaterial as r,Vector2 as l}from"three";import{getDevicePixelRatio as a}from"../PixelSnap.js";import{Disposables as s}from"../../common/Disposables.js";import{toGLSLColor as d,toGLSLFloat as m}from"../../common/GlslTools.js";import{getTimePartsInHours as c}from"../../common/TimeTools.js";import{TimeSliderSplittingFixedHistogram as f}from"../../data/histogram/TimeSliderSplittingFixedHistogram.js";import{TimeSliderHistogramLayer as v}from"../../layer/TimeSliderHistogramLayer.js";import{TimeSliderIntervalsLayer as g}from"../../layer/TimeSliderIntervalsLayer.js";import{Three3DTimeSliderEngine as h}from"../Three3DTimeSliderEngine.js";import{GrowingInstanceMatrix as p}from"./GrowingInstanceMatrix.js";const u=123456;export class Three3DIntervalsTimeLayerLoader{constructor(){this.engineClass=h,this.followsLayerBand=!0}canLoad(e){return e instanceof g}load(g,h,x,y){return this.canLoad(g)?function(g,h,x,y){const b=new s,S=g.model,{material:w,dispose:C}=(g.style,function(e,o,i){const g=void 0!==e.styler,h=function(e){return e instanceof v&&void 0!==e.histogram.classifier&&e.histogram instanceof f}(e),p=new s,x=()=>{const n=e.style,t=n.border?n.border.width||1:0,o="boolean"!=typeof n.border?.centered||n.border?.centered;let i="",r="";if(h){const n=e.maxClassifications||4;for(let e=0;e<n;e+=4)i+=`attribute vec4 split${e/4};`,i+=`flat out vec4 vSplit${e/4};`,r+=`vSplit${e/4} = split${e/4};`;console.log("createVertexShader",{splitAttributes:i,maxSplits:n})}return`\n // precision mediump float;\n // precision highp float;\n \n uniform vec2 layerRange;\n uniform float layerDirection;\n const bool borderCentered = ${o};\n const float borderWidth = ${m(t)};\n uniform float pixelRatio;\n // Borders are a whole number of device pixels wide, so with smoothing 0 they're crisp wherever they land.\n float snappedBorderWidth() { return borderWidth > 0.0 ? max(1.0, floor(borderWidth * pixelRatio + 0.5)) / pixelRatio : 0.0; }\n const float infinityDuration = ${m(u)};\n const float msInHour = 3600000.0;\n\n ${i}\n\n uniform vec2 originTime;\n uniform vec2 nowTime;\n uniform vec2 valueRange;\n uniform int selection;\n uniform vec2 worldPerPixel;\n // uniform float split;\n // attribute float split;\n \n ${g?"attribute vec3 borderColor;":""}\n ${g?"varying vec3 fillColor;":""}\n ${g?"varying vec3 strokeColor;":""}\n varying vec2 vUv;\n\n // flat out float vSplit;\n flat out int vSelected;\n flat out int vTopBottom;\n flat out vec2 vSize;\n flat out vec2 vTransform;\n\n void main() {\n ${g?"fillColor = instanceColor;":""}\n ${g?"strokeColor = borderColor;":""}\n vUv = uv;\n // vSplit = split;\n ${r}\n \n bool selected = gl_InstanceID == selection;\n vSelected = selected ? 1 : 0;\n\n float minValue = valueRange.x;\n float valueSize = valueRange.y - minValue;\n\n float width = length(instanceMatrix[0].xyz);\n float height = length(instanceMatrix[1].xyz) / valueSize;\n float depth = length(instanceMatrix[2].xyz);\n\n height *= abs(layerRange.y - layerRange.x);\n\n if (selected) {\n depth += 0.00001;\n }\n\n vec3 pos = vec3(instanceMatrix[3].xyz);\n\n float deltaHours = pos.x - originTime.x;\n float deltaMs = pos.z - originTime.y;\n float relativeTime = deltaHours * msInHour + deltaMs;\n pos.x = relativeTime;\n \n pos.y = layerRange.x + ((pos.y - minValue) / valueSize) * (layerRange.y - layerRange.x);\n pos.z = 1.0 - 0.5 * depth;\n \n if (selected) {\n pos.z += 0.00001;\n }\n\n if (borderCentered) {\n vec3 borderOffset = snappedBorderWidth() * vec3(worldPerPixel, 0.0);\n width += borderOffset.x;\n height += borderOffset.y;\n depth += borderOffset.z;\n }\n\n // Analyze the normal to determine the face\n // Use abs() to handle both positive and negative directions\n vec3 absNormal = abs(normal);\n\n if (absNormal.x > 0.99) {\n // Side is on the X-axis (left/right face)\n // The 2D plane is defined by the geometry's Depth and Height\n vSize = vec2(depth, height);\n vTransform = vec2(0.001, worldPerPixel.y);\n vTopBottom = 0;\n // Hanging down: value 0 is at the top, so stacked segments run top to bottom.\n if (layerDirection < 0.0) vUv.y = 1.0 - vUv.y;\n } else if (absNormal.y > 0.99) {\n // Side is on the Y-axis (top/bottom face)\n // The 2D plane is defined by the geometry's Width and Depth\n vSize = vec2(width, depth);\n vTransform = vec2(worldPerPixel.x, 0.001);\n // 1: the face at the bars' far end (last segment), 2: at value 0 (first segment); hanging down swaps them.\n vTopBottom = (normal.y > 0.99) == (layerDirection > 0.0) ? 1 : 2;\n } else {\n // Side is on the Z-axis (front/back face)\n // The 2D plane is defined by the geometry's Width and Height\n vSize = vec2(width, height);\n vTransform = vec2(worldPerPixel);\n vTopBottom = 0;\n if (layerDirection < 0.0) vUv.y = 1.0 - vUv.y;\n }\n\n mat4 adjustedInstanceMatrix = instanceMatrix;\n adjustedInstanceMatrix[0].xyz *= width / length(instanceMatrix[0].xyz);\n adjustedInstanceMatrix[1].xyz *= height / length(instanceMatrix[1].xyz);\n adjustedInstanceMatrix[2].xyz *= depth / length(instanceMatrix[2].xyz);\n adjustedInstanceMatrix[3].xyz = pos;\n \n vec4 localPos = adjustedInstanceMatrix * vec4(position, 1.0);\n gl_Position = projectionMatrix * modelViewMatrix * localPos;\n }\n `},y=()=>{const t=e.style,o=new n(t.fill?.color||"#df0"),i=t.fill?.opacity||.5,r=new n(t.border?.color||"#000"),l=t.border?t.border.opacity||1:0,a=t.border?t.border.width||1:0,s="number"==typeof t.border?.smoothing?Math.max(0,t.border.smoothing):1;let c="";if(h){const n=e.maxClassifications||4,t=e.classificationColors||[];c+=`const int maxSplits = ${n};`;let o="",i="";for(let e=0;e<t.length;e++)c+=`\n const vec3 splitColor${e} = ${d(t[e]||"#777")};\n `,i+=`\n if (idx == ${e}) return splitColor${e};\n `;for(let e=0;e<n;e+=4)c+=`\n flat in vec4 vSplit${e/4};\n `,o+=`\n if (idx < ${e+4}) return vSplit${e/4}[idx - ${e}];\n `;c+=`\n float getSplit(int idx) {\n ${o}\n return 0.0;\n }\n vec3 getSplitColor(int idx) {\n ${i}\n return fillColor; // Fallback to default fill color\n }\n `,console.log("createFragmentShader",{splitVars:c,maxSplits:n,classificationColors:t})}return`\n // precision mediump float;\n // precision highp float;\n const float fillOpacity = ${m(i)};\n const float borderOpacity = ${m(l)};\n const float borderWidth = ${m(a)};\n uniform float pixelRatio;\n // Borders are a whole number of device pixels wide, so with smoothing 0 they're crisp wherever they land.\n float snappedBorderWidth() { return borderWidth > 0.0 ? max(1.0, floor(borderWidth * pixelRatio + 0.5)) / pixelRatio : 0.0; }\n const float borderSmoothing = ${m(s)};\n const bool borderSidesVertical = ${"vertical"===t.border?.sides?"true":"false"};\n\n ${g?"varying vec3 fillColor;":`const vec3 fillColor = vec3(${m(o.r)},${m(o.g)},${m(o.b)});`}\n ${g?"varying vec3 strokeColor;":`const vec3 strokeColor = vec3(${m(r.r)},${m(r.g)},${m(r.b)});`}\n\n varying vec2 vUv;\n flat in int vSelected;\n flat in int vTopBottom;\n flat in vec2 vSize;\n flat in vec2 vTransform;\n\n ${c}\n\n // Coverage of the inside of an edge at lo; smoothing 0 gives a hard step (smoothstep is undefined for equal edges).\n float edgeAlpha(float lo, float smoothing, float x) {\n return smoothing > 0.0 ? smoothstep(lo, lo + smoothing, x) : step(lo, x);\n }\n\n void main() {\n ${h?"\n bool selected = vSelected > 0;\n vec3 finalFillColor = selected ? vec3(1.0 - fillColor) : fillColor;\n vec3 finalBorderColor = selected ? vec3(1.0 - strokeColor) : strokeColor;\n\n vec3 segmentFillColor;\n float segmentStart = 0.0;\n float segmentEnd = 1.0;\n if (vTopBottom < 1) {\n // Determine which segment the current fragment is in\n int segment = 0;\n for (int i = 0; i < maxSplits; i++) {\n float spliti = getSplit(i);\n if (vUv.y <= spliti) {\n segmentEnd = spliti;\n segment = i;\n break;\n }\n else {\n segmentStart = spliti;\n }\n // if (vUv.y <= segmentStart + spliti) {\n // segmentEnd = segmentStart + spliti;\n // segment = i;\n // break;\n // }\n // else {\n // segmentStart += spliti;\n // }\n }\n\n // if (segment == 0 && segmentStart > 0.0) {\n // segmentEnd = 1.0;\n // for (int i = maxSplits - 1; i >= 0; i--) {\n // float spliti = getSplit(i);\n // if (spliti > 0.0) {\n // segmentStart = 1.0 - spliti;\n // segment = i;\n // break;\n // }\n // }\n // }\n \n segmentFillColor = getSplitColor(segment);\n }\n else if (vTopBottom < 2) {\n // Determine the last segment with a value\n int segment = 0;\n for (int i = maxSplits - 1; i >= 0; i--) {\n if (getSplit(i) < 0.99999) {\n segment = i + 1;\n break;\n }\n }\n segmentFillColor = getSplitColor(segment);\n }\n else {\n // Determine the first segment with a value\n int segment = 0;\n for (int i = 0; i < maxSplits; i++) {\n if (getSplit(i) > 0.0) {\n segment = i;\n break;\n }\n }\n segmentFillColor = getSplitColor(segment);\n }\n\n vec3 segmentBorderColor = segmentFillColor;\n float segmentSize = segmentEnd - segmentStart;\n\n // Half border size in world units\n vec2 bwWorld = snappedBorderWidth() * vTransform;\n // Half border UV width if centered, full if inside-only\n vec2 bw = (bwWorld / vSize) / vec2(1.0, segmentSize);\n float smoothingPixels = borderSmoothing;\n vec2 smoothing = smoothingPixels * vTransform / vSize;\n\n // Adjust UV coordinates for the current segment (y-axis only)\n float uvYAdjusted = (vUv.y - segmentStart) / segmentSize;\n\n // Calculate alpha for borders and fill for the adjusted UV\n float alphaX = edgeAlpha(bw.x, smoothing.x, vUv.x) *\n edgeAlpha(bw.x, smoothing.x, 1.0 - vUv.x);\n // borderSidesVertical: skip the border between stacked segments (top/bottom), keep only left/right.\n float alphaY = borderSidesVertical ? 1.0 : edgeAlpha(bw.y, smoothing.y, uvYAdjusted) *\n edgeAlpha(bw.y, smoothing.y, 1.0 - uvYAdjusted);\n float fillAlpha = alphaX * alphaY;\n\n vec3 color = mix(segmentBorderColor, segmentFillColor, fillAlpha);\n float alpha = mix(borderOpacity, fillOpacity, fillAlpha);\n\n gl_FragColor = linearToOutputTexel(vec4(color, alpha));\n ":"\n bool selected = vSelected > 0;\n vec3 finalFillColor = selected ? vec3(1.0 - fillColor) : fillColor;\n vec3 finalBorderColor = selected ? vec3(1.0 - strokeColor) : strokeColor;\n\n // Half border size in world units\n vec2 bwWorld = snappedBorderWidth() * vTransform;\n\n // Half border UV width if centered, full if inside-only\n vec2 bw = bwWorld / vSize;\n\n float smoothingPixels = borderSmoothing;\n vec2 smoothing = smoothingPixels * vTransform / vSize;\n\n float alphaX = edgeAlpha(bw.x, smoothing.x, vUv.x) *\n edgeAlpha(bw.x, smoothing.x, 1.0 - vUv.x);\n float alphaY = borderSidesVertical ? 1.0 : edgeAlpha(bw.y, smoothing.y, vUv.y) *\n edgeAlpha(bw.y, smoothing.y, 1.0 - vUv.y);\n\n float fillAlpha = alphaX * alphaY;\n\n vec3 color = mix(finalBorderColor, finalFillColor, fillAlpha);\n float alpha = mix(borderOpacity, fillOpacity, fillAlpha);\n\n gl_FragColor = linearToOutputTexel(vec4(color, alpha));\n "}\n }\n `},b=new r({transparent:!0,side:t,uniforms:{worldPerPixel:{value:new l(i.camera.scale.x/i.getSize().width,i.camera.scale.y/i.getSize().height)},pixelRatio:{value:a()},layerRange:{value:new l(e.min,e.max)},layerDirection:{value:1},selection:{value:-1},valueRange:{value:new l(0,1)},originTime:{value:new l(0,0)}},vertexShader:x(),fragmentShader:y()}),S=()=>{b.uniforms.pixelRatio.value=a(),b.uniforms.worldPerPixel.value.set(i.camera.scale.x/i.getSize().width,i.camera.scale.y/i.getSize().height)},w=()=>{const e=i.getTimeRange(),n=e.startTime+.5*e.duration,[t,o]=c(n);b.uniforms.originTime.value.set(t,o)},C=()=>{const n=o.valueRanges.get(e.yAxis.group||e.id);if(n){const e=b.uniforms.valueRange.value;e.x===n.min&&e.y===n.max||b.uniforms.valueRange.value.set(n.min,n.max)}},T=()=>{S(),w()};p.add(o.on("valueRanges",C)),p.add(i.on("time",T)),p.add(i.on("value",S)),p.add(i.on("size",S)),p.add(e.on("loaded",C)),p.add(e.on("visible",C));const $=()=>{const n="down"===e.direction;b.uniforms.layerRange.value.set(n?e.max:e.min,n?e.min:e.max),b.uniforms.layerDirection.value=n?-1:1};return p.add(e.on("min",$)),p.add(e.on("max",$)),p.add(e.on("direction",$)),$(),w(),C(),p.add(e.on("invalidateAll",()=>{b.vertexShader=x(),b.fragmentShader=y(),b.needsUpdate=!0})),{material:b,dispose:()=>p.dispose()}}(g,x,y));b.add(C);const T=new e(1,1,1),$=new o(T,w,S.data.length);$.frustumCulled=!1,$.receiveShadow=!0,$.castShadow=!0;const z=function(e,n,t){const o=new p(n,e=>{const n=new i,t=.5*(e.t[0]+e.t[1]),[o,r]=c(t),l=e.v[1]-e.v[0],a=e.v[0]+.5*l;n.position.set(o,a,r);const s=e.t[1]-e.t[0];return n.scale.set(s,l,.1),n.updateMatrix(),n.matrix},t);return o.registerMesh(e),o}($,S,{colorProvider:g.styler?e=>{const n=g.styler(e);return n?{fill:n.fill?.color,stroke:n.border?.color}:void 0}:void 0,defaultFill:g.style.fill?.color,defaultBorder:g.style.border?.color,splitProvider:g instanceof v&&g.histogram instanceof f?e=>e.attributes.splits||[1]:void 0,maxSplits:g instanceof v?g.maxClassifications:void 0});b.add(()=>z.dispose()),x.selectionOptions.highlight&&b.add(g.on("selection",e=>{if(e.length>0){const n=z.getIndex(e[0]);w.uniforms.selection.value="number"==typeof n?n:-1}else w.uniforms.selection.value=-1}));return h.add($),b.add(()=>h.remove($)),b.disposeLater()}(g,h,y,x):(console.error("Layer not supported by ThreePointsTimeLayerLoader",{layer:g}),()=>{})}}
1
+ import{BoxGeometry as e,Color as n,DoubleSide as t,InstancedMesh as o,Object3D as i,ShaderMaterial as r,Vector2 as l}from"three";import{getDevicePixelRatio as a}from"../PixelSnap.js";import{Disposables as s}from"../../common/Disposables.js";import{toGLSLColor as d,toGLSLFloat as m}from"../../common/GlslTools.js";import{getTimePartsInHours as f}from"../../common/TimeTools.js";import{TimeSliderSplittingFixedHistogram as c}from"../../data/histogram/TimeSliderSplittingFixedHistogram.js";import{TimeSliderHistogramLayer as v}from"../../layer/TimeSliderHistogramLayer.js";import{TimeSliderIntervalsLayer as g}from"../../layer/TimeSliderIntervalsLayer.js";import{Three3DTimeSliderEngine as h}from"../Three3DTimeSliderEngine.js";import{GrowingInstanceMatrix as p}from"./GrowingInstanceMatrix.js";const u=123456;export class Three3DIntervalsTimeLayerLoader{constructor(){this.engineClass=h,this.followsLayerBand=!0}canLoad(e){return e instanceof g}load(g,h,y,x){return this.canLoad(g)?function(g,h,y,x){const b=new s,S=g.model,{material:w,dispose:C}=(g.style,function(e,o,i){const g=void 0!==e.styler,h=function(e){return e instanceof v&&void 0!==e.histogram.classifier&&e.histogram instanceof c}(e),p=new s,y=()=>{const n=e.style,t=n.border?n.border.width||1:0,o="boolean"!=typeof n.border?.centered||n.border?.centered;let i="",r="";if(h){const n=e.maxClassifications||4;for(let e=0;e<n;e+=4)i+=`attribute vec4 split${e/4};`,i+=`flat out vec4 vSplit${e/4};`,r+=`vSplit${e/4} = split${e/4};`;console.log("createVertexShader",{splitAttributes:i,maxSplits:n})}return`\n // precision mediump float;\n // precision highp float;\n \n uniform vec2 layerRange;\n uniform float layerDirection;\n const bool borderCentered = ${o};\n const float borderWidth = ${m(t)};\n uniform float pixelRatio;\n // Borders are a whole number of device pixels wide, so with smoothing 0 they're crisp wherever they land.\n const bool borderBaseEdge = ${"vertical+base"===e.style.border?.sides?"true":"false"};\n float snappedBorderWidth() { return borderWidth > 0.0 ? max(1.0, floor(borderWidth * pixelRatio + 0.5)) / pixelRatio : 0.0; }\n // The value-0 edge is as wide as the sides: bars meeting base to base overlap like neighbours do, so they're divided alike.\n float snappedBaseWidth() { return snappedBorderWidth(); }\n const float infinityDuration = ${m(u)};\n const float msInHour = 3600000.0;\n\n ${i}\n\n uniform vec2 originTime;\n uniform vec2 nowTime;\n uniform vec2 valueRange;\n uniform int selection;\n uniform vec2 worldPerPixel;\n // uniform float split;\n // attribute float split;\n \n ${g?"attribute vec3 borderColor;":""}\n ${g?"varying vec3 fillColor;":""}\n ${g?"varying vec3 strokeColor;":""}\n varying vec2 vUv;\n\n // flat out float vSplit;\n flat out int vSelected;\n flat out int vTopBottom;\n flat out vec2 vSize;\n flat out vec2 vTransform;\n\n void main() {\n ${g?"fillColor = instanceColor;":""}\n ${g?"strokeColor = borderColor;":""}\n vUv = uv;\n // vSplit = split;\n ${r}\n \n bool selected = gl_InstanceID == selection;\n vSelected = selected ? 1 : 0;\n\n float minValue = valueRange.x;\n float valueSize = valueRange.y - minValue;\n\n float width = length(instanceMatrix[0].xyz);\n float height = length(instanceMatrix[1].xyz) / valueSize;\n float depth = length(instanceMatrix[2].xyz);\n\n height *= abs(layerRange.y - layerRange.x);\n\n if (selected) {\n depth += 0.00001;\n }\n\n vec3 pos = vec3(instanceMatrix[3].xyz);\n\n float deltaHours = pos.x - originTime.x;\n float deltaMs = pos.z - originTime.y;\n float relativeTime = deltaHours * msInHour + deltaMs;\n pos.x = relativeTime;\n \n pos.y = layerRange.x + ((pos.y - minValue) / valueSize) * (layerRange.y - layerRange.x);\n pos.z = 1.0 - 0.5 * depth;\n \n if (selected) {\n pos.z += 0.00001;\n }\n\n if (borderCentered) {\n vec3 borderOffset = snappedBorderWidth() * vec3(worldPerPixel, 0.0);\n width += borderOffset.x;\n height += borderOffset.y;\n depth += borderOffset.z;\n }\n\n // Analyze the normal to determine the face\n // Use abs() to handle both positive and negative directions\n vec3 absNormal = abs(normal);\n\n if (absNormal.x > 0.99) {\n // Side is on the X-axis (left/right face)\n // The 2D plane is defined by the geometry's Depth and Height\n vSize = vec2(depth, height);\n vTransform = vec2(0.001, worldPerPixel.y);\n vTopBottom = 0;\n // Hanging down: value 0 is at the top, so stacked segments run top to bottom.\n if (layerDirection < 0.0) vUv.y = 1.0 - vUv.y;\n } else if (absNormal.y > 0.99) {\n // Side is on the Y-axis (top/bottom face)\n // The 2D plane is defined by the geometry's Width and Depth\n vSize = vec2(width, depth);\n vTransform = vec2(worldPerPixel.x, 0.001);\n // 1: the face at the bars' far end (last segment), 2: at value 0 (first segment); hanging down swaps them.\n vTopBottom = (normal.y > 0.99) == (layerDirection > 0.0) ? 1 : 2;\n } else {\n // Side is on the Z-axis (front/back face)\n // The 2D plane is defined by the geometry's Width and Height\n vSize = vec2(width, height);\n vTransform = vec2(worldPerPixel);\n vTopBottom = 0;\n if (layerDirection < 0.0) vUv.y = 1.0 - vUv.y;\n }\n\n mat4 adjustedInstanceMatrix = instanceMatrix;\n adjustedInstanceMatrix[0].xyz *= width / length(instanceMatrix[0].xyz);\n adjustedInstanceMatrix[1].xyz *= height / length(instanceMatrix[1].xyz);\n adjustedInstanceMatrix[2].xyz *= depth / length(instanceMatrix[2].xyz);\n adjustedInstanceMatrix[3].xyz = pos;\n \n vec4 localPos = adjustedInstanceMatrix * vec4(position, 1.0);\n gl_Position = projectionMatrix * modelViewMatrix * localPos;\n }\n `},x=()=>{const t=e.style,o=new n(t.fill?.color||"#df0"),i=t.fill?.opacity||.5,r=new n(t.border?.color||"#000"),l=t.border?t.border.opacity||1:0,a=t.border?t.border.width||1:0,s="number"==typeof t.border?.smoothing?Math.max(0,t.border.smoothing):1;let f="";if(h){const n=e.maxClassifications||4,t=e.classificationColors||[];f+=`const int maxSplits = ${n};`;let o="",i="";for(let e=0;e<t.length;e++)f+=`\n const vec3 splitColor${e} = ${d(t[e]||"#777")};\n `,i+=`\n if (idx == ${e}) return splitColor${e};\n `;for(let e=0;e<n;e+=4)f+=`\n flat in vec4 vSplit${e/4};\n `,o+=`\n if (idx < ${e+4}) return vSplit${e/4}[idx - ${e}];\n `;f+=`\n float getSplit(int idx) {\n ${o}\n return 0.0;\n }\n vec3 getSplitColor(int idx) {\n ${i}\n return fillColor; // Fallback to default fill color\n }\n `,console.log("createFragmentShader",{splitVars:f,maxSplits:n,classificationColors:t})}return`\n // precision mediump float;\n // precision highp float;\n const float fillOpacity = ${m(i)};\n const float borderOpacity = ${m(l)};\n const float borderWidth = ${m(a)};\n uniform float pixelRatio;\n // Borders are a whole number of device pixels wide, so with smoothing 0 they're crisp wherever they land.\n const bool borderBaseEdge = ${"vertical+base"===e.style.border?.sides?"true":"false"};\n float snappedBorderWidth() { return borderWidth > 0.0 ? max(1.0, floor(borderWidth * pixelRatio + 0.5)) / pixelRatio : 0.0; }\n // The value-0 edge is as wide as the sides: bars meeting base to base overlap like neighbours do, so they're divided alike.\n float snappedBaseWidth() { return snappedBorderWidth(); }\n const float borderSmoothing = ${m(s)};\n const bool borderSidesVertical = ${"vertical"===t.border?.sides||"vertical+base"===t.border?.sides?"true":"false"};\n\n ${g?"varying vec3 fillColor;":`const vec3 fillColor = vec3(${m(o.r)},${m(o.g)},${m(o.b)});`}\n ${g?"varying vec3 strokeColor;":`const vec3 strokeColor = vec3(${m(r.r)},${m(r.g)},${m(r.b)});`}\n\n varying vec2 vUv;\n flat in int vSelected;\n flat in int vTopBottom;\n flat in vec2 vSize;\n flat in vec2 vTransform;\n\n ${f}\n\n // Coverage of the inside of an edge at lo; smoothing 0 gives a hard step (smoothstep is undefined for equal edges).\n float edgeAlpha(float lo, float smoothing, float x) {\n return smoothing > 0.0 ? smoothstep(lo, lo + smoothing, x) : step(lo, x);\n }\n\n void main() {\n ${h?"\n bool selected = vSelected > 0;\n vec3 finalFillColor = selected ? vec3(1.0 - fillColor) : fillColor;\n vec3 finalBorderColor = selected ? vec3(1.0 - strokeColor) : strokeColor;\n\n vec3 segmentFillColor;\n float segmentStart = 0.0;\n float segmentEnd = 1.0;\n if (vTopBottom < 1) {\n // Determine which segment the current fragment is in\n int segment = 0;\n for (int i = 0; i < maxSplits; i++) {\n float spliti = getSplit(i);\n if (vUv.y <= spliti) {\n segmentEnd = spliti;\n segment = i;\n break;\n }\n else {\n segmentStart = spliti;\n }\n // if (vUv.y <= segmentStart + spliti) {\n // segmentEnd = segmentStart + spliti;\n // segment = i;\n // break;\n // }\n // else {\n // segmentStart += spliti;\n // }\n }\n\n // if (segment == 0 && segmentStart > 0.0) {\n // segmentEnd = 1.0;\n // for (int i = maxSplits - 1; i >= 0; i--) {\n // float spliti = getSplit(i);\n // if (spliti > 0.0) {\n // segmentStart = 1.0 - spliti;\n // segment = i;\n // break;\n // }\n // }\n // }\n \n segmentFillColor = getSplitColor(segment);\n }\n else if (vTopBottom < 2) {\n // Determine the last segment with a value\n int segment = 0;\n for (int i = maxSplits - 1; i >= 0; i--) {\n if (getSplit(i) < 0.99999) {\n segment = i + 1;\n break;\n }\n }\n segmentFillColor = getSplitColor(segment);\n }\n else {\n // Determine the first segment with a value\n int segment = 0;\n for (int i = 0; i < maxSplits; i++) {\n if (getSplit(i) > 0.0) {\n segment = i;\n break;\n }\n }\n segmentFillColor = getSplitColor(segment);\n }\n\n vec3 segmentBorderColor = segmentFillColor;\n float segmentSize = segmentEnd - segmentStart;\n\n // Half border size in world units\n vec2 bwWorld = snappedBorderWidth() * vTransform;\n // Half border UV width if centered, full if inside-only\n vec2 bw = (bwWorld / vSize) / vec2(1.0, segmentSize);\n float smoothingPixels = borderSmoothing;\n vec2 smoothing = smoothingPixels * vTransform / vSize;\n\n // Adjust UV coordinates for the current segment (y-axis only)\n float uvYAdjusted = (vUv.y - segmentStart) / segmentSize;\n\n // Calculate alpha for borders and fill for the adjusted UV\n float alphaX = edgeAlpha(bw.x, smoothing.x, vUv.x) *\n edgeAlpha(bw.x, smoothing.x, 1.0 - vUv.x);\n // borderSidesVertical: skip the border between stacked segments (top/bottom), keep only left/right.\n float alphaY = borderSidesVertical ? 1.0 : edgeAlpha(bw.y, smoothing.y, uvYAdjusted) *\n edgeAlpha(bw.y, smoothing.y, 1.0 - uvYAdjusted);\n // Base edge: on the first segment's value-0 side (vUv.y is flipped for layers hanging down).\n if (borderBaseEdge && vTopBottom == 0 && segmentStart == 0.0) alphaY *= edgeAlpha(snappedBaseWidth() * vTransform.y / vSize.y / segmentSize, smoothing.y, uvYAdjusted);\n float fillAlpha = alphaX * alphaY;\n\n vec3 color = mix(segmentBorderColor, segmentFillColor, fillAlpha);\n float alpha = mix(borderOpacity, fillOpacity, fillAlpha);\n\n gl_FragColor = linearToOutputTexel(vec4(color, alpha));\n ":"\n bool selected = vSelected > 0;\n vec3 finalFillColor = selected ? vec3(1.0 - fillColor) : fillColor;\n vec3 finalBorderColor = selected ? vec3(1.0 - strokeColor) : strokeColor;\n\n // Half border size in world units\n vec2 bwWorld = snappedBorderWidth() * vTransform;\n\n // Half border UV width if centered, full if inside-only\n vec2 bw = bwWorld / vSize;\n\n float smoothingPixels = borderSmoothing;\n vec2 smoothing = smoothingPixels * vTransform / vSize;\n\n float alphaX = edgeAlpha(bw.x, smoothing.x, vUv.x) *\n edgeAlpha(bw.x, smoothing.x, 1.0 - vUv.x);\n float alphaY = borderSidesVertical ? 1.0 : edgeAlpha(bw.y, smoothing.y, vUv.y) *\n edgeAlpha(bw.y, smoothing.y, 1.0 - vUv.y);\n // Base edge: the value-0 side (vUv.y is flipped for layers hanging down).\n if (borderBaseEdge && vTopBottom == 0) alphaY *= edgeAlpha(snappedBaseWidth() * vTransform.y / vSize.y, smoothing.y, vUv.y);\n\n float fillAlpha = alphaX * alphaY;\n\n vec3 color = mix(finalBorderColor, finalFillColor, fillAlpha);\n float alpha = mix(borderOpacity, fillOpacity, fillAlpha);\n\n gl_FragColor = linearToOutputTexel(vec4(color, alpha));\n "}\n }\n `},b=new r({transparent:!0,side:t,uniforms:{worldPerPixel:{value:new l(i.camera.scale.x/i.getSize().width,i.camera.scale.y/i.getSize().height)},pixelRatio:{value:a()},layerRange:{value:new l(e.min,e.max)},layerDirection:{value:1},selection:{value:-1},valueRange:{value:new l(0,1)},originTime:{value:new l(0,0)}},vertexShader:y(),fragmentShader:x()}),S=()=>{b.uniforms.pixelRatio.value=a(),b.uniforms.worldPerPixel.value.set(i.camera.scale.x/i.getSize().width,i.camera.scale.y/i.getSize().height)},w=()=>{const e=i.getTimeRange(),n=e.startTime+.5*e.duration,[t,o]=f(n);b.uniforms.originTime.value.set(t,o)},C=()=>{const n=o.valueRanges.get(e.yAxis.group||e.id);if(n){const e=b.uniforms.valueRange.value;e.x===n.min&&e.y===n.max||b.uniforms.valueRange.value.set(n.min,n.max)}},T=()=>{S(),w()};p.add(o.on("valueRanges",C)),p.add(i.on("time",T)),p.add(i.on("value",S)),p.add(i.on("size",S)),p.add(e.on("loaded",C)),p.add(e.on("visible",C));const $=()=>{const n="down"===e.direction;b.uniforms.layerRange.value.set(n?e.max:e.min,n?e.min:e.max),b.uniforms.layerDirection.value=n?-1:1};return p.add(e.on("min",$)),p.add(e.on("max",$)),p.add(e.on("direction",$)),$(),w(),C(),p.add(e.on("invalidateAll",()=>{b.vertexShader=y(),b.fragmentShader=x(),b.needsUpdate=!0})),{material:b,dispose:()=>p.dispose()}}(g,y,x));b.add(C);const T=new e(1,1,1),$=new o(T,w,S.data.length);$.frustumCulled=!1,$.receiveShadow=!0,$.castShadow=!0;const z=function(e,n,t){const o=new p(n,e=>{const n=new i,t=.5*(e.t[0]+e.t[1]),[o,r]=f(t),l=e.v[1]-e.v[0],a=e.v[0]+.5*l;n.position.set(o,a,r);const s=e.t[1]-e.t[0];return n.scale.set(s,l,.1),n.updateMatrix(),n.matrix},t);return o.registerMesh(e),o}($,S,{colorProvider:g.styler?e=>{const n=g.styler(e);return n?{fill:n.fill?.color,stroke:n.border?.color}:void 0}:void 0,defaultFill:g.style.fill?.color,defaultBorder:g.style.border?.color,splitProvider:g instanceof v&&g.histogram instanceof c?e=>e.attributes.splits||[1]:void 0,maxSplits:g instanceof v?g.maxClassifications:void 0});b.add(()=>z.dispose()),y.selectionOptions.highlight&&b.add(g.on("selection",e=>{if(e.length>0){const n=z.getIndex(e[0]);w.uniforms.selection.value="number"==typeof n?n:-1}else w.uniforms.selection.value=-1}));return h.add($),b.add(()=>h.remove($)),b.disposeLater()}(g,h,x,y):(console.error("Layer not supported by ThreePointsTimeLayerLoader",{layer:g}),()=>{})}}
@@ -1 +1 @@
1
- import{Color as e,InstancedMesh as o,Object3D as n,PlaneGeometry as r,ShaderMaterial as t,Vector2 as i}from"three";import{getDevicePixelRatio as a}from"../PixelSnap.js";import{Disposables as l}from"../../common/Disposables.js";import{toGLSLFloat as s}from"../../common/GlslTools.js";import{getTimePartsInHours as d}from"../../common/TimeTools.js";import{TimeSliderIntervalsLayer as c}from"../../layer/TimeSliderIntervalsLayer.js";import{GrowingInstanceMatrix as m}from"./GrowingInstanceMatrix.js";import{ThreeTimeLayerLoader as v}from"./ThreeTimeLayerLoader.js";const f=123456;export class ThreeIntervalsTimeLayerLoader extends v{constructor(){super(...arguments),this.followsLayerBand=!0}canLoad(e){return e instanceof c}load(c,v,u,p){return this.canLoad(c)?function(c,v,u,p){const h=new l,g=c.model,{material:x,dispose:y}=function(o,n,r){const c=void 0!==o.styler,m=new l,v=()=>{const e=o.style,n=e.border?e.border.width||1:0;return`\n // precision mediump float;\n // precision highp float;\n \n const bool borderCentered = ${"boolean"!=typeof e.border?.centered||e.border?.centered};\n const float borderWidth = ${s(n)};\n uniform float pixelRatio;\n // Borders are a whole number of device pixels wide, so with smoothing 0 they're crisp wherever they land.\n float snappedBorderWidth() { return borderWidth > 0.0 ? max(1.0, floor(borderWidth * pixelRatio + 0.5)) / pixelRatio : 0.0; }\n uniform vec2 layerRange;\n const float infinityDuration = ${s(f)};\n const float msInHour = 3600000.0;\n\n uniform vec2 originTime;\n uniform vec2 valueRange;\n uniform int selection;\n uniform vec2 worldPerPixel;\n \n ${c?"attribute vec3 borderColor;":""}\n ${c?"varying vec3 fillColor;":""}\n ${c?"varying vec3 strokeColor;":""}\n \n varying vec2 vUv;\n flat out int vSelected;\n flat out vec2 vSize;\n\n void main() {\n ${c?"fillColor = instanceColor;":""}\n ${c?"strokeColor = borderColor;":""}\n vUv = uv;\n \n bool selected = gl_InstanceID == selection;\n vSelected = selected ? 1 : 0;\n\n float minValue = valueRange.x;\n float valueSize = valueRange.y - minValue;\n \n vSize.x = length(instanceMatrix[0].xyz);\n vSize.y = length(instanceMatrix[1].xyz) / valueSize;\n\n vSize.y *= abs(layerRange.y - layerRange.x);\n \n vec3 pos = vec3(instanceMatrix[3].xyz);\n float deltaHours = pos.x - originTime.x;\n float deltaMs = pos.z - originTime.y;\n float relativeTime = deltaHours * 3600000.0 + deltaMs;\n pos.x = relativeTime;\n pos.y = layerRange.x + ((pos.y - minValue) / valueSize) * (layerRange.y - layerRange.x);\n pos.z = 0.0;\n \n if (selected) {\n pos.z += 0.0001;\n }\n\n if (borderCentered) {\n vec3 borderOffset = snappedBorderWidth() * vec3(worldPerPixel, 0.0);\n vSize += borderOffset.xy;\n }\n \n mat4 adjustedInstanceMatrix = instanceMatrix;\n adjustedInstanceMatrix[0].xyz *= vSize.x / length(instanceMatrix[0].xyz);\n adjustedInstanceMatrix[1].xyz *= vSize.y / length(instanceMatrix[1].xyz);\n adjustedInstanceMatrix[3].xyz = pos;\n \n vec4 localPos = adjustedInstanceMatrix * vec4(position, 1.0);\n gl_Position = projectionMatrix * modelViewMatrix * localPos;\n }\n `},u=()=>{const n=o.style,r=new e(n.fill?.color||"#df0"),t=n.fill?.opacity||.5,i=new e(n.border?.color||"#000"),a=n.border?n.border.opacity||1:0,l=n.border?n.border.width||1:0,d="number"==typeof n.border?.smoothing?Math.max(0,n.border.smoothing):1.5;return`\n // precision mediump float;\n // precision highp float;\n\n const float fillOpacity = ${s(t)};\n const float borderOpacity = ${s(a)};\n const float borderWidth = ${s(l)};\n uniform float pixelRatio;\n // Borders are a whole number of device pixels wide, so with smoothing 0 they're crisp wherever they land.\n float snappedBorderWidth() { return borderWidth > 0.0 ? max(1.0, floor(borderWidth * pixelRatio + 0.5)) / pixelRatio : 0.0; }\n const float borderSmoothing = ${s(d)};\n const bool borderSidesVertical = ${"vertical"===n.border?.sides?"true":"false"};\n\n ${c?"varying vec3 fillColor;":`const vec3 fillColor = vec3(${s(r.r)},${s(r.g)},${s(r.b)});`}\n ${c?"varying vec3 strokeColor;":`const vec3 strokeColor = vec3(${s(i.r)},${s(i.g)},${s(i.b)});`}\n \n uniform vec2 worldPerPixel;\n \n varying vec2 vUv;\n flat in int vSelected;\n flat in vec2 vSize;\n\n // Coverage of the inside of an edge at lo; smoothing 0 gives a hard step (smoothstep is undefined for equal edges).\n float edgeAlpha(float lo, float smoothing, float x) {\n return smoothing > 0.0 ? smoothstep(lo, lo + smoothing, x) : step(lo, x);\n }\n\n void main() {\n bool selected = vSelected > 0;\n vec3 finalFillColor = selected ? vec3(1.0 - fillColor) : fillColor;\n vec3 finalBorderColor = selected ? vec3(1.0 - strokeColor) : strokeColor;\n\n // Half border size in world units\n vec2 bwWorld = snappedBorderWidth() * worldPerPixel;\n\n // Half border UV width if centered, full if inside-only\n vec2 bw = bwWorld / vSize;\n\n float smoothingPixels = borderSmoothing;\n vec2 smoothing = smoothingPixels * worldPerPixel / vSize;\n\n float alphaX = edgeAlpha(bw.x, smoothing.x, vUv.x) *\n edgeAlpha(bw.x, smoothing.x, 1.0 - vUv.x);\n float alphaY = borderSidesVertical ? 1.0 : edgeAlpha(bw.y, smoothing.y, vUv.y) *\n edgeAlpha(bw.y, smoothing.y, 1.0 - vUv.y);\n\n float fillAlpha = alphaX * alphaY;\n float borderAlpha = 1.0 - fillAlpha;\n\n vec3 color = mix(finalBorderColor, finalFillColor, fillAlpha);\n float alpha = mix(borderOpacity, fillOpacity, fillAlpha);\n\n gl_FragColor = linearToOutputTexel(vec4(color, alpha));\n }\n `},p=new t({transparent:!0,uniforms:{worldPerPixel:{value:new i((n.camera.right-n.camera.left)/n.getSize().width,(n.camera.top-n.camera.bottom)/n.getSize().height)},pixelRatio:{value:a()},layerRange:{value:new i(o.min,o.max)},layerDirection:{value:1},selection:{value:-1},valueRange:{value:new i(0,1)},originTime:{value:new i(0,0)}},vertexShader:v(),fragmentShader:u()}),h=()=>{p.uniforms.pixelRatio.value=a(),p.uniforms.worldPerPixel.value.set((n.camera.right-n.camera.left)/n.getSize().width,(n.camera.top-n.camera.bottom)/n.getSize().height)},g=()=>{const e=n.getTimeRange(),o=e.startTime+.5*e.duration,[r,t]=d(o);p.uniforms.originTime.value.set(r,t)},x=()=>{const e=r.valueRanges.get(o.yAxis.group||o.id);if(e){const o=p.uniforms.valueRange.value;o.x===e.min&&o.y===e.max||p.uniforms.valueRange.value.set(e.min,e.max)}},y=()=>{h(),g(),x()};m.add(r.on("valueRanges",x)),m.add(n.on("time",y)),m.add(n.on("value",h)),m.add(n.on("size",h)),m.add(o.on("loaded",x)),m.add(o.on("visible",x));const b=()=>{const e="down"===o.direction;p.uniforms.layerRange.value.set(e?o.max:o.min,e?o.min:o.max),p.uniforms.layerDirection.value=e?-1:1};return m.add(o.on("min",b)),m.add(o.on("max",b)),m.add(o.on("direction",b)),b(),g(),x(),m.add(o.on("invalidate",()=>{p.vertexShader=v(),p.fragmentShader=u(),p.needsUpdate=!0})),{material:p,dispose:()=>m.dispose()}}(c,v,p);h.add(y);const b=new r(1,1),w=new o(b,x,g.data.length);w.frustumCulled=!1;const S=function(e,o,r,t,i){const a=new m(o,e=>{const o=new n,r=e.v[1]-e.v[0],t=e.v[0]+.5*r,i=.5*(e.t[0]+e.t[1]),[a,l]=d(i);o.position.set(a,t,l);const s=e.t[1]-e.t[0];return o.scale.set(s,r,.1),o.updateMatrix(),o.matrix},i);return a.registerMesh(e),a}(w,g,c.min,c.max,{colorProvider:c.styler?e=>{const o=c.styler(e);return o?{fill:o.fill?.color,stroke:o.border?.color}:void 0}:void 0,defaultFill:c.style.fill?.color,defaultBorder:c.style.border?.color});p.selectionOptions.highlight&&h.add(c.on("selection",e=>{if(e.length>0){const o=S.getIndex(e[0]);x.uniforms.selection.value="number"==typeof o?o:-1}else x.uniforms.selection.value=-1}));return u.add(w),h.add(()=>u.remove(w)),h.disposeLater()}(c,u,v,p):(console.error("Layer not supported by ThreePointsTimeLayerLoader",{layer:c}),()=>{})}}
1
+ import{Color as e,InstancedMesh as o,Object3D as n,PlaneGeometry as r,ShaderMaterial as i,Vector2 as t}from"three";import{getDevicePixelRatio as a}from"../PixelSnap.js";import{Disposables as l}from"../../common/Disposables.js";import{toGLSLFloat as s}from"../../common/GlslTools.js";import{getTimePartsInHours as d}from"../../common/TimeTools.js";import{TimeSliderIntervalsLayer as c}from"../../layer/TimeSliderIntervalsLayer.js";import{GrowingInstanceMatrix as v}from"./GrowingInstanceMatrix.js";import{ThreeTimeLayerLoader as m}from"./ThreeTimeLayerLoader.js";const f=123456;export class ThreeIntervalsTimeLayerLoader extends m{constructor(){super(...arguments),this.followsLayerBand=!0}canLoad(e){return e instanceof c}load(c,m,h,u){return this.canLoad(c)?function(c,m,h,u){const p=new l,g=c.model,{material:y,dispose:x}=function(o,n,r){const c=void 0!==o.styler,v=new l,m=()=>{const e=o.style,n=e.border?e.border.width||1:0;return`\n // precision mediump float;\n // precision highp float;\n \n const bool borderCentered = ${"boolean"!=typeof e.border?.centered||e.border?.centered};\n const float borderWidth = ${s(n)};\n uniform float pixelRatio;\n // Borders are a whole number of device pixels wide, so with smoothing 0 they're crisp wherever they land.\n const bool borderBaseEdge = ${"vertical+base"===o.style.border?.sides?"true":"false"};\n float snappedBorderWidth() { return borderWidth > 0.0 ? max(1.0, floor(borderWidth * pixelRatio + 0.5)) / pixelRatio : 0.0; }\n // The value-0 edge is as wide as the sides: bars meeting base to base overlap like neighbours do, so they're divided alike.\n float snappedBaseWidth() { return snappedBorderWidth(); }\n uniform vec2 layerRange;\n uniform float layerDirection;\n const float infinityDuration = ${s(f)};\n const float msInHour = 3600000.0;\n\n uniform vec2 originTime;\n uniform vec2 valueRange;\n uniform int selection;\n uniform vec2 worldPerPixel;\n \n ${c?"attribute vec3 borderColor;":""}\n ${c?"varying vec3 fillColor;":""}\n ${c?"varying vec3 strokeColor;":""}\n \n varying vec2 vUv;\n flat out int vSelected;\n flat out vec2 vSize;\n\n void main() {\n ${c?"fillColor = instanceColor;":""}\n ${c?"strokeColor = borderColor;":""}\n vUv = uv;\n // Hanging down, value 0 is at the top: vUv.y = 0 stays the value-0 edge.\n if (layerDirection < 0.0) vUv.y = 1.0 - vUv.y;\n \n bool selected = gl_InstanceID == selection;\n vSelected = selected ? 1 : 0;\n\n float minValue = valueRange.x;\n float valueSize = valueRange.y - minValue;\n \n vSize.x = length(instanceMatrix[0].xyz);\n vSize.y = length(instanceMatrix[1].xyz) / valueSize;\n\n vSize.y *= abs(layerRange.y - layerRange.x);\n \n vec3 pos = vec3(instanceMatrix[3].xyz);\n float deltaHours = pos.x - originTime.x;\n float deltaMs = pos.z - originTime.y;\n float relativeTime = deltaHours * 3600000.0 + deltaMs;\n pos.x = relativeTime;\n pos.y = layerRange.x + ((pos.y - minValue) / valueSize) * (layerRange.y - layerRange.x);\n pos.z = 0.0;\n \n if (selected) {\n pos.z += 0.0001;\n }\n\n if (borderCentered) {\n vec3 borderOffset = snappedBorderWidth() * vec3(worldPerPixel, 0.0);\n vSize += borderOffset.xy;\n }\n \n mat4 adjustedInstanceMatrix = instanceMatrix;\n adjustedInstanceMatrix[0].xyz *= vSize.x / length(instanceMatrix[0].xyz);\n adjustedInstanceMatrix[1].xyz *= vSize.y / length(instanceMatrix[1].xyz);\n adjustedInstanceMatrix[3].xyz = pos;\n \n vec4 localPos = adjustedInstanceMatrix * vec4(position, 1.0);\n gl_Position = projectionMatrix * modelViewMatrix * localPos;\n }\n `},h=()=>{const n=o.style,r=new e(n.fill?.color||"#df0"),i=n.fill?.opacity||.5,t=new e(n.border?.color||"#000"),a=n.border?n.border.opacity||1:0,l=n.border?n.border.width||1:0,d="number"==typeof n.border?.smoothing?Math.max(0,n.border.smoothing):1.5;return`\n // precision mediump float;\n // precision highp float;\n\n const float fillOpacity = ${s(i)};\n const float borderOpacity = ${s(a)};\n const float borderWidth = ${s(l)};\n uniform float pixelRatio;\n // Borders are a whole number of device pixels wide, so with smoothing 0 they're crisp wherever they land.\n const bool borderBaseEdge = ${"vertical+base"===o.style.border?.sides?"true":"false"};\n float snappedBorderWidth() { return borderWidth > 0.0 ? max(1.0, floor(borderWidth * pixelRatio + 0.5)) / pixelRatio : 0.0; }\n // The value-0 edge is as wide as the sides: bars meeting base to base overlap like neighbours do, so they're divided alike.\n float snappedBaseWidth() { return snappedBorderWidth(); }\n const float borderSmoothing = ${s(d)};\n const bool borderSidesVertical = ${"vertical"===n.border?.sides||"vertical+base"===n.border?.sides?"true":"false"};\n\n ${c?"varying vec3 fillColor;":`const vec3 fillColor = vec3(${s(r.r)},${s(r.g)},${s(r.b)});`}\n ${c?"varying vec3 strokeColor;":`const vec3 strokeColor = vec3(${s(t.r)},${s(t.g)},${s(t.b)});`}\n \n uniform vec2 worldPerPixel;\n \n varying vec2 vUv;\n flat in int vSelected;\n flat in vec2 vSize;\n\n // Coverage of the inside of an edge at lo; smoothing 0 gives a hard step (smoothstep is undefined for equal edges).\n float edgeAlpha(float lo, float smoothing, float x) {\n return smoothing > 0.0 ? smoothstep(lo, lo + smoothing, x) : step(lo, x);\n }\n\n void main() {\n bool selected = vSelected > 0;\n vec3 finalFillColor = selected ? vec3(1.0 - fillColor) : fillColor;\n vec3 finalBorderColor = selected ? vec3(1.0 - strokeColor) : strokeColor;\n\n // Half border size in world units\n vec2 bwWorld = snappedBorderWidth() * worldPerPixel;\n\n // Half border UV width if centered, full if inside-only\n vec2 bw = bwWorld / vSize;\n\n float smoothingPixels = borderSmoothing;\n vec2 smoothing = smoothingPixels * worldPerPixel / vSize;\n\n float alphaX = edgeAlpha(bw.x, smoothing.x, vUv.x) *\n edgeAlpha(bw.x, smoothing.x, 1.0 - vUv.x);\n float alphaY = borderSidesVertical ? 1.0 : edgeAlpha(bw.y, smoothing.y, vUv.y) *\n edgeAlpha(bw.y, smoothing.y, 1.0 - vUv.y);\n // Base edge: the value-0 side (vUv.y is flipped for layers hanging down).\n if (borderBaseEdge) alphaY *= edgeAlpha(snappedBaseWidth() * worldPerPixel.y / vSize.y, smoothing.y, vUv.y);\n\n float fillAlpha = alphaX * alphaY;\n float borderAlpha = 1.0 - fillAlpha;\n\n vec3 color = mix(finalBorderColor, finalFillColor, fillAlpha);\n float alpha = mix(borderOpacity, fillOpacity, fillAlpha);\n\n gl_FragColor = linearToOutputTexel(vec4(color, alpha));\n }\n `},u=new i({transparent:!0,uniforms:{worldPerPixel:{value:new t((n.camera.right-n.camera.left)/n.getSize().width,(n.camera.top-n.camera.bottom)/n.getSize().height)},pixelRatio:{value:a()},layerRange:{value:new t(o.min,o.max)},layerDirection:{value:1},selection:{value:-1},valueRange:{value:new t(0,1)},originTime:{value:new t(0,0)}},vertexShader:m(),fragmentShader:h()}),p=()=>{u.uniforms.pixelRatio.value=a(),u.uniforms.worldPerPixel.value.set((n.camera.right-n.camera.left)/n.getSize().width,(n.camera.top-n.camera.bottom)/n.getSize().height)},g=()=>{const e=n.getTimeRange(),o=e.startTime+.5*e.duration,[r,i]=d(o);u.uniforms.originTime.value.set(r,i)},y=()=>{const e=r.valueRanges.get(o.yAxis.group||o.id);if(e){const o=u.uniforms.valueRange.value;o.x===e.min&&o.y===e.max||u.uniforms.valueRange.value.set(e.min,e.max)}},x=()=>{p(),g(),y()};v.add(r.on("valueRanges",y)),v.add(n.on("time",x)),v.add(n.on("value",p)),v.add(n.on("size",p)),v.add(o.on("loaded",y)),v.add(o.on("visible",y));const b=()=>{const e="down"===o.direction;u.uniforms.layerRange.value.set(e?o.max:o.min,e?o.min:o.max),u.uniforms.layerDirection.value=e?-1:1};return v.add(o.on("min",b)),v.add(o.on("max",b)),v.add(o.on("direction",b)),b(),g(),y(),v.add(o.on("invalidate",()=>{u.vertexShader=m(),u.fragmentShader=h(),u.needsUpdate=!0})),{material:u,dispose:()=>v.dispose()}}(c,m,u);p.add(x);const b=new r(1,1),w=new o(b,y,g.data.length);w.frustumCulled=!1;const S=function(e,o,r,i,t){const a=new v(o,e=>{const o=new n,r=e.v[1]-e.v[0],i=e.v[0]+.5*r,t=.5*(e.t[0]+e.t[1]),[a,l]=d(t);o.position.set(a,i,l);const s=e.t[1]-e.t[0];return o.scale.set(s,r,.1),o.updateMatrix(),o.matrix},t);return a.registerMesh(e),a}(w,g,c.min,c.max,{colorProvider:c.styler?e=>{const o=c.styler(e);return o?{fill:o.fill?.color,stroke:o.border?.color}:void 0}:void 0,defaultFill:c.style.fill?.color,defaultBorder:c.style.border?.color});u.selectionOptions.highlight&&p.add(c.on("selection",e=>{if(e.length>0){const o=S.getIndex(e[0]);y.uniforms.selection.value="number"==typeof o?o:-1}else y.uniforms.selection.value=-1}));return h.add(w),p.add(()=>h.remove(w)),p.disposeLater()}(c,h,m,u):(console.error("Layer not supported by ThreePointsTimeLayerLoader",{layer:c}),()=>{})}}
@@ -9,6 +9,10 @@ export type YAxisSettings = {
9
9
  color?: string;
10
10
  /** Overrides the slider's font (TimeSliderOptions.font) for this axis' labels. */
11
11
  font?: TimeSliderFont;
12
+ /** Breaks each grid line around its label (measured, outline included), `padding` CSS px (default 4) clear of it, so the label never reads as struck through. Works with gridOrder 'front' and 'behind'. */
13
+ knockout?: boolean | {
14
+ padding?: number;
15
+ };
12
16
  stroke?: {
13
17
  color?: string;
14
18
  width?: number;
@@ -20,12 +24,23 @@ export type YAxisSettings = {
20
24
  };
21
25
  };
22
26
  };
27
+ /** A horizontal line behind the layers, see TimeSliderLayerOptions.baseline. y is a fraction of the height from the bottom, like layer min/max. */
28
+ export type YAxisBaseline = {
29
+ y: number;
30
+ color: string;
31
+ width: number;
32
+ };
23
33
  export declare class YAxisTicks {
34
+ /** In front of the layers: labels, and the grid lines unless they go behind. */
24
35
  canvas: HTMLCanvasElement;
36
+ /** Behind the layers (between the slider's background and a transparent engine): baselines, and the grid lines with gridOrder 'behind'. */
37
+ behindCanvas: HTMLCanvasElement;
25
38
  private font;
26
39
  /** The labels' font for axes that don't set their own; undefined for the default 12px sans-serif. */
27
40
  setFont(font: TimeSliderFont | undefined): void;
28
41
  constructor();
42
+ /** Sizes a canvas' backing store to its own box in device pixels and clears it; the context then draws in CSS pixels. */
43
+ private prepare;
29
44
  render(currents: (YAxisSettings & {
30
45
  min: number;
31
46
  max: number;
@@ -33,7 +48,11 @@ export declare class YAxisTicks {
33
48
  maxValue: number;
34
49
  /** 'down': the axis' min value at the band's top, values increasing downwards (layers hanging down). */
35
50
  direction?: 'up' | 'down';
36
- })[]): void;
51
+ })[], options?: {
52
+ /** Draw the grid lines behind the layers (on behindCanvas) instead of in front. Labels stay in front. */
53
+ gridBehind?: boolean;
54
+ baselines?: YAxisBaseline[];
55
+ }): void;
37
56
  }
38
57
  export declare function getNiceTicks(min: number, max: number, targetCount?: number): number[];
39
58
  export declare function niceNum(range: number, round: boolean): number;
@@ -1 +1 @@
1
- import{resolveFont as t,toCssFont as e}from"../common/Font.js";import{getDevicePixelRatio as i,snapLineToDevicePixels as n}from"../common/Pixels.js";const o={family:"sans-serif",weight:"normal",size:12};export class YAxisTicks{setFont(e){this.font=e?t(e,o):o}constructor(){this.canvas=document.createElement("canvas"),this.font=o,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(o){const s=this.canvas;if(!s.parentElement)return;const l=s.getBoundingClientRect();if(l.width<=0||l.height<=0)return;const r=i(),a=Math.round(l.width*r),h=Math.round(l.height*r);s.width===a&&s.height===h||(s.width=a,s.height=h);const c=l.width,f=l.height,m=s.getContext("2d");if(m){m.setTransform(1,0,0,1,0,0),m.clearRect(0,0,s.width,s.height),m.setTransform(r,0,0,r,0,0);for(const i of o){if(!1===i.visible)continue;const{min:o,max:s,minValue:l,maxValue:a}=i,h=f*(1-s),d=f*(1-o),u=d-h,g=getNiceTicks(l,a),y=i.format??(t=>t.toString()),p=i.style?.labels?.position??"right",x=i.style?.labels?.color??"#777",v=void 0!==i.style?.labels?.stroke,w=i.style?.labels?.stroke?.color??"#000",M=i.style?.labels?.stroke?.width??1,k=i.style?.lines?.color??"#7773",T=parseFloat(i.style?.lines?.width??"1");m.save(),m.fillStyle=x,m.font=e(t(i.style?.labels?.font,this.font)),m.textBaseline="middle",m.lineJoin="round",m.miterLimit=2,m.textAlign="left"===p?"right":"right"===p?"left":"center";for(const t of g){const e=(t-l)/(a-l);if(e<0||e>1)continue;const o="down"===i.direction?h+e*u:d-e*u;if(o<h+4||o>d-4)continue;const s=n(o,T,r);if(m.beginPath(),m.moveTo(0,s.position),m.lineTo(c,s.position),m.lineWidth=s.width,m.strokeStyle=k,m.stroke(),"none"!==p){const e=y(t),i=m.measureText(e).width;let n,s="center";"left"===p?(s="right",n=Math.max(4+i,4+i),n=Math.min(n,c/2-4)):"right"===p?(s="left",n=Math.min(c-4-i,c-4-i),n=Math.max(n,c/2+4)):(s="center",n=c/2),m.textAlign=s,v&&(m.lineWidth=2*M,m.strokeStyle=w,m.strokeText(e,n,o)),m.fillText(e,n,o)}}m.restore()}}}}export function getNiceTicks(t,e,i=6){const n=niceNum(e-t,!1),o=niceNum(n/(i-1),!0),s=Math.floor(t/o)*o,l=Math.ceil(e/o)*o,r=[];for(let i=s;i<=l+o/2;i+=o)i>=t&&i<=e&&r.push(parseFloat(i.toFixed(10)));return r}export function niceNum(t,e){const i=Math.floor(Math.log10(t)),n=t/Math.pow(10,i);let o=0;return o=e?n<1.5?1:n<3?2:n<7?5:10:n<=1?1:n<=2?2:n<=5?5:10,o*Math.pow(10,i)}
1
+ import{resolveFont as t,toCssFont as e}from"../common/Font.js";import{getDevicePixelRatio as i,snapLineToDevicePixels as n}from"../common/Pixels.js";const o={family:"sans-serif",weight:"normal",size:12};export class YAxisTicks{setFont(e){this.font=e?t(e,o):o}constructor(){this.canvas=document.createElement("canvas"),this.behindCanvas=document.createElement("canvas"),this.font=o,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%",this.behindCanvas.style.position="absolute",this.behindCanvas.style.width="100%",this.behindCanvas.style.pointerEvents="none"}prepare(t,e){if(!t.parentElement)return;const i=t.getBoundingClientRect();if(i.width<=0||i.height<=0)return;const n=Math.round(i.width*e),o=Math.round(i.height*e);t.width===n&&t.height===o||(t.width=n,t.height=o);const s=t.getContext("2d");return s?(s.setTransform(1,0,0,1,0,0),s.clearRect(0,0,t.width,t.height),s.setTransform(e,0,0,e,0,0),{ctx:s,width:i.width,height:i.height}):void 0}render(o,s){const l=i(),a=this.prepare(this.canvas,l),h=this.prepare(this.behindCanvas,l);if(!a)return;const{ctx:r,width:c,height:d}=a,f=s?.gridBehind&&h?h.ctx:r;if(h)for(const t of s?.baselines??[]){const e=n(h.height*(1-t.y),t.width,l);h.ctx.beginPath(),h.ctx.moveTo(0,e.position),h.ctx.lineTo(h.width,e.position),h.ctx.lineWidth=e.width,h.ctx.strokeStyle=t.color,h.ctx.stroke()}for(const i of o){if(!1===i.visible)continue;const{min:o,max:s,minValue:a,maxValue:h}=i,x=d*(1-s),m=d*(1-o),g=m-x,p=getNiceTicks(a,h),u=i.format??(t=>t.toString()),y=i.style?.labels?.position??"right",v=i.style?.labels?.color??"#777",w=void 0!==i.style?.labels?.stroke,b=i.style?.labels?.stroke?.color??"#000",k=i.style?.labels?.stroke?.width??1,M=i.style?.lines?.color??"#7773",T=parseFloat(i.style?.lines?.width??"1"),C=i.style?.labels?.knockout,E=void 0!==C&&!1!==C,F="object"==typeof C&&"number"==typeof C.padding?C.padding:4;r.save(),r.fillStyle=v,r.font=e(t(i.style?.labels?.font,this.font)),r.textBaseline="middle",r.lineJoin="round",r.miterLimit=2,r.textAlign="left"===y?"right":"right"===y?"left":"center";for(const t of p){const e=(t-a)/(h-a);if(e<0||e>1)continue;const o="down"===i.direction?x+e*g:m-e*g;if(o<x+4||o>m-4)continue;let s;if("none"!==y){const e=u(t),i=r.measureText(e).width;let n,o="center";"left"===y?(o="right",n=Math.max(4+i,4+i),n=Math.min(n,c/2-4)):"right"===y?(o="left",n=Math.min(c-4-i,c-4-i),n=Math.max(n,c/2+4)):(o="center",n=c/2),s={text:e,x:n,align:o}}const d=n(o,T,l);let p=[[0,c]];if(s&&E){const t=r.measureText(s.text).width,e="right"===s.align?s.x-t:"left"===s.align?s.x:s.x-.5*t,i=F+(w?k:0);p=[[0,Math.floor((e-i)*l)/l],[Math.ceil((e+t+i)*l)/l,c]].filter(([t,e])=>e>t)}f.beginPath();for(const[t,e]of p)f.moveTo(t,d.position),f.lineTo(e,d.position);f.lineWidth=d.width,f.lineCap="butt",f.strokeStyle=M,f.stroke(),s&&(r.textAlign=s.align,w&&(r.lineWidth=2*k,r.strokeStyle=b,r.strokeText(s.text,s.x,o)),r.fillText(s.text,s.x,o))}r.restore()}}}export function getNiceTicks(t,e,i=6){const n=niceNum(e-t,!1),o=niceNum(n/(i-1),!0),s=Math.floor(t/o)*o,l=Math.ceil(e/o)*o,a=[];for(let i=s;i<=l+o/2;i+=o)i>=t&&i<=e&&a.push(parseFloat(i.toFixed(10)));return a}export function niceNum(t,e){const i=Math.floor(Math.log10(t)),n=t/Math.pow(10,i);let o=0;return o=e?n<1.5?1:n<3?2:n<7?5:10:n<=1?1:n<=2?2:n<=5?5:10,o*Math.pow(10,i)}