124 lines
3.3 KiB
TypeScript
124 lines
3.3 KiB
TypeScript
import {createRandomID} from '@/helpers/randomId'
|
|
import {computePosition, flip, shift, offset} from '@floating-ui/dom'
|
|
import {nextTick} from 'vue'
|
|
import {eventToShortcutString} from '@/helpers/shortcut'
|
|
|
|
export default function inputPrompt(pos: ClientRect, oldValue: string = ''): Promise<string> {
|
|
return new Promise((resolve) => {
|
|
const id = 'link-input-' + createRandomID()
|
|
|
|
// Create popup element
|
|
const popupElement = document.createElement('div')
|
|
popupElement.style.position = 'fixed'
|
|
popupElement.style.top = '0'
|
|
popupElement.style.left = '0'
|
|
popupElement.style.zIndex = '4700'
|
|
popupElement.style.background = 'var(--white)'
|
|
popupElement.style.border = '1px solid var(--grey-300)'
|
|
popupElement.style.borderRadius = '4px'
|
|
popupElement.style.padding = '8px'
|
|
popupElement.style.boxShadow = 'var(--shadow-md)'
|
|
const wrapperDiv = document.createElement('div')
|
|
const inputElement = document.createElement('input')
|
|
inputElement.className = 'input'
|
|
inputElement.placeholder = 'URL'
|
|
inputElement.id = id
|
|
inputElement.value = oldValue
|
|
wrapperDiv.appendChild(inputElement)
|
|
popupElement.appendChild(wrapperDiv)
|
|
document.body.appendChild(popupElement)
|
|
|
|
// Create a local mutable copy of the position for scroll tracking
|
|
let currentRect = new DOMRect(pos.left, pos.top, pos.width, pos.height)
|
|
|
|
// Virtual reference for positioning
|
|
const virtualReference = {
|
|
getBoundingClientRect: () => currentRect,
|
|
}
|
|
|
|
// Function to update popup position
|
|
const updatePosition = () => {
|
|
computePosition(virtualReference, popupElement, {
|
|
placement: 'top-start',
|
|
strategy: 'fixed',
|
|
middleware: [
|
|
offset(8),
|
|
flip(),
|
|
shift({ padding: 8 }),
|
|
],
|
|
}).then(({ x, y }) => {
|
|
popupElement.style.left = `${x}px`
|
|
popupElement.style.top = `${y}px`
|
|
})
|
|
}
|
|
|
|
// Position the popup initially
|
|
updatePosition()
|
|
|
|
// Track scroll position
|
|
let lastScrollY = window.scrollY
|
|
let lastScrollX = window.scrollX
|
|
|
|
// Update position on scroll
|
|
const handleScroll = () => {
|
|
const deltaY = window.scrollY - lastScrollY
|
|
const deltaX = window.scrollX - lastScrollX
|
|
|
|
// Update the local mutable rect to account for scroll
|
|
currentRect = new DOMRect(
|
|
currentRect.x - deltaX,
|
|
currentRect.y - deltaY,
|
|
currentRect.width,
|
|
currentRect.height,
|
|
)
|
|
|
|
lastScrollY = window.scrollY
|
|
lastScrollX = window.scrollX
|
|
|
|
updatePosition()
|
|
}
|
|
|
|
window.addEventListener('scroll', handleScroll, true)
|
|
|
|
nextTick(() => document.getElementById(id)?.focus())
|
|
|
|
const cleanup = () => {
|
|
window.removeEventListener('scroll', handleScroll, true)
|
|
if (document.body.contains(popupElement)) {
|
|
document.body.removeChild(popupElement)
|
|
}
|
|
}
|
|
|
|
document.getElementById(id)?.addEventListener('keydown', event => {
|
|
const shortcutString = eventToShortcutString(event)
|
|
if (shortcutString !== 'Enter') {
|
|
return
|
|
}
|
|
|
|
if (event.isComposing) {
|
|
return
|
|
}
|
|
|
|
const url = (event.target as HTMLInputElement).value
|
|
|
|
resolve(url)
|
|
cleanup()
|
|
})
|
|
|
|
// Close on click outside
|
|
const handleClickOutside = (event: MouseEvent) => {
|
|
if (!popupElement.contains(event.target as Node)) {
|
|
resolve('')
|
|
cleanup()
|
|
document.removeEventListener('click', handleClickOutside)
|
|
}
|
|
}
|
|
|
|
// Add slight delay to prevent immediate closing
|
|
setTimeout(() => {
|
|
document.addEventListener('click', handleClickOutside)
|
|
}, 100)
|
|
|
|
})
|
|
}
|