@unovis/ts 1.5.1-xplg.4 → 1.6.0-beta.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.
Files changed (151) hide show
  1. package/README.md +1 -1
  2. package/components/annotations/style.js.map +1 -1
  3. package/components/area/config.d.ts +7 -3
  4. package/components/area/config.js +1 -1
  5. package/components/area/config.js.map +1 -1
  6. package/components/area/index.js +25 -9
  7. package/components/area/index.js.map +1 -1
  8. package/components/area/style.js.map +1 -1
  9. package/components/axis/index.d.ts +1 -0
  10. package/components/axis/index.js +10 -2
  11. package/components/axis/index.js.map +1 -1
  12. package/components/axis/style.js.map +1 -1
  13. package/components/brush/config.d.ts +1 -1
  14. package/components/brush/config.js.map +1 -1
  15. package/components/brush/style.js.map +1 -1
  16. package/components/bullet-legend/style.js.map +1 -1
  17. package/components/chord-diagram/index.js +3 -1
  18. package/components/chord-diagram/index.js.map +1 -1
  19. package/components/chord-diagram/style.js.map +1 -1
  20. package/components/crosshair/style.js.map +1 -1
  21. package/components/donut/style.js.map +1 -1
  22. package/components/flow-legend/style.js.map +1 -1
  23. package/components/free-brush/style.js.map +1 -1
  24. package/components/graph/index.js +3 -4
  25. package/components/graph/index.js.map +1 -1
  26. package/components/graph/modules/link/index.js +1 -2
  27. package/components/graph/modules/link/index.js.map +1 -1
  28. package/components/graph/modules/link/style.js.map +1 -1
  29. package/components/graph/modules/node/style.js.map +1 -1
  30. package/components/graph/modules/panel/style.js.map +1 -1
  31. package/components/graph/style.js.map +1 -1
  32. package/components/grouped-bar/style.js.map +1 -1
  33. package/components/leaflet-flow-map/config.js +0 -1
  34. package/components/leaflet-flow-map/config.js.map +1 -1
  35. package/components/leaflet-flow-map/shaders.js.map +1 -1
  36. package/components/leaflet-map/config.js +0 -1
  37. package/components/leaflet-map/config.js.map +1 -1
  38. package/components/leaflet-map/modules/map.js +3 -3
  39. package/components/leaflet-map/modules/map.js.map +1 -1
  40. package/components/leaflet-map/renderer/leaflet-maplibre-gl.js.map +1 -1
  41. package/components/leaflet-map/style.js.map +1 -1
  42. package/components/line/config.d.ts +5 -0
  43. package/components/line/config.js +1 -1
  44. package/components/line/config.js.map +1 -1
  45. package/components/line/index.js +41 -2
  46. package/components/line/index.js.map +1 -1
  47. package/components/line/style.d.ts +1 -0
  48. package/components/line/style.js +12 -1
  49. package/components/line/style.js.map +1 -1
  50. package/components/line/types.d.ts +1 -0
  51. package/components/nested-donut/config.d.ts +1 -1
  52. package/components/nested-donut/config.js.map +1 -1
  53. package/components/nested-donut/style.js.map +1 -1
  54. package/components/sankey/modules/label.js.map +1 -1
  55. package/components/sankey/modules/link.js.map +1 -1
  56. package/components/sankey/style.js.map +1 -1
  57. package/components/scatter/style.js.map +1 -1
  58. package/components/stacked-bar/style.js.map +1 -1
  59. package/components/timeline/config.d.ts +14 -65
  60. package/components/timeline/config.js +1 -15
  61. package/components/timeline/config.js.map +1 -1
  62. package/components/timeline/index.d.ts +10 -21
  63. package/components/timeline/index.js +93 -379
  64. package/components/timeline/index.js.map +1 -1
  65. package/components/timeline/style.d.ts +0 -7
  66. package/components/timeline/style.js +1 -40
  67. package/components/timeline/style.js.map +1 -1
  68. package/components/tooltip/config.d.ts +6 -4
  69. package/components/tooltip/config.js +3 -1
  70. package/components/tooltip/config.js.map +1 -1
  71. package/components/tooltip/index.d.ts +7 -3
  72. package/components/tooltip/index.js +39 -9
  73. package/components/tooltip/index.js.map +1 -1
  74. package/components/tooltip/style.js.map +1 -1
  75. package/components/topojson-map/index.js.map +1 -1
  76. package/components/topojson-map/style.js.map +1 -1
  77. package/components/vis-controls/style.js.map +1 -1
  78. package/components/xy-labels/style.js.map +1 -1
  79. package/components.d.ts +0 -4
  80. package/components.js +0 -2
  81. package/components.js.map +1 -1
  82. package/containers/single-container/config.d.ts +0 -3
  83. package/containers/single-container/config.js.map +1 -1
  84. package/containers/single-container/index.js +1 -2
  85. package/containers/single-container/index.js.map +1 -1
  86. package/containers/xy-container/config.d.ts +0 -5
  87. package/containers/xy-container/config.js +1 -1
  88. package/containers/xy-container/config.js.map +1 -1
  89. package/containers/xy-container/index.d.ts +0 -1
  90. package/containers/xy-container/index.js +11 -15
  91. package/containers/xy-container/index.js.map +1 -1
  92. package/core/container/config.js +0 -1
  93. package/core/container/config.js.map +1 -1
  94. package/index.js +3 -5
  95. package/index.js.map +1 -1
  96. package/maps/ind-regions.json.js +1 -1
  97. package/maps/us-counties.json.js +8 -8
  98. package/package.json +6 -5
  99. package/styles/index.js.map +1 -1
  100. package/styles/patterns.js.map +1 -1
  101. package/styles/sizes.js.map +1 -1
  102. package/types/position.d.ts +1 -2
  103. package/types/position.js +0 -1
  104. package/types/position.js.map +1 -1
  105. package/types.d.ts +0 -2
  106. package/types.js +1 -4
  107. package/types.js.map +1 -1
  108. package/utils/color.d.ts +0 -14
  109. package/utils/color.js +2 -26
  110. package/utils/color.js.map +1 -1
  111. package/utils/data.d.ts +1 -1
  112. package/utils/data.js +1 -1
  113. package/utils/data.js.map +1 -1
  114. package/utils/index.js +3 -3
  115. package/utils/path.d.ts +0 -8
  116. package/utils/path.js +1 -109
  117. package/utils/path.js.map +1 -1
  118. package/utils/text.d.ts +1 -2
  119. package/utils/text.js +2 -10
  120. package/utils/text.js.map +1 -1
  121. package/components/rolling-pin-legend/config.d.ts +0 -19
  122. package/components/rolling-pin-legend/config.js +0 -9
  123. package/components/rolling-pin-legend/config.js.map +0 -1
  124. package/components/rolling-pin-legend/index.d.ts +0 -16
  125. package/components/rolling-pin-legend/index.js +0 -63
  126. package/components/rolling-pin-legend/index.js.map +0 -1
  127. package/components/rolling-pin-legend/style.d.ts +0 -5
  128. package/components/rolling-pin-legend/style.js +0 -40
  129. package/components/rolling-pin-legend/style.js.map +0 -1
  130. package/components/rolling-pin-legend/types.d.ts +0 -1
  131. package/components/timeline/constants.d.ts +0 -3
  132. package/components/timeline/constants.js +0 -6
  133. package/components/timeline/constants.js.map +0 -1
  134. package/components/timeline/types.d.ts +0 -62
  135. package/components/timeline/types.js +0 -2
  136. package/components/timeline/types.js.map +0 -1
  137. package/components/timeline/utils.d.ts +0 -2
  138. package/components/timeline/utils.js +0 -16
  139. package/components/timeline/utils.js.map +0 -1
  140. package/components/treemap/config.d.ts +0 -44
  141. package/components/treemap/config.js +0 -6
  142. package/components/treemap/config.js.map +0 -1
  143. package/components/treemap/index.d.ts +0 -16
  144. package/components/treemap/index.js +0 -263
  145. package/components/treemap/index.js.map +0 -1
  146. package/components/treemap/style.d.ts +0 -22
  147. package/components/treemap/style.js +0 -62
  148. package/components/treemap/style.js.map +0 -1
  149. package/components/treemap/types.d.ts +0 -11
  150. package/components/treemap/types.js +0 -2
  151. package/components/treemap/types.js.map +0 -1
