@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 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');
@@ -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
- svgBackground.appendChild(svgHighlight);
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' && activeHighlightId && pointerType === 'mouse' && !activeSelectionHandle) {
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
- selectionHandles[0].style.visibility = 'hidden';
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
- let annotatableParagraphsInRange = Array.from(this.annotatableParagraphs).filter((paragraph) => {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@samuelbradshaw/highlight-helper-js",
3
- "version": "0.7.4",
3
+ "version": "0.8.0",
4
4
  "description": "JavaScript tool that enables highlighting or underlining text in an HTML page.",
5
5
  "type": "module",
6
6
  "main": "highlight-helper.mjs",