슬래시 메뉴 스크롤 계산 분리
This commit is contained in:
@@ -57,6 +57,7 @@ import {
|
|||||||
DEFAULT_SLASH_MENU_HEIGHT,
|
DEFAULT_SLASH_MENU_HEIGHT,
|
||||||
DEFAULT_SLASH_MENU_WIDTH
|
DEFAULT_SLASH_MENU_WIDTH
|
||||||
} from '../../lib/markdown-slash-menu-position.js'
|
} from '../../lib/markdown-slash-menu-position.js'
|
||||||
|
import { calculateSlashMenuScrollTop } from '../../lib/markdown-slash-menu-scroll.js'
|
||||||
import { buildCalloutOpenerLine, CALLOUT_BACKGROUND_OPTIONS, QUOTE_BACKGROUND_OPTIONS } from '../../lib/markdown-callout.js'
|
import { buildCalloutOpenerLine, CALLOUT_BACKGROUND_OPTIONS, QUOTE_BACKGROUND_OPTIONS } from '../../lib/markdown-callout.js'
|
||||||
import { buildCodeFenceOpener } from '../../lib/markdown-code-block.js'
|
import { buildCodeFenceOpener } from '../../lib/markdown-code-block.js'
|
||||||
import { buildToggleOpenerLine } from '../../lib/markdown-toggle.js'
|
import { buildToggleOpenerLine } from '../../lib/markdown-toggle.js'
|
||||||
@@ -2497,16 +2498,12 @@ const scrollLiveSlashHighlightedIntoView = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const itemTop = highlightedItem.offsetTop
|
menu.scrollTop = calculateSlashMenuScrollTop({
|
||||||
const itemBottom = itemTop + highlightedItem.offsetHeight
|
itemTop: highlightedItem.offsetTop,
|
||||||
const viewTop = menu.scrollTop
|
itemHeight: highlightedItem.offsetHeight,
|
||||||
const viewBottom = viewTop + menu.clientHeight
|
viewTop: menu.scrollTop,
|
||||||
|
viewHeight: menu.clientHeight
|
||||||
if (itemTop < viewTop) {
|
})
|
||||||
menu.scrollTop = itemTop
|
|
||||||
} else if (itemBottom > viewBottom) {
|
|
||||||
menu.scrollTop = itemBottom - menu.clientHeight
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@
|
|||||||
| lib/markdown-toc.js | 공개 게시글 TOC용 H1~H3 제목 추출과 앵커 ID 생성 |
|
| lib/markdown-toc.js | 공개 게시글 TOC용 H1~H3 제목 추출과 앵커 ID 생성 |
|
||||||
| lib/markdown-slash-commands.js | 관리자 Markdown-first 에디터 슬래시 명령 목록과 삽입 줄 정의, 미디어 명령 action 판별, 미디어 명령 지연 commit 무시·슬래시 원문 줄 교체 삽입 helper |
|
| lib/markdown-slash-commands.js | 관리자 Markdown-first 에디터 슬래시 명령 목록과 삽입 줄 정의, 미디어 명령 action 판별, 미디어 명령 지연 commit 무시·슬래시 원문 줄 교체 삽입 helper |
|
||||||
| lib/markdown-slash-menu-position.js | 관리자 Markdown-first 에디터 슬래시 메뉴 뷰포트 위치 보정 helper |
|
| lib/markdown-slash-menu-position.js | 관리자 Markdown-first 에디터 슬래시 메뉴 뷰포트 위치 보정 helper |
|
||||||
|
| lib/markdown-slash-menu-scroll.js | 관리자 Markdown-first 에디터 슬래시 메뉴 선택 항목 표시용 scrollTop 계산 helper |
|
||||||
| lib/markdown-inline.js | 공개·관리자 공통 인라인 마크다운 파서, 링크·굵게·기울임·인라인 코드·첨자·인라인 색상 토큰(`::color[텍스트]`) 변환, 링크·굵게·기울임·색상 내부 중첩 인라인 문법과 `<br>` 줄바꿈 처리, 라이브 멀티라인 본문의 기본 불릿/번호 목록 HTML 변환과 contenteditable 목록 마커 기준 커서 offset 계산·직렬화 |
|
| lib/markdown-inline.js | 공개·관리자 공통 인라인 마크다운 파서, 링크·굵게·기울임·인라인 코드·첨자·인라인 색상 토큰(`::color[텍스트]`) 변환, 링크·굵게·기울임·색상 내부 중첩 인라인 문법과 `<br>` 줄바꿈 처리, 라이브 멀티라인 본문의 기본 불릿/번호 목록 HTML 변환과 contenteditable 목록 마커 기준 커서 offset 계산·직렬화 |
|
||||||
| lib/post-tag-limit.js | 게시물별 태그 최대 개수 기본값·허용 범위·정규화 |
|
| lib/post-tag-limit.js | 게시물별 태그 최대 개수 기본값·허용 범위·정규화 |
|
||||||
| lib/analytics-shared.js | 통계 추적 경로 필터·체류/스크롤 상수(클라이언트·서버 공용) |
|
| lib/analytics-shared.js | 통계 추적 경로 필터·체류/스크롤 상수(클라이언트·서버 공용) |
|
||||||
@@ -123,6 +124,7 @@
|
|||||||
| tests/markdown-editor-history.test.js | 관리자 Markdown 에디터 undo/redo 히스토리 항목과 선택 범위 보정 단위 회귀 테스트 |
|
| tests/markdown-editor-history.test.js | 관리자 Markdown 에디터 undo/redo 히스토리 항목과 선택 범위 보정 단위 회귀 테스트 |
|
||||||
| tests/markdown-editor-media.test.js | 관리자 Markdown 에디터 미디어 선택 helper의 종류·선택 가능 여부·미리보기·선택 상태·블록 생성 단위 회귀 테스트 |
|
| tests/markdown-editor-media.test.js | 관리자 Markdown 에디터 미디어 선택 helper의 종류·선택 가능 여부·미리보기·선택 상태·블록 생성 단위 회귀 테스트 |
|
||||||
| tests/markdown-slash-commands.test.js | 관리자 슬래시 명령 파싱·미디어 action·포커스 대상·원문 줄 교체 삽입 단위 회귀 테스트 |
|
| tests/markdown-slash-commands.test.js | 관리자 슬래시 명령 파싱·미디어 action·포커스 대상·원문 줄 교체 삽입 단위 회귀 테스트 |
|
||||||
|
| tests/markdown-slash-menu-scroll.test.js | 관리자 슬래시 메뉴 선택 항목 scrollTop 보정 단위 테스트 |
|
||||||
| tests/markdown-block-parser.test.js | 공개 Markdown 본문 블록 파싱 단위 회귀 테스트 |
|
| tests/markdown-block-parser.test.js | 공개 Markdown 본문 블록 파싱 단위 회귀 테스트 |
|
||||||
| tests/markdown-fenced-meta.test.js | Markdown fenced 블록 시작·종료 줄 탐색, 본문 수집, 메타 파싱 단위 회귀 테스트 |
|
| tests/markdown-fenced-meta.test.js | Markdown fenced 블록 시작·종료 줄 탐색, 본문 수집, 메타 파싱 단위 회귀 테스트 |
|
||||||
| tests/markdown-quote.test.js | Markdown 인용 옵션 파싱과 편집 줄 변환 단위 회귀 테스트 |
|
| tests/markdown-quote.test.js | Markdown 인용 옵션 파싱과 편집 줄 변환 단위 회귀 테스트 |
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
# 업데이트 이력
|
# 업데이트 이력
|
||||||
|
|
||||||
|
## v1.11.85
|
||||||
|
|
||||||
|
- 관리자 Markdown 에디터의 슬래시 메뉴 선택 항목 스크롤 계산을 `markdown-slash-menu-scroll` helper로 분리.
|
||||||
|
- 슬래시 메뉴 선택 항목의 위·아래 스크롤 보정 단위 테스트 추가.
|
||||||
|
|
||||||
## v1.11.84
|
## v1.11.84
|
||||||
|
|
||||||
- 관리자 Markdown 에디터의 슬래시 메뉴 재배치 이벤트 리스너를 안정적인 참조로 정리.
|
- 관리자 Markdown 에디터의 슬래시 메뉴 재배치 이벤트 리스너를 안정적인 참조로 정리.
|
||||||
|
|||||||
32
lib/markdown-slash-menu-scroll.js
Normal file
32
lib/markdown-slash-menu-scroll.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* 슬래시 메뉴 선택 항목이 보이도록 필요한 scrollTop을 계산한다.
|
||||||
|
* @param {Object} options - 계산 옵션
|
||||||
|
* @param {number} options.itemTop - 항목의 메뉴 내부 위쪽 위치
|
||||||
|
* @param {number} options.itemHeight - 항목 높이
|
||||||
|
* @param {number} options.viewTop - 현재 메뉴 scrollTop
|
||||||
|
* @param {number} options.viewHeight - 메뉴 표시 높이
|
||||||
|
* @returns {number} 적용할 scrollTop
|
||||||
|
*/
|
||||||
|
export const calculateSlashMenuScrollTop = ({
|
||||||
|
itemTop,
|
||||||
|
itemHeight,
|
||||||
|
viewTop,
|
||||||
|
viewHeight
|
||||||
|
}) => {
|
||||||
|
const safeItemTop = Number.isFinite(itemTop) ? itemTop : 0
|
||||||
|
const safeItemHeight = Number.isFinite(itemHeight) ? Math.max(0, itemHeight) : 0
|
||||||
|
const safeViewTop = Number.isFinite(viewTop) ? Math.max(0, viewTop) : 0
|
||||||
|
const safeViewHeight = Number.isFinite(viewHeight) ? Math.max(0, viewHeight) : 0
|
||||||
|
const itemBottom = safeItemTop + safeItemHeight
|
||||||
|
const viewBottom = safeViewTop + safeViewHeight
|
||||||
|
|
||||||
|
if (safeItemTop < safeViewTop) {
|
||||||
|
return Math.max(0, safeItemTop)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemBottom > viewBottom) {
|
||||||
|
return Math.max(0, itemBottom - safeViewHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
return safeViewTop
|
||||||
|
}
|
||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "sori.studio",
|
"name": "sori.studio",
|
||||||
"version": "1.11.84",
|
"version": "1.11.85",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "sori.studio",
|
"name": "sori.studio",
|
||||||
"version": "1.11.84",
|
"version": "1.11.85",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxtjs/tailwindcss": "^6.14.0",
|
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sori.studio",
|
"name": "sori.studio",
|
||||||
"version": "1.11.84",
|
"version": "1.11.85",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"imports": {
|
"imports": {
|
||||||
|
|||||||
40
tests/markdown-slash-menu-scroll.test.js
Normal file
40
tests/markdown-slash-menu-scroll.test.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import test from 'node:test'
|
||||||
|
import assert from 'node:assert/strict'
|
||||||
|
|
||||||
|
import { calculateSlashMenuScrollTop } from '../lib/markdown-slash-menu-scroll.js'
|
||||||
|
|
||||||
|
test('슬래시 메뉴 항목이 보이는 범위 안이면 현재 scrollTop을 유지한다', () => {
|
||||||
|
assert.equal(calculateSlashMenuScrollTop({
|
||||||
|
itemTop: 48,
|
||||||
|
itemHeight: 32,
|
||||||
|
viewTop: 40,
|
||||||
|
viewHeight: 120
|
||||||
|
}), 40)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('슬래시 메뉴 항목이 위로 벗어나면 항목 위쪽으로 스크롤한다', () => {
|
||||||
|
assert.equal(calculateSlashMenuScrollTop({
|
||||||
|
itemTop: 20,
|
||||||
|
itemHeight: 32,
|
||||||
|
viewTop: 60,
|
||||||
|
viewHeight: 120
|
||||||
|
}), 20)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('슬래시 메뉴 항목이 아래로 벗어나면 항목 아래쪽이 보이도록 스크롤한다', () => {
|
||||||
|
assert.equal(calculateSlashMenuScrollTop({
|
||||||
|
itemTop: 180,
|
||||||
|
itemHeight: 32,
|
||||||
|
viewTop: 60,
|
||||||
|
viewHeight: 120
|
||||||
|
}), 92)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('슬래시 메뉴 스크롤 계산은 잘못된 숫자를 안전한 기본값으로 보정한다', () => {
|
||||||
|
assert.equal(calculateSlashMenuScrollTop({
|
||||||
|
itemTop: Number.NaN,
|
||||||
|
itemHeight: -10,
|
||||||
|
viewTop: -20,
|
||||||
|
viewHeight: Number.POSITIVE_INFINITY
|
||||||
|
}), 0)
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user