@xtia/timeline 1.0.2 → 1.0.4

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
@@ -94,6 +94,7 @@ const frames = eased
94
94
  .tween(0, 30)
95
95
  .map(Math.floor)
96
96
  .noRepeat()
97
+ .tap(n => console.log("Showing frame #", n))
97
98
  .map(n => `animation-frame-${n}.png`)
98
99
  .listen(filename => img.src = filename);
99
100
  ```
@@ -162,7 +163,8 @@ Tween emitters can interpolate numbers, arrays of numbers, strings, and objects
162
163
  * Otherwise the `from` string is progressively replaced, left-to-right, with the `to` string
163
164
 
164
165
  ```ts
165
- // note: this looks really cool
166
+ // tween four values in a CSS string
167
+ // see this live: https://codepen.io/xtaltia/pen/PwZYbKY
166
168
  timeline
167
169
  .range(0, 2000)
168
170
  .ease("elastic")
@@ -231,9 +233,10 @@ timeline.currentTime += 500;
231
233
  Seeking lets us control a Timeline with anything:
232
234
 
233
235
  ```ts
234
- // syncronise with a video, to show subtitles or related
236
+ // synchronise with a video, to show subtitles or related
235
237
  // activities:
236
238
  videoElement.addEventListener(
239
+ "timeupdate",
237
240
  () => timeline.seek(videoElement.currentTime)
238
241
  );
239
242
 
package/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { Timeline, animate, ChainingInterface } from "./internal/timeline";
2
2
  export { TimelinePoint, PointEvent } from "./internal/point";
3
- export { RangeProgression, TimelineRange } from "./internal/range";
4
- export { Emitter, UnsubscribeFunc } from "./internal/emitters";
3
+ export { TimelineRange } from "./internal/range";
4
+ export { Emitter, RangeProgression, UnsubscribeFunc } from "./internal/emitters";
5
5
  export { easers } from "./internal/easing";
@@ -1,13 +1,15 @@
1
- import { RangeProgression } from "./range";
1
+ import { Easer, easers } from "./easing";
2
+ import { Blendable } from "./tween";
2
3
  /** @internal */
3
- export declare function createEmitter<T>(onListen: (handler: Handler<T>) => UnsubscribeFunc): Emitter<T>;
4
+ export declare function createEmitter<T>(listen: ListenFunc<T>): Emitter<T>;
4
5
  /** @internal */
5
- export declare function createEmitter<T, API extends object>(onListen: (handler: Handler<T>) => UnsubscribeFunc, api: Omit<API, keyof Emitter<T>>): Emitter<T> & API;
6
+ export declare function createEmitter<T, API extends object>(onListen: ListenFunc<T>, api: Omit<API, keyof Emitter<T>>): Emitter<T> & API;
6
7
  /** @internal */
7
- export declare function createProgressEmitter<API extends object>(onListen: (handler: Handler<number>) => UnsubscribeFunc, api: Omit<API, keyof RangeProgression>): RangeProgression & API;
8
+ export declare function createProgressEmitter<API extends object>(onListen: ListenFunc<number>, api: Omit<API, keyof RangeProgression>): RangeProgression & API;
8
9
  /** @internal */
9
- export declare function createProgressEmitter(onListen: (handler: Handler<number>) => UnsubscribeFunc): RangeProgression;
10
+ export declare function createProgressEmitter(onListen: ListenFunc<number>): RangeProgression;
10
11
  type Handler<T> = (value: T) => void;
12
+ type ListenFunc<T> = (handler: Handler<T>) => UnsubscribeFunc;
11
13
  export type UnsubscribeFunc = () => void;
12
14
  export interface Emitter<T> {
13
15
  /**
@@ -49,4 +51,121 @@ export interface Emitter<T> {
49
51
  */
50
52
  tap(cb: Handler<T>): Emitter<T>;
51
53
  }
54
+ export interface RangeProgression extends Emitter<number> {
55
+ /**
56
+ * Creates a chainable progress emitter that applies an easing function to its parent's emitted values
57
+ *
58
+ * @param easer An easing function of the form `(progression: number) => number`
59
+ * @returns Listenable: emits eased progression values
60
+ */
61
+ ease(easer?: Easer | keyof typeof easers): RangeProgression;
62
+ /**
63
+ * Creates a chainable emitter that interpolates two given values by progression emitted by its parent
64
+ *
65
+ * Can interpolate types `number`, `number[]`, string and objects with a `blend(from: this, to: this): this` method
66
+ *
67
+ * @param from Value to interpolate from
68
+ * @param to Value to interpolate to
69
+ * @returns Listenable: emits interpolated values
70
+ */
71
+ tween(from: number, to: number): Emitter<number>;
72
+ /**
73
+ * Creates a chainable emitter that interpolates two given values by progression emitted by its parent
74
+ *
75
+ * Can interpolate types `number`, `number[]`, string and objects with a `blend(from: this, to: this): this` method
76
+ *
77
+ * #### String interpolation
78
+ * * If the strings contain tweenable tokens (numbers, colour codes) and are otherwise identical, those tokens are interpolated
79
+ * * Otherwise the `from` string is progressively replaced, left-to-right, with the `to` string
80
+ *
81
+ * eg
82
+ * ```ts
83
+ * range
84
+ * .tween("0px 0px 0px #0000", "4px 4px 8px #0005")
85
+ * .listen(s => element.style.textShadow = s);
86
+ * ```
87
+ *
88
+ * @param from Value to interpolate from
89
+ * @param to Value to interpolate to
90
+ * @returns Listenable: emits interpolated values
91
+ */
92
+ tween(from: string, to: string): Emitter<string>;
93
+ /**
94
+ * Creates a chainable emitter that interpolates two given values by progression emitted by its parent
95
+ *
96
+ * Can interpolate types `number`, `number[]`, string and objects with a `blend(from: this, to: this): this` method
97
+ *
98
+ * @param from Value to interpolate from
99
+ * @param to Value to interpolate to
100
+ * @returns Listenable: emits interpolated values
101
+ */
102
+ tween<T extends Blendable | number[]>(from: T, to: T): Emitter<T>;
103
+ /**
104
+ * Creates a chainable progress emitter that quantises progress, as emitted by its parent, to the nearest of `steps` discrete values.
105
+ *
106
+ * @param steps – positive integer (e.g. 10 → 0, .1, .2 … 1)
107
+ * @throws RangeError if steps is not a positive integer
108
+ * @returns Listenable: emits quantised progression values
109
+ */
110
+ snap(steps: number): RangeProgression;
111
+ /**
112
+ * Creates a chainable progress emitter that emits `1` when the incoming progress value is greater‑than‑or‑equal to the supplied `threshold`, otherwise emits `0`
113
+ *
114
+ * @param threshold the cut‑off value
115
+ * @returns Listenable: emits 0 or 1 after comparing progress with a threshold
116
+ */
117
+ threshold(threshold: number): RangeProgression;
118
+ /**
119
+ * Creates a chainable progress emitter that clamps incoming values
120
+ * @param min default 0
121
+ * @param max default 1
122
+ * @returns Listenable: emits clamped progression values
123
+ */
124
+ clamp(min?: number, max?: number): RangeProgression;
125
+ /**
126
+ * Creates a chainable progress emitter that maps incoming values to a repeating linear scale
127
+ * @param count Number of repetitions
128
+ */
129
+ repeat(count: number): RangeProgression;
130
+ /**
131
+ * Creates a chainable progress emitter that mirrors emissions from the parent emitter, invoking the provided callback `cb` as a side effect for each emission.
132
+ *
133
+ * The callback `cb` is called exactly once per parent emission, regardless of how many listeners are attached to the returned emitter.
134
+ * All listeners attached to the returned emitter receive the same values as the parent emitter.
135
+ *
136
+ * *Note*, the side effect `cb` is only invoked when there is at least one listener attached to the returned emitter
137
+ *
138
+ * @param cb A function to be called as a side effect for each value emitted by the parent emitter.
139
+ * @returns A new emitter that forwards all values from the parent, invoking `cb` as a side effect.
140
+ */
141
+ tap(cb: (value: number) => void): RangeProgression;
142
+ /**
143
+ * Creates a chainable progress emitter that selectively forwards emissions along the chain
144
+ * @param check Function that takes an emitted value and returns true if the emission should be forwarded along the chain
145
+ * @returns Listenable: emits values that pass the filter
146
+ */
147
+ filter(check: (value: number) => boolean): RangeProgression;
148
+ /**
149
+ * Creates a chainable progress emitter that discards emitted values that are the same as the last value emitted by the new emitter
150
+ * @returns Listenable: emits non-repeating values
151
+ */
152
+ noRepeat(): RangeProgression;
153
+ /**
154
+ * Creates a chainable progress emitter that offsets its parent's values by the given delta, wrapping between 0 and 1
155
+ *
156
+ * ```plain
157
+ *‎ 1
158
+ *‎ | /
159
+ *‎ o| /
160
+ *‎ u|/ __ delta=.5
161
+ *‎ t| /
162
+ *‎ | /
163
+ *‎ |___/__
164
+ *‎ 0 in 1
165
+ * ```
166
+ *
167
+ * @param delta
168
+ */
169
+ offset(delta: number): RangeProgression;
170
+ }
52
171
  export {};
@@ -6,31 +6,18 @@ const easing_1 = require("./easing");
6
6
  const tween_1 = require("./tween");
7
7
  const utils_1 = require("./utils");
8
8
  /** @internal */
9
- function createEmitter(onListen, api) {
9
+ function createEmitter(listen, api) {
10
10
  const propertyDescriptor = Object.fromEntries(Object.entries({
11
- listen: (handler) => {
12
- const uniqueHandler = (value) => {
11
+ listen: (handler) => listen((value) => {
12
+ handler(value);
13
+ }),
14
+ map: (mapFunc) => createEmitter(handler => listen((value) => {
15
+ handler(mapFunc(value));
16
+ })),
17
+ filter: (filterFunc) => createEmitter(handler => listen((value) => {
18
+ if (filterFunc(value))
13
19
  handler(value);
14
- };
15
- return onListen(uniqueHandler);
16
- },
17
- map: (mapFunc) => {
18
- return createEmitter(handler => {
19
- const pipedHandler = (value) => {
20
- handler(mapFunc(value));
21
- };
22
- return onListen(pipedHandler);
23
- });
24
- },
25
- filter: (filterFunc) => {
26
- return createEmitter(handler => {
27
- const filteredHandler = (value) => {
28
- if (filterFunc(value))
29
- handler(value);
30
- };
31
- return onListen(filteredHandler);
32
- });
33
- },
20
+ })),
34
21
  noRepeat: (compare) => {
35
22
  let previous = null;
36
23
  return createEmitter(handler => {
@@ -42,30 +29,10 @@ function createEmitter(onListen, api) {
42
29
  previous = { value };
43
30
  }
44
31
  };
45
- return onListen(filteredHandler);
32
+ return listen(filteredHandler);
46
33
  });
47
34
  },
48
- tap: (cb) => {
49
- let listeners = [];
50
- let parentUnsubscribe = null;
51
- const tapOnListen = (handler) => {
52
- listeners.push(handler);
53
- if (listeners.length === 1) {
54
- parentUnsubscribe = onListen(value => {
55
- cb(value);
56
- listeners.slice().forEach(fn => fn(value));
57
- });
58
- }
59
- return () => {
60
- listeners = listeners.filter(l => l !== handler);
61
- if (listeners.length === 0 && parentUnsubscribe) {
62
- parentUnsubscribe();
63
- parentUnsubscribe = null;
64
- }
65
- };
66
- };
67
- return createEmitter(tapOnListen);
68
- },
35
+ tap: (cb) => createTap(cb, createEmitter, listen),
69
36
  }).map(([key, value]) => [
70
37
  key,
71
38
  { value }
@@ -73,40 +40,79 @@ function createEmitter(onListen, api) {
73
40
  return Object.create(api ?? {}, propertyDescriptor);
74
41
  }
75
42
  /** @internal */
76
- function createProgressEmitter(onListen, api = {}) {
43
+ function createProgressEmitter(listen, api = {}) {
77
44
  const propertyDescriptor = Object.fromEntries(Object.entries({
78
45
  ease: (easer) => {
79
46
  const easerFunc = typeof easer == "string"
80
47
  ? easing_1.easers[easer]
81
48
  : easer;
82
- return createProgressEmitter(easer ? (handler => onListen((progress) => {
49
+ return createProgressEmitter(easer ? (handler => listen((progress) => {
83
50
  handler(easerFunc(progress));
84
- })) : onListen);
51
+ })) : listen);
85
52
  },
86
- tween: (from, to) => createEmitter(handler => onListen(progress => handler((0, tween_1.tweenValue)(from, to, progress)))),
53
+ tween: (from, to) => createEmitter(handler => listen(progress => handler((0, tween_1.tweenValue)(from, to, progress)))),
87
54
  snap: (steps) => {
88
55
  if (!Number.isInteger(steps) || steps <= 0) {
89
56
  throw new RangeError('snap(steps) requires a positive integer');
90
57
  }
91
- return createProgressEmitter(handler => onListen(progress => {
58
+ return createProgressEmitter(handler => listen(progress => {
92
59
  const snapped = Math.round(progress * steps) / steps;
93
60
  handler((0, utils_1.clamp)(snapped, 0, 1));
94
61
  }));
95
62
  },
96
- threshold: (threshold) => createProgressEmitter(handler => onListen(progress => {
63
+ threshold: (threshold) => createProgressEmitter(handler => listen(progress => {
97
64
  handler(progress >= threshold ? 1 : 0);
98
65
  })),
99
- clamp: (min = 0, max = 1) => createProgressEmitter(handler => onListen(progress => handler((0, utils_1.clamp)(progress, min, max)))),
66
+ clamp: (min = 0, max = 1) => createProgressEmitter(handler => listen(progress => handler((0, utils_1.clamp)(progress, min, max)))),
100
67
  repeat: (repetitions) => {
101
68
  repetitions = Math.max(0, repetitions);
102
- return createProgressEmitter(handler => onListen(progress => {
69
+ return createProgressEmitter(handler => listen(progress => {
103
70
  const out = (progress * repetitions) % 1;
104
71
  handler(out);
105
72
  }));
106
73
  },
74
+ tap: (cb) => createTap(cb, createProgressEmitter, listen),
75
+ filter: (filterFunc) => createProgressEmitter(handler => listen((value) => {
76
+ if (filterFunc(value))
77
+ handler(value);
78
+ })),
79
+ noRepeat: () => {
80
+ let previous = null;
81
+ return createProgressEmitter(handler => {
82
+ return listen((value) => {
83
+ if (!previous || (previous.value !== value)) {
84
+ handler(value);
85
+ previous = { value };
86
+ }
87
+ });
88
+ });
89
+ },
90
+ offset: (delta) => createProgressEmitter(handler => listen(value => handler((value + delta) % 1))),
107
91
  }).map(([key, value]) => [
108
92
  key,
109
93
  { value }
110
94
  ]));
111
- return createEmitter(onListen, Object.create(api, propertyDescriptor));
95
+ return Object.create(api ?? {}, propertyDescriptor);
96
+ }
97
+ function createTap(callback, create, parentListen) {
98
+ const listeners = [];
99
+ let parentUnsubscribe = null;
100
+ const tapOnListen = (handler) => {
101
+ listeners.push(handler);
102
+ if (listeners.length === 1) {
103
+ parentUnsubscribe = parentListen(value => {
104
+ callback(value);
105
+ listeners.slice().forEach(fn => fn(value));
106
+ });
107
+ }
108
+ return () => {
109
+ const idx = listeners.indexOf(handler);
110
+ listeners.splice(idx, 1);
111
+ if (listeners.length === 0 && parentUnsubscribe) {
112
+ parentUnsubscribe();
113
+ parentUnsubscribe = null;
114
+ }
115
+ };
116
+ };
117
+ return create(tapOnListen);
112
118
  }
@@ -7,16 +7,19 @@ export interface TimelinePoint extends Emitter<PointEvent> {
7
7
  /**
8
8
  * Creates a range on the Timeline, with a given duration, starting at this point
9
9
  * @param duration
10
+ * @returns Listenable: emits normalised (0..1) range progression
10
11
  */
11
12
  range(duration: number): TimelineRange;
12
13
  /**
13
14
  * Creates a range on the Timeline, with a given end point, starting at this point
14
15
  * @param endPoint
16
+ * @returns Listenable: emits normalised (0..1) range progression
15
17
  */
16
18
  to(endPoint: number | TimelinePoint): TimelineRange;
17
19
  /**
18
20
  * Creates a point on the Timeline at an offset position from this one
19
21
  * @param timeOffset
22
+ * @returns Listenable: emits a PointEvent when the point is reached or passed by a Timeline seek
20
23
  */
21
24
  delta(timeOffset: number): TimelinePoint;
22
25
  /**
@@ -1,25 +1,26 @@
1
1
  import { Easer, easers } from "./easing";
2
- import { Emitter } from "./emitters";
2
+ import { RangeProgression } from "./emitters";
3
3
  import { TimelinePoint } from "./point";
4
- import { Blendable } from "./tween";
5
4
  export interface TimelineRange extends RangeProgression {
6
5
  /**
7
6
  * Creates two ranges by seperating one at a given point
8
7
  * @param position Point of separation, relative to the range's start - if omitted, the range will be separated halfway
9
8
  *
10
9
  * Must be greater than 0 and less than the range's duration
10
+ * @returns Tuple of two ranges
11
11
  */
12
12
  bisect(position?: number): [TimelineRange, TimelineRange];
13
13
  /**
14
14
  * Creates a series of evenly-spread points across the range, excluding the range's start and end
15
15
  * @param count Number of Points to return
16
+ * @returns Array(count) of points
16
17
  */
17
18
  spread(count: number): TimelinePoint[];
18
19
  /**
19
20
  * Progresses the Timeline across the range
20
21
  * @param easer
21
22
  */
22
- play(easer?: Easer): Promise<void>;
23
+ play(easer?: Easer | keyof typeof easers): Promise<void>;
23
24
  /**
24
25
  * Creates a new range representing a direct expansion of this one
25
26
  * @param delta Amount to grow by (in time units)
@@ -34,6 +35,12 @@ export interface TimelineRange extends RangeProgression {
34
35
  * @returns Listenable: this range will emit a progression value (0..1) when a `seek()` passes or intersects it
35
36
  */
36
37
  scale(factor: number, anchor?: number): TimelineRange;
38
+ /**
39
+ * Checks if a point is within this range
40
+ * @param point The point to check
41
+ * @returns true if the provided point is within the range
42
+ */
43
+ contains(point: TimelinePoint): boolean;
37
44
  /** The point on the Timeline at which this range begins */
38
45
  readonly start: TimelinePoint;
39
46
  /** The point on the Timeline at which this range ends */
@@ -41,80 +48,3 @@ export interface TimelineRange extends RangeProgression {
41
48
  /** The duration of this range */
42
49
  readonly duration: number;
43
50
  }
44
- export interface RangeProgression extends Emitter<number> {
45
- /**
46
- * Creates a chainable progress emitter that applies an easing function to its parent's emitted values
47
- *
48
- * @param easer An easing function of the form `(progression: number) => number`
49
- * @returns Listenable: emits eased progression values
50
- */
51
- ease(easer?: Easer | keyof typeof easers): RangeProgression;
52
- /**
53
- * Creates a chainable emitter that interpolates two given values by progression emitted by its parent
54
- *
55
- * Can interpolate types `number`, `number[]`, string and objects with a `blend(from: this, to: this): this` method
56
- *
57
- * @param from Value to interpolate from
58
- * @param to Value to interpolate to
59
- * @returns Listenable: emits interpolated values
60
- */
61
- tween(from: number, to: number): Emitter<number>;
62
- /**
63
- * Creates a chainable emitter that interpolates two given values by progression emitted by its parent
64
- *
65
- * Can interpolate types `number`, `number[]`, string and objects with a `blend(from: this, to: this): this` method
66
- *
67
- * #### String interpolation
68
- * * If the strings contain tweenable tokens (numbers, colour codes) and are otherwise identical, those tokens are interpolated
69
- * * Otherwise the `from` string is progressively replaced, left-to-right, with the `to` string
70
- *
71
- * eg
72
- * ```ts
73
- * range
74
- * .tween("0px 0px 0px #0000", "4px 4px 8px #0005")
75
- * .listen(s => element.style.textShadow = s);
76
- * ```
77
- *
78
- * @param from Value to interpolate from
79
- * @param to Value to interpolate to
80
- * @returns Listenable: emits interpolated values
81
- */
82
- tween(from: string, to: string): Emitter<string>;
83
- /**
84
- * Creates a chainable emitter that interpolates two given values by progression emitted by its parent
85
- *
86
- * Can interpolate types `number`, `number[]`, string and objects with a `blend(from: this, to: this): this` method
87
- *
88
- * @param from Value to interpolate from
89
- * @param to Value to interpolate to
90
- * @returns Listenable: emits interpolated values
91
- */
92
- tween<T extends Blendable | number[]>(from: T, to: T): Emitter<T>;
93
- /**
94
- * Creates a chainable progress emitter that quantises progress, as emitted by its parent, to the nearest of `steps` discrete values.
95
- *
96
- * @param steps – positive integer (e.g. 10 → 0, .1, .2 … 1)
97
- * @throws RangeError if steps is not a positive integer
98
- * @returns Listenable: emits quantised progression values
99
- */
100
- snap(steps: number): RangeProgression;
101
- /**
102
- * Creates a chainable progress emitter that emits `1` when the incoming progress value is greater‑than‑or‑equal to the supplied `threshold`, otherwise emits `0`
103
- *
104
- * @param threshold the cut‑off value
105
- * @returns Listenable: emits 0 or 1 after comparing progress with a threshold
106
- */
107
- threshold(threshold: number): RangeProgression;
108
- /**
109
- * Creates a chainable progress emitter that clamps incoming values
110
- * @param min default 0
111
- * @param max default 1
112
- * @returns Listenable: emits clamped progression values
113
- */
114
- clamp(min?: number, max?: number): RangeProgression;
115
- /**
116
- * Creates a chainable progress emitter that maps incoming values to a repeating linear scale
117
- * @param count Number of repetitions
118
- */
119
- repeat(count: number): RangeProgression;
120
- }
@@ -201,6 +201,9 @@ class Timeline {
201
201
  }
202
202
  return this.range(newStart, newEnd - newStart);
203
203
  },
204
+ contains: point => {
205
+ return point.position >= startPosition && point.position < endPosition;
206
+ }
204
207
  });
205
208
  }
206
209
  getWrappedPosition(n) {
package/internal/tween.js CHANGED
@@ -93,27 +93,27 @@ function blendColours(from, to, bias) {
93
93
  return ("#" + blended.map(n => Math.round(n).toString(16).padStart(2, "0")).join("")).replace(/ff$/, "");
94
94
  }
95
95
  const tweenableTokenRegex = /(#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})\b|[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/g;
96
+ const tokenise = (s) => {
97
+ const chunks = [];
98
+ let lastIdx = 0;
99
+ let m;
100
+ while ((m = tweenableTokenRegex.exec(s))) {
101
+ const token = m[0];
102
+ const prefix = s.slice(lastIdx, m.index); // literal before token
103
+ chunks.push({ prefix, token });
104
+ lastIdx = m.index + token.length;
105
+ }
106
+ // trailing literal after the last token – stored as a final chunk
107
+ // with an empty token (so the consumer can easily append it)
108
+ const tail = s.slice(lastIdx);
109
+ if (tail.length) {
110
+ chunks.push({ prefix: tail, token: "" });
111
+ }
112
+ return chunks;
113
+ };
96
114
  function blendStrings(from, to, progress) {
97
115
  if (from === to || progress === 0)
98
116
  return from;
99
- const tokenise = (s) => {
100
- const chunks = [];
101
- let lastIdx = 0;
102
- let m;
103
- while ((m = tweenableTokenRegex.exec(s))) {
104
- const token = m[0];
105
- const prefix = s.slice(lastIdx, m.index); // literal before token
106
- chunks.push({ prefix, token });
107
- lastIdx = m.index + token.length;
108
- }
109
- // trailing literal after the last token – stored as a final chunk
110
- // with an empty token (so the consumer can easily append it)
111
- const tail = s.slice(lastIdx);
112
- if (tail.length) {
113
- chunks.push({ prefix: tail, token: "" });
114
- }
115
- return chunks;
116
- };
117
117
  const fromChunks = tokenise(from);
118
118
  const toChunks = tokenise(to);
119
119
  const tokenCount = fromChunks.filter(c => c.token).length;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xtia/timeline",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "repository": {
5
5
  "url": "https://github.com/tiadrop/timeline",
6
6
  "type": "github"