123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- import { createVNode as _createVNode, mergeProps as _mergeProps } from "vue";
- // Styles
- import "./VPagination.css";
- // Components
- import { VBtn } from "../VBtn/index.mjs"; // Composables
- import { useDisplay } from "../../composables/index.mjs";
- import { makeBorderProps } from "../../composables/border.mjs";
- import { makeComponentProps } from "../../composables/component.mjs";
- import { provideDefaults } from "../../composables/defaults.mjs";
- import { makeDensityProps } from "../../composables/density.mjs";
- import { makeElevationProps } from "../../composables/elevation.mjs";
- import { IconValue } from "../../composables/icons.mjs";
- import { useLocale, useRtl } from "../../composables/locale.mjs";
- import { useProxiedModel } from "../../composables/proxiedModel.mjs";
- import { useRefs } from "../../composables/refs.mjs";
- import { useResizeObserver } from "../../composables/resizeObserver.mjs";
- import { makeRoundedProps } from "../../composables/rounded.mjs";
- import { makeSizeProps } from "../../composables/size.mjs";
- import { makeTagProps } from "../../composables/tag.mjs";
- import { makeThemeProps, provideTheme } from "../../composables/theme.mjs";
- import { makeVariantProps } from "../../composables/variant.mjs"; // Utilities
- import { computed, nextTick, shallowRef, toRef } from 'vue';
- import { createRange, genericComponent, keyValues, propsFactory, useRender } from "../../util/index.mjs"; // Types
- export const makeVPaginationProps = propsFactory({
- activeColor: String,
- start: {
- type: [Number, String],
- default: 1
- },
- modelValue: {
- type: Number,
- default: props => props.start
- },
- disabled: Boolean,
- length: {
- type: [Number, String],
- default: 1,
- validator: val => val % 1 === 0
- },
- totalVisible: [Number, String],
- firstIcon: {
- type: IconValue,
- default: '$first'
- },
- prevIcon: {
- type: IconValue,
- default: '$prev'
- },
- nextIcon: {
- type: IconValue,
- default: '$next'
- },
- lastIcon: {
- type: IconValue,
- default: '$last'
- },
- ariaLabel: {
- type: String,
- default: '$vuetify.pagination.ariaLabel.root'
- },
- pageAriaLabel: {
- type: String,
- default: '$vuetify.pagination.ariaLabel.page'
- },
- currentPageAriaLabel: {
- type: String,
- default: '$vuetify.pagination.ariaLabel.currentPage'
- },
- firstAriaLabel: {
- type: String,
- default: '$vuetify.pagination.ariaLabel.first'
- },
- previousAriaLabel: {
- type: String,
- default: '$vuetify.pagination.ariaLabel.previous'
- },
- nextAriaLabel: {
- type: String,
- default: '$vuetify.pagination.ariaLabel.next'
- },
- lastAriaLabel: {
- type: String,
- default: '$vuetify.pagination.ariaLabel.last'
- },
- ellipsis: {
- type: String,
- default: '...'
- },
- showFirstLastPage: Boolean,
- ...makeBorderProps(),
- ...makeComponentProps(),
- ...makeDensityProps(),
- ...makeElevationProps(),
- ...makeRoundedProps(),
- ...makeSizeProps(),
- ...makeTagProps({
- tag: 'nav'
- }),
- ...makeThemeProps(),
- ...makeVariantProps({
- variant: 'text'
- })
- }, 'VPagination');
- export const VPagination = genericComponent()({
- name: 'VPagination',
- props: makeVPaginationProps(),
- emits: {
- 'update:modelValue': value => true,
- first: value => true,
- prev: value => true,
- next: value => true,
- last: value => true
- },
- setup(props, _ref) {
- let {
- slots,
- emit
- } = _ref;
- const page = useProxiedModel(props, 'modelValue');
- const {
- t,
- n
- } = useLocale();
- const {
- isRtl
- } = useRtl();
- const {
- themeClasses
- } = provideTheme(props);
- const {
- width
- } = useDisplay();
- const maxButtons = shallowRef(-1);
- provideDefaults(undefined, {
- scoped: true
- });
- const {
- resizeRef
- } = useResizeObserver(entries => {
- if (!entries.length) return;
- const {
- target,
- contentRect
- } = entries[0];
- const firstItem = target.querySelector('.v-pagination__list > *');
- if (!firstItem) return;
- const totalWidth = contentRect.width;
- const itemWidth = firstItem.offsetWidth + parseFloat(getComputedStyle(firstItem).marginRight) * 2;
- maxButtons.value = getMax(totalWidth, itemWidth);
- });
- const length = computed(() => parseInt(props.length, 10));
- const start = computed(() => parseInt(props.start, 10));
- const totalVisible = computed(() => {
- if (props.totalVisible != null) return parseInt(props.totalVisible, 10);else if (maxButtons.value >= 0) return maxButtons.value;
- return getMax(width.value, 58);
- });
- function getMax(totalWidth, itemWidth) {
- const minButtons = props.showFirstLastPage ? 5 : 3;
- return Math.max(0, Math.floor(
- // Round to two decimal places to avoid floating point errors
- +((totalWidth - itemWidth * minButtons) / itemWidth).toFixed(2)));
- }
- const range = computed(() => {
- if (length.value <= 0 || isNaN(length.value) || length.value > Number.MAX_SAFE_INTEGER) return [];
- if (totalVisible.value <= 0) return [];else if (totalVisible.value === 1) return [page.value];
- if (length.value <= totalVisible.value) {
- return createRange(length.value, start.value);
- }
- const even = totalVisible.value % 2 === 0;
- const middle = even ? totalVisible.value / 2 : Math.floor(totalVisible.value / 2);
- const left = even ? middle : middle + 1;
- const right = length.value - middle;
- if (left - page.value >= 0) {
- return [...createRange(Math.max(1, totalVisible.value - 1), start.value), props.ellipsis, length.value];
- } else if (page.value - right >= (even ? 1 : 0)) {
- const rangeLength = totalVisible.value - 1;
- const rangeStart = length.value - rangeLength + start.value;
- return [start.value, props.ellipsis, ...createRange(rangeLength, rangeStart)];
- } else {
- const rangeLength = Math.max(1, totalVisible.value - 3);
- const rangeStart = rangeLength === 1 ? page.value : page.value - Math.ceil(rangeLength / 2) + start.value;
- return [start.value, props.ellipsis, ...createRange(rangeLength, rangeStart), props.ellipsis, length.value];
- }
- });
- // TODO: 'first' | 'prev' | 'next' | 'last' does not work here?
- function setValue(e, value, event) {
- e.preventDefault();
- page.value = value;
- event && emit(event, value);
- }
- const {
- refs,
- updateRef
- } = useRefs();
- provideDefaults({
- VPaginationBtn: {
- color: toRef(props, 'color'),
- border: toRef(props, 'border'),
- density: toRef(props, 'density'),
- size: toRef(props, 'size'),
- variant: toRef(props, 'variant'),
- rounded: toRef(props, 'rounded'),
- elevation: toRef(props, 'elevation')
- }
- });
- const items = computed(() => {
- return range.value.map((item, index) => {
- const ref = e => updateRef(e, index);
- if (typeof item === 'string') {
- return {
- isActive: false,
- key: `ellipsis-${index}`,
- page: item,
- props: {
- ref,
- ellipsis: true,
- icon: true,
- disabled: true
- }
- };
- } else {
- const isActive = item === page.value;
- return {
- isActive,
- key: item,
- page: n(item),
- props: {
- ref,
- ellipsis: false,
- icon: true,
- disabled: !!props.disabled || +props.length < 2,
- color: isActive ? props.activeColor : props.color,
- 'aria-current': isActive,
- 'aria-label': t(isActive ? props.currentPageAriaLabel : props.pageAriaLabel, item),
- onClick: e => setValue(e, item)
- }
- };
- }
- });
- });
- const controls = computed(() => {
- const prevDisabled = !!props.disabled || page.value <= start.value;
- const nextDisabled = !!props.disabled || page.value >= start.value + length.value - 1;
- return {
- first: props.showFirstLastPage ? {
- icon: isRtl.value ? props.lastIcon : props.firstIcon,
- onClick: e => setValue(e, start.value, 'first'),
- disabled: prevDisabled,
- 'aria-label': t(props.firstAriaLabel),
- 'aria-disabled': prevDisabled
- } : undefined,
- prev: {
- icon: isRtl.value ? props.nextIcon : props.prevIcon,
- onClick: e => setValue(e, page.value - 1, 'prev'),
- disabled: prevDisabled,
- 'aria-label': t(props.previousAriaLabel),
- 'aria-disabled': prevDisabled
- },
- next: {
- icon: isRtl.value ? props.prevIcon : props.nextIcon,
- onClick: e => setValue(e, page.value + 1, 'next'),
- disabled: nextDisabled,
- 'aria-label': t(props.nextAriaLabel),
- 'aria-disabled': nextDisabled
- },
- last: props.showFirstLastPage ? {
- icon: isRtl.value ? props.firstIcon : props.lastIcon,
- onClick: e => setValue(e, start.value + length.value - 1, 'last'),
- disabled: nextDisabled,
- 'aria-label': t(props.lastAriaLabel),
- 'aria-disabled': nextDisabled
- } : undefined
- };
- });
- function updateFocus() {
- const currentIndex = page.value - start.value;
- refs.value[currentIndex]?.$el.focus();
- }
- function onKeydown(e) {
- if (e.key === keyValues.left && !props.disabled && page.value > +props.start) {
- page.value = page.value - 1;
- nextTick(updateFocus);
- } else if (e.key === keyValues.right && !props.disabled && page.value < start.value + length.value - 1) {
- page.value = page.value + 1;
- nextTick(updateFocus);
- }
- }
- useRender(() => _createVNode(props.tag, {
- "ref": resizeRef,
- "class": ['v-pagination', themeClasses.value, props.class],
- "style": props.style,
- "role": "navigation",
- "aria-label": t(props.ariaLabel),
- "onKeydown": onKeydown,
- "data-test": "v-pagination-root"
- }, {
- default: () => [_createVNode("ul", {
- "class": "v-pagination__list"
- }, [props.showFirstLastPage && _createVNode("li", {
- "key": "first",
- "class": "v-pagination__first",
- "data-test": "v-pagination-first"
- }, [slots.first ? slots.first(controls.value.first) : _createVNode(VBtn, _mergeProps({
- "_as": "VPaginationBtn"
- }, controls.value.first), null)]), _createVNode("li", {
- "key": "prev",
- "class": "v-pagination__prev",
- "data-test": "v-pagination-prev"
- }, [slots.prev ? slots.prev(controls.value.prev) : _createVNode(VBtn, _mergeProps({
- "_as": "VPaginationBtn"
- }, controls.value.prev), null)]), items.value.map((item, index) => _createVNode("li", {
- "key": item.key,
- "class": ['v-pagination__item', {
- 'v-pagination__item--is-active': item.isActive
- }],
- "data-test": "v-pagination-item"
- }, [slots.item ? slots.item(item) : _createVNode(VBtn, _mergeProps({
- "_as": "VPaginationBtn"
- }, item.props), {
- default: () => [item.page]
- })])), _createVNode("li", {
- "key": "next",
- "class": "v-pagination__next",
- "data-test": "v-pagination-next"
- }, [slots.next ? slots.next(controls.value.next) : _createVNode(VBtn, _mergeProps({
- "_as": "VPaginationBtn"
- }, controls.value.next), null)]), props.showFirstLastPage && _createVNode("li", {
- "key": "last",
- "class": "v-pagination__last",
- "data-test": "v-pagination-last"
- }, [slots.last ? slots.last(controls.value.last) : _createVNode(VBtn, _mergeProps({
- "_as": "VPaginationBtn"
- }, controls.value.last), null)])])]
- }));
- return {};
- }
- });
- //# sourceMappingURL=VPagination.mjs.map
|