123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- import React, { useState, useEffect } from 'react';
- import { setTimeout } from 'timers';
- import useActions from '../actions';
- import useStore from '../store';
- import SearchComponent from '../components/Search';
- import { getPdfPage, normalize, calculatePhraseMatch } from '../helpers/pdf';
- import { extractTextItems } from '../helpers/search';
- import { scrollIntoView } from '../helpers/utility';
- let timer: ReturnType<typeof setTimeout> = setTimeout(() => '', 100);
- let localMatchesMap: MatchType[] = [];
- let localTotal = 0;
- const Search: React.FC = () => {
- const [isProcessing, setProcessing] = useState(false);
- const [matchTotal, setMatchTotal] = useState(0);
- const [
- { navbarState, pdf, totalPage, queryString, currentIndex, matchesMap },
- dispatch,
- ] = useStore();
- const {
- setNavbar,
- setQueryString,
- setMatchesMap,
- setCurrentIndex,
- } = useActions(dispatch);
- const getMatchTextIndex = async (
- pageNum: number,
- queryStr: string,
- ): Promise<void> => {
- const contentItems = await extractTextItems(() => getPdfPage(pdf, pageNum));
- const content = normalize(contentItems.join('').toLowerCase());
- const matches = calculatePhraseMatch(content, queryStr);
- if (matches.length) {
- matches.forEach((ele) => {
- localMatchesMap.push({
- page: pageNum,
- index: ele,
- });
- });
- localTotal += matches.length;
- }
- if (pageNum === totalPage) {
- setMatchesMap(localMatchesMap);
- setMatchTotal(localTotal);
- setProcessing(false);
- }
- if (pageNum < totalPage) {
- await getMatchTextIndex(pageNum + 1, queryStr);
- }
- };
- const startSearchPdf = async (queryStr: string): Promise<void> => {
- getMatchTextIndex(1, queryStr);
- };
- const reset = (): void => {
- localMatchesMap = [];
- localTotal = 0;
- setMatchesMap([]);
- setMatchTotal(0);
- setCurrentIndex(-1);
- setQueryString('');
- };
- const handleSearch = (val: string): void => {
- if (!val) return;
- const newQueryString = normalize(val.toLowerCase());
- if (newQueryString !== queryString) {
- setProcessing(true);
- reset();
- setQueryString(newQueryString);
- startSearchPdf(newQueryString);
- }
- };
- const scrollToPage = (pageNum: number) => {
- const pageDiv: HTMLDivElement = document.getElementById(
- `page_${pageNum}`,
- ) as HTMLDivElement;
- scrollIntoView(pageDiv);
- };
- const highlightTarget = (match: MatchType, color: string) => {
- timer = setTimeout(() => {
- const id = `${match.page}_${match.index}`;
- const pageElement: HTMLDivElement = document.getElementById(
- `page_${match.page}`,
- ) as HTMLDivElement;
- const textLayer: HTMLDivElement = pageElement.querySelector(
- '[data-id="text-layer"]',
- ) as HTMLDivElement;
- if (textLayer) {
- const span = textLayer.querySelector(`[class="${id}"]`);
- if (span) {
- (span as HTMLElement).style.backgroundColor = color;
- } else {
- highlightTarget(match, color);
- }
- } else {
- highlightTarget(match, color);
- }
- }, 200);
- };
- const handleClickPrev = (): void => {
- if (currentIndex > 0) {
- const currentMatch = matchesMap[currentIndex];
- if (currentMatch) {
- highlightTarget(currentMatch, 'rgba(255, 211, 0, 0.7)');
- }
- setCurrentIndex(currentIndex - 1);
- const nextMatch = matchesMap[currentIndex - 1];
- scrollToPage(nextMatch.page);
- }
- };
- const handleClickNext = (): void => {
- if (currentIndex + 1 < matchTotal) {
- const currentMatch = matchesMap[currentIndex];
- if (currentMatch) {
- highlightTarget(currentMatch, 'rgba(255, 211, 0, 0.7)');
- }
- setCurrentIndex(currentIndex + 1);
- const indexObj = matchesMap[currentIndex + 1];
- scrollToPage(indexObj.page);
- }
- };
- const handleClose = (): void => {
- setNavbar('');
- reset();
- };
- useEffect(() => {
- if (matchTotal >= 1 && currentIndex === -1) {
- setCurrentIndex(currentIndex + 1);
- const indexObj = localMatchesMap[currentIndex + 1];
- scrollToPage(indexObj.page);
- }
- }, [matchTotal, currentIndex]);
- useEffect(() => {
- if (currentIndex >= 0) {
- clearTimeout(timer);
- const match = matchesMap[currentIndex];
- highlightTarget(match, 'rgb(255, 141, 0)');
- }
- }, [currentIndex, matchesMap]);
- return (
- <SearchComponent
- matchesTotal={matchTotal}
- current={currentIndex + 1}
- onPrev={handleClickPrev}
- onNext={handleClickNext}
- onEnter={handleSearch}
- isActive={navbarState === 'search'}
- close={handleClose}
- isProcessing={isProcessing}
- />
- );
- };
- export default Search;
|