box-annotations 5.2.1-beta.8 → 5.3.1-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 (121) hide show
  1. package/dist/annotations.css +1 -1
  2. package/dist/annotations.js +1 -1
  3. package/dist/annotations.js.LICENSE.txt +1 -10
  4. package/i18n/bn-IN.js +6 -0
  5. package/i18n/bn-IN.properties +12 -0
  6. package/i18n/da-DK.js +6 -0
  7. package/i18n/da-DK.properties +12 -0
  8. package/i18n/de-DE.js +6 -0
  9. package/i18n/de-DE.properties +12 -0
  10. package/i18n/en-AU.js +6 -0
  11. package/i18n/en-AU.properties +12 -0
  12. package/i18n/en-CA.js +6 -0
  13. package/i18n/en-CA.properties +12 -0
  14. package/i18n/en-GB.js +6 -0
  15. package/i18n/en-GB.properties +12 -0
  16. package/i18n/es-419.js +6 -0
  17. package/i18n/es-419.properties +12 -0
  18. package/i18n/es-ES.js +6 -0
  19. package/i18n/es-ES.properties +12 -0
  20. package/i18n/fi-FI.js +6 -0
  21. package/i18n/fi-FI.properties +12 -0
  22. package/i18n/fr-CA.js +6 -0
  23. package/i18n/fr-CA.properties +12 -0
  24. package/i18n/fr-FR.js +6 -0
  25. package/i18n/fr-FR.properties +12 -0
  26. package/i18n/hi-IN.js +6 -0
  27. package/i18n/hi-IN.properties +12 -0
  28. package/i18n/it-IT.js +6 -0
  29. package/i18n/it-IT.properties +12 -0
  30. package/i18n/ja-JP.js +7 -1
  31. package/i18n/ja-JP.properties +13 -1
  32. package/i18n/ko-KR.js +6 -0
  33. package/i18n/ko-KR.properties +12 -0
  34. package/i18n/nb-NO.js +6 -0
  35. package/i18n/nb-NO.properties +12 -0
  36. package/i18n/nl-NL.js +6 -0
  37. package/i18n/nl-NL.properties +12 -0
  38. package/i18n/pl-PL.js +6 -0
  39. package/i18n/pl-PL.properties +12 -0
  40. package/i18n/pt-BR.js +6 -0
  41. package/i18n/pt-BR.properties +12 -0
  42. package/i18n/ru-RU.js +6 -0
  43. package/i18n/ru-RU.properties +12 -0
  44. package/i18n/sv-SE.js +6 -0
  45. package/i18n/sv-SE.properties +12 -0
  46. package/i18n/tr-TR.js +6 -0
  47. package/i18n/tr-TR.properties +12 -0
  48. package/i18n/zh-CN.js +6 -0
  49. package/i18n/zh-CN.properties +12 -0
  50. package/i18n/zh-TW.js +6 -0
  51. package/i18n/zh-TW.properties +12 -0
  52. package/index.js +1 -1
  53. package/package.json +54 -42
  54. package/src/@types/api.ts +3 -2
  55. package/src/@types/events.ts +27 -10
  56. package/src/@types/global.ts +0 -9
  57. package/src/@types/model.ts +9 -0
  58. package/src/BoxAnnotations.ts +6 -2
  59. package/src/adapters/threadedAnnotationsAdapters.ts +49 -28
  60. package/src/api/APIFactory.ts +6 -1
  61. package/src/api/types.ts +23 -1
  62. package/src/boundingBoxHighlight/BoundingBoxHighlightContainer.tsx +4 -1
  63. package/src/common/AnnotationCallbacksContext.ts +7 -0
  64. package/src/common/BaseAnnotator.scss +12 -0
  65. package/src/common/BaseAnnotator.ts +32 -2
  66. package/src/common/BaseManager.ts +2 -0
  67. package/src/common/DeselectListener.tsx +1 -1
  68. package/src/common/withProviders.tsx +6 -2
  69. package/src/components/BoundingBoxHighlight/BoundingBoxHighlightNav.scss +3 -3
  70. package/src/components/BoundingBoxHighlight/BoundingBoxHighlightRect.scss +11 -6
  71. package/src/components/Popups/PopupBase.scss +2 -2
  72. package/src/components/Popups/PopupDrawingToolbar.scss +4 -4
  73. package/src/components/Popups/PopupDrawingToolbar.tsx +0 -7
  74. package/src/components/Popups/PopupList.scss +1 -1
  75. package/src/components/Popups/PopupV2.scss +2 -2
  76. package/src/components/Popups/PopupV2.tsx +128 -72
  77. package/src/components/ReplyForm/ReplyForm.tsx +1 -8
  78. package/src/constants.ts +2 -3
  79. package/src/document/DocumentAnnotator.ts +29 -5
  80. package/src/document/docUtil.ts +8 -1
  81. package/src/drawing/DrawingAnnotations.tsx +30 -21
  82. package/src/drawing/DrawingAnnotationsContainer.tsx +3 -0
  83. package/src/drawing/DrawingCreator.tsx +9 -16
  84. package/src/drawing/DrawingCursor.ts +2 -1
  85. package/src/drawing/DrawingManager.tsx +21 -2
  86. package/src/highlight/HighlightAnnotations.tsx +28 -14
  87. package/src/highlight/HighlightContainer.tsx +3 -0
  88. package/src/highlight/HighlightCreator.scss +9 -2
  89. package/src/highlight/HighlightCreatorManager.ts +8 -5
  90. package/src/highlight/HighlightListener.ts +4 -2
  91. package/src/highlight/HighlightManager.tsx +13 -2
  92. package/src/image/ImageAnnotator.ts +2 -1
  93. package/src/media/MediaAnnotator.ts +39 -16
  94. package/src/popup/PopupLayer.tsx +38 -11
  95. package/src/popup/PopupManager.tsx +4 -2
  96. package/src/region/RegionAnnotations.tsx +2 -3
  97. package/src/region/RegionCreation.tsx +15 -9
  98. package/src/region/RegionCreationContainer.tsx +2 -2
  99. package/src/region/RegionCreationManager.tsx +8 -1
  100. package/src/region/RegionCreator.scss +9 -3
  101. package/src/region/RegionCreator.tsx +9 -17
  102. package/src/region/RegionManager.tsx +8 -1
  103. package/src/region/RegionRect.scss +17 -9
  104. package/src/store/annotations/actions.ts +104 -33
  105. package/src/store/annotations/reducer.ts +49 -1
  106. package/src/store/creator/selectors.ts +6 -6
  107. package/src/store/eventing/create.ts +6 -4
  108. package/src/store/eventing/delete.ts +26 -0
  109. package/src/store/eventing/middleware.ts +53 -15
  110. package/src/store/eventing/replyCreate.ts +30 -0
  111. package/src/store/eventing/replyDelete.ts +31 -0
  112. package/src/store/eventing/replyUpdate.ts +35 -0
  113. package/src/store/eventing/types.ts +4 -4
  114. package/src/store/eventing/update.ts +28 -0
  115. package/src/store/highlight/actions.ts +50 -19
  116. package/src/store/highlight/types.ts +1 -0
  117. package/src/store/options/selectors.ts +2 -2
  118. package/src/store/options/types.ts +2 -2
  119. package/src/test-utils/videoAnnotations.tsx +11 -11
  120. package/src/utils/rotate.ts +43 -0
  121. package/src/utils/useVideoTiming.ts +9 -13
