@kteneyck/cesium-timeline-angular 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,7 +17,7 @@ A canvas-based timeline component for **React** and **Angular** with Cesium Cloc
17
17
  ### React
18
18
 
19
19
  ```bash
20
- npm install @kteneyck/cesium-timeline-react @kteneyck/cesium-timeline-core
20
+ npm install @kteneyck/cesium-timeline-react
21
21
  ```
22
22
 
23
23
  Peer dependencies: `react` ≥ 19, `cesium` ≥ 1.100
@@ -25,7 +25,7 @@ Peer dependencies: `react` ≥ 19, `cesium` ≥ 1.100
25
25
  ### Angular
26
26
 
27
27
  ```bash
28
- npm install @kteneyck/cesium-timeline-angular @kteneyck/cesium-timeline-core
28
+ npm install @kteneyck/cesium-timeline-angular
29
29
  ```
30
30
 
31
31
  Peer dependencies: `@angular/core` ≥ 17, `cesium` ≥ 1.100
@@ -127,12 +127,14 @@ Angular components use standalone imports — no NgModule required. Selectors: `
127
127
  - **Netflix/Hulu-style controls** — transport buttons (⏮ ◀◀ ▶/⏸ ▶▶ ⏭) always stay centered; speed badge and LIVE button in the left column never cause layout shift.
128
128
  - **Conditional start/end buttons** — ⏮ and ⏭ are only rendered when `startTime` and `endTime` props are explicitly provided.
129
129
  - **Speed cycling** — FF cycles through `ffSpeeds` (default `2×→4×→8×→16×→32×→1×`); RW cycles through `rwSpeeds` (default `−1×→−2×→−4×→−8×→−16×→−32×`). Both arrays are fully configurable.
130
- - **LIVE button** — filled `● LIVE` when within 10 s of wall clock; dim outline `LIVE` otherwise; clicking jumps to `Date.now()` and resets speed to 1×.
130
+ - **LIVE button** — shows a red dot + filled background when within 10 s of wall clock; dim outline otherwise. Clicking jumps to `Date.now()` and resets speed to 1×. Configurable size (`sm`/`md`/`lg`) and position (`left`/`right`). Dot color is themeable via `liveDotColor`.
131
131
  - **Speed badge** — shown in the left column when multiplier ≠ 1×; click to reset to 1×.
132
132
  - **Two-line datetime display** — time displayed large/bold; date displayed smaller in the theme's active color.
133
133
  - **Clickable datetime** — pass `onDateTimeClick` to open your own date picker; pass the result back via `jumpToTime` to pan the canvas and set the time.
134
134
  - **Token-based datetime format** — built-in presets plus custom format strings with 17 supported tokens.
135
135
  - **Max tick limit** — `maxTicks` prop prevents the canvas from becoming overloaded at wide zoom levels by coarsening the tick scale automatically.
136
+ - **Ghost / preview needle** — while hovering over the timeline a semi-transparent (15% opacity) preview needle tracks the cursor so you can see exactly where a click or zoom-to-selection drag would land before committing. The ghost disappears when the cursor leaves the canvas or an interaction begins.
137
+ - **Zoom to selection** — drag in the tick area (away from the needle) to draw a time-range highlight with a crosshair cursor; on release the visible window zooms to exactly the selected span and fires `onRangeSelect` (React) / `rangeSelect` (Angular) with the resulting start and end times.
136
138
  - **Swim lanes** — display time intervals and instants as horizontal rows inside the canvas. Supports customizable styling, click/hover/double-click event hooks, drag-to-reorder, and vertical scrolling when lanes overflow.
137
139
  - **Fully themeable** — 16 theme properties cover every color, size, and font setting, including swim lane item border defaults.
138
140
  - **Localizable labels** — every control-bar label and tooltip is overridable via the `labels` prop; dynamic tooltips accept a `(multiplier: number) => string` callback. See [Labels & i18n](#labels--i18n).
@@ -144,37 +146,41 @@ Angular components use standalone imports — no NgModule required. Selectors: `
144
146
 
145
147
  ### `TimelineProps`
146
148
 
