joaom00 / selection-popover Goto Github PK
View Code? Open in Web Editor NEWEasy-to-use, unstyled, composable react selection popover.
Home Page: https://selection-popover.vercel.app
License: MIT License
Easy-to-use, unstyled, composable react selection popover.
Home Page: https://selection-popover.vercel.app
License: MIT License
./node_modules/selection-popover/dist/index.mjs
Can't import the named export 'Portal' from non EcmaScript module (only default export is available)
I used selection-popover with radix-ui dialog component together, the selection-popover can work correctly, however when the dialog pops over and clicking any area on the dialog will make the dialog dismissed incorrectly.
The modal dialog dismissed incorrectly.
See the sandbox demo: https://codesandbox.io/p/sandbox/distracted-worker-2hgg6n?file=%2FApp.jsx%3A21%2C30
The dialog should be working as normal and should not be dismissed by clicking any area.
See the codesandbox for more dependency info:
{
"dependencies": {
"@radix-ui/colors": "latest",
"@radix-ui/react-dialog": "latest",
"@radix-ui/react-icons": "latest",
"classnames": "latest",
"clsx": "^1.2.1",
"react": "latest",
"react-dom": "latest",
"selection-popover": "^0.2.0"
},
"devDependencies": {
"@vitejs/plugin-react": "latest",
"autoprefixer": "latest",
"postcss": "latest",
"tailwindcss": "latest",
"vite": "latest"
},
"scripts": {
"start": "vite"
}
}
<textarea>
and <input>
text is not supported at the moment. This would be good for rich text editor support. I'm currently using this code locally to make it work but there could be another solution that doesn't involve adding and removing a div to get the coordinates.
const getDummyRect = (el: HTMLTextAreaElement | HTMLInputElement): DOMRect => {
const dummyDiv = document.createElement("div");
dummyDiv.textContent = el.value;
var computedStyle = window.getComputedStyle(el);
Array.from(computedStyle).forEach(function (key) {
return dummyDiv.style.setProperty(key, computedStyle.getPropertyValue(key), computedStyle.getPropertyPriority(key));
});
dummyDiv.style.position = "absolute";
dummyDiv.style.visibility = "hidden";
const textareaRect = el.getBoundingClientRect();
const scrolledTop = textareaRect.top + window.scrollY;
const scrolledLeft = textareaRect.left + window.scrollX;
dummyDiv.style.top = `${scrolledTop}px`;
dummyDiv.style.left = `${scrolledLeft}px`;
dummyDiv.style.width = `${textareaRect.width}px`;
dummyDiv.style.height = `${textareaRect.height}px`;
document.body.appendChild(dummyDiv);
const range = document.createRange();
range.setStart(dummyDiv.firstChild!, el.selectionStart || 0);
range.setEnd(dummyDiv.firstChild!, el.selectionEnd || 0);
const rect = range.getBoundingClientRect();
document.body.removeChild(dummyDiv);
return rect;
}
const handleSelection = () => {
if (pointerTypeRef.current !== 'mouse') return
const selection = document.getSelection()
if (!selection) return
const node = ref.current
const wasSelectionInsideTrigger = node?.contains(selection.anchorNode)
if (!wasSelectionInsideTrigger) {
hasOpenedRef.current = false
return
}
const activeEl = document.activeElement as HTMLInputElement | HTMLTextAreaElement;
let isCollapsed = true;
let selectedText = "";
if (activeEl && (activeEl.tagName === 'TEXTAREA' || activeEl.tagName === 'INPUT')) {
const start = activeEl.selectionStart ?? 0;
const end = activeEl.selectionEnd ?? 0;
isCollapsed = start === end;
selectedText = activeEl.value.substring(start, end);
} else {
isCollapsed = selection.isCollapsed;
selectedText = selection.toString();
}
if (isCollapsed) {
hasOpenedRef.current = false
return
}
const hasTextSelected = selectedText.trim() !== ''
if (hasTextSelected) {
if (!hasOpenedRef.current) onOpen(() => onOpenChange(true))
hasOpenedRef.current = true
if (activeEl && (activeEl.tagName === 'TEXTAREA' || activeEl.tagName === 'INPUT')) {
const rect = getDummyRect(activeEl)
onVirtualRefChange({
getBoundingClientRect: () => rect,
getClientRects: () => ({
0: rect,
length: 1,
item: (index: number) => index === 0 ? rect : null,
[Symbol.iterator]: function* () {
yield rect;
}
}),
});
} else {
const range = selection?.getRangeAt(0)
onVirtualRefChange({
getBoundingClientRect: () => range.getBoundingClientRect(),
getClientRects: () => range.getClientRects(),
})
}
}
}
The above code is for whileSelected. Here would be the updates for the default functionality.
context.onOpen(() => {
const selection = document.getSelection()
if (!selection) return
const trigger = ref.current
const wasSelectionInsideTrigger = trigger?.contains(selection.anchorNode)
if (!wasSelectionInsideTrigger) return
const activeEl = document.activeElement as HTMLInputElement | HTMLTextAreaElement
let isCollapsed = true
let selectedText = ''
if (activeEl && (activeEl.tagName === 'TEXTAREA' || activeEl.tagName === 'INPUT')) {
const start = activeEl.selectionStart ?? 0
const end = activeEl.selectionEnd ?? 0
isCollapsed = start === end
selectedText = activeEl.value.substring(start, end)
} else {
isCollapsed = selection.isCollapsed
selectedText = selection.toString()
}
if (isCollapsed) return
if (selectedText.trim() === '') return
context.onOpenChange(true)
if (activeEl && (activeEl.tagName === 'TEXTAREA' || activeEl.tagName === 'INPUT')) {
const rect = getDummyRect(activeEl)
context.onVirtualRefChange({
getBoundingClientRect: () => rect,
getClientRects: () => ({
0: rect,
length: 1,
item: (index: number) => (index === 0 ? rect : null),
[Symbol.iterator]: function* () {
yield rect
},
}),
})
} else {
const range = selection.getRangeAt(0)
context.onVirtualRefChange({
getBoundingClientRect: () => range.getBoundingClientRect(),
getClientRects: () => range.getClientRects(),
})
}
})
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.