|
import { addMeltEventListener, makeElement, createElHelpers, executeCallbacks, isHTMLElement, kbd, omit, overridable, toWritableStores, } from '../../internal/helpers/index.js'; |
|
import { derived, readonly, writable } from 'svelte/store'; |
|
import { getPageItems } from './helpers.js'; |
|
import { withGet } from '../../internal/helpers/withGet.js'; |
|
const defaults = { |
|
perPage: 1, |
|
siblingCount: 1, |
|
defaultPage: 1, |
|
}; |
|
const { name, selector } = createElHelpers('pagination'); |
|
export function createPagination(props) { |
|
const withDefaults = { ...defaults, ...props }; |
|
const pageWritable = withDefaults.page ?? writable(withDefaults.defaultPage); |
|
const page = overridable(pageWritable, withDefaults?.onPageChange); |
|
|
|
const options = toWritableStores(omit(withDefaults, 'page', 'onPageChange', 'defaultPage')); |
|
const { perPage, siblingCount, count } = options; |
|
const totalPages = withGet.derived([count, perPage], ([$count, $perPage]) => { |
|
return Math.ceil($count / $perPage); |
|
}); |
|
const range = derived([page, perPage, count], ([$page, $perPage, $count]) => { |
|
const start = ($page - 1) * $perPage; |
|
const end = Math.min(start + $perPage, $count); |
|
return { start, end }; |
|
}); |
|
const root = makeElement(name(), { |
|
returned: () => ({ |
|
'data-scope': 'pagination', |
|
}), |
|
}); |
|
const pages = derived([page, totalPages, siblingCount], ([$page, $totalPages, $siblingCount]) => { |
|
return getPageItems({ page: $page, totalPages: $totalPages, siblingCount: $siblingCount }); |
|
}); |
|
const keydown = (e) => { |
|
const thisEl = e.target; |
|
if (!isHTMLElement(thisEl)) |
|
return; |
|
const rootEl = thisEl.closest('[data-scope="pagination"]'); |
|
if (!isHTMLElement(rootEl)) |
|
return; |
|
const triggers = Array.from(rootEl.querySelectorAll(selector('page'))).filter((el) => isHTMLElement(el)); |
|
const prevButton = rootEl.querySelector(selector('prev')); |
|
const nextButton = rootEl.querySelector(selector('next')); |
|
if (isHTMLElement(prevButton)) { |
|
triggers.unshift(prevButton); |
|
} |
|
if (isHTMLElement(nextButton)) { |
|
triggers.push(nextButton); |
|
} |
|
const index = triggers.indexOf(thisEl); |
|
if (e.key === kbd.ARROW_LEFT && index !== 0) { |
|
e.preventDefault(); |
|
triggers[index - 1].focus(); |
|
} |
|
else if (e.key === kbd.ARROW_RIGHT && index !== triggers.length - 1) { |
|
e.preventDefault(); |
|
triggers[index + 1].focus(); |
|
} |
|
else if (e.key === kbd.HOME) { |
|
e.preventDefault(); |
|
triggers[0].focus(); |
|
} |
|
else if (e.key === kbd.END) { |
|
e.preventDefault(); |
|
triggers[triggers.length - 1].focus(); |
|
} |
|
}; |
|
const pageTrigger = makeElement(name('page'), { |
|
stores: page, |
|
returned: ($page) => { |
|
return (pageItem) => { |
|
return { |
|
'aria-label': `Page ${pageItem.value}`, |
|
'data-value': pageItem.value, |
|
'data-selected': pageItem.value === $page ? '' : undefined, |
|
}; |
|
}; |
|
}, |
|
action: (node) => { |
|
const unsub = executeCallbacks(addMeltEventListener(node, 'click', () => { |
|
const value = node.dataset.value; |
|
if (!value || Number.isNaN(+value)) |
|
return; |
|
page.set(Number(value)); |
|
}), addMeltEventListener(node, 'keydown', keydown)); |
|
return { |
|
destroy: unsub, |
|
}; |
|
}, |
|
}); |
|
const prevButton = makeElement(name('prev'), { |
|
stores: page, |
|
returned: ($page) => { |
|
return { |
|
'aria-label': 'Previous', |
|
disabled: $page <= 1, |
|
}; |
|
}, |
|
action: (node) => { |
|
const unsub = executeCallbacks(addMeltEventListener(node, 'click', () => { |
|
page.update((p) => Math.max(p - 1, 1)); |
|
}), addMeltEventListener(node, 'keydown', keydown)); |
|
return { |
|
destroy: unsub, |
|
}; |
|
}, |
|
}); |
|
const nextButton = makeElement(name('next'), { |
|
stores: [page, totalPages], |
|
returned: ([$page, $totalPages]) => { |
|
return { |
|
'aria-label': 'Next', |
|
disabled: $page >= $totalPages, |
|
}; |
|
}, |
|
action: (node) => { |
|
const unsub = executeCallbacks(addMeltEventListener(node, 'click', () => { |
|
const $totalPages = totalPages.get(); |
|
page.update((p) => Math.min(p + 1, $totalPages)); |
|
}), addMeltEventListener(node, 'keydown', keydown)); |
|
return { |
|
destroy: unsub, |
|
}; |
|
}, |
|
}); |
|
return { |
|
elements: { |
|
root, |
|
pageTrigger, |
|
prevButton, |
|
nextButton, |
|
}, |
|
states: { |
|
range: readonly(range), |
|
page: page, |
|
pages: readonly(pages), |
|
totalPages: readonly(totalPages), |
|
}, |
|
options, |
|
}; |
|
} |
|
|