feat: Table footer design updates (#9194)

* feat: table footer component cleanup

* Update TableFooter.vue

* feat: Update design

* chore: remove RTL mixin

* chore: Make component in composable format

* chore: review fixes
This commit is contained in:
Muhsin Keloth
2024-04-09 06:20:41 +05:30
committed by GitHub
parent 1038d1500e
commit 78724f7459
10 changed files with 224 additions and 163 deletions

View File

@@ -1,171 +1,48 @@
<template> <template>
<footer <footer
v-if="isFooterVisible" v-if="isFooterVisible"
class="bg-white dark:bg-slate-800 h-12 border-t border-solid border-slate-75 dark:border-slate-700/50 flex items-center justify-between px-6" class="bg-white dark:bg-slate-900 h-12 flex items-center justify-between px-6"
> >
<div class="left-aligned-wrap"> <table-footer-results
<div class="text-xs text-slate-600 dark:text-slate-200"> :first-index="firstIndex"
<strong>{{ firstIndex }}</strong> :last-index="lastIndex"
- <strong>{{ lastIndex }}</strong> of :total-count="totalCount"
<strong>{{ totalCount }}</strong> items />
</div> <table-footer-pagination
</div> v-if="totalCount"
<div class="right-aligned-wrap"> :current-page="currentPage"
<div :total-pages="totalPages"
v-if="totalCount" :total-count="totalCount"
class="primary button-group pagination-button-group" :page-size="pageSize"
> @page-change="$emit('page-change', $event)"
<woot-button />
size="small"
variant="smooth"
color-scheme="secondary"
class-names="goto-first"
:is-disabled="hasFirstPage"
@click="onFirstPage"
>
<fluent-icon icon="chevron-left" size="18" />
<fluent-icon
icon="chevron-left"
size="18"
:class="pageFooterIconClass"
/>
</woot-button>
<woot-button
size="small"
variant="smooth"
color-scheme="secondary"
:is-disabled="hasPrevPage"
@click="onPrevPage"
>
<fluent-icon icon="chevron-left" size="18" />
</woot-button>
<woot-button
size="small"
variant="smooth"
color-scheme="secondary"
@click.prevent
>
{{ currentPage }}
</woot-button>
<woot-button
size="small"
variant="smooth"
color-scheme="secondary"
:is-disabled="hasNextPage"
@click="onNextPage"
>
<fluent-icon icon="chevron-right" size="18" />
</woot-button>
<woot-button
size="small"
variant="smooth"
color-scheme="secondary"
class-names="goto-last"
:is-disabled="hasLastPage"
@click="onLastPage"
>
<fluent-icon icon="chevron-right" size="18" />
<fluent-icon
icon="chevron-right"
size="18"
:class="pageFooterIconClass"
/>
</woot-button>
</div>
</div>
</footer> </footer>
</template> </template>
<script> <script setup>
import rtlMixin from 'shared/mixins/rtlMixin'; import { computed } from 'vue';
import TableFooterResults from './TableFooterResults.vue';
export default { import TableFooterPagination from './TableFooterPagination.vue';
components: {}, const props = defineProps({
mixins: [rtlMixin], currentPage: {
props: { type: Number,
currentPage: { default: 1,
type: Number,
default: 1,
},
pageSize: {
type: Number,
default: 25,
},
totalCount: {
type: Number,
default: 0,
},
}, },
computed: { pageSize: {
pageFooterIconClass() { type: Number,
return this.isRTLView ? '-mr-3' : '-ml-3'; default: 25,
},
isFooterVisible() {
return this.totalCount && !(this.firstIndex > this.totalCount);
},
firstIndex() {
return this.pageSize * (this.currentPage - 1) + 1;
},
lastIndex() {
return Math.min(this.totalCount, this.pageSize * this.currentPage);
},
searchButtonClass() {
return this.searchQuery !== '' ? 'show' : '';
},
hasLastPage() {
return !!Math.ceil(this.totalCount / this.pageSize);
},
hasFirstPage() {
return this.currentPage === 1;
},
hasNextPage() {
return this.currentPage === Math.ceil(this.totalCount / this.pageSize);
},
hasPrevPage() {
return this.currentPage === 1;
},
}, },
methods: { totalCount: {
onNextPage() { type: Number,
if (this.hasNextPage) { default: 0,
return;
}
const newPage = this.currentPage + 1;
this.onPageChange(newPage);
},
onPrevPage() {
if (this.hasPrevPage) {
return;
}
const newPage = this.currentPage - 1;
this.onPageChange(newPage);
},
onFirstPage() {
if (this.hasFirstPage) {
return;
}
const newPage = 1;
this.onPageChange(newPage);
},
onLastPage() {
if (this.hasLastPage) {
return;
}
const newPage = Math.ceil(this.totalCount / this.pageSize);
this.onPageChange(newPage);
},
onPageChange(page) {
this.$emit('page-change', page);
},
}, },
}; });
const totalPages = computed(() => Math.ceil(props.totalCount / props.pageSize));
const firstIndex = computed(() => props.pageSize * (props.currentPage - 1) + 1);
const lastIndex = computed(() =>
Math.min(props.totalCount, props.pageSize * props.currentPage)
);
const isFooterVisible = computed(
() => props.totalCount && !(firstIndex.value > props.totalCount)
);
</script> </script>
<style lang="scss" scoped>
.goto-first,
.goto-last {
i:last-child {
@apply -ml-1;
}
}
</style>

View File

@@ -0,0 +1,143 @@
<template>
<div class="flex items-center bg-slate-50 dark:bg-slate-800 h-8 rounded-lg">
<woot-button
size="small"
variant="smooth"
color-scheme="secondary"
:is-disabled="hasFirstPage"
class-names="dark:!bg-slate-800 !opacity-100 ltr:rounded-l-lg ltr:rounded-r-none rtl:rounded-r-lg rtl:rounded-l-none"
:class="buttonClass(hasFirstPage)"
@click="onFirstPage"
>
<fluent-icon
icon="chevrons-left"
size="20"
icon-lib="lucide"
:class="hasFirstPage && 'opacity-40'"
/>
</woot-button>
<div class="bg-slate-75 dark:bg-slate-700/50 w-px rounded-sm h-4" />
<woot-button
size="small"
variant="smooth"
color-scheme="secondary"
:is-disabled="hasPrevPage"
class-names="dark:!bg-slate-800 !opacity-100 rounded-none"
:class="buttonClass(hasPrevPage)"
@click="onPrevPage"
>
<fluent-icon
icon="chevron-left-single"
size="20"
icon-lib="lucide"
:class="hasPrevPage && 'opacity-40'"
/>
</woot-button>
<div
class="flex px-3 items-center gap-3 tabular-nums bg-slate-50 dark:bg-slate-800 text-slate-700 dark:text-slate-100"
>
<span class="text-sm text-slate-800 dark:text-slate-75">
{{ currentPage }}
</span>
<span class="text-slate-600 dark:text-slate-500">/</span>
<span class="text-sm text-slate-600 dark:text-slate-500">
{{ totalPages }}
</span>
</div>
<woot-button
size="small"
variant="smooth"
color-scheme="secondary"
:is-disabled="hasNextPage"
class-names="dark:!bg-slate-800 !opacity-100 rounded-none"
:class="buttonClass(hasNextPage)"
@click="onNextPage"
>
<fluent-icon
icon="chevron-right-single"
size="20"
icon-lib="lucide"
:class="hasNextPage && 'opacity-40'"
/>
</woot-button>
<div class="bg-slate-75 dark:bg-slate-700/50 w-px rounded-sm h-4" />
<woot-button
size="small"
variant="smooth"
color-scheme="secondary"
class-names="dark:!bg-slate-800 !opacity-100 ltr:rounded-r-lg ltr:rounded-l-none rtl:rounded-l-lg rtl:rounded-r-none"
:class="buttonClass(hasLastPage)"
:is-disabled="hasLastPage"
@click="onLastPage"
>
<fluent-icon
icon="chevrons-right"
size="20"
icon-lib="lucide"
:class="hasLastPage && 'opacity-40'"
/>
</woot-button>
</div>
</template>
<script setup>
import { computed, defineEmits } from 'vue';
// Props
const props = defineProps({
currentPage: {
type: Number,
default: 1,
},
totalCount: {
type: Number,
default: 0,
},
totalPages: {
type: Number,
default: 0,
},
});
const hasLastPage = computed(
() => props.currentPage === props.totalPages || props.totalPages === 1
);
const hasFirstPage = computed(() => props.currentPage === 1);
const hasNextPage = computed(() => props.currentPage === props.totalPages);
const hasPrevPage = computed(() => props.currentPage === 1);
const emit = defineEmits(['page-change']);
function buttonClass(hasPage) {
if (hasPage) {
return 'hover:!bg-slate-50 dark:hover:!bg-slate-800';
}
return 'dark:hover:!bg-slate-700/50';
}
function onPageChange(newPage) {
emit('page-change', newPage);
}
const onNextPage = () => {
if (!onNextPage.value) {
onPageChange(props.currentPage + 1);
}
};
const onPrevPage = () => {
if (!hasPrevPage.value) {
onPageChange(props.currentPage - 1);
}
};
const onFirstPage = () => {
if (!hasFirstPage.value) {
onPageChange(1);
}
};
const onLastPage = () => {
if (!hasLastPage.value) {
onPageChange(props.totalPages);
}
};
</script>

View File

@@ -0,0 +1,28 @@
<template>
<span class="text-sm text-slate-700 dark:text-slate-200 font-medium">
{{
$t('GENERAL.SHOWING_RESULTS', {
firstIndex,
lastIndex,
totalCount,
})
}}
</span>
</template>
<script setup>
defineProps({
firstIndex: {
type: Number,
default: 0,
},
lastIndex: {
type: Number,
default: 0,
},
totalCount: {
type: Number,
default: 0,
},
});
</script>

View File

@@ -0,0 +1,5 @@
{
"GENERAL": {
"SHOWING_RESULTS": "Showing {firstIndex}-{lastIndex} of {totalCount} items"
}
}

View File

@@ -31,6 +31,7 @@ import teamsSettings from './teamsSettings.json';
import whatsappTemplates from './whatsappTemplates.json'; import whatsappTemplates from './whatsappTemplates.json';
import sla from './sla.json'; import sla from './sla.json';
import inbox from './inbox.json'; import inbox from './inbox.json';
import general from './general.json';
export default { export default {
...advancedFilters, ...advancedFilters,
@@ -66,4 +67,5 @@ export default {
...teamsSettings, ...teamsSettings,
...whatsappTemplates, ...whatsappTemplates,
...inbox, ...inbox,
...general,
}; };

View File

@@ -25,6 +25,7 @@
@on-sort-change="onSortChange" @on-sort-change="onSortChange"
/> />
<table-footer <table-footer
class="border-t border-slate-75 dark:border-slate-700/50"
:current-page="Number(meta.currentPage)" :current-page="Number(meta.currentPage)"
:total-count="meta.count" :total-count="meta.count"
:page-size="15" :page-size="15"

View File

@@ -59,7 +59,7 @@
:current-page="currentPage" :current-page="currentPage"
:total-count="totalCount" :total-count="totalCount"
:page-size="pageSize" :page-size="pageSize"
class="dark:bg-slate-900 sticky bottom-0" class="dark:bg-slate-900 sticky bottom-0 border-t border-slate-75 dark:border-slate-700/50"
@page-change="onPageChange" @page-change="onPageChange"
/> />
</div> </div>

View File

@@ -9,6 +9,7 @@
:on-mark-all-done-click="onMarkAllDoneClick" :on-mark-all-done-click="onMarkAllDoneClick"
/> />
<table-footer <table-footer
class="border-t border-slate-75 dark:border-slate-700/50"
:current-page="Number(meta.currentPage)" :current-page="Number(meta.currentPage)"
:total-count="meta.count" :total-count="meta.count"
:page-size="15" :page-size="15"

View File

@@ -57,7 +57,7 @@
:current-page="Number(meta.currentPage)" :current-page="Number(meta.currentPage)"
:total-count="meta.totalEntries" :total-count="meta.totalEntries"
:page-size="meta.perPage" :page-size="meta.perPage"
class="dark:bg-slate-900" class="!bg-slate-25 dark:!bg-slate-900 border-t border-slate-75 dark:border-slate-700/50"
@page-change="onPageChange" @page-change="onPageChange"
/> />
</div> </div>

View File

@@ -268,5 +268,9 @@
"M8.66675 11.1667L10.3334 12.8333L13.6667 9.5", "M8.66675 11.1667L10.3334 12.8333L13.6667 9.5",
"M11.1667 17.8333C14.8486 17.8333 17.8333 14.8486 17.8333 11.1667C17.8333 7.48477 14.8486 4.5 11.1667 4.5C7.48477 4.5 4.5 7.48477 4.5 11.1667C4.5 14.8486 7.48477 17.8333 11.1667 17.8333Z", "M11.1667 17.8333C14.8486 17.8333 17.8333 14.8486 17.8333 11.1667C17.8333 7.48477 14.8486 4.5 11.1667 4.5C7.48477 4.5 4.5 7.48477 4.5 11.1667C4.5 14.8486 7.48477 17.8333 11.1667 17.8333Z",
"M19.5001 19.5001L15.9167 15.9167" "M19.5001 19.5001L15.9167 15.9167"
] ],
"chevrons-left-outline": ["m11 17-5-5 5-5", "m18 17-5-5 5-5"],
"chevron-left-single-outline": "m15 18-6-6 6-6",
"chevrons-right-outline": ["m6 17 5-5-5-5", "m13 17 5-5-5-5"],
"chevron-right-single-outline": "m9 18 6-6-6-6"
} }