@openstax/highlighter
Advanced tools
Comparing version 1.7.0-alpha1 to 1.7.0-alpha2
import Highlight, { IOptions as HighlightOptions } from './Highlight'; | ||
import SerializedHighlight from './SerializedHighlight'; | ||
export declare const ON_SELECT_DELAY = 500; | ||
export declare const ON_SELECT_DELAY = 300; | ||
interface IOptions { | ||
@@ -17,3 +17,2 @@ snapTableRows?: boolean; | ||
private options; | ||
private selectionTimeout; | ||
private previousRange; | ||
@@ -35,2 +34,4 @@ constructor(container: HTMLElement, options?: IOptions); | ||
readonly document: Document; | ||
private snapSelection; | ||
private debouncedOnSelect; | ||
private onSelectionChange; | ||
@@ -37,0 +38,0 @@ private onClickHandler; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const lodash_1 = require("lodash"); | ||
const dom_1 = require("./dom"); | ||
@@ -10,7 +11,6 @@ const Highlight_1 = require("./Highlight"); | ||
const SerializedHighlight_1 = require("./SerializedHighlight"); | ||
exports.ON_SELECT_DELAY = 500; | ||
exports.ON_SELECT_DELAY = 300; | ||
class Highlighter { | ||
constructor(container, options = {}) { | ||
this.highlights = {}; | ||
this.selectionTimeout = null; | ||
this.previousRange = null; | ||
@@ -28,2 +28,11 @@ this.eraseAll = () => { | ||
}; | ||
this.snapSelection = () => { | ||
const selection = this.document.getSelection(); | ||
if (!selection || selection.isCollapsed) { | ||
return; | ||
} | ||
return selection_1.snapSelection(selection, this.options); | ||
}; | ||
// Created in the constructor | ||
this.debouncedOnSelect = () => undefined; | ||
this.onSelectionChange = () => { | ||
@@ -39,12 +48,3 @@ const selection = this.document.getSelection(); | ||
} | ||
if (this.selectionTimeout) { | ||
clearTimeout(this.selectionTimeout); | ||
} | ||
this.selectionTimeout = setTimeout(() => { | ||
const sel = this.document.getSelection(); | ||
if (!sel) { | ||
return; | ||
} | ||
this.onSelect(sel); | ||
}, exports.ON_SELECT_DELAY); | ||
this.debouncedOnSelect(); | ||
}; | ||
@@ -54,6 +54,29 @@ this.onClickHandler = (event) => { | ||
}; | ||
this.onSelect = () => { | ||
const { onSelect } = this.options; | ||
const selection = document.getSelection(); | ||
if (!selection) { | ||
return; | ||
} | ||
const range = this.snapSelection(); | ||
this.previousRange = range || null; | ||
if (onSelect && range) { | ||
const highlights = Object.values(this.highlights) | ||
.filter((other) => other.intersects(range)); | ||
if (highlights.length === 0) { | ||
const highlight = new Highlight_1.default(range, { content: rangeContents_1.rangeContentsString(range) }, this.getHighlightOptions()); | ||
onSelect(highlights, highlight); | ||
} | ||
else { | ||
onSelect(highlights); | ||
} | ||
} | ||
}; | ||
this.container = container; | ||
this.options = Object.assign({ className: 'highlight' }, options); | ||
this.debouncedOnSelect = lodash_1.debounce(this.onSelect, exports.ON_SELECT_DELAY); | ||
this.container.addEventListener('click', this.onClickHandler); | ||
document.addEventListener('selectionchange', this.onSelectionChange); | ||
this.container.addEventListener('keyup', this.snapSelection); | ||
this.container.addEventListener('mouseup', this.snapSelection); | ||
} | ||
@@ -63,2 +86,4 @@ unmount() { | ||
document.removeEventListener('selectionchange', this.onSelectionChange); | ||
this.container.removeEventListener('keyup', this.snapSelection); | ||
this.container.removeEventListener('mouseup', this.snapSelection); | ||
} | ||
@@ -137,18 +162,2 @@ highlight(highlight) { | ||
} | ||
onSelect(selection) { | ||
const { onSelect } = this.options; | ||
const range = selection_1.snapSelection(selection, this.options); | ||
this.previousRange = range || null; | ||
if (onSelect && range) { | ||
const highlights = Object.values(this.highlights) | ||
.filter((other) => other.intersects(range)); | ||
if (highlights.length === 0) { | ||
const highlight = new Highlight_1.default(range, { content: rangeContents_1.rangeContentsString(range) }, this.getHighlightOptions()); | ||
onSelect(highlights, highlight); | ||
} | ||
else { | ||
onSelect(highlights); | ||
} | ||
} | ||
} | ||
compareRanges(range1, range2) { | ||
@@ -164,11 +173,4 @@ if (range1 === null && range2 === null) { | ||
} | ||
if (range1 | ||
&& range2 | ||
&& range1.startContainer === range2.startContainer | ||
&& range1.endContainer === range2.endContainer | ||
&& range1.startOffset === range2.startOffset | ||
&& range1.endOffset === range2.endOffset) { | ||
return true; | ||
} | ||
return false; | ||
return range1.compareBoundaryPoints(Range.START_TO_START, range2) === 0 | ||
&& range1.compareBoundaryPoints(Range.END_TO_END, range2) === 0; | ||
} | ||
@@ -175,0 +177,0 @@ } |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const lodash = require("lodash"); | ||
const Highlight_1 = require("./Highlight"); | ||
@@ -108,4 +109,7 @@ const Highlighter_1 = require("./Highlighter"); | ||
container.appendChild(node); | ||
const spyDebounce = jest.spyOn(lodash, 'debounce') | ||
.mockImplementation((fn) => fn); | ||
// tslint:disable-next-line no-unused-expression | ||
new Highlighter_1.default(container, { onSelect: (_, newHighlight) => highlight = newHighlight }); | ||
expect(spyDebounce).toHaveBeenCalled(); | ||
const inputSelection = new Selection(); | ||
@@ -127,3 +131,2 @@ const selectionRange = new Range(); | ||
document.dispatchEvent(e); | ||
jest.runTimersToTime(Highlighter_1.ON_SELECT_DELAY); | ||
if (highlight === undefined) { | ||
@@ -142,4 +145,7 @@ expect(highlight).toBeDefined(); | ||
container.appendChild(node); | ||
const spyDebounce = jest.spyOn(lodash, 'debounce') | ||
.mockImplementation((fn) => fn); | ||
// tslint:disable-next-line no-unused-expression | ||
new Highlighter_1.default(container, { onSelect: spyOnSelect }); | ||
expect(spyDebounce).toHaveBeenCalled(); | ||
getSelectionSpy.mockImplementation(() => null); | ||
@@ -149,9 +155,6 @@ const e = document.createEvent('Event'); | ||
document.dispatchEvent(e); | ||
jest.runTimersToTime(Highlighter_1.ON_SELECT_DELAY + 100); | ||
expect(spyOnSelect).not.toHaveBeenCalled(); | ||
getSelectionSpy.mockImplementation(() => ({ isCollapsed: true })); | ||
jest.runTimersToTime(Highlighter_1.ON_SELECT_DELAY + 100); | ||
expect(spyOnSelect).not.toHaveBeenCalled(); | ||
getSelectionSpy.mockImplementation(() => ({ isCollapsed: false, type: 'None' })); | ||
jest.runTimersToTime(Highlighter_1.ON_SELECT_DELAY + 100); | ||
expect(spyOnSelect).not.toHaveBeenCalled(); | ||
@@ -165,4 +168,7 @@ }); | ||
container.appendChild(nodeInside); | ||
const spyDebounce = jest.spyOn(lodash, 'debounce') | ||
.mockImplementation((fn) => fn); | ||
// tslint:disable-next-line no-unused-expression | ||
new Highlighter_1.default(container, { onSelect: spyOnSelect }); | ||
expect(spyDebounce).toHaveBeenCalled(); | ||
getSelectionSpy.mockImplementation(() => ({ isCollapsed: false, anchorNode: nodeOutside, focusNode: nodeInside })); | ||
@@ -172,6 +178,4 @@ const e = document.createEvent('Event'); | ||
document.dispatchEvent(e); | ||
jest.runTimersToTime(Highlighter_1.ON_SELECT_DELAY + 100); | ||
expect(spyOnSelect).not.toHaveBeenCalled(); | ||
getSelectionSpy.mockImplementation(() => ({ isCollapsed: false, anchorNode: nodeInside, focusNode: nodeOutside })); | ||
jest.runTimersToTime(Highlighter_1.ON_SELECT_DELAY + 100); | ||
expect(spyOnSelect).not.toHaveBeenCalled(); | ||
@@ -178,0 +182,0 @@ }); |
@@ -14,3 +14,13 @@ "use strict"; | ||
}; | ||
const getDirection = (selection) => { | ||
const anchorNode = selection.anchorNode; | ||
const anchorOffset = selection.anchorOffset; | ||
const range = exports.getRange(selection); | ||
if (anchorNode !== range.startContainer || anchorOffset !== range.startOffset) { | ||
return 'backward'; | ||
} | ||
return 'forward'; | ||
}; | ||
exports.snapSelection = (selection, options) => { | ||
const selectionDirection = getDirection(selection); | ||
const range = exports.getRange(selection); | ||
@@ -76,2 +86,11 @@ if (!range) { | ||
} | ||
if (selectionDirection === 'backward') { | ||
// https://stackoverflow.com/a/10705853 | ||
const endRange = range.cloneRange(); | ||
endRange.collapse(false); | ||
selection.removeAllRanges(); | ||
selection.addRange(endRange); | ||
selection.extend(range.startContainer, range.startOffset); | ||
return range; | ||
} | ||
selection.removeAllRanges(); | ||
@@ -78,0 +97,0 @@ selection.addRange(range); |
{ | ||
"name": "@openstax/highlighter", | ||
"version": "1.7.0-alpha1", | ||
"version": "1.7.0-alpha2", | ||
"main": "dist/index.js", | ||
@@ -31,2 +31,3 @@ "license": "MIT", | ||
"@types/jest": "^23.3.7", | ||
"@types/lodash": "^4.14.168", | ||
"@types/uuid": "^3.4.6", | ||
@@ -74,2 +75,3 @@ "babel-jest": "^23.6.0", | ||
"change-case": "^4.0.0", | ||
"lodash": "^4.17.21", | ||
"serialize-selection": "^1.1.1", | ||
@@ -76,0 +78,0 @@ "uuid": "^3.3.3" |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
179570
2103
5
21
+ Addedlodash@^4.17.21
+ Addedlodash@4.17.21(transitive)