This repository has been archived by the owner on Feb 19, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from PDFTron/2.1.0
2.1.0
- Loading branch information
Showing
12 changed files
with
683 additions
and
17 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import React, { createElement } from "react"; | ||
|
||
interface ListItemInputProps { | ||
item: any; | ||
render: any; | ||
parentAddEventListener: any; | ||
parentRemoveEventListener: any; | ||
} | ||
|
||
interface ListItemState { | ||
renderTarget: any; | ||
shouldRenderItem: boolean; | ||
isVisible: boolean; | ||
} | ||
|
||
class ListItem extends React.Component<ListItemInputProps, ListItemState> { | ||
private static MAX_SIZE = 0; | ||
private _containerRef: React.RefObject<HTMLDivElement>; | ||
private _measurementRef: React.RefObject<HTMLDivElement>; | ||
private _resizeObserver: ResizeObserver; | ||
private _height = 0; | ||
private _scrollHandle: any; | ||
constructor(props: ListItemInputProps) { | ||
super(props); | ||
this._containerRef = React.createRef(); | ||
this._measurementRef = React.createRef(); | ||
this._resizeObserver = new ResizeObserver(() => { | ||
const rect = this._measurementRef.current?.getBoundingClientRect(); | ||
if (!rect || rect.height === 0 || (this._height && rect.height < this._height)) { | ||
return; | ||
} | ||
this._height = rect.height; | ||
if (this._height > ListItem.MAX_SIZE) { | ||
ListItem.MAX_SIZE = this._height; | ||
} | ||
}); | ||
this.props.parentAddEventListener("scroll", this.onParentScroll); | ||
const renderTarget = this.props.render(this.props.item); | ||
const isPromise = renderTarget instanceof Promise; | ||
if (isPromise) { | ||
renderTarget.then((result: any) => this.setState({ renderTarget: result, shouldRenderItem: true })); | ||
} | ||
this.state = { | ||
renderTarget: isPromise ? undefined : renderTarget, | ||
shouldRenderItem: !isPromise, | ||
isVisible: true | ||
}; | ||
} | ||
componentDidMount(): void { | ||
// @ts-ignore | ||
this._resizeObserver.observe(this._measurementRef.current); | ||
} | ||
componentWillUnmount(): void { | ||
if (this._measurementRef.current) { | ||
this._resizeObserver.unobserve(this._measurementRef.current); | ||
} | ||
this.props.parentRemoveEventListener("scroll", this.onParentScroll); | ||
} | ||
onParentScroll = (parentRect: any, _scrollTop: number, padding: number) => { | ||
clearTimeout(this._scrollHandle); | ||
this._scrollHandle = setTimeout(() => { | ||
const rect = this._containerRef.current?.getBoundingClientRect(); | ||
if (this.doRectanglesIntersect(parentRect, rect, padding)) { | ||
this.setState({ isVisible: true }); | ||
} else { | ||
this.setState({ isVisible: false }); | ||
} | ||
}, 100); | ||
}; | ||
doRectanglesIntersect = (rect1: any, rect2: any, padding = 13): boolean => { | ||
const itemPadding = ListItem.MAX_SIZE * padding; | ||
const rect1Top = rect1.y - itemPadding; | ||
const rect1Bottom = rect1.y + rect1.height + itemPadding; | ||
const rect2Top = rect2.y; | ||
const rect2Bottom = rect2.y + rect2.height; | ||
|
||
const verticalIntersection = rect1Top < rect2Bottom && rect1Bottom > rect2Top; | ||
|
||
return verticalIntersection; | ||
}; | ||
render(): JSX.Element { | ||
if (!this.state.shouldRenderItem) { | ||
return <></>; | ||
} | ||
return ( | ||
<div | ||
ref={this._containerRef} | ||
style={{ | ||
display: "flex", | ||
alignItems: "center", | ||
justifyContent: "center", | ||
padding: "0.5em 0px", | ||
// DEBUGGING ONLY | ||
// backgroundColor: this.state.isVisible ? "green" : "red" | ||
}} | ||
> | ||
<div ref={this._measurementRef}> | ||
{this.state.isVisible ? ( | ||
this.state.renderTarget | ||
) : ( | ||
<div | ||
style={{ | ||
height: `${ListItem.MAX_SIZE < this._height ? this._height : ListItem.MAX_SIZE}px` | ||
}} | ||
></div> | ||
)} | ||
</div> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
export default ListItem; |
117 changes: 117 additions & 0 deletions
117
src/components/PageExtractionModal/PageExtractionThumbnail.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import React, { createElement } from "react"; | ||
|
||
interface PageExtractionThumbnailInputProps { | ||
wvInstance: any; | ||
pageNumber: number; | ||
addFileInputEventListener: any; | ||
removeFileInputEventListener: any; | ||
onClick: any; | ||
} | ||
|
||
interface PageExtractionThumbnailState { | ||
thumbnail?: string; | ||
isHover: boolean; | ||
isSelected: boolean; | ||
isDisabled: boolean; | ||
} | ||
|
||
const ListItemStyle = { display: "inline-block", boxShadow: "1px 1px 8px black", position: "relative" }; | ||
const ListItemHoverStyle = { ...ListItemStyle, boxShadow: "1px 1px 5px #3183c8" }; | ||
|
||
class PageExtractionThumbnail extends React.Component<PageExtractionThumbnailInputProps, PageExtractionThumbnailState> { | ||
constructor(props: PageExtractionThumbnailInputProps) { | ||
super(props); | ||
this.props.wvInstance.Core.documentViewer | ||
.getDocument() | ||
.getDocumentCompletePromise() | ||
.then(() => { | ||
this.props.wvInstance.Core.documentViewer | ||
.getDocument() | ||
.loadThumbnail(this.props.pageNumber, (thumbnailCanvas: HTMLCanvasElement) => { | ||
this.setState({ | ||
thumbnail: thumbnailCanvas.toDataURL() | ||
}); | ||
}); | ||
}); | ||
this.state = { | ||
thumbnail: undefined, | ||
isHover: false, | ||
isSelected: this.props.pageNumber === 1, | ||
isDisabled: false | ||
}; | ||
} | ||
componentDidMount(): void { | ||
this.props.addFileInputEventListener(this.props.pageNumber, this.onFileInputChanged); | ||
} | ||
componentWillUnmount(): void { | ||
this.props.removeFileInputEventListener(this.props.pageNumber, this.onFileInputChanged); | ||
} | ||
onFileInputChanged = (input: string) => { | ||
const parts = input.split(",").sort(); | ||
let occurrances = 0; | ||
let isSelected = false; | ||
for (const part of parts) { | ||
const rangeParts = part.split("-").sort(); | ||
const isRange = rangeParts.length === 2; | ||
|
||
if (isRange) { | ||
const lower = Number(rangeParts[0]); | ||
const upper = Number(rangeParts[1]); | ||
if (this.props.pageNumber >= lower && this.props.pageNumber <= upper) { | ||
isSelected = true; | ||
occurrances = occurrances ? occurrances++ : 2; | ||
} | ||
} else if (Number(part) === this.props.pageNumber) { | ||
isSelected = Number(rangeParts[0]) === this.props.pageNumber; | ||
occurrances++; | ||
} | ||
} | ||
this.setState({ | ||
isSelected, | ||
isDisabled: occurrances > 1 | ||
}); | ||
}; | ||
onHoverEnter = () => { | ||
if (this.state.isDisabled) { | ||
return; | ||
} | ||
this.setState({ isHover: true }); | ||
}; | ||
onHoverLeave = () => { | ||
if (this.state.isDisabled) { | ||
return; | ||
} | ||
this.setState({ isHover: false }); | ||
}; | ||
onClick = () => { | ||
if (this.state.isDisabled) { | ||
return; | ||
} | ||
this.props.onClick(this.props.pageNumber, !this.state.isSelected); | ||
this.setState({ isSelected: !this.state.isSelected }); | ||
}; | ||
render(): JSX.Element { | ||
const { thumbnail, isHover, isSelected } = this.state; | ||
const listItemStyle = isHover ? ListItemHoverStyle : ListItemStyle; | ||
return ( | ||
<div | ||
// @ts-ignore | ||
style={listItemStyle} | ||
onMouseEnter={this.onHoverEnter} | ||
onMouseLeave={this.onHoverLeave} | ||
onClick={this.onClick} | ||
> | ||
<img src={thumbnail} /> | ||
<input | ||
type="checkbox" | ||
style={{ position: "absolute", top: 0, left: 0 }} | ||
disabled={this.state.isDisabled} | ||
checked={isSelected} | ||
onClick={this.onClick} | ||
/> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
export default PageExtractionThumbnail; |
Oops, something went wrong.