Skip to content

Commit 02c7e43

Browse files
committed
input and textarea support
1 parent 1dc4020 commit 02c7e43

File tree

2 files changed

+40
-10
lines changed

2 files changed

+40
-10
lines changed

src/ExamplePage.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import {
1313
FormControl,
1414
FormLabel,
1515
Switch,
16+
Input,
17+
Textarea,
1618
} from "@chakra-ui/react";
1719
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
1820
import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism";
@@ -175,6 +177,14 @@ function ExamplePage() {
175177
showStyles={true}
176178
styleColor={styleColor}
177179
/>
180+
<Heading size="md">Input components</Heading>
181+
<Text>
182+
The popover should also work inside of Input and TextArea
183+
components, but has limited support for the X,Y due to browser
184+
constraints.
185+
</Text>
186+
<Input defaultValue="Highlight my value" />
187+
<Textarea defaultValue="Highlight my text" />
178188
</CardBody>
179189
</Card>
180190
</ChakraProvider>

src/lib/useTextSelection.js

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,38 @@ function resolveTargets(target) {
99
return [target?.current];
1010
}
1111

12-
function hasSelection() {
13-
const selection = window?.getSelection();
14-
return !selection?.isCollapsed;
12+
function getSelection(e) {
13+
return getDomSelection(e) || getUISelection(e);
1514
}
1615

17-
function getSelection() {
18-
if (!hasSelection()) return;
16+
/* Input and Textarea selections are rendered in the browsers native UI. We need to handle them differently */
17+
function getUISelection(e) {
18+
const focusedElement = document?.activeElement;
19+
20+
const selectedText = focusedElement?.value?.substring?.(
21+
focusedElement?.selectionStart,
22+
focusedElement?.selectionEnd
23+
);
24+
25+
if (!selectedText) return;
26+
27+
return {
28+
baseNode: focusedElement,
29+
extentNode: focusedElement,
30+
range: focusedElement /* TODO: better support X,Y of mouse */,
31+
selectedText,
32+
};
33+
}
34+
35+
/* Get the selection for non-UI elements */
36+
function getDomSelection() {
37+
if (window?.getSelection?.()?.isCollapsed) return;
1938
const selection = window?.getSelection();
2039
const range = selection?.getRangeAt(0);
2140
const selectedHtml = Serializer.serializeToString(range?.cloneContents());
2241
const selectedText = selection?.toString();
2342
return {
2443
baseNode: selection?.anchorNode,
25-
baseOffset: selection?.anchorOffset,
2644
extentNode: selection?.focusNode,
2745
range,
2846
selectedHtml,
@@ -37,16 +55,17 @@ function isTargetInSelection(targets, selection) {
3755

3856
export function useTextSelection(target) {
3957
const [state, setState] = useState();
40-
const updateAnchorPos = () => {
58+
59+
const updateAnchorPos = (e) => {
4160
const targets = resolveTargets(target);
42-
const selection = getSelection();
61+
const selection = getSelection(e);
4362
if (isTargetInSelection(targets, selection)) {
4463
setState(selection);
4564
}
4665
};
4766

48-
const onSelectionChange = () => {
49-
const selection = getSelection();
67+
const onSelectionChange = (e) => {
68+
const selection = getSelection(e);
5069
if (!selection?.range) {
5170
setState({});
5271
}
@@ -64,6 +83,7 @@ export function useTextSelection(target) {
6483
document.addEventListener("scroll", onWindowScroll, {
6584
capture: true,
6685
});
86+
6787
return () => {
6888
document.removeEventListener("mouseup", updateAnchorPos);
6989
document.removeEventListener("selectionchange", onSelectionChange);

0 commit comments

Comments
 (0)