mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
create table comp + a18y + style
* move tabulator table from Test page to own component * add a18y for sortable header and footer actions * move style from component to input.css tailwind * start updating light mode : header done, main hover effect done, container done, select and active footer buttons done
This commit is contained in:
parent
32aac4bb68
commit
36fef06eea
6 changed files with 2579 additions and 2424 deletions
198
src/ui/client/dashboard/components/Widget/Tabulator.vue
Normal file
198
src/ui/client/dashboard/components/Widget/Tabulator.vue
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
<script setup>
|
||||
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 {
|
||||
addColumnsSorter,
|
||||
addColumnsWidth,
|
||||
a18yTable,
|
||||
} from "@utils/tabulator.js";
|
||||
|
||||
// TODO : ADD JSDOC COMPONENT
|
||||
|
||||
const customComponents = ["Icons", "Text", "Fields", "Button", "ButtonGroup"];
|
||||
|
||||
const props = defineProps({
|
||||
columns: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: [],
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: [],
|
||||
},
|
||||
rowHeight: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
colMinWidth: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 100,
|
||||
},
|
||||
colMaxWidth: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
isPagination: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
paginationSize: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 10,
|
||||
},
|
||||
paginationInitialPage: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 1,
|
||||
},
|
||||
paginationSizeSelector: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: [10, 25, 50, 100, true],
|
||||
},
|
||||
});
|
||||
|
||||
const tableEl = ref(null); //reference to your table element
|
||||
|
||||
const table = reactive({
|
||||
test: true,
|
||||
instance: null,
|
||||
columns: props.columns,
|
||||
items: props.items,
|
||||
customComponents: [],
|
||||
options: computed(() => {
|
||||
const opts = {
|
||||
data: table.items, //link data to table
|
||||
reactiveData: true, //enable data reactivity
|
||||
autoResize: true, // prevent auto resizing of table
|
||||
resizableRows: true, // this option takes a boolean value (default = false)
|
||||
layout: "fitDataFill",
|
||||
};
|
||||
|
||||
if (props.rowHeight) opts.rowHeight = props.rowHeight;
|
||||
|
||||
// columns formatting
|
||||
let columns = JSON.parse(JSON.stringify(table.columns));
|
||||
columns = formatColumns(columns);
|
||||
opts.columns = columns;
|
||||
|
||||
if (props.isPagination) {
|
||||
opts.pagination = true;
|
||||
opts.paginationSize = props.paginationSize;
|
||||
opts.paginationInitialPage = props.paginationInitialPage;
|
||||
opts.paginationButtonCount = 2;
|
||||
opts.paginationSizeSelector = props.paginationSizeSelector;
|
||||
opts.paginationCounter = "rows";
|
||||
}
|
||||
return opts;
|
||||
}),
|
||||
});
|
||||
|
||||
/**
|
||||
* @name formatColumns
|
||||
* @description This will add some key to columns that can be passed from props like minWidth or maxWidth.
|
||||
* Case key already exists, this will override it.
|
||||
* @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 formatColumns(columns) {
|
||||
for (let i = 0; i < columns.length; i++) {
|
||||
const column = columns[i];
|
||||
addColumnsSorter(column);
|
||||
addColumnsWidth(column, props.colMinWidth, props.colMaxWidth);
|
||||
}
|
||||
return columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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,
|
||||
elDOM: elDOM,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
addComponentsFormats();
|
||||
table.instance = new Tabulator(tableEl.value, table.options);
|
||||
table.instance.on("tableBuilt", () => {
|
||||
table.instance.redraw();
|
||||
a18yTable(table.instance);
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
table.instance.destroy();
|
||||
table.instance = null;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div data-is="table" ref="tableEl"></div>
|
||||
<template
|
||||
:key="table.customComponents"
|
||||
v-for="comp in table.customComponents"
|
||||
>
|
||||
<Teleport :to="comp.elDOM">
|
||||
<Icons
|
||||
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>
|
||||
</template>
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
import { reactive, onBeforeMount, onMounted } from "vue";
|
||||
import DashboardLayout from "@components/Dashboard/Layout.vue";
|
||||
import BuilderPlugins from "@components/Builder/Plugins.vue";
|
||||
import { useGlobal } from "@utils/global";
|
||||
import { useGlobal } from "@utils/global.js";
|
||||
|
||||
/**
|
||||
* @name Page/PLugins.vue
|
||||
|
|
@ -36,7 +36,7 @@ function redirectPlugin() {
|
|||
|
||||
window.location.href = `./${pluginId}`;
|
||||
},
|
||||
true,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -81,7 +81,7 @@ function deletePlugin() {
|
|||
modalPluginName.textContent = deleteData.name;
|
||||
modal.classList.remove("hidden");
|
||||
},
|
||||
true,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,3 +1,5 @@
|
|||
import { contentIndex } from "@utils/tabindex.js";
|
||||
|
||||
/**
|
||||
* @name utils/tabulator.js
|
||||
* @description This file contains utils to work with the tabulator library and Vue instance.
|
||||
|
|
@ -89,4 +91,54 @@ function _sortText(column, formatName) {
|
|||
column.sorter = "string";
|
||||
}
|
||||
|
||||
export { addColumnsSorter, addColumnsWidth };
|
||||
/**
|
||||
* @name a18yTable
|
||||
* @description Wrapper to add some accessibility to the table.
|
||||
* @returns {void}
|
||||
*/
|
||||
function a18yTable() {
|
||||
_a18ySortable();
|
||||
_a18yFooter();
|
||||
}
|
||||
|
||||
/**
|
||||
* @name _a18ySortable
|
||||
* @description Allow the user to get to the sortable header by pressing the tab key.
|
||||
* The user can then press the enter key to sort the column.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _a18ySortable() {
|
||||
const sortableHeaders = document.querySelectorAll(
|
||||
".tabulator-col.tabulator-sortable .tabulator-col-sorter"
|
||||
);
|
||||
for (let i = 0; i < sortableHeaders.length; i++) {
|
||||
// Try to get child or keep current
|
||||
const sortableHeader = sortableHeaders[i].closest(".tabulator-col-content");
|
||||
if (!sortableHeader.hasAttribute("role"))
|
||||
sortableHeader.setAttribute("role", "button");
|
||||
sortableHeader.setAttribute("tabindex", contentIndex);
|
||||
sortableHeader.setAttribute("data-sort", "true");
|
||||
}
|
||||
// Add eventlistener to make sort working with enter key
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter" && e.target.getAttribute("data-sort") === "true") {
|
||||
e.target.click();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @name _a18yFooter
|
||||
* @description Update pagination tabindex to get continuity in the tab order in the table footer.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _a18yFooter() {
|
||||
const tableFooter = document.querySelector(".tabulator-footer");
|
||||
// query button and select tag
|
||||
const interactiveElements = tableFooter.querySelectorAll("button, select");
|
||||
for (let i = 0; i < interactiveElements.length; i++) {
|
||||
interactiveElements[i].setAttribute("tabindex", contentIndex);
|
||||
}
|
||||
}
|
||||
|
||||
export { addColumnsSorter, addColumnsWidth, a18yTable };
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue