Skip to content

Commit 6de30b7

Browse files
authored
Merge pull request #95 from SafeExamBrowser/Refactor-Entity-Table
Refactor entity table
2 parents 750efae + c222dc3 commit 6de30b7

42 files changed

Lines changed: 1315 additions & 979 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

client/src/components/blocks/entity-table/EntityTable.vue

Lines changed: 106 additions & 446 deletions
Large diffs are not rendered by default.
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<template>
2+
<div class="pt-4">
3+
<v-divider />
4+
5+
<v-row no-gutters class="px-5 pt-4 pb-3 align-center">
6+
<v-col cols="12" md="4" class="d-none d-md-block" />
7+
8+
<v-col cols="12" md="4" class="d-flex justify-center">
9+
<v-pagination
10+
:model-value="page"
11+
:length="pageCount"
12+
:total-visible="5"
13+
active-color="primary"
14+
density="comfortable"
15+
rounded="lg"
16+
variant="text"
17+
prev-icon="mdi-chevron-left"
18+
next-icon="mdi-chevron-right"
19+
class="text-medium-emphasis"
20+
@update:model-value="emit('update:page', $event)"
21+
/>
22+
</v-col>
23+
24+
<v-col
25+
cols="12"
26+
md="4"
27+
class="d-flex justify-center justify-md-end"
28+
>
29+
<div
30+
class="d-flex flex-wrap align-center justify-center justify-md-end ga-3"
31+
>
32+
<span
33+
class="text-body-2 text-medium-emphasis font-weight-medium text-no-wrap"
34+
>
35+
{{ $t("general.itemsPerPage") }}
36+
</span>
37+
38+
<v-select
39+
:model-value="itemsPerPage"
40+
:items="itemsPerPageOptions"
41+
base-color="primary"
42+
color="primary"
43+
max-width="90"
44+
min-width="90"
45+
variant="outlined"
46+
density="compact"
47+
rounded="lg"
48+
hide-details
49+
class="text-body-2 font-weight-semibold"
50+
@update:model-value="
51+
emit('update:items-per-page', $event)
52+
"
53+
/>
54+
</div>
55+
</v-col>
56+
</v-row>
57+
</div>
58+
</template>
59+
60+
<script setup lang="ts">
61+
defineProps<{
62+
page: number;
63+
itemsPerPage: number;
64+
pageCount: number;
65+
itemsPerPageOptions: number[];
66+
}>();
67+
68+
const emit = defineEmits<{
69+
"update:page": [page: number];
70+
"update:items-per-page": [itemsPerPage: number];
71+
}>();
72+
</script>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<template>
2+
<div class="d-flex justify-center align-center ga-1">
3+
<v-hover
4+
v-for="action in visibleActions"
5+
:key="action.key"
6+
v-slot="{ isHovering, props: hoverProps }"
7+
>
8+
<v-btn
9+
v-bind="hoverProps"
10+
:icon="action.icon"
11+
:disabled="action.disabled?.(item) ?? false"
12+
:aria-label="action.label"
13+
variant="text"
14+
density="comfortable"
15+
size="small"
16+
rounded="lg"
17+
class="text-medium-emphasis"
18+
:color="isHovering ? (action.color ?? 'primary') : undefined"
19+
@click.stop="action.onClick(item)"
20+
/>
21+
</v-hover>
22+
</div>
23+
</template>
24+
25+
<script setup lang="ts">
26+
import { computed } from "vue";
27+
import type {
28+
TableAction,
29+
TableItem,
30+
} from "@/components/blocks/entity-table/types.ts";
31+
32+
const props = defineProps<{
33+
item: TableItem;
34+
actions: TableAction[];
35+
}>();
36+
37+
const visibleActions = computed(() =>
38+
props.actions.filter((a) => a.visible?.(props.item) ?? true),
39+
);
40+
</script>

client/src/components/blocks/entity-table/composables/useTableCellFormatters.ts

Lines changed: 0 additions & 95 deletions
This file was deleted.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { computed, type MaybeRefOrGetter, toValue } from "vue";
2+
import type { TableHeader } from "@/components/blocks/entity-table/types.ts";
3+
4+
export function useTableHeaders(
5+
headers: MaybeRefOrGetter<TableHeader[]>,
6+
hasActions: MaybeRefOrGetter<boolean>,
7+
) {
8+
const computedHeaders = computed<TableHeader[]>(() => {
9+
const base = [...toValue(headers)];
10+
11+
if (toValue(hasActions)) {
12+
base.push({
13+
title: "",
14+
key: "_actions",
15+
sortable: false,
16+
width: "1%",
17+
align: "center",
18+
});
19+
}
20+
21+
return base;
22+
});
23+
24+
return { computedHeaders };
25+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { type MaybeRefOrGetter, toValue } from "vue";
2+
import type {
3+
CellFormatter,
4+
TableItem,
5+
} from "@/components/blocks/entity-table/types.ts";
6+
7+
export function useTableItems(
8+
cellFormatters: MaybeRefOrGetter<Record<string, CellFormatter> | undefined>,
9+
) {
10+
function getRawItem(item: unknown): TableItem {
11+
if (
12+
item &&
13+
typeof item === "object" &&
14+
"raw" in item &&
15+
item.raw &&
16+
typeof item.raw === "object"
17+
) {
18+
return item.raw as TableItem;
19+
}
20+
21+
return item as TableItem;
22+
}
23+
24+
function formatCell(key: string, item: TableItem): string {
25+
const formatter = toValue(cellFormatters)?.[key];
26+
27+
if (formatter) return formatter(item[key], item);
28+
29+
const value = item[key];
30+
31+
if (value === null || value === undefined) return "";
32+
33+
return String(value);
34+
}
35+
36+
return { getRawItem, formatCell };
37+
}

client/src/components/blocks/entity-table/composables/useTableNavigation.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ export function useTableNavigation(
5151

5252
return {
5353
identifierKey,
54-
getItemIdentifier,
5554
buildItemRoute,
5655
navigateToItem,
5756
};
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { computed, type MaybeRefOrGetter, toValue } from "vue";
2+
import type { ServerTablePaging } from "@/models/types.ts";
3+
4+
type SortItemForTable = { key: string; order?: boolean | "asc" | "desc" };
5+
6+
const DEFAULT_ITEMS_PER_PAGE = 10;
7+
8+
export function useTablePagination(deps: {
9+
options: MaybeRefOrGetter<ServerTablePaging | undefined>;
10+
itemsPerPage: MaybeRefOrGetter<number | undefined>;
11+
pageCount: MaybeRefOrGetter<number | undefined>;
12+
items: MaybeRefOrGetter<readonly unknown[]>;
13+
itemsLength: MaybeRefOrGetter<number | undefined>;
14+
emit: (options: ServerTablePaging) => void;
15+
}) {
16+
const currentPage = computed(() => toValue(deps.options)?.page ?? 1);
17+
18+
const currentItemsPerPage = computed(
19+
() =>
20+
toValue(deps.options)?.itemsPerPage ??
21+
toValue(deps.itemsPerPage) ??
22+
DEFAULT_ITEMS_PER_PAGE,
23+
);
24+
25+
const resolvedPageCount = computed(() => toValue(deps.pageCount) ?? 0);
26+
27+
const itemsPerPageOptions = computed(() => {
28+
const pc = resolvedPageCount.value;
29+
const ic = toValue(deps.items).length;
30+
const opts = [5];
31+
32+
if (pc > 1 || ic > 5) opts.push(10);
33+
if (pc > 2 || ic > 10) opts.push(15);
34+
35+
return opts;
36+
});
37+
38+
const internalItemsLength = computed(() => {
39+
const override = toValue(deps.itemsLength);
40+
if (override !== undefined) return override;
41+
42+
const page = currentPage.value;
43+
const perPage = currentItemsPerPage.value;
44+
const pc = resolvedPageCount.value;
45+
const ic = toValue(deps.items).length;
46+
47+
if (pc === 0) return ic;
48+
if (page === pc) {
49+
return (pc - 1) * perPage + ic;
50+
}
51+
return pc * perPage;
52+
});
53+
54+
const sortByForTable = computed<SortItemForTable[]>(() =>
55+
(toValue(deps.options)?.sortBy ?? []).map((s) => ({
56+
key: s.key,
57+
order: s.order as "asc" | "desc" | undefined,
58+
})),
59+
);
60+
61+
function emitOptionsUpdate(partial: Partial<ServerTablePaging>) {
62+
deps.emit({
63+
page: currentPage.value,
64+
itemsPerPage: currentItemsPerPage.value,
65+
sortBy: toValue(deps.options)?.sortBy ?? [],
66+
...partial,
67+
});
68+
}
69+
70+
function onPageUpdate(page: number) {
71+
emitOptionsUpdate({ page });
72+
}
73+
74+
function onItemsPerPageUpdate(itemsPerPage: number) {
75+
emitOptionsUpdate({ page: 1, itemsPerPage });
76+
}
77+
78+
return {
79+
currentPage,
80+
currentItemsPerPage,
81+
resolvedPageCount,
82+
itemsPerPageOptions,
83+
internalItemsLength,
84+
sortByForTable,
85+
onPageUpdate,
86+
onItemsPerPageUpdate,
87+
};
88+
}

0 commit comments

Comments
 (0)