Skip to content

Commit 07b2619

Browse files
authored
chore: improve annotated texts (#68)
1 parent 6cb8f0f commit 07b2619

File tree

9 files changed

+73
-83
lines changed

9 files changed

+73
-83
lines changed

packages/docs/app/(home)/_components/anara.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const PDFContent = ({
4444
onAnnotationClick,
4545
}: PDFContentProps) => {
4646
const { addAnnotation, annotations } = useAnnotations();
47-
const { getSelection } = useSelectionDimensions();
47+
const { getDimension } = useSelectionDimensions();
4848
const { jumpToHighlightRects } = usePdfJump();
4949

5050

@@ -53,17 +53,17 @@ const PDFContent = ({
5353
}, [annotations, onAnnotationsChange]);
5454

5555
const handleCreateAnnotation = useCallback(() => {
56-
const selection = getSelection();
56+
const selection = getDimension();
5757
if (!selection || !selection.highlights.length) return;
5858

5959
const newAnnotation = {
6060
pageNumber: selection.highlights[0].pageNumber,
6161
highlights: selection.highlights,
6262
color: "rgba(255, 255, 0, 0.3)",
63+
text: selection.text,
6364
};
6465

6566
addAnnotation(newAnnotation);
66-
6767
window.getSelection()?.removeAllRanges();
6868
}, [addAnnotation, getSelection]);
6969

packages/lector/src/components/default-annotation-tooltip.tsx renamed to packages/docs/app/(home)/_components/default-annotation-tooltip.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { useAnnotationActions } from "../hooks/useAnnotationActions";
2-
import type { Annotation } from "../hooks/useAnnotations";
1+
import { useAnnotationActions } from "./useAnnotationActions";
2+
import type { Annotation } from "@anaralabs/lector";
33

44
interface DefaultAnnotationTooltipContentProps {
55
annotation: Annotation;

packages/lector/src/hooks/useAnnotationActions.ts renamed to packages/docs/app/(home)/_components/useAnnotationActions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useCallback, useState } from "react";
22

3-
import type { Annotation } from "./useAnnotations";
4-
import { useAnnotations } from "./useAnnotations";
3+
import type { Annotation } from "@anaralabs/lector";
4+
import { useAnnotations } from "@anaralabs/lector";
55

66
interface UseAnnotationActionsProps {
77
annotation: Annotation;

packages/lector/size.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{
33
"name": "Client",
44
"passed": true,
5-
"size": 69735,
5+
"size": 69742,
66
"sizeLimit": 150000
77
}
88
]

packages/lector/src/components/layers/annotation-highlight-layer.tsx

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@ import type { Annotation } from "../../hooks/useAnnotations";
22
import { useAnnotations } from "../../hooks/useAnnotations";
33
import { usePDFPageNumber } from "../../hooks/usePdfPageNumber";
44
import { AnnotationTooltip } from "../annotation-tooltip";
5-
import { DefaultAnnotationTooltipContent } from "../default-annotation-tooltip";
65

76
interface AnnotationHighlightLayerProps {
87
className?: string;
98
style?: React.CSSProperties;
10-
renderTooltipContent?: (props: {
9+
renderTooltipContent: (props: {
1110
annotation: Annotation;
1211
onClose: () => void;
1312
}) => React.ReactNode;
@@ -41,18 +40,12 @@ export const AnnotationHighlightLayer = ({
4140
onAnnotationClick(annotation);
4241
}
4342
}}
44-
tooltipContent={
45-
renderTooltipContent ? (
43+
tooltipContent={(
4644
renderTooltipContent({
4745
annotation,
4846
onClose: () => {},
4947
})
50-
) : (
51-
<DefaultAnnotationTooltipContent
52-
annotation={annotation}
53-
onClose={() => {}}
54-
/>
55-
)
48+
)
5649
}
5750
>
5851
<div
@@ -69,10 +62,11 @@ export const AnnotationHighlightLayer = ({
6962
width: highlight.width,
7063
height: highlight.height,
7164
backgroundColor: annotation.color || "rgba(255, 255, 0, 0.3)",
72-
transition: "background-color 0.2s ease",
65+
mixBlendMode: "multiply",
66+
transition: "all 0.2s ease",
7367
cursor: "pointer",
74-
opacity: focusedAnnotationId === annotation.id ? 1 : 0.5,
7568
}}
69+
data-highlight-id={annotation.id}
7670
/>
7771
))}
7872
</div>

packages/lector/src/components/layers/colored-highlight/colored-highlight-layer.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,18 @@ export const ColoredHighlightLayer = ({
1515
onHighlight,
1616
}: ColoredHighlightLayerProps) => {
1717
const pageNumber = usePDFPageNumber();
18-
const { getSelection } = useSelectionDimensions();
18+
const { getDimension } = useSelectionDimensions();
1919

2020
const highlights: ColoredHighlight[] = usePdf(
2121
(state) => state.coloredHighlights,
2222
);
2323
const addColoredHighlight = usePdf((state) => state.addColoredHighlight);
2424

2525
const handleHighlighting = useCallback((color: string) => {
26-
const { highlights, text } = getSelection();
26+
const dimension = getDimension();
27+
if (!dimension) return;
28+
29+
const { highlights, text } = dimension;
2730

2831
if (highlights[0]) {
2932
const highlight: ColoredHighlight = {

packages/lector/src/hooks/useAnnotationTooltip.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ export const useAnnotationTooltip = ({
107107
}, [annotation.highlights, annotation.pageNumber, refs, viewportRef]);
108108

109109
useEffect(() => {
110+
const viewport = viewportRef.current;
110111
updateTooltipPosition();
111112

112113
const handleScroll = () => {
@@ -117,17 +118,17 @@ export const useAnnotationTooltip = ({
117118
requestAnimationFrame(updateTooltipPosition);
118119
};
119120

120-
if (viewportRef.current) {
121-
viewportRef.current.addEventListener("scroll", handleScroll, {
121+
if (viewport) {
122+
viewport.addEventListener("scroll", handleScroll, {
122123
passive: true,
123124
});
124125
}
125126

126127
window.addEventListener("resize", handleResize, { passive: true });
127128

128129
return () => {
129-
if (viewportRef.current) {
130-
viewportRef.current.removeEventListener("scroll", handleScroll);
130+
if (viewport) {
131+
viewport.removeEventListener("scroll", handleScroll);
131132
}
132133
window.removeEventListener("resize", handleResize);
133134
};

packages/lector/src/hooks/useSelectionDimensions.tsx

Lines changed: 32 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
import { type HighlightRect, PDFStore } from "../internal";
22

3-
const MERGE_THRESHOLD = 20; // Increased threshold for more aggressive merging
3+
const MERGE_THRESHOLD = 5; // Increased threshold for more aggressive merging
44

55
type CollapsibleSelection = {
66
highlights: HighlightRect[];
77
text: string;
88
isCollapsed: boolean;
99
};
1010

11-
type Selection = Omit<CollapsibleSelection, "isCollapsed">;
12-
13-
const shouldMergeRectangles = (
11+
const shouldMergeRects = (
1412
rect1: HighlightRect,
1513
rect2: HighlightRect,
1614
): boolean => {
@@ -29,7 +27,7 @@ const shouldMergeRectangles = (
2927
return verticalOverlap && horizontallyClose;
3028
};
3129

32-
const mergeRectangles = (
30+
const mergeRects = (
3331
rect1: HighlightRect,
3432
rect2: HighlightRect,
3533
): HighlightRect => {
@@ -50,57 +48,40 @@ const consolidateRects = (rects: HighlightRect[]): HighlightRect[] => {
5048
if (rects.length <= 1) return rects;
5149

5250
// Sort by vertical position primarily
53-
const sortedRectangles = rects.toSorted((a, b) => a.top - b.top);
54-
55-
let withinThresholdMergedRectangles: HighlightRect =
56-
sortedRectangles[0] as HighlightRect; // initialize with first rectangle to bootstrap the process
57-
const allMergedRectangles: HighlightRect[] = [];
58-
59-
const blowUpMergedRectangles = (rect: HighlightRect): HighlightRect => {
60-
return {
61-
...rect,
62-
left: rect.left - 2,
63-
top: rect.top - 2,
64-
width: rect.width + 4,
65-
height: rect.height + 2,
66-
};
67-
};
68-
69-
for (const [index, currentRectangle] of sortedRectangles.entries()) {
70-
withinThresholdMergedRectangles = mergeRectangles(
71-
withinThresholdMergedRectangles,
72-
currentRectangle,
73-
);
74-
75-
const nextRectangle = sortedRectangles[index + 1];
76-
77-
// establish the last merged rectangle if there are no more rectangles to merge
78-
if (!nextRectangle) {
79-
allMergedRectangles.push(
80-
blowUpMergedRectangles(withinThresholdMergedRectangles),
81-
);
82-
break;
51+
const sortedRects = [...rects].sort((a, b) => a.top - b.top);
52+
53+
// Keep merging until no more merges are possible
54+
let hasChanges: boolean;
55+
do {
56+
hasChanges = false;
57+
let currentRect = sortedRects[0];
58+
const tempResult: HighlightRect[] = [];
59+
60+
for (let i = 1; i < sortedRects.length; i++) {
61+
const sorted = sortedRects[i];
62+
if (!currentRect || !sorted) continue;
63+
64+
if (shouldMergeRects(currentRect, sorted)) {
65+
currentRect = mergeRects(currentRect, sorted);
66+
hasChanges = true;
67+
} else {
68+
tempResult.push(currentRect);
69+
currentRect = sorted;
70+
}
8371
}
72+
if (currentRect) tempResult.push(currentRect);
8473

85-
// check if the next rectangle should be merged
86-
if (
87-
!shouldMergeRectangles(withinThresholdMergedRectangles, nextRectangle)
88-
) {
89-
// establish already merged rectangles
90-
allMergedRectangles.push(
91-
blowUpMergedRectangles(withinThresholdMergedRectangles),
92-
);
93-
// reset merged rectangles with the next unmerged rectangle in the queue
94-
withinThresholdMergedRectangles = nextRectangle;
95-
}
96-
}
97-
return allMergedRectangles;
74+
sortedRects.length = 0;
75+
sortedRects.push(...tempResult);
76+
} while (hasChanges);
77+
78+
return sortedRects;
9879
};
9980

10081
export const useSelectionDimensions = () => {
10182
const store = PDFStore.useContext();
10283

103-
const getDimension = (): CollapsibleSelection | undefined => {
84+
const getDimension = () => {
10485
const selection = window.getSelection();
10586
if (!selection || selection.isCollapsed) return;
10687

@@ -155,8 +136,8 @@ export const useSelectionDimensions = () => {
155136
isCollapsed: false,
156137
};
157138
};
158-
159-
const getSelection = (): Selection => getDimension() as Selection;
139+
const getSelection = (): CollapsibleSelection => getDimension() as CollapsibleSelection;
160140

161141
return { getDimension, getSelection };
142+
162143
};

packages/lector/tsconfig.json

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,20 @@
1212
"resolveJsonModule": true,
1313
// Language & Environment
1414
"target": "ESNext",
15-
"lib": ["DOM", "DOM.Iterable", "ESNext"],
16-
"types": ["node"],
15+
"lib": [
16+
"DOM",
17+
"DOM.Iterable",
18+
"ESNext"
19+
],
20+
"types": [
21+
"node"
22+
],
1723
// Emit
1824
"noEmit": true,
1925
"declaration": true,
2026
"declarationMap": true,
2127
"verbatimModuleSyntax": true,
2228
"moduleDetection": "force",
23-
2429
"downlevelIteration": true,
2530
// Interop
2631
"allowJs": true,
@@ -33,6 +38,12 @@
3338
"incremental": true,
3439
"tsBuildInfoFile": ".tsbuildinfo"
3540
},
36-
"include": ["**/*.ts", "**/*.tsx"],
37-
"exclude": ["node_modules", "dist"]
38-
}
41+
"include": [
42+
"**/*.ts",
43+
"**/*.tsx"
44+
],
45+
"exclude": [
46+
"node_modules",
47+
"dist"
48+
]
49+
}

0 commit comments

Comments
 (0)