feat(Pagination): refactor DOM to improve a11y (#9901)

This commit is contained in:
neverland 2021-11-19 10:52:16 +08:00 committed by GitHub
parent 42a5f79c2c
commit 3885fd00ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 336 additions and 232 deletions

View File

@ -1,10 +1,11 @@
import { computed, watch, defineComponent, ExtractPropTypes } from 'vue'; import { computed, watchEffect, defineComponent, ExtractPropTypes } from 'vue';
import { import {
BORDER, clamp,
makeStringProp, makeStringProp,
makeNumberProp, makeNumberProp,
makeNumericProp, makeNumericProp,
createNamespace, createNamespace,
BORDER_SURROUND,
} from '../utils'; } from '../utils';
const [name, bem, t] = createNamespace('pagination'); const [name, bem, t] = createNamespace('pagination');
@ -57,10 +58,6 @@ export default defineComponent({
const showPageSize = +props.showPageSize; const showPageSize = +props.showPageSize;
const { modelValue, forceEllipses } = props; const { modelValue, forceEllipses } = props;
if (props.mode !== 'multi') {
return items;
}
// Default page limits // Default page limits
let startPage = 1; let startPage = 1;
let endPage = pageCount; let endPage = pageCount;
@ -101,85 +98,96 @@ export default defineComponent({
return items; return items;
}); });
const select = (page: number, emitChange?: boolean) => { const updateModelValue = (value: number, emitChange?: boolean) => {
page = Math.min(count.value, Math.max(1, page)); value = clamp(value, 1, count.value);
if (props.modelValue !== page) { if (props.modelValue !== value) {
emit('update:modelValue', page); emit('update:modelValue', value);
if (emitChange) { if (emitChange) {
emit('change', page); emit('change', value);
} }
} }
}; };
watch( // format modelValue
() => props.modelValue, watchEffect(() => updateModelValue(props.modelValue));
(value) => {
select(value); const renderDesc = () => (
}, <li class={bem('page-desc')}>
{ immediate: true } {slots.pageDesc
? slots.pageDesc()
: `${props.modelValue}/${count.value}`}
</li>
); );
const renderDesc = () => { const renderPrevButton = () => {
if (props.mode !== 'multi') { const { mode, modelValue } = props;
return ( const slot = slots['prev-text'];
<li class={bem('page-desc')}> const disabled = modelValue === 1;
{slots.pageDesc
? slots.pageDesc()
: `${props.modelValue}/${count.value}`}
</li>
);
}
};
return () => {
const value = props.modelValue;
const simple = props.mode !== 'multi';
const onSelect = (value: number) => () => select(value, true);
return ( return (
<ul class={bem({ simple })}> <li
<li class={[
class={[ bem('item', { disabled, border: mode === 'simple', prev: true }),
bem('item', { disabled: value === 1 }), BORDER_SURROUND,
bem('prev'), ]}
BORDER, >
]} <button
onClick={onSelect(value - 1)} disabled={disabled}
onClick={() => updateModelValue(modelValue - 1)}
> >
{slots['prev-text'] {slot ? slot() : props.prevText || t('prev')}
? slots['prev-text']() </button>
: props.prevText || t('prev')} </li>
</li>
{pages.value.map((page) => (
<li
class={[
bem('item', { active: page.active }),
bem('page'),
BORDER,
]}
onClick={onSelect(page.number)}
>
{slots.page ? slots.page(page) : page.text}
</li>
))}
{renderDesc()}
<li
class={[
bem('item', { disabled: value === count.value }),
bem('next'),
BORDER,
]}
onClick={onSelect(value + 1)}
>
{slots['next-text']
? slots['next-text']()
: props.nextText || t('next')}
</li>
</ul>
); );
}; };
const renderNextButton = () => {
const { mode, modelValue } = props;
const slot = slots['next-text'];
const disabled = modelValue === count.value;
return (
<li
class={[
bem('item', { disabled, border: mode === 'simple', next: true }),
BORDER_SURROUND,
]}
>
<button
disabled={disabled}
onClick={() => updateModelValue(modelValue + 1)}
>
{slot ? slot() : props.nextText || t('next')}
</button>
</li>
);
};
const renderPages = () =>
pages.value.map((page) => (
<li
class={[
bem('item', { active: page.active, page: true }),
BORDER_SURROUND,
]}
>
<button
aria-current={page.active || undefined}
onClick={() => updateModelValue(page.number)}
>
{slots.page ? slots.page(page) : page.text}
</button>
</li>
));
return () => (
<nav role="navigation" class={bem()}>
<ul class={bem('items')}>
{renderPrevButton()}
{props.mode === 'simple' ? renderDesc() : renderPages()}
{renderNextButton()}
</ul>
</nav>
);
}, },
}); });

