gerhardsletten / react-reader

An ePub-reader for React, powered by Epub.js
https://react-reader.metabits.no
Apache License 2.0
721 stars 132 forks source link

Text Selection is very fast #152

Closed fciannella closed 1 year ago

fciannella commented 1 year ago

When trying to select the text in the highlight example below, the selection is fired as soon as the mouse stops.

I believe this is coming from epubjs:

https://github.com/futurepress/epub.js/issues/952

Is there a way to fix the issue at this React Reader level? Basically the text selection should be triggered only on mouseup.

I see a possible solution here: https://github.com/johnfactotum/foliate/commit/6b5cef3a3273bc22c2f2241d51100212826bd3e9

but I wouldn´t know how to have that working from the example script.

gerhardsletten commented 1 year ago

@fciannella Not used this function myself, but in the example here hightlight-selection-in-epub you have access to contents and can apply the same code as example in foliate I guess

function setRenderSelection(cfiRange, contents) {
}
j-d90 commented 1 year ago

Here's an approach that I tried out from the foliate example that seems to work based on a little testing. Just wrapping the existing logic in a mouseup handler on the contents.document object. Very simple change from what was in the example before, and behaves more like what you'd expect from a select-based feature:

useEffect(() => {
    if (renditionRef.current) {
      function setRenderSelection(cfiRange, contents) {
        contents.document.onmouseup = function () {
          setSelections(
            selections.concat({
              text: renditionRef.current.getRange(cfiRange).toString(),
              cfiRange
            })
          )
          renditionRef.current.annotations.add(
            'highlight',
            cfiRange,
            {},
            null,
            'hl',
            { fill: 'red', 'fill-opacity': '0.5', 'mix-blend-mode': 'multiply' }
          )
          contents.window.getSelection().removeAllRanges()
        }
      }
      renditionRef.current.on('selected', setRenderSelection)
      return () => {
        renditionRef.current.off('selected', setRenderSelection)
      }
    }
  }, [setSelections, selections])