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>
|
<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 type { PropType } from "vue";
|
||||||
import axios from "axios";
|
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({
|
const props = defineProps({
|
||||||
fields: {
|
fields: {
|
||||||
type: Array as PropType<TableField[]>,
|
type: Array as PropType<TableField[]>,
|
||||||
|
@ -32,130 +16,60 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const items = ref([] as TableItem[]);
|
const DEFAULT_MAX_ITEM_BY_PAGE = 10;
|
||||||
const filters = ref({} as FilterList);
|
|
||||||
const pageNumber = ref(0);
|
const table = reactive(new PaginatedFilteredTable(DEFAULT_MAX_ITEM_BY_PAGE));
|
||||||
const elemByPage = ref(10);
|
|
||||||
|
const filtersFieldMap = computed(() => {
|
||||||
|
return table?.filteredTable?.filtersFieldMap;
|
||||||
|
});
|
||||||
|
|
||||||
const displayedFieldKeys = computed(() => {
|
const displayedFieldKeys = computed(() => {
|
||||||
return Object.entries(props.fields).map(([key, value]) => value.key);
|
return table?.filteredTable?.table.displayedFieldKeys;
|
||||||
});
|
|
||||||
|
|
||||||
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 paginatedList = computed(() => {
|
const paginatedList = computed(() => {
|
||||||
var filteredItem = filteredItems.value as TableItem[];
|
return table?.items;
|
||||||
|
|
||||||
filteredItem = filteredItem.filter((value, index) => {
|
|
||||||
const pageBegin = pageNumber.value * elemByPage.value;
|
|
||||||
return index >= pageBegin && index < pageBegin + elemByPage.value;
|
|
||||||
});
|
|
||||||
|
|
||||||
return filteredItem;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
for (const field of props.fields) {
|
table.fields = props.fields;
|
||||||
if (field.canBeFiltered) {
|
|
||||||
filters.value[field.key] = {
|
|
||||||
title: field.label,
|
|
||||||
filters: {} as Filters,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const file of props.files) {
|
for (const file of props.files) {
|
||||||
const listItems = `/jdr/objets/${file}.json`;
|
const listItems = `/jdr/objets/${file}.json`;
|
||||||
axios.get(listItems).then((response) => {
|
axios.get(listItems).then((response) => {
|
||||||
items.value = items.value.concat(response.data);
|
table.addItems(response.data);
|
||||||
response.data.forEach((element: TableItem) => {
|
table.currentPage = 0;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function switchFilter(filterSet: string | number, filterName: string | number) {
|
function switchFilter(filterSet: string | number, filterName: string | number) {
|
||||||
if (filters.value[filterSet]?.filters[filterName] !== undefined) {
|
table.switchFilter(filterSet as string, filterName as string);
|
||||||
filters.value[filterSet].filters[filterName] =
|
|
||||||
!filters.value[filterSet].filters[filterName];
|
|
||||||
}
|
|
||||||
setPage(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeFilter(filterSet: string | number) {
|
function removeFilter(filterSet: string | number) {
|
||||||
for (const val in filters.value[filterSet].filters) {
|
table.removeAllFilters(filterSet as string);
|
||||||
if (
|
|
||||||
Object.prototype.hasOwnProperty.call(
|
|
||||||
filters.value[filterSet].filters,
|
|
||||||
val
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
filters.value[filterSet].filters[val] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setPage(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTotalPage = computed(() => {
|
const currentPage = computed(() => {
|
||||||
return Math.floor((filteredItems.value.length - 1) / elemByPage.value);
|
return table?.currentPage;
|
||||||
});
|
});
|
||||||
|
|
||||||
function setPage(page: number) {
|
const getTotalPage = computed(() => {
|
||||||
pageNumber.value = page;
|
return table?.pageNumber;
|
||||||
}
|
});
|
||||||
|
|
||||||
function canGo(rel: number) {
|
function canGo(rel: number) {
|
||||||
return (
|
return table?.canGo(rel);
|
||||||
pageNumber.value + rel >= 0 && pageNumber.value + rel <= getTotalPage.value
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function go(rel: number) {
|
function go(rel: number) {
|
||||||
if (canGo(rel)) {
|
table?.go(rel);
|
||||||
setPage(pageNumber.value + rel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<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>
|
<strong>{{ filter.title }} : </strong>
|
||||||
<button
|
<button
|
||||||
v-for="(isTrue, filterName) in filter.filters"
|
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 class="d-flex f-between f-middle nextprevious small-text">
|
||||||
<div>
|
<div>
|
||||||
<strong class="btn m-0 pages pl-0">
|
<strong class="btn m-0 pages pl-0">
|
||||||
Page {{ pageNumber + 1 }} / {{ getTotalPage + 1 }}
|
Page {{ currentPage + 1 }} / {{ getTotalPage + 1 }}
|
||||||
</strong>
|
</strong>
|
||||||
</div>
|
</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