147
- | Prop | Type | Default | Description |
148
- |------|------|---------|-------------|
149
- | `clock` | `Cesium.Clock` | — | Cesium clock to sync with. Falls back to `setInterval` if omitted. |
150
- | `startTime` | `JulianDate \| Date` | now − 12 h | Left bound of initial visible window. Also shows the ⏮ button when provided. |
151
- | `endTime` | `JulianDate \| Date` | now + 12 h | Right bound of initial visible window. Also shows the ⏭ button when provided. |
152
- | `currentTime` | `JulianDate \| Date` | `startTime` | Initial needle position |
153
- | `height` | `number` | `120` | Canvas height in pixels |
154
- | `showControls` | `boolean` | `true` | Show/hide the control bar |
155
- | `enableDrag` | `boolean` | `true` | Show/hide the canvas (drag/seek area) |
156
- | `showLabels` | `boolean` | — | Show/hide tick labels on the canvas |
157
- | `snapToTicks` | `boolean` | — | Snap needle to nearest tick on drag |
158
- | `tickInterval` | `TickInterval \| number` | auto | Override automatic tick interval |
159
- | `maxTicks` | `number` | unlimited | Maximum number of major ticks on the canvas at once. When exceeded the tick scale is automatically coarsened. |
160
- | `ffSpeeds` | `number[]` | `[2,4,8,16,32,1]` | Speed steps cycled by the ▶▶ button. Last entry wraps back to first. |
161
- | `rwSpeeds` | `number[]` | `[1,2,4,8,16,32]` | Absolute-value speed steps cycled by the ◀◀ button (negated internally). |
162
- | `dateTimeFormat` | `string` | `'MMM DD YYYY HH:mm:ss'` | Token-based format string for the controls datetime display |
149
+ | Prop | Type | Default | Description |
150
+ |------|------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
151
+ | `clock` | `Cesium.Clock` | — | Cesium clock to sync with. Falls back to `setInterval` if omitted. |
152
+ | `startTime` | `JulianDate \| Date` | now − 12 h | Left bound of initial visible window. Also shows the ⏮ button when provided. |
153
+ | `endTime` | `JulianDate \| Date` | now + 12 h | Right bound of initial visible window. Also shows the ⏭ button when provided. |
154
+ | `currentTime` | `JulianDate \| Date` | `startTime` | Initial needle position |
155
+ | `height` | `number` | `120` | Canvas height in pixels |
156
+ | `showControls` | `boolean` | `true` | Show/hide the control bar |
157
+ | `enableDrag` | `boolean` | `true` | Show/hide the canvas (drag/seek area) |
158
+ | `showLabels` | `boolean` | — | Show/hide tick labels on the canvas |
159
+ | `snapToTicks` | `boolean` | — | Snap needle to nearest tick on drag |
160
+ | `tickInterval` | `TickInterval \| number` | auto | Override automatic tick interval |
161
+ | `maxTicks` | `number` | unlimited | Maximum number of major ticks on the canvas at once. When exceeded the tick scale is automatically coarsened. |
162
+ | `ffSpeeds` | `number[]` | `[2,4,8,16,32,1]` | Speed steps cycled by the ▶▶ button. Last entry wraps back to first. |
163
+ | `rwSpeeds` | `number[]` | `[1,2,4,8,16,32]` | Absolute-value speed steps cycled by the ◀◀ button (negated internally). |
164
+ | `dateTimeFormat` | `string` | `'MMM DD YYYY HH:mm:ss'` | Token-based format string for the controls datetime display |
163
165
  | `timezone` | `string` | browser local | IANA timezone name (e.g. `'UTC'`, `'America/New_York'`) or `'local'` for the browser's timezone. Controls both tick labels and the datetime display. When set, a short abbreviation (e.g. `UTC`, `EST`) appears to the right of the date. |
