continue tabulator vue implementation

* add jsdoc function comments
* add base props for component
* add loop logic to add needed formatters
* now vue component will be store in a list that will allow to render them and use Teleport Vue utils to add them inside the right table cell
* retrieve previous cell component
* create a utils/tabulator.js for some purposes like custom sort, custom mutating, etc...
* start some custom format (Icons, Text) and add a wrapper to use in the component
This commit is contained in:
Jordan Blasenhauer 2024-08-10 17:27:16 +02:00
parent 20975be247
commit 01b04004ea
2 changed files with 247 additions and 37 deletions

View file

@ -60,11 +60,107 @@ onMounted(() => {
</template> -->
<script setup>
import { ref, reactive, onMounted, Teleport, computed } from "vue";
import { ref, reactive, onMounted, Teleport, computed, onUnmounted } from "vue";
import Icons from "@components/Widget/Icons.vue";
import Text from "@components/Widget/Text.vue";
import Fields from "@components/Form/Fields.vue";
import Button from "@components/Widget/Button.vue";
import ButtonGroup from "@components/Widget/ButtonGroup.vue";
import { TabulatorFull as Tabulator } from "tabulator-tables"; //import Tabulator library
import { useEqualStr } from "@utils/global.js";
import { addSorter } from "@utils/tabulator.js";
// TODO : ADD JSDOC COMPONENT
const customComponents = ["Icons", "Text", "Fields", "Button", "ButtonGroup"];
const props = defineProps({
columns: {
type: Array,
required: true,
default: [
{ title: "Name", field: "name", width: 150 },
{ title: "Icon", field: "icon", formatter: "icons" },
],
},
data: {
type: Array,
required: false,
default: [
{ id: 1, name: "Oli Bob", icon: { iconName: "box", color: "amber" } },
{
id: 2,
name: "Mary May",
icon: { iconName: "document", color: "blue" },
},
{
id: 3,
name: "Christine Lobowski",
icon: { iconName: "box", color: "amber" },
},
{
id: 4,
name: "Brendon Philips",
icon: { iconName: "document", color: "blue" },
},
{
id: 5,
name: "Margret Marmajuke",
icon: { iconName: "box", color: "amber" },
},
{
id: 6,
name: "Frank Harbours",
icon: { iconName: "document", color: "blue" },
},
{
id: 7,
name: "Jamie Newhart",
icon: { iconName: "box", color: "amber" },
},
{
id: 8,
name: "Gemma Jane",
icon: { iconName: "document", color: "blue" },
},
{ id: 9, name: "Emily Sykes", icon: { iconName: "box", color: "amber" } },
{
id: 10,
name: "James Newman",
icon: { iconName: "document", color: "blue" },
},
{
id: 11,
name: "James Newman",
icon: { iconName: "document", color: "blue" },
},
{
id: 12,
name: "James Newman",
icon: { iconName: "document", color: "blue" },
},
{
id: 13,
name: "James Newman",
icon: { iconName: "document", color: "blue" },
},
{
id: 14,
name: "James Newman",
icon: { iconName: "document", color: "blue" },
},
{
id: 15,
name: "James Newman",
icon: { iconName: "document", color: "blue" },
},
{
id: 16,
name: "James Newman",
icon: { iconName: "document", color: "blue" },
},
],
},
isPagination: {
type: Boolean,
required: false,
@ -73,7 +169,7 @@ const props = defineProps({
paginationSize: {
type: Number,
required: false,
default: 1,
default: 10,
},
paginationInitialPage: {
type: Number,
@ -90,32 +186,22 @@ const props = defineProps({
const tableEl = ref(null); //reference to your table element
const table = reactive({
test: true,
instance: null,
columns: [
{ title: "Name", field: "name", width: 150 },
{ title: "Icon", field: "icon", formatter: "icons" },
],
data: [
{ id: 1, name: "test", icon: { iconName: "box", color: "amber" } },
{
id: 2,
name: "test",
icon: { iconName: "document", color: "blue" },
},
{ id: 3, name: "test", icon: { iconName: "box", color: "amber" } },
{ id: 4, name: "test", icon: { iconName: "document", color: "blue" } },
{ id: 5, name: "test", icon: { iconName: "box", color: "amber" } },
{ id: 6, name: "test", icon: { iconName: "document", color: "blue" } },
{ id: 7, name: "test", icon: { iconName: "box", color: "amber" } },
],
columns: props.columns,
data: props.data,
customComponents: [],
options: computed(() => {
const opts = {
data: table.data, //link data to table
reactiveData: true, //enable data reactivity
columns: table.columns, //define table columns
};
// columns formatting
let columns = JSON.parse(JSON.stringify(table.columns));
columns = addSortComponents(columns);
opts.columns = columns;
if (props.isPagination) {
opts.pagination = true;
opts.paginationSize = props.paginationSize;
@ -128,7 +214,17 @@ const table = reactive({
}),
});
function setCustomComponent(type, values, elDOM) {
/**
* @name addCustomComponent
* @description Utils to add needed data when we have a custom component.
* We will use the type to render the Vue component, the values to pass and the elDOM to teleport the component inside the right cell.
* @example { type: "Icons", values: { iconName: "box", color: "amber" }, elDOM: HTMLElement }
* @param {str} type - The type is the name of the component.
* @param {object} values - The values are the props that we want to pass to the component.
* @param {HTMLElement} elDOM - The elDOM is the element where we want to teleport the component.
* @returns {void}
*/
function addCustomComponent(type, values, elDOM) {
table.customComponents.push({
type: type,
values: values,
@ -136,22 +232,51 @@ function setCustomComponent(type, values, elDOM) {
});
}
function addModules() {
Tabulator.extendModule("format", "formatters", {
icons: function (cell, formatterParams) {
setCustomComponent("Icons", cell.getValue(), cell.getElement());
/**
* @name addComponentsFormats
* @description Add all custom components on a list to later add them to each tabulator cell.
* We are using the Tabulart.extendModule() that allow use to execute a custom function when we are matching a custom formatter.
* We need to define on rows the formatter that we want to use to render the custom component.
* @returns {void}
*/
function addComponentsFormats() {
const formatOpts = {};
for (let i = 0; i < customComponents.length; i++) {
const module = customComponents[i];
formatOpts[module.toLowerCase()] = function (cell, formatterParams) {
addCustomComponent(module, cell.getValue(), cell.getElement());
return "";
},
});
};
}
Tabulator.extendModule("format", "formatters", formatOpts);
}
/**
* @name addSortComponents
* @description Check every columns and add a custom sort for some components, so this is link to customComponents list and previous formatters set.
* @example For Icons, we will add a custom sorter that will check between iconNames.
* @param {array} columns - The columns are the list of columns that we want to check.
* @returns {array} - Return the columns with the custom sort added.
*/
function addSortComponents(columns) {
for (let i = 0; i < columns.length; i++) {
const column = columns[i];
if (!("formatter" in column)) continue;
const formatName = column.formatter.toLowerCase();
addSorter(column, formatName);
}
return columns;
}
onMounted(() => {
addModules();
//instantiate Tabulator when element is mounted
addComponentsFormats();
table.instance = new Tabulator(tableEl.value, table.options);
table.instance.on("tableBuilt", () => {
console.log("tableBuilt");
});
});
onUnmounted(() => {
table.instance.destroy();
table.instance = null;
});
</script>
@ -160,16 +285,28 @@ onMounted(() => {
href="https://unpkg.com/tabulator-tables/dist/css/tabulator.min.css"
rel="stylesheet"
/>
<div ref="tableEl"></div>
<template
:key="table.customComponents"
v-for="component in table.customComponents"
v-for="comp in table.customComponents"
>
<Teleport :to="component.elDOM">
<Teleport :to="comp.elDOM">
<Icons
v-if="component.type === 'Icons'"
v-bind="{ ...component.values }"
v-if="useEqualStr(comp.type, 'Icons')"
v-bind="{ ...comp.values }"
/>
<Text v-if="useEqualStr(comp.type, 'Text')" v-bind="{ ...comp.values }" />
<Fields
v-if="useEqualStr(comp.type, 'Fields')"
v-bind="{ ...comp.values }"
/>
<Button
v-if="useEqualStr(comp.type, 'Button')"
v-bind="{ ...comp.values }"
/>
<ButtonGroup
v-if="useEqualStr(comp.type, 'ButtonGroup')"
v-bind="{ ...comp.values }"
/>
</Teleport>
</template>

View file

@ -0,0 +1,73 @@
/**
* @name utils/tabulator.js
* @description This file contains utils to work with the tabulator library and Vue instance.
*/
/**
* @name addSorter
* @description This is a wrapper that will execute every sorter function in order to add a new sorter to the tabulator library.
* @example { title: "Icon", field: "icon", formatter: "icons" }
* @param {object} column - The column object to update in case we have the right format.
* @param {formatName} column - Check if the current column format is the right one.
* @returns {void}
*/
function addSorter(column, formatName) {
sortIcons(column, formatName);
sortText(column, formatName);
}
/**
* @name sortIcons
* @description Add sorter for Icons components in the tabulator.
* @example { title: "Icon", field: "icon", formatter: "icons" }
* @param {object} column - The column object to update in case we have the right format.
* @param {formatName} column - Check if the current column format is the right one.
* @returns {void}
*/
function sortIcons(column, formatName) {
if (formatName !== "icons") return;
column.sorter = (a, b, aRow, bRow, column, dir, params) => {
const aName = a.iconName;
const bName = b.iconName;
let alignEmptyValues = params.alignEmptyValues;
let emptyAlign = 0;
let locale;
if (aName && bName) {
locale =
typeof params.locale == "boolean" ? this.langLocale() : params.locale;
return String(aName)
.toLowerCase()
.localeCompare(String(bName).toLowerCase(), locale);
}
if (!aName) emptyAlign = !bName ? 0 : -1;
if (!bName) emptyAlign = 1;
//fix empty values in position
if (
(alignEmptyValues === "top" && dir === "desc") ||
(alignEmptyValues === "bottom" && dir === "asc")
) {
emptyAlign *= -1;
}
return emptyAlign;
};
}
/**
* @name sortText
* @description Add sorter for Text components in the tabulator. Under the hood, this will use the default tabulator sorter for strings.
* @example { title: "Icon", field: "icon", formatter: "icons" }
* @param {object} column - The column object to update in case we have the right format.
* @param {formatName} column - Check if the current column format is the right one.
* @returns {void}
*/
function sortText(column, formatName) {
if (formatName !== "text") return;
column.sorter = "string";
}
export { addSorter, sortIcons, sortText };