@iamproperty/components 3.4.5 → 3.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/css/components/accordion.css +1 -0
- package/assets/css/components/accordion.css.map +1 -0
- package/assets/css/components/alert.css +1 -0
- package/assets/css/components/alert.css.map +1 -0
- package/assets/css/components/applied-filters.css +1 -0
- package/assets/css/components/applied-filters.css.map +1 -0
- package/assets/css/components/buttons.css +1 -0
- package/assets/css/components/buttons.css.map +1 -0
- package/assets/css/components/card.css +1 -0
- package/assets/css/components/card.css.map +1 -0
- package/assets/css/components/carousel.css +1 -0
- package/assets/css/components/carousel.css.map +1 -0
- package/assets/css/components/charts.css +1 -0
- package/assets/css/components/charts.css.map +1 -0
- package/assets/css/components/container.css +1 -0
- package/assets/css/components/container.css.map +1 -0
- package/assets/css/components/dialog.css +1 -0
- package/assets/css/components/dialog.css.map +1 -0
- package/assets/css/components/forms.css +1 -0
- package/assets/css/components/forms.css.map +1 -0
- package/assets/css/components/header.css +1 -0
- package/assets/css/components/header.css.map +1 -0
- package/assets/css/components/lists.css +1 -0
- package/assets/css/components/lists.css.map +1 -0
- package/assets/css/components/nav.css +1 -0
- package/assets/css/components/nav.css.map +1 -0
- package/assets/css/components/pagination.css +1 -0
- package/assets/css/components/pagination.css.map +1 -0
- package/assets/css/components/panel.css +1 -0
- package/assets/css/components/panel.css.map +1 -0
- package/assets/css/components/property-searchbar.css +1 -0
- package/assets/css/components/property-searchbar.css.map +1 -0
- package/assets/css/components/snapshot.css +1 -0
- package/assets/css/components/snapshot.css.map +1 -0
- package/assets/css/components/stepper.css +1 -0
- package/assets/css/components/stepper.css.map +1 -0
- package/assets/css/components/table.css +1 -0
- package/assets/css/components/table.css.map +1 -0
- package/assets/css/components/tabs.css +1 -0
- package/assets/css/components/tabs.css.map +1 -0
- package/assets/css/components/testimonial.css +1 -0
- package/assets/css/components/testimonial.css.map +1 -0
- package/assets/css/components/timeline.css +1 -0
- package/assets/css/components/timeline.css.map +1 -0
- package/assets/css/components/tooltips.css +1 -0
- package/assets/css/components/tooltips.css.map +1 -0
- package/assets/css/core.min.css +1 -1
- package/assets/css/core.min.css.map +1 -1
- package/assets/css/style.min.css +1 -1
- package/assets/css/style.min.css.map +1 -1
- package/assets/fonts/qanelas-bold-webfont.woff +0 -0
- package/assets/fonts/qanelas-bold-webfont.woff2 +0 -0
- package/assets/js/bundle.js +68 -0
- package/assets/js/components/accordion/accordion.component.js +33 -0
- package/assets/js/components/accordion/accordion.component.min.js +14 -0
- package/assets/js/components/accordion/accordion.component.min.js.map +1 -0
- package/assets/js/components/applied-filters/applied-filters.component.js +26 -0
- package/assets/js/components/card/card.component.js +91 -0
- package/assets/js/components/card/card.component.min.js +21 -0
- package/assets/js/components/card/card.component.min.js.map +1 -0
- package/assets/js/components/filterlist/filterlist.component.js +49 -0
- package/assets/js/components/filterlist/filterlist.component.min.js +23 -0
- package/assets/js/components/filterlist/filterlist.component.min.js.map +1 -0
- package/assets/js/components/header/header.component.js +51 -0
- package/assets/js/components/header/header.component.min.js +30 -0
- package/assets/js/components/header/header.component.min.js.map +1 -0
- package/assets/js/components/pagination/pagination.component.js +34 -0
- package/assets/js/components/table/table.component.js +104 -0
- package/assets/js/components/table/table.component.min.js +24 -0
- package/assets/js/components/table/table.component.min.js.map +1 -0
- package/assets/js/components/tabs/tabs.component.js +34 -0
- package/assets/js/components/tabs/tabs.component.min.js +17 -0
- package/assets/js/components/tabs/tabs.component.min.js.map +1 -0
- package/assets/js/dynamic.js +74 -0
- package/assets/js/dynamic.min.js +5 -0
- package/assets/js/dynamic.min.js.map +1 -0
- package/assets/js/flat-components.js +79 -0
- package/assets/js/modules/accordion.js +11 -14
- package/assets/js/modules/applied-filters.js +100 -0
- package/assets/js/modules/data-layer.js +45 -0
- package/assets/js/modules/filterlist.js +32 -0
- package/assets/js/modules/helpers.js +80 -47
- package/assets/js/modules/pagination.js +33 -0
- package/assets/js/modules/table.js +507 -420
- package/assets/js/modules/tabs.js +97 -0
- package/assets/js/modules/youtubevideo.js +53 -61
- package/assets/js/scripts.bundle.js +111 -984
- package/assets/js/scripts.bundle.js.map +1 -1
- package/assets/js/scripts.bundle.min.js +3 -4
- package/assets/js/scripts.bundle.min.js.map +1 -1
- package/assets/js/tests/filterlist.spec.js +22 -0
- package/assets/js/tests/pagination.spec.js +15 -0
- package/assets/js/tests/table.spec.js +147 -0
- package/assets/sass/_components.scss +1 -2
- package/assets/sass/_corefiles.scss +5 -4
- package/assets/sass/_fonts.scss +4 -4
- package/assets/sass/_func.scss +1 -0
- package/assets/sass/_functions/functions.scss +6 -0
- package/assets/sass/_functions/mixins.scss +9 -9
- package/assets/sass/_functions/utilities.scss +16 -0
- package/assets/sass/_functions/variables.scss +128 -86
- package/assets/sass/_tests/colours.spec.scss +1 -1
- package/assets/sass/_tests/mixins.spec.scss +1 -1
- package/assets/sass/_tests/typography.spec.scss +2 -2
- package/assets/sass/components/accordion.scss +9 -6
- package/assets/sass/components/applied-filters.scss +65 -0
- package/assets/sass/components/card.scss +178 -227
- package/assets/sass/components/charts.scss +4 -0
- package/assets/sass/components/container.scss +13 -8
- package/assets/sass/components/dialog.scss +202 -0
- package/assets/sass/components/forms.scss +39 -5
- package/assets/sass/components/header.scss +34 -11
- package/assets/sass/components/lists.scss +15 -0
- package/assets/sass/components/nav.scss +5 -1
- package/assets/sass/components/pagination.scss +140 -0
- package/assets/sass/components/panel.scss +3 -4
- package/assets/sass/components/snapshot.scss +1 -1
- package/assets/sass/components/table.scss +419 -0
- package/assets/sass/components/tabs.scss +52 -36
- package/assets/sass/components/timeline.scss +2 -2
- package/assets/sass/foundations/icons.scss +1 -1
- package/assets/sass/{components → foundations}/links.scss +29 -2
- package/assets/sass/foundations/reboot.scss +21 -15
- package/assets/sass/foundations/root.scss +12 -5
- package/assets/sass/foundations/type.scss +90 -66
- package/assets/svg/illustrations/table.svg +165 -0
- package/assets/ts/README.md +12 -0
- package/assets/ts/bundle.ts +87 -0
- package/assets/ts/components/accordion/README.md +17 -0
- package/assets/ts/components/accordion/accordion.component.ts +43 -0
- package/assets/ts/components/applied-filters/README.md +5 -0
- package/assets/ts/components/applied-filters/applied-filters.component.ts +33 -0
- package/assets/ts/components/card/README.md +22 -0
- package/assets/ts/components/card/card.component.ts +117 -0
- package/assets/ts/components/filterlist/README.md +17 -0
- package/assets/ts/components/filterlist/filterlist.component.ts +60 -0
- package/assets/ts/components/header/README.md +26 -0
- package/assets/ts/components/header/header.component.ts +61 -0
- package/assets/ts/components/pagination/README.md +11 -0
- package/assets/ts/components/pagination/pagination.component.ts +45 -0
- package/assets/ts/components/table/README.md +23 -0
- package/assets/ts/components/table/table.component.ts +128 -0
- package/assets/ts/components/tabs/README.md +18 -0
- package/assets/ts/components/tabs/tabs.component.ts +41 -0
- package/assets/ts/dynamic.ts +98 -0
- package/assets/ts/flat-components.ts +100 -0
- package/assets/ts/html.d.ts +4 -0
- package/assets/ts/modules/accordion.ts +15 -21
- package/assets/ts/modules/applied-filters.ts +146 -0
- package/assets/ts/modules/data-layer.ts +58 -0
- package/assets/ts/modules/filterlist.ts +46 -0
- package/assets/ts/modules/helpers.ts +93 -55
- package/assets/ts/modules/pagination.ts +44 -0
- package/assets/ts/modules/table.ts +598 -433
- package/assets/ts/modules/tabs.ts +136 -0
- package/assets/ts/modules/youtubevideo.ts +58 -63
- package/assets/ts/tests/filterlist.spec.ts +29 -0
- package/assets/ts/tests/pagination.spec.ts +21 -0
- package/assets/ts/tests/table.spec.ts +191 -0
- package/dist/components.es.js +1359 -1356
- package/dist/components.umd.js +103 -54
- package/dist/style.css +1 -1
- package/package.json +20 -12
- package/src/components/Accordion/Accordion.spec.js +1 -1
- package/src/components/Accordion/Accordion.vue +7 -5
- package/src/components/Accordion/AccordionItem.vue +3 -6
- package/src/components/Accordion/README.md +0 -2
- package/src/components/AppliedFilters/AppliedFilters.vue +20 -0
- package/src/components/AppliedFilters/README.md +5 -0
- package/src/components/Card/Card.vue +11 -112
- package/src/components/Card/README.md +16 -18
- package/src/components/Carousel/Carousel.vue +49 -10
- package/src/components/Chart/Chart.vue +46 -4
- package/src/components/Filterlist/Filterlist.vue +20 -0
- package/src/components/Filterlist/README.md +17 -0
- package/src/components/Header/Header.spec.js +5 -4
- package/src/components/Header/Header.vue +14 -20
- package/src/components/Pagination/Pagination.vue +30 -0
- package/src/components/Pagination/README.md +11 -0
- package/src/components/Snapshot/Snapshot.vue +1 -1
- package/src/components/Table/README.md +29 -44
- package/src/components/Table/Table.spec.js +5 -37
- package/src/components/Table/Table.vue +16 -91
- package/src/components/Tabs/README.md +0 -2
- package/src/components/Tabs/Tab.vue +3 -2
- package/src/components/Tabs/Tabs.vue +8 -64
- package/src/foundations/YoutubeVideo/YoutubeVideo.vue +1 -1
- package/src/index.js +3 -2
- package/assets/fonts/qanelassoft-extrabold-webfont.woff +0 -0
- package/assets/fonts/qanelassoft-extrabold-webfont.woff2 +0 -0
- package/assets/js/main.js +0 -57
- package/assets/js/modules/modal.js +0 -69
- package/assets/sass/components/cardDeck.scss +0 -108
- package/assets/sass/components/modal.scss +0 -136
- package/assets/sass/components/tables.scss +0 -291
- package/assets/ts/main.ts +0 -68
- package/assets/ts/modules/modal.ts +0 -91
- package/src/components/CardDeck/CardDeck.spec.js +0 -99
- package/src/components/CardDeck/CardDeck.vue +0 -77
- package/src/components/CardDeck/README.md +0 -25
- package/src/components/Modal/Modal.spec.js +0 -22
- package/src/components/Modal/Modal.vue +0 -43
- package/src/components/Modal/README.md +0 -20
|
@@ -1,451 +1,538 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
|
-
import { zeroPad, isNumeric } from "./helpers.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const dataRow = {
|
|
24
|
-
index: rowIndex,
|
|
25
|
-
row: tableRow
|
|
26
|
-
};
|
|
27
|
-
tableArr.push(dataRow);
|
|
28
|
-
});
|
|
29
|
-
// Sort array
|
|
30
|
-
tableArr.sort((a, b) => (a.index > b.index) ? 1 : -1);
|
|
31
|
-
// Reverse if descending
|
|
32
|
-
if (sort == "descending")
|
|
33
|
-
tableArr = tableArr.reverse();
|
|
34
|
-
// Create a string to return and populate the tbody
|
|
35
|
-
let strTbody = '';
|
|
36
|
-
tableArr.forEach((tableRow, index) => {
|
|
37
|
-
strTbody += tableRow.row.outerHTML;
|
|
38
|
-
});
|
|
39
|
-
tbody.innerHTML = strTbody;
|
|
40
|
-
// Dispatch the sortable event
|
|
41
|
-
tableElement.dispatchEvent(sortedEvent);
|
|
42
|
-
};
|
|
43
|
-
// Declare event handlers
|
|
44
|
-
tableElement.addEventListener('click', function (e) {
|
|
45
|
-
for (var target = e.target; target && target != this; target = target.parentNode) {
|
|
46
|
-
if (target.matches('[data-sortable]')) {
|
|
47
|
-
// Get current sort order
|
|
48
|
-
let sort = target.getAttribute('aria-sort') == "ascending" ? "descending" : "ascending";
|
|
49
|
-
// unset sort attributes
|
|
50
|
-
Array.from(tableElement.querySelectorAll('[data-sortable]')).forEach((col, index) => {
|
|
51
|
-
col.setAttribute('aria-sort', 'none');
|
|
52
|
-
});
|
|
53
|
-
// Set the sort order attribute
|
|
54
|
-
target.setAttribute('aria-sort', sort);
|
|
55
|
-
// Save the sort options on the table element so that it can be re-sorted later
|
|
56
|
-
tableElement.setAttribute('data-sort', sort);
|
|
57
|
-
tableElement.setAttribute('data-sortBy', target.textContent);
|
|
58
|
-
// Sort the table
|
|
59
|
-
sortTable(target.textContent, sort);
|
|
60
|
-
Array.from(tableElement.querySelectorAll('tr[draggable]')).forEach((tableRow, index) => {
|
|
61
|
-
tableRow.removeAttribute('draggable');
|
|
62
|
-
});
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}, false);
|
|
67
|
-
// On page load check if the table should be pre-sorted, if so trigger a click
|
|
68
|
-
if (tableElement.getAttribute('data-sortBy')) {
|
|
69
|
-
let sort = tableElement.getAttribute('data-sort') == "ascending" ? "descending" : "ascending";
|
|
70
|
-
Array.from(tableElement.querySelectorAll('[data-sortable]')).forEach((col, index) => {
|
|
71
|
-
if (col.textContent == tableElement.getAttribute('data-sortBy')) {
|
|
72
|
-
col.setAttribute('aria-sort', sort);
|
|
73
|
-
col.click();
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
// #endregion Sortable
|
|
78
|
-
// #region Filters
|
|
79
|
-
const createFilterForm = function (count) {
|
|
80
|
-
// Create wrapper div
|
|
81
|
-
const form = document.createElement("div");
|
|
82
|
-
form.classList.add('table__filters');
|
|
83
|
-
form.classList.add('row');
|
|
84
|
-
form.classList.add('pt-1');
|
|
85
|
-
form.classList.add('pb-3');
|
|
86
|
-
// Create the filter options array
|
|
87
|
-
const filterColumns = Array.from(tableElement.querySelectorAll('th[data-filterable]'));
|
|
88
|
-
// Populate a list of searchable terms from the cells of the columns that could be used as a filter
|
|
89
|
-
let searchableTerms = {};
|
|
90
|
-
filterColumns.forEach((columnHeading, index) => {
|
|
91
|
-
Array.from(tableElement.querySelectorAll('td[data-label="' + columnHeading.textContent + '"]')).forEach((label, index) => {
|
|
92
|
-
searchableTerms[label.textContent] = label.textContent;
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
// Create the form
|
|
96
|
-
const filterTitle = filterColumns.length == 1 ? "Filter by " + filterColumns[0].textContent : "Filter"; // Update title if only one filter is chosen
|
|
97
|
-
const checkboxClass = filterColumns.length == 1 ? "d-none" : "d-sm-flex"; // Hide controls when only one filter is chosen
|
|
98
|
-
form.innerHTML = `<div class="col-sm-6 col-md-4 pb-3">
|
|
99
|
-
<div class="form-control__wrapper form-control-inline mb-0">
|
|
100
|
-
<label for="${randID}_filter" class="form-label">${filterTitle}:</label>
|
|
101
|
-
<input type="search" name="${randID}_filter" id="${randID}_filter" class="form-control form-control-sm" placeholder="" list="${randID}_list" />
|
|
102
|
-
</div>
|
|
103
|
-
<datalist id="${randID}_list">
|
|
104
|
-
${Object.keys(searchableTerms).map(term => `<option value="${term}"></option>`).join("")}
|
|
105
|
-
</datalist>
|
|
106
|
-
</div>
|
|
107
|
-
<div class="col-md-8 align-items-center pb-3 ${checkboxClass}">
|
|
108
|
-
${`<span class="pe-3 text-nowrap h5 mb-0">Filter by: </span>` + filterColumns.map(column => `<div class="form-check pe-3 mt-0 mb-0"><input class="form-check-input" type="checkbox" id="${randID}_${column.textContent.replace(' ', '_').toLowerCase()}" checked="checked" /><label class="form-check-label text-nowrap" for="${randID}_${column.textContent.replace(' ', '_').toLowerCase()}">${column.textContent}</label></div>`).join("")}
|
|
109
|
-
</div>`;
|
|
110
|
-
// Add before the actual table
|
|
111
|
-
tableElement.prepend(form);
|
|
112
|
-
};
|
|
113
|
-
const filterTable = function (searchTerm) {
|
|
114
|
-
// Create an array of rows that match the search term
|
|
115
|
-
let tableArr = [];
|
|
116
|
-
Array.from(storedData.querySelectorAll('tr')).forEach((tableRow, index) => {
|
|
117
|
-
// We want one long search string per row including each filterable table cell
|
|
118
|
-
let rowSearchString = '';
|
|
119
|
-
Array.from(tableElement.querySelectorAll('[type="checkbox"]:checked + label')).forEach((label, index) => {
|
|
120
|
-
rowSearchString += tableRow.querySelector('td[data-label="' + label.textContent + '"]').textContent + ' | ';
|
|
121
|
-
});
|
|
122
|
-
// Check if the table row search string contains the search term
|
|
123
|
-
if (rowSearchString.indexOf(searchTerm) >= 0) {
|
|
124
|
-
const dataRow = { row: tableRow };
|
|
125
|
-
tableArr.push(dataRow);
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
// Create a string to return and populate the tbody
|
|
129
|
-
let strTbody = '';
|
|
130
|
-
tableArr.forEach((tableRow, index) => {
|
|
131
|
-
strTbody += tableRow.row.outerHTML;
|
|
132
|
-
});
|
|
133
|
-
tbody.innerHTML = strTbody;
|
|
134
|
-
// Dispatch the filter event.
|
|
135
|
-
tableElement.dispatchEvent(filteredEvent);
|
|
136
|
-
};
|
|
137
|
-
const createFilterList = function () {
|
|
138
|
-
// Check which options are checked
|
|
139
|
-
let filterOptions = [];
|
|
140
|
-
Array.from(tableElement.querySelectorAll('[type="checkbox"]:checked + label')).forEach((label, index) => {
|
|
141
|
-
filterOptions.push(label.textContent);
|
|
142
|
-
});
|
|
143
|
-
// Build up the list of searchable terms
|
|
144
|
-
let searchableTerms = [];
|
|
145
|
-
filterOptions.forEach((option, index) => {
|
|
146
|
-
Array.from(tableElement.querySelectorAll('td[data-label="' + option + '"]')).forEach((label, index) => {
|
|
147
|
-
searchableTerms[label.textContent] = label.textContent;
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
// Rebuild the list
|
|
151
|
-
let dataList = tableElement.querySelector('datalist');
|
|
152
|
-
dataList.innerHTML = Object.keys(searchableTerms).map(term => `<option value="${term}"></option>`).join("");
|
|
153
|
-
};
|
|
154
|
-
// On page load check if filters are needed
|
|
155
|
-
if (Array.from(tableElement.querySelectorAll('[data-filterable]')).length) {
|
|
156
|
-
// Create the filter options
|
|
157
|
-
createFilterForm(tableElement, Array.from(tableElement.querySelectorAll('[data-filterable]')).length);
|
|
158
|
-
// Add event handlers for the filter options
|
|
159
|
-
tableElement.addEventListener('keyup', function (e) {
|
|
160
|
-
for (var target = e.target; target && target != this; target = target.parentNode) {
|
|
161
|
-
if (target.matches('input[type="search"]')) {
|
|
162
|
-
const searchTerm = target.value;
|
|
163
|
-
filterTable(searchTerm);
|
|
2
|
+
import { zeroPad, isNumeric, ucfirst } from "./helpers.js";
|
|
3
|
+
import createPaginationButttons from "./pagination.js";
|
|
4
|
+
// Basic functionality needed
|
|
5
|
+
export const addDataAttributes = (table) => {
|
|
6
|
+
const colHeadings = Array.from(table.querySelectorAll('thead th'));
|
|
7
|
+
const colRows = Array.from(table.querySelectorAll('tbody tr'));
|
|
8
|
+
colRows.forEach((row, index) => {
|
|
9
|
+
const cells = Array.from(row.querySelectorAll('th, td'));
|
|
10
|
+
const statuses = ['Low', 'Medium', 'High', 'N/A', 'Pending', 'Verified', 'Incomplete', 'Completed', 'Requires approval'];
|
|
11
|
+
cells.forEach((cell, cellIndex) => {
|
|
12
|
+
const heading = colHeadings[cellIndex];
|
|
13
|
+
if (typeof heading != "undefined") {
|
|
14
|
+
let tempDiv = document.createElement("div");
|
|
15
|
+
tempDiv.innerHTML = heading.innerHTML;
|
|
16
|
+
let headingText = tempDiv.textContent || tempDiv.innerText || "";
|
|
17
|
+
cell.setAttribute('data-label', headingText);
|
|
18
|
+
if (heading.hasAttribute('class'))
|
|
19
|
+
cell.setAttribute('class', heading.getAttribute('class'));
|
|
20
|
+
if (heading.hasAttribute('data-format')) {
|
|
21
|
+
cell.setAttribute('data-format', heading.getAttribute('data-format'));
|
|
22
|
+
cell.innerHTML = formatCell('date', cell.textContent.trim()); //Make sure date format is consistent
|
|
164
23
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
tableElement.addEventListener('change', function (e) {
|
|
168
|
-
for (var target = e.target; target && target != this; target = target.parentNode) {
|
|
169
|
-
if (target.matches('input[type="search"]')) {
|
|
170
|
-
const searchTerm = target.value;
|
|
171
|
-
filterTable(searchTerm);
|
|
24
|
+
if (statuses.includes(cell.textContent.trim())) {
|
|
25
|
+
cell.setAttribute('data-content', cell.textContent.trim());
|
|
172
26
|
}
|
|
173
27
|
}
|
|
174
28
|
});
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
export const getLargestLastColWidth = (table) => {
|
|
32
|
+
let largestWidth = 0;
|
|
33
|
+
Array.from(table.querySelectorAll('tr')).forEach((row, index) => {
|
|
34
|
+
let htmlStyles = window.getComputedStyle(document.querySelector('html'));
|
|
35
|
+
let lastColChild = row.querySelector(':scope > *:last-child > *:first-child');
|
|
36
|
+
if (lastColChild) {
|
|
37
|
+
let responsiveWidth = lastColChild.offsetWidth / parseFloat(htmlStyles.fontSize);
|
|
38
|
+
responsiveWidth += 1.5;
|
|
39
|
+
largestWidth = largestWidth > responsiveWidth ? largestWidth : responsiveWidth;
|
|
40
|
+
}
|
|
41
|
+
let rowHeight = row.offsetHeight / parseFloat(htmlStyles.fontSize);
|
|
42
|
+
row.style.setProperty("--row-height", `${rowHeight}rem`);
|
|
43
|
+
});
|
|
44
|
+
return largestWidth;
|
|
45
|
+
};
|
|
46
|
+
export const createMobileButton = (table) => {
|
|
47
|
+
Array.from(table.querySelectorAll('tbody tr')).forEach((row, index) => {
|
|
48
|
+
let firstCol = row.querySelector(':scope > :is(td,th):first-child');
|
|
49
|
+
let colContent = firstCol.textContent;
|
|
50
|
+
firstCol.innerHTML = `<span class="td__content">${colContent}</span><button type="button" class="d-none">${colContent}</button>`;
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
export const addTableEventListeners = (table) => {
|
|
54
|
+
table.addEventListener('click', (event) => {
|
|
55
|
+
if (event && event.target instanceof HTMLElement && event.target.closest('tr > :is(td,th):first-child button')) {
|
|
56
|
+
let firstCol = event.target.closest('tr > :is(td,th):first-child button');
|
|
57
|
+
let tableRow = firstCol.parentNode.closest('tr');
|
|
58
|
+
if (tableRow.getAttribute('data-view') == "full")
|
|
59
|
+
tableRow.setAttribute('data-view', 'default');
|
|
60
|
+
else
|
|
61
|
+
tableRow.setAttribute('data-view', 'full');
|
|
62
|
+
firstCol.blur();
|
|
63
|
+
}
|
|
64
|
+
;
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
// Filters
|
|
68
|
+
export const createSearchDataList = (table, form) => {
|
|
69
|
+
let searchInput = form.querySelector('[data-search]');
|
|
70
|
+
if (!searchInput)
|
|
71
|
+
return false;
|
|
72
|
+
const searchID = searchInput.getAttribute('id');
|
|
73
|
+
const searchableColumns = searchInput.getAttribute('data-search').split(',');
|
|
74
|
+
let inputWrapper = searchInput.parentNode;
|
|
75
|
+
let searchableTerms = {};
|
|
76
|
+
searchableColumns.forEach((columnHeading, index) => {
|
|
77
|
+
Array.from(table.querySelectorAll('td[data-label="' + columnHeading.trim() + '"]')).forEach((td, index) => {
|
|
78
|
+
if (td.querySelector('.td__content'))
|
|
79
|
+
searchableTerms[td.querySelector('.td__content').textContent] = td.querySelector('.td__content').textContent;
|
|
80
|
+
else
|
|
81
|
+
searchableTerms[td.textContent] = td.textContent;
|
|
183
82
|
});
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
83
|
+
});
|
|
84
|
+
searchInput.setAttribute('list', `${searchID}_list`);
|
|
85
|
+
searchInput.setAttribute('autocomplete', 'off');
|
|
86
|
+
if (!inputWrapper.querySelector('datalist'))
|
|
87
|
+
inputWrapper.innerHTML += `<datalist id="${searchID}_list"></datalist>`;
|
|
88
|
+
inputWrapper.querySelector('datalist').innerHTML = `${Object.keys(searchableTerms).map(term => `<option value="${term}"></option>`).join("")}`;
|
|
89
|
+
};
|
|
90
|
+
export const addFilterEventListeners = (table, form, pagination, wrapper, savedTableBody) => {
|
|
91
|
+
var timer;
|
|
92
|
+
// Check what conditions are set on the table to see what the form actions are
|
|
93
|
+
let formSubmit = function () {
|
|
94
|
+
if (form.hasAttribute('data-ajax'))
|
|
95
|
+
loadAjaxTable(table, form, pagination, wrapper);
|
|
96
|
+
else if (form.hasAttribute('data-submit'))
|
|
97
|
+
form.submit();
|
|
98
|
+
else {
|
|
99
|
+
filterTable(table, form, wrapper);
|
|
100
|
+
createPaginationButttons(wrapper, pagination);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
form.addEventListener('keyup', (event) => {
|
|
104
|
+
clearTimeout(timer);
|
|
105
|
+
if (event && event.target instanceof HTMLElement && event.target.closest('[data-search]')) {
|
|
106
|
+
timer = setTimeout(function () {
|
|
107
|
+
formSubmit();
|
|
108
|
+
}, 500);
|
|
109
|
+
}
|
|
110
|
+
;
|
|
111
|
+
});
|
|
112
|
+
form.addEventListener('change', (event) => {
|
|
113
|
+
clearTimeout(timer);
|
|
114
|
+
if (event && event.target instanceof HTMLElement && event.target.closest('[data-sort]')) {
|
|
115
|
+
if (!form.hasAttribute('data-submit'))
|
|
116
|
+
sortTable(table, form, savedTableBody);
|
|
117
|
+
formSubmit();
|
|
118
|
+
}
|
|
119
|
+
if (event && event.target instanceof HTMLElement && event.target.closest('[data-search]')) {
|
|
120
|
+
formSubmit();
|
|
121
|
+
}
|
|
122
|
+
if (event && event.target instanceof HTMLElement && event.target.closest('[data-filter]') && !event.target.closest('form dialog')) {
|
|
123
|
+
formSubmit();
|
|
124
|
+
}
|
|
125
|
+
if (event && event.target instanceof HTMLElement && event.target.closest('[data-show]')) {
|
|
126
|
+
formSubmit();
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
form.addEventListener('click', (event) => {
|
|
130
|
+
clearTimeout(timer);
|
|
131
|
+
if (event && event.target instanceof HTMLElement && event.target.closest('dialog button:not([type="button"])')) {
|
|
132
|
+
let button = event.target.closest('dialog button:not([type="button"])');
|
|
133
|
+
let modal = button.closest('dialog');
|
|
134
|
+
modal.close();
|
|
135
|
+
}
|
|
136
|
+
// Prevent the form from submitting
|
|
137
|
+
if (event && event.target instanceof HTMLElement && event.target.closest('.dialog__close')) {
|
|
138
|
+
event.preventDefault();
|
|
139
|
+
event.stopPropagation();
|
|
193
140
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
141
|
+
if (event && event.target instanceof HTMLElement && event.target.closest('[data-clear]')) {
|
|
142
|
+
form.reset();
|
|
143
|
+
if (!form.hasAttribute('data-submit'))
|
|
144
|
+
sortTable(table, form, savedTableBody);
|
|
145
|
+
formSubmit();
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
form.addEventListener('submit', (event) => {
|
|
149
|
+
clearTimeout(timer);
|
|
150
|
+
if (!form.hasAttribute('data-submit'))
|
|
151
|
+
event.preventDefault();
|
|
152
|
+
formSubmit();
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
export const sortTable = (table, form, savedTableBody) => {
|
|
156
|
+
if (form.getAttribute('data-ajax')) {
|
|
157
|
+
return false;
|
|
199
158
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
159
|
+
let tbody = table.querySelector('tbody');
|
|
160
|
+
let select = form.querySelector('[data-sort]');
|
|
161
|
+
let selectedOption = select.querySelector(`option:nth-child(${select.selectedIndex + 1})`);
|
|
162
|
+
let sortBy = selectedOption.getAttribute('data-sort');
|
|
163
|
+
let order = selectedOption.getAttribute('data-order');
|
|
164
|
+
let format = selectedOption.getAttribute('data-format');
|
|
165
|
+
if (!sortBy) {
|
|
166
|
+
tbody.innerHTML = savedTableBody.innerHTML;
|
|
167
|
+
addDataAttributes(table);
|
|
168
|
+
return false;
|
|
203
169
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
display: table-row;
|
|
208
|
-
}
|
|
170
|
+
let orderArray = [];
|
|
171
|
+
if (!['asc', 'desc', 'descending'].includes(order)) {
|
|
172
|
+
orderArray = order.split(',');
|
|
209
173
|
}
|
|
210
|
-
|
|
211
|
-
|
|
174
|
+
// Create an array from the table rows, the index created is then used to sort the array
|
|
175
|
+
let tableArr = [];
|
|
176
|
+
Array.from(tbody.querySelectorAll('tr')).forEach((tableRow, index) => {
|
|
177
|
+
let rowIndex = tableRow.querySelector('td[data-label="' + sortBy + '"], th[data-label="' + sortBy + '"]').textContent.trim();
|
|
178
|
+
// If a predefined order set replace the search term with an ordered numeric value so it can be sorted
|
|
179
|
+
if (orderArray.length && orderArray.includes(rowIndex)) {
|
|
180
|
+
rowIndex = orderArray.indexOf(rowIndex);
|
|
181
|
+
}
|
|
182
|
+
if (isNumeric(rowIndex))
|
|
183
|
+
rowIndex = zeroPad(rowIndex, 10);
|
|
184
|
+
// If the sort format is date then lets transform the index to a sortable date (this is never displayed)
|
|
185
|
+
if (format && format == "date")
|
|
186
|
+
rowIndex = new Date(rowIndex);
|
|
187
|
+
const dataRow = {
|
|
188
|
+
index: rowIndex,
|
|
189
|
+
row: tableRow
|
|
190
|
+
};
|
|
191
|
+
tableArr.push(dataRow);
|
|
192
|
+
});
|
|
193
|
+
// Sort array alphabetically
|
|
194
|
+
tableArr.sort((a, b) => (a.index > b.index) ? 1 : -1);
|
|
195
|
+
// Reverse if descending
|
|
196
|
+
if (order == "descending" || order == "desc")
|
|
197
|
+
tableArr = tableArr.reverse();
|
|
198
|
+
// Create a string to return and populate the tbody
|
|
199
|
+
let strTbody = '';
|
|
200
|
+
tableArr.forEach((tableRow, index) => {
|
|
201
|
+
strTbody += tableRow.row.outerHTML;
|
|
202
|
+
});
|
|
203
|
+
tbody.innerHTML = strTbody;
|
|
204
|
+
};
|
|
205
|
+
export const filterTable = (table, form, wrapper) => {
|
|
206
|
+
table.classList.remove('table--filtered');
|
|
207
|
+
let filters = [];
|
|
208
|
+
let searches = [];
|
|
209
|
+
let matched = 0;
|
|
210
|
+
let page = form.querySelector('[data-pagination]') ? parseInt(form.querySelector('[data-pagination]').value) : 1;
|
|
211
|
+
let showRows = form.querySelector('[data-show]') ? parseInt(form.querySelector('[data-show]').value) : 15;
|
|
212
|
+
// Filter
|
|
213
|
+
let filterInputs = Array.from(form.querySelectorAll('[data-filter]'));
|
|
214
|
+
filterInputs.forEach((filterInput, index) => {
|
|
215
|
+
// Ignore uncked radio inputs
|
|
216
|
+
if (filterInput.type == 'radio' && !filterInput.checked) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (filterInput.type == 'checkbox' && !filterInput.checked) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (filterInput.getAttribute('data-filter') == "multi") {
|
|
223
|
+
for (const [key, value] of Object.entries(JSON.parse(filterInput.value))) {
|
|
224
|
+
filters[filterInput.getAttribute('data-filter')].push(value);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
else if (filterInput.value) {
|
|
228
|
+
if (!filters[filterInput.getAttribute('data-filter')])
|
|
229
|
+
filters[filterInput.getAttribute('data-filter')] = new Array();
|
|
230
|
+
filters[filterInput.getAttribute('data-filter')].push(filterInput.value);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
// Add search columns too
|
|
234
|
+
if (form.querySelector('[data-search]')) {
|
|
235
|
+
let searchInput = form.querySelector('[data-search]');
|
|
236
|
+
let searchColumns = form.querySelector('[data-search]').getAttribute('data-search').split(',');
|
|
237
|
+
searchColumns.forEach((column, index) => {
|
|
238
|
+
searches.push({ 'column': `${column.trim()}`, 'value': `${searchInput.value}` });
|
|
239
|
+
});
|
|
212
240
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if (
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
241
|
+
//Display the filter count
|
|
242
|
+
Array.from(form.querySelectorAll('[data-filter-count]')).forEach((element, index) => {
|
|
243
|
+
element.innerHTML = '';
|
|
244
|
+
});
|
|
245
|
+
if (filters.length) {
|
|
246
|
+
Array.from(form.querySelectorAll('[data-filter-count]')).forEach((element, index) => {
|
|
247
|
+
element.innerHTML += `(${filters.length})`;
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
// Stop function if no filters identified
|
|
251
|
+
if (!searches.length && !filters.length)
|
|
252
|
+
return false;
|
|
253
|
+
table.classList.add('table--filtered');
|
|
254
|
+
// Reset
|
|
255
|
+
Array.from(table.querySelectorAll('tbody tr')).forEach((row, index) => {
|
|
256
|
+
row.classList.remove('filtered');
|
|
257
|
+
row.classList.remove('filtered--matched');
|
|
258
|
+
row.classList.remove('filtered--show');
|
|
259
|
+
row.removeAttribute('data-filtered-by');
|
|
260
|
+
});
|
|
261
|
+
// Filter the table
|
|
262
|
+
for (const [key, filterValue] of Object.entries(filters)) {
|
|
263
|
+
console.log(filterValue);
|
|
264
|
+
Array.from(table.querySelectorAll('tbody tr:not(.filtered)')).forEach((row, index) => {
|
|
265
|
+
let isMatched = false;
|
|
266
|
+
filterValue.forEach((filter, index) => {
|
|
267
|
+
let filterTd = row.querySelector(`[data-label="${key}"]`);
|
|
268
|
+
// Dynamic values
|
|
269
|
+
if (filter && filter == "$today")
|
|
270
|
+
filter = formatCell('date', new Date());
|
|
271
|
+
else if (filter && filter == "$yesterday") {
|
|
272
|
+
let yesterday = new Date();
|
|
273
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
274
|
+
filter = formatCell('date', yesterday);
|
|
232
275
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
276
|
+
else if (filter && (filter == "$thisWeek" || filter == "$lastWeek")) {
|
|
277
|
+
let today = new Date();
|
|
278
|
+
let mondayThisWeek = new Date(today.setDate(today.getDate() - (today.getDay() - 1)));
|
|
279
|
+
let sundayThisWeek = new Date(today.setDate(today.getDate() - today.getDay() + 7));
|
|
280
|
+
let checkDate = new Date(filterTd.textContent.toLowerCase());
|
|
281
|
+
today.setHours(0, 0, 0, 0);
|
|
282
|
+
mondayThisWeek.setHours(0, 0, 0, 0);
|
|
283
|
+
sundayThisWeek.setHours(0, 0, 0, 0);
|
|
284
|
+
checkDate.setHours(0, 0, 0, 0);
|
|
285
|
+
if (filter == "$thisWeek") {
|
|
286
|
+
isMatched = (checkDate >= mondayThisWeek && checkDate <= sundayThisWeek);
|
|
239
287
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
createPaginationButttons(randID, tableElement, tableElement.getAttribute('data-show'), target.value, totalRows);
|
|
288
|
+
else {
|
|
289
|
+
let mondayLastWeek = new Date(mondayThisWeek.setDate(mondayThisWeek.getDate() - 7));
|
|
290
|
+
let sundayLastWeek = new Date(sundayThisWeek.setDate(sundayThisWeek.getDate() - 7));
|
|
291
|
+
mondayLastWeek.setHours(0, 0, 0, 0);
|
|
292
|
+
sundayLastWeek.setHours(0, 0, 0, 0);
|
|
293
|
+
isMatched = (checkDate >= mondayLastWeek && checkDate <= sundayLastWeek);
|
|
247
294
|
}
|
|
248
295
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
e.target.classList.add('tr--dragging');
|
|
259
|
-
}
|
|
260
|
-
// Create the order column and event handler for rows
|
|
261
|
-
const setReorderRows = function () {
|
|
262
|
-
Array.from(tbody.querySelectorAll('tr')).forEach((tableRow, index) => {
|
|
263
|
-
// Create column if not already created
|
|
264
|
-
if (tableRow.querySelector('[data-label="Order"]') == null) {
|
|
265
|
-
const orderColumn = document.createElement('th');
|
|
266
|
-
orderColumn.innerHTML = index + 1;
|
|
267
|
-
orderColumn.setAttribute('data-label', 'Order');
|
|
268
|
-
tableRow.prepend(orderColumn);
|
|
269
|
-
}
|
|
270
|
-
// Make draggable
|
|
271
|
-
tableRow.setAttribute('id', randID + '_row_' + (index + 1));
|
|
272
|
-
tableRow.setAttribute('data-order', index + 1);
|
|
273
|
-
tableRow.setAttribute('draggable', 'true');
|
|
274
|
-
tableRow.addEventListener("dragstart", setDraggedRow);
|
|
275
|
-
});
|
|
276
|
-
};
|
|
277
|
-
if (tableElement.getAttribute('data-reorder') && tableElement.getAttribute('data-reorder') != "false") {
|
|
278
|
-
// Add column heading
|
|
279
|
-
const orderHeading = document.createElement('th');
|
|
280
|
-
orderHeading.innerHTML = 'Order';
|
|
281
|
-
orderHeading.title = 'Click here to enable re-ordering via drag and drop';
|
|
282
|
-
orderHeading.classList.add('table-order-reset');
|
|
283
|
-
thead.querySelector('tr').prepend(orderHeading);
|
|
284
|
-
setReorderRows();
|
|
285
|
-
// Reset order button
|
|
286
|
-
tableElement.addEventListener('click', function (e) {
|
|
287
|
-
for (var target = e.target; target && target != this; target = target.parentNode) {
|
|
288
|
-
if (target.matches('.table-order-reset')) {
|
|
289
|
-
// unset sort attributes
|
|
290
|
-
Array.from(tableElement.querySelectorAll('[data-sortable]')).forEach((col, index) => {
|
|
291
|
-
col.setAttribute('aria-sort', 'none');
|
|
292
|
-
});
|
|
293
|
-
// Save the sort options on the table element so that it can be re-sorted later
|
|
294
|
-
tableElement.removeAttribute('data-sort');
|
|
295
|
-
tableElement.removeAttribute('data-sortBy');
|
|
296
|
-
// Sort the table
|
|
297
|
-
sortTable('Order', 'ascending');
|
|
298
|
-
Array.from(tableElement.querySelectorAll('tbody tr')).forEach((tableRow, index) => {
|
|
299
|
-
tableRow.setAttribute('draggable', 'true');
|
|
300
|
-
});
|
|
301
|
-
break;
|
|
296
|
+
else if (filter && filter == "$thisMonth") {
|
|
297
|
+
let today = new Date(), year = today.getFullYear(), month = today.getMonth();
|
|
298
|
+
var firstDayMonth = new Date(year, month, 1);
|
|
299
|
+
var lastDayMonth = new Date(year, month + 1, 0);
|
|
300
|
+
let checkDate = new Date(filterTd.textContent.toLowerCase());
|
|
301
|
+
firstDayMonth.setHours(0, 0, 0, 0);
|
|
302
|
+
lastDayMonth.setHours(0, 0, 0, 0);
|
|
303
|
+
checkDate.setHours(0, 0, 0, 0);
|
|
304
|
+
isMatched = (checkDate >= firstDayMonth && checkDate <= lastDayMonth);
|
|
302
305
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
e.dataTransfer.dropEffect = "move";
|
|
313
|
-
for (var target = e.target; target && target != this; target = target.parentNode) {
|
|
314
|
-
if (target.matches('[data-reorder] tbody tr')) {
|
|
315
|
-
target.classList.add('tr--dropable');
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}, false);
|
|
319
|
-
document.addEventListener("dragleave", function (e) {
|
|
320
|
-
// prevent default to allow drop
|
|
321
|
-
e.preventDefault();
|
|
322
|
-
for (var target = e.target; target && target != this; target = target.parentNode) {
|
|
323
|
-
if (target.matches('[data-reorder] tbody tr')) {
|
|
324
|
-
target.classList.remove('tr--dropable');
|
|
306
|
+
else if (filter && filter == "$lastMonth") {
|
|
307
|
+
let today = new Date(), year = today.getFullYear(), month = today.getMonth();
|
|
308
|
+
var firstDayLastMonth = new Date(year, month - 1, 1);
|
|
309
|
+
var lastDayLastMonth = new Date(year, month, 0);
|
|
310
|
+
let checkDate = new Date(filterTd.textContent.toLowerCase());
|
|
311
|
+
firstDayLastMonth.setHours(0, 0, 0, 0);
|
|
312
|
+
lastDayLastMonth.setHours(0, 0, 0, 0);
|
|
313
|
+
checkDate.setHours(0, 0, 0, 0);
|
|
314
|
+
isMatched = (checkDate >= firstDayLastMonth && checkDate <= lastDayLastMonth);
|
|
325
315
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
document.addEventListener("drop", function (e) {
|
|
329
|
-
e.preventDefault();
|
|
330
|
-
for (var target = e.target; target && target != this; target = target.parentNode) {
|
|
331
|
-
if (target.matches('[data-reorder] tbody tr')) {
|
|
332
|
-
if (target.parentNode != null && draggedRow.parentNode != null && target != draggedRow) {
|
|
333
|
-
draggedRow.parentNode.removeChild(draggedRow);
|
|
334
|
-
if (draggedRow.getAttribute('data-order') > target.getAttribute('data-order'))
|
|
335
|
-
target.parentNode.insertBefore(draggedRow, target);
|
|
336
|
-
else
|
|
337
|
-
target.parentNode.insertBefore(draggedRow, target.nextElementSibling);
|
|
338
|
-
// Re label the rows
|
|
339
|
-
Array.from(tbody.querySelectorAll('tr')).forEach((tableRowOrder, index) => {
|
|
340
|
-
tableRowOrder.classList.remove('tr--dragging');
|
|
341
|
-
tableRowOrder.classList.remove('tr--dropable');
|
|
342
|
-
tableRowOrder.querySelector('th').innerHTML = index + 1;
|
|
343
|
-
tableRowOrder.setAttribute('data-order', index + 1);
|
|
344
|
-
});
|
|
345
|
-
tableElement.dispatchEvent(reorderedEvent);
|
|
346
|
-
}
|
|
347
|
-
break;
|
|
316
|
+
if (filterTd && filterTd.textContent.toLowerCase().includes(filter.toLowerCase())) {
|
|
317
|
+
isMatched = true;
|
|
348
318
|
}
|
|
319
|
+
});
|
|
320
|
+
if (!isMatched) {
|
|
321
|
+
row.classList.add('filtered');
|
|
322
|
+
row.setAttribute('data-filtered-by', key);
|
|
349
323
|
}
|
|
350
|
-
}
|
|
324
|
+
});
|
|
351
325
|
}
|
|
352
|
-
//
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
const totalRows = tableElement.querySelectorAll('tbody tr').length;
|
|
360
|
-
const tablePagination = tableElement.querySelector('.table__pagination');
|
|
361
|
-
if (tablePagination != null)
|
|
362
|
-
tablePagination.remove();
|
|
363
|
-
if (show < totalRows) {
|
|
364
|
-
paginateRows(show, 1);
|
|
365
|
-
createPaginationForm(randID, tableElement, show, 1, totalRows);
|
|
366
|
-
createPaginationButttons(randID, tableElement, show, 1, totalRows);
|
|
326
|
+
// Search whats left of the table after filtering
|
|
327
|
+
Array.from(table.querySelectorAll('tbody tr:not(.filtered)')).forEach((row, index) => {
|
|
328
|
+
let isSearched = searches.length > 0 && searches[0].value.length >= 3 ? false : true;
|
|
329
|
+
searches.forEach((search, index) => {
|
|
330
|
+
let searchTd = row.querySelector(`[data-label="${search.column}"]`);
|
|
331
|
+
if (searchTd && search.value.length >= 3 && searchTd.textContent.toLowerCase().includes(search.value.toLowerCase())) {
|
|
332
|
+
isSearched = true;
|
|
367
333
|
}
|
|
334
|
+
});
|
|
335
|
+
if (!isSearched)
|
|
336
|
+
row.classList.add('filtered');
|
|
337
|
+
});
|
|
338
|
+
// Work out what to display after pagination
|
|
339
|
+
Array.from(table.querySelectorAll('tbody tr:not(.filtered')).forEach((row, index) => {
|
|
340
|
+
matched++;
|
|
341
|
+
row.classList.add('filtered--matched');
|
|
342
|
+
// pagination bit
|
|
343
|
+
if (Math.ceil(matched / showRows) == parseInt(page))
|
|
344
|
+
row.classList.add('filtered--show');
|
|
345
|
+
});
|
|
346
|
+
if (wrapper) {
|
|
347
|
+
wrapper.setAttribute('data-page', page);
|
|
348
|
+
wrapper.setAttribute('data-pages', Math.ceil(matched / showRows));
|
|
349
|
+
wrapper.setAttribute('data-total', matched);
|
|
350
|
+
wrapper.setAttribute('data-show', showRows);
|
|
351
|
+
}
|
|
352
|
+
populateDataQueries(table, form);
|
|
353
|
+
};
|
|
354
|
+
export const populateDataQueries = (table, form) => {
|
|
355
|
+
const dataQueries = Array.from(form.querySelectorAll('[data-query]'));
|
|
356
|
+
dataQueries.forEach((queryElement, index) => {
|
|
357
|
+
let query = queryElement.getAttribute('data-query');
|
|
358
|
+
let numberOfMatchedRows;
|
|
359
|
+
if (query == 'total') {
|
|
360
|
+
numberOfMatchedRows = table.classList.contains('table--filtered') ? table.querySelectorAll('tbody tr:not(.filtered)').length : table.querySelectorAll('tbody tr').length;
|
|
368
361
|
}
|
|
369
|
-
if (
|
|
370
|
-
|
|
362
|
+
else if (!query.includes(' == ') && query.includes(' & ')) {
|
|
363
|
+
let queries = query.split(' & ');
|
|
364
|
+
let selector = '';
|
|
365
|
+
queries.forEach(element => {
|
|
366
|
+
selector += `:not([data-filtered-by="${element}"])`;
|
|
367
|
+
});
|
|
368
|
+
console.log(selector);
|
|
369
|
+
numberOfMatchedRows = Array.from(table.querySelectorAll(`tbody tr${selector}`)).length;
|
|
370
|
+
}
|
|
371
|
+
else if (!query.includes(' == ')) {
|
|
372
|
+
numberOfMatchedRows = Array.from(table.querySelectorAll(`tbody tr:not([data-filtered-by="${query}"])`)).length;
|
|
371
373
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
374
|
+
else if (query.includes(' && ')) {
|
|
375
|
+
let queries = query.split(' && ');
|
|
376
|
+
numberOfMatchedRows = Array.from(table.querySelectorAll(`tbody tr:not(.filtered)`)).filter(function (row) {
|
|
377
|
+
let matched = true;
|
|
378
|
+
for (const [index, value] of Object.entries(queries)) {
|
|
379
|
+
let queryParts = value.split(' == ');
|
|
380
|
+
if (!row.querySelector(`td[data-label="${queryParts[0]}"]`) || row.querySelector(`td[data-label="${queryParts[0]}"]`).textContent != `${queryParts[1]}`)
|
|
381
|
+
matched = false;
|
|
382
|
+
}
|
|
383
|
+
return matched;
|
|
384
|
+
}).length;
|
|
376
385
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
386
|
+
else {
|
|
387
|
+
let queryParts = query.split(' == ');
|
|
388
|
+
console.log(queryParts[0]);
|
|
389
|
+
numberOfMatchedRows = Array.from(table.querySelectorAll(`tbody tr.filtered--matched td[data-label="${queryParts[0]}"], tbody tr[data-filtered-by="${queryParts[0]}"] td[data-label="${queryParts[0]}"]`)).filter(function (element) {
|
|
390
|
+
return element.textContent === queryParts[1];
|
|
391
|
+
}).length;
|
|
392
|
+
}
|
|
393
|
+
if (queryElement.hasAttribute('data-total'))
|
|
394
|
+
queryElement.setAttribute('data-total', numberOfMatchedRows);
|
|
395
|
+
else
|
|
396
|
+
queryElement.innerHTML = numberOfMatchedRows;
|
|
397
|
+
});
|
|
398
|
+
};
|
|
399
|
+
// Pagination
|
|
400
|
+
export const addPaginationEventListeners = function (table, form, pagination, wrapper) {
|
|
401
|
+
pagination.addEventListener('click', (event) => {
|
|
402
|
+
if (event && event.target instanceof HTMLElement && event.target.closest('[data-page]')) {
|
|
403
|
+
event.preventDefault();
|
|
404
|
+
let paginationInput = form.querySelector('[data-pagination]');
|
|
405
|
+
let newPage = event.target.closest('[data-page]').getAttribute('data-page');
|
|
406
|
+
paginationInput.value = newPage;
|
|
407
|
+
wrapper.setAttribute('data-page', newPage);
|
|
408
|
+
form.dispatchEvent(new Event("submit"));
|
|
409
|
+
const url = new URL(location);
|
|
410
|
+
url.searchParams.set("page", newPage);
|
|
411
|
+
history.pushState({ 'type': 'pagination', 'form': form.getAttribute('id'), 'page': newPage }, "", url);
|
|
412
|
+
}
|
|
413
|
+
if (event && event.target instanceof HTMLElement && event.target.closest('[data-show]')) {
|
|
414
|
+
event.preventDefault();
|
|
415
|
+
let showInput = form.querySelector('[data-show]');
|
|
416
|
+
let showRows = event.target.closest('[data-show]').getAttribute('data-show');
|
|
417
|
+
showInput.value = showRows;
|
|
418
|
+
wrapper.setAttribute('data-show', showRows);
|
|
419
|
+
form.dispatchEvent(new Event("submit"));
|
|
420
|
+
}
|
|
421
|
+
});
|
|
411
422
|
};
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
if (
|
|
423
|
+
// Export CSV Data
|
|
424
|
+
export const addExportEventListeners = (button, table) => {
|
|
425
|
+
if (!button)
|
|
415
426
|
return false;
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
+
button.addEventListener('click', (event) => {
|
|
428
|
+
exportAsCSV(table);
|
|
429
|
+
});
|
|
430
|
+
};
|
|
431
|
+
export const exportAsCSV = function (table) {
|
|
432
|
+
var csvData = [];
|
|
433
|
+
// Get each row data
|
|
434
|
+
var rows = table.getElementsByTagName('tr');
|
|
435
|
+
for (var i = 0; i < rows.length; i++) {
|
|
436
|
+
// Get each column data
|
|
437
|
+
var cols = rows[i].querySelectorAll('td,th');
|
|
438
|
+
// Stores each csv row data
|
|
439
|
+
var csvRow = [];
|
|
440
|
+
for (var j = 0; j < cols.length; j++) {
|
|
441
|
+
// Get the text data of each cell of a row and push it to csvrow
|
|
442
|
+
csvRow.push(`"${cols[j].textContent}"`);
|
|
427
443
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
${strButtons}
|
|
431
|
-
${page == numberPages ? `<li class="page-item disabled"><span class="page-link">Next</span></li>` : `<li class="page-item"><button class="page-link" data-page="${parseInt(page) + 1}">Next</button></li>`}
|
|
432
|
-
</ul>`;
|
|
444
|
+
// Combine each column value with comma
|
|
445
|
+
csvData.push(csvRow.join(","));
|
|
433
446
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
447
|
+
// Combine each row data with new line character
|
|
448
|
+
csvData = csvData.join('\n');
|
|
449
|
+
// Create CSV file object and feed our csvData into it
|
|
450
|
+
let CSVFile = new Blob([csvData], {
|
|
451
|
+
type: "text/csv"
|
|
452
|
+
});
|
|
453
|
+
// Create to temporary link to initiate download process
|
|
454
|
+
var tempLink = document.createElement('a');
|
|
455
|
+
tempLink.download = "export.csv";
|
|
456
|
+
var url = window.URL.createObjectURL(CSVFile);
|
|
457
|
+
tempLink.href = url;
|
|
458
|
+
// This link should not be displayed
|
|
459
|
+
tempLink.style.display = "none";
|
|
460
|
+
document.body.appendChild(tempLink);
|
|
461
|
+
// Automatically click the link to trigger download
|
|
462
|
+
tempLink.click();
|
|
463
|
+
document.body.removeChild(tempLink);
|
|
464
|
+
};
|
|
465
|
+
// After table is loaded
|
|
466
|
+
export const makeTableFunctional = function (table, form, pagination, wrapper) {
|
|
467
|
+
createMobileButton(table);
|
|
468
|
+
addDataAttributes(table);
|
|
469
|
+
populateDataQueries(table, form);
|
|
470
|
+
// Work out the largest width of the CTA's in the last column
|
|
471
|
+
if (wrapper && wrapper.classList.contains('table--cta')) {
|
|
472
|
+
const largestWidth = getLargestLastColWidth(table);
|
|
473
|
+
wrapper.style.setProperty("--cta-width", `${largestWidth}rem`);
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
export const loadAjaxTable = function (table, form, pagination, wrapper) {
|
|
477
|
+
const resolvePath = (object, path, defaultValue) => path.split(/[\.\[\]\'\"]/).filter(p => p).reduce((o, p) => o ? o[p] : defaultValue, object);
|
|
478
|
+
let queryString = new URLSearchParams(new FormData(form)).toString();
|
|
479
|
+
let columns = table.querySelectorAll('thead tr th');
|
|
480
|
+
let tbody = table.querySelector('tbody');
|
|
481
|
+
fetch(form.getAttribute('data-ajax'), {
|
|
482
|
+
method: 'get',
|
|
483
|
+
credentials: 'same-origin',
|
|
484
|
+
headers: new Headers({
|
|
485
|
+
'Content-Type': 'application/json',
|
|
486
|
+
Accept: 'application/json',
|
|
487
|
+
'X-Requested-With': 'XMLHttpRequest'
|
|
488
|
+
})
|
|
489
|
+
}).then((response) => response.json()).then((response) => {
|
|
490
|
+
if (response.data) {
|
|
491
|
+
tbody.innerHTML = '';
|
|
492
|
+
response.data.forEach((row, index) => {
|
|
493
|
+
var table_row = document.createElement('tr');
|
|
494
|
+
columns.forEach((col, index) => {
|
|
495
|
+
let cellOutput = '';
|
|
496
|
+
var table_cell = document.createElement('td');
|
|
497
|
+
// Add some data to help with the mobile layout design
|
|
498
|
+
table_cell.setAttribute('data-label', col.innerText);
|
|
499
|
+
if (col.getAttribute('data-output')) {
|
|
500
|
+
var cellTemplate = col.getAttribute('data-output');
|
|
501
|
+
// Use a regex to replace {var} with actual values from the json data
|
|
502
|
+
cellOutput = cellTemplate.replace(new RegExp(/{(.*?)}/, "gm"), function (matched) { return resolvePath(row, matched.replace('{', '').replace('}', '')); });
|
|
503
|
+
}
|
|
504
|
+
if (col.hasAttribute('data-format')) {
|
|
505
|
+
cellOutput = formatCell(col.getAttribute('data-format'), cellOutput);
|
|
506
|
+
}
|
|
507
|
+
table_cell.innerHTML = cellOutput;
|
|
508
|
+
table_row.appendChild(table_cell);
|
|
509
|
+
});
|
|
510
|
+
tbody.appendChild(table_row);
|
|
511
|
+
});
|
|
512
|
+
createSearchDataList(table, form);
|
|
513
|
+
// Add data to the pagination
|
|
514
|
+
makeTableFunctional(table, form, pagination, wrapper);
|
|
515
|
+
wrapper.setAttribute('data-total', (response.meta.total ? response.meta.total : 1));
|
|
516
|
+
wrapper.setAttribute('data-page', (response.meta.current_page ? response.meta.current_page : 1));
|
|
517
|
+
wrapper.setAttribute('data-pages', Math.ceil(wrapper.getAttribute('data-total') / wrapper.getAttribute('data-show')));
|
|
518
|
+
createPaginationButttons(wrapper, pagination);
|
|
519
|
+
if (response.data.length == 0) {
|
|
520
|
+
tbody.innerHTML = '<tr><td colspan="100%"><span class="h4 m-0">No results found</span></td></tr>';
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
tbody.innerHTML = '<tr><td colspan="100%"><span class="h6 m-0">Error loading table</span></td></tr>';
|
|
441
525
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
526
|
+
});
|
|
527
|
+
};
|
|
528
|
+
export const formatCell = (format, cellOutput) => {
|
|
529
|
+
switch (format) {
|
|
530
|
+
case 'date':
|
|
531
|
+
cellOutput = new Date(cellOutput).toLocaleDateString('en-gb', { year: "2-digit", month: "long", day: "numeric" });
|
|
532
|
+
break;
|
|
533
|
+
case 'capitalise':
|
|
534
|
+
cellOutput = ucfirst(cellOutput);
|
|
535
|
+
break;
|
|
449
536
|
}
|
|
537
|
+
return cellOutput;
|
|
450
538
|
};
|
|
451
|
-
export default table;
|