@atlaskit/renderer
Version:
Renderer component
204 lines • 7.04 kB
JavaScript
import { useLayoutEffect, useMemo, useState } from 'react';
import { AnnotationUpdateEvent } from '@atlaskit/editor-common/types';
import { AnnotationTypes } from '@atlaskit/adf-schema';
import { ACTION, ACTION_SUBJECT, EVENT_TYPE, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
import { FabricChannel } from '@atlaskit/analytics-listeners/types';
import { useAnnotationManagerDispatch, useAnnotationManagerState } from '../contexts/AnnotationManagerContext';
export const useAnnotationStateByTypeEvent = ({
type,
updateSubscriber
}) => {
const [states, setStates] = useState({});
const {
annotationManager,
dispatch
} = useAnnotationManagerDispatch();
const {
annotations
} = useAnnotationManagerState();
const isAnnotationManagerEnabled = !!annotationManager;
useLayoutEffect(() => {
if (!updateSubscriber) {
return;
}
const cb = payload => {
if (!payload) {
return;
}
const nextStates = Object.values(payload).reduce((acc, curr) => {
if (curr.id && curr.annotationType === type) {
// Check for empty state to prevent additional renders
const isEmpty = curr.state === null || curr.state === undefined;
return {
...acc,
[curr.id]: !isEmpty ? curr.state : states[curr.id]
};
}
return acc;
}, {});
setStates({
...states,
...nextStates
});
};
if (!isAnnotationManagerEnabled) {
updateSubscriber.on(AnnotationUpdateEvent.SET_ANNOTATION_STATE, cb);
return () => {
updateSubscriber.off(AnnotationUpdateEvent.SET_ANNOTATION_STATE, cb);
};
}
}, [states, type, updateSubscriber, dispatch, isAnnotationManagerEnabled]);
const annotationMarkStates = useMemo(() => {
return Object.values(annotations).reduce((acc, curr) => {
return {
...acc,
[curr.id]: curr.markState
};
}, {});
}, [annotations]);
if (isAnnotationManagerEnabled) {
return annotationMarkStates;
} else {
return states;
}
};
export const useHasFocusEvent = ({
id,
updateSubscriber
}) => {
const [hasFocus, setHasFocus] = useState(false);
const [isHovered, setIsHovered] = useState(false);
const {
currentSelectedAnnotationId,
currentHoveredAnnotationId
} = useAnnotationManagerState();
const {
annotationManager
} = useAnnotationManagerDispatch();
const isAnnotationManagerEnabled = !!annotationManager;
useLayoutEffect(() => {
if (!updateSubscriber) {
return;
}
const cb = payload => {
setHasFocus(payload && payload.annotationId === id);
};
const callbackForHoveredAnnotation = payload => {
setIsHovered(payload && payload.annotationId === id);
};
const removeFocus = () => {
setHasFocus(false);
};
const removeHoverEffect = () => {
setIsHovered(false);
};
if (!isAnnotationManagerEnabled) {
updateSubscriber.on(AnnotationUpdateEvent.SET_ANNOTATION_FOCUS, cb);
updateSubscriber.on(AnnotationUpdateEvent.SET_ANNOTATION_HOVERED, callbackForHoveredAnnotation);
updateSubscriber.on(AnnotationUpdateEvent.REMOVE_ANNOTATION_FOCUS, removeFocus);
updateSubscriber.on(AnnotationUpdateEvent.REMOVE_ANNOTATION_HOVERED, removeHoverEffect);
return () => {
updateSubscriber.off(AnnotationUpdateEvent.SET_ANNOTATION_FOCUS, cb);
updateSubscriber.off(AnnotationUpdateEvent.SET_ANNOTATION_HOVERED, callbackForHoveredAnnotation);
updateSubscriber.off(AnnotationUpdateEvent.REMOVE_ANNOTATION_FOCUS, removeFocus);
updateSubscriber.off(AnnotationUpdateEvent.SET_ANNOTATION_HOVERED, removeHoverEffect);
};
}
}, [id, updateSubscriber, isAnnotationManagerEnabled]);
if (isAnnotationManagerEnabled) {
return {
hasFocus: currentSelectedAnnotationId === id,
isHovered: currentHoveredAnnotationId === id
};
} else {
return {
hasFocus,
isHovered
};
}
};
const NO_ANNOTATION_SELECTED = {
annotations: [],
clickElementTarget: undefined
};
export const useAnnotationClickEvent = props => {
const [annotationClickEvent, setAnnotationClickEvent] = useState(null);
const {
updateSubscriber,
createAnalyticsEvent,
isNestedRender
} = props;
const {
annotationManager
} = useAnnotationManagerDispatch();
const isAnnotationManagerEnabled = !!annotationManager;
const {
currentSelectedAnnotationId,
currentSelectedMarkRef
} = useAnnotationManagerState();
const selectedAnnotation = useMemo(() => {
return currentSelectedAnnotationId && currentSelectedMarkRef && currentSelectedMarkRef.id === currentSelectedAnnotationId ? {
annotations: [{
id: currentSelectedAnnotationId,
type: AnnotationTypes.INLINE_COMMENT
}],
clickElementTarget: currentSelectedMarkRef
} :
// This is a constant to represent the case when no annotation is selected.
// When creating a new annotation, currentSelectedAnnotationId and currentSelectedMarkRef will be set one after another,
// if this isn't a const, it will cause useMemo to return two different "empty" objects and causes infinite re-renders.
NO_ANNOTATION_SELECTED;
}, [currentSelectedAnnotationId, currentSelectedMarkRef]);
useLayoutEffect(() => {
if (!updateSubscriber || isNestedRender) {
return;
}
const clickCb = ({
annotationIds,
eventTarget,
eventTargetType,
viewMethod
}) => {
const annotationsByType = annotationIds.filter(id => !!id).map(id => ({
id,
type: AnnotationTypes.INLINE_COMMENT
}));
if (createAnalyticsEvent) {
createAnalyticsEvent({
action: ACTION.VIEWED,
actionSubject: ACTION_SUBJECT.ANNOTATION,
actionSubjectId: ACTION_SUBJECT_ID.INLINE_COMMENT,
eventType: EVENT_TYPE.TRACK,
attributes: {
overlap: annotationsByType.length || 0,
targetNodeType: eventTargetType,
method: viewMethod
}
}).fire(FabricChannel.editor);
}
setAnnotationClickEvent({
annotations: annotationsByType,
clickElementTarget: eventTarget
});
};
const deselectCb = () => {
setAnnotationClickEvent({
annotations: [],
clickElementTarget: undefined
});
};
if (!isAnnotationManagerEnabled) {
updateSubscriber.on(AnnotationUpdateEvent.ON_ANNOTATION_CLICK, clickCb);
updateSubscriber.on(AnnotationUpdateEvent.DESELECT_ANNOTATIONS, deselectCb);
return () => {
updateSubscriber.off(AnnotationUpdateEvent.ON_ANNOTATION_CLICK, clickCb);
updateSubscriber.off(AnnotationUpdateEvent.DESELECT_ANNOTATIONS, deselectCb);
};
}
}, [updateSubscriber, createAnalyticsEvent, isNestedRender, isAnnotationManagerEnabled]);
if (isAnnotationManagerEnabled) {
return isNestedRender ? null : selectedAnnotation;
} else {
return annotationClickEvent;
}
};