@itwin/imodel-components-react 4.10.0-dev.1 → 4.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.
Files changed (96) hide show
  1. package/CHANGELOG.md +9 -1
  2. package/lib/cjs/imodel-components-react/color/AlphaSlider.d.ts.map +1 -1
  3. package/lib/cjs/imodel-components-react/color/AlphaSlider.js +8 -6
  4. package/lib/cjs/imodel-components-react/color/AlphaSlider.js.map +1 -1
  5. package/lib/cjs/imodel-components-react/color/HueSlider.d.ts.map +1 -1
  6. package/lib/cjs/imodel-components-react/color/HueSlider.js +8 -6
  7. package/lib/cjs/imodel-components-react/color/HueSlider.js.map +1 -1
  8. package/lib/cjs/imodel-components-react/color/SaturationPicker.js +8 -8
  9. package/lib/cjs/imodel-components-react/color/SaturationPicker.js.map +1 -1
  10. package/lib/cjs/imodel-components-react/editors/ColorEditor.js +1 -1
  11. package/lib/cjs/imodel-components-react/editors/ColorEditor.js.map +1 -1
  12. package/lib/cjs/imodel-components-react/editors/WeightEditor.d.ts.map +1 -1
  13. package/lib/cjs/imodel-components-react/editors/WeightEditor.js.map +1 -1
  14. package/lib/cjs/imodel-components-react/inputs/QuantityNumberInput.js +4 -4
  15. package/lib/cjs/imodel-components-react/inputs/QuantityNumberInput.js.map +1 -1
  16. package/lib/cjs/imodel-components-react/lineweight/WeightPickerButton.js +3 -3
  17. package/lib/cjs/imodel-components-react/lineweight/WeightPickerButton.js.map +1 -1
  18. package/lib/cjs/imodel-components-react/navigationaids/DrawingNavigationAid.d.ts.map +1 -1
  19. package/lib/cjs/imodel-components-react/navigationaids/DrawingNavigationAid.js +2 -1
  20. package/lib/cjs/imodel-components-react/navigationaids/DrawingNavigationAid.js.map +1 -1
  21. package/lib/cjs/imodel-components-react/quantityformat/FormatSample.js +1 -1
  22. package/lib/cjs/imodel-components-react/quantityformat/FormatSample.js.map +1 -1
  23. package/lib/cjs/imodel-components-react/quantityformat/MiscFormatOptions.js +1 -1
  24. package/lib/cjs/imodel-components-react/quantityformat/MiscFormatOptions.js.map +1 -1
  25. package/lib/cjs/imodel-components-react/timeline/InlineEdit.js +2 -2
  26. package/lib/cjs/imodel-components-react/timeline/InlineEdit.js.map +1 -1
  27. package/lib/cjs/imodel-components-react/timeline/PlayButton.d.ts +24 -0
  28. package/lib/cjs/imodel-components-react/timeline/PlayButton.d.ts.map +1 -0
  29. package/lib/cjs/imodel-components-react/timeline/{PlayerButton.js → PlayButton.js} +5 -25
  30. package/lib/cjs/imodel-components-react/timeline/PlayButton.js.map +1 -0
  31. package/lib/cjs/imodel-components-react/timeline/{PlayerButton.scss → PlayButton.scss} +1 -1
  32. package/lib/cjs/imodel-components-react/timeline/Scrubber.d.ts +0 -1
  33. package/lib/cjs/imodel-components-react/timeline/Scrubber.d.ts.map +1 -1
  34. package/lib/cjs/imodel-components-react/timeline/Scrubber.js.map +1 -1
  35. package/lib/cjs/imodel-components-react/timeline/SolarTimeline.js +2 -2
  36. package/lib/cjs/imodel-components-react/timeline/SolarTimeline.js.map +1 -1
  37. package/lib/cjs/imodel-components-react/timeline/TimelineComponent.d.ts +28 -66
  38. package/lib/cjs/imodel-components-react/timeline/TimelineComponent.d.ts.map +1 -1
  39. package/lib/cjs/imodel-components-react/timeline/TimelineComponent.js +216 -329
  40. package/lib/cjs/imodel-components-react/timeline/TimelineComponent.js.map +1 -1
  41. package/lib/cjs/imodel-components-react/timeline/TimelineComponent.scss +27 -140
  42. package/lib/cjs/imodel-components-react.d.ts +0 -1
  43. package/lib/cjs/imodel-components-react.d.ts.map +1 -1
  44. package/lib/cjs/imodel-components-react.js +0 -1
  45. package/lib/cjs/imodel-components-react.js.map +1 -1
  46. package/lib/esm/imodel-components-react/color/AlphaSlider.d.ts.map +1 -1
  47. package/lib/esm/imodel-components-react/color/AlphaSlider.js +8 -6
  48. package/lib/esm/imodel-components-react/color/AlphaSlider.js.map +1 -1
  49. package/lib/esm/imodel-components-react/color/HueSlider.d.ts.map +1 -1
  50. package/lib/esm/imodel-components-react/color/HueSlider.js +8 -6
  51. package/lib/esm/imodel-components-react/color/HueSlider.js.map +1 -1
  52. package/lib/esm/imodel-components-react/color/SaturationPicker.js +8 -8
  53. package/lib/esm/imodel-components-react/color/SaturationPicker.js.map +1 -1
  54. package/lib/esm/imodel-components-react/editors/ColorEditor.js +1 -1
  55. package/lib/esm/imodel-components-react/editors/ColorEditor.js.map +1 -1
  56. package/lib/esm/imodel-components-react/editors/WeightEditor.d.ts.map +1 -1
  57. package/lib/esm/imodel-components-react/editors/WeightEditor.js.map +1 -1
  58. package/lib/esm/imodel-components-react/inputs/QuantityNumberInput.js +4 -4
  59. package/lib/esm/imodel-components-react/inputs/QuantityNumberInput.js.map +1 -1
  60. package/lib/esm/imodel-components-react/lineweight/WeightPickerButton.js +3 -3
  61. package/lib/esm/imodel-components-react/lineweight/WeightPickerButton.js.map +1 -1
  62. package/lib/esm/imodel-components-react/navigationaids/DrawingNavigationAid.d.ts.map +1 -1
  63. package/lib/esm/imodel-components-react/navigationaids/DrawingNavigationAid.js +2 -1
  64. package/lib/esm/imodel-components-react/navigationaids/DrawingNavigationAid.js.map +1 -1
  65. package/lib/esm/imodel-components-react/quantityformat/FormatSample.js +1 -1
  66. package/lib/esm/imodel-components-react/quantityformat/FormatSample.js.map +1 -1
  67. package/lib/esm/imodel-components-react/quantityformat/MiscFormatOptions.js +1 -1
  68. package/lib/esm/imodel-components-react/quantityformat/MiscFormatOptions.js.map +1 -1
  69. package/lib/esm/imodel-components-react/timeline/InlineEdit.js +2 -2
  70. package/lib/esm/imodel-components-react/timeline/InlineEdit.js.map +1 -1
  71. package/lib/esm/imodel-components-react/timeline/PlayButton.d.ts +24 -0
  72. package/lib/esm/imodel-components-react/timeline/PlayButton.d.ts.map +1 -0
  73. package/lib/esm/imodel-components-react/timeline/{PlayerButton.js → PlayButton.js} +4 -23
  74. package/lib/esm/imodel-components-react/timeline/PlayButton.js.map +1 -0
  75. package/lib/esm/imodel-components-react/timeline/{PlayerButton.scss → PlayButton.scss} +1 -1
  76. package/lib/esm/imodel-components-react/timeline/Scrubber.d.ts +0 -1
  77. package/lib/esm/imodel-components-react/timeline/Scrubber.d.ts.map +1 -1
  78. package/lib/esm/imodel-components-react/timeline/Scrubber.js.map +1 -1
  79. package/lib/esm/imodel-components-react/timeline/SolarTimeline.js +1 -1
  80. package/lib/esm/imodel-components-react/timeline/SolarTimeline.js.map +1 -1
  81. package/lib/esm/imodel-components-react/timeline/TimelineComponent.d.ts +28 -66
  82. package/lib/esm/imodel-components-react/timeline/TimelineComponent.d.ts.map +1 -1
  83. package/lib/esm/imodel-components-react/timeline/TimelineComponent.js +217 -327
  84. package/lib/esm/imodel-components-react/timeline/TimelineComponent.js.map +1 -1
  85. package/lib/esm/imodel-components-react/timeline/TimelineComponent.scss +27 -140
  86. package/lib/esm/imodel-components-react.d.ts +0 -1
  87. package/lib/esm/imodel-components-react.d.ts.map +1 -1
  88. package/lib/esm/imodel-components-react.js +0 -1
  89. package/lib/esm/imodel-components-react.js.map +1 -1
  90. package/package.json +16 -16
  91. package/lib/cjs/imodel-components-react/timeline/PlayerButton.d.ts +0 -34
  92. package/lib/cjs/imodel-components-react/timeline/PlayerButton.d.ts.map +0 -1
  93. package/lib/cjs/imodel-components-react/timeline/PlayerButton.js.map +0 -1
  94. package/lib/esm/imodel-components-react/timeline/PlayerButton.d.ts +0 -34
  95. package/lib/esm/imodel-components-react/timeline/PlayerButton.d.ts.map +0 -1
  96. package/lib/esm/imodel-components-react/timeline/PlayerButton.js.map +0 -1