View File

@ -13,9 +13,12 @@
} }
.van-pagination { .van-pagination {
display: flex;
font-size: var(--van-pagination-font-size); font-size: var(--van-pagination-font-size);
&__items {
display: flex;
}
&__item, &__item,
&__page-desc { &__page-desc {
display: flex; display: flex;
@ -33,58 +36,56 @@
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
button {
flex: 1;
height: 100%;
border: none;
padding: 0;
background: transparent;
&[disabled] {
cursor: not-allowed;
}
}
&:active { &:active {
color: var(--van-white); color: var(--van-white);
background-color: var(--van-pagination-item-default-color); background-color: var(--van-pagination-item-default-color);
} }
&::after { &:not(:last-child)::after {
border-width: var(--van-border-width-base) 0 var(--van-border-width-base) border-right-width: 0;
var(--van-border-width-base);
}
&:last-child::after {
border-right-width: var(--van-border-width-base);
} }
&--active { &--active {
color: var(--van-white); color: var(--van-white);
background-color: var(--van-pagination-item-default-color); background-color: var(--van-pagination-item-default-color);
} }
}
&__prev, &--page {
&__next { flex-grow: 0;
padding: 0 var(--van-padding-base); }
cursor: pointer;
}
&__item--disabled { &--prev,
&, &--next {
&:active { padding: 0 var(--van-padding-base);
cursor: pointer;
}
&--border::after {
border-width: var(--van-border-width-base);
}
&--disabled {
color: var(--van-pagination-item-disabled-color); color: var(--van-pagination-item-disabled-color);
background-color: var(--van-pagination-item-disabled-background-color); background-color: var(--van-pagination-item-disabled-background-color);
cursor: not-allowed;
opacity: var(--van-pagination-disabled-opacity); opacity: var(--van-pagination-disabled-opacity);
} }
} }
&__page {
flex-grow: 0;
}
&__page-desc { &__page-desc {
flex: 1; flex: 1;
height: var(--van-pagination-height); height: var(--van-pagination-height);
color: var(--van-pagination-desc-color); color: var(--van-pagination-desc-color);
} }
&--simple {
.van-pagination__prev,
.van-pagination__next {
&::after {
border-width: var(--van-border-width-base);
}
}
}
} }

View File