@@ -1,22 +1,15 @@
1
1
  import { select } from 'd3-selection';
2
- import { max, min, minIndex } from 'd3-array';
3
2
  import { scaleOrdinal } from 'd3-scale';
4
3
  import { drag } from 'd3-drag';
4
+ import { max } from 'd3-array';
5
5
  import { XYComponentCore } from '../../core/xy-component/index.js';
6
- import { getNumber, isNumber, arrayOfIndices, getValue, isPlainObject, isFunction, getString, groupBy, getMin, getMax } from '../../utils/data.js';
6
+ import { isNumber, unique, arrayOfIndices, getString, getNumber, getMin, getMax } from '../../utils/data.js';
7
7
  import { smartTransition } from '../../utils/d3.js';
8
8
  import { getColor } from '../../utils/color.js';
9
- import { trimSVGText, textAlignToAnchor } from '../../utils/text.js';
10
- import { arrowPolylinePath } from '../../utils/path.js';
11
- import { guid } from '../../utils/misc.js';
12
- import '../../types.js';
9
+ import { trimSVGText } from '../../utils/text.js';
13
10
  import { TimelineDefaultConfig } from './config.js';
14
11
  import * as style from './style.js';
15
- import { background, rows, arrows, lines, labels, rowIcons, scrollbar, scrollbarBackground, scrollbarHandle, label, rowIcon, row, rowOdd, lineGroup, line, lineStartIcon, lineEndIcon, arrow } from './style.js';
16
- import { TIMELINE_DEFAULT_ARROW_HEAD_LENGTH, TIMELINE_DEFAULT_ARROW_HEAD_WIDTH, TIMELINE_DEFAULT_ARROW_MARGIN } from './constants.js';
17
- import { getIconBleed } from './utils.js';
18
- import { TextAlign } from '../../types/text.js';
19
- import { Arrangement } from '../../types/position.js';
12
+ import { background, rows, lines, labels, scrollbar, scrollbarBackground, scrollbarHandle, label, row, rowOdd, line } from './style.js';
20
13
 