@@ -1,361 +1,251 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3
+ * See LICENSE.md in the project root for license terms and full copyright notice.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ /** @packageDocumentation
6
+ * @module Timeline
7
+ */
8
+ import "./TimelineComponent.scss";
9
+ import * as React from "react";
1
10
  import { UiAdmin } from "@itwin/appui-abstract";
2
11
  import { toDateString, toTimeString, UiComponents, } from "@itwin/components-react";
3
- import { Icon } from "@itwin/core-react";
4
- import { SvgCaretLeft, SvgCaretRight, SvgCheckmark, SvgMoreVertical, SvgPlayCircular, } from "@itwin/itwinui-icons-react";
5
- import { DropdownMenu, MenuDivider, MenuItem } from "@itwin/itwinui-react";
6
- import classnames from "classnames";
7
- import * as React from "react";
12
+ import { SvgCheckmark, SvgMoreVertical } from "@itwin/itwinui-icons-react";
13
+ import { DropdownMenu, IconButton, MenuDivider, MenuItem, } from "@itwin/itwinui-react";
8
14
  import { UiIModelComponents } from "../UiIModelComponents";
9
15
  import { InlineEdit } from "./InlineEdit";
10
16
  import { TimelinePausePlayAction } from "./interfaces";
11
- import { PlayButton, PlayerButton } from "./PlayerButton";
17
+ import { PlayButton } from "./PlayButton";
12
18
  import { Scrubber } from "./Scrubber";
