react-text-range 1.0.11 → 1.0.13

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
@@ -1,6 +1,6 @@
1
1
  # react-text-range
2
2
 
3
- ![cast](https://github.com/yetanothervan/react-text-range/assets/5338279/a6b79ef7-3cc1-4724-9ac4-1163e2f26fc1)
3
+ ![cast2](https://github.com/yetanothervan/react-text-range/assets/5338279/f5977266-4de9-412f-8f8c-e7639e8d6bc8)
4
4
 
5
5
  ## using
6
6
 
@@ -9,14 +9,7 @@
9
9
  import { TextContainer, RangeState, ReactTextRange } from "./ReactTextRange";
10
10
 
11
11
  const MyTextContainer: TextContainer = React.forwardRef(({ children }, ref) =>
12
- <div ref={ref} style={{
13
- fontSize: 24,
14
- width: '320px',
15
- backgroundColor: 'rgba(253, 224, 71, .2)',
16
- userSelect: 'none',
17
- padding: '20px',
18
- whiteSpace: 'pre-wrap',
19
- }}>
12
+ <div ref={ref} className="text-2xl text-gray-300 w-80 bg-yellow-100 select-none p-5 whitespace-pre-wrap">
20
13
  {children}
21
14
  </div>
22
15
  );
@@ -27,7 +20,8 @@ const App: FunctionComponent = () => {
27
20
  <div style={{ margin: 20 }}>
28
21
  <ReactTextRange initLeftPos={23} initRightPos={47}
29
22
  Container={MyTextContainer} onChange={setMyPos}
30
- selectionColor='yellow' handlerWidth={18}>{
23
+ handlerWidth={18}
24
+ selectionClass='bg-yellow-300 text-black'>{
31
25
  `Some text
32
26
  or even some real good multiline text
33
27
  here and there`}
@@ -41,3 +35,5 @@ here and there`}
41
35
  )
42
36
  }
43
37
  ```
38
+
39
+ You can set selectionClass, headClass, tailClass, leftHandlerClass and RightHandlerClass in ReactTextRange
@@ -15,9 +15,11 @@ export declare const ReactTextRange: FC<{
15
15
  onChange: (state: RangeState) => void;
16
16
  props?: React.CSSProperties;
17
17
  className?: string;
18
- selectionColor?: string;
19
18
  handlerWidth?: number;
20
19
  leftHandlerClass?: string;
21
20
  rightHandlerClass?: string;
21
+ headClass?: string;
22
+ selectionClass?: string;
23
+ tailClass?: string;
22
24
  }>;
23
25
  export default ReactTextRange;
@@ -6,7 +6,6 @@ export declare const SelectionHandler: FC<{
6
6
  grab: boolean;
7
7
  setGrab: (value: boolean) => void;
8
8
  left: boolean;
9
- bgColor?: string;
10
9
  width?: number;
11
10
  className?: string;
12
11
  }>;
@@ -1,14 +1,12 @@
1
1
  import React from 'react';
2
2
  import { HandlerPos } from './handler-pos';
3
- import { Rect } from './rect';
4
3
  declare global {
5
4
  interface Document {
6
5
  caretPositionFromPoint: any;
7
6
  }
8
7
  }
9
- export declare const useTextSelectionEditor: (initLeftPos: number, initRightPos: number, leftDrag: boolean, rightDrag: boolean) => [
8
+ export declare const useTextSelectionEditor: (initLeftPos: number, initRightPos: number, leftDrag: boolean, rightDrag: boolean, headClass?: string, selectionClass?: string, tailClass?: string) => [
10
9
  React.MutableRefObject<HTMLDivElement | null>,
11
10
  HandlerPos | null,
12
- HandlerPos | null,
13
- Rect[] | null
11
+ HandlerPos | null
14
12
  ];
package/dist/cjs/index.js CHANGED
@@ -21,27 +21,53 @@ function _interopNamespaceDefault(e) {
21
21
 
22
22
  var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
23
23
 
24
- const getHandlerRect = (index, node) => {
25
- const range = document.createRange();
26
- range.setStart(node, index);
27
- range.setEnd(node, index);
28
- if (range.getClientRects) {
29
- const rects = range.getClientRects();
30
- if (rects.length >= 1) {
31
- return rects[0];
24
+ const getHandlerRect = (node, left) => {
25
+ var _a, _b, _c, _d, _e, _f;
26
+ if (!node || !node.childNodes || node.childNodes.length != 6)
27
+ return null;
28
+ const headLength = (_b = (_a = node.childNodes[1].firstChild) === null || _a === void 0 ? void 0 : _a.nodeValue) === null || _b === void 0 ? void 0 : _b.length;
29
+ const selLength = (_d = (_c = node.childNodes[3].firstChild) === null || _c === void 0 ? void 0 : _c.nodeValue) === null || _d === void 0 ? void 0 : _d.length;
30
+ const tailLength = (_f = (_e = node.childNodes[5].firstChild) === null || _e === void 0 ? void 0 : _e.nodeValue) === null || _f === void 0 ? void 0 : _f.length;
31
+ if (left) {
32
+ const range = document.createRange();
33
+ if (selLength > 0) {
34
+ range.selectNodeContents(node.childNodes[3]);
32
35
  }
33
- }
34
- return null;
35
- };
36
- const getAllRects = (node, start, end) => {
37
- const range = document.createRange();
38
- range.setStart(node, start);
39
- range.setEnd(node, end);
40
- if (range.getClientRects) {
41
- return range.getClientRects();
36
+ else if (tailLength > 0) {
37
+ range.selectNodeContents(node.childNodes[5]);
38
+ }
39
+ else {
40
+ range.setStart(node.childNodes[1].childNodes[0], headLength);
41
+ range.setEnd(node.childNodes[1].childNodes[0], headLength);
42
+ }
43
+ if (range.getClientRects) {
44
+ const rects = range.getClientRects();
45
+ if (rects.length >= 1) {
46
+ return rects[0];
47
+ }
48
+ }
49
+ return null;
42
50
  }
43
51
  else {
44
- return new DOMRectList();
52
+ const range = document.createRange();
53
+ if (tailLength > 0) {
54
+ range.selectNodeContents(node.childNodes[5]);
55
+ }
56
+ else if (selLength > 0) {
57
+ range.setStart(node.childNodes[3].childNodes[0], selLength);
58
+ range.setEnd(node.childNodes[3].childNodes[0], selLength);
59
+ }
60
+ else {
61
+ range.setStart(node.childNodes[1].childNodes[0], headLength);
62
+ range.setEnd(node.childNodes[1].childNodes[0], headLength);
63
+ }
64
+ if (range.getClientRects) {
65
+ const rects = range.getClientRects();
66
+ if (rects.length >= 1) {
67
+ return rects[0];
68
+ }
69
+ }
70
+ return null;
45
71
  }
46
72
  };
47
73
  const getNodeAndOffsetFromPoint = (x, y) => {
@@ -70,73 +96,145 @@ const getNodeAndOffsetFromPoint = (x, y) => {
70
96
  }
71
97
  return null;
72
98
  };
73
- const useTextSelectionEditor = (initLeftPos, initRightPos, leftDrag, rightDrag) => {
99
+ const useTextSelectionEditor = (initLeftPos, initRightPos, leftDrag, rightDrag, headClass, selectionClass, tailClass) => {
74
100
  // left handler pos
75
101
  const [leftHandler, setLeftHandler] = React.useState(null);
76
102
  const [currentLeftPos, setCurrentLeftPos] = React.useState(initLeftPos);
77
103
  // right handler pos
78
104
  const [rightHandler, setRightHandler] = React.useState(null);
79
105
  const [currentRightPos, setCurrentRightPos] = React.useState(initRightPos);
80
- const [rects, setRects] = React.useState(null);
81
106
  // reference
82
107
  const textDiv = React.useRef(null);
83
- React.useEffect(() => {
108
+ React.useLayoutEffect(() => {
84
109
  if (textDiv.current) {
85
110
  textDiv.current.style.position = 'relative';
86
111
  }
87
112
  }, [textDiv]);
88
- // mouse move handler
89
- // left handler
90
- React.useEffect(() => {
91
- const handlerMove = (e) => {
92
- var _a;
93
- if (!leftDrag)
94
- return;
95
- const sm = getNodeAndOffsetFromPoint(e.clientX, e.clientY);
96
- if (sm
97
- && sm.node === ((_a = textDiv.current) === null || _a === void 0 ? void 0 : _a.childNodes[0])
98
- && sm.offset <= currentRightPos) {
99
- setCurrentLeftPos(sm.offset);
113
+ // break text into three spans
114
+ React.useLayoutEffect(() => {
115
+ var _a, _b, _c;
116
+ let textLeftNode = (_a = textDiv.current) === null || _a === void 0 ? void 0 : _a.childNodes[0];
117
+ if (!textLeftNode || textLeftNode.nodeType !== document.TEXT_NODE || textLeftNode.nodeValue === null) {
118
+ return;
119
+ }
120
+ const head = document.createRange();
121
+ head.setStart(textLeftNode, 0);
122
+ head.setEnd(textLeftNode, currentLeftPos);
123
+ const headSpan = document.createElement('span');
124
+ if (headClass)
125
+ headSpan.classList.value = headClass;
126
+ head.surroundContents(headSpan);
127
+ textLeftNode = (_b = textDiv.current) === null || _b === void 0 ? void 0 : _b.childNodes[2];
128
+ if (!textLeftNode
129
+ || !textLeftNode.nodeValue
130
+ || textLeftNode.nodeValue.length < currentRightPos - currentLeftPos)
131
+ return;
132
+ const selection = document.createRange();
133
+ selection.setStart(textLeftNode, 0);
134
+ selection.setEnd(textLeftNode, currentRightPos - currentLeftPos);
135
+ const selectionSpan = document.createElement('span');
136
+ if (selectionClass)
137
+ selectionSpan.classList.value = selectionClass;
138
+ selection.surroundContents(selectionSpan);
139
+ textLeftNode = (_c = textDiv.current) === null || _c === void 0 ? void 0 : _c.childNodes[4];
140
+ if (!textLeftNode)
141
+ return;
142
+ const tail = document.createRange();
143
+ tail.setStart(textLeftNode, 0);
144
+ tail.setEndAfter(textLeftNode);
145
+ const tailSpan = document.createElement('span');
146
+ if (tailClass)
147
+ tailSpan.classList.value = tailClass;
148
+ tail.surroundContents(tailSpan);
149
+ return () => {
150
+ if (textDiv.current) {
151
+ textDiv.current.childNodes[0].nodeValue = textDiv.current.textContent;
152
+ while (textDiv.current.childNodes.length > 1 && textDiv.current.lastChild) {
153
+ textDiv.current.removeChild(textDiv.current.lastChild);
154
+ }
100
155
  }
101
156
  };
102
- document.addEventListener('mousemove', handlerMove);
157
+ }, [textDiv.current]);
158
+ // mouse move handler
159
+ // left handler
160
+ const leftMoveHandler = React.useCallback((e) => {
161
+ var _a, _b;
162
+ const sm = getNodeAndOffsetFromPoint(e.clientX, e.clientY);
163
+ if (!sm)
164
+ return;
165
+ let posToSet = currentLeftPos;
166
+ if (sm.node === ((_a = textDiv.current) === null || _a === void 0 ? void 0 : _a.childNodes[1].firstChild)) {
167
+ posToSet = sm.offset;
168
+ }
169
+ else if (sm.node === ((_b = textDiv.current) === null || _b === void 0 ? void 0 : _b.childNodes[3].firstChild)) {
170
+ posToSet = currentLeftPos + sm.offset;
171
+ }
172
+ if (posToSet !== currentLeftPos) {
173
+ const headText = textDiv.current.childNodes[1].firstChild.nodeValue;
174
+ const selText = textDiv.current.childNodes[3].firstChild.nodeValue;
175
+ const full = headText + selText;
176
+ textDiv.current.childNodes[1].firstChild.nodeValue = full.substring(0, posToSet);
177
+ textDiv.current.childNodes[3].firstChild.nodeValue = full.substring(posToSet);
178
+ setCurrentLeftPos(posToSet);
179
+ }
180
+ }, [currentLeftPos, textDiv.current]);
181
+ React.useLayoutEffect(() => {
182
+ if (!leftDrag) {
183
+ document.removeEventListener('mousemove', leftMoveHandler);
184
+ }
185
+ else {
186
+ document.addEventListener('mousemove', leftMoveHandler);
187
+ }
103
188
  return () => {
104
- document.removeEventListener('mousemove', handlerMove);
189
+ document.removeEventListener('mousemove', leftMoveHandler);
105
190
  };
106
- }, [leftDrag, currentRightPos]);
107
- React.useEffect(() => {
191
+ }, [leftDrag, currentLeftPos, textDiv.current]);
192
+ React.useLayoutEffect(() => {
108
193
  setCurrentLeftPos(initLeftPos);
109
194
  }, [initLeftPos]);
110
195
  // right handler
111
- React.useEffect(() => {
112
- const handlerMove = (e) => {
113
- var _a;
114
- if (!rightDrag)
115
- return;
116
- const sm = getNodeAndOffsetFromPoint(e.clientX, e.clientY);
117
- if (sm
118
- && sm.node === ((_a = textDiv.current) === null || _a === void 0 ? void 0 : _a.childNodes[0])
119
- && sm.offset >= currentLeftPos) {
120
- setCurrentRightPos(sm.offset);
121
- }
122
- };
123
- document.addEventListener('mousemove', handlerMove);
196
+ const rightMoveHandler = React.useCallback((e) => {
197
+ var _a, _b;
198
+ const sm = getNodeAndOffsetFromPoint(e.clientX, e.clientY);
199
+ if (!sm)
200
+ return;
201
+ let posToSet = currentRightPos;
202
+ if (sm.node === ((_a = textDiv.current) === null || _a === void 0 ? void 0 : _a.childNodes[3].firstChild)) {
203
+ posToSet = currentLeftPos + sm.offset;
204
+ }
205
+ else if (sm.node === ((_b = textDiv.current) === null || _b === void 0 ? void 0 : _b.childNodes[5].firstChild)) {
206
+ posToSet = currentRightPos + sm.offset;
207
+ }
208
+ if (posToSet !== currentRightPos) {
209
+ const selText = textDiv.current.childNodes[3].firstChild.nodeValue;
210
+ const tailText = textDiv.current.childNodes[5].firstChild.nodeValue;
211
+ const full = selText + tailText;
212
+ textDiv.current.childNodes[3].firstChild.nodeValue = full.substring(0, posToSet - currentLeftPos);
213
+ textDiv.current.childNodes[5].firstChild.nodeValue = full.substring(posToSet - currentLeftPos);
214
+ setCurrentRightPos(posToSet);
215
+ }
216
+ }, [currentLeftPos, currentRightPos, textDiv.current]);
217
+ React.useLayoutEffect(() => {
218
+ if (!rightDrag) {
219
+ document.removeEventListener('mousemove', rightMoveHandler);
220
+ }
221
+ else {
222
+ document.addEventListener('mousemove', rightMoveHandler);
223
+ }
124
224
  return () => {
125
- document.removeEventListener('mousemove', handlerMove);
225
+ document.removeEventListener('mousemove', rightMoveHandler);
126
226
  };
127
- }, [rightDrag, currentLeftPos]);
128
- React.useEffect(() => {
227
+ }, [rightDrag, currentLeftPos, currentRightPos, textDiv.current]);
228
+ React.useLayoutEffect(() => {
129
229
  setCurrentRightPos(initRightPos);
130
230
  }, [initRightPos]);
131
231
  // draw init left handler
132
- React.useEffect(() => {
232
+ React.useLayoutEffect(() => {
133
233
  if (textDiv.current
134
- && textDiv.current.childNodes.length === 1
135
- && textDiv.current.childNodes[0].nodeType === document.TEXT_NODE) {
136
- const rect = getHandlerRect(currentLeftPos, textDiv.current.childNodes[0]);
234
+ && textDiv.current.childNodes.length === 6) {
235
+ const rect = getHandlerRect(textDiv.current, true);
137
236
  if (rect === null) {
138
237
  setLeftHandler(null);
139
- setRects(null);
140
238
  }
141
239
  else {
142
240
  const divRect = textDiv.current.getBoundingClientRect();
@@ -150,14 +248,12 @@ const useTextSelectionEditor = (initLeftPos, initRightPos, leftDrag, rightDrag)
150
248
  }
151
249
  }, [currentLeftPos]);
152
250
  // draw init right handler
153
- React.useEffect(() => {
251
+ React.useLayoutEffect(() => {
154
252
  if (textDiv.current
155
- && textDiv.current.childNodes.length === 1
156
- && textDiv.current.childNodes[0].nodeType === document.TEXT_NODE) {
157
- const rect = getHandlerRect(currentRightPos, textDiv.current.childNodes[0]);
253
+ && textDiv.current.childNodes.length === 6) {
254
+ const rect = getHandlerRect(textDiv.current, false);
158
255
  if (rect === null) {
159
256
  setRightHandler(null);
160
- setRects(null);
161
257
  }
162
258
  else {
163
259
  const divRect = textDiv.current.getBoundingClientRect();
@@ -170,32 +266,8 @@ const useTextSelectionEditor = (initLeftPos, initRightPos, leftDrag, rightDrag)
170
266
  }
171
267
  }
172
268
  }, [currentRightPos]);
173
- React.useEffect(() => {
174
- var _a;
175
- const n = (_a = textDiv.current) === null || _a === void 0 ? void 0 : _a.childNodes[0];
176
- if (!n) {
177
- setRects(null);
178
- return;
179
- }
180
- const rawRects = getAllRects(n, currentLeftPos, currentRightPos);
181
- let rectArray = [];
182
- if (rawRects && textDiv.current) {
183
- const divRect = textDiv.current.getBoundingClientRect();
184
- for (let i = 0; i < rawRects.length; ++i) {
185
- const aa = rawRects.item(i);
186
- if (aa)
187
- rectArray.push({
188
- height: aa.height,
189
- left: aa.left - divRect.left,
190
- top: aa.top - divRect.top,
191
- width: aa.width,
192
- });
193
- }
194
- }
195
- setRects(rectArray);
196
- }, [currentLeftPos, currentRightPos]);
197
269
  // return
198
- return [textDiv, leftHandler, rightHandler, rects];
270
+ return [textDiv, leftHandler, rightHandler];
199
271
  };
200
272
 
201
273
  var _path$1;
@@ -247,11 +319,10 @@ function styleInject(css, ref) {
247
319
  }
248
320
  }
249
321
 
250
- var css_248z = "/*! tailwindcss v3.3.3 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:\"\"}html{-webkit-text-size-adjust:100%;font-feature-settings:normal;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-variation-settings:normal;line-height:1.5;-moz-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{font-feature-settings:inherit;color:inherit;font-family:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.absolute{position:absolute}.relative{position:relative}.flex{display:flex}.rounded-l-md{border-bottom-left-radius:.375rem;border-top-left-radius:.375rem}.rounded-r-md{border-bottom-right-radius:.375rem;border-top-right-radius:.375rem}";
322
+ var css_248z = "/*! tailwindcss v3.3.3 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:\"\"}html{-webkit-text-size-adjust:100%;font-feature-settings:normal;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-variation-settings:normal;line-height:1.5;-moz-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{font-feature-settings:inherit;color:inherit;font-family:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.absolute{position:absolute}.relative{position:relative}.flex{display:flex}.w-80{width:20rem}.select-none{user-select:none}.whitespace-pre-wrap{white-space:pre-wrap}.rounded-l-md{border-bottom-left-radius:.375rem;border-top-left-radius:.375rem}.rounded-r-md{border-bottom-right-radius:.375rem;border-top-right-radius:.375rem}.bg-yellow-100{--tw-bg-opacity:1;background-color:rgb(254 249 195/var(--tw-bg-opacity))}.bg-yellow-300{--tw-bg-opacity:1;background-color:rgb(253 224 71/var(--tw-bg-opacity))}.p-5{padding:1.25rem}.text-2xl{font-size:1.5rem;line-height:2rem}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}";
251
323
  styleInject(css_248z,{"insertAt":"top"});
252
324
 
253
- const SelectionHandler = ({ pos, grab, setGrab, left, bgColor, width, className }) => {
254
- const bgColorDef = bgColor !== null && bgColor !== void 0 ? bgColor : 'rgb(253 224 71)';
325
+ const SelectionHandler = ({ pos, grab, setGrab, left, width, className }) => {
255
326
  const widthDef = width !== null && width !== void 0 ? width : 25;
256
327
  return (pos &&
257
328
  React.createElement("div", { draggable: false, className: `${left ? 'rounded-l-md' : 'rounded-r-md'} ${className}`, style: {
@@ -263,7 +334,6 @@ const SelectionHandler = ({ pos, grab, setGrab, left, bgColor, width, className
263
334
  height: pos.height,
264
335
  cursor: grab ? 'grabbing' : 'grab',
265
336
  alignItems: left ? 'flex-start' : 'flex-end',
266
- backgroundColor: bgColorDef,
267
337
  }, onMouseDown: () => {
268
338
  setGrab(true);
269
339
  const handler = () => {
@@ -278,10 +348,10 @@ const SelectionHandler = ({ pos, grab, setGrab, left, bgColor, width, className
278
348
  : React.createElement(SvgQuoteRight, null)));
279
349
  };
280
350
 
281
- const ReactTextRange = ({ initLeftPos, initRightPos, Container, children, onChange, props, selectionColor, handlerWidth, className, leftHandlerClass, rightHandlerClass, }) => {
351
+ const ReactTextRange = ({ initLeftPos, initRightPos, Container, children, onChange, props, handlerWidth, className, leftHandlerClass, rightHandlerClass, headClass, selectionClass, tailClass, }) => {
282
352
  const [mouseOnLeft, setMouseOnLeft] = React.useState(false);
283
353
  const [mouseOnRight, setMouseOnRight] = React.useState(false);
284
- const [textDiv, leftHandler, rightHandler, rects] = useTextSelectionEditor(initLeftPos, initRightPos, mouseOnLeft, mouseOnRight);
354
+ const [textDiv, leftHandler, rightHandler] = useTextSelectionEditor(initLeftPos, initRightPos, mouseOnLeft, mouseOnRight, headClass, selectionClass, tailClass);
285
355
  React.useEffect(() => {
286
356
  if (leftHandler && rightHandler) {
287
357
  onChange({
@@ -290,29 +360,10 @@ const ReactTextRange = ({ initLeftPos, initRightPos, Container, children, onChan
290
360
  });
291
361
  }
292
362
  }, [leftHandler, rightHandler]);
293
- const bgColor = selectionColor;
294
363
  return (React.createElement("div", { className: className, draggable: false, style: Object.assign({ position: 'relative' }, props) },
295
- React.createElement(SelectionRects, { rects: rects, bgColor: bgColor }),
296
364
  React.createElement(Container, { ref: textDiv }, children),
297
- React.createElement(SelectionHandler, { className: leftHandlerClass, bgColor: bgColor, width: handlerWidth, grab: mouseOnLeft, left: true, pos: leftHandler, setGrab: (v) => setMouseOnLeft(v) }),
298
- React.createElement(SelectionHandler, { className: rightHandlerClass, bgColor: bgColor, width: handlerWidth, grab: mouseOnRight, left: false, pos: rightHandler, setGrab: (v) => setMouseOnRight(v) })));
299
- };
300
- const SelectionRects = ({ rects, bgColor }) => {
301
- if (!rects)
302
- return null;
303
- return (React.createElement(React.Fragment, null, rects.map((d, i) => React.createElement(SelectionRect, { key: i, rect: d, bgColor: bgColor }))));
304
- };
305
- const SelectionRect = ({ rect, bgColor }) => {
306
- const bgColorDef = bgColor !== null && bgColor !== void 0 ? bgColor : 'rgb(253 224 71)';
307
- return (React.createElement("div", { style: {
308
- userSelect: 'none',
309
- position: 'absolute',
310
- top: `${rect.top}px`,
311
- left: `${rect.left}px`,
312
- width: `${rect.width}px`,
313
- height: `${rect.height}px`,
314
- backgroundColor: bgColorDef,
315
- } }, "\u00A0"));
365
+ React.createElement(SelectionHandler, { className: leftHandlerClass, width: handlerWidth, grab: mouseOnLeft, left: true, pos: leftHandler, setGrab: (v) => setMouseOnLeft(v) }),
366
+ React.createElement(SelectionHandler, { className: rightHandlerClass, width: handlerWidth, grab: mouseOnRight, left: false, pos: rightHandler, setGrab: (v) => setMouseOnRight(v) })));
316
367
  };
317
368
 
318
369
  exports.ReactTextRange = ReactTextRange;
@@ -15,9 +15,11 @@ export declare const ReactTextRange: FC<{
15
15
  onChange: (state: RangeState) => void;
16
16
  props?: React.CSSProperties;
17
17
  className?: string;
18
- selectionColor?: string;
19
18
  handlerWidth?: number;
20
19
  leftHandlerClass?: string;
21
20
  rightHandlerClass?: string;
21
+ headClass?: string;
22
+ selectionClass?: string;
23
+ tailClass?: string;
22
24
  }>;
23
25
  export default ReactTextRange;
@@ -6,7 +6,6 @@ export declare const SelectionHandler: FC<{
6
6
  grab: boolean;
7
7
  setGrab: (value: boolean) => void;
8
8
  left: boolean;
9
- bgColor?: string;
10
9
  width?: number;
11
10
  className?: string;
12
11
  }>;
package/dist/esm/index.js CHANGED
@@ -1,27 +1,53 @@
1
1
  import * as React from 'react';
2
- import React__default, { useState, useRef, useEffect } from 'react';
2
+ import React__default, { useState, useRef, useLayoutEffect, useCallback, useEffect } from 'react';
3
3
 
4
- const getHandlerRect = (index, node) => {
5
- const range = document.createRange();
6
- range.setStart(node, index);
7
- range.setEnd(node, index);
8
- if (range.getClientRects) {
9
- const rects = range.getClientRects();
10
- if (rects.length >= 1) {
11
- return rects[0];
4
+ const getHandlerRect = (node, left) => {
5
+ var _a, _b, _c, _d, _e, _f;
6
+ if (!node || !node.childNodes || node.childNodes.length != 6)
7
+ return null;
8
+ const headLength = (_b = (_a = node.childNodes[1].firstChild) === null || _a === void 0 ? void 0 : _a.nodeValue) === null || _b === void 0 ? void 0 : _b.length;
9
+ const selLength = (_d = (_c = node.childNodes[3].firstChild) === null || _c === void 0 ? void 0 : _c.nodeValue) === null || _d === void 0 ? void 0 : _d.length;
10
+ const tailLength = (_f = (_e = node.childNodes[5].firstChild) === null || _e === void 0 ? void 0 : _e.nodeValue) === null || _f === void 0 ? void 0 : _f.length;
11
+ if (left) {
12
+ const range = document.createRange();
13
+ if (selLength > 0) {
14
+ range.selectNodeContents(node.childNodes[3]);
12
15
  }
13
- }
14
- return null;
15
- };
16
- const getAllRects = (node, start, end) => {
17
- const range = document.createRange();
18
- range.setStart(node, start);
19
- range.setEnd(node, end);
20
- if (range.getClientRects) {
21
- return range.getClientRects();
16
+ else if (tailLength > 0) {
17
+ range.selectNodeContents(node.childNodes[5]);
18
+ }
19
+ else {
20
+ range.setStart(node.childNodes[1].childNodes[0], headLength);
21
+ range.setEnd(node.childNodes[1].childNodes[0], headLength);
22
+ }
23
+ if (range.getClientRects) {
24
+ const rects = range.getClientRects();
25
+ if (rects.length >= 1) {
26
+ return rects[0];
27
+ }
28
+ }
29
+ return null;
22
30
  }
23
31
  else {
24
- return new DOMRectList();
32
+ const range = document.createRange();
33
+ if (tailLength > 0) {
34
+ range.selectNodeContents(node.childNodes[5]);
35
+ }
36
+ else if (selLength > 0) {
37
+ range.setStart(node.childNodes[3].childNodes[0], selLength);
38
+ range.setEnd(node.childNodes[3].childNodes[0], selLength);
39
+ }
40
+ else {
41
+ range.setStart(node.childNodes[1].childNodes[0], headLength);
42
+ range.setEnd(node.childNodes[1].childNodes[0], headLength);
43
+ }
44
+ if (range.getClientRects) {
45
+ const rects = range.getClientRects();
46
+ if (rects.length >= 1) {
47
+ return rects[0];
48
+ }
49
+ }
50
+ return null;
25
51
  }
26
52
  };
27
53
  const getNodeAndOffsetFromPoint = (x, y) => {
@@ -50,73 +76,145 @@ const getNodeAndOffsetFromPoint = (x, y) => {
50
76
  }
51
77
  return null;
52
78
  };
53
- const useTextSelectionEditor = (initLeftPos, initRightPos, leftDrag, rightDrag) => {
79
+ const useTextSelectionEditor = (initLeftPos, initRightPos, leftDrag, rightDrag, headClass, selectionClass, tailClass) => {
54
80
  // left handler pos
55
81
  const [leftHandler, setLeftHandler] = useState(null);
56
82
  const [currentLeftPos, setCurrentLeftPos] = useState(initLeftPos);
57
83
  // right handler pos
58
84
  const [rightHandler, setRightHandler] = useState(null);
59
85
  const [currentRightPos, setCurrentRightPos] = useState(initRightPos);
60
- const [rects, setRects] = useState(null);
61
86
  // reference
62
87
  const textDiv = useRef(null);
63
- useEffect(() => {
88
+ useLayoutEffect(() => {
64
89
  if (textDiv.current) {
65
90
  textDiv.current.style.position = 'relative';
66
91
  }
67
92
  }, [textDiv]);
68
- // mouse move handler
69
- // left handler
70
- useEffect(() => {
71
- const handlerMove = (e) => {
72
- var _a;
73
- if (!leftDrag)
74
- return;
75
- const sm = getNodeAndOffsetFromPoint(e.clientX, e.clientY);
76
- if (sm
77
- && sm.node === ((_a = textDiv.current) === null || _a === void 0 ? void 0 : _a.childNodes[0])
78
- && sm.offset <= currentRightPos) {
79
- setCurrentLeftPos(sm.offset);
93
+ // break text into three spans
94
+ useLayoutEffect(() => {
95
+ var _a, _b, _c;
96
+ let textLeftNode = (_a = textDiv.current) === null || _a === void 0 ? void 0 : _a.childNodes[0];
97
+ if (!textLeftNode || textLeftNode.nodeType !== document.TEXT_NODE || textLeftNode.nodeValue === null) {
98
+ return;
99
+ }
100
+ const head = document.createRange();
101
+ head.setStart(textLeftNode, 0);
102
+ head.setEnd(textLeftNode, currentLeftPos);
103
+ const headSpan = document.createElement('span');
104
+ if (headClass)
105
+ headSpan.classList.value = headClass;
106
+ head.surroundContents(headSpan);
107
+ textLeftNode = (_b = textDiv.current) === null || _b === void 0 ? void 0 : _b.childNodes[2];
108
+ if (!textLeftNode
109
+ || !textLeftNode.nodeValue
110
+ || textLeftNode.nodeValue.length < currentRightPos - currentLeftPos)
111
+ return;
112
+ const selection = document.createRange();
113
+ selection.setStart(textLeftNode, 0);
114
+ selection.setEnd(textLeftNode, currentRightPos - currentLeftPos);
115
+ const selectionSpan = document.createElement('span');
116
+ if (selectionClass)
117
+ selectionSpan.classList.value = selectionClass;
118
+ selection.surroundContents(selectionSpan);
119
+ textLeftNode = (_c = textDiv.current) === null || _c === void 0 ? void 0 : _c.childNodes[4];
120
+ if (!textLeftNode)
121
+ return;
122
+ const tail = document.createRange();
123
+ tail.setStart(textLeftNode, 0);
124
+ tail.setEndAfter(textLeftNode);
125
+ const tailSpan = document.createElement('span');
126
+ if (tailClass)
127
+ tailSpan.classList.value = tailClass;
128
+ tail.surroundContents(tailSpan);
129
+ return () => {
130
+ if (textDiv.current) {
131
+ textDiv.current.childNodes[0].nodeValue = textDiv.current.textContent;
132
+ while (textDiv.current.childNodes.length > 1 && textDiv.current.lastChild) {
133
+ textDiv.current.removeChild(textDiv.current.lastChild);
134
+ }
80
135
  }
81
136
  };
82
- document.addEventListener('mousemove', handlerMove);
137
+ }, [textDiv.current]);
138
+ // mouse move handler
139
+ // left handler
140
+ const leftMoveHandler = useCallback((e) => {
141
+ var _a, _b;
142
+ const sm = getNodeAndOffsetFromPoint(e.clientX, e.clientY);
143
+ if (!sm)
144
+ return;
145
+ let posToSet = currentLeftPos;
146
+ if (sm.node === ((_a = textDiv.current) === null || _a === void 0 ? void 0 : _a.childNodes[1].firstChild)) {
147
+ posToSet = sm.offset;
148
+ }
149
+ else if (sm.node === ((_b = textDiv.current) === null || _b === void 0 ? void 0 : _b.childNodes[3].firstChild)) {
150
+ posToSet = currentLeftPos + sm.offset;
151
+ }
152
+ if (posToSet !== currentLeftPos) {
153
+ const headText = textDiv.current.childNodes[1].firstChild.nodeValue;
154
+ const selText = textDiv.current.childNodes[3].firstChild.nodeValue;
155
+ const full = headText + selText;
156
+ textDiv.current.childNodes[1].firstChild.nodeValue = full.substring(0, posToSet);
157
+ textDiv.current.childNodes[3].firstChild.nodeValue = full.substring(posToSet);
158
+ setCurrentLeftPos(posToSet);
159
+ }
160
+ }, [currentLeftPos, textDiv.current]);
161
+ useLayoutEffect(() => {
162
+ if (!leftDrag) {
163
+ document.removeEventListener('mousemove', leftMoveHandler);
164
+ }
165
+ else {
166
+ document.addEventListener('mousemove', leftMoveHandler);
167
+ }
83
168
  return () => {
84
- document.removeEventListener('mousemove', handlerMove);
169
+ document.removeEventListener('mousemove', leftMoveHandler);
85
170
  };
86
- }, [leftDrag, currentRightPos]);
87
- useEffect(() => {
171
+ }, [leftDrag, currentLeftPos, textDiv.current]);
172
+ useLayoutEffect(() => {
88
173
  setCurrentLeftPos(initLeftPos);
89
174
  }, [initLeftPos]);
90
175
  // right handler
91
- useEffect(() => {
92
- const handlerMove = (e) => {
93
- var _a;
94
- if (!rightDrag)
95
- return;
96
- const sm = getNodeAndOffsetFromPoint(e.clientX, e.clientY);
97
- if (sm
98
- && sm.node === ((_a = textDiv.current) === null || _a === void 0 ? void 0 : _a.childNodes[0])
99
- && sm.offset >= currentLeftPos) {
100
- setCurrentRightPos(sm.offset);
101
- }
102
- };
103
- document.addEventListener('mousemove', handlerMove);
176
+ const rightMoveHandler = useCallback((e) => {
177
+ var _a, _b;
178
+ const sm = getNodeAndOffsetFromPoint(e.clientX, e.clientY);
179
+ if (!sm)
180
+ return;
181
+ let posToSet = currentRightPos;
182
+ if (sm.node === ((_a = textDiv.current) === null || _a === void 0 ? void 0 : _a.childNodes[3].firstChild)) {
183
+ posToSet = currentLeftPos + sm.offset;
184
+ }
185
+ else if (sm.node === ((_b = textDiv.current) === null || _b === void 0 ? void 0 : _b.childNodes[5].firstChild)) {
186
+ posToSet = currentRightPos + sm.offset;
187
+ }
188
+ if (posToSet !== currentRightPos) {
189
+ const selText = textDiv.current.childNodes[3].firstChild.nodeValue;
190
+ const tailText = textDiv.current.childNodes[5].firstChild.nodeValue;
191
+ const full = selText + tailText;
192
+ textDiv.current.childNodes[3].firstChild.nodeValue = full.substring(0, posToSet - currentLeftPos);
193
+ textDiv.current.childNodes[5].firstChild.nodeValue = full.substring(posToSet - currentLeftPos);
194
+ setCurrentRightPos(posToSet);
195
+ }
196
+ }, [currentLeftPos, currentRightPos, textDiv.current]);
197
+ useLayoutEffect(() => {
198
+ if (!rightDrag) {
199
+ document.removeEventListener('mousemove', rightMoveHandler);
200
+ }
201
+ else {
202
+ document.addEventListener('mousemove', rightMoveHandler);
203
+ }
104
204
  return () => {
105
- document.removeEventListener('mousemove', handlerMove);
205
+ document.removeEventListener('mousemove', rightMoveHandler);
106
206
  };
107
- }, [rightDrag, currentLeftPos]);
108
- useEffect(() => {
207
+ }, [rightDrag, currentLeftPos, currentRightPos, textDiv.current]);
208
+ useLayoutEffect(() => {
109
209
  setCurrentRightPos(initRightPos);
110
210
  }, [initRightPos]);
111
211
  // draw init left handler
112
- useEffect(() => {
212
+ useLayoutEffect(() => {
113
213
  if (textDiv.current
114
- && textDiv.current.childNodes.length === 1
115
- && textDiv.current.childNodes[0].nodeType === document.TEXT_NODE) {
116
- const rect = getHandlerRect(currentLeftPos, textDiv.current.childNodes[0]);
214
+ && textDiv.current.childNodes.length === 6) {
215
+ const rect = getHandlerRect(textDiv.current, true);
117
216
  if (rect === null) {
118
217
  setLeftHandler(null);
119
- setRects(null);
120
218
  }
121
219
  else {
122
220
  const divRect = textDiv.current.getBoundingClientRect();
@@ -130,14 +228,12 @@ const useTextSelectionEditor = (initLeftPos, initRightPos, leftDrag, rightDrag)
130
228
  }
131
229
  }, [currentLeftPos]);
132
230
  // draw init right handler
133
- useEffect(() => {
231
+ useLayoutEffect(() => {
134
232
  if (textDiv.current
135
- && textDiv.current.childNodes.length === 1
136
- && textDiv.current.childNodes[0].nodeType === document.TEXT_NODE) {
137
- const rect = getHandlerRect(currentRightPos, textDiv.current.childNodes[0]);
233
+ && textDiv.current.childNodes.length === 6) {
234
+ const rect = getHandlerRect(textDiv.current, false);
138
235
  if (rect === null) {
139
236
  setRightHandler(null);
140
- setRects(null);
141
237
  }
142
238
  else {
143
239
  const divRect = textDiv.current.getBoundingClientRect();
@@ -150,32 +246,8 @@ const useTextSelectionEditor = (initLeftPos, initRightPos, leftDrag, rightDrag)
150
246
  }
151
247
  }
152
248
  }, [currentRightPos]);
153
- useEffect(() => {
154
- var _a;
155
- const n = (_a = textDiv.current) === null || _a === void 0 ? void 0 : _a.childNodes[0];
156
- if (!n) {
157
- setRects(null);
158
- return;
159
- }
160
- const rawRects = getAllRects(n, currentLeftPos, currentRightPos);
161
- let rectArray = [];
162
- if (rawRects && textDiv.current) {
163
- const divRect = textDiv.current.getBoundingClientRect();
164
- for (let i = 0; i < rawRects.length; ++i) {
165
- const aa = rawRects.item(i);
166
- if (aa)
167
- rectArray.push({
168
- height: aa.height,
169
- left: aa.left - divRect.left,
170
- top: aa.top - divRect.top,
171
- width: aa.width,
172
- });
173
- }
174
- }
175
- setRects(rectArray);
176
- }, [currentLeftPos, currentRightPos]);
177
249
  // return
178
- return [textDiv, leftHandler, rightHandler, rects];
250
+ return [textDiv, leftHandler, rightHandler];
179
251
  };
180
252
 
181
253
  var _path$1;
@@ -227,11 +299,10 @@ function styleInject(css, ref) {
227
299
  }
228
300
  }
229
301
 
230
- var css_248z = "/*! tailwindcss v3.3.3 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:\"\"}html{-webkit-text-size-adjust:100%;font-feature-settings:normal;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-variation-settings:normal;line-height:1.5;-moz-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{font-feature-settings:inherit;color:inherit;font-family:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.absolute{position:absolute}.relative{position:relative}.flex{display:flex}.rounded-l-md{border-bottom-left-radius:.375rem;border-top-left-radius:.375rem}.rounded-r-md{border-bottom-right-radius:.375rem;border-top-right-radius:.375rem}";
302
+ var css_248z = "/*! tailwindcss v3.3.3 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:\"\"}html{-webkit-text-size-adjust:100%;font-feature-settings:normal;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-variation-settings:normal;line-height:1.5;-moz-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{font-feature-settings:inherit;color:inherit;font-family:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.absolute{position:absolute}.relative{position:relative}.flex{display:flex}.w-80{width:20rem}.select-none{user-select:none}.whitespace-pre-wrap{white-space:pre-wrap}.rounded-l-md{border-bottom-left-radius:.375rem;border-top-left-radius:.375rem}.rounded-r-md{border-bottom-right-radius:.375rem;border-top-right-radius:.375rem}.bg-yellow-100{--tw-bg-opacity:1;background-color:rgb(254 249 195/var(--tw-bg-opacity))}.bg-yellow-300{--tw-bg-opacity:1;background-color:rgb(253 224 71/var(--tw-bg-opacity))}.p-5{padding:1.25rem}.text-2xl{font-size:1.5rem;line-height:2rem}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}";
231
303
  styleInject(css_248z,{"insertAt":"top"});
232
304
 
233
- const SelectionHandler = ({ pos, grab, setGrab, left, bgColor, width, className }) => {
234
- const bgColorDef = bgColor !== null && bgColor !== void 0 ? bgColor : 'rgb(253 224 71)';
305
+ const SelectionHandler = ({ pos, grab, setGrab, left, width, className }) => {
235
306
  const widthDef = width !== null && width !== void 0 ? width : 25;
236
307
  return (pos &&
237
308
  React__default.createElement("div", { draggable: false, className: `${left ? 'rounded-l-md' : 'rounded-r-md'} ${className}`, style: {
@@ -243,7 +314,6 @@ const SelectionHandler = ({ pos, grab, setGrab, left, bgColor, width, className
243
314
  height: pos.height,
244
315
  cursor: grab ? 'grabbing' : 'grab',
245
316
  alignItems: left ? 'flex-start' : 'flex-end',
246
- backgroundColor: bgColorDef,
247
317
  }, onMouseDown: () => {
248
318
  setGrab(true);
249
319
  const handler = () => {
@@ -258,10 +328,10 @@ const SelectionHandler = ({ pos, grab, setGrab, left, bgColor, width, className
258
328
  : React__default.createElement(SvgQuoteRight, null)));
259
329
  };
260
330
 
261
- const ReactTextRange = ({ initLeftPos, initRightPos, Container, children, onChange, props, selectionColor, handlerWidth, className, leftHandlerClass, rightHandlerClass, }) => {
331
+ const ReactTextRange = ({ initLeftPos, initRightPos, Container, children, onChange, props, handlerWidth, className, leftHandlerClass, rightHandlerClass, headClass, selectionClass, tailClass, }) => {
262
332
  const [mouseOnLeft, setMouseOnLeft] = useState(false);
263
333
  const [mouseOnRight, setMouseOnRight] = useState(false);
264
- const [textDiv, leftHandler, rightHandler, rects] = useTextSelectionEditor(initLeftPos, initRightPos, mouseOnLeft, mouseOnRight);
334
+ const [textDiv, leftHandler, rightHandler] = useTextSelectionEditor(initLeftPos, initRightPos, mouseOnLeft, mouseOnRight, headClass, selectionClass, tailClass);
265
335
  useEffect(() => {
266
336
  if (leftHandler && rightHandler) {
267
337
  onChange({
@@ -270,29 +340,10 @@ const ReactTextRange = ({ initLeftPos, initRightPos, Container, children, onChan
270
340
  });
271
341
  }
272
342
  }, [leftHandler, rightHandler]);
273
- const bgColor = selectionColor;
274
343
  return (React__default.createElement("div", { className: className, draggable: false, style: Object.assign({ position: 'relative' }, props) },
275
- React__default.createElement(SelectionRects, { rects: rects, bgColor: bgColor }),
276
344
  React__default.createElement(Container, { ref: textDiv }, children),
277
- React__default.createElement(SelectionHandler, { className: leftHandlerClass, bgColor: bgColor, width: handlerWidth, grab: mouseOnLeft, left: true, pos: leftHandler, setGrab: (v) => setMouseOnLeft(v) }),
278
- React__default.createElement(SelectionHandler, { className: rightHandlerClass, bgColor: bgColor, width: handlerWidth, grab: mouseOnRight, left: false, pos: rightHandler, setGrab: (v) => setMouseOnRight(v) })));
279
- };
280
- const SelectionRects = ({ rects, bgColor }) => {
281
- if (!rects)
282
- return null;
283
- return (React__default.createElement(React__default.Fragment, null, rects.map((d, i) => React__default.createElement(SelectionRect, { key: i, rect: d, bgColor: bgColor }))));
284
- };
285
- const SelectionRect = ({ rect, bgColor }) => {
286
- const bgColorDef = bgColor !== null && bgColor !== void 0 ? bgColor : 'rgb(253 224 71)';
287
- return (React__default.createElement("div", { style: {
288
- userSelect: 'none',
289
- position: 'absolute',
290
- top: `${rect.top}px`,
291
- left: `${rect.left}px`,
292
- width: `${rect.width}px`,
293
- height: `${rect.height}px`,
294
- backgroundColor: bgColorDef,
295
- } }, "\u00A0"));
345
+ React__default.createElement(SelectionHandler, { className: leftHandlerClass, width: handlerWidth, grab: mouseOnLeft, left: true, pos: leftHandler, setGrab: (v) => setMouseOnLeft(v) }),
346
+ React__default.createElement(SelectionHandler, { className: rightHandlerClass, width: handlerWidth, grab: mouseOnRight, left: false, pos: rightHandler, setGrab: (v) => setMouseOnRight(v) })));
296
347
  };
297
348
 
298
349
  export { ReactTextRange };
@@ -1,14 +1,12 @@
1
1
  import React from 'react';
2
2
  import { HandlerPos } from './handler-pos';
3
- import { Rect } from './rect';
4
3
  declare global {
5
4
  interface Document {
6
5
  caretPositionFromPoint: any;
7
6
  }
8
7
  }
9
- export declare const useTextSelectionEditor: (initLeftPos: number, initRightPos: number, leftDrag: boolean, rightDrag: boolean) => [
8
+ export declare const useTextSelectionEditor: (initLeftPos: number, initRightPos: number, leftDrag: boolean, rightDrag: boolean, headClass?: string, selectionClass?: string, tailClass?: string) => [
10
9
  React.MutableRefObject<HTMLDivElement | null>,
11
10
  HandlerPos | null,
12
- HandlerPos | null,
13
- Rect[] | null
11
+ HandlerPos | null
14
12
  ];
package/dist/index.d.ts CHANGED
@@ -15,10 +15,12 @@ declare const ReactTextRange: FC<{
15
15
  onChange: (state: RangeState) => void;
16
16
  props?: React.CSSProperties;
17
17
  className?: string;
18
- selectionColor?: string;
19
18
  handlerWidth?: number;
20
19
  leftHandlerClass?: string;
21
20
  rightHandlerClass?: string;
21
+ headClass?: string;
22
+ selectionClass?: string;
23
+ tailClass?: string;
22
24
  }>;
23
25
 
24
26
  export { type RangeState, ReactTextRange, type TextContainer };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-text-range",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "text selection editor for React",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",