21
14
  class Timeline extends XYComponentCore {
22
15
  constructor(config) {
@@ -24,12 +17,6 @@ class Timeline extends XYComponentCore {
24
17
  this._defaultConfig = TimelineDefaultConfig;
25
18
  this.config = this._defaultConfig;
26
19
  this.events = {
27
- [Timeline.selectors.background]: {
28
- wheel: this._onMouseWheel.bind(this),
29
- },
30
- [Timeline.selectors.label]: {
31
- wheel: this._onMouseWheel.bind(this),
32
- },
33
20
  [Timeline.selectors.rows]: {
34
21
  wheel: this._onMouseWheel.bind(this),
35
22
  },
@@ -43,29 +30,14 @@ class Timeline extends XYComponentCore {
43
30
  this._maxScroll = 0;
44
31
  this._scrollbarHeight = 0;
45
32
  this._labelMargin = 5;
46
- this._labelWidth = 0; // Will be overridden in `get bleed ()`
47
- this._rowIconBleed = [0, 0];
48
- this._lineBleed = [0, 0];
49
- /** We define a dedicated clipping path for this component because it needs to behave
50
- * differently than the regular XYContainer's clipPath */
51
- this._clipPathId = guid();
52
33
  if (config)
53
34
  this.setConfig(config);
54
35
  // Invisible background rect to track events
55
36
  this._background = this.g.append('rect').attr('class', background);
56
- // Clip path
57
- this._clipPath = this.g.append('clipPath')
58
- .attr('id', this._clipPathId);
59
- this._clipPath.append('rect');
60
37
  // Group for content
61
- this._rowsGroup = this.g.append('g').attr('class', rows)
62
- .style('clip-path', `url(#${this._clipPathId})`);
63
- this._arrowsGroup = this.g.append('g').attr('class', arrows)
64
- .style('clip-path', `url(#${this._clipPathId})`);
65
- this._linesGroup = this.g.append('g').attr('class', lines)
66
- .style('clip-path', `url(#${this._clipPathId})`);
38
+ this._rowsGroup = this.g.append('g').attr('class', rows);
39
+ this._linesGroup = this.g.append('g').attr('class', lines);
67
40
  this._labelsGroup = this.g.append('g').attr('class', labels);
68
- this._rowIconsGroup = this.g.append('g').attr('class', rowIcons);
69
41
  this._scrollBarGroup = this.g.append('g').attr('class', scrollbar);
70
42
  // Scroll bar
71
43
  this._scrollBarBackground = this._scrollBarGroup.append('rect')
@@ -77,80 +49,35 @@ class Timeline extends XYComponentCore {
77
49
  .on('drag', this._onScrollbarDrag.bind(this));
78
50
  this._scrollBarHandle.call(dragBehaviour);
79
51
  }
80
- setConfig(config) {
81
- super.setConfig(config);
82
- }
83
- setData(data) {
84
- super.setData(data);
85
- }
86
52
  get bleed() {
87
- var _a, _b, _c, _d;
88
53
  const { config, datamodel: { data } } = this;
89
- const rowLabels = this._getRowLabels(data);
90
- const rowHeight = config.rowHeight || (this._height / rowLabels.length);
91
- const hasIcons = rowLabels.some(l => l.iconHref);
92
- const maxIconSize = max(rowLabels.map(l => l.iconSize || 0));
93
54
  // We calculate the longest label width to set the bleed values accordingly
94
- if ((_a = config.showRowLabels) !== null && _a !== void 0 ? _a : config.showLabels) {
95
- if ((_b = config.rowLabelWidth) !== null && _b !== void 0 ? _b : config.labelWidth)
96
- this._labelWidth = ((_c = config.rowLabelWidth) !== null && _c !== void 0 ? _c : config.labelWidth) + this._labelMargin;
55
+ let labelsBleed = 0;
56
+ if (config.showLabels) {
57
+ if (config.labelWidth)
58
+ labelsBleed = config.labelWidth + this._labelMargin;
97
59
  else {
98
- const longestLabel = rowLabels.reduce((longestLabel, l) => longestLabel.formattedLabel.length > l.formattedLabel.length ? longestLabel : l, rowLabels[0]);
60
+ const recordLabels = this._getRecordLabels(data);
61
+ const longestLabel = recordLabels.reduce((acc, val) => acc.length > val.length ? acc : val, '');
99
62
  const label$1 = this._labelsGroup.append('text')
100
63
  .attr('class', label)
101
- .text((longestLabel === null || longestLabel === void 0 ? void 0 : longestLabel.formattedLabel) || '')
102
- .call(trimSVGText, (_d = config.rowMaxLabelWidth) !== null && _d !== void 0 ? _d : config.maxLabelWidth);
64
+ .text(longestLabel)
65
+ .call(trimSVGText, config.maxLabelWidth);
103
66
  const labelWidth = label$1.node().getBBox().width;
104
- label$1.remove();
67
+ this._labelsGroup.empty();
105
68
  const tolerance = 1.15; // Some characters are wider than others so we add a little of extra space to take that into account
106
- this._labelWidth = labelWidth ? tolerance * labelWidth + this._labelMargin : 0;
69
+ labelsBleed = labelWidth ? tolerance * labelWidth + this._labelMargin : 0;
107
70
  }
108
71
  }
109
- // There can be multiple start / end items with the same timestamp, so we need to find the shortest one
110
- const minTimestamp = min(data, (d, i) => getNumber(d, config.x, i));
111
- const dataMin = data.filter((d, i) => getNumber(d, config.x, i) === minTimestamp);
112
- const dataMinShortestItemIdx = minIndex(dataMin, (d, i) => this._getLineDuration(d, i));
113
- const firstItemIdx = data.findIndex(d => d === dataMin[dataMinShortestItemIdx]);
114
- const firstItem = data[firstItemIdx];
115
- const maxTimestamp = max(data, (d, i) => getNumber(d, config.x, i) + this._getLineDuration(d, i));
116
- const dataMax = data.filter((d, i) => getNumber(d, config.x, i) + this._getLineDuration(d, i) === maxTimestamp);
117
- const dataMaxShortestItemIdx = minIndex(dataMax, (d, i) => this._getLineDuration(d, i));
118
- const lastItemIdx = data.findIndex(d => d === dataMax[dataMaxShortestItemIdx]);
119
- const lastItem = data[lastItemIdx];
120
- // Small segments bleed
121
- const lineBleed = [1, 1];
122
- if (config.showEmptySegments && config.lineCap && firstItem && lastItem) {
123
- const firstItemStart = getNumber(firstItem, config.x, firstItemIdx);
124
- const firstItemEnd = getNumber(firstItem, config.x, firstItemIdx) + this._getLineDuration(firstItem, firstItemIdx);
125
- const lastItemStart = getNumber(lastItem, config.x, lastItemIdx);
126
- const lastItemEnd = getNumber(lastItem, config.x, lastItemIdx) + this._getLineDuration(lastItem, lastItemIdx);
127
- const fullTimeRange = lastItemEnd - firstItemStart;
128
- const firstItemHeight = this._getLineWidth(firstItem, firstItemIdx, rowHeight);
129
- const lastItemHeight = this._getLineWidth(lastItem, lastItemIdx, rowHeight);
130
- if ((firstItemEnd - firstItemStart) / fullTimeRange * this._width < firstItemHeight)
131
- lineBleed[0] = firstItemHeight / 2;
132
- if ((lastItemEnd - lastItemStart) / fullTimeRange * this._width < lastItemHeight)
133
- lineBleed[1] = lastItemHeight / 2;
134
- }
135
- this._lineBleed = lineBleed;
136
- // Icon bleed
137
- const iconBleed = [0, 0];
138
- if (config.lineStartIcon) {
139
- iconBleed[0] = max(data, (d, i) => getIconBleed(d, i, config.lineStartIcon, config.lineStartIconSize, config.lineStartIconArrangement, rowHeight)) || 0;
140
- }
141
- if (config.lineEndIcon) {
142
- iconBleed[1] = max(data, (d, i) => getIconBleed(d, i, config.lineEndIcon, config.lineEndIconSize, config.lineEndIconArrangement, rowHeight)) || 0;
143
- }
144
- this._rowIconBleed = iconBleed;
72
+ const maxLineWidth = this._getMaxLineWidth();
145
73
  return {
146
74
  top: 0,
147
75
  bottom: 0,
148
- left: this._labelWidth + iconBleed[0] + (hasIcons ? maxIconSize : 0) + lineBleed[0],
149
- right: this._scrollBarWidth + this._scrollBarMargin + iconBleed[1] + lineBleed[1],
76
+ left: maxLineWidth / 2 + labelsBleed,
77
+ right: maxLineWidth / 2 + this._scrollBarWidth + this._scrollBarMargin,
150
78
  };
151
79
  }
152
80
  _render(customDuration) {
153
- var _a;
154
81
  super._render(customDuration);
155
82
  const { config, datamodel: { data } } = this;
156
83
  const duration = isNumber(customDuration) ? customDuration : config.duration;
@@ -158,185 +85,71 @@ class Timeline extends XYComponentCore {
158
85
  const yRange = this.yScale.range();
159
86
  const yStart = Math.min(...yRange);
160
87
  const yHeight = Math.abs(yRange[1] - yRange[0]);
161
- const rowLabels = this._getRowLabels(data);
162
- const numRowLabels = rowLabels.length;
163
- const rowHeight = config.rowHeight || (yHeight / numRowLabels);
164
- const yOrdinalScale = scaleOrdinal()
165
- .range(arrayOfIndices(numRowLabels))
166
- .domain(rowLabels.map(l => l.label));
167
- const lineDataPrepared = this._prepareLinesData(data, yOrdinalScale, rowHeight);
88
+ const maxLineWidth = this._getMaxLineWidth();
89
+ const recordLabels = this._getRecordLabels(data);
90
+ const recordLabelsUnique = unique(recordLabels);
91
+ const numUniqueRecords = recordLabelsUnique.length;
92
+ // Ordinal scale to handle records on the same type
93
+ const ordinalScale = scaleOrdinal();
94
+ ordinalScale.range(arrayOfIndices(numUniqueRecords));
168
95
  // Invisible Background rect to track events
169
96
  this._background
170
97
  .attr('width', this._width)
171
98
  .attr('height', this._height)
172
99
  .attr('opacity', 0);
173
- // Row Icons
174
- const rowIcons = this._rowIconsGroup.selectAll(`.${rowIcon}`)
175
- .data(rowLabels.filter(d => d.iconSize), l => l === null || l === void 0 ? void 0 : l.label);
176
- const rowIconsEnter = rowIcons.enter().append('use')
177
- .attr('class', rowIcon)
178
- .attr('x', 0)
179
- .attr('width', l => l.iconSize)
180
- .attr('height', l => l.iconSize)
181
- .attr('y', l => yStart + (yOrdinalScale(l.label) + 0.5) * rowHeight - l.iconSize / 2)
182
- .style('opacity', 0);
183
- smartTransition(rowIconsEnter.merge(rowIcons), duration)
184
- .attr('href', l => l.iconHref)
185
- .attr('x', 0)
186
- .attr('y', l => yStart + (yOrdinalScale(l.label) + 0.5) * rowHeight - l.iconSize / 2)
187
- .attr('width', l => l.iconSize)
188
- .attr('height', l => l.iconSize)
189
- .style('color', l => l.iconColor)
190
- .style('opacity', 1);
191
- smartTransition(rowIcons.exit(), duration)
192
- .style('opacity', 0)
193
- .remove();
194
100
  // Labels
195
101
  const labels = this._labelsGroup.selectAll(`.${label}`)
196
- .data(((_a = config.showRowLabels) !== null && _a !== void 0 ? _a : config.showLabels) ? rowLabels : [], l => l === null || l === void 0 ? void 0 : l.label);
197
- const labelOffset = config.rowLabelTextAlign === TextAlign.Center ? this._labelWidth / 2
198
- : config.rowLabelTextAlign === TextAlign.Left ? this._labelWidth
199
- : this._labelMargin;
200
- const xStart = xRange[0] - this._rowIconBleed[0] - this._lineBleed[0];
201
- const labelXStart = xStart - labelOffset;
102
+ .data(config.showLabels ? recordLabelsUnique : []);
202
103
  const labelsEnter = labels.enter().append('text')
203
- .attr('class', label)
204
- .attr('x', labelXStart)
205
- .attr('y', l => yStart + (yOrdinalScale(l.label) + 0.5) * rowHeight)
206
- .style('opacity', 0);
207
- const labelsMerged = labelsEnter.merge(labels)
208
- .text(l => l.formattedLabel)
104
+ .attr('class', label);
105
+ labelsEnter.merge(labels)
106
+ .attr('x', xRange[0] - maxLineWidth / 2 - this._labelMargin)
107
+ .attr('y', (label, i) => yStart + (ordinalScale(label) + 0.5) * config.rowHeight)
108
+ .text(label => label)
209
109
  .each((label, i, els) => {
210
- var _a, _b;
211
- const labelSelection = select(els[i]);
212
- trimSVGText(labelSelection, ((_a = config.rowLabelWidth) !== null && _a !== void 0 ? _a : config.labelWidth) || ((_b = config.rowMaxLabelWidth) !== null && _b !== void 0 ? _b : config.maxLabelWidth));
213
- // Apply custom label style if it has been provided
214
- const customStyle = getValue(label, config.rowLabelStyle);
215
- if (!isPlainObject(customStyle))
216
- return;
217
- for (const [prop, value] of Object.entries(customStyle)) {
218
- labelSelection.style(prop, value);
219
- }
220
- })
221
- .style('text-anchor', textAlignToAnchor(config.rowLabelTextAlign));
222
- smartTransition(labelsMerged, duration)
223
- .attr('x', labelXStart)
224
- .attr('y', l => yStart + (yOrdinalScale(l.label) + 0.5) * rowHeight)
225
- .style('opacity', 1);
226
- smartTransition(labels.exit(), duration)
227
- .style('opacity', 0)
228
- .remove();
110
+ trimSVGText(select(els[i]), config.labelWidth || config.maxLabelWidth);
111
+ });
112
+ labels.exit().remove();
229
113
  // Row background rects
230
- const timelineWidth = xRange[1] - xRange[0] + this._rowIconBleed[0] + this._rowIconBleed[1] + this._lineBleed[0] + this._lineBleed[1];
231
- const numRows = Math.max(Math.floor(yHeight / rowHeight), numRowLabels);
232
- const recordTypes = Array(numRows).fill(null).map((_, i) => rowLabels[i]);
114
+ const xStart = xRange[0];
115
+ const numRows = Math.max(Math.floor(yHeight / config.rowHeight), numUniqueRecords);
116
+ const recordTypes = Array(numRows).fill(null).map((_, i) => recordLabelsUnique[i]);
233
117
  const rects = this._rowsGroup.selectAll(`.${row}`)
234
118
  .data(recordTypes);
235
119
  const rectsEnter = rects.enter().append('rect')
236
- .attr('class', row)
237
- .attr('x', xStart)
238
- .attr('width', timelineWidth)
239
- .attr('y', (_, i) => yStart + i * rowHeight)
240
- .attr('height', rowHeight)
241
- .style('opacity', 0);
242
- const rectsMerged = rectsEnter.merge(rects)
243
- .classed(rowOdd, config.alternatingRowColors ? (_, i) => !(i % 2) : null);
244
- smartTransition(rectsMerged, duration)
245
- .attr('x', xStart)
246
- .attr('width', timelineWidth)
247
- .attr('y', (_, i) => yStart + i * rowHeight)
248
- .attr('height', rowHeight)
249
- .style('opacity', 1);
250
- smartTransition(rects.exit(), duration)
251
- .style('opacity', 0)
252
- .remove();
120
+ .attr('class', row);
121
+ rectsEnter.merge(rects)
122
+ .classed(rowOdd, config.alternatingRowColors ? (_, i) => !(i % 2) : null)
123
+ .attr('x', xStart - maxLineWidth / 2)
124
+ .attr('width', xRange[1] - xStart + maxLineWidth)
125
+ .attr('y', (_, i) => yStart + i * config.rowHeight)
126
+ .attr('height', config.rowHeight);
127
+ rects.exit().remove();
253
128
  // Lines
254
- const lines = this._linesGroup.selectAll(`.${lineGroup}`)
255
- .data(lineDataPrepared, (d) => d._id);
256
- const linesEnter = lines.enter().append('g')
257
- .attr('class', lineGroup)
258
- .style('opacity', 0)
259
- .attr('transform', (d, i) => {
260
- var _a, _b;
261
- const configuredPos = isFunction(config.animationLineEnterPosition)
262
- ? config.animationLineEnterPosition(d, i, lineDataPrepared)
263
- : config.animationLineEnterPosition;
264
- const [x, y] = [(_a = configuredPos === null || configuredPos === void 0 ? void 0 : configuredPos[0]) !== null && _a !== void 0 ? _a : d._xPx, (_b = configuredPos === null || configuredPos === void 0 ? void 0 : configuredPos[1]) !== null && _b !== void 0 ? _b : d._yPx];
265
- return `translate(${x}, ${y})`;
129
+ const lines = this._linesGroup.selectAll(`.${line}`)
130
+ .data(data, (d, i) => {
131
+ var _a;
132
+ return (_a = getString(d, config.id, i)) !== null && _a !== void 0 ? _a : [
133
+ this._getRecordType(d, i), getNumber(d, config.x, i),
134
+ ].join('-');
266
135
  });
267
- linesEnter.append('rect')
136
+ const linesEnter = lines.enter().append('rect')
268
137
  .attr('class', line)
269
- .style('fill', (d, i) => getColor(d, config.color, yOrdinalScale(this._getRecordKey(d, i))))
270
- .call(this._renderLines.bind(this), rowHeight);
271
- linesEnter.append('use').attr('class', lineStartIcon);
272
- linesEnter.append('use').attr('class', lineEndIcon);
273
- const linesMerged = linesEnter.merge(lines);
274
- smartTransition(linesMerged, duration)
275
- .attr('transform', d => `translate(${d._xPx + d._xOffsetPx}, ${d._yPx})`)
276
- .style('opacity', 1);
277
- const lineRectElementsSelection = linesMerged.selectAll(`.${line}`)
278
- .data(d => [d]);
279
- smartTransition(lineRectElementsSelection, duration)
280
- .style('fill', (d, i) => getColor(d, config.color, yOrdinalScale(this._getRecordKey(d, i))))
281
- .style('cursor', (d, i) => { var _a; return getString(d, (_a = config.lineCursor) !== null && _a !== void 0 ? _a : config.cursor, i); })
282
- .call(this._renderLines.bind(this), rowHeight);
283
- linesMerged.selectAll(`.${lineStartIcon}`)
284
- .data(d => [d])
285
- .attr('href', (d, i) => getString(d, config.lineStartIcon, i))
286
- .attr('x', (d, i) => {
287
- const iconSize = d._startIconSize;
288
- const iconArrangement = d._startIconArrangement;
289
- const offset = iconArrangement === Arrangement.Inside ? 0
290
- : iconArrangement === Arrangement.Center ? -iconSize / 2
291
- : -iconSize;
292
- return offset;
293
- })
294
- .attr('y', d => (-(d._startIconSize - d._height) / 2) || 0)
295
- .attr('width', d => d._startIconSize)
296
- .attr('height', d => d._startIconSize)
297
- .style('color', d => d._startIconColor);
298
- linesMerged.selectAll(`.${lineEndIcon}`)
299
- .data(d => [d])
300
- .attr('href', (d, i) => getString(d, config.lineEndIcon, i))
301
- .attr('x', (d, i) => {
302
- const lineLength = d._lengthCorrected;
303
- const iconSize = d._endIconSize;
304
- const iconArrangement = d._endIconArrangement;
305
- const offset = iconArrangement === Arrangement.Inside ? -iconSize
306
- : iconArrangement === Arrangement.Center ? -iconSize / 2
307
- : 0;
308
- return lineLength + offset;
309
- })
310
- .attr('y', d => -((d._endIconSize - d._height) / 2) || 0)
311
- .attr('width', d => d._endIconSize)
312
- .attr('height', d => d._endIconSize)
313
- .style('color', d => d._endIconColor);
314
- const linesExit = lines.exit();
315
- smartTransition(linesExit, duration)
316
- .style('opacity', 0)
317
- .attr('transform', (d, i) => {
318
- var _a, _b;
319
- const configuredPos = isFunction(config.animationLineExitPosition)
320
- ? config.animationLineExitPosition(d, i, lineDataPrepared)
321
- : config.animationLineExitPosition;
322
- const [x, y] = [(_a = configuredPos === null || configuredPos === void 0 ? void 0 : configuredPos[0]) !== null && _a !== void 0 ? _a : d._xPx, (_b = configuredPos === null || configuredPos === void 0 ? void 0 : configuredPos[1]) !== null && _b !== void 0 ? _b : d._yPx];
323
- return `translate(${x}, ${y})`;
324
- })
325
- .remove();
326
- // Arrows
327
- const arrowsData = this._prepareArrowsData(data, yOrdinalScale, rowHeight);
328
- const arrows = this._arrowsGroup.selectAll(`.${arrow}`)
329
- .data(arrowsData !== null && arrowsData !== void 0 ? arrowsData : [], d => d.id);
330
- const arrowsEnter = arrows.enter().append('path')
331
- .attr('class', arrow)
138
+ .classed(rowOdd, config.alternatingRowColors
139
+ ? (d, i) => !(recordLabelsUnique.indexOf(this._getRecordType(d, i)) % 2)
140
+ : null)
141
+ .style('fill', (d, i) => getColor(d, config.color, ordinalScale(this._getRecordType(d, i))))
142
+ .call(this._positionLines.bind(this), ordinalScale)
143
+ .attr('transform', 'translate(0, 10)')
332
144
  .style('opacity', 0);
333
- smartTransition(arrowsEnter.merge(arrows), duration)
334
- .attr('d', (d) => {
335
- var _a, _b;
336
- return arrowPolylinePath(d._points, (_a = d.arrowHeadLength) !== null && _a !== void 0 ? _a : TIMELINE_DEFAULT_ARROW_HEAD_LENGTH, (_b = d.arrowHeadWidth) !== null && _b !== void 0 ? _b : TIMELINE_DEFAULT_ARROW_HEAD_WIDTH);
337
- })
145
+ const linesMerged = linesEnter.merge(lines)
146
+ .style('fill', (d, i) => getColor(d, config.color, ordinalScale(this._getRecordType(d, i))))
147
+ .style('cursor', (d, i) => getString(d, config.cursor, i))
148
+ .call(this._positionLines.bind(this), ordinalScale);
149
+ smartTransition(linesMerged, duration)
150
+ .attr('transform', 'translate(0, 0)')
338
151
  .style('opacity', 1);
339
- smartTransition(arrows.exit(), duration)
152
+ smartTransition(lines.exit(), duration)
340
153
  .style('opacity', 0)
341
154
  .remove();
342
155
  // Scroll Bar
@@ -358,119 +171,32 @@ class Timeline extends XYComponentCore {
358
171
  .attr('rx', this._scrollBarWidth / 2)
359
172
  .attr('ry', this._scrollBarWidth / 2);
360
173
  this._updateScrollPosition(0);
361
- // Clip path
362
- const clipPathRect = this._clipPath.select('rect');
363
- smartTransition(clipPathRect, clipPathRect.attr('width') ? duration : 0)
364
- .attr('x', xStart)
365
- .attr('width', timelineWidth)
366
- .attr('height', this._height);
367
- }
368
- _getLineLength(d, i) {
369
- var _a, _b;
370
- const { config, xScale } = this;
371
- const x = getNumber(d, config.x, i);
372
- const length = (_b = getNumber(d, (_a = config.lineDuration) !== null && _a !== void 0 ? _a : config.length, i)) !== null && _b !== void 0 ? _b : 0;
373
- const lineLength = xScale(x + length) - xScale(x);
374
- return lineLength;
375
174
  }
376
- _getLineWidth(d, i, rowHeight) {
377
- var _a;
378
- const { config } = this;
379
- return (_a = getNumber(d, config.lineWidth, i)) !== null && _a !== void 0 ? _a : Math.max(Math.floor(rowHeight / 2), 1);
380
- }
381
- _getLineDuration(d, i) {
382
- var _a, _b;
383
- const { config } = this;
384
- return (_b = getNumber(d, (_a = config.lineDuration) !== null && _a !== void 0 ? _a : config.length, i)) !== null && _b !== void 0 ? _b : 0;
385
- }
386
- _prepareLinesData(data, rowOrdinalScale, rowHeight) {
175
+ _positionLines(selection, ordinalScale) {
387
176
  const { config, xScale, yScale } = this;
388
177
  const yRange = yScale.range();
389
178
  const yStart = Math.min(...yRange);
390
- return data.map((d, i) => {
391
- var _a, _b, _c, _d, _e;
392
- const id = (_a = getString(d, config.id, i)) !== null && _a !== void 0 ? _a : [
393
- this._getRecordKey(d, i), getNumber(d, config.x, i),
394
- ].join('-');
395
- const lineWidth = this._getLineWidth(d, i, rowHeight);
396
- const lineLength = this._getLineLength(d, i);
397
- if (lineLength < 0) {
179
+ selection.each((d, i, elements) => {
180
+ var _a;
181
+ const x = getNumber(d, config.x, i);
182
+ const y = ordinalScale(this._getRecordType(d, i)) * config.rowHeight;
183
+ const length = (_a = getNumber(d, config.length, i)) !== null && _a !== void 0 ? _a : 0;
184
+ // Rect dimensions
185
+ const height = getNumber(d, config.lineWidth, i);
186
+ const width = xScale(x + length) - xScale(x);
187
+ if (width < 0) {
398
188
  console.warn('Unovis | Timeline: Line segments should not have negative lengths. Setting to 0.');
399
189
  }
400
- const isLineTooShort = config.showEmptySegments && config.lineCap && (lineLength < lineWidth);
401
- const lineLengthCorrected = config.showEmptySegments
402
- ? Math.max(config.lineCap ? lineWidth : 1, lineLength)
403
- : Math.max(0, lineLength);
404
- const x = xScale(getNumber(d, config.x, i));
405
- const y = yStart + rowOrdinalScale(this._getRecordKey(d, i)) * rowHeight + (rowHeight - lineWidth) / 2;
406
- const xOffset = isLineTooShort ? -(lineLengthCorrected - lineLength) / 2 : 0;
407
- return Object.assign(Object.assign({}, d), { _id: id, _xPx: x, _yPx: y, _xOffsetPx: xOffset, _length: lineLength, _height: lineWidth, _lengthCorrected: lineLengthCorrected, _startIconSize: (_b = getNumber(d, config.lineStartIconSize, i)) !== null && _b !== void 0 ? _b : lineWidth, _endIconSize: (_c = getNumber(d, config.lineEndIconSize, i)) !== null && _c !== void 0 ? _c : lineWidth, _startIconColor: getString(d, config.lineStartIconColor, i), _endIconColor: getString(d, config.lineEndIconColor, i), _startIconArrangement: (_d = getValue(d, config.lineStartIconArrangement, i)) !== null && _d !== void 0 ? _d : Arrangement.Outside, _endIconArrangement: (_e = getValue(d, config.lineEndIconArrangement, i)) !== null && _e !== void 0 ? _e : Arrangement.Outside });
190
+ select(elements[i])
191
+ .attr('x', xScale(x))
192
+ .attr('y', yStart + y + (config.rowHeight - height) / 2)
193
+ .attr('width', config.showEmptySegments
194
+ ? Math.max(config.lineCap ? height : 1, width)
195
+ : Math.max(0, width))
196
+ .attr('height', height)
197
+ .attr('rx', config.lineCap ? height / 2 : null);
408
198
  });
409
199
  }
410
- _prepareArrowsData(data, rowOrdinalScale, rowHeight) {
411
- var _a;
412
- const { config } = this;
413
- const arrowsData = (_a = config.arrows) === null || _a === void 0 ? void 0 : _a.map(a => {
414
- var _a, _b, _c, _d, _e;
415
- const sourceLineIndex = data.findIndex((d, i) => getString(d, config.id, i) === a.lineSourceId);
416
- const targetLineIndex = data.findIndex((d, i) => getString(d, config.id, i) === a.lineTargetId);
417
- const sourceLine = data[sourceLineIndex];
418
- const targetLine = data[targetLineIndex];
419
- if (!sourceLine || !targetLine) {
420
- console.warn('Unovis | Timeline: Arrow references a non-existent line. Skipping...', a);
421
- return undefined;
422
- }
423
- const sourceLineY = rowOrdinalScale(this._getRecordKey(sourceLine, sourceLineIndex)) * rowHeight + rowHeight / 2;
424
- const targetLineY = rowOrdinalScale(this._getRecordKey(targetLine, targetLineIndex)) * rowHeight + rowHeight / 2;
425
- const sourceLineWidth = this._getLineWidth(sourceLine, sourceLineIndex, rowHeight);
426
- const targetLineWidth = this._getLineWidth(targetLine, targetLineIndex, rowHeight);
427
- const x1 = (a.xSource
428
- ? this.xScale(a.xSource)
429
- : this.xScale(getNumber(sourceLine, config.x, sourceLineIndex)) + this._getLineLength(sourceLine, sourceLineIndex)) + ((_a = a.xSourceOffsetPx) !== null && _a !== void 0 ? _a : 0);
430
- const targetLineLength = this._getLineLength(targetLine, targetLineIndex);
431
- const isTargetLineTooShort = config.showEmptySegments && config.lineCap && (targetLineLength < targetLineWidth);
432
- const targetLineStart = this.xScale(getNumber(targetLine, config.x, targetLineIndex)) + (isTargetLineTooShort ? -targetLineWidth / 2 : 0);
433
- const x2 = (a.xTarget ? this.xScale(a.xTarget) : targetLineStart) + ((_b = a.xTargetOffsetPx) !== null && _b !== void 0 ? _b : 0);
434
- const isX2OutsideTargetLineStart = (x2 < targetLineStart) || (x2 > targetLineStart);
435
- // Points array
436
- const sourceMargin = (_c = a.lineSourceMarginPx) !== null && _c !== void 0 ? _c : TIMELINE_DEFAULT_ARROW_MARGIN;
437
- const targetMargin = (_d = a.lineTargetMarginPx) !== null && _d !== void 0 ? _d : TIMELINE_DEFAULT_ARROW_MARGIN;
438
- const y1 = sourceLineY < targetLineY ? sourceLineY + sourceLineWidth / 2 + sourceMargin : sourceLineY - sourceLineWidth / 2 - sourceMargin;
439
- const y2 = sourceLineY < targetLineY ? targetLineY - targetLineWidth / 2 - targetMargin : targetLineY + targetLineWidth / 2 + targetMargin;
440
- const arrowHeadLength = (_e = a.arrowHeadLength) !== null && _e !== void 0 ? _e : TIMELINE_DEFAULT_ARROW_HEAD_LENGTH;
441
- const isForwardArrow = x1 < x2 && !isX2OutsideTargetLineStart;
442
- const threshold = arrowHeadLength + (isForwardArrow ? targetMargin : 0);
443
- const points = [[x1, y1]];
444
- if (Math.abs(x2 - x1) > threshold) {
445
- if (isForwardArrow) {
446
- points.push([x1, (y1 + targetLineY) / 2]); // A dummy point to enable smooth transitions when arrows change
447
- points.push([x1, targetLineY]);
448
- points.push([x2 - targetMargin, targetLineY]);
449
- }
450
- else {
451
- const verticalOffset = Math.sign(targetLineY - sourceLineY) * (rowHeight / 4);
452
- points.push([x1, y2 - verticalOffset]);
453
- points.push([x2, y2 - verticalOffset]);
454
- points.push([x2, y2]);
455
- }
456
- }
457
- else {
458
- const quarterOffset = (y2 - y1) / 4;
459
- points.push([x1, y1 + quarterOffset]); // A dummy point to enable smooth transitions
460
- points.push([x1, y1 + 3 * quarterOffset]); // A dummy point to enable smooth transitions
461
- points.push([x1, y2]);
462
- }
463
- return Object.assign(Object.assign({}, a), { _points: points });
464
- }).filter(Boolean);
465
- return arrowsData;
466
- }
467
- _renderLines(selection) {
468
- const { config } = this;
469
- selection
470
- .attr('width', d => d._lengthCorrected)
471
- .attr('height', d => d._height)
472
- .attr('rx', d => config.lineCap ? d._height / 2 : null);
473
- }
474
200
  _onScrollbarDrag(event) {
475
201
  const yRange = this.yScale.range();
476
202
  const yHeight = Math.abs(yRange[1] - yRange[0]);
@@ -493,40 +219,28 @@ class Timeline extends XYComponentCore {
493
219
  this._scrollDistance += diff;
494
220
  this._scrollDistance = Math.max(0, this._scrollDistance);
495
221
  this._scrollDistance = Math.min(this._maxScroll, this._scrollDistance);
496
- this._clipPath.attr('transform', `translate(0,${this._scrollDistance})`);
497
222
  this._linesGroup.attr('transform', `translate(0,${-this._scrollDistance})`);
498
223
  this._rowsGroup.attr('transform', `translate(0,${-this._scrollDistance})`);
499
224
  this._labelsGroup.attr('transform', `translate(0,${-this._scrollDistance})`);
500
- this._rowIconsGroup.attr('transform', `translate(0,${-this._scrollDistance})`);
501
- this._arrowsGroup.attr('transform', `translate(0,${-this._scrollDistance})`);
502
225
  const scrollBarPosition = (this._scrollDistance / this._maxScroll * (yHeight - this._scrollbarHeight)) || 0;
503
226
  this._scrollBarHandle.attr('y', scrollBarPosition);
504
227
  }
505
- _getRecordKey(d, i) {
228
+ _getMaxLineWidth() {
506
229
  var _a;
507
- return getString(d, (_a = this.config.lineRow) !== null && _a !== void 0 ? _a : this.config.type) || `__${i}`;
230
+ const { config, datamodel: { data } } = this;
231
+ return (_a = max(data, (d, i) => getNumber(d, config.lineWidth, i))) !== null && _a !== void 0 ? _a : 0;
508
232
  }
509
- _getRowLabels(data) {
510
- const grouped = groupBy(data, (d, i) => { var _a; return getString(d, (_a = this.config.lineRow) !== null && _a !== void 0 ? _a : this.config.type) || `${i + 1}`; });
511
- const rowLabels = Object.entries(grouped).map(([key, items], i) => {
512
- var _a, _b, _c, _d, _e;
513
- const icon = (_b = (_a = this.config).rowIcon) === null || _b === void 0 ? void 0 : _b.call(_a, key, items, i);
514
- return {
515
- label: key,
516
- formattedLabel: (_e = (_d = (_c = this.config).rowLabelFormatter) === null || _d === void 0 ? void 0 : _d.call(_c, key, items, i)) !== null && _e !== void 0 ? _e : key,
517
- iconHref: icon === null || icon === void 0 ? void 0 : icon.href,
518
- iconSize: icon === null || icon === void 0 ? void 0 : icon.size,
519
- iconColor: icon === null || icon === void 0 ? void 0 : icon.color,
520
- data: items,
521
- };
522
- });
523
- return rowLabels;
233
+ _getRecordType(d, i) {
234
+ return getString(d, this.config.type) || `__${i}`;
235
+ }
236
+ _getRecordLabels(data) {
237
+ return data.map((d, i) => getString(d, this.config.type) || `${i + 1}`);
524
238
  }
525
239
  // Override the default XYComponent getXDataExtent method to take into account line lengths
526
240
  getXDataExtent() {
527
241
  const { config, datamodel } = this;
528
242
  const min = getMin(datamodel.data, config.x);
529
- const max = getMax(datamodel.data, (d, i) => { var _a, _b; return getNumber(d, config.x, i) + ((_b = getNumber(d, (_a = config.lineDuration) !== null && _a !== void 0 ? _a : config.length, i)) !== null && _b !== void 0 ? _b : 0); });
243
+ const max = getMax(datamodel.data, (d, i) => { var _a; return getNumber(d, config.x, i) + ((_a = getNumber(d, config.length, i)) !== null && _a !== void 0 ? _a : 0); });
530
244
  return [min, max];
531
245
  }
532
246
  }