react-text-range 1.0.10 → 1.0.12

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,7 +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;
19
+ leftHandlerClass?: string;
20
+ rightHandlerClass?: string;
21
+ headClass?: string;
22
+ selectionClass?: string;
23
+ tailClass?: string;
20
24
  }>;
21
25
  export default ReactTextRange;
@@ -8,4 +8,5 @@ export declare const SelectionHandler: FC<{
8
8
  left: boolean;
9
9
  bgColor?: string;
10
10
  width?: number;
11
+ className?: string;
11
12
  }>;
@@ -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,14 +319,14 @@ 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}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.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 }) => {
325
+ const SelectionHandler = ({ pos, grab, setGrab, left, bgColor, width, className }) => {
254
326
  const bgColorDef = bgColor !== null && bgColor !== void 0 ? bgColor : 'rgb(253 224 71)';
255
327
  const widthDef = width !== null && width !== void 0 ? width : 25;
256
328
  return (pos &&
257
- React.createElement("div", { draggable: false, className: `${left ? 'rounded-l-md' : 'rounded-r-md'}`, style: {
329
+ React.createElement("div", { draggable: false, className: `${left ? 'rounded-l-md' : 'rounded-r-md'} ${className}`, style: {
258
330
  position: 'absolute',
259
331
  display: 'flex',
260
332
  left: left ? pos.left - (widthDef - 1) : pos.left - 1,
@@ -264,8 +336,6 @@ const SelectionHandler = ({ pos, grab, setGrab, left, bgColor, width }) => {
264
336
  cursor: grab ? 'grabbing' : 'grab',
265
337
  alignItems: left ? 'flex-start' : 'flex-end',
266
338
  backgroundColor: bgColorDef,
267
- opacity: .8,
268
- // transform: 'scale(.5)'
269
339
  }, onMouseDown: () => {
270
340
  setGrab(true);
271
341
  const handler = () => {
@@ -280,10 +350,10 @@ const SelectionHandler = ({ pos, grab, setGrab, left, bgColor, width }) => {
280
350
  : React.createElement(SvgQuoteRight, null)));
281
351
  };
282
352
 
283
- const ReactTextRange = ({ initLeftPos, initRightPos, Container, children, onChange, props, selectionColor, handlerWidth, className, }) => {
353
+ const ReactTextRange = ({ initLeftPos, initRightPos, Container, children, onChange, props, handlerWidth, className, leftHandlerClass, rightHandlerClass, headClass, selectionClass, tailClass, }) => {
284
354
  const [mouseOnLeft, setMouseOnLeft] = React.useState(false);
285
355
  const [mouseOnRight, setMouseOnRight] = React.useState(false);
286
- const [textDiv, leftHandler, rightHandler, rects] = useTextSelectionEditor(initLeftPos, initRightPos, mouseOnLeft, mouseOnRight);
356
+ const [textDiv, leftHandler, rightHandler] = useTextSelectionEditor(initLeftPos, initRightPos, mouseOnLeft, mouseOnRight, headClass, selectionClass, tailClass);
287
357
  React.useEffect(() => {
288
358
  if (leftHandler && rightHandler) {
289
359
  onChange({
@@ -292,29 +362,10 @@ const ReactTextRange = ({ initLeftPos, initRightPos, Container, children, onChan
292
362
  });
293
363
  }
294
364
  }, [leftHandler, rightHandler]);
295
- const bgColor = selectionColor;
296
365
  return (React.createElement("div", { className: className, draggable: false, style: Object.assign({ position: 'relative' }, props) },
297
- React.createElement(SelectionRects, { rects: rects, bgColor: bgColor }),
298
366
  React.createElement(Container, { ref: textDiv }, children),
299
- React.createElement(SelectionHandler, { bgColor: bgColor, width: handlerWidth, grab: mouseOnLeft, left: true, pos: leftHandler, setGrab: (v) => setMouseOnLeft(v) }),
300
- React.createElement(SelectionHandler, { bgColor: bgColor, width: handlerWidth, grab: mouseOnRight, left: false, pos: rightHandler, setGrab: (v) => setMouseOnRight(v) })));
301
- };
302
- const SelectionRects = ({ rects, bgColor }) => {
303
- if (!rects)
304
- return null;
305
- return (React.createElement(React.Fragment, null, rects.map((d, i) => React.createElement(SelectionRect, { key: i, rect: d, bgColor: bgColor }))));
306
- };
307
- const SelectionRect = ({ rect, bgColor }) => {
308
- const bgColorDef = bgColor !== null && bgColor !== void 0 ? bgColor : 'rgb(253 224 71)';
309
- return (React.createElement("div", { style: {
310
- userSelect: 'none',
311
- position: 'absolute',
312
- top: `${rect.top}px`,
313
- left: `${rect.left}px`,
314
- width: `${rect.width}px`,
315
- height: `${rect.height}px`,
316
- backgroundColor: bgColorDef,
317
- } }, "\u00A0"));
367
+ React.createElement(SelectionHandler, { className: leftHandlerClass, width: handlerWidth, grab: mouseOnLeft, left: true, pos: leftHandler, setGrab: (v) => setMouseOnLeft(v) }),
368
+ React.createElement(SelectionHandler, { className: rightHandlerClass, width: handlerWidth, grab: mouseOnRight, left: false, pos: rightHandler, setGrab: (v) => setMouseOnRight(v) })));
318
369
  };
319
370
 
320
371
  exports.ReactTextRange = ReactTextRange;
@@ -15,7 +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;
19
+ leftHandlerClass?: string;
20
+ rightHandlerClass?: string;
21
+ headClass?: string;
22
+ selectionClass?: string;
23
+ tailClass?: string;
20
24
  }>;
21
25
  export default ReactTextRange;
@@ -8,4 +8,5 @@ export declare const SelectionHandler: FC<{
8
8
  left: boolean;
9
9
  bgColor?: string;
10
10
  width?: number;
11
+ className?: string;
11
12
  }>;
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,14 +299,14 @@ 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}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.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 }) => {
305
+ const SelectionHandler = ({ pos, grab, setGrab, left, bgColor, width, className }) => {
234
306
  const bgColorDef = bgColor !== null && bgColor !== void 0 ? bgColor : 'rgb(253 224 71)';
235
307
  const widthDef = width !== null && width !== void 0 ? width : 25;
236
308
  return (pos &&
237
- React__default.createElement("div", { draggable: false, className: `${left ? 'rounded-l-md' : 'rounded-r-md'}`, style: {
309
+ React__default.createElement("div", { draggable: false, className: `${left ? 'rounded-l-md' : 'rounded-r-md'} ${className}`, style: {
238
310
  position: 'absolute',
239
311
  display: 'flex',
240
312
  left: left ? pos.left - (widthDef - 1) : pos.left - 1,
@@ -244,8 +316,6 @@ const SelectionHandler = ({ pos, grab, setGrab, left, bgColor, width }) => {
244
316
  cursor: grab ? 'grabbing' : 'grab',
245
317
  alignItems: left ? 'flex-start' : 'flex-end',
246
318
  backgroundColor: bgColorDef,
247
- opacity: .8,
248
- // transform: 'scale(.5)'
249
319
  }, onMouseDown: () => {
250
320
  setGrab(true);
251
321
  const handler = () => {
@@ -260,10 +330,10 @@ const SelectionHandler = ({ pos, grab, setGrab, left, bgColor, width }) => {
260
330
  : React__default.createElement(SvgQuoteRight, null)));
261
331
  };
262
332
 
263
- const ReactTextRange = ({ initLeftPos, initRightPos, Container, children, onChange, props, selectionColor, handlerWidth, className, }) => {
333
+ const ReactTextRange = ({ initLeftPos, initRightPos, Container, children, onChange, props, handlerWidth, className, leftHandlerClass, rightHandlerClass, headClass, selectionClass, tailClass, }) => {
264
334
  const [mouseOnLeft, setMouseOnLeft] = useState(false);
265
335
  const [mouseOnRight, setMouseOnRight] = useState(false);
266
- const [textDiv, leftHandler, rightHandler, rects] = useTextSelectionEditor(initLeftPos, initRightPos, mouseOnLeft, mouseOnRight);
336
+ const [textDiv, leftHandler, rightHandler] = useTextSelectionEditor(initLeftPos, initRightPos, mouseOnLeft, mouseOnRight, headClass, selectionClass, tailClass);
267
337
  useEffect(() => {
268
338
  if (leftHandler && rightHandler) {
269
339
  onChange({
@@ -272,29 +342,10 @@ const ReactTextRange = ({ initLeftPos, initRightPos, Container, children, onChan
272
342
  });
273
343
  }
274
344
  }, [leftHandler, rightHandler]);
275
- const bgColor = selectionColor;
276
345
  return (React__default.createElement("div", { className: className, draggable: false, style: Object.assign({ position: 'relative' }, props) },
277
- React__default.createElement(SelectionRects, { rects: rects, bgColor: bgColor }),
278
346
  React__default.createElement(Container, { ref: textDiv }, children),
279
- React__default.createElement(SelectionHandler, { bgColor: bgColor, width: handlerWidth, grab: mouseOnLeft, left: true, pos: leftHandler, setGrab: (v) => setMouseOnLeft(v) }),
280
- React__default.createElement(SelectionHandler, { bgColor: bgColor, width: handlerWidth, grab: mouseOnRight, left: false, pos: rightHandler, setGrab: (v) => setMouseOnRight(v) })));
281
- };
282
- const SelectionRects = ({ rects, bgColor }) => {
283
- if (!rects)
284
- return null;
285
- return (React__default.createElement(React__default.Fragment, null, rects.map((d, i) => React__default.createElement(SelectionRect, { key: i, rect: d, bgColor: bgColor }))));
286
- };
287
- const SelectionRect = ({ rect, bgColor }) => {
288
- const bgColorDef = bgColor !== null && bgColor !== void 0 ? bgColor : 'rgb(253 224 71)';
289
- return (React__default.createElement("div", { style: {
290
- userSelect: 'none',
291
- position: 'absolute',
292
- top: `${rect.top}px`,
293
- left: `${rect.left}px`,
294
- width: `${rect.width}px`,
295
- height: `${rect.height}px`,
296
- backgroundColor: bgColorDef,
297
- } }, "\u00A0"));
347
+ React__default.createElement(SelectionHandler, { className: leftHandlerClass, width: handlerWidth, grab: mouseOnLeft, left: true, pos: leftHandler, setGrab: (v) => setMouseOnLeft(v) }),
348
+ React__default.createElement(SelectionHandler, { className: rightHandlerClass, width: handlerWidth, grab: mouseOnRight, left: false, pos: rightHandler, setGrab: (v) => setMouseOnRight(v) })));
298
349
  };
299
350
 
300
351
  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,8 +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;
19
+ leftHandlerClass?: string;
20
+ rightHandlerClass?: string;
21
+ headClass?: string;
22
+ selectionClass?: string;
23
+ tailClass?: string;
20
24
  }>;
21
25
 
22
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.10",
3
+ "version": "1.0.12",
4
4
  "description": "text selection editor for React",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",