@windborne/grapher 1.0.21 → 1.0.24

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.
Files changed (44) hide show
  1. package/dist/26b23c7580ea3345d644.wasm +0 -0
  2. package/dist/744.bundle.cjs +2 -1
  3. package/dist/744.bundle.cjs.map +1 -0
  4. package/dist/744.bundle.esm.js +2 -1
  5. package/dist/744.bundle.esm.js.map +1 -0
  6. package/dist/bundle.cjs +1 -1
  7. package/dist/bundle.cjs.map +1 -1
  8. package/dist/bundle.esm.js +1 -1
  9. package/dist/bundle.esm.js.map +1 -1
  10. package/package.json +1 -1
  11. package/readme.md +1 -1
  12. package/src/components/graph_body.jsx +41 -1
  13. package/src/components/range_graph.jsx +70 -22
  14. package/src/components/x_axis.jsx +139 -21
  15. package/src/components/y_axis.jsx +0 -5
  16. package/src/grapher.jsx +4 -3
  17. package/src/grapher.scss +84 -43
  18. package/src/helpers/color_to_vector.js +6 -1
  19. package/src/helpers/colors.js +82 -16
  20. package/src/helpers/custom_prop_types.js +2 -1
  21. package/src/helpers/place_grid.js +219 -0
  22. package/src/index.d.ts +150 -24
  23. package/src/renderer/create_gl_program.js +11 -3
  24. package/src/renderer/draw_area.js +1161 -102
  25. package/src/renderer/draw_background.js +5 -0
  26. package/src/renderer/draw_bars.js +100 -11
  27. package/src/renderer/draw_line.js +338 -38
  28. package/src/renderer/draw_zero_line.js +5 -0
  29. package/src/renderer/extract_vertices.js +1 -1
  30. package/src/renderer/graph_body_renderer.js +278 -17
  31. package/src/renderer/line.frag +16 -1
  32. package/src/renderer/line_program.js +200 -10
  33. package/src/renderer/shadow.frag +98 -0
  34. package/src/renderer/shadow.vert +20 -0
  35. package/src/renderer/shadow_program.js +479 -0
  36. package/src/rust/Cargo.lock +22 -31
  37. package/src/rust/pkg/grapher_rs.d.ts +6 -0
  38. package/src/rust/pkg/grapher_rs.js +5 -0
  39. package/src/rust/pkg/grapher_rs_bg.js +305 -0
  40. package/src/rust/pkg/grapher_rs_bg.wasm +0 -0
  41. package/src/rust/pkg/grapher_rs_bg.wasm.d.ts +11 -0
  42. package/src/rust/pkg/index.js +397 -0
  43. package/src/state/calculate_tooltip_state.js +6 -6
  44. package/src/state/state_controller.js +20 -6
package/src/grapher.scss CHANGED
@@ -2,17 +2,17 @@
2
2
  $primary-graph-body-height: 400px,
3
3
  $secondary-graph-body-height: 50px,
4
4
 
5
- $background-color-1: #2a2a2b,
6
- $background-color-2: #3e3e40,
5
+ $background-color-1: #131313,
6
+ $background-color-2: #1e1e1e,
7
7
 
8
8
  $tooltip-line-color: #CCC,
9
9
  $tooltip-background-color: rgba(0, 0, 0, 0.6),
10
10
  $tooltip-text-color: #F0F0F0,
11
11
  $vertical-line-color: #CCC,
12
12
 
13
- $axis-line-color: silver,
14
- $axis-line-width: 2px,
15
- $axis-tick-color: #505053,
13
+ $axis-line-color: rgb(192, 192, 192, 0.3),
14
+ $axis-line-width: 1px,
15
+ $axis-tick-color: rgb(192, 192, 192, 0.3),
16
16
  $axis-text-color: #E0E0E3,
17
17
 
18
18
  $range-selection-text-color: silver,
@@ -24,15 +24,18 @@
24
24
  $range-selection-button-border: null,
25
25
  $range-selection-selected-button-border: null,
26
26
 
27
- $range-graph-selection-bar-size: 14px,
27
+ $range-graph-selection-background-color: rgba(0, 0, 0, 0.3),
28
28
  $range-graph-selection-range-color: rgba(255, 255, 255, 0.1),
