chore: split DynTable into classes
This commit is contained in:
parent
787146ec6d
commit
07e8986ed6
6 changed files with 231 additions and 111 deletions
|
@ -1,26 +1,10 @@
|
|||
<script lang="ts" setup>
|
||||
import { computed, ref, onMounted } from "vue";
|
||||
import { computed, reactive, onMounted } from "vue";
|
||||
import PaginatedFilteredTable from "@/utils/tables/PaginatedFilteredTable";
|
||||
import type { TableField } from "@/utils/tables/types";
|
||||
import type { PropType } from "vue";
|
||||
import axios from "axios";
|
||||
|
||||
interface TableField {
|
||||
key: string;
|
||||
label: string;
|
||||
canBeFiltered?: boolean;
|
||||
}
|
||||
|
||||
interface Filters {
|
||||
[key: string]: boolean;
|
||||
}
|
||||
|
||||
interface FilterList {
|
||||
[key: string]: { title: string; filters: Filters };
|
||||
}
|
||||
|
||||
interface TableItem {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
fields: {
|
||||
type: Array as PropType<TableField[]>,
|
||||
|
@ -32,130 +16,60 @@ const props = defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
const items = ref([] as TableItem[]);
|
||||
const filters = ref({} as FilterList);
|
||||
const pageNumber = ref(0);
|
||||
const elemByPage = ref(10);
|
||||
const DEFAULT_MAX_ITEM_BY_PAGE = 10;
|
||||
|
||||
const displayedFieldKeys = computed(() => {
|
||||
return Object.entries(props.fields).map(([key, value]) => value.key);
|
||||
const table = reactive(new PaginatedFilteredTable(DEFAULT_MAX_ITEM_BY_PAGE));
|
||||
|
||||
const filtersFieldMap = computed(() => {
|
||||
return table?.filteredTable?.filtersFieldMap;
|
||||
});
|
||||
|
||||
function haveFilter(filterName: string) {
|
||||
for (const val in filters.value[filterName].filters) {
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
filters.value[filterName].filters,
|
||||
val
|
||||
)
|
||||
) {
|
||||
if (filters.value[filterName].filters[val]) {
|
||||
console.log(filterName, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(filterName, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
const filteredItems = computed(() => {
|
||||
var filteredItem = items.value as TableItem[];
|
||||
|
||||
for (const filter in filters.value) {
|
||||
if (haveFilter(filter)) {
|
||||
filteredItem = filteredItem.filter((value) => {
|
||||
return filters.value[filter].filters[value[filter] as string];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return filteredItem;
|
||||
const displayedFieldKeys = computed(() => {
|
||||
return table?.filteredTable?.table.displayedFieldKeys;
|
||||
});
|
||||
|
||||
const paginatedList = computed(() => {
|
||||
var filteredItem = filteredItems.value as TableItem[];
|
||||
|
||||
filteredItem = filteredItem.filter((value, index) => {
|
||||
const pageBegin = pageNumber.value * elemByPage.value;
|
||||
return index >= pageBegin && index < pageBegin + elemByPage.value;
|
||||
});
|
||||
|
||||
return filteredItem;
|
||||
return table?.items;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
for (const field of props.fields) {
|
||||
if (field.canBeFiltered) {
|
||||
filters.value[field.key] = {
|
||||
title: field.label,
|
||||
filters: {} as Filters,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
table.fields = props.fields;
|
||||
for (const file of props.files) {
|
||||
const listItems = `/jdr/objets/${file}.json`;
|
||||
axios.get(listItems).then((response) => {
|
||||
items.value = items.value.concat(response.data);
|
||||
response.data.forEach((element: TableItem) => {
|
||||
for (const key in element) {
|
||||
if (Object.prototype.hasOwnProperty.call(element, key)) {
|
||||
if (filters.value[key]) {
|
||||
filters.value[key].filters[element[key] as string] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
table.addItems(response.data);
|
||||
table.currentPage = 0;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function switchFilter(filterSet: string | number, filterName: string | number) {
|
||||
if (filters.value[filterSet]?.filters[filterName] !== undefined) {
|
||||
filters.value[filterSet].filters[filterName] =
|
||||
!filters.value[filterSet].filters[filterName];
|
||||
}
|
||||
setPage(0);
|
||||
table.switchFilter(filterSet as string, filterName as string);
|
||||
}
|
||||
|
||||
function removeFilter(filterSet: string | number) {
|
||||
for (const val in filters.value[filterSet].filters) {
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
filters.value[filterSet].filters,
|
||||
val
|
||||
)
|
||||
) {
|
||||
filters.value[filterSet].filters[val] = false;
|
||||
}
|
||||
}
|
||||
setPage(0);
|
||||
table.removeAllFilters(filterSet as string);
|
||||
}
|
||||
|
||||
const getTotalPage = computed(() => {
|
||||
return Math.floor((filteredItems.value.length - 1) / elemByPage.value);
|
||||
const currentPage = computed(() => {
|
||||
return table?.currentPage;
|
||||
});
|
||||
|
||||
function setPage(page: number) {
|
||||
pageNumber.value = page;
|
||||
}
|
||||
const getTotalPage = computed(() => {
|
||||
return table?.pageNumber;
|
||||
});
|
||||
|
||||
function canGo(rel: number) {
|
||||
return (
|
||||
pageNumber.value + rel >= 0 && pageNumber.value + rel <= getTotalPage.value
|
||||
);
|
||||
return table?.canGo(rel);
|
||||
}
|
||||
|
||||
function go(rel: number) {
|
||||
if (canGo(rel)) {
|
||||
setPage(pageNumber.value + rel);
|
||||
}
|
||||
table?.go(rel);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="small-text" v-for="(filter, key) in filters" :key="key">
|
||||
<div class="small-text" v-for="(filter, key) in filtersFieldMap" :key="key">
|
||||
<strong>{{ filter.title }} : </strong>
|
||||
<button
|
||||
v-for="(isTrue, filterName) in filter.filters"
|
||||
|
@ -189,7 +103,7 @@ function go(rel: number) {
|
|||
<div class="d-flex f-between f-middle nextprevious small-text">
|
||||
<div>
|
||||
<strong class="btn m-0 pages pl-0">
|
||||
Page {{ pageNumber + 1 }} / {{ getTotalPage + 1 }}
|
||||
Page {{ currentPage + 1 }} / {{ getTotalPage + 1 }}
|
||||
</strong>
|
||||
</div>
|
||||
|
||||
|
|
89
src/utils/tables/FilteredHtmlTable.ts
Normal file
89
src/utils/tables/FilteredHtmlTable.ts
Normal file
|
@ -0,0 +1,89 @@
|
|||
import SimpleHtmlTable from "./SimpleHtmlTable";
|
||||
import { forEachCols, getColumn } from "./tableUtils";
|
||||
import type { FilterFieldsMap, Filters, TableField, TableItem } from "./types";
|
||||
|
||||
export default class FilteredHtmlTable {
|
||||
table: SimpleHtmlTable;
|
||||
filtersFieldMap: FilterFieldsMap = {};
|
||||
|
||||
constructor() {
|
||||
this.table = new SimpleHtmlTable();
|
||||
}
|
||||
|
||||
private initFilters() {
|
||||
this.filtersFieldMap = {};
|
||||
for (const field of this.table.fields) {
|
||||
if (field.canBeFiltered) {
|
||||
// Create a filter for the field, that'll contain every
|
||||
// filters possible for the field
|
||||
this.filtersFieldMap[field.key] = {
|
||||
title: field.label,
|
||||
filters: {} as Filters,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set fields(fields: TableField[]) {
|
||||
this.table.fields = fields;
|
||||
this.initFilters();
|
||||
}
|
||||
|
||||
addItems(rows: TableItem[]) {
|
||||
this.table.addItems(rows);
|
||||
forEachCols(rows, (colName: string, col: string) => {
|
||||
this.setFilterableValue(colName, col);
|
||||
});
|
||||
}
|
||||
|
||||
get items() {
|
||||
let filteredItem = this.table.items as TableItem[];
|
||||
|
||||
for (const fieldKey in this.filtersFieldMap) {
|
||||
if (this.haveFilter(fieldKey)) {
|
||||
filteredItem = filteredItem.filter((row) => {
|
||||
const filters = this.getFiltersForField(fieldKey);
|
||||
return !filters || filters[getColumn(row, fieldKey) as string];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return filteredItem;
|
||||
}
|
||||
|
||||
private setFilterableValue(fieldKey: string, filterableValue: string) {
|
||||
const filters = this.getFiltersForField(fieldKey);
|
||||
if (filters) {
|
||||
filters[filterableValue] = false;
|
||||
}
|
||||
}
|
||||
|
||||
private getFiltersForField(fieldKey: string) {
|
||||
if (this.filtersFieldMap[fieldKey]) {
|
||||
return this.filtersFieldMap[fieldKey].filters;
|
||||
}
|
||||
}
|
||||
|
||||
private haveFilter(fieldKey: string) {
|
||||
for (const val in this.getFiltersForField(fieldKey)) {
|
||||
if (this.filtersFieldMap[fieldKey].filters[val]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public switchFilter(fieldKey: string, filterValue: string) {
|
||||
const filters = this.getFiltersForField(fieldKey);
|
||||
if (filters && filters[filterValue] !== undefined) {
|
||||
filters[filterValue] = !filters[filterValue];
|
||||
}
|
||||
}
|
||||
|
||||
public removeAllFilters(fieldKey: string) {
|
||||
const filters = this.getFiltersForField(fieldKey);
|
||||
for (const filterValue in filters) {
|
||||
filters[filterValue] = false;
|
||||
}
|
||||
}
|
||||
}
|
58
src/utils/tables/PaginatedFilteredTable.ts
Normal file
58
src/utils/tables/PaginatedFilteredTable.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
import FilteredHtmlTable from "./FilteredHtmlTable";
|
||||
import type { TableField, TableItem } from "./types";
|
||||
|
||||
export default class PaginatedFilteredTable {
|
||||
filteredTable: FilteredHtmlTable;
|
||||
pageLenght: number;
|
||||
currentPage = 0;
|
||||
|
||||
constructor(pageLenght: number) {
|
||||
this.filteredTable = new FilteredHtmlTable();
|
||||
this.pageLenght = pageLenght;
|
||||
}
|
||||
|
||||
get pageNumber() {
|
||||
return Math.floor((this.filteredTable.items.length - 1) / this.pageLenght);
|
||||
}
|
||||
|
||||
get items() {
|
||||
let items = this.filteredTable.items as TableItem[];
|
||||
|
||||
items = items.filter((value, index) => {
|
||||
const pageBegin = this.currentPage * this.pageLenght;
|
||||
return index >= pageBegin && index < pageBegin + this.pageLenght;
|
||||
});
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
set fields(fields: TableField[]) {
|
||||
this.filteredTable.fields = fields;
|
||||
}
|
||||
|
||||
public addItems(items: TableItem[]) {
|
||||
this.filteredTable.addItems(items);
|
||||
}
|
||||
|
||||
public canGo(rel: number) {
|
||||
return (
|
||||
this.currentPage + rel >= 0 && this.currentPage + rel <= this.pageNumber
|
||||
);
|
||||
}
|
||||
|
||||
public go(rel: number) {
|
||||
if (this.canGo(rel)) {
|
||||
this.currentPage = this.currentPage + rel;
|
||||
}
|
||||
}
|
||||
|
||||
public switchFilter(fieldKey: string, filterValue: string) {
|
||||
this.filteredTable.switchFilter(fieldKey, filterValue);
|
||||
this.currentPage = 0;
|
||||
}
|
||||
|
||||
public removeAllFilters(fieldKey: string) {
|
||||
this.filteredTable.removeAllFilters(fieldKey);
|
||||
this.currentPage = 0;
|
||||
}
|
||||
}
|
18
src/utils/tables/SimpleHtmlTable.ts
Normal file
18
src/utils/tables/SimpleHtmlTable.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import type { TableField, TableItem } from "./types";
|
||||
|
||||
export default class SimpleHtmlTable {
|
||||
fields = [] as TableField[];
|
||||
items = [] as TableItem[];
|
||||
|
||||
setFields(fields: TableField[]) {
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
addItems(items: TableItem[]) {
|
||||
this.items = this.items.concat(items);
|
||||
}
|
||||
|
||||
get displayedFieldKeys() {
|
||||
return Object.entries(this.fields).map(([, value]) => value.key);
|
||||
}
|
||||
}
|
17
src/utils/tables/tableUtils.ts
Normal file
17
src/utils/tables/tableUtils.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import type { TableItem } from "./types";
|
||||
|
||||
export function getColumn(row: TableItem, colName: string) {
|
||||
return row[colName];
|
||||
}
|
||||
|
||||
export function forEachCols(rows: TableItem[], func: Function) {
|
||||
rows.forEach((row: TableItem) => {
|
||||
for (const colName in row) {
|
||||
if (Object.prototype.hasOwnProperty.call(row, colName)) {
|
||||
if (getColumn(row, colName)) {
|
||||
func(colName, getColumn(row, colName));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
24
src/utils/tables/types.ts
Normal file
24
src/utils/tables/types.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
interface Table {
|
||||
fields: TableField[];
|
||||
items: TableItem[];
|
||||
}
|
||||
|
||||
interface TableField {
|
||||
key: string;
|
||||
label: string;
|
||||
canBeFiltered?: boolean;
|
||||
}
|
||||
|
||||
interface Filters {
|
||||
[key: string]: boolean;
|
||||
}
|
||||
|
||||
interface FilterFieldsMap {
|
||||
[key: string]: { title: string; filters: Filters };
|
||||
}
|
||||
|
||||
interface TableItem {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export type { Table, TableField, Filters, FilterFieldsMap, TableItem };
|
Loading…
Reference in a new issue