index.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import React, { useEffect, useState, useRef, useCallback } from 'react';
  2. import { useTranslation } from 'react-i18next';
  3. import Typography from '../Typography';
  4. import Item from '../AnnotationItem';
  5. import { watchScroll } from '../../helpers/utility';
  6. import { getAnnotationText } from '../../helpers/annotation';
  7. import { Body } from '../../global/sidebarStyled';
  8. type Props = {
  9. isActive?: boolean;
  10. annotations: AnnotationType[];
  11. viewport: ViewportType;
  12. scale: number;
  13. pdf: any;
  14. };
  15. const AnnotationList: React.FC<Props> = ({
  16. isActive = false,
  17. annotations,
  18. viewport,
  19. scale,
  20. pdf,
  21. }: Props) => {
  22. let observer: any = null;
  23. const { t } = useTranslation('sidebar');
  24. const [renderQueue, setQueue] = useState<AnnotationType[]>([]);
  25. const containerRef = useRef<HTMLDivElement>(null);
  26. const innerRef = useRef<HTMLDivElement>(null);
  27. const getText = async (
  28. page: number,
  29. position: PositionType[]
  30. ): Promise<any> => {
  31. const text = await getAnnotationText({
  32. pdf,
  33. viewport,
  34. scale,
  35. page,
  36. coords: position,
  37. });
  38. return text;
  39. };
  40. const scrollUpdate = useCallback(
  41. (state: ScrollStateType): void => {
  42. const innerHeight = innerRef.current?.offsetHeight || 0;
  43. const wrapperHeight = containerRef.current?.offsetHeight || 0;
  44. if (
  45. wrapperHeight + state.lastY >= innerHeight &&
  46. renderQueue.length !== annotations.length
  47. ) {
  48. const start = renderQueue.length;
  49. const end = renderQueue.length + 15;
  50. const newQueue = [...renderQueue, ...annotations.slice(start, end)];
  51. setQueue(newQueue);
  52. }
  53. },
  54. [renderQueue, annotations]
  55. );
  56. useEffect(() => {
  57. observer = watchScroll(containerRef.current, scrollUpdate);
  58. return (): void => {
  59. if (observer) {
  60. observer.subscriber.unsubscribe();
  61. }
  62. };
  63. }, [containerRef, scrollUpdate]);
  64. useEffect(() => {
  65. if (isActive) {
  66. setQueue(annotations.slice(0, 15));
  67. }
  68. }, [isActive, annotations]);
  69. return (
  70. <Body ref={containerRef}>
  71. <div ref={innerRef}>
  72. <Typography light align="left">
  73. {`${annotations.length} ${t('annotation')}`}
  74. </Typography>
  75. {isActive &&
  76. renderQueue.map((ele, index) => {
  77. const key = `annot_item_${index}`;
  78. const {
  79. obj_type,
  80. obj_attr: { page, bdcolor, position, transparency },
  81. } = ele;
  82. const actualPage = page + 1;
  83. const prevAnnot = annotations[index - 1];
  84. const prevPage =
  85. index > 0 && prevAnnot ? prevAnnot.obj_attr.page + 1 : -1;
  86. return (
  87. <Item
  88. key={key}
  89. type={obj_type}
  90. page={actualPage}
  91. bdcolor={bdcolor || ''}
  92. transparency={transparency || 0}
  93. getText={(): Promise<any> =>
  94. getText(actualPage, position as PositionType[])}
  95. showPageNum={actualPage !== prevPage}
  96. />
  97. );
  98. })}
  99. </div>
  100. </Body>
  101. );
  102. };
  103. export default AnnotationList;