@samuelbradshaw/highlight-helper-js 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/highlight-helper.js +42 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -609,6 +609,7 @@ document.getElementById('copy-html').addEventListener('click', (event) => {
|
|
|
609
609
|
```css
|
|
610
610
|
@media print {
|
|
611
611
|
[data-hh-additions] { display: none; } /* Hide SVG highlights and custom drag handles */
|
|
612
|
+
[data-hh-wrapper] { display: none; } /* Hide wrappers */
|
|
612
613
|
mark[data-hh-highlight-id] { all: unset; } /* Hide mark-element highlights */
|
|
613
614
|
}
|
|
614
615
|
```
|
|
@@ -618,5 +619,4 @@ document.getElementById('copy-html').addEventListener('click', (event) => {
|
|
|
618
619
|
```javascript
|
|
619
620
|
// After a period of inactivity with a locked screen, certain versions of iOS Safari may get into a state where system selection handles don't show when the app tries to select text programmatically (such as when a user taps an existing highlight). Sending a visibilitychange event triggers HighlightHelper.js to run code that enables programmatic text selection again.
|
|
620
621
|
document.dispatchEvent(new Event('visibilitychange'));
|
|
621
|
-
});
|
|
622
622
|
```
|
package/highlight-helper.js
CHANGED
|
@@ -409,7 +409,11 @@
|
|
|
409
409
|
const height = Math.round(entry.contentBoxSize[0].blockSize);
|
|
410
410
|
// Respond to width changes (resize window, device rotation) and height changes (lazy-loaded images, dynamically-inserted elements)
|
|
411
411
|
if (width !== previousWidth || height !== previousHeight) {
|
|
412
|
-
if (options.drawingMode === 'svg')
|
|
412
|
+
if (options.drawingMode === 'svg') {
|
|
413
|
+
this.drawHighlights();
|
|
414
|
+
} else {
|
|
415
|
+
this._updateHighlightRects();
|
|
416
|
+
}
|
|
413
417
|
if (this._previousSelectionRange) this._updateSelectionState();
|
|
414
418
|
previousWidth = width;
|
|
415
419
|
previousHeight = height;
|
|
@@ -531,13 +535,23 @@
|
|
|
531
535
|
startParagraphOffset = Number.parseInt(properties.startParagraphOffset ?? oldHighlightInfo?.startParagraphOffset);
|
|
532
536
|
endParagraphId = properties.endParagraphId ?? oldHighlightInfo?.endParagraphId;
|
|
533
537
|
endParagraphOffset = Number.parseInt(properties.endParagraphOffset ?? oldHighlightInfo?.endParagraphOffset);
|
|
538
|
+
if (!this._paragraphIds.includes(startParagraphId) || !this._paragraphIds.includes(endParagraphId)) return;
|
|
534
539
|
([ startNode, startOffset ] = this._getTextNodeAndOffset(document.getElementById(startParagraphId), startParagraphOffset, 'start'));
|
|
535
540
|
([ endNode, endOffset ] = this._getTextNodeAndOffset(document.getElementById(endParagraphId), endParagraphOffset, 'end'));
|
|
536
541
|
} else if (adjustedSelectionRange) {
|
|
537
542
|
startNode = adjustedSelectionRange.startContainer;
|
|
538
543
|
startOffset = adjustedSelectionRange.startOffset;
|
|
544
|
+
if (this._shouldSkipTextNode(startNode)) {
|
|
545
|
+
startNode = this._getNextValidTextNode(startNode);
|
|
546
|
+
startOffset = 0;
|
|
547
|
+
}
|
|
539
548
|
endNode = adjustedSelectionRange.endContainer;
|
|
540
549
|
endOffset = adjustedSelectionRange.endOffset;
|
|
550
|
+
if (this._shouldSkipTextNode(endNode)) {
|
|
551
|
+
endNode = this._getPreviousValidTextNode(endNode);
|
|
552
|
+
endOffset = endNode?.length;
|
|
553
|
+
}
|
|
554
|
+
if (!startNode || !endNode) return;
|
|
541
555
|
([ startParagraphId, startParagraphOffset ] = this._getParagraphOffset(startNode, startOffset));
|
|
542
556
|
([ endParagraphId, endParagraphOffset ] = this._getParagraphOffset(endNode, endOffset));
|
|
543
557
|
}
|
|
@@ -952,9 +966,9 @@
|
|
|
952
966
|
this._additionsRect = this._additionsDiv.getBoundingClientRect();
|
|
953
967
|
const x = clientX - this._additionsRect.left;
|
|
954
968
|
const y = clientY - this._additionsRect.top;
|
|
955
|
-
for (const highlightId of Object.
|
|
969
|
+
for (const [highlightId, highlightInfo] of Object.entries(this._highlightsById)) {
|
|
956
970
|
if (highlightIds.has(highlightId)) continue;
|
|
957
|
-
const rangeLineRects =
|
|
971
|
+
const rangeLineRects = highlightInfo.rangeLineRects;
|
|
958
972
|
if (!rangeLineRects) continue;
|
|
959
973
|
for (const rect of rangeLineRects) {
|
|
960
974
|
const padding = rect.height / 8;
|
|
@@ -1131,8 +1145,12 @@
|
|
|
1131
1145
|
const styleString = this._getStyleString(highlightInfo.style, 'css', false, highlightInfo.variables, highlightInfo.rangeRect);
|
|
1132
1146
|
if (resolvedMode === 'highlight-api') {
|
|
1133
1147
|
const colorString = this._options.colorDefs[highlightInfo.color];
|
|
1134
|
-
cssText +=
|
|
1135
|
-
|
|
1148
|
+
cssText += `
|
|
1149
|
+
${this._containerSelector} ::highlight(${highlightInfo.escapedHighlightId}) { all: unset; }
|
|
1150
|
+
${this._containerSelector} :is(${this._paragraphSelector})::highlight(${highlightInfo.escapedHighlightId}),
|
|
1151
|
+
${this._containerSelector} :is(${this._paragraphSelector}) *::highlight(${highlightInfo.escapedHighlightId}) { --hh-color: ${colorString}; ${styleString} }
|
|
1152
|
+
${this._containerSelector} :is(rt, img)::highlight(${highlightInfo.escapedHighlightId}) { color: inherit; background-color: transparent; }
|
|
1153
|
+
`;
|
|
1136
1154
|
} else {
|
|
1137
1155
|
cssText += `mark[data-hh-highlight-id=${highlightInfo.escapedHighlightId}][data-hh-style] { ${styleString} }\n`;
|
|
1138
1156
|
}
|
|
@@ -1250,8 +1268,10 @@
|
|
|
1250
1268
|
${this._containerSelector} ::highlight(${highlightInfo.escapedHighlightId}) { all: unset; }
|
|
1251
1269
|
${this._containerSelector} mark[data-hh-highlight-id="${activeHighlightId}"][data-hh-style] { all: unset; }
|
|
1252
1270
|
${this._containerSelector} [data-hh-wrapper][data-hh-highlight-id="${activeHighlightId}"] { visibility: hidden; }
|
|
1253
|
-
${this._containerSelector} ::selection {
|
|
1254
|
-
${this._containerSelector}
|
|
1271
|
+
${this._containerSelector} ::selection { background-color: transparent; }
|
|
1272
|
+
${this._containerSelector} :is(${this._paragraphSelector})::selection,
|
|
1273
|
+
${this._containerSelector} :is(${this._paragraphSelector}) *::selection { --hh-color: ${colorString}; ${styleString} }
|
|
1274
|
+
${this._containerSelector} :is(rt, img)::selection { background-color: transparent; }
|
|
1255
1275
|
`);
|
|
1256
1276
|
|
|
1257
1277
|
// No active highlight (show the regular selection UI)
|
|
@@ -1268,7 +1288,7 @@
|
|
|
1268
1288
|
(options.showDragHandles.includes('touch') || pointerType === 'mouse')
|
|
1269
1289
|
&& (options.showDragHandles.includes('highlights') && activeHighlightId
|
|
1270
1290
|
|| options.showDragHandles.includes('selection') && !activeHighlightId);
|
|
1271
|
-
if (selection.type === 'Range' && showDragHandles) {
|
|
1291
|
+
if (selection.type === 'Range' && showDragHandles && rangeLineRects.length > 0) {
|
|
1272
1292
|
const startNodeIsRtl = globalThis.getComputedStyle(selectionRange.startContainer.parentElement).direction === 'rtl';
|
|
1273
1293
|
const endNodeIsRtl = globalThis.getComputedStyle(selectionRange.endContainer.parentElement).direction === 'rtl';
|
|
1274
1294
|
for (const handle of this._dragHandles) {
|
|
@@ -1356,7 +1376,7 @@
|
|
|
1356
1376
|
}
|
|
1357
1377
|
if (skipEnd) {
|
|
1358
1378
|
endNode = this._getPreviousValidTextNode(endNode);
|
|
1359
|
-
endOffset = endNode
|
|
1379
|
+
endOffset = endNode?.length;
|
|
1360
1380
|
}
|
|
1361
1381
|
if (!startNode || !endNode || (startNode === endNode && startOffset === endOffset)) {
|
|
1362
1382
|
return range.cloneRange().collapse(true);
|
|
@@ -1459,7 +1479,7 @@
|
|
|
1459
1479
|
// Determine if text node should be skipped when snapping to word or calculating character offsets
|
|
1460
1480
|
Highlighter.prototype._shouldSkipTextNode = function (textNode) {
|
|
1461
1481
|
const parentParagraph = this._closestValidParagraph(textNode.parentNode);
|
|
1462
|
-
const ignoreParent = textNode.parentNode.closest(
|
|
1482
|
+
const ignoreParent = textNode.parentNode.closest(`${this._containerSelector} [data-hh-ignore]`);
|
|
1463
1483
|
if (!parentParagraph || ignoreParent || textNode.textContent === '') return true;
|
|
1464
1484
|
return false;
|
|
1465
1485
|
}
|
|
@@ -1675,7 +1695,7 @@
|
|
|
1675
1695
|
range = document.caretRangeFromPoint(clientX, clientY);
|
|
1676
1696
|
}
|
|
1677
1697
|
if (!range) return;
|
|
1678
|
-
if (range.startContainer.parentElement?.closest(
|
|
1698
|
+
if (range.startContainer.parentElement?.closest(`${this._containerSelector} [data-hh-ignore]`)) return;
|
|
1679
1699
|
if (checkXDistance || checkYDistance) {
|
|
1680
1700
|
const maxDistance = 30;
|
|
1681
1701
|
const caretClientRect = range.getBoundingClientRect();
|
|
@@ -1737,6 +1757,16 @@
|
|
|
1737
1757
|
return { lines, snapTolerance, pseudoElemRects };
|
|
1738
1758
|
}
|
|
1739
1759
|
|
|
1760
|
+
Highlighter.prototype._updateHighlightRects = function (highlightIds = Object.keys(this._highlightsById), paragraphLineRectsCache = new Map(), columnGeometryCache = new Map()) {
|
|
1761
|
+
for (const highlightId of highlightIds) {
|
|
1762
|
+
const highlightInfo = this._highlightsById[highlightId];
|
|
1763
|
+
const rangeParagraphs = highlightInfo.rangeParagraphIds.map(id => document.getElementById(id));
|
|
1764
|
+
const [rangeRect, rangeLineRects] = this._getRangeRects(highlightInfo.rangeObj, rangeParagraphs, paragraphLineRectsCache, columnGeometryCache);
|
|
1765
|
+
highlightInfo.rangeRect = rangeRect;
|
|
1766
|
+
highlightInfo.rangeLineRects = rangeLineRects;
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1740
1770
|
// Get merged client rects from the highlight range
|
|
1741
1771
|
Highlighter.prototype._getRangeRects = function (range, paragraphs, paragraphLineRectsCache = new Map(), columnGeometryCache = new Map()) {
|
|
1742
1772
|
let rangeRectCoordinates = { top: Number.POSITIVE_INFINITY, bottom: Number.NEGATIVE_INFINITY, left: Number.POSITIVE_INFINITY, right: Number.NEGATIVE_INFINITY };
|
|
@@ -1752,6 +1782,7 @@
|
|
|
1752
1782
|
}
|
|
1753
1783
|
const { lines, snapTolerance, pseudoElemRects } = paragraphLineRects;
|
|
1754
1784
|
// If the previous paragraph is below the current paragraph (such as in multi-column layouts), reset prevParagraphBottom
|
|
1785
|
+
if (lines.length === 0) continue;
|
|
1755
1786
|
if (prevParagraphBottom > lines[0].bottom) prevParagraphBottom = Number.NEGATIVE_INFINITY;
|
|
1756
1787
|
const lineStates = lines.map((lineRect, i) => ({
|
|
1757
1788
|
topY: i === 0 ? prevParagraphBottom : lines[i - 1].bottom,
|
package/package.json
CHANGED