@ -2,92 +2,151 @@
exports[`should render demo and match snapshot 1`] = ` exports[`should render demo and match snapshot 1`] = `
<div> <div>
<ul class="van-pagination"> <nav role="navigation"
<li class="van-pagination__item van-pagination__item--disabled van-pagination__prev van-hairline"> class="van-pagination"
Prev
</li>
<li class="van-pagination__item van-pagination__item--active van-pagination__page van-hairline">
1
</li>
<li class="van-pagination__item van-pagination__page van-hairline">
2
</li>
<li class="van-pagination__item van-pagination__page van-hairline">
3
</li>
<li class="van-pagination__item van-pagination__page van-hairline">
4
</li>
<li class="van-pagination__item van-pagination__page van-hairline">
5
</li>
<li class="van-pagination__item van-pagination__next van-hairline">
Next
</li>
</ul>
</div>
<div>
<ul class="van-pagination van-pagination--simple"
size="small"
> >
<li class="van-pagination__item van-pagination__item--disabled van-pagination__prev van-hairline"> <ul class="van-pagination__items">
Prev <li class="van-pagination__item van-pagination__item--disabled van-pagination__item--prev van-hairline--surround">
</li> <button disabled>
<li class="van-pagination__page-desc"> Prev
1/12 </button>
</li> </li>
<li class="van-pagination__item van-pagination__next van-hairline"> <li class="van-pagination__item van-pagination__item--active van-pagination__item--page van-hairline--surround">
Next <button aria-current="true">
</li> 1
</ul> </button>
</li>
<li class="van-pagination__item van-pagination__item--page van-hairline--surround">
<button>
2
</button>
</li>
<li class="van-pagination__item van-pagination__item--page van-hairline--surround">
<button>
3
</button>
</li>
<li class="van-pagination__item van-pagination__item--page van-hairline--surround">
<button>
4
</button>
</li>
<li class="van-pagination__item van-pagination__item--page van-hairline--surround">
<button>
5
</button>
</li>
<li class="van-pagination__item van-pagination__item--next van-hairline--surround">
<button>
Next
</button>
</li>
</ul>
</nav>
</div> </div>
<div> <div>
<ul class="van-pagination"> <nav role="navigation"
<li class="van-pagination__item van-pagination__item--disabled van-pagination__prev van-hairline"> class="van-pagination"
Prev size="small"
</li> >
<li class="van-pagination__item van-pagination__item--active van-pagination__page van-hairline"> <ul class="van-pagination__items">
1 <li class="van-pagination__item van-pagination__item--disabled van-pagination__item--border van-pagination__item--prev van-hairline--surround">
</li> <button disabled>
<li class="van-pagination__item van-pagination__page van-hairline"> Prev
2 </button>
</li> </li>
<li class="van-pagination__item van-pagination__page van-hairline"> <li class="van-pagination__page-desc">
3 1/12
</li> </li>
<li class="van-pagination__item van-pagination__page van-hairline"> <li class="van-pagination__item van-pagination__item--border van-pagination__item--next van-hairline--surround">
... <button>
</li> Next
<li class="van-pagination__item van-pagination__next van-hairline"> </button>
Next </li>
</li> </ul>
</ul> </nav>
</div> </div>
<div> <div>
<ul class="van-pagination"> <nav role="navigation"
<li class="van-pagination__item van-pagination__item--disabled van-pagination__prev van-hairline"> class="van-pagination"
<i class="van-badge__wrapper van-icon van-icon-arrow-left"> >
</i> <ul class="van-pagination__items">
</li> <li class="van-pagination__item van-pagination__item--disabled van-pagination__item--prev van-hairline--surround">
<li class="van-pagination__item van-pagination__item--active van-pagination__page van-hairline"> <button disabled>
1 Prev
</li> </button>
<li class="van-pagination__item van-pagination__page van-hairline"> </li>
2 <li class="van-pagination__item van-pagination__item--active van-pagination__item--page van-hairline--surround">
</li> <button aria-current="true">
<li class="van-pagination__item van-pagination__page van-hairline"> 1
3 </button>
</li> </li>
<li class="van-pagination__item van-pagination__page van-hairline"> <li class="van-pagination__item van-pagination__item--page van-hairline--surround">
4 <button>
</li> 2
<li class="van-pagination__item van-pagination__page van-hairline"> </button>
5 </li>
</li> <li class="van-pagination__item van-pagination__item--page van-hairline--surround">
<li class="van-pagination__item van-pagination__next van-hairline"> <button>
<i class="van-badge__wrapper van-icon van-icon-arrow"> 3
</i> </button>
</li> </li>
</ul> <li class="van-pagination__item van-pagination__item--page van-hairline--surround">
<button>
...
</button>
</li>
<li class="van-pagination__item van-pagination__item--next van-hairline--surround">
<button>
Next
</button>
</li>
</ul>
</nav>
</div>
<div>
<nav role="navigation"
class="van-pagination"
>
<ul class="van-pagination__items">
<li class="van-pagination__item van-pagination__item--disabled van-pagination__item--prev van-hairline--surround">
<button disabled>
<i class="van-badge__wrapper van-icon van-icon-arrow-left">
</i>
</button>
</li>
<li class="van-pagination__item van-pagination__item--active van-pagination__item--page van-hairline--surround">
<button aria-current="true">
1
</button>
</li>
<li class="van-pagination__item van-pagination__item--page van-hairline--surround">
<button>
2
</button>
</li>
<li class="van-pagination__item van-pagination__item--page van-hairline--surround">
<button>
3
</button>
</li>
<li class="van-pagination__item van-pagination__item--page van-hairline--surround">
<button>
4
</button>
</li>
<li class="van-pagination__item van-pagination__item--page van-hairline--surround">
<button>
5
</button>
</li>
<li class="van-pagination__item van-pagination__item--next van-hairline--surround">
<button>
<i class="van-badge__wrapper van-icon van-icon-arrow">
</i>
</button>
</li>
</ul>
</nav>
</div> </div>
`; `;

View File

@ -1,53 +1,89 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should render page slot correctly 1`] = ` exports[`should render page slot correctly 1`] = `
<ul class="van-pagination"> <nav role="navigation"
<li class="van-pagination__item van-pagination__prev van-hairline"> class="van-pagination"
Previous >
</li> <ul class="van-pagination__items">
<li class="van-pagination__item van-pagination__page van-hairline"> <li class="van-pagination__item van-pagination__item--prev van-hairline--surround">
foo 1 <button>
</li> Previous
<li class="van-pagination__item van-pagination__page van-hairline"> </button>
foo 2 </li>
</li> <li class="van-pagination__item van-pagination__item--page van-hairline--surround">
<li class="van-pagination__item van-pagination__page van-hairline"> <button>
foo 3 foo 1
</li> </button>
<li class="van-pagination__item van-pagination__page van-hairline"> </li>
foo 4 <li class="van-pagination__item van-pagination__item--page van-hairline--surround">
</li> <button>
<li class="van-pagination__item van-pagination__page van-hairline"> foo 2
foo 5 </button>
</li> </li>
<li class="van-pagination__item van-pagination__next van-hairline"> <li class="van-pagination__item van-pagination__item--page van-hairline--surround">
Next <button>
</li> foo 3
</ul> </button>
</li>
<li class="van-pagination__item van-pagination__item--page van-hairline--surround">
<button>
foo 4
</button>
</li>
<li class="van-pagination__item van-pagination__item--page van-hairline--surround">
<button>
foo 5
</button>
</li>
<li class="van-pagination__item van-pagination__item--next van-hairline--surround">
<button>
Next
</button>
</li>
</ul>
</nav>
`; `;
exports[`should render prev-text、next-text slot correctly 1`] = ` exports[`should render prev-text、next-text slot correctly 1`] = `
<ul class="van-pagination"> <nav role="navigation"
<li class="van-pagination__item van-pagination__prev van-hairline"> class="van-pagination"
Custom PrevText >
</li> <ul class="van-pagination__items">
<li class="van-pagination__item van-pagination__page van-hairline"> <li class="van-pagination__item van-pagination__item--prev van-hairline--surround">
1 <button>
</li> Custom PrevText
<li class="van-pagination__item van-pagination__page van-hairline"> </button>
2 </li>
</li> <li class="van-pagination__item van-pagination__item--page van-hairline--surround">
<li class="van-pagination__item van-pagination__page van-hairline"> <button>
3 1
</li> </button>
<li class="van-pagination__item van-pagination__page van-hairline"> </li>
4 <li class="van-pagination__item van-pagination__item--page van-hairline--surround">
</li> <button>
<li class="van-pagination__item van-pagination__page van-hairline"> 2
5 </button>
</li> </li>
<li class="van-pagination__item van-pagination__next van-hairline"> <li class="van-pagination__item van-pagination__item--page van-hairline--surround">
Custom NextText <button>
</li> 3
</ul> </button>
</li>
<li class="van-pagination__item van-pagination__item--page van-hairline--surround">
<button>
4
</button>
</li>
<li class="van-pagination__item van-pagination__item--page van-hairline--surround">
<button>
5
</button>
</li>
<li class="van-pagination__item van-pagination__item--next van-hairline--surround">
<button>
Custom NextText
</button>
</li>
</ul>
</nav>
`; `;