chore: split DynTable into classes

This commit is contained in:
Kazhnuz 2023-02-11 19:46:58 +01:00
parent 787146ec6d
commit 07e8986ed6
6 changed files with 231 additions and 111 deletions

View file

@ -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>

View 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;
}
}
}

View 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;
}
}

View 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);
}
}

View 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
View 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 };