v1.2.9: 라이브 에디터·홈 피드·메인 커버 개선
라이브 모드 코드/콜아웃/토글 편집, 슬래시 명령, 홈 Latest List·Compact·Cards 보기, 사이트 설정 메인 화면 커버(720px) 및 HomeHero 반영. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
82
lib/textarea-caret-coordinates.js
Normal file
82
lib/textarea-caret-coordinates.js
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 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 }
|
||||
}
|
||||
Reference in New Issue
Block a user