@@ -0,0 +1,35 @@
1
+ import eventManager from '../../common/EventManager';
2
+ import { Reply, Event } from '../../@types';
3
+ import { AppState } from '../types';
4
+ import { AsyncAction, Status } from './types';
5
+
6
+ type ReplyUpdateArg = {
7
+ annotationId: string;
8
+ replyId: string;
9
+ payload: { message?: string; status?: string };
10
+ };
11
+ type ReplyUpdatePayload = { annotationId: string; reply: Reply };
12
+
13
+ const emitReplyUpdateEvent = (action: AsyncAction<ReplyUpdateArg, ReplyUpdatePayload>, status: Status): void => {
14
+ const { error, meta: { arg, requestId } = {}, payload } = action;
15
+ if (status === Status.SUCCESS && !payload?.reply) return;
16
+ const annotationId = payload?.annotationId ?? arg?.annotationId;
17
+ const annotationReply = payload?.reply ?? (arg ? { id: arg.replyId } : undefined);
18
+ eventManager.emit(Event.ANNOTATION_REPLY_UPDATE, {
19
+ annotation: annotationId ? { id: annotationId } : undefined,
20
+ annotationReply,
21
+ error,
22
+ meta: { requestId, status },
23
+ });
24
+ };
25
+
26
+ const replyUpdateHandler =
27
+ (status: Status) =>
28
+ (_prev: AppState, _next: AppState, action: AsyncAction): void =>
29
+ emitReplyUpdateEvent(action as AsyncAction<ReplyUpdateArg, ReplyUpdatePayload>, status);
30
+
31
+ const handleReplyUpdateErrorEvents = replyUpdateHandler(Status.ERROR);
32
+ const handleReplyUpdatePendingEvents = replyUpdateHandler(Status.PENDING);
33
+ const handleReplyUpdateSuccessEvents = replyUpdateHandler(Status.SUCCESS);
34
+
35
+ export { handleReplyUpdateErrorEvents, handleReplyUpdatePendingEvents, handleReplyUpdateSuccessEvents };
@@ -1,6 +1,6 @@
1
1
  import { Action, SerializedError } from '@reduxjs/toolkit';