164
- | `onDateTimeClick` | `() => void` | — | Called when the user clicks the datetime display. Use to open your own date picker. |
165
- | `jumpToTime` | `JulianDate \| Date` | — | Set to programmatically jump the timeline to a moment (pans canvas + sets time). |
166
- | `theme` | `Partial<TimelineTheme>` | `defaultTheme` | Theme overrides (merged with defaults) |
167
- | `className` | `string` | — | CSS class applied to the root div |
168
- | `onTimeChange` | `(t: JulianDate) => void` | — | Fires when needle moves (drag, click, or clock tick) |
169
- | `onPlayPause` | `(playing: boolean) => void` | — | Fires on play/pause toggle |
170
- | `onMultiplierChange` | `(m: number) => void` | — | Fires when speed changes |
171
- | `swimLanes` | `SwimLane[]` | — | Array of swim lane definitions to render on the canvas |
172
- | `showSwimLanes` | `boolean` | `true` | Show or hide the swim lanes |
173
- | `onSwimLaneItemClick` | `(info: SwimLaneEventInfo) => void` | — | Fires when a swim lane item is clicked |
174
- | `onSwimLaneItemHover` | `(info: SwimLaneEventInfo \| null) => void` | — | Fires when mouse enters/leaves a swim lane item |
175
- | `onSwimLaneItemDoubleClick` | `(info: SwimLaneEventInfo) => void` | — | Fires when a swim lane item is double-clicked |
176
- | `onSwimLaneReorder` | `(orderedIds: string[]) => void` | — | Fires when swim lanes are reordered via drag. Receives the new lane id order. |
177
- | `labels` | `Partial<TimelineLabels>` | English defaults | Override any control-bar label or tooltip string. See [Labels & i18n](#labels--i18n). |
166
+ | `onDateTimeClick` | `() => void` | — | Called when the user clicks the datetime display. Use to open your own date picker. |
167
+ | `jumpToTime` | `JulianDate \| Date` | — | Set to programmatically jump the timeline to a moment (pans canvas + sets time). |
168
+ | `theme` | `Partial<TimelineTheme>` | `defaultTheme` | Theme overrides (merged with defaults) |
169
+ | `className` | `string` | — | CSS class applied to the root div |
170
+ | `onTimeChange` | `(t: JulianDate) => void` | — | Fires when needle moves (drag, click, or clock tick) |
171
+ | `onPlayPause` | `(playing: boolean) => void` | — | Fires on play/pause toggle |
172
+ | `onMultiplierChange` | `(m: number) => void` | — | Fires when speed changes |
173
+ | `swimLanes` | `SwimLane[]` | — | Array of swim lane definitions to render on the canvas |
174
+ | `showSwimLanes` | `boolean` | `true` | Show or hide the swim lanes |
175
+ | `onSwimLaneItemClick` | `(info: SwimLaneEventInfo) => void` | — | Fires when a swim lane item is clicked |
176
+ | `onSwimLaneItemHover` | `(info: SwimLaneEventInfo \| null) => void` | — | Fires when mouse enters/leaves a swim lane item |
177
+ | `onSwimLaneItemDoubleClick` | `(info: SwimLaneEventInfo) => void` | — | Fires when a swim lane item is double-clicked |
178
+ | `onSwimLaneReorder` | `(orderedIds: string[]) => void` | — | Fires when swim lanes are reordered via drag. Receives the new lane id order. |
179
+ | `onRangeSelect` | `(start: JulianDate, end: JulianDate) => void` | | Fires when the user completes a click-and-drag in the tick area. The visible window zooms to the selected span; receives the resulting start and end times. |
180
+ | `labels` | `Partial<TimelineLabels>` | English defaults | Override any control-bar label or tooltip string. See [Labels & i18n](#labels--i18n). |
181
+ | `liveButtonSize` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of the LIVE button in the control bar. |
182
+ | `liveButtonPosition` | `'left' \| 'right'` | `'left'` | Position of the LIVE button — beside the datetime display (`'left'`) or the opposite side of the control bar (`'right'`). |
183
+ | `invertScrollZoom` | `boolean` | `false` | Reverses the scroll-wheel zoom direction. By default scroll up zooms in and scroll down zooms out. Set to `true` for scroll down zooms in, scroll up zooms out. |
178
184
 
179
185
  ---
180
186
 
@@ -213,6 +219,7 @@ Pass a partial `TimelineTheme` object to the `theme` prop. Any omitted propertie
213
219
  | `buttonActiveColor` | `#d69826` | Active buttons, LIVE, speed badge, and date line colour |
214
220
  | `swimLaneItemBorderColor` | `#666666` | Default border colour for swim lane interval bars. Can be overridden per-lane or per-item. |
215
221
  | `swimLaneItemBorderWidth` | `0` | Default border width (px) for swim lane interval bars. Set to `0` to remove borders globally. Can be overridden per-lane or per-item. |
222
+ | `liveDotColor` | `#e53e3e` | Colour of the dot shown on the LIVE button when playback is live. |
216
223
 
217
224
  > **Note:** Theme colours must be resolved hex/rgb values. CSS variables like `var(--primary-color)` do **not** work in canvas `ctx.fillStyle`. Use `getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim()` to resolve them first.
218
225
 
@@ -362,13 +369,16 @@ The control bar uses a 3-column CSS grid so the transport buttons are always cen
362
369
 
363
370
  ### LIVE Button
364
371
 
365
- - Shows `● LIVE` (filled background) when the current time is within 10 seconds of `Date.now()`.
366
- - Shows `LIVE` (dim outline) otherwise.
367
- - Clicking jumps to `Date.now()`, centers the visible window ±12 h, and resets speed to 1×.
372
+ - Shows a red dot and filled background when the current time is within 2 seconds of `Date.now()`.
373
+ - Shows `LIVE` (dim outline, no dot) otherwise.
374
+ - Clicking jumps to `Date.now()`, centers the visible window, and resets speed to 1×.
375
+ - **Size** — controlled by the `liveButtonSize` prop: `'sm'` | `'md'` (default) | `'lg'`.
376
+ - **Position** — controlled by the `liveButtonPosition` prop: `'left'` (default, beside the datetime display) | `'right'` (right side of the control bar).
377
+ - **Dot color** — controlled by `theme.liveDotColor` (default `#e53e3e`).
368
378
 
369
379
  ### Speed Badge
370
380
 
371
- - Appears to the right of LIVE when multiplier ≠ 1×.
381
+ - Appears inline beside the LIVE button when multiplier ≠ 1×.
372
382
  - Shows `◀ N×` for reverse, `N× ▶` for fast-forward.
373
383
  - Clicking resets to 1× speed.
374
384
 
@@ -424,7 +434,7 @@ Every label and tooltip in the control bar is overridable via the `labels` prop.
424
434
  |-----|-------------------|-------|
425
435
  | `dateTimeClickTooltip` | `"Click to jump to a date/time"` | Tooltip on the datetime display when `onDateTimeClick` is wired up |
426
436
  | `liveLabel` | `"LIVE"` | LIVE button text when not at live time |
427
- | `liveActiveLabel` | `"LIVE"` | LIVE button text when at live time |
437
+ | `liveActiveLabel` | `"LIVE"` | LIVE button text when at live time (red dot is rendered separately) |
428
438
  | `liveTooltip` | `"Jump to live (now)"` | LIVE button tooltip when not at live time |
429
439
  | `liveActiveTooltip` | `"Currently live"` | LIVE button tooltip when at live time |
430
440
  | `resetSpeedTooltip` | `"Reset to 1× speed"` | Tooltip on the speed-reset badge |
@@ -454,7 +464,7 @@ const frLabels: Partial<TimelineLabels> = {
454
464
  playTooltip: 'Lecture',
455
465
  pauseTooltip: 'Pause',
456
466
  liveLabel: 'EN DIRECT',
457
- liveActiveLabel: 'EN DIRECT',
467
+ liveActiveLabel: 'EN DIRECT',
458
468
  liveTooltip: 'Aller en direct',
459
469
  liveActiveTooltip: 'Vous êtes en direct',
460
470
  rewindTooltip: 'Retour rapide',
@@ -480,7 +490,7 @@ export class AppComponent {
480
490
  playTooltip: 'Lecture',
481
491
  pauseTooltip: 'Pause',
482
492
  liveLabel: 'EN DIRECT',
483
- liveActiveLabel: 'EN DIRECT',
493
+ liveActiveLabel: 'EN DIRECT',
484
494
  liveTooltip: 'Aller en direct',
485
495
  liveActiveTooltip: 'Vous êtes en direct',
486
496
  rewindActiveTooltip: (n) => `Retour ${n}× — cliquer pour accélérer`,
@@ -596,6 +606,12 @@ const CesiumWithTimeline = () => {
596
606
  onTimeChange={(t) => { clock.currentTime = t; }}
597
607
  onPlayPause={(playing) => { clock.shouldAnimate = playing; }}
598
608
  onMultiplierChange={(m) => { clock.multiplier = m; }}
609
+ onRangeSelect={(start, end) => {
610
+ // Zoom the Cesium clock range to match the selected span
611
+ clock.startTime = start;
612
+ clock.stopTime = end;
613
+ clock.currentTime = start;
614
+ }}
599
615
  />
600
616
  )}
601
617
  </div>
@@ -695,6 +711,90 @@ const StandaloneTimeline = () => {
695
711
  };
696
712
  ```
697
713
 
714
+ ### Zoom to Selection
715
+
716
+ Click-and-drag in the tick area (the bottom strip, away from the needle) to draw a time-range highlight. The cursor becomes a crosshair while dragging. On mouse-up the **visible window zooms to exactly the selected span** and `onRangeSelect` fires with the start and end `JulianDate`.
717
+
718
+ A short click (no drag) in the tick area still moves the needle normally.
719
+
720
+ ```tsx
721
+ import { useState } from 'react';
722
+ import * as Cesium from 'cesium';
723
+ import { Timeline } from '@kteneyck/cesium-timeline-react';
724
+
725
+ const ZoomableTimeline = () => {
726
+ const [selectedRange, setSelectedRange] = useState<{ start: Date; end: Date } | null>(null);
727
+
728
+ return (
729
+ <>
730
+ {selectedRange && (
731
+ <p>
732
+ Selected: {selectedRange.start.toISOString()} → {selectedRange.end.toISOString()}
733
+ </p>
734
+ )}
735
+ <Timeline
736
+ clock={viewer.clock}
737
+ height={120}
738
+ onRangeSelect={(start, end) => {
739
+ setSelectedRange({
740
+ start: Cesium.JulianDate.toDate(start),
741
+ end: Cesium.JulianDate.toDate(end),
742
+ });
743
+ }}
744
+ />
745
+ </>
746
+ );
747
+ };
748
+ ```
749
+
750
+ In Angular, listen to the `(rangeSelect)` output — the event payload is `{ start: Cesium.JulianDate, end: Cesium.JulianDate }`:
751
+
752
+ ```html
753
+ <ct-timeline
754
+ [clock]="viewer.clock"
755
+ [height]="120"
756
+ (rangeSelect)="onRangeSelect($event)"
757
+ />
758
+ ```
759
+
760
+ ```typescript
761
+ onRangeSelect(range: { start: Cesium.JulianDate; end: Cesium.JulianDate }) {
762
+ this.viewer.clock.startTime = range.start;
763
+ this.viewer.clock.stopTime = range.end;
764
+ this.viewer.clock.currentTime = range.start;
765
+ }
766
+ ```
767
+
768
+ ---
769
+
770
+ ## Ghost / Preview Needle
771
+
772
+ When the user hovers over the timeline canvas a **ghost needle** (a semi-transparent version of the current-time indicator) tracks the cursor position in real time. It renders at **15% opacity** using the same `indicatorColor` and `indicatorLineWidth` as the real needle, giving a clear preview of where a click-to-seek or zoom-to-selection drag would begin — without moving the actual current time.
773
+
774
+ ### Behaviour summary
775
+
776
+ | State | Ghost visible? |
777
+ |---|---|
778
+ | Mouse idle over canvas | ✅ Follows cursor |
779
+ | Dragging (scrub / range-select / slide / zoom) | ❌ Hidden |
780
+ | Mouse leaves canvas | ❌ Hidden |
781
+
782
+ ### Theming the ghost needle
783
+
784
+ The ghost needle inherits `indicatorColor` and `indicatorLineWidth` from your theme — no additional configuration is needed. To change its appearance, update those two theme properties:
785
+
786
+ ```tsx
787
+ <Timeline
788
+ clock={viewer.clock}
789
+ theme={{
790
+ indicatorColor: '#ffd54f',
791
+ indicatorLineWidth: 3,
792
+ }}
793
+ />
794
+ ```
795
+
796
+ The ghost will render as the same colour at 15% opacity, and the real needle will render at full opacity.
797
+
698
798
  ---
699
799
 
700
800
  ## Swim Lanes