index.tsx 3.1 KB

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