2
2
  import { AppState } from '../types';
3
- import { Annotation, NewAnnotation } from '../../@types';
3
+ import { Annotation } from '../../@types';
4
4
 
5
5
  export enum Status {
6
6
  ERROR = 'error',
@@ -22,10 +22,10 @@ export interface ThunkMeta<M> {
22
22
  requestId: string;
23
23
  }
24
24
 
25
- export interface AsyncAction extends Action {
25
+ export interface AsyncAction<TArg = unknown, TPayload = unknown> extends Action {
26
26
  error?: SerializedError;
27
- meta?: ThunkMeta<NewAnnotation>;
28
- payload?: Annotation;
27
+ meta?: ThunkMeta<TArg>;
28
+ payload?: TPayload;
29
29
  }
30
30
 
31
31
  export type EventHandler = (prevState: AppState, nextState: AppState, action: Action | AsyncAction) => void;
@@ -0,0 +1,28 @@
1
+ import eventManager from '../../common/EventManager';
2
+ import { Annotation, Event } from '../../@types';
3
+ import { AppState } from '../types';
4
+ import { AsyncAction, Status } from './types';
5
+
6
+ type UpdateArg = { annotationId: string; payload: { message?: string; status?: string } };
7
+
8
+ const emitUpdateEvent = (action: AsyncAction<UpdateArg, Annotation>, status: Status): void => {
9
+ const { error, meta: { arg, requestId } = {}, payload } = action;
10
+ if (status === Status.SUCCESS && !payload) return;
11
+ const annotation = payload ?? (arg ? { id: arg.annotationId } : undefined);
12
+ eventManager.emit(Event.ANNOTATION_UPDATE, {
13
+ annotation,
14
+ error,
15
+ meta: { requestId, status },
16
+ });
17
+ };
18
+
19
+ const updateHandler =
20
+ (status: Status) =>
21
+ (_prev: AppState, _next: AppState, action: AsyncAction): void =>
22
+ emitUpdateEvent(action as AsyncAction<UpdateArg, Annotation>, status);
23
+
24
+ const handleUpdateErrorEvents = updateHandler(Status.ERROR);
25
+ const handleUpdatePendingEvents = updateHandler(Status.PENDING);
26
+ const handleUpdateSuccessEvents = updateHandler(Status.SUCCESS);
27
+
28
+ export { handleUpdateErrorEvents, handleUpdatePendingEvents, handleUpdateSuccessEvents };
@@ -1,13 +1,16 @@
1
1
  import { createAction } from '@reduxjs/toolkit';
2
- import { combineRects } from '../../highlight/highlightUtil';
2
+ import { combineRects, getBoundingRect } from '../../highlight/highlightUtil';
3
+ import { getElementLocalPosition } from '../../utils/rotate';
3
4
  import { SelectionItem } from './types';
4
5
  import { Shape } from '../../@types';
5
6
 
6
7
  export type SelectionArg = {
8
+ containerEl?: HTMLElement | null;
7
9
  containerRect: DOMRect;
8
10
  hasError?: boolean;
9
11
  location: number;
10
12
  range: Range;
13
+ rotation?: number;
11
14
  };
12
15
 
13
16
  type Payload = {
@@ -74,33 +77,61 @@ export const getShape = ({ height, left, top, width }: DOMRect): Shape => ({
74
77
  y: top,
75
78
  });
76
79
 
77
- export const setSelectionAction = createAction(
78
- 'SET_SELECTION',
79
- (arg: SelectionArg | null): Payload => {
80
- if (!arg) {
81
- return {
82
- payload: null,
83
- };
84
- }
80
+ export const setSelectionAction = createAction('SET_SELECTION', (arg: SelectionArg | null): Payload => {
81
+ if (!arg) {
82
+ return {
83
+ payload: null,
84
+ };
85
+ }
85
86
 
86
- const { containerRect, hasError, location, range } = arg;
87
+ const { containerEl, containerRect, hasError, location, range, rotation } = arg;
87
88
 
88
- let rects = Array.from(range.getClientRects());
89
- // getClientRects on IE/Edge might return 0 rects
90
- if (!rects.length) {
91
- rects = getClientRects(range);
92
- }
89
+ let rects = Array.from(range.getClientRects());
90
+ // getClientRects on IE/Edge might return 0 rects
91
+ if (!rects.length) {
92
+ rects = getClientRects(range);
93
+ }
94
+
95
+ const screenShapes = rects.map(getShape);
96
+
97
+ if (rotation && containerEl) {
98
+ // Convert screen rects to element-local coordinates
99
+ const localRects = screenShapes.map(shape => {
100
+ const [lx1, ly1] = getElementLocalPosition(shape.x, shape.y, containerEl, rotation);
101
+ const [lx2, ly2] = getElementLocalPosition(
102
+ shape.x + shape.width,
103
+ shape.y + shape.height,
104
+ containerEl,
105
+ rotation,
106
+ );
107
+ return {
108
+ x: Math.min(lx1, lx2),
109
+ y: Math.min(ly1, ly2),
110
+ width: Math.abs(lx2 - lx1),
111
+ height: Math.abs(ly2 - ly1),
112
+ };
113
+ });
93
114
 
94
115
  return {
95
116
  payload: {
96
- containerRect: getShape(containerRect),
117
+ containerRect: { x: 0, y: 0, width: containerEl.offsetWidth, height: containerEl.offsetHeight },
97
118
  hasError,
98
119
  location,
99
- rects: combineRects(rects.map(getShape)),
120
+ popupRect: getBoundingRect(screenShapes),
121
+ rects: combineRects(localRects),
100
122
  },
101
123
  };
102
- },
103
- );
124
+ }
125
+
126
+ return {
127
+ payload: {
128
+ containerRect: getShape(containerRect),
129
+ hasError,
130
+ location,
131
+ rects: combineRects(screenShapes),
132
+ },
133
+ };
134
+ });
104
135
 
105
136
  export const setIsPromotingAction = createAction<boolean>('SET_IS_PROMOTING');
106
137
  export const setIsSelectingAction = createAction<boolean>('SET_IS_SELECTING');
@@ -10,5 +10,6 @@ export type SelectionItem = {
10
10
  containerRect: Shape;
11
11
  hasError?: boolean;
12
12
  location: number;
13
+ popupRect?: Shape;
13
14
  rects: Array<Shape>;
14
15
  };
@@ -1,6 +1,6 @@
1
1
  import getProp from 'lodash/get';
2
2
  import { AppState } from '../types';
3
- import { Permissions } from '../../@types';
3
+ import { Permissions, Token } from '../../@types';
4
4
  import { Features } from '../../BoxAnnotations';
5
5
  import { ViewMode } from './types';
6
6
 
@@ -15,6 +15,6 @@ export const getIsCurrentFileVersion = (state: State): boolean => state.options.
15
15
  export const getPermissions = (state: State): Permissions => state.options.permissions;
16
16
  export const getRotation = (state: State): number => state.options.rotation;
17
17
  export const getScale = (state: State): number => state.options.scale;
18
- export const getToken = (state: State): string => state.options.token;
18
+ export const getToken = (state: State): Token => state.options.token;
19
19
  export const isFeatureEnabled = (state: State, featurename: string): boolean =>
20
20
  getProp(getFeatures(state), featurename, false);
@@ -1,5 +1,5 @@
1
1
  import { Features } from '../../BoxAnnotations';
2
- import { Permissions } from '../../@types';
2
+ import { Permissions, Token } from '../../@types';
3
3
 
4
4
  /** View mode: annotations and bounding boxes are mutually exclusive. */
5
5
  export type ViewMode = 'annotations' | 'boundingBoxes';
@@ -13,6 +13,6 @@ export type OptionsState = {
13
13
  permissions: Permissions;
14
14
  rotation: number;
15
15
  scale: number;
16
- token: string;
16
+ token: Token;
17
17
  viewMode: ViewMode;
18
18
  };
@@ -18,8 +18,8 @@ export interface VideoAnnotationTestConfig {
18
18
  componentName: string;
19
19
  getWrapper: (props: unknown) => ReactWrapper;
20
20
  findListComponent: (wrapper: ReactWrapper) => ListComponentWrapper;
21
- videoAnnotations: AnnotationDrawing[]|AnnotationRegion[] ;
22
- regularAnnotations: AnnotationDrawing[]|AnnotationRegion[];
21
+ videoAnnotations: AnnotationDrawing[] | AnnotationRegion[];
22
+ regularAnnotations: AnnotationDrawing[] | AnnotationRegion[];
23
23
  activeAnnotationId: string;
24
24
  nonExistentAnnotationId: string;
25
25
  }
@@ -43,7 +43,7 @@ export const createVideoAnnotationTests = (config: VideoAnnotationTestConfig): v
43
43
  describe('TARGET_TYPE.FRAME target type', () => {
44
44
  test('should show only active annotation when not seeking and active annotation exists', () => {
45
45
  mockVideoTimingReturn.isVideoSeeking = false;
46
-
46
+
47
47
  const wrapper = getWrapper({
48
48
  targetType: TARGET_TYPE.FRAME,
49
49
  annotations: videoAnnotations,
@@ -58,7 +58,7 @@ export const createVideoAnnotationTests = (config: VideoAnnotationTestConfig): v
58
58
 
59
59
  test('should show no annotations when seeking and active annotation exists', () => {
60
60
  mockVideoTimingReturn.isVideoSeeking = true;
61
-
61
+
62
62
  const wrapper = getWrapper({
63
63
  targetType: TARGET_TYPE.FRAME,
64
64
  annotations: videoAnnotations,
@@ -71,7 +71,7 @@ export const createVideoAnnotationTests = (config: VideoAnnotationTestConfig): v
71
71
 
72
72
  test('should show no annotations when not seeking but no active annotation', () => {
73
73
  mockVideoTimingReturn.isVideoSeeking = false;
74
-
74
+
75
75
  const wrapper = getWrapper({
76
76
  targetType: TARGET_TYPE.FRAME,
77
77
  annotations: videoAnnotations,
@@ -84,7 +84,7 @@ export const createVideoAnnotationTests = (config: VideoAnnotationTestConfig): v
84
84
 
85
85
  test('should show no annotations when seeking and no active annotation', () => {
86
86
  mockVideoTimingReturn.isVideoSeeking = true;
87
-
87
+
88
88
  const wrapper = getWrapper({
89
89
  targetType: TARGET_TYPE.FRAME,
90
90
  annotations: videoAnnotations,
@@ -97,7 +97,7 @@ export const createVideoAnnotationTests = (config: VideoAnnotationTestConfig): v
97
97
 
98
98
  test('should call useVideoTiming with correct parameters for TARGET_TYPE.FRAME type', () => {
99
99
  const referenceEl = document.createElement('video');
100
-
100
+
101
101
  getWrapper({
102
102
  targetType: TARGET_TYPE.FRAME,
103
103
  annotations: videoAnnotations,
@@ -117,7 +117,7 @@ export const createVideoAnnotationTests = (config: VideoAnnotationTestConfig): v
117
117
  describe('useVideoTiming integration', () => {
118
118
  test('should call useVideoTiming hook on component mount', () => {
119
119
  const referenceEl = document.createElement('video');
120
-
120
+
121
121
  getWrapper({
122
122
  targetType: TARGET_TYPE.FRAME,
123
123
  annotations: videoAnnotations,
@@ -130,7 +130,7 @@ export const createVideoAnnotationTests = (config: VideoAnnotationTestConfig): v
130
130
 
131
131
  test('should pass correct parameters to useVideoTiming for TARGET_TYPE.PAGE type', () => {
132
132
  const referenceEl = document.createElement('div');
133
-
133
+
134
134
  getWrapper({
135
135
  targetType: TARGET_TYPE.PAGE,
136
136
  annotations: regularAnnotations,
@@ -150,7 +150,7 @@ export const createVideoAnnotationTests = (config: VideoAnnotationTestConfig): v
150
150
  describe('annotation filtering edge cases', () => {
151
151
  test('should handle empty annotations array for TARGET_TYPE.FRAME type', () => {
152
152
  mockVideoTimingReturn.isVideoSeeking = false;
153
-
153
+
154
154
  const wrapper = getWrapper({
155
155
  targetType: TARGET_TYPE.FRAME,
156
156
  annotations: [],
@@ -163,7 +163,7 @@ export const createVideoAnnotationTests = (config: VideoAnnotationTestConfig): v
163
163
 
164
164
  test('should handle active annotation that does not exist in annotations array', () => {
165
165
  mockVideoTimingReturn.isVideoSeeking = false;
166
-
166
+
167
167
  const wrapper = getWrapper({
168
168
  targetType: TARGET_TYPE.FRAME,
169
169
  annotations: videoAnnotations,
@@ -129,3 +129,46 @@ export function getRotatedPosition({ x, y }: Position, rotation: number): Positi
129
129
  const { x: rotatedX, y: rotatedY } = getRotatedShape({ height: 0, width: 0, x, y }, rotation);
130
130
  return { x: rotatedX, y: rotatedY };
131
131
  }
132
+
133
+ /**
134
+ * Converts a mouse position (in browser window coordinates) to a position relative to
135
+ * a CSS-rotated element's own top-left corner.
136
+ *
137
+ * When an element has a CSS rotation, its internal axes are rotated with it.
138
+ * This function maps window coordinates to element-local coordinates.
139
+ *
140
+ * If element is null, returns the raw coordinates as a passthrough.
141
+ *
142
+ * Assumes transform-origin: center (the CSS default), which matches how
143
+ * box-content-preview applies rotation to annotation layers.
144
+ */
145
+ export function getElementLocalPosition(
146
+ clientX: number,
147
+ clientY: number,
148
+ element: HTMLElement | null,
149
+ rotation: number,
150
+ ): [number, number] {
151
+ if (!element) {
152
+ return [clientX, clientY];
153
+ }
154
+
155
+ const rect = element.getBoundingClientRect();
156
+
157
+ if (!rotation) {
158
+ return [clientX - rect.left, clientY - rect.top];
159
+ }
160
+
161
+ // 1. Find the element's center (rotation doesn't move the center)
162
+ const centerX = rect.left + rect.width / 2;
163
+ const centerY = rect.top + rect.height / 2;
164
+
165
+ // 2. Get the mouse position relative to the center (in browser window coordinates)
166
+ const relX = clientX - centerX;
167
+ const relY = clientY - centerY;
168
+
169
+ // 3. Apply inverse rotation to obtain the mouse position from center in element-local coordinates
170
+ const local = rotatePoint({ x: relX, y: relY }, -rotation);
171
+
172
+ // 4. Convert coordinates from center-relative to top-left-relative
173
+ return [local.x + element.offsetWidth / 2, local.y + element.offsetHeight / 2];
174
+ }
@@ -1,7 +1,5 @@
1
- import { useState, useEffect, useCallback } from "react";
2
- import { TARGET_TYPE } from "../constants";
3
-
4
-
1
+ import { useState, useEffect, useCallback } from 'react';
2
+ import { TARGET_TYPE } from '../constants';
5
3
 
6
4
  export interface UseVideoTimingProps {
7
5
  targetType: TARGET_TYPE;
@@ -22,7 +20,7 @@ export function getVideoCurrentTimeInMilliseconds(video: HTMLVideoElement): numb
22
20
  /**
23
21
  * This hook is used to keep track of when the video is currently seeking and when it has reached
24
22
  * the target time definined in the annotation. This is necessary because we cannot control how long it
25
- * takes for the video to seek to the target time and we do not want to display an annotation on the video player
23
+ * takes for the video to seek to the target time and we do not want to display an annotation on the video player
26
24
  * at the wrong time. For example, if an annotation is defined at being at 20 seconds into the video and
27
25
  * the video just loaded and needs to buffer in order to get to 20 seconds, we do not want the annotation
28
26
  * drawing or region to be shown until the video has finished seeking and is at the correct time. The hook
@@ -38,9 +36,8 @@ const useVideoTiming = ({
38
36
  const [isVideoSeeking, setIsVideoSeeking] = useState<boolean>(false);
39
37
  const [targetVideoTime, setTargetVideoTime] = useState<number | null>(null);
40
38
 
41
-
42
- const getCurrentVideoTimeStamp = useCallback((): number => {
43
- return getVideoCurrentTimeInMilliseconds(referenceEl as HTMLVideoElement);
39
+ const getCurrentVideoTimeStamp = useCallback((): number => {
40
+ return getVideoCurrentTimeInMilliseconds(referenceEl as HTMLVideoElement);
44
41
  }, [referenceEl]);
45
42
 
46
43
  // Handle video seeking events
@@ -48,7 +45,7 @@ const useVideoTiming = ({
48
45
  if (targetType !== TARGET_TYPE.FRAME || !referenceEl) {
49
46
  return undefined;
50
47
  }
51
-
48
+
52
49
  const handleSeeking = (): void => {
53
50
  setIsVideoSeeking(true);
54
51
  };
@@ -62,7 +59,7 @@ const useVideoTiming = ({
62
59
  if (isVideoSeeking && targetVideoTime !== null) {
63
60
  const currentVideoTimePosition = getCurrentVideoTimeStamp();
64
61
  const timeDiff = Math.abs(currentVideoTimePosition - targetVideoTime);
65
-
62
+
66
63
  // Consider the video has reached the target time if within 100ms
67
64
  if (timeDiff <= 100) {
68
65
  setIsVideoSeeking(false);
@@ -86,13 +83,13 @@ const useVideoTiming = ({
86
83
  useEffect(() => {
87
84
  if (targetType === TARGET_TYPE.FRAME && activeAnnotationId && referenceEl) {
88
85
  const currentVideoTimePosition = getCurrentVideoTimeStamp();
89
-
86
+
90
87
  // Find the annotation to get its target time
91
88
  const annotation = annotations.find(ann => ann.id === activeAnnotationId);
92
89
  if (annotation) {
93
90
  const annotationTimePosition = annotation.target.location.value;
94
91
  const timeDiff = Math.abs(currentVideoTimePosition - annotationTimePosition);
95
-
92
+
96
93
  // If the video is not at the annotation's time, set seeking state
97
94
  if (timeDiff > 100) {
98
95
  setTargetVideoTime(annotationTimePosition);
@@ -109,4 +106,3 @@ const useVideoTiming = ({
109
106
  };
110
107
 
111
108
  export default useVideoTiming;
112
-