index.tsx 3.0 KB

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