├── .babelrc
├── .gitignore
├── .npmignore
├── .vscode
└── settings.json
├── README.md
├── lib
└── Highlighter.js
├── package-lock.json
├── package.json
├── src
└── Highlighter.jsx
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/react",
4 | "@babel/env"
5 | ]
6 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 | .env.test
60 |
61 | # parcel-bundler cache (https://parceljs.org/)
62 | .cache
63 |
64 | # next.js build output
65 | .next
66 |
67 | # nuxt.js build output
68 | .nuxt
69 |
70 | # vuepress build output
71 | .vuepress/dist
72 |
73 | # Serverless directories
74 | .serverless/
75 |
76 | # FuseBox cache
77 | .fusebox/
78 |
79 | # DynamoDB Local files
80 | .dynamodb/
81 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | src
2 | demo
3 | .babelrc
4 | webpack.config.js
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "terminal.integrated.fontSize": 18
3 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Highlight Selection
2 |
3 | React component to highlight and retrieve text selected with mouse.
4 |
5 | ## Installation
6 | ```
7 | npm i react-highlight-selection
8 | ```
9 |
10 | ## Usage
11 | [](https://codesandbox.io/s/y0vw15o39j)
12 |
13 | - Provide required **text** prop. This will be the text contained inside the component.
14 | - Highlight the required portion of text by dragging the mouse over it with left-button pressed. The text would be highlighted on button's release
15 | - Optionally, provide a custom class to give your own background color or any other styling.
16 | - Optionally, provide a call back function **selectionHandler**. An object containing the following will be returned to the function
17 | - selected text
18 | - selection start index
19 | - selection end index
20 |
21 | ```css
22 | .custom-class {
23 | background-color: #e06f6f
24 | }
25 | ```
26 |
27 | ```js
28 | import React, { Component } from 'react';
29 | import ReactDOM from 'react-dom';
30 | import SelectionHighlighter from 'react-highlight-selection';
31 | import './App.css';
32 |
33 | class App extends Component {
34 | constructor() {
35 | super();
36 | this.selectionHandler = this.selectionHandler.bind(this);
37 | }
38 |
39 | selectionHandler(selection) {
40 | //do something with selection
41 | console.log(selection);
42 |
43 | }
44 | render() {
45 | const text = "Let there be light, let there be Sun.";
46 | return (
47 |
52 | );
53 | }
54 | }
55 |
56 | ReactDOM.render(, document.getElementById('root'));
57 |
58 |
59 | ```
60 |
61 | ## What's unique
62 | - It's a very simple and lightweight component.
63 | - Most other existing components highlight only given text (somewhat like search/replace). This component, however, helps in retrieving dynamic selection done with the mouse.
64 |
65 |
66 |
--------------------------------------------------------------------------------
/lib/Highlighter.js:
--------------------------------------------------------------------------------
1 | module.exports=function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=2)}([function(e,t,r){"use strict";e.exports=r(3)},function(e,t,r){e.exports=r(5)()},function(e,t,r){"use strict";r.r(t),r.d(t,"default",function(){return d});var n=r(0),o=r.n(n),i=r(1),u=r.n(i);function a(e){return(a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function c(e,t){for(var r=0;r0);var l=c?i:u;c?(n.parentNode.getAttribute("data-order")&&"middle"===n.parentNode.getAttribute("data-order")&&(l+=this.state.selectionStart),n.parentNode.getAttribute("data-order")&&"last"===n.parentNode.getAttribute("data-order")&&(l+=this.state.selectionEnd)):(o.parentNode.getAttribute("data-order")&&"middle"===o.parentNode.getAttribute("data-order")&&(l+=this.state.selectionStart),o.parentNode.getAttribute("data-order")&&"last"===o.parentNode.getAttribute("data-order")&&(l+=this.state.selectionEnd));var f=l+r.length,s=this.state.text.slice(0,l),p=this.state.text.slice(l,f),d=this.state.text.slice(f);this.setState({selection:r,anchorNode:n,focusNode:o,selectionStart:l,selectionEnd:f,first:s,middle:p,last:d}),this.props.selectionHandler&&this.props.selectionHandler({selection:r,selectionStart:l,selectionEnd:f})}},{key:"render",value:function(){return this.state.selection?o.a.createElement("span",{onMouseUp:this.onMouseUpHandler},o.a.createElement("span",{"data-order":"first"},this.state.first),o.a.createElement("span",{"data-order":"middle",className:this.props.customClass||"default"},this.state.middle),o.a.createElement("span",{"data-order":"last"},this.state.last)):o.a.createElement("span",{onMouseUp:this.onMouseUpHandler},this.state.text)}}])&&c(r.prototype,i),u&&c(r,u),t}();d.propTypes=p},function(e,t,r){"use strict";
2 | /** @license React v16.7.0
3 | * react.production.min.js
4 | *
5 | * Copyright (c) Facebook, Inc. and its affiliates.
6 | *
7 | * This source code is licensed under the MIT license found in the
8 | * LICENSE file in the root directory of this source tree.
9 | */var n=r(4),o="function"==typeof Symbol&&Symbol.for,i=o?Symbol.for("react.element"):60103,u=o?Symbol.for("react.portal"):60106,a=o?Symbol.for("react.fragment"):60107,c=o?Symbol.for("react.strict_mode"):60108,l=o?Symbol.for("react.profiler"):60114,f=o?Symbol.for("react.provider"):60109,s=o?Symbol.for("react.context"):60110,p=o?Symbol.for("react.concurrent_mode"):60111,d=o?Symbol.for("react.forward_ref"):60112,y=o?Symbol.for("react.suspense"):60113,b=o?Symbol.for("react.memo"):60115,h=o?Symbol.for("react.lazy"):60116,m="function"==typeof Symbol&&Symbol.iterator;function v(e){for(var t=arguments.length-1,r="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=0;n$.length&&$.push(e)}function A(e,t,r){return null==e?0:function e(t,r,n,o){var a=typeof t;"undefined"!==a&&"boolean"!==a||(t=null);var c=!1;if(null===t)c=!0;else switch(a){case"string":case"number":c=!0;break;case"object":switch(t.$$typeof){case i:case u:c=!0}}if(c)return n(o,t,""===r?"."+M(t,0):r),1;if(c=0,r=""===r?".":r+":",Array.isArray(t))for(var l=0;l 0;
52 | }
53 |
54 | let selectionStart = forward ? anchorOffset : focusOffset;
55 |
56 | if (forward) {
57 | if (anchorNode.parentNode.getAttribute('data-order')
58 | && anchorNode.parentNode.getAttribute('data-order') === 'middle') {
59 | selectionStart += this.state.selectionStart;
60 | }
61 | if (anchorNode.parentNode.getAttribute('data-order')
62 | && anchorNode.parentNode.getAttribute('data-order') === 'last') {
63 | selectionStart += this.state.selectionEnd;
64 | }
65 | } else {
66 | if (focusNode.parentNode.getAttribute('data-order')
67 | && focusNode.parentNode.getAttribute('data-order') === 'middle') {
68 | selectionStart += this.state.selectionStart;
69 | }
70 | if (focusNode.parentNode.getAttribute('data-order')
71 | && focusNode.parentNode.getAttribute('data-order') === 'last') {
72 | selectionStart += this.state.selectionEnd;
73 | }
74 | }
75 |
76 | const selectionEnd = selectionStart + selection.length;
77 | const first = this.state.text.slice(0, selectionStart);
78 | const middle = this.state.text.slice(selectionStart, selectionEnd);
79 | const last = this.state.text.slice(selectionEnd);
80 |
81 | this.setState({
82 | selection,
83 | anchorNode,
84 | focusNode,
85 | selectionStart,
86 | selectionEnd,
87 | first,
88 | middle,
89 | last
90 | });
91 |
92 | if (this.props.selectionHandler) {
93 | this.props.selectionHandler({
94 | selection,
95 | selectionStart,
96 | selectionEnd
97 | });
98 | }
99 |
100 | }
101 |
102 | render() {
103 | if (!this.state.selection) {
104 | return (
105 | {this.state.text}
107 |
108 | )
109 | } else {
110 | return (
111 |
113 |
115 | {this.state.first}
116 |
117 |
120 | {this.state.middle}
121 |
122 |
124 | {this.state.last}
125 |
126 |
127 | )
128 | }
129 |
130 | }
131 | }
132 |
133 | HighLighter.propTypes = propTypes;
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 |
3 | module.exports = {
4 | mode: 'production',
5 | entry: './src/Highlighter.jsx',
6 | output: {
7 | path: path.resolve('lib'),
8 | filename: 'Highlighter.js',
9 | libraryTarget: 'commonjs2'
10 | },
11 | module: {
12 | rules: [
13 | {
14 | test: /\.jsx?$/,
15 | exclude: /(node_modules)/,
16 | use: 'babel-loader'
17 | }
18 | ]
19 | }
20 | }
--------------------------------------------------------------------------------