42 lines
1.5 KiB
JavaScript
42 lines
1.5 KiB
JavaScript
const titleLevel = row => Math.min(Math.max(Number(row.titleLevel) || 1, 1), 3)
|
|
|
|
// A section includes every row up to the next title at the same or a higher level.
|
|
const sectionEnd = (rows, start) => {
|
|
const level = titleLevel(rows[start])
|
|
let end = start + 1
|
|
while (end < rows.length && (rows[end].mode !== 'title' || titleLevel(rows[end]) > level)) end += 1
|
|
return end
|
|
}
|
|
|
|
export const getTitleSectionMove = (rows, rowId, direction) => {
|
|
if (!['up', 'down'].includes(direction)) return null
|
|
const start = rows.findIndex(row => row.id === rowId)
|
|
if (start < 0 || rows[start].mode !== 'title') return null
|
|
|
|
const level = titleLevel(rows[start])
|
|
const end = sectionEnd(rows, start)
|
|
if (direction === 'down') {
|
|
// A title at a higher level ends the parent section: never cross it.
|
|
if (end === rows.length || titleLevel(rows[end]) !== level) return null
|
|
return { start, end, target: sectionEnd(rows, end) }
|
|
}
|
|
|
|
for (let previous = start - 1; previous >= 0; previous -= 1) {
|
|
if (rows[previous].mode !== 'title' || titleLevel(rows[previous]) > level) continue
|
|
if (titleLevel(rows[previous]) < level) return null
|
|
return { start, end, target: previous }
|
|
}
|
|
return null
|
|
}
|
|
|
|
export const moveTitleSection = (rows, rowId, direction) => {
|
|
const move = getTitleSectionMove(rows, rowId, direction)
|
|
if (!move) return rows
|
|
|
|
const { start, end, target } = move
|
|
const result = rows.slice()
|
|
const section = result.splice(start, end - start)
|
|
result.splice(target > start ? target - section.length : target, 0, ...section)
|
|
return result
|
|
}
|