29
29
  $range-graph-selection-outline-color: #AAA,
30
30
  $range-graph-selection-bar-color: #808083,
31
31
  $range-graph-selection-bar-track-color: #303033,
32
32
  $range-graph-selection-bar-rifles-color: #FFF,
33
+ $range-graph-selection-bar-height: 14px,
33
34
  $range-graph-handle-color: #AAA,
34
35
  $range-graph-handle-fill-color: #666,
35
36
  $range-graph-axis-text-color: #9f9f9f,
37
+ $range-graph-tick-highlighted-color: #FFF,
38
+ $range-graph-text-highlighted-color: #FFF,
36
39
 
37
40
  $annotation-background-color: rgba(255, 255, 255, 0.3),
38
41
 
@@ -52,6 +55,7 @@
52
55
  $new-grapher-color: #DDD
53
56
  ) {
54
57
  $x-axis-height: 20px;
58
+ $x-axis-dual-height: 30px;
55
59
  $padding: 10px;
56
60
 
57
61
  $axis-z-index: 0;
@@ -431,6 +435,7 @@
431
435
 
432
436
  .range-graph-container {
433
437
  display: flex;
438
+ margin-top: 10px;
434
439
  }
435
440
 
436
441
  .graph-body {
@@ -580,25 +585,11 @@
580
585
  &.x-axis {
581
586
  width: 1px;
582
587
  height: $x-axis-height;
583
- display: block;
584
-
585
- .axis-item {
586
- text {
587
- text-anchor: middle;
588
- }
589
-
590
- &.axis-item-first {
591
- text {
592
- text-anchor: start;
593
- }
594
- }
595
-
596
- &.axis-item-last {
597
- text {
598
- text-anchor: end;
599
- }
600
- }
588
+
589
+ &.x-axis-dual {
590
+ height: $x-axis-dual-height;
601
591
  }
592
+ display: block;
602
593
  }
603
594
 
604
595
  &.y-axis {
@@ -606,7 +597,6 @@
606
597
 
607
598
  .axis-item text {
608
599
  text-anchor: end;
609
- alignment-baseline: middle;
610
600
  }
611
601
 
612
602
  .y-axis-label {
@@ -634,11 +624,6 @@
634
624
  stroke-width: $axis-line-width;
635
625
  }
636
626
 
637
- .axis-line-shadow {
638
- stroke: $axis-tick-color;
639
- stroke-width: 1px;
640
- }
641
-
642
627
  .axis-item {
643
628
  path {
644
629
  stroke: $axis-tick-color;
@@ -652,7 +637,7 @@
652
637
 
653
638
  &.axis-item-major {
654
639
  .axis-tick {
655
- stroke-width: 2px;
640
+ stroke-width: 1px;
656
641
  }
657
642
  }
658
643
 
@@ -666,15 +651,19 @@
666
651
 
667
652
  .range-selection-graph {
668
653
  width: 100%;
669
- padding-bottom: $range-graph-selection-bar-size;
654
+ background-color: $range-graph-selection-background-color;
655
+ height: calc(#{$secondary-graph-body-height} + #{$range-graph-selection-bar-height});
670
656
 
671
657
  .graph-body-secondary {
672
658
  height: $secondary-graph-body-height;
673
659
 
660
+ canvas {
661
+ height: 100%;
662
+ }
663
+
674
664
  svg {
675
665
  position: absolute;
676
666
  left: 0;
677
- height: $secondary-graph-body-height;
678
667
  width: 100%;
679
668
  overflow: visible;
680
669
 
@@ -683,31 +672,65 @@
683
672
  cursor: ew-resize;
684
673
  }
685
674
 
686
- .selection-bar {
687
- fill: $range-graph-selection-bar-color;
688
- cursor: ew-resize;
675
+
676
+ .target-selection-outline {
677
+ stroke: $range-graph-selection-outline-color;
678
+ fill: none;
689
679
  }
690
680
 
691
681
  .selection-bar-track {
692
682
  fill: $range-graph-selection-bar-track-color;
693
683
  }
694
684
 
685
+ .selection-bar {
686
+ fill: $range-graph-selection-bar-color;
687
+ cursor: ew-resize;
688
+ }
689
+
695
690
  .selection-bar-rifles {
696
- fill: none;
697
- stroke-width: 1;
698
691
  stroke: $range-graph-selection-bar-rifles-color;
692
+ stroke-width: 1px;
693
+ cursor: ew-resize;
694
+ }
695
+
696
+ .selection-bar-handle-hit {
697
+ cursor: ew-resize;
699
698
  }
700
699
 
701
700
  .selection-bar-handle {
702
701
  stroke: $range-graph-handle-color;
703
- stroke-width: 1;
704
702
  fill: $range-graph-handle-fill-color;
703
+ stroke-width: 1px;
705
704
  cursor: ew-resize;
705
+ transition: stroke-width 0.15s ease, fill 0.15s ease;
706
706
  }
707
707
 
708
- .target-selection-outline {
709
- stroke: $range-graph-selection-outline-color;
710
- fill: none;
708
+ .selection-handle {
709
+ .selection-handle-hit {
710
+ cursor: ew-resize;
711
+ }
712
+
713
+ .selection-handle-line {
714
+ stroke: $range-graph-handle-color;
715
+ stroke-width: 1;
716
+ transition: stroke-width 0.15s ease;
717
+ }
718
+
719
+ &:hover .selection-handle-line {
720
+ stroke-width: 3;
721
+ }
722
+
723
+ &.selection-handle-dragging .selection-handle-line {
724
+ stroke-width: 3;
725
+ }
726
+
727
+ &:hover .selection-bar-handle {
728
+ stroke-width: 2px;
729
+ }
730
+
731
+ &.selection-handle-dragging .selection-bar-handle {
732
+ stroke-width: 2px;
733
+ }
711
734
  }
712
735
 
713
736
  .axis-item {
@@ -720,9 +743,19 @@
720
743
  }
721
744
 
722
745
  path {
723
- stroke: $axis-tick-color;
746
+ stroke: $range-graph-axis-text-color;
724
747
  stroke-width: 1px;
725
748
  }
749
+
750
+ &.axis-item-highlighted {
751
+ path {
752
+ stroke: $range-graph-tick-highlighted-color;
753
+ }
754
+
755
+ text {
756
+ fill: $range-graph-text-highlighted-color;
757
+ }
758
+ }
726
759
  }
727
760
  }
728
761
  }
@@ -841,8 +874,12 @@
841
874
  $range-graph-selection-bar-color: #B0B0B7,
842
875
  $range-graph-selection-bar-track-color: #EEE,
843
876
  $range-graph-selection-bar-rifles-color: #333,
877
+ $range-graph-selection-bar-height: 14px,
844
878
  $range-graph-handle-color: #333,
845
879
  $range-graph-handle-fill-color: #B0B0B7,
880
+ $range-graph-axis-text-color: #555,
881
+ $range-graph-tick-highlighted-color: #000,
882
+ $range-graph-text-highlighted-color: #000,
846
883
 
847
884
  $annotation-background-color: rgba(0, 0, 0, 0.3),
848
885
 
@@ -898,8 +935,12 @@
898
935
  $range-graph-selection-bar-color: #B0B0B7,
899
936
  $range-graph-selection-bar-track-color: #EEE,
900
937
  $range-graph-selection-bar-rifles-color: #333,
938
+ $range-graph-selection-bar-height: 14px,
901
939
  $range-graph-handle-color: #333,
902
940
  $range-graph-handle-fill-color: #B0B0B7,
941
+ $range-graph-axis-text-color: #333,
942
+ $range-graph-tick-highlighted-color: #000,
943
+ $range-graph-text-highlighted-color: #000,
903
944
 
904
945
  $annotation-background-color: rgba(0, 0, 0, 0.3),
905
946
 
@@ -23,10 +23,15 @@ export default function colorToVector(color) {
23
23
  ];
24
24
  }
25
25
 
26
- if (typeof color !== 'string' || !/^#[\dA-F]{6}$/i.test(color)) {
26
+ if (typeof color !== 'string' || !/^#[\dA-F]{3}$|^#[\dA-F]{6}$/i.test(color)) {
27
27
  throw new Error(`Color must be a hex string: ${color}`);
28
28
  }
29
29
 
30
+ // support for short hex codes, expanding 3-digit hex to 6-digit
31
+ if (color.length === 4) {
32
+ color = '#' + color[1] + color[1] + color[2] + color[2] + color[3] + color[3];
33
+ }
34
+
30
35
  const r = parseInt(color.substr(1, 2), 16)/255;
31
36
  const g = parseInt(color.substr(3, 2), 16)/255;
32
37
  const b = parseInt(color.substr(5, 2), 16)/255;
@@ -1,27 +1,93 @@
1
1
  export const LINE_COLORS = [
2
- '#F1C232',
3
- '#1259f8',
4
- '#cb4b4b',
5
- '#4da74d',
6
- '#9440ed',
7
- '#61e0ed',
8
- '#ed6d2c',
9
- '#ed13c6',
10
- '#bbed59'
2
+ "#F1C232",
3
+ "#1259f8",
4
+ "#cb4b4b",
5
+ "#4da74d",
6
+ "#9440ed",
7
+ "#61e0ed",
8
+ "#ed6d2c",
9
+ "#ed13c6",
10
+ "#bbed59",
11
11
  ];
12
12
 
13
13
  export default function getColor(seriesColor, i, multigrapherSeriesIndex) {
14
- if (typeof seriesColor === 'string') {
15
- return seriesColor;
14
+ if (typeof seriesColor === "string") {
15
+ return seriesColor;
16
+ }
17
+
18
+ if (typeof seriesColor === "number") {
19
+ return LINE_COLORS[seriesColor % LINE_COLORS.length];
20
+ }
21
+
22
+ if (multigrapherSeriesIndex !== undefined) {
23
+ return LINE_COLORS[multigrapherSeriesIndex % LINE_COLORS.length];
24
+ }
25
+
26
+ return LINE_COLORS[i % LINE_COLORS.length];
27
+ }
28
+
29
+ /**
30
+ * Applies reduced opacity to a color
31
+ * @param {string} color
32
+ * @param {number} opacityFactor
33
+ * @returns {string}
34
+ */
35
+ export function applyReducedOpacity(color, opacityFactor) {
36
+ if (!color) return color;
37
+
38
+ if (color.startsWith("rgba(")) {
39
+ const matches = color.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/);
40
+ if (matches) {
41
+ const [, r, g, b, a] = matches;
42
+ const newAlpha = parseFloat(a) * opacityFactor;
43
+ return `rgba(${r}, ${g}, ${b}, ${newAlpha})`;
16
44
  }
45
+ }
17
46
 
18
- if (typeof seriesColor === 'number') {
19
- return LINE_COLORS[seriesColor % LINE_COLORS.length];
47
+ if (color.startsWith("rgb(")) {
48
+ const matches = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
49
+ if (matches) {
50
+ const [, r, g, b] = matches;
51
+ return `rgba(${r}, ${g}, ${b}, ${opacityFactor})`;
20
52
  }
53
+ }
21
54
 
22
- if (multigrapherSeriesIndex !== undefined) {
23
- return LINE_COLORS[multigrapherSeriesIndex % LINE_COLORS.length];
55
+ if (color.startsWith("#")) {
56
+ let hex = color.slice(1);
57
+ if (hex.length === 3) {
58
+ hex = hex
59
+ .split("")
60
+ .map((char) => char + char)
61
+ .join("");
24
62
  }
63
+ const r = parseInt(hex.slice(0, 2), 16);
64
+ const g = parseInt(hex.slice(2, 4), 16);
65
+ const b = parseInt(hex.slice(4, 6), 16);
66
+ return `rgba(${r}, ${g}, ${b}, ${opacityFactor})`;
67
+ }
68
+
69
+ return color;
70
+ }
25
71
 
26
- return LINE_COLORS[i % LINE_COLORS.length];
72
+ /**
73
+ * Applies reduced opacity to a gradient
74
+ * @param {Array<[number, string]>} gradient
75
+ * @param {number} opacityFactor
76
+ * @returns {Array<[number, string]>}
77
+ */
78
+ export function applyReducedOpacityToGradient(gradient, opacityFactor) {
79
+ if (!gradient || !Array.isArray(gradient)) {
80
+ return gradient;
81
+ }
82
+
83
+ return gradient.map((stop) => {
84
+ if (Array.isArray(stop) && stop.length === 2) {
85
+ const [position, color] = stop;
86
+ const translucentColor = applyReducedOpacity(color, opacityFactor);
87
+ return [position, translucentColor];
88
+ } else if (typeof stop === "string") {
89
+ return applyReducedOpacity(stop, opacityFactor);
90
+ }
91
+ return stop;
92
+ });
27
93
  }
@@ -65,7 +65,7 @@ const SingleSeries = PropTypes.shape({
65
65
  background: PropTypes.object,
66
66
  hideFromKey: PropTypes.bool,
67
67
  showIndividualPoints: PropTypes.bool,
68
- rendering: PropTypes.oneOf(['line', 'bar', 'area']), // defaults to line
68
+ rendering: PropTypes.oneOf(['line', 'bar', 'area', 'shadow']), // defaults to line
69
69
  negativeColor: PropTypes.string, // only applies to bar
70
70
  gradient: PropTypes.array, // only applies to area
71
71
  zeroLineWidth: PropTypes.number, // only applies to bar and area
@@ -75,6 +75,7 @@ const SingleSeries = PropTypes.shape({
75
75
  tooltipWidth: PropTypes.number,
76
76
  hasAreaBottom: PropTypes.bool,
77
77
  shadowColor: PropTypes.string,
78
+ gradient: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.array])), // gradient colors or [position, color] pairs
78
79
  rangeKey: PropTypes.string
79
80
  });
80
81
 
@@ -248,6 +248,225 @@ function placeDatesGrid({ min, max, precision, expectedLabelSize, labelPadding,
248
248
  return ticks.filter(({ pixelValue }) => pixelValue <= totalSize && pixelValue >= 0);
249
249
  }
250
250
 
251
+ function placeTimeOnlyGrid({ min, max, precision, expectedLabelSize, labelPadding, totalSize, skipFirst=false, skipLast=false, scale='linear', formatter, inverted=false, formatOptions={} }) {
252
+ const paddedLabelSize = expectedLabelSize + 2*labelPadding;
253
+ const ticks = [];
254
+ const placementParams = { scale, min, max, inverted, totalSize, precision, formatter, formatOptions, dates: true };
255
+
256
+ const desiredCount = Math.floor(totalSize/paddedLabelSize);
257
+ const span = max - min;
258
+
259
+ let unit, amount;
260
+
261
+ const hourSpan = span / (60 * 60 * 1000);
262
+ const minuteSpan = span / (60 * 1000);
263
+
264
+ if (hourSpan <= desiredCount * 8) {
265
+ unit = 'h';
266
+ amount = Math.max(1, Math.ceil(hourSpan / desiredCount));
267
+ if (amount <= 6) {
268
+ } else if (amount <= 12) {
269
+ amount = 12;
270
+ } else if (amount <= 24) {
271
+ amount = 24;
272
+ } else {
273
+ amount = Math.ceil(amount / 24) * 24;
274
+ }
275
+ } else {
276
+ unit = 'h';
277
+ amount = 24;
278
+ }
279
+
280
+ if (!skipFirst) {
281
+ ticks.push(placeTickByPixel(0, {...placementParams, justTime: true}, {position: 'first'}));
282
+ }
283
+
284
+ let currentDate = new Date(min);
285
+
286
+ if (unit === 'h') {
287
+ currentDate.setMinutes(0, 0, 0);
288
+ if (amount === 24) {
289
+ currentDate.setHours(0);
290
+ } else if (amount === 12) {
291
+ const currentHour = currentDate.getHours();
292
+ const alignedHour = currentHour < 12 ? 0 : 12;
293
+ currentDate.setHours(alignedHour);
294
+ } else {
295
+ const currentHour = currentDate.getHours();
296
+ const alignedHour = Math.floor(currentHour / amount) * amount;
297
+ currentDate.setHours(alignedHour);
298
+ }
299
+ } else if (unit === 'm') {
300
+ currentDate.setSeconds(0, 0);
301
+ const currentMinute = currentDate.getMinutes();
302
+ const alignedMinute = Math.floor(currentMinute / amount) * amount;
303
+ currentDate.setMinutes(alignedMinute);
304
+ } else if (unit === 's') {
305
+ currentDate.setMilliseconds(0);
306
+ const currentSecond = currentDate.getSeconds();
307
+ const alignedSecond = Math.floor(currentSecond / amount) * amount;
308
+ currentDate.setSeconds(alignedSecond);
309
+ }
310
+
311
+ while (currentDate < min) {
312
+ if (unit === 'h') {
313
+ currentDate = new Date(currentDate.valueOf() + amount * 60 * 60 * 1000);
314
+ } else if (unit === 'm') {
315
+ currentDate = new Date(currentDate.valueOf() + amount * 60 * 1000);
316
+ } else if (unit === 's') {
317
+ currentDate = new Date(currentDate.valueOf() + amount * 1000);
318
+ }
319
+ }
320
+
321
+ while (currentDate < max) {
322
+ const tick = placeTick(currentDate, {...placementParams, justTime: true});
323
+
324
+ const reservedSpaceForLast = expectedLabelSize + labelPadding;
325
+ const maxPixelForMiddleTicks = totalSize - reservedSpaceForLast;
326
+
327
+ if (ticks.length && (tick.pixelValue - ticks[ticks.length - 1].pixelValue) < (expectedLabelSize + labelPadding)) {
328
+ } else if (tick.pixelValue + expectedLabelSize < maxPixelForMiddleTicks) {
329
+ ticks.push(tick);
330
+ } else {
331
+ break;
332
+ }
333
+
334
+ if (unit === 'h') {
335
+ currentDate = new Date(currentDate.valueOf() + amount * 60 * 60 * 1000);
336
+ } else if (unit === 'm') {
337
+ currentDate = new Date(currentDate.valueOf() + amount * 60 * 1000);
338
+ } else if (unit === 's') {
339
+ currentDate = new Date(currentDate.valueOf() + amount * 1000);
340
+ } else {
341
+ break;
342
+ }
343
+ }
344
+
345
+ if (!skipLast) {
346
+ const lastTick = placeTick(max, {...placementParams, justTime: true}, {position: 'last'});
347
+ if (ticks.length === 0 || (lastTick.pixelValue - ticks[ticks.length - 1].pixelValue) >= (expectedLabelSize + labelPadding/2)) {
348
+ ticks.push(lastTick);
349
+ } else {
350
+ ticks[ticks.length - 1] = lastTick;
351
+ }
352
+ }
353
+
354
+ return ticks.filter(({ pixelValue }) => pixelValue <= totalSize && pixelValue >= 0);
355
+ }
356
+
357
+ function placeDateOnlyGrid({ min, max, precision, expectedLabelSize, labelPadding, totalSize, skipFirst=false, skipLast=false, scale='linear', formatter, inverted=false, formatOptions={} }) {
358
+ const paddedLabelSize = expectedLabelSize + 2*labelPadding;
359
+ const ticks = [];
360
+
361
+ const minYear = new Date(min).getFullYear();
362
+ const maxYear = new Date(max).getFullYear();
363
+ const spanMultipleYears = minYear !== maxYear;
364
+
365
+ const customDateFormatter = (date, options) => {
366
+ const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
367
+ const d = new Date(date);
368
+ const month = monthNames[d.getMonth()];
369
+ const day = d.getDate();
370
+ const year = d.getFullYear();
371
+
372
+ if (spanMultipleYears) {
373
+ return `${month} ${day} ${year}`;
374
+ } else {
375
+ return `${month} ${day}`;
376
+ }
377
+ };
378
+
379
+ const placementParams = { scale, min, max, inverted, totalSize, precision, formatter: customDateFormatter, formatOptions, dates: true };
380
+
381
+ const span = max - min;
382
+ const hourSpan = span / (60 * 60 * 1000);
383
+
384
+ let { amount, unit } = getEvenDateTickSpacing(span, totalSize/paddedLabelSize, formatOptions.unitOverride);
385
+
386
+ if ((unit === 'h' || unit === 'm' || unit === 's') && hourSpan >= 48) {
387
+ unit = 'd';
388
+ amount = Math.max(1, Math.ceil(hourSpan / 24 / Math.floor(totalSize/paddedLabelSize)));
389
+ } else if ((unit === 'h' || unit === 'm' || unit === 's') && hourSpan < 48) {
390
+ const startTick = placeTick(min, {...placementParams, justDate: true}, {position: 'first'});
391
+ const endTick = placeTick(max, {...placementParams, justDate: true}, {position: 'last'});
392
+
393
+ const ticks = [startTick];
394
+ if ((endTick.pixelValue - startTick.pixelValue) >= (expectedLabelSize + labelPadding)) {
395
+ ticks.push(endTick);
396
+ }
397
+
398
+ return ticks.filter(({ pixelValue }) => pixelValue <= totalSize && pixelValue >= 0);
399
+ }
400
+
401
+ if (!skipFirst) {
402
+ ticks.push(placeTickByPixel(0, {...placementParams, justDate: true}, {position: 'first'}));
403
+ }
404
+
405
+ let currentDate = new Date(min);
406
+
407
+ if (unit === 'month') {
408
+ currentDate = startOfDayInTimezone(currentDate, formatOptions.timeZone);
409
+ currentDate.setDate(1);
410
+ } else if (unit === 'd') {
411
+ currentDate = startOfDayInTimezone(currentDate, formatOptions.timeZone);
412
+ currentDate.setHours(0, 0, 0, 0);
413
+ }
414
+
415
+ while (currentDate < max) {
416
+ let delta = 24*60*60*1000;
417
+
418
+ if (unit === 'month') {
419
+ delta = 0;
420
+ if (currentDate.getMonth() === 11) {
421
+ currentDate = new Date(currentDate.getFullYear() + 1, 0, 1);
422
+ } else {
423
+ currentDate = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1);
424
+ }
425
+ } else if (unit === 'year') {
426
+ currentDate = new Date(currentDate.getFullYear() + 1, 0, 1);
427
+ delta = 0;
428
+ } else if (unit === 'd') {
429
+ delta = amount * 24 * 60 * 60 * 1000;
430
+ }
431
+
432
+ if (delta > 0) {
433
+ currentDate = new Date(currentDate.valueOf() + delta);
434
+ }
435
+
436
+ const tick = placeTick(currentDate, {...placementParams, justDate: true});
437
+
438
+ const reservedSpaceForLast = expectedLabelSize + labelPadding;
439
+ const maxPixelForMiddleTicks = totalSize - reservedSpaceForLast;
440
+
441
+ if (ticks.length && (tick.pixelValue - ticks[ticks.length - 1].pixelValue) < (expectedLabelSize + (labelPadding || 0))) {
442
+ continue;
443
+ }
444
+
445
+ if (tick.pixelValue + expectedLabelSize < maxPixelForMiddleTicks) {
446
+ ticks.push(tick);
447
+ } else {
448
+ break;
449
+ }
450
+ }
451
+
452
+ if (!skipLast) {
453
+ const lastTick = placeTick(max, {...placementParams, justDate: true}, {position: 'last'});
454
+ if (ticks.length === 0 || (lastTick.pixelValue - ticks[ticks.length - 1].pixelValue) >= (expectedLabelSize + labelPadding/2)) {
455
+ ticks.push(lastTick);
456
+ } else {
457
+ ticks[ticks.length - 1] = lastTick;
458
+ }
459
+ }
460
+
461
+ if (ticks.length === 0 && hourSpan >= 48) {
462
+ ticks.push(placeTickByPixel(0, {...placementParams, justDate: true}, {position: 'first'}));
463
+ }
464
+
465
+ return ticks.filter(({ pixelValue }) => pixelValue <= totalSize && pixelValue >= 0);
466
+ }
467
+
468
+ export { placeTimeOnlyGrid, placeDateOnlyGrid };
469
+
251
470
  export default function placeGrid(opts) {
252
471
  if (opts.dates) {
253
472
  return placeDatesGrid(opts);