13
- import "./TimelineComponent.scss";
14
- // cspell:ignore millisec
15
19
  const slowSpeed = 60 * 1000;
16
20
  const mediumSpeed = 20 * 1000;
17
21
  const fastSpeed = 10 * 1000;
18
- /**
19
- * [[TimelineComponent]] is used to playback timeline data
22
+ /** [[TimelineComponent]] is used to playback timeline data
20
23
  * @public
21
24
  */
22
- export class TimelineComponent extends React.Component {
23
- constructor(props) {
24
- super(props);
25
- this._timeLastCycle = 0;
26
- this._requestFrame = 0;
27
- this._unmounted = false;
28
- this._standardTimelineMenuItems = [
29
- {
30
- label: UiIModelComponents.translate("timeline.slow"),
31
- timelineDuration: slowSpeed,
32
- },
33
- {
34
- label: UiIModelComponents.translate("timeline.medium"),
35
- timelineDuration: mediumSpeed,
36
- },
37
- {
38
- label: UiIModelComponents.translate("timeline.fast"),
39
- timelineDuration: fastSpeed,
40
- },
41
- ];
42
- this._handleTimelinePausePlayEvent = (args) => {
25
+ export function TimelineComponent(props) {
26
+ const { appMenuItemOption = "replace", startDate, endDate, showDuration, timeZoneOffset, dateFormatOptions, timeFormatOptions, includeRepeat = true, onSettingsChange, onChange, onPlayPause, } = props;
27
+ const [isPlaying, setIsPlaying] = React.useState(false);
28
+ const [currentDuration, setCurrentDuration] = React.useState(props.initialDuration ?? 0);
29
+ const [totalDuration, setTotalDuration] = React.useState(props.totalDuration);
30
+ const [repeat, setRepeat] = React.useState(props.repeat ?? false);
31
+ const playOrReplay = React.useCallback(() => {
32
+ if (isPlaying)
33
+ return;
34
+ if (currentDuration >= totalDuration) {
35
+ setCurrentDuration(0);
36
+ }
37
+ setIsPlaying(true);
38
+ onPlayPause?.(true);
39
+ }, [isPlaying, onPlayPause, currentDuration, totalDuration]);
40
+ const pause = React.useCallback(() => {
41
+ if (!isPlaying)
42
+ return;
43
+ setIsPlaying(false);
44
+ onPlayPause?.(false);
45
+ }, [isPlaying, onPlayPause]);
46
+ const updateTotalDuration = (newTotalDuration) => {
47
+ if (newTotalDuration === totalDuration)
48
+ return;
49
+ const fraction = currentDuration / totalDuration;
50
+ const newCurrentDuration = fraction * newTotalDuration;
51
+ setTotalDuration(newTotalDuration);
52
+ setCurrentDuration(newCurrentDuration);
53
+ onSettingsChange?.({ duration: newTotalDuration });
54
+ };
55
+ const updateDuration = (newDuration, currentTotalDuration = totalDuration) => {
56
+ newDuration = Math.max(newDuration, 0);
57
+ newDuration = Math.min(newDuration, currentTotalDuration);
58
+ if (newDuration === currentDuration)
59
+ return;
60
+ setCurrentDuration(newDuration);
61
+ const fraction = newDuration / currentTotalDuration;
62
+ onChange?.(fraction);
63
+ };
64
+ const updateRepeat = (newRepeat) => {
65
+ if (newRepeat === repeat)
66
+ return;
67
+ setRepeat(newRepeat);
68
+ onSettingsChange?.({ loop: newRepeat });
69
+ };
70
+ const [prevTotalDuration, setPrevTotalDuration] = React.useState(props.totalDuration);
71
+ if (prevTotalDuration !== props.totalDuration) {
72
+ setPrevTotalDuration(props.totalDuration);
73
+ updateTotalDuration(props.totalDuration);
74
+ }
75
+ const [prevInitialDuration, setPrevInitialDuration] = React.useState(props.initialDuration);
76
+ if (prevInitialDuration !== props.initialDuration) {
77
+ setPrevInitialDuration(props.initialDuration);
78
+ updateDuration(props.initialDuration ?? 0, props.totalDuration);
79
+ }
80
+ const [prevRepeat, setPrevRepeat] = React.useState(props.repeat);
81
+ if (prevRepeat !== props.repeat) {
82
+ setPrevRepeat(props.repeat);
83
+ updateRepeat(props.repeat ?? repeat);
84
+ }
85
+ React.useEffect(() => {
86
+ if (!props.componentId)
87
+ return;
88
+ return UiAdmin.onGenericUiEvent.addListener((args) => {
43
89
  const timelineArgs = args;
44
- // istanbul ignore else
45
90
  if (!timelineArgs ||
46
- this.props.componentId !== timelineArgs.uiComponentId ||
91
+ props.componentId !== timelineArgs.uiComponentId ||
47
92
  timelineArgs.timelineAction === undefined)
48
93
  return;
49
- let startPlaying;
50
94
  switch (timelineArgs.timelineAction) {
51
95
  case TimelinePausePlayAction.Play:
52
- startPlaying = true;
96
+ playOrReplay();
53
97
  break;
54
98
  case TimelinePausePlayAction.Pause:
55
- startPlaying = false;
99
+ pause();
56
100
  break;
57
101
  case TimelinePausePlayAction.Toggle:
58
- startPlaying = !this.state.isPlaying;
102
+ if (isPlaying) {
103
+ pause();
104
+ }
105
+ else {
106
+ playOrReplay();
107
+ }
59
108
  break;
60
- // istanbul ignore next
61
- default:
62
- return; // throw error?
63
- }
64
- if (startPlaying)
65
- this._onPlay();
66
- else
67
- this._onPause();
68
- };
69
- // user clicked backward button
70
- this._onBackward = () => {
71
- // istanbul ignore else
72
- if (this.props.onJump)
73
- this.props.onJump(false);
74
- };
75
- // user clicked forward button
76
- this._onForward = () => {
77
- // istanbul ignore else
78
- if (this.props.onJump)
79
- this.props.onJump(true);
80
- };
81
- // user clicked play button
82
- this._onPlay = () => {
83
- // istanbul ignore if
84
- if (this.state.isPlaying)
85
- return;
86
- // timeline was complete, restart from the beginning
87
- if (this.state.currentDuration >= this.state.totalDuration) {
88
- this._replay();
89
- }
90
- else
91
- this._play();
92
- // istanbul ignore else
93
- if (this.props.onPlayPause)
94
- this.props.onPlayPause(true);
95
- };
96
- // user clicked pause button
97
- this._onPause = () => {
98
- // istanbul ignore if
99
- if (!this.state.isPlaying)
100
- return;
101
- // stop requesting frames
102
- window.cancelAnimationFrame(this._requestFrame);
103
- // stop playing
104
- this.setState({ isPlaying: false });
105
- // istanbul ignore else
106
- if (this.props.onPlayPause)
107
- this.props.onPlayPause(false);
108
- };
109
- // user clicked on timeline rail or dragged scrubber handle
110
- this._onTimelineChange = (values) => {
111
- const currentDuration = values[0];
112
- this._setDuration(currentDuration);
113
- };
114
- // set the current duration, which will call the OnChange callback
115
- this._setDuration = (currentDuration, updatedTotal) => {
116
- const actualDuration = Math.min(updatedTotal ?? this.state.totalDuration, Math.max(currentDuration, 0));
117
- this._timeLastCycle = new Date().getTime();
118
- // istanbul ignore else
119
- if (!this._unmounted)
120
- this.setState({ currentDuration: actualDuration });
121
- // istanbul ignore else
122
- if (this.props.onChange) {
123
- const fraction = actualDuration / (updatedTotal ?? this.state.totalDuration);
124
- this.props.onChange(fraction);
125
- }
126
- };
127
- // recursively update the animation until we hit the end or the pause button is clicked
128
- this._updateAnimation = (_timestamp) => {
129
- // istanbul ignore else
130
- if (!this.state.isPlaying && !this._unmounted) {
131
- return;
132
109
  }
133
- // update duration
134
- const currentTime = new Date().getTime();
135
- const millisecondsElapsed = currentTime - this._timeLastCycle;
136
- const duration = this.state.currentDuration + millisecondsElapsed;
137
- this._setDuration(duration);
138
- // stop the animation!
139
- if (duration >= this.state.totalDuration) {
140
- window.cancelAnimationFrame(this._requestFrame);
141
- this.setState({ isPlaying: false });
142
- if (this.state.repeat)
143
- this._replay();
144
- else {
145
- // istanbul ignore else
146
- if (this.props.onPlayPause)
147
- this.props.onPlayPause(false);
148
- }
110
+ });
111
+ }, [isPlaying, pause, playOrReplay, props.componentId]);
112
+ useAnimation(({ delta }) => {
113
+ const duration = currentDuration + delta;
114
+ updateDuration(duration);
115
+ if (duration >= totalDuration) {
116
+ if (repeat) {
117
+ setCurrentDuration(0);
149
118
  return;
150
119
  }
151
- // continue the animation - request the next frame
152
- this._requestFrame = window.requestAnimationFrame(this._updateAnimation);
153
- };
154
- this._replay = () => {
155
- // timeline was complete, restart from the beginning
156
- this.setState({ currentDuration: 0 }, () => this._play());
157
- };
158
- // user changed the total duration
159
- this._onTotalDurationChange = (value) => {
160
- // NOTE: we should reset the current duration to 0
161
- const milliseconds = this._getMilliseconds(value);
162
- this._onSetTotalDuration(milliseconds);
163
- };
164
- this._changeRepeatSetting = (newValue) => {
165
- // istanbul ignore else
166
- if (newValue !== undefined) {
167
- this.setState(() => ({ repeat: newValue }), () => {
168
- // istanbul ignore else
169
- if (this.props.onSettingsChange) {
170
- this.props.onSettingsChange({ loop: this.state.repeat });
171
- }
172
- });
173
- }
174
- };
175
- this._onRepeatChanged = () => {
176
- this.setState((prevState) => ({ repeat: !prevState.repeat }), () => {
177
- // istanbul ignore else
178
- if (this.props.onSettingsChange) {
179
- this.props.onSettingsChange({ loop: this.state.repeat });
180
- }
181
- });
182
- };
183
- this._currentDate = () => {
184
- const fraction = this.state.currentDuration / this.state.totalDuration;
185
- if (this.props.endDate && this.props.startDate) {
186
- const timeElapsed = (this.props.endDate.getTime() - this.props.startDate.getTime()) *
187
- fraction;
188
- return new Date(this.props.startDate.getTime() + timeElapsed);
189
- }
190
- return new Date();
191
- };
192
- this._onSetTotalDuration = (milliseconds) => {
193
- this.setState({ totalDuration: milliseconds }, () => {
194
- // istanbul ignore else
195
- if (this.props.onSettingsChange) {
196
- this.props.onSettingsChange({ duration: milliseconds });
197
- }
198
- });
199
- };
200
- this._renderSettings = () => {
201
- const createMenuItemNodes = (close) => {
202
- const { totalDuration } = this.state;
203
- let contextMenuItems = [];
204
- if (!this.props.appMenuItems) {
205
- contextMenuItems = this._standardTimelineMenuItems;
206
- }
207
- else {
208
- if (this.props.appMenuItemOption === "append") {
209
- contextMenuItems = this._standardTimelineMenuItems.concat(this.props.appMenuItems);
210
- }
211
- else if (this.props.appMenuItemOption === "prefix") {
212
- contextMenuItems = this.props.appMenuItems.concat(this._standardTimelineMenuItems);
213
- }
214
- else {
215
- contextMenuItems = this.props.appMenuItems;
216
- }
217
- }
218
- const itemNodes = [];
219
- let keyIndex = 0;
220
- if (this.state.includeRepeat) {
221
- const checked = this.state.repeat;
222
- const icon = checked ? (React.createElement("span", { className: "icon" },
223
- React.createElement(Icon, { iconSpec: React.createElement(SvgCheckmark, null) }),
224
- " ")) : (React.createElement("span", null));
225
- itemNodes.push(React.createElement(MenuItem, { key: ++keyIndex, onClick: () => {
226
- this._onRepeatChanged();
227
- close();
228
- }, startIcon: icon }, this._repeatLabel));
229
- itemNodes.push(React.createElement(MenuDivider, { key: ++keyIndex }));
230
- }
231
- contextMenuItems.forEach((item, index) => {
232
- itemNodes.push(this._createMenuItemNode(item, index + keyIndex + 1, totalDuration, close));
233
- });
234
- return itemNodes;
235
- };
236
- return (React.createElement(DropdownMenu, { menuItems: createMenuItemNodes, placement: "top-start" },
237
- React.createElement("span", { "data-testid": "timeline-settings", className: "timeline-settings icon", role: "button", tabIndex: -1, title: UiComponents.translate("button.label.settings") },
238
- React.createElement(Icon, { iconSpec: React.createElement(SvgMoreVertical, null) }))));
239
- };
240
- this.state = {
241
- isPlaying: false,
242
- minimized: true,
243
- currentDuration: props.initialDuration
244
- ? props.initialDuration
245
- : /* istanbul ignore next */ 0,
246
- totalDuration: this.props.totalDuration,
247
- repeat: this.props.repeat ? true : false,
248
- includeRepeat: this.props.includeRepeat || this.props.includeRepeat === undefined
249
- ? true
250
- : false,
251
- };
252
- this._repeatLabel = UiIModelComponents.translate("timeline.repeat");
253
- // istanbul ignore else
254
- if (this.props.componentId) {
255
- // can't handle an event if we have no id
256
- this._removeListener = UiAdmin.onGenericUiEvent.addListener(this._handleTimelinePausePlayEvent);
120
+ pause();
257
121
  }
258
- }
259
- componentWillUnmount() {
260
- if (this._removeListener)
261
- this._removeListener();
262
- window.cancelAnimationFrame(this._requestFrame);
263
- this._unmounted = true;
264
- }
265
- shouldComponentUpdate(nextProps, nextState) {
266
- let result = false;
267
- // istanbul ignore next
268
- if (nextState !== this.state ||
269
- nextProps !== this.props ||
270
- nextProps.startDate !== this.props.startDate ||
271
- nextProps.endDate !== this.props.endDate ||
272
- nextProps.initialDuration !== this.props.initialDuration ||
273
- nextProps.repeat !== this.props.repeat)
274
- result = true;
275
- return result;
276
- }
277
- componentDidUpdate(prevProps) {
278
- // istanbul ignore else
279
- if (this.props.initialDuration !== prevProps.initialDuration) {
280
- this._setDuration(this.props.initialDuration
281
- ? this.props.initialDuration
282
- : /* istanbul ignore next */ 0, this.props.totalDuration !== prevProps.totalDuration
283
- ? this.props.totalDuration
284
- : undefined);
122
+ }, isPlaying);
123
+ const onTimelineChange = (values) => {
124
+ const newDuration = values[0];
125
+ updateDuration(newDuration);
126
+ };
127
+ const currentDurationStr = toDisplayTime(currentDuration);
128
+ const totalDurationStr = toDisplayTime(totalDuration);
129
+ const hasDates = !!startDate && !!endDate;
130
+ return (React.createElement("div", { "data-testid": "timeline-component", className: "timeline-component" },
131
+ React.createElement(PlayButton, { isPlaying: isPlaying, onPlay: playOrReplay, onPause: pause }),
132
+ React.createElement("div", { className: "time-container" },
133
+ hasDates && (React.createElement("span", { "data-testid": "test-start-date", className: "timeline-date" }, toDateString(startDate, timeZoneOffset, dateFormatOptions))),
134
+ hasDates && !showDuration && (React.createElement("span", { "data-testid": "test-start-time", className: "timeline-time" }, toTimeString(startDate, timeZoneOffset, timeFormatOptions))),
135
+ showDuration && (React.createElement("span", { className: "timeline-duration-time" }, currentDurationStr))),
136
+ React.createElement(Scrubber, { className: "slider", currentDuration: currentDuration, totalDuration: totalDuration, startDate: startDate, endDate: endDate, isPlaying: isPlaying, showTime: !showDuration, onChange: onTimelineChange, onUpdate: onTimelineChange, timeZoneOffset: props.timeZoneOffset, markDate: props.markDate }),
137
+ React.createElement("div", { className: "time-container" },
138
+ hasDates && (React.createElement("span", { "data-testid": "test-end-date", className: "timeline-date" }, toDateString(endDate, timeZoneOffset))),
139
+ hasDates && !showDuration && (React.createElement("span", { className: "timeline-time" }, toTimeString(endDate, timeZoneOffset, timeFormatOptions))),
140
+ showDuration && (React.createElement(InlineEdit, { className: "timeline-duration-time", defaultValue: totalDurationStr, onChange: (value) => {
141
+ const newTotalDuration = timeToMs(value);
142
+ updateTotalDuration(newTotalDuration);
143
+ } }))),
144
+ React.createElement(SettingsMenu, { appMenuItems: props.appMenuItems, appMenuItemOption: appMenuItemOption, includeRepeat: includeRepeat, repeat: repeat, totalDuration: totalDuration, onRepeatClick: () => {
145
+ updateRepeat(!repeat);
146
+ }, onTotalDurationChange: (newTotalDuration) => {
147
+ updateTotalDuration(newTotalDuration);
148
+ } })));
149
+ }
150
+ function SettingsMenu({ appMenuItems, appMenuItemOption, includeRepeat, repeat, totalDuration, onRepeatClick, onTotalDurationChange, }) {
151
+ const settingsLabel = React.useMemo(() => UiComponents.translate("button.label.settings"), []);
152
+ const repeatLabel = React.useMemo(() => UiIModelComponents.translate("timeline.repeat"), []);
153
+ const standardItems = React.useMemo(() => [
154
+ {
155
+ label: UiIModelComponents.translate("timeline.slow"),
156
+ timelineDuration: slowSpeed,
157
+ },
158
+ {
159
+ label: UiIModelComponents.translate("timeline.medium"),
160
+ timelineDuration: mediumSpeed,
161
+ },
162
+ {
163
+ label: UiIModelComponents.translate("timeline.fast"),
164
+ timelineDuration: fastSpeed,
165
+ },
166
+ ], []);
167
+ const menuItems = React.useMemo(() => {
168
+ if (!appMenuItems) {
169
+ return standardItems;
285
170
  }
286
- // istanbul ignore else
287
- if (this.props.repeat !== prevProps.repeat) {
288
- this._changeRepeatSetting(this.props.repeat);
171
+ if (appMenuItemOption === "append") {
172
+ return [...standardItems, ...appMenuItems];
289
173
  }
290
- // istanbul ignore else
291
- if (this.props.totalDuration !== prevProps.totalDuration) {
292
- this._onSetTotalDuration(this.props.totalDuration);
174
+ if (appMenuItemOption === "prefix") {
175
+ return [...appMenuItems, ...standardItems];
293
176
  }
294
- }
295
- _play() {
296
- // start playing
297
- this.setState({ isPlaying: true }, () => {
298
- // request the next frame
299
- this._timeLastCycle = new Date().getTime();
300
- this._requestFrame = window.requestAnimationFrame(this._updateAnimation);
301
- });
302
- }
303
- _displayTime(millisec) {
304
- const addZero = (i) => {
305
- return i < 10 ? `0${i}` : i;
177
+ // Replace
178
+ return appMenuItems;
179
+ }, [appMenuItemOption, appMenuItems, standardItems]);
180
+ return (React.createElement(DropdownMenu, { menuItems: (close) => {
181
+ return [
182
+ ...(includeRepeat
183
+ ? [
184
+ React.createElement(MenuItem, { key: "repeat", onClick: () => {
185
+ onRepeatClick();
186
+ close();
187
+ }, startIcon: repeat ? React.createElement(SvgCheckmark, null) : React.createElement(React.Fragment, null) }, repeatLabel),
188
+ React.createElement(MenuDivider, { key: "divider" }),
189
+ ]
190
+ : []),
191
+ ...menuItems.map((item, index) => {
192
+ const checked = totalDuration === item.timelineDuration;
193
+ return (React.createElement(MenuItem, { key: index, startIcon: checked ? React.createElement(SvgCheckmark, null) : React.createElement(React.Fragment, null), onClick: () => {
194
+ onTotalDurationChange(item.timelineDuration);
195
+ close();
196
+ } }, item.label));
197
+ }),
198
+ ];
199
+ }, placement: "top-start" },
200
+ React.createElement(IconButton, { "data-testid": "timeline-settings", title: settingsLabel, styleType: "borderless" },
201
+ React.createElement(SvgMoreVertical, null))));
202
+ }
203
+ function useAnimation(onAnimate, playing = true) {
204
+ const onAnimateRef = React.useRef(onAnimate);
205
+ onAnimateRef.current = onAnimate;
206
+ const lastTimeRef = React.useRef();
207
+ React.useEffect(() => {
208
+ if (!playing)
209
+ return;
210
+ let handle = 0;
211
+ let didCancel = false;
212
+ const animate = (time) => {
213
+ if (lastTimeRef.current === undefined) {
214
+ lastTimeRef.current = time;
215
+ }
216
+ const delta = time - lastTimeRef.current;
217
+ lastTimeRef.current = time;
218
+ onAnimateRef.current({
219
+ delta,
220
+ });
221
+ if (didCancel)
222
+ return;
223
+ handle = window.requestAnimationFrame(animate);
306
224
  };
307
- const date = new Date(millisec);
308
- // const hours = date.getUTCHours();
309
- const minutes = date.getUTCMinutes();
310
- const seconds = date.getUTCSeconds();
311
- return `${addZero(minutes)}:${addZero(seconds)}`;
312
- }
313
- _getMilliseconds(time) {
314
- // istanbul ignore else - WIP
315
- if (time.indexOf(":") !== -1) {
316
- return ((Number(time.split(":")[0]) * 60 + Number(time.split(":")[1])) * 1000);
317
- }
318
- else {
319
- return Number(time) * 1000;
320
- }
321
- }
322
- _createMenuItemNode(item, index, currentTimelineDuration, close) {
323
- const label = item.label;
324
- const checked = currentTimelineDuration === item.timelineDuration;
325
- const icon = checked ? (React.createElement("span", { className: "icon" },
326
- React.createElement(Icon, { iconSpec: React.createElement(SvgCheckmark, null) }),
327
- " ")) : (React.createElement("span", null));
328
- return (React.createElement(MenuItem, { key: index, startIcon: icon, onClick: () => {
329
- this._onSetTotalDuration(item.timelineDuration);
330
- close();
331
- } }, label));
225
+ handle = window.requestAnimationFrame(animate);
226
+ return () => {
227
+ didCancel = true;
228
+ lastTimeRef.current = undefined;
229
+ window.cancelAnimationFrame(handle);
230
+ };
231
+ }, [playing]);
232
+ }
233
+ function timeToMs(time) {
234
+ if (time.indexOf(":") !== -1) {
235
+ return ((Number(time.split(":")[0]) * 60 + Number(time.split(":")[1])) * 1000);
332
236
  }
333
- render() {
334
- const { startDate, endDate, showDuration, timeZoneOffset, dateFormatOptions, timeFormatOptions, } = this.props;
335
- const { currentDuration, totalDuration, minimized } = this.state;
336
- const currentDate = this._currentDate();
337
- const durationString = this._displayTime(currentDuration);
338
- const totalDurationString = this._displayTime(totalDuration);
339
- const hasDates = !!startDate && !!endDate;
340
- return (React.createElement("div", { "data-testid": "timeline-component", className: classnames("timeline-component", !!minimized && "minimized", hasDates && "has-dates") },
341
- React.createElement("div", { className: "header" },
342
- React.createElement(PlayButton, { className: "play-button", isPlaying: this.state.isPlaying, onPlay: this._onPlay, onPause: this._onPause, "data-testid": "timeline-play" }),
343
- React.createElement(PlayerButton, { className: "play-backward", icon: React.createElement(SvgCaretLeft, null), onClick: this._onBackward, title: UiIModelComponents.translate("timeline.backward") }),
344
- React.createElement(PlayerButton, { className: "play-button-step", icon: React.createElement(SvgPlayCircular, null), isPlaying: this.state.isPlaying, onPlay: this._onPlay, onPause: this._onPause, title: UiIModelComponents.translate("timeline.step") }),
345
- React.createElement(PlayerButton, { className: "play-forward", icon: React.createElement(SvgCaretRight, null), onClick: this._onForward, title: UiIModelComponents.translate("timeline.backward") }),
346
- React.createElement("span", { className: "current-date" }, toDateString(currentDate, timeZoneOffset, dateFormatOptions))),
347
- React.createElement("div", { className: "scrubber" },
348
- React.createElement(PlayButton, { className: "play-button", isPlaying: this.state.isPlaying, onPlay: this._onPlay, onPause: this._onPause }),
349
- React.createElement("div", { className: "start-time-container" },
350
- hasDates && (React.createElement("span", { "data-testid": "test-start-date", className: "start-date" }, toDateString(startDate, timeZoneOffset, dateFormatOptions))),
351
- hasDates && !showDuration && (React.createElement("span", { "data-testid": "test-start-time", className: "start-time" }, toTimeString(startDate, timeZoneOffset, timeFormatOptions))),
352
- showDuration && (React.createElement("span", { className: "duration-start-time" }, durationString))),
353
- React.createElement(Scrubber, { className: "slider", currentDuration: this.state.currentDuration, totalDuration: totalDuration, startDate: startDate, endDate: endDate, isPlaying: this.state.isPlaying, inMiniMode: !!minimized, showTime: !showDuration, onChange: this._onTimelineChange, onUpdate: this._onTimelineChange, timeZoneOffset: this.props.timeZoneOffset, markDate: this.props.markDate }),
354
- React.createElement("div", { className: "end-time-container" },
355
- hasDates && (React.createElement("span", { className: "end-date" }, toDateString(endDate, timeZoneOffset))),
356
- hasDates && !showDuration && (React.createElement("span", { className: "end-time" }, toTimeString(endDate, timeZoneOffset, timeFormatOptions))),
357
- showDuration && (React.createElement(InlineEdit, { className: "duration-end-time", defaultValue: totalDurationString, onChange: this._onTotalDurationChange }))),
358
- minimized && this._renderSettings())));
237
+ else {
238
+ return Number(time) * 1000;
359
239
  }
360
240
  }
241
+ function toDisplayTime(ms) {
242
+ const addZero = (i) => {
243
+ return i < 10 ? `0${i}` : i;
244
+ };
245
+ const date = new Date(ms);
246
+ // const hours = date.getUTCHours();
247
+ const minutes = date.getUTCMinutes();
248
+ const seconds = date.getUTCSeconds();
249
+ return `${addZero(minutes)}:${addZero(seconds)}`;
250
+ }
361
251
  //# sourceMappingURL=TimelineComponent.js.map