라이브 모드 코드/콜아웃/토글 편집, 슬래시 명령, 홈 Latest List·Compact·Cards 보기, 사이트 설정 메인 화면 커버(720px) 및 HomeHero 반영. Co-authored-by: Cursor <cursoragent@cursor.com>
83 lines
2.3 KiB
JavaScript
83 lines
2.3 KiB
JavaScript
/**
|
|
* textarea 커서 위치의 박스 기준 좌표를 계산한다(미러 div 방식).
|
|
* @param {HTMLTextAreaElement} textarea - 대상 textarea
|
|
* @param {number} position - 문자 인덱스
|
|
* @returns {{ top: number, left: number, height: number }} top·left·line height(px)
|
|
*/
|
|
export const getTextareaCaretCoordinates = (textarea, position) => {
|
|
const style = window.getComputedStyle(textarea)
|
|
const mirror = document.createElement('div')
|
|
|
|
mirror.setAttribute('aria-hidden', 'true')
|
|
mirror.style.position = 'absolute'
|
|
mirror.style.visibility = 'hidden'
|
|
mirror.style.whiteSpace = 'pre-wrap'
|
|
mirror.style.wordWrap = 'break-word'
|
|
mirror.style.overflow = 'hidden'
|
|
|
|
const properties = [
|
|
'direction',
|
|
'boxSizing',
|
|
'width',
|
|
'height',
|
|
'overflowX',
|
|
'overflowY',
|
|
'borderTopWidth',
|
|
'borderRightWidth',
|
|
'borderBottomWidth',
|
|
'borderLeftWidth',
|
|
'paddingTop',
|
|
'paddingRight',
|
|
'paddingBottom',
|
|
'paddingLeft',
|
|
'fontStyle',
|
|
'fontVariant',
|
|
'fontWeight',
|
|
'fontStretch',
|
|
'fontSize',
|
|
'lineHeight',
|
|
'fontFamily',
|
|
'textAlign',
|
|
'textTransform',
|
|
'textIndent',
|
|
'textDecoration',
|
|
'letterSpacing',
|
|
'wordSpacing',
|
|
'tabSize'
|
|
]
|
|
|
|
for (const property of properties) {
|
|
mirror.style[property] = style[property]
|
|
}
|
|
|
|
const isBoxSizingBorderBox = style.boxSizing === 'border-box'
|
|
const width = isBoxSizingBorderBox
|
|
? textarea.offsetWidth
|
|
: textarea.offsetWidth
|
|
- parseFloat(style.borderLeftWidth)
|
|
- parseFloat(style.borderRightWidth)
|
|
- parseFloat(style.paddingLeft)
|
|
- parseFloat(style.paddingRight)
|
|
|
|
mirror.style.width = `${width}px`
|
|
|
|
const value = textarea.value
|
|
const before = value.slice(0, position)
|
|
const after = value.slice(position) || '.'
|
|
|
|
mirror.textContent = before
|
|
const marker = document.createElement('span')
|
|
marker.textContent = after
|
|
mirror.appendChild(marker)
|
|
|
|
document.body.appendChild(mirror)
|
|
|
|
const top = marker.offsetTop + parseFloat(style.borderTopWidth) + parseFloat(style.paddingTop)
|
|
const left = marker.offsetLeft + parseFloat(style.borderLeftWidth) + parseFloat(style.paddingLeft)
|
|
const height = parseFloat(style.lineHeight) || marker.offsetHeight || 0
|
|
|
|
document.body.removeChild(mirror)
|
|
|
|
return { top, left, height }
|
|
}
|