@samuelbradshaw/highlight-helper-js 0.7.4 → 0.8.0
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 +3 -0
- package/demo.html +12 -0
- package/highlight-helper.js +30 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -101,6 +101,9 @@ Options can be provided when Highlight Helper is initialized. They can also be s
|
|
|
101
101
|
- **defaultStyle** – Key of the default highlight style. Default: `fill`.
|
|
102
102
|
- **defaultWrapper** – Key of the default highlight wrapper. Default: `none`.
|
|
103
103
|
- **highlightIdFunction** – Identifier of a function that provides unique IDs for new highlights. Default: `hhGetNewHighlightId`.
|
|
104
|
+
- **showCustomSelectionHandlesForActiveHighlights** – Whether custom selection handles should show for active highlights. Default: `true`.
|
|
105
|
+
- **showCustomSelectionHandlesForTextSelection** – Whether custom selection handles should show for text selection. Default: `false`.
|
|
106
|
+
- **showCustomSelectionHandlesOnTouch** – Whether custom selection handles should show on touch devices. Default: `false`.
|
|
104
107
|
|
|
105
108
|
|
|
106
109
|
### <a name="custom-events"></a>Custom events
|
package/demo.html
CHANGED
|
@@ -147,6 +147,12 @@
|
|
|
147
147
|
<label><input type="radio" name="hh-drawing-mode" value="highlight-api"> Custom Highlight API</label><br>
|
|
148
148
|
<label><input type="radio" name="hh-drawing-mode" value="mark-elements"> HTML mark elements</label><br>
|
|
149
149
|
</p>
|
|
150
|
+
<p>
|
|
151
|
+
Show custom selection handles:<br>
|
|
152
|
+
<label><input type="checkbox" id="hh-selection-handles-active-highlights" checked> For active highlights</label><br>
|
|
153
|
+
<label><input type="checkbox" id="hh-selection-handles-text-selection"> For text selection</label><br>
|
|
154
|
+
<label><input type="checkbox" id="hh-selection-handles-touch"> On touch devices</label><br>
|
|
155
|
+
</p>
|
|
150
156
|
<b>Active highlight:</b>
|
|
151
157
|
<div id="snippet-container">
|
|
152
158
|
<div id="snippet">N/A</div>
|
|
@@ -263,6 +269,12 @@
|
|
|
263
269
|
}
|
|
264
270
|
});
|
|
265
271
|
}
|
|
272
|
+
const selectionHandlesActiveHighlightsCheckbox = document.getElementById('hh-selection-handles-active-highlights');
|
|
273
|
+
selectionHandlesActiveHighlightsCheckbox.addEventListener('change', (event) => { highlighter.setOption('showCustomSelectionHandlesForActiveHighlights', event.currentTarget.checked); });
|
|
274
|
+
const selectionHandlesTextSelectionCheckbox = document.getElementById('hh-selection-handles-text-selection');
|
|
275
|
+
selectionHandlesTextSelectionCheckbox.addEventListener('change', (event) => { highlighter.setOption('showCustomSelectionHandlesForTextSelection', event.currentTarget.checked); });
|
|
276
|
+
const selectionHandlesOnTouchCheckbox = document.getElementById('hh-selection-handles-touch');
|
|
277
|
+
selectionHandlesOnTouchCheckbox.addEventListener('change', (event) => { highlighter.setOption('showCustomSelectionHandlesOnTouch', event.currentTarget.checked); });
|
|
266
278
|
|
|
267
279
|
const container = document.querySelector(highlighter.getOptions().containerSelector);
|
|
268
280
|
const snippetDiv = document.getElementById('snippet');
|
package/highlight-helper.js
CHANGED
|
@@ -70,6 +70,9 @@ function Highlighter(options = hhDefaultOptions) {
|
|
|
70
70
|
width: 0px;
|
|
71
71
|
visibility: hidden;
|
|
72
72
|
}
|
|
73
|
+
[data-hh-pointer-down="true"] .hh-selection-handle {
|
|
74
|
+
visibility: hidden !important;
|
|
75
|
+
}
|
|
73
76
|
.hh-selection-handle-content {
|
|
74
77
|
position: absolute;
|
|
75
78
|
height: 100%;
|
|
@@ -457,7 +460,7 @@ function Highlighter(options = hhDefaultOptions) {
|
|
|
457
460
|
this.activateHighlight(highlightId);
|
|
458
461
|
} else if (activeHighlightId && highlightId === activeHighlightId && appearanceChanges.length > 0) {
|
|
459
462
|
updateSelectionUi('appearance');
|
|
460
|
-
} else {
|
|
463
|
+
} else if (highlightId !== activeHighlightId) {
|
|
461
464
|
this.drawHighlights([highlightId]);
|
|
462
465
|
}
|
|
463
466
|
|
|
@@ -642,13 +645,13 @@ function Highlighter(options = hhDefaultOptions) {
|
|
|
642
645
|
this.annotatableContainer.addEventListener('pointerdown', (event) => respondToPointerDown(event), { signal: controller.signal });
|
|
643
646
|
const respondToPointerDown = (event) => {
|
|
644
647
|
const isSecondaryClick = (event.button !== 0 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey);
|
|
648
|
+
if (!isSecondaryClick) this.annotatableContainer.dataset.hhPointerDown = 'true';
|
|
645
649
|
pointerType = event.pointerType;
|
|
646
650
|
|
|
647
651
|
// Pointer down on a selection handle
|
|
648
652
|
if (event.target?.closest('.hh-selection-handle')) {
|
|
649
653
|
if (!isSecondaryClick) {
|
|
650
654
|
activeSelectionHandle = event.target.parentElement.closest('.hh-selection-handle');
|
|
651
|
-
this.annotatableContainer.dataset.hhDragging = 'true';
|
|
652
655
|
const selectionHandleClientRect = activeSelectionHandle.getBoundingClientRect();
|
|
653
656
|
const lineHeight = selectionHandleClientRect.bottom - selectionHandleClientRect.top;
|
|
654
657
|
activeSelectionHandle.dataset.dragYOffset = Math.max(0, event.clientY - selectionHandleClientRect.bottom + (lineHeight / 6));
|
|
@@ -748,10 +751,10 @@ function Highlighter(options = hhDefaultOptions) {
|
|
|
748
751
|
}
|
|
749
752
|
tapResult = null;
|
|
750
753
|
longPressTimeoutId = clearTimeout(longPressTimeoutId);
|
|
754
|
+
this.annotatableContainer.dataset.hhPointerDown = 'false';
|
|
751
755
|
if (activeSelectionHandle) {
|
|
752
756
|
activeSelectionHandle = null;
|
|
753
757
|
updateSelectionUi('bounds');
|
|
754
|
-
this.annotatableContainer.dataset.hhDragging = 'false';
|
|
755
758
|
this.annotatableContainer.removeEventListener('pointermove', respondToSelectionHandleDrag);
|
|
756
759
|
}
|
|
757
760
|
}
|
|
@@ -898,7 +901,7 @@ function Highlighter(options = hhDefaultOptions) {
|
|
|
898
901
|
const highlightInfo = highlightsById[activeHighlightId] ?? null;
|
|
899
902
|
const color = highlightInfo?.color ?? null;
|
|
900
903
|
const style = highlightInfo?.style ?? null;
|
|
901
|
-
const colorString = options.colors[color] ?? 'AccentColor';
|
|
904
|
+
const colorString = options.colors[color] ?? (CSS.supports('color', 'AccentColor') ? 'AccentColor' : 'dodgerblue');
|
|
902
905
|
|
|
903
906
|
// Draw SVG selection rects (SVG drawing mode only)
|
|
904
907
|
svgActiveOverlay.innerHTML = '';
|
|
@@ -916,10 +919,8 @@ function Highlighter(options = hhDefaultOptions) {
|
|
|
916
919
|
|
|
917
920
|
// Bring active highlight to the front
|
|
918
921
|
const svgHighlight = svgBackground.querySelector(`g[data-highlight-id="${activeHighlightId}"]`);
|
|
919
|
-
if (svgHighlight)
|
|
920
|
-
|
|
921
|
-
svgBackground.appendChild(svgActiveOverlay);
|
|
922
|
-
}
|
|
922
|
+
if (svgHighlight) svgBackground.appendChild(svgHighlight);
|
|
923
|
+
svgBackground.appendChild(svgActiveOverlay);
|
|
923
924
|
}
|
|
924
925
|
|
|
925
926
|
if (changeType === 'appearance') {
|
|
@@ -955,7 +956,7 @@ function Highlighter(options = hhDefaultOptions) {
|
|
|
955
956
|
|
|
956
957
|
} else if (changeType === 'bounds') {
|
|
957
958
|
// Update selection handle location and visibility
|
|
958
|
-
if (selection.type === 'Range'
|
|
959
|
+
if (selection.type === 'Range') {
|
|
959
960
|
const selectionRangeRects = selectionRange.getClientRects();
|
|
960
961
|
const startRect = selectionRangeRects[0];
|
|
961
962
|
const endRect = selectionRangeRects[selectionRangeRects.length-1];
|
|
@@ -963,8 +964,14 @@ function Highlighter(options = hhDefaultOptions) {
|
|
|
963
964
|
const startNodeIsRtl = window.getComputedStyle(selectionRange.startContainer.parentElement).direction === 'rtl';
|
|
964
965
|
const endNodeIsRtl = window.getComputedStyle(selectionRange.endContainer.parentElement).direction === 'rtl';
|
|
965
966
|
|
|
967
|
+
if (
|
|
968
|
+
(options.showCustomSelectionHandlesOnTouch || pointerType === 'mouse')
|
|
969
|
+
&& ((options.showCustomSelectionHandlesForActiveHighlights && activeHighlightId)
|
|
970
|
+
|| (options.showCustomSelectionHandlesForTextSelection && !activeHighlightId))
|
|
971
|
+
) {
|
|
972
|
+
setCustomSelectionHandleVisibility(true);
|
|
973
|
+
}
|
|
966
974
|
for (const selectionHandle of selectionHandles) {
|
|
967
|
-
selectionHandle.style.visibility = 'visible';
|
|
968
975
|
let side;
|
|
969
976
|
if (selectionHandle.dataset.position === 'start') {
|
|
970
977
|
side = startNodeIsRtl ? 'right' : 'left';
|
|
@@ -983,12 +990,17 @@ function Highlighter(options = hhDefaultOptions) {
|
|
|
983
990
|
}
|
|
984
991
|
}
|
|
985
992
|
} else {
|
|
986
|
-
|
|
987
|
-
selectionHandles[1].style.visibility = 'hidden';
|
|
993
|
+
setCustomSelectionHandleVisibility(false);
|
|
988
994
|
}
|
|
989
995
|
}
|
|
990
996
|
}
|
|
991
997
|
|
|
998
|
+
const setCustomSelectionHandleVisibility = (visible = selectionHandles[0].style.visibility === 'hidden') => {
|
|
999
|
+
for (const selectionHandle of selectionHandles) {
|
|
1000
|
+
selectionHandle.style.visibility = visible ? 'visible' : 'hidden';
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
992
1004
|
// Update the selection or highlight range to stay within the annotatable container
|
|
993
1005
|
const snapRangeToBoundaries = (range, anchorNode = null) => {
|
|
994
1006
|
let startNode = range.startContainer;
|
|
@@ -1014,7 +1026,7 @@ function Highlighter(options = hhDefaultOptions) {
|
|
|
1014
1026
|
|
|
1015
1027
|
// Prevent the range from starting or ending in an element that doesn't match the paragraph selector
|
|
1016
1028
|
if (this.annotatableContainer.contains(range.commonAncestorContainer) && (!startNode.parentElement.closest(options.paragraphSelector) || !endNode.parentElement.closest(options.paragraphSelector))) {
|
|
1017
|
-
|
|
1029
|
+
const annotatableParagraphsInRange = Array.from(this.annotatableParagraphs).filter((paragraph) => {
|
|
1018
1030
|
const relativeStartPosition = startNode.compareDocumentPosition(paragraph);
|
|
1019
1031
|
const relativeEndPosition = endNode.compareDocumentPosition(paragraph);
|
|
1020
1032
|
return (relativeStartPosition & Node.DOCUMENT_POSITION_FOLLOWING || relativeStartPosition & Node.DOCUMENT_POSITION_CONTAINS) && (relativeEndPosition & Node.DOCUMENT_POSITION_PRECEDING || relativeEndPosition & Node.DOCUMENT_POSITION_CONTAINS);
|
|
@@ -1310,8 +1322,8 @@ function Highlighter(options = hhDefaultOptions) {
|
|
|
1310
1322
|
mergedRect.width = rect.width;
|
|
1311
1323
|
}
|
|
1312
1324
|
// Process then remove rects that apply to the current line
|
|
1313
|
-
const minLeft = Math.min(mergedRect.x, rect.x);
|
|
1314
|
-
const maxRight = Math.max(mergedRect.right, rect.right);
|
|
1325
|
+
const minLeft = Math.max(paragraphRect.left, Math.min(mergedRect.x, rect.x));
|
|
1326
|
+
const maxRight = Math.min(paragraphRect.right, Math.max(mergedRect.right, rect.right));
|
|
1315
1327
|
mergedRect.width = maxRight - minLeft;
|
|
1316
1328
|
mergedRect.x = minLeft;
|
|
1317
1329
|
unmergedRects.splice(r, 1); r--;
|
|
@@ -1452,6 +1464,9 @@ let hhDefaultOptions = {
|
|
|
1452
1464
|
defaultStyle: 'fill',
|
|
1453
1465
|
defaultWrapper: 'none',
|
|
1454
1466
|
highlightIdFunction: hhGetNewHighlightId,
|
|
1467
|
+
showCustomSelectionHandlesForActiveHighlights: true,
|
|
1468
|
+
showCustomSelectionHandlesForTextSelection: false,
|
|
1469
|
+
showCustomSelectionHandlesOnTouch: false,
|
|
1455
1470
|
}
|
|
1456
1471
|
|
|
1457
1472
|
console.info('Highlighter loaded');
|
package/package.json
CHANGED