refactor(frontend): extract PaginationItem to own pagination-link styling
BasePagination was reaching across slot boundaries with :deep() to style .pagination-previous / -next / -link — markup it doesn't actually render. Move that markup and the related scoped rules into a new PaginationItem component that polymorphically renders RouterLink (when `to` is given) or BaseButton (emit-based). BasePagination keeps only the scaffold it actually owns: .pagination, .pagination-list, .pagination-ellipsis. Pagination.vue and PaginationEmit.vue become thin wrappers around BasePagination + PaginationItem; no more raw pagination-* class usage or BaseButton imports in the emit wrapper. The .app-container.has-background / .link-share-container.has-background theme override moves with the .pagination-link rules into PaginationItem as its own unscoped <style> block. Result: 0 remaining :deep(.pagination-*) selectors (was 14).
This commit is contained in:
parent
5ea7853dd6
commit
8f64836999
|
|
@ -69,7 +69,7 @@ function createPagination(totalPages: number, currentPage: number): PaginationPa
|
|||
}
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
pages.push({
|
||||
number: i + 1,
|
||||
isEllipsis: false,
|
||||
|
|
@ -82,10 +82,10 @@ const pages = computed(() => createPagination(props.totalPages, props.currentPag
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// Rules ported from bulma-css-variables/sass/components/pagination.sass.
|
||||
// Slot content (.pagination-previous, .pagination-next, .pagination-link) is
|
||||
// rendered by Pagination.vue / PaginationEmit.vue, so scoped attributes don't
|
||||
// reach them — we use :deep() to style across the slot boundary.
|
||||
// Layout/scaffold rules ported from bulma-css-variables/sass/components/pagination.sass.
|
||||
// BasePagination only owns .pagination / .pagination-list / .pagination-ellipsis —
|
||||
// the actual pagination items (.pagination-previous / -next / -link) and their
|
||||
// styles live in PaginationItem.vue.
|
||||
|
||||
.pagination {
|
||||
align-items: center;
|
||||
|
|
@ -113,9 +113,6 @@ const pages = computed(() => createPagination(props.totalPages, props.currentPag
|
|||
}
|
||||
}
|
||||
|
||||
:deep(.pagination-previous),
|
||||
:deep(.pagination-next),
|
||||
:deep(.pagination-link),
|
||||
.pagination-ellipsis {
|
||||
appearance: none;
|
||||
align-items: center;
|
||||
|
|
@ -136,64 +133,6 @@ const pages = computed(() => createPagination(props.totalPages, props.currentPag
|
|||
-webkit-touch-callout: none;
|
||||
user-select: none;
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&[disabled],
|
||||
fieldset[disabled] & {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.pagination-previous),
|
||||
:deep(.pagination-next),
|
||||
:deep(.pagination-link) {
|
||||
border-color: var(--border);
|
||||
color: var(--text-strong);
|
||||
min-inline-size: 2.5em;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--link-hover-border);
|
||||
color: var(--link-hover);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: var(--link-focus-border);
|
||||
}
|
||||
|
||||
&:active {
|
||||
box-shadow: inset 0 1px 2px rgba($scheme-invert, 0.2);
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
background-color: var(--border);
|
||||
border-color: var(--border);
|
||||
box-shadow: none;
|
||||
color: var(--text-light);
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.pagination-previous),
|
||||
:deep(.pagination-next) {
|
||||
padding-inline: 0.75em;
|
||||
white-space: nowrap;
|
||||
|
||||
&:not(:disabled):hover {
|
||||
background: $scheme-main;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.pagination-link.is-current) {
|
||||
background-color: var(--link);
|
||||
border-color: var(--link);
|
||||
color: var(--link-invert);
|
||||
}
|
||||
|
||||
.pagination-ellipsis {
|
||||
color: var(--grey-light);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
|
@ -203,12 +142,6 @@ const pages = computed(() => createPagination(props.totalPages, props.currentPag
|
|||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
:deep(.pagination-previous),
|
||||
:deep(.pagination-next) {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
.pagination-list li {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
|
@ -221,9 +154,6 @@ const pages = computed(() => createPagination(props.totalPages, props.currentPag
|
|||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
:deep(.pagination-previous),
|
||||
:deep(.pagination-next),
|
||||
:deep(.pagination-link),
|
||||
.pagination-ellipsis {
|
||||
margin-block: 0;
|
||||
}
|
||||
|
|
@ -239,14 +169,3 @@ const pages = computed(() => createPagination(props.totalPages, props.currentPag
|
|||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
// Unscoped: this rule relies on ancestors (.app-container.has-background /
|
||||
// .link-share-container.has-background) that live outside BasePagination.
|
||||
// Previously lived in styles/theme/background.scss.
|
||||
.app-container.has-background .pagination-link:not(.is-current),
|
||||
.link-share-container.has-background .pagination-link:not(.is-current) {
|
||||
background: var(--grey-100);
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
|||
|
|
@ -4,38 +4,39 @@
|
|||
:current-page="currentPage"
|
||||
>
|
||||
<template #previous="{ disabled }">
|
||||
<RouterLink
|
||||
:disabled="disabled || undefined"
|
||||
<PaginationItem
|
||||
variant="previous"
|
||||
:to="getRouteForPagination(currentPage - 1)"
|
||||
class="pagination-previous"
|
||||
:disabled="disabled"
|
||||
>
|
||||
{{ $t('misc.previous') }}
|
||||
</RouterLink>
|
||||
</PaginationItem>
|
||||
</template>
|
||||
<template #next="{ disabled }">
|
||||
<RouterLink
|
||||
:disabled="disabled || undefined"
|
||||
<PaginationItem
|
||||
variant="next"
|
||||
:to="getRouteForPagination(currentPage + 1)"
|
||||
class="pagination-next"
|
||||
:disabled="disabled"
|
||||
>
|
||||
{{ $t('misc.next') }}
|
||||
</RouterLink>
|
||||
</PaginationItem>
|
||||
</template>
|
||||
<template #page-link="{ page, isCurrent }">
|
||||
<RouterLink
|
||||
class="pagination-link"
|
||||
:aria-label="'Goto page ' + page.number"
|
||||
:class="{ 'is-current': isCurrent }"
|
||||
<PaginationItem
|
||||
variant="link"
|
||||
:to="getRouteForPagination(page.number)"
|
||||
:is-current="isCurrent"
|
||||
:aria-label="'Goto page ' + page.number"
|
||||
>
|
||||
{{ page.number }}
|
||||
</RouterLink>
|
||||
</PaginationItem>
|
||||
</template>
|
||||
</BasePagination>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import BasePagination from '@/components/base/BasePagination.vue'
|
||||
import PaginationItem from '@/components/misc/PaginationItem.vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
withDefaults(defineProps<{
|
||||
|
|
|
|||
|
|
@ -4,39 +4,39 @@
|
|||
:current-page="currentPage"
|
||||
>
|
||||
<template #previous="{ disabled }">
|
||||
<BaseButton
|
||||
<PaginationItem
|
||||
variant="previous"
|
||||
:disabled="disabled"
|
||||
class="pagination-previous"
|
||||
@click="changePage(currentPage - 1)"
|
||||
>
|
||||
{{ $t('misc.previous') }}
|
||||
</BaseButton>
|
||||
</PaginationItem>
|
||||
</template>
|
||||
<template #next="{ disabled }">
|
||||
<BaseButton
|
||||
<PaginationItem
|
||||
variant="next"
|
||||
:disabled="disabled"
|
||||
class="pagination-next"
|
||||
@click="changePage(currentPage + 1)"
|
||||
>
|
||||
{{ $t('misc.next') }}
|
||||
</BaseButton>
|
||||
</PaginationItem>
|
||||
</template>
|
||||
<template #page-link="{ page, isCurrent }">
|
||||
<BaseButton
|
||||
class="pagination-link"
|
||||
<PaginationItem
|
||||
variant="link"
|
||||
:is-current="isCurrent"
|
||||
:aria-label="'Goto page ' + page.number"
|
||||
:class="{ 'is-current': isCurrent }"
|
||||
@click="changePage(page.number)"
|
||||
>
|
||||
{{ page.number }}
|
||||
</BaseButton>
|
||||
</PaginationItem>
|
||||
</template>
|
||||
</BasePagination>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import BasePagination from '@/components/base/BasePagination.vue'
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
import PaginationItem from '@/components/misc/PaginationItem.vue'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
totalPages: number,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,146 @@
|
|||
<template>
|
||||
<RouterLink
|
||||
v-if="to !== undefined"
|
||||
:to="to"
|
||||
:disabled="disabled || undefined"
|
||||
:class="[`pagination-${variant}`, {'is-current': isCurrent}]"
|
||||
>
|
||||
<slot />
|
||||
</RouterLink>
|
||||
<BaseButton
|
||||
v-else
|
||||
:disabled="disabled"
|
||||
:class="[`pagination-${variant}`, {'is-current': isCurrent}]"
|
||||
@click="emit('click')"
|
||||
>
|
||||
<slot />
|
||||
</BaseButton>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type {RouteLocationRaw} from 'vue-router'
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
|
||||
withDefaults(defineProps<{
|
||||
variant: 'previous' | 'next' | 'link',
|
||||
isCurrent?: boolean,
|
||||
disabled?: boolean,
|
||||
to?: RouteLocationRaw,
|
||||
}>(), {
|
||||
isCurrent: false,
|
||||
disabled: false,
|
||||
to: undefined,
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'click'): void,
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// Rules ported from bulma-css-variables/sass/components/pagination.sass.
|
||||
// PaginationItem owns the .pagination-previous / .pagination-next /
|
||||
// .pagination-link markup, so scoped attributes attach directly to these
|
||||
// classes — no :deep() necessary.
|
||||
|
||||
.pagination-previous,
|
||||
.pagination-next,
|
||||
.pagination-link {
|
||||
appearance: none;
|
||||
align-items: center;
|
||||
border: 1px solid transparent;
|
||||
border-radius: $radius;
|
||||
box-shadow: none;
|
||||
display: inline-flex;
|
||||
font-size: 1em;
|
||||
block-size: 2.5em;
|
||||
justify-content: center;
|
||||
line-height: 1.5;
|
||||
margin: 0.25rem;
|
||||
padding: calc(0.5em - 1px) 0.5em;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
|
||||
-webkit-touch-callout: none;
|
||||
user-select: none;
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&[disabled],
|
||||
fieldset[disabled] & {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
border-color: var(--border);
|
||||
color: var(--text-strong);
|
||||
min-inline-size: 2.5em;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--link-hover-border);
|
||||
color: var(--link-hover);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: var(--link-focus-border);
|
||||
}
|
||||
|
||||
&:active {
|
||||
box-shadow: inset 0 1px 2px rgba($scheme-invert, 0.2);
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
background-color: var(--border);
|
||||
border-color: var(--border);
|
||||
box-shadow: none;
|
||||
color: var(--text-light);
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-previous,
|
||||
.pagination-next {
|
||||
padding-inline: 0.75em;
|
||||
white-space: nowrap;
|
||||
|
||||
&:not(:disabled):hover {
|
||||
background: $scheme-main;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-link.is-current {
|
||||
background-color: var(--link);
|
||||
border-color: var(--link);
|
||||
color: var(--link-invert);
|
||||
}
|
||||
|
||||
@media screen and (max-width: $tablet - 1px) {
|
||||
.pagination-previous,
|
||||
.pagination-next {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: $tablet), print {
|
||||
.pagination-previous,
|
||||
.pagination-next,
|
||||
.pagination-link {
|
||||
margin-block: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
// Unscoped: this rule relies on ancestors (.app-container.has-background /
|
||||
// .link-share-container.has-background) that live outside PaginationItem.
|
||||
// Previously lived in styles/theme/background.scss, then BasePagination.vue.
|
||||
.app-container.has-background .pagination-link:not(.is-current),
|
||||
.link-share-container.has-background .pagination-link:not(.is-current) {
|
||||
background: var(--grey-100);
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue