Paginator
この実装を Vue3 用に移植してみました。
https://github.com/lokyoung/vuejs-paginate/blob/master/src/components/Paginate.vue
実際にリストに実装しての確認は、まだしていません。。。
Code
import { onBeforeUpdate, ref, computed } from 'https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.2/vue.esm-browser.js'; const Paginator = { template: ` <component is="style"> ul.paginator a { cursor: pointer; } </component> <ul class="paginator" :class="containerClass" v-if="!noLiSurround"> <li v-if="firstLastButton" :class="[pageClass, firstPageSelected() ? disabledClass : '']"> <a :class="pageLinkClass" :tabindex="firstPageSelected() ? -1 : 0" v-html="firstButtonText" @click="selectFirstPage()" @keyup.enter="selectFirstPage()" ></a> </li> <li v-if="!(firstPageSelected() && hidePrevNext)" :class="[prevClass, firstPageSelected() ? disabledClass : '']"> <a :class="prevLinkClass" :tabindex="firstPageSelected() ? -1 : 0" v-html="prevText" @click="prevPage()" @keyup.enter="prevPage()" ></a> </li> <li v-for="page in pages" :class="[ pageClass, page.selected ? activeClass : '', page.disabled ? disabledClass : '', page.breakView ? breakViewClass: '']" > <a v-if="page.breakView" :class="[pageLinkClass, breakViewLinkClass]" tabindex="0"> <slot name="breakViewContent">{{ breakViewText }}</slot> </a> <a v-else-if="page.disabled" :class="pageLinkClass" tabindex="0">{{ page.content }}</a> <a v-else :class="pageLinkClass" tabindex="0" @click="handlePageSelected(page.index + 1)" @keyup.enter="handlePageSelected(page.index + 1)" >{{ page.content }}</a> </li> <li v-if="!(lastPageSelected() && hidePrevNext)" :class="[nextClass, lastPageSelected() ? disabledClass : '']"> <a :class="nextLinkClass" :tabindex="lastPageSelected() ? -1 : 0" v-html="nextText" @click="nextPage()" @keyup.enter="nextPage()" ></a> </li> <li v-if="firstLastButton" :class="[pageClass, lastPageSelected() ? disabledClass : '']"> <a :class="pageLinkClass" :tabindex="lastPageSelected() ? -1 : 0" v-html="lastButtonText" @click="selectLastPage()" @keyup.enter="selectLastPage()" ></a> </li> </ul> <div :class="containerClass" v-else> <a v-if="firstLastButton" tabindex="0" v-html="firstButtonText" :class="[pageLinkClass, firstPageSelected() ? disabledClass : '']" @click="selectFirstPage()" @keyup.enter="selectFirstPage()"> </a> <a v-if="!(firstPageSelected() && hidePrevNext)" tabindex="0" v-html="prevText" :class="[prevLinkClass, firstPageSelected() ? disabledClass : '']" @click="prevPage()" @keyup.enter="prevPage()" ></a> <template v-for="page in pages"> <a v-if="page.breakView" tabindex="0"> <slot name="breakViewContent" :class="[pageLinkClass, breakViewLinkClass, page.disabled ? disabledClass : '']"> {{ breakViewText }} </slot> </a> <a v-else-if="page.disabled" :class="[pageLinkClass, page.selected ? activeClass : '', disabledClass]" tabindex="0"> {{ page.content }} </a> <a v-else :class="[pageLinkClass, page.selected ? activeClass : '']" tabindex="0" @click="handlePageSelected(page.index + 1)" @keyup.enter="handlePageSelected(page.index + 1)" > {{ page.content }} </a> </template> <a v-if="!(lastPageSelected() && hidePrevNext)" tabindex="0" v-html="nextText" :class="[nextLinkClass, lastPageSelected() ? disabledClass : '']" @click="nextPage()" @keyup.enter="nextPage()" ></a> <a v-if="firstLastButton" tabindex="0" v-html="lastButtonText" :class="[pageLinkClass, lastPageSelected() ? disabledClass : '']" @click="selectLastPage()" @keyup.enter="selectLastPage()" ></a> </div> `, props: { value: { type: Number }, pageCount: { type: Number, required: true }, forcePage: { type: Number }, clickHandler: { type: Function, default: () => {} }, pageRange: { type: Number, default: 3 }, marginPages: { type: Number, default: 1 }, prevText: { type: String, default: '<' }, nextText: { type: String, default: '>' }, breakViewText: { type: String, default: '…' }, containerClass: { type: String, default: 'pagination pagination-sm pull-right' }, pageClass: { type: String }, pageLinkClass: { type: String }, prevClass: { type: String }, prevLinkClass: { type: String }, nextClass: { type: String }, nextLinkClass: { type: String }, breakViewClass: { type: String }, breakViewLinkClass: { type: String }, activeClass: { type: String, default: 'active' }, disabledClass: { type: String, default: 'disabled' }, noLiSurround: { type: Boolean, default: false }, firstLastButton: { type: Boolean, default: true }, firstButtonText: { type: String, default: '<<' }, lastButtonText: { type: String, default: '>>' }, hidePrevNext: { type: Boolean, default: false } }, setup(props, context) { const innerValue = ref(1) const selected = computed({ get: function() { return props.value || innerValue.value }, set: function(newValue) { innerValue.value = newValue } }) const pages = computed(function() { let items = {} if (props.pageCount <= props.pageRange) { for (let index = 0; index < props.pageCount; index++) { let page = { index: index, content: index + 1, selected: index === (selected - 1) } items[index] = page } } else { const halfPageRange = Math.floor(props.pageRange / 2) let setPageItem = index => { let page = { index: index, content: index + 1, selected: index === (selected - 1) } items[index] = page } let setBreakView = index => { let breakView = { disabled: true, breakView: true } items[index] = breakView } // 1st - loop thru low end of margin pages for (let i = 0; i < props.marginPages; i++) { setPageItem(i); } // 2nd - loop thru selected range let selectedRangeLow = 0; if (selected.value - halfPageRange > 0) { selectedRangeLow = selected.value - 1 - halfPageRange; } let selectedRangeHigh = selectedRangeLow + props.pageRange - 1; if (selectedRangeHigh >= props.pageCount) { selectedRangeHigh = props.pageCount - 1; selectedRangeLow = selectedRangeHigh - props.pageRange + 1; } for (let i = selectedRangeLow; i <= selectedRangeHigh && i <= props.pageCount - 1; i++) { setPageItem(i); } // Check if there is breakView in the left of selected range if (selectedRangeLow > props.marginPages) { setBreakView(selectedRangeLow - 1) } // Check if there is breakView in the right of selected range if (selectedRangeHigh + 1 < props.pageCount - props.marginPages) { setBreakView(selectedRangeHigh + 1) } // 3rd - loop thru high end of margin pages for (let i = props.pageCount - 1; i >= props.pageCount - props.marginPages; i--) { setPageItem(i); } } return items }) onBeforeUpdate(() => { if (props.forcePage === undefined) return if (props.forcePage !== selected.value) { selected.value = props.forcePage } }) function handlePageSelected(value) { if (selected.value === value) return innerValue.value = value context.emit('input', value) props.clickHandler(value) } function prevPage() { if (selected.value <= 1) return handlePageSelected(selected.value - 1) } function nextPage() { if (selected.value >= props.pageCount) return handlePageSelected(selected.value + 1) } function firstPageSelected() { return selected.value === 1 } function lastPageSelected() { return (selected.value === props.pageCount) || (props.pageCount === 0) } function selectFirstPage() { if (selected.value <= 1) return handlePageSelected(1) } function selectLastPage() { if (selected.value >= props.pageCount) return handlePageSelected(props.pageCount) } return { innerValue, selected, pages, handlePageSelected, prevPage, nextPage, firstPageSelected, lastPageSelected, selectFirstPage, selectLastPage } } }; export default Paginator;
Demo
<html> <div id="app"> <paginator :page-count="500" :click-handler="clickFunc"> </paginator> </div> <script type="module"> import { createApp } from 'https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.2/vue.esm-browser.js'; import Paginator from '/_export/code/vuejs/vue3/paginator?codeblock=0'; const App = { components: { paginator : Paginator }, data() { return { currentPage: 1 } }, methods: { clickFunc(value) { alert(value); this.currentPage = value; } } } createApp(App).mount('#app') </script> </html>