@iamproperty/components 2.7.9 → 3.0.0

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.
Files changed (172) hide show
  1. package/README.md +16 -137
  2. package/assets/css/core.min.css +1 -1
  3. package/assets/css/core.min.css.map +1 -1
  4. package/assets/css/email.min.css +1 -1
  5. package/assets/css/email.min.css.map +1 -1
  6. package/assets/css/error.min.css +1 -1
  7. package/assets/css/error.min.css.map +1 -1
  8. package/assets/css/style.min.css +1 -1
  9. package/assets/css/style.min.css.map +1 -1
  10. package/assets/favicons/manifest.json +32 -0
  11. package/assets/js/main.js +57 -78
  12. package/assets/js/modules/accordion.js +32 -36
  13. package/assets/js/modules/alert.js +56 -56
  14. package/assets/js/modules/carousel.js +101 -101
  15. package/assets/js/modules/chart.js +218 -217
  16. package/assets/js/modules/drawer.js +15 -15
  17. package/assets/js/modules/form.js +158 -158
  18. package/assets/js/modules/helpers.js +119 -119
  19. package/assets/js/modules/modal.js +89 -89
  20. package/assets/js/modules/nav.js +28 -27
  21. package/assets/js/modules/table.js +585 -585
  22. package/assets/js/modules/testimonial.js +82 -82
  23. package/assets/js/modules/youtubevideo.js +145 -145
  24. package/assets/js/scripts.bundle.js +174 -102
  25. package/assets/js/scripts.bundle.js.map +1 -1
  26. package/assets/js/scripts.bundle.min.js +2 -2
  27. package/assets/js/scripts.bundle.min.js.map +1 -1
  28. package/assets/sass/_components.scss +14 -14
  29. package/assets/sass/_corefiles.scss +40 -40
  30. package/assets/sass/_fonts.scss +16 -16
  31. package/assets/sass/_forms.scss +9 -9
  32. package/assets/sass/_func.scss +12 -10
  33. package/assets/sass/_functions/functions.scss +141 -141
  34. package/assets/sass/_functions/mixins.scss +170 -170
  35. package/assets/sass/_functions/utilities.scss +250 -250
  36. package/assets/sass/_functions/variables.scss +467 -462
  37. package/assets/sass/_print.scss +61 -61
  38. package/assets/sass/_tests/colours.spec.scss +45 -0
  39. package/assets/sass/_tests/func.spec.scss +233 -0
  40. package/assets/sass/_tests/mixins.spec.scss +194 -0
  41. package/assets/sass/_tests/sass.spec.js +9 -0
  42. package/assets/sass/_tests/typography.spec.scss +36 -0
  43. package/assets/sass/components/accordion.scss +197 -197
  44. package/assets/sass/components/alert.scss +98 -98
  45. package/assets/sass/components/cardDeck.scss +107 -107
  46. package/assets/sass/components/carousel.scss +234 -234
  47. package/assets/sass/components/charts.scss +569 -569
  48. package/assets/sass/components/drawer.scss +46 -46
  49. package/assets/sass/components/header.scss +63 -63
  50. package/assets/sass/components/modal.scss +136 -136
  51. package/assets/sass/components/nav.scss +919 -820
  52. package/assets/sass/components/property-searchbar.scss +143 -143
  53. package/assets/sass/components/snapshot.scss +70 -70
  54. package/assets/sass/components/stepper.scss +164 -164
  55. package/assets/sass/components/tabs.scss +87 -87
  56. package/assets/sass/components/testimonial.scss +132 -132
  57. package/assets/sass/components/timeline.scss +95 -95
  58. package/assets/sass/core.scss +6 -6
  59. package/assets/sass/elements/buttons.scss +209 -209
  60. package/assets/sass/elements/card.scss +177 -177
  61. package/assets/sass/elements/container.scss +225 -225
  62. package/assets/sass/elements/forms.scss +194 -194
  63. package/assets/sass/elements/links.scss +96 -96
  64. package/assets/sass/elements/lists.scss +112 -112
  65. package/assets/sass/elements/panel.scss +161 -161
  66. package/assets/sass/elements/tables.scss +290 -290
  67. package/assets/sass/elements/tooltips.scss +84 -84
  68. package/assets/sass/elements/type.scss +136 -136
  69. package/assets/sass/email.scss +65 -65
  70. package/assets/sass/error.scss +4 -4
  71. package/assets/sass/foundations/brand.scss +72 -72
  72. package/assets/sass/foundations/circles.scss +74 -74
  73. package/assets/sass/foundations/icons.scss +72 -72
  74. package/assets/sass/foundations/media.scss +50 -50
  75. package/assets/sass/foundations/reboot.scss +130 -130
  76. package/assets/sass/foundations/root.scss +106 -104
  77. package/assets/sass/main.scss +7 -7
  78. package/assets/svg/icons.svg +598 -588
  79. package/assets/svg/logo.svg +42 -42
  80. package/assets/ts/main.js +57 -0
  81. package/assets/ts/main.js.map +1 -0
  82. package/assets/ts/main.ts +68 -0
  83. package/assets/ts/modules/accordion.js +33 -0
  84. package/assets/ts/modules/accordion.js.map +1 -0
  85. package/assets/ts/modules/accordion.ts +43 -0
  86. package/dist/components.es.js +2504 -0
  87. package/dist/components.umd.js +56 -3760
  88. package/dist/style.css +1 -0
  89. package/package.json +108 -103
  90. package/src/components/Accordion/Accordion.screenshot.vue +57 -0
  91. package/src/components/Accordion/Accordion.spec.js +63 -0
  92. package/src/components/Accordion/Accordion.vue +22 -22
  93. package/src/components/Accordion/AccordionItem.vue +52 -52
  94. package/src/components/Accordion/README.md +34 -34
  95. package/src/components/Accordion/__screenshots__/win32/laptop/Accordion.png +0 -0
  96. package/src/components/Accordion/__screenshots__/win32/mobile/Accordion.png +0 -0
  97. package/src/components/Accordion/__screenshots__/win32/tablet/Accordion.png +0 -0
  98. package/src/components/Alert/Alert.spec.js +49 -0
  99. package/src/components/Alert/Alert.vue +39 -39
  100. package/src/components/Alert/README.md +28 -28
  101. package/src/components/Banner/Banner.spec.js +28 -0
  102. package/src/components/Banner/Banner.vue +38 -38
  103. package/src/components/Banner/README.md +23 -23
  104. package/src/components/CardDeck/CardDeck.spec.js +99 -0
  105. package/src/components/CardDeck/CardDeck.vue +77 -77
  106. package/src/components/CardDeck/README.md +24 -24
  107. package/src/components/Carousel/Carousel.spec.js +45 -0
  108. package/src/components/Carousel/Carousel.vue +85 -85
  109. package/src/components/Carousel/README.md +19 -19
  110. package/src/components/Chart/Chart.spec.js +201 -0
  111. package/src/components/Chart/Chart.vue +88 -88
  112. package/src/components/Chart/README.md +17 -17
  113. package/src/components/Drawer/Drawer.vue +53 -53
  114. package/src/components/Drawer/README.md +22 -22
  115. package/src/components/Header/Header.spec.js +33 -0
  116. package/src/components/Header/Header.vue +38 -38
  117. package/src/components/Header/README.md +27 -27
  118. package/src/components/Modal/Modal.spec.js +22 -0
  119. package/src/components/Modal/Modal.vue +43 -43
  120. package/src/components/Modal/README.md +19 -19
  121. package/src/components/Nav/Nav.spec.js +35 -0
  122. package/src/components/Nav/Nav.vue +215 -189
  123. package/src/components/Nav/README.md +22 -22
  124. package/src/components/NoteFeed/NoteFeed.vue +79 -79
  125. package/src/components/NoteFeed/README.md +16 -16
  126. package/src/components/PropertySearchbar/PropertySearchbar.vue +204 -204
  127. package/src/components/PropertySearchbar/README.md +25 -25
  128. package/src/components/Snapshot/README.md +20 -20
  129. package/src/components/Snapshot/Snapshot.vue +32 -32
  130. package/src/components/Stepper/README.md +32 -32
  131. package/src/components/Stepper/Step.vue +28 -28
  132. package/src/components/Stepper/Stepper.spec.js +99 -0
  133. package/src/components/Stepper/Stepper.vue +33 -33
  134. package/src/components/Tabs/README.md +27 -27
  135. package/src/components/Tabs/Tab.vue +32 -26
  136. package/src/components/Tabs/Tabs.vue +77 -75
  137. package/src/components/Testimonial/README.md +25 -25
  138. package/src/components/Testimonial/Testimonial.spec.js +57 -0
  139. package/src/components/Testimonial/Testimonial.vue +60 -60
  140. package/src/components/Timeline/README.md +18 -18
  141. package/src/components/Timeline/Timeline.spec.js +17 -0
  142. package/src/components/Timeline/Timeline.vue +24 -24
  143. package/src/elements/Card/Card.vue +113 -113
  144. package/src/elements/Card/README.md +24 -24
  145. package/src/elements/FileUploads/FileUploads.vue +48 -48
  146. package/src/elements/FileUploads/README.md +24 -24
  147. package/src/elements/Input/Input.vue +268 -268
  148. package/src/elements/Input/README.md +19 -19
  149. package/src/elements/Table/README.md +62 -62
  150. package/src/elements/Table/Table.spec.js +90 -0
  151. package/src/elements/Table/Table.vue +129 -129
  152. package/src/foundations/Icon/Icon.spec.js +24 -0
  153. package/src/foundations/Icon/Icon.vue +24 -24
  154. package/src/foundations/Icon/README.md +11 -11
  155. package/src/foundations/Logo/Logo.spec.js +56 -0
  156. package/src/foundations/Logo/Logo.vue +39 -39
  157. package/src/foundations/Logo/README.md +20 -20
  158. package/src/foundations/YoutubeVideo/README.md +11 -11
  159. package/src/foundations/YoutubeVideo/YoutubeVideo.vue +24 -24
  160. package/src/helpers/strings.js +12 -12
  161. package/src/index.js +27 -27
  162. package/src/vue-shim.d.ts +6 -0
  163. package/assets/.DS_Store +0 -0
  164. package/dist/components.common.js +0 -3749
  165. package/dist/components.common.js.map +0 -1
  166. package/dist/components.css +0 -2
  167. package/dist/components.css.map +0 -1
  168. package/dist/components.umd.js.map +0 -1
  169. package/dist/components.umd.min.js +0 -2
  170. package/dist/components.umd.min.js.map +0 -1
  171. package/dist/demo.html +0 -1
  172. package/src/.DS_Store +0 -0
@@ -1,585 +1,585 @@
1
- import { zeroPad, isNumeric } from "./helpers";
2
-
3
- function table(tableElement) {
4
-
5
- if(typeof tableElement != "object")
6
- return false;
7
-
8
- const thead = tableElement.querySelector('thead');
9
- const tbody = tableElement.querySelector('tbody');
10
- const storedData = tbody.cloneNode(true);
11
- const sortedEvent = new Event('sorted');
12
- const filteredEvent = new Event('filtered');
13
- const reorderedEvent = new Event('reordered');
14
- const randID = 'table_'+Math.random().toString(36).substr(2, 9); // Random to make sure IDs created are unique
15
- let draggedRow;
16
-
17
- tableElement.setAttribute('id',randID)
18
-
19
- // #region Sortable
20
- const sortTable = function(sortBy,sort){
21
-
22
- // Create an array from the table rows, the index created is then used to sort the array
23
- let tableArr = [];
24
- Array.from(tbody.querySelectorAll('tr')).forEach((tableRow, index) => {
25
-
26
- let rowIndex = tableRow.querySelector('td[data-label="'+sortBy+'"], th[data-label="'+sortBy+'"]').textContent;
27
-
28
- if(isNumeric(rowIndex))
29
- rowIndex = zeroPad(rowIndex,10)
30
-
31
- const dataRow = {
32
- index: rowIndex,
33
- row: tableRow
34
- }
35
- tableArr.push(dataRow);
36
- });
37
-
38
- // Sort array
39
- tableArr.sort((a, b) => (a.index > b.index) ? 1 : -1)
40
-
41
- // Reverse if descending
42
- if(sort == "descending")
43
- tableArr = tableArr.reverse();
44
-
45
- // Create a string to return and populate the tbody
46
- let strTbody = '';
47
- tableArr.forEach((tableRow, index) => {
48
- strTbody += tableRow.row.outerHTML;
49
- });
50
- tbody.innerHTML = strTbody;
51
-
52
- // Dispatch the sortable event
53
- tableElement.dispatchEvent(sortedEvent);
54
- }
55
-
56
- // Declare event handlers
57
- tableElement.addEventListener('click', function(e){
58
- for (var target = e.target; target && target != this; target = target.parentNode) {
59
- if (target.matches('[data-sortable]')) {
60
-
61
- // Get current sort order
62
- let sort = target.getAttribute('aria-sort') == "ascending" ? "descending" : "ascending";
63
-
64
- // unset sort attributes
65
- Array.from(tableElement.querySelectorAll('[data-sortable]')).forEach((col, index) => {
66
- col.setAttribute('aria-sort','none');
67
- });
68
-
69
- // Set the sort order attribute
70
- target.setAttribute('aria-sort', sort);
71
-
72
- // Save the sort options on the table element so that it can be re-sorted later
73
- tableElement.setAttribute('data-sort', sort);
74
- tableElement.setAttribute('data-sortBy', target.textContent);
75
-
76
- // Sort the table
77
- sortTable(target.textContent, sort);
78
-
79
- Array.from(tableElement.querySelectorAll('tr[draggable]')).forEach((tableRow, index) => {
80
-
81
- tableRow.removeAttribute('draggable');
82
- });
83
- break;
84
- }
85
- }
86
- }, false);
87
-
88
- // On page load check if the table should be pre-sorted, if so trigger a click
89
- if(tableElement.getAttribute('data-sortBy')){
90
-
91
- let sort = tableElement.getAttribute('data-sort') == "ascending" ? "descending" : "ascending";
92
-
93
- Array.from(tableElement.querySelectorAll('[data-sortable]')).forEach((col, index) => {
94
- if(col.textContent == tableElement.getAttribute('data-sortBy')){
95
- col.setAttribute('aria-sort',sort)
96
- col.click();
97
- }
98
- });
99
- }
100
-
101
- // #endregion Sortable
102
-
103
- // #region Filters
104
- const createFilterForm = function(count){
105
-
106
- // Create wrapper div
107
- const form = document.createElement("div");
108
- form.classList.add('table__filters');
109
- form.classList.add('row');
110
- form.classList.add('pt-1');
111
- form.classList.add('pb-3');
112
-
113
- // Create the filter options array
114
- const filterColumns = Array.from(tableElement.querySelectorAll('th[data-filterable]'));
115
-
116
- // Populate a list of searchable terms from the cells of the columns that could be used as a filter
117
- let searchableTerms = {};
118
- filterColumns.forEach((columnHeading, index) => {
119
- Array.from(tableElement.querySelectorAll('td[data-label="'+columnHeading.textContent+'"]')).forEach((label, index) => {
120
-
121
- searchableTerms[label.textContent] = label.textContent;
122
- });
123
- });
124
-
125
- // Create the form
126
- const filterTitle = filterColumns.length == 1 ? "Filter by "+filterColumns[0].textContent : "Filter"; // Update title if only one filter is chosen
127
- const checkboxClass = filterColumns.length == 1 ? "d-none" : "d-sm-flex"; // Hide controls when only one filter is chosen
128
-
129
- form.innerHTML = `<div class="col-sm-6 col-md-4 pb-3">
130
- <div class="form-control__wrapper form-control-inline mb-0">
131
- <label for="${randID}_filter" class="form-label">${filterTitle}:</label>
132
- <input type="search" name="${randID}_filter" id="${randID}_filter" class="form-control form-control-sm" placeholder="" list="${randID}_list" />
133
- </div>
134
- <datalist id="${randID}_list">
135
- ${Object.keys(searchableTerms).map(term => `<option value="${term}"></option>`).join("")}
136
- </datalist>
137
- </div>
138
- <div class="col-md-8 align-items-center pb-3 ${checkboxClass}">
139
- ${`<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("")}
140
- </div>`;
141
-
142
- // Add before the actual table
143
- tableElement.prepend(form)
144
- }
145
-
146
- const filterTable = function(searchTerm){
147
-
148
- // Create an array of rows that match the search term
149
- let tableArr = [];
150
- Array.from(storedData.querySelectorAll('tr')).forEach((tableRow, index) => {
151
-
152
- // We want one long search string per row including each filterable table cell
153
- let rowSearchString = '';
154
- Array.from(tableElement.querySelectorAll('[type="checkbox"]:checked + label')).forEach((label, index) => {
155
- rowSearchString += tableRow.querySelector('td[data-label="'+label.textContent+'"]').textContent+' | ';
156
- });
157
-
158
- // Check if the table row search string contains the search term
159
- if(rowSearchString.indexOf(searchTerm) >= 0){
160
-
161
- const dataRow = { row: tableRow }
162
- tableArr.push(dataRow);
163
- }
164
- });
165
-
166
- // Create a string to return and populate the tbody
167
- let strTbody = '';
168
- tableArr.forEach((tableRow, index) => {
169
- strTbody += tableRow.row.outerHTML;
170
- });
171
- tbody.innerHTML = strTbody;
172
-
173
- // Dispatch the filter event.
174
- tableElement.dispatchEvent(filteredEvent);
175
- }
176
-
177
- const createFilterList = function(){
178
-
179
- // Check which options are checked
180
- let filterOptions = [];
181
- Array.from(tableElement.querySelectorAll('[type="checkbox"]:checked + label')).forEach((label, index) => {
182
- filterOptions.push(label.textContent);
183
- });
184
-
185
- // Build up the list of searchable terms
186
- let searchableTerms = [];
187
- filterOptions.forEach((option, index) => {
188
- Array.from(tableElement.querySelectorAll('td[data-label="'+option+'"]')).forEach((label, index) => {
189
- searchableTerms[label.textContent] = label.textContent;
190
- });
191
- });
192
-
193
- // Rebuild the list
194
- let dataList = tableElement.querySelector('datalist');
195
- dataList.innerHTML = Object.keys(searchableTerms).map(term => `<option value="${term}"></option>`).join("");
196
- }
197
-
198
- // On page load check if filters are needed
199
- if(Array.from(tableElement.querySelectorAll('[data-filterable]')).length){
200
-
201
- // Create the filter options
202
- createFilterForm(tableElement,Array.from(tableElement.querySelectorAll('[data-filterable]')).length);
203
-
204
- // Add event handlers for the filter options
205
- tableElement.addEventListener('keyup', function(e){
206
- for (var target = e.target; target && target != this; target = target.parentNode) {
207
- if (target.matches('input[type="search"]')) {
208
-
209
- const searchTerm = target.value;
210
- filterTable(searchTerm)
211
- }
212
- }
213
- });
214
-
215
- tableElement.addEventListener('change', function(e){
216
- for (var target = e.target; target && target != this; target = target.parentNode) {
217
- if (target.matches('input[type="search"]')) {
218
-
219
- const searchTerm = target.value;
220
- filterTable(searchTerm)
221
- }
222
- }
223
- });
224
-
225
- tableElement.addEventListener('change', function(e){
226
- for (var target = e.target; target && target != this; target = target.parentNode) {
227
- if (target.matches('input[type="checkbox"]')) {
228
-
229
- const searchTerm = tableElement.querySelector('input[type="search"]').value;
230
- filterTable(searchTerm)
231
- createFilterList()
232
- }
233
- }
234
- });
235
- }
236
- // #endregion Filters
237
-
238
- // #region Pagination
239
- const paginateRows = function(show, page){
240
-
241
- // Create some inline CSS to control what is viewed on the table, unline the filters we are just hiding the rable rows not removing them from the DOM.
242
- let style = document.getElementById(randID+'_style');
243
-
244
- if(style == null){
245
- style = document.createElement("style");
246
- style.setAttribute('id',randID+'_style')
247
- }
248
-
249
- const startShowing = (show*(page-1))+1;
250
- const stopShowing = show*(page);
251
-
252
- style.innerHTML = `
253
- #${randID} tbody tr {
254
- display: none;
255
- }
256
- #${randID} tbody tr:nth-child(${startShowing}),
257
- #${randID} tbody tr:nth-child(${startShowing}) ~ tr{
258
- display: block;
259
- }
260
- @media screen and (min-width: 36em) {
261
- #${randID} tbody tr:nth-child(${startShowing}),
262
- #${randID} tbody tr:nth-child(${startShowing}) ~ tr{
263
- display: table-row;
264
- }
265
- }
266
- #${randID} tbody tr:nth-child(${stopShowing}) ~ tr{
267
- display: none;
268
- }
269
- `;
270
-
271
- tableElement.append(style);
272
- }
273
-
274
- // On page load check if the table should be paginated
275
- if(tableElement.getAttribute('data-show')){
276
-
277
- const show = parseInt(tableElement.getAttribute('data-show'));
278
- const page = parseInt(tableElement.getAttribute('data-page')) ? parseInt(tableElement.getAttribute('data-page')) : 1;
279
- const totalRows = tableElement.querySelectorAll('tbody tr').length;
280
-
281
- if(show < totalRows){
282
- paginateRows(show,page);
283
- createPaginationForm(randID,tableElement,show,page,totalRows);
284
- createPaginationButttons(randID,tableElement,show,page,totalRows);
285
-
286
- tableElement.addEventListener('change', function(e){
287
- for (var target = e.target; target && target != this; target = target.parentNode) {
288
- if (target.matches('.table__pagination input[type="number"]')) {
289
-
290
- paginateRows(target.value,page);
291
- createPaginationButttons(randID,tableElement,target.value,page,totalRows);
292
- tableElement.setAttribute('data-show',target.value)
293
- }
294
- }
295
- });
296
-
297
- tableElement.addEventListener('click', function(e){
298
- for (var target = e.target; target && target != this; target = target.parentNode) {
299
- if (target.matches('.page-item:not(.active):not(.disabled) .page-link')) {
300
-
301
- paginateRows(tableElement.getAttribute('data-show'),target.getAttribute('data-page'));
302
- createPaginationButttons(randID,tableElement,tableElement.getAttribute('data-show'),target.getAttribute('data-page'),totalRows);
303
- }
304
- }
305
- }, false);
306
-
307
- tableElement.addEventListener('change', function(e){
308
- for (var target = e.target; target && target != this; target = target.parentNode) {
309
- if (target.matches('.table__pagination select')) {
310
-
311
- paginateRows(tableElement.getAttribute('data-show'),target.value);
312
- createPaginationButttons(randID,tableElement,tableElement.getAttribute('data-show'),target.value,totalRows);
313
- }
314
- }
315
- });
316
- }
317
- }
318
- // #endregion Pagination
319
-
320
- // #region Reorderable
321
- // Set the row thats being dragged and copy the row
322
- function setDraggedRow(e) {
323
- e.dataTransfer.setData("text/plain", e.target.id);
324
- draggedRow = e.target;
325
- e.target.classList.add('tr--dragging');
326
- }
327
-
328
- // Create the order column and event handler for rows
329
- const setReorderRows = function(){
330
-
331
- Array.from(tbody.querySelectorAll('tr')).forEach((tableRow, index) => {
332
-
333
- // Create column if not already created
334
- if(tableRow.querySelector('[data-label="Order"]') == null){
335
-
336
- const orderColumn = document.createElement('th');
337
- orderColumn.innerHTML = index + 1;
338
- orderColumn.setAttribute('data-label','Order');
339
- tableRow.prepend(orderColumn);
340
- }
341
-
342
- // Make draggable
343
- tableRow.setAttribute('id',randID+'_row_'+(index+1));
344
- tableRow.setAttribute('data-order',index+1);
345
- tableRow.setAttribute('draggable','true');
346
- tableRow.addEventListener("dragstart", setDraggedRow);
347
- });
348
- }
349
-
350
- if(tableElement.getAttribute('data-reorder')){
351
-
352
- // Add column heading
353
- const orderHeading = document.createElement('th');
354
- orderHeading.innerHTML = 'Order';
355
- orderHeading.title = 'Click here to enable re-ordering via drag and drop';
356
- orderHeading.classList.add('table-order-reset');
357
- thead.querySelector('tr').prepend(orderHeading);
358
-
359
- setReorderRows();
360
-
361
- // Reset order button
362
- tableElement.addEventListener('click', function(e){
363
- for (var target = e.target; target && target != this; target = target.parentNode) {
364
- if (target.matches('.table-order-reset')) {
365
-
366
- // unset sort attributes
367
- Array.from(tableElement.querySelectorAll('[data-sortable]')).forEach((col, index) => {
368
- col.setAttribute('aria-sort','none');
369
- });
370
-
371
- // Save the sort options on the table element so that it can be re-sorted later
372
- tableElement.removeAttribute('data-sort');
373
- tableElement.removeAttribute('data-sortBy');
374
-
375
- // Sort the table
376
- sortTable('Order', 'ascending');
377
-
378
- Array.from(tableElement.querySelectorAll('tbody tr')).forEach((tableRow, index) => {
379
-
380
- tableRow.setAttribute('draggable','true');
381
- });
382
-
383
- break;
384
- }
385
- }
386
- }, false);
387
-
388
-
389
- document.addEventListener("dragover", function( e ) {
390
- // prevent default to allow drop
391
- e.preventDefault();
392
- }, false);
393
-
394
- document.addEventListener("dragenter", function( e ) {
395
- // prevent default to allow drop
396
- e.preventDefault();
397
- e.dataTransfer.dropEffect = "move";
398
-
399
- for (var target = e.target; target && target != this; target = target.parentNode) {
400
- if (target.matches('[data-reorder] tbody tr')) {
401
-
402
- target.classList.add('tr--dropable')
403
- }
404
- }
405
- }, false);
406
-
407
- document.addEventListener("dragleave", function( e ) {
408
- // prevent default to allow drop
409
- e.preventDefault();
410
- for (var target = e.target; target && target != this; target = target.parentNode) {
411
- if (target.matches('[data-reorder] tbody tr')) {
412
-
413
- target.classList.remove('tr--dropable')
414
- }
415
- }
416
- }, false);
417
-
418
- document.addEventListener("drop", function(e) {
419
-
420
- e.preventDefault();
421
-
422
- for (var target = e.target; target && target != this; target = target.parentNode) {
423
- if (target.matches('[data-reorder] tbody tr')) {
424
-
425
- if(target.parentNode != null && draggedRow.parentNode != null && target != draggedRow){
426
-
427
- draggedRow.parentNode.removeChild( draggedRow );
428
-
429
- if(draggedRow.getAttribute('data-order') > target.getAttribute('data-order'))
430
- target.parentNode.insertBefore(draggedRow, target);
431
- else
432
- target.parentNode.insertBefore(draggedRow, target.nextElementSibling);
433
-
434
- // Re label the rows
435
- Array.from(tbody.querySelectorAll('tr')).forEach((tableRowOrder, index) => {
436
- tableRowOrder.classList.remove('tr--dragging')
437
- tableRowOrder.classList.remove('tr--dropable')
438
- tableRowOrder.querySelector('th').innerHTML = index + 1;
439
- tableRowOrder.setAttribute('data-order',index+1);
440
- });
441
-
442
- tableElement.dispatchEvent(reorderedEvent);
443
- }
444
- break;
445
- }
446
- }
447
- }, false);
448
-
449
- }
450
- // #endregion Reorderable
451
-
452
- // Watch for the filterable event and re-sort the tbody
453
- tableElement.addEventListener('filtered', function (e) {
454
-
455
- if(tableElement.getAttribute('data-sortBy') && tableElement.getAttribute('data-sort'))
456
- sortTable(tableElement.getAttribute('data-sortBy'), tableElement.getAttribute('data-sort'));
457
-
458
- if(tableElement.getAttribute('data-show')){
459
-
460
- const show = parseInt(tableElement.getAttribute('data-show'));
461
- const totalRows = tableElement.querySelectorAll('tbody tr').length;
462
- const tablePagination = tableElement.querySelector('.table__pagination');
463
-
464
- if(tablePagination != null)
465
- tablePagination.remove();
466
-
467
- if(show < totalRows){
468
-
469
- paginateRows(show,1);
470
- createPaginationForm(randID,tableElement,show,1,totalRows);
471
- createPaginationButttons(randID,tableElement,show,1,totalRows);
472
- }
473
- }
474
-
475
- if(tableElement.getAttribute('data-reorder')){
476
-
477
- setReorderRows();
478
- }
479
- }, false);
480
-
481
- tableElement.addEventListener('sorted', function (e) {
482
-
483
- if(tableElement.getAttribute('data-reorder')){
484
-
485
- setReorderRows();
486
- }
487
- }, false);
488
-
489
- tableElement.addEventListener('populated', function (e) {
490
-
491
- var tableFilter = tableElement.querySelector('.table__filters')
492
- tableFilter.remove();
493
-
494
- var tablePagination = tableElement.querySelector('.table__pagination')
495
- tablePagination.remove();
496
-
497
- var newTable = tableElement.cloneNode(true);
498
- tableElement.parentNode.replaceChild(newTable, tableElement);
499
-
500
- table(newTable);
501
- }, false);
502
- }
503
-
504
- export const createPaginationForm = function(randID,tableElement,show,page,totalRows){
505
-
506
- const form = document.createElement("div");
507
- form.classList.add('table__pagination');
508
- form.classList.add('row');
509
- form.classList.add('pt-3');
510
- form.classList.add('pb-3');
511
-
512
- // Create the form and create a container div to hold the pagination buttons
513
- form.innerHTML = `<div class="col mw-fit-content mb-3">
514
- <div class="form-control__wrapper form-control-inline mb-0">
515
- <label for="${randID}_showing" class="form-label">Showing:</label>
516
- <input type="number" name="${randID}_showing" id="${randID}_showing" class="form-control form-control-sm showing-input-field" placeholder="" list="${randID}_pagination" value="${show}" min="1" max="${totalRows}" />
517
- </div>
518
- <datalist id="${randID}_pagination">
519
- <option value="5">5</option>
520
- ${totalRows > 10 ? `<option value="10">10</option>` : ''}
521
- ${totalRows > 20 ? `<option value="20">20</option>` : ''}
522
- <option value="${totalRows}">${totalRows}</option>
523
- </datalist>
524
- </div>
525
- <div class="col mw-fit-content me-auto d-flex align-items-center mb-3"><span class="label">per page</span></div>
526
- <div class="col mw-fit-content d-sm-flex justify-content-end align-items-center" id="${randID}_paginationBtns"></div>`;
527
-
528
- // Add after the actual table
529
- tableElement.append(form)
530
- }
531
-
532
- export const createPaginationButttons = function(randID,tableElement,show,page,totalRows){
533
-
534
- const paginationButtonsWrapper = document.getElementById(randID+'_paginationBtns')
535
-
536
- if(paginationButtonsWrapper == null)
537
- return false;
538
-
539
- const numberPages = Math.ceil(totalRows / show)
540
-
541
- if(numberPages == 1){ // Remore the buttons or dont display any if we dont need them
542
- paginationButtonsWrapper.innerHTML = '';
543
- }
544
- else if(numberPages < 5){ // If less than 5 pages (which fits comfortably on mobile) we display buttons
545
-
546
- let strButtons = '';
547
-
548
- for (let i = 1; i <= numberPages; i++) {
549
-
550
- if(i == page)
551
- strButtons += `<li class="page-item active" aria-current="page"><span class="page-link">${i}</span></li>`;
552
- else
553
- strButtons += `<li class="page-item"><button class="page-link" data-page="${i}">${i}</button></li>`;
554
- }
555
-
556
- paginationButtonsWrapper.innerHTML = `<span class="pe-2 mb-3">Page: </span><ul class="pagination mb-3">
557
- ${page == 1 ? `<li class="page-item disabled"><span class="page-link">Previous</span></li>` : `<li class="page-item"><button class="page-link" data-page="${parseInt(page)-1}">Previous</button></li>`}
558
- ${strButtons}
559
- ${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>`}
560
- </ul>`;
561
-
562
- }
563
- else { // If more than 5 lets show a select field instead so that we dont have loads and loads of buttons
564
-
565
- let strOptions = '';
566
-
567
- for (let i = 1; i <= numberPages; i++) {
568
-
569
- if(i == page)
570
- strOptions += `<option value="${i}" selected>Page ${i}</option>`;
571
- else
572
- strOptions += `<option value="${i}">Page ${i}</option>`;
573
- }
574
-
575
- paginationButtonsWrapper.innerHTML = `
576
- <div class="form-control__wrapper page-number mb-2">
577
- <select class="form-select">
578
- ${strOptions}
579
- </select>
580
- </div>
581
- `;
582
- }
583
- }
584
-
585
- export default table
1
+ import { zeroPad, isNumeric } from "./helpers";
2
+
3
+ function table(tableElement) {
4
+
5
+ if(typeof tableElement != "object")
6
+ return false;
7
+
8
+ const thead = tableElement.querySelector('thead');
9
+ const tbody = tableElement.querySelector('tbody');
10
+ const storedData = tbody.cloneNode(true);
11
+ const sortedEvent = new Event('sorted');
12
+ const filteredEvent = new Event('filtered');
13
+ const reorderedEvent = new Event('reordered');
14
+ const randID = 'table_'+Math.random().toString(36).substr(2, 9); // Random to make sure IDs created are unique
15
+ let draggedRow;
16
+
17
+ tableElement.setAttribute('id',randID)
18
+
19
+ // #region Sortable
20
+ const sortTable = function(sortBy,sort){
21
+
22
+ // Create an array from the table rows, the index created is then used to sort the array
23
+ let tableArr = [];
24
+ Array.from(tbody.querySelectorAll('tr')).forEach((tableRow, index) => {
25
+
26
+ let rowIndex = tableRow.querySelector('td[data-label="'+sortBy+'"], th[data-label="'+sortBy+'"]').textContent;
27
+
28
+ if(isNumeric(rowIndex))
29
+ rowIndex = zeroPad(rowIndex,10)
30
+
31
+ const dataRow = {
32
+ index: rowIndex,
33
+ row: tableRow
34
+ }
35
+ tableArr.push(dataRow);
36
+ });
37
+
38
+ // Sort array
39
+ tableArr.sort((a, b) => (a.index > b.index) ? 1 : -1)
40
+
41
+ // Reverse if descending
42
+ if(sort == "descending")
43
+ tableArr = tableArr.reverse();
44
+
45
+ // Create a string to return and populate the tbody
46
+ let strTbody = '';
47
+ tableArr.forEach((tableRow, index) => {
48
+ strTbody += tableRow.row.outerHTML;
49
+ });
50
+ tbody.innerHTML = strTbody;
51
+
52
+ // Dispatch the sortable event
53
+ tableElement.dispatchEvent(sortedEvent);
54
+ }
55
+
56
+ // Declare event handlers
57
+ tableElement.addEventListener('click', function(e){
58
+ for (var target = e.target; target && target != this; target = target.parentNode) {
59
+ if (target.matches('[data-sortable]')) {
60
+
61
+ // Get current sort order
62
+ let sort = target.getAttribute('aria-sort') == "ascending" ? "descending" : "ascending";
63
+
64
+ // unset sort attributes
65
+ Array.from(tableElement.querySelectorAll('[data-sortable]')).forEach((col, index) => {
66
+ col.setAttribute('aria-sort','none');
67
+ });
68
+
69
+ // Set the sort order attribute
70
+ target.setAttribute('aria-sort', sort);
71
+
72
+ // Save the sort options on the table element so that it can be re-sorted later
73
+ tableElement.setAttribute('data-sort', sort);
74
+ tableElement.setAttribute('data-sortBy', target.textContent);
75
+
76
+ // Sort the table
77
+ sortTable(target.textContent, sort);
78
+
79
+ Array.from(tableElement.querySelectorAll('tr[draggable]')).forEach((tableRow, index) => {
80
+
81
+ tableRow.removeAttribute('draggable');
82
+ });
83
+ break;
84
+ }
85
+ }
86
+ }, false);
87
+
88
+ // On page load check if the table should be pre-sorted, if so trigger a click
89
+ if(tableElement.getAttribute('data-sortBy')){
90
+
91
+ let sort = tableElement.getAttribute('data-sort') == "ascending" ? "descending" : "ascending";
92
+
93
+ Array.from(tableElement.querySelectorAll('[data-sortable]')).forEach((col, index) => {
94
+ if(col.textContent == tableElement.getAttribute('data-sortBy')){
95
+ col.setAttribute('aria-sort',sort)
96
+ col.click();
97
+ }
98
+ });
99
+ }
100
+
101
+ // #endregion Sortable
102
+
103
+ // #region Filters
104
+ const createFilterForm = function(count){
105
+
106
+ // Create wrapper div
107
+ const form = document.createElement("div");
108
+ form.classList.add('table__filters');
109
+ form.classList.add('row');
110
+ form.classList.add('pt-1');
111
+ form.classList.add('pb-3');
112
+
113
+ // Create the filter options array
114
+ const filterColumns = Array.from(tableElement.querySelectorAll('th[data-filterable]'));
115
+
116
+ // Populate a list of searchable terms from the cells of the columns that could be used as a filter
117
+ let searchableTerms = {};
118
+ filterColumns.forEach((columnHeading, index) => {
119
+ Array.from(tableElement.querySelectorAll('td[data-label="'+columnHeading.textContent+'"]')).forEach((label, index) => {
120
+
121
+ searchableTerms[label.textContent] = label.textContent;
122
+ });
123
+ });
124
+
125
+ // Create the form
126
+ const filterTitle = filterColumns.length == 1 ? "Filter by "+filterColumns[0].textContent : "Filter"; // Update title if only one filter is chosen
127
+ const checkboxClass = filterColumns.length == 1 ? "d-none" : "d-sm-flex"; // Hide controls when only one filter is chosen
128
+
129
+ form.innerHTML = `<div class="col-sm-6 col-md-4 pb-3">
130
+ <div class="form-control__wrapper form-control-inline mb-0">
131
+ <label for="${randID}_filter" class="form-label">${filterTitle}:</label>
132
+ <input type="search" name="${randID}_filter" id="${randID}_filter" class="form-control form-control-sm" placeholder="" list="${randID}_list" />
133
+ </div>
134
+ <datalist id="${randID}_list">
135
+ ${Object.keys(searchableTerms).map(term => `<option value="${term}"></option>`).join("")}
136
+ </datalist>
137
+ </div>
138
+ <div class="col-md-8 align-items-center pb-3 ${checkboxClass}">
139
+ ${`<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("")}
140
+ </div>`;
141
+
142
+ // Add before the actual table
143
+ tableElement.prepend(form)
144
+ }
145
+
146
+ const filterTable = function(searchTerm){
147
+
148
+ // Create an array of rows that match the search term
149
+ let tableArr = [];
150
+ Array.from(storedData.querySelectorAll('tr')).forEach((tableRow, index) => {
151
+
152
+ // We want one long search string per row including each filterable table cell
153
+ let rowSearchString = '';
154
+ Array.from(tableElement.querySelectorAll('[type="checkbox"]:checked + label')).forEach((label, index) => {
155
+ rowSearchString += tableRow.querySelector('td[data-label="'+label.textContent+'"]').textContent+' | ';
156
+ });
157
+
158
+ // Check if the table row search string contains the search term
159
+ if(rowSearchString.indexOf(searchTerm) >= 0){
160
+
161
+ const dataRow = { row: tableRow }
162
+ tableArr.push(dataRow);
163
+ }
164
+ });
165
+
166
+ // Create a string to return and populate the tbody
167
+ let strTbody = '';
168
+ tableArr.forEach((tableRow, index) => {
169
+ strTbody += tableRow.row.outerHTML;
170
+ });
171
+ tbody.innerHTML = strTbody;
172
+
173
+ // Dispatch the filter event.
174
+ tableElement.dispatchEvent(filteredEvent);
175
+ }
176
+
177
+ const createFilterList = function(){
178
+
179
+ // Check which options are checked
180
+ let filterOptions = [];
181
+ Array.from(tableElement.querySelectorAll('[type="checkbox"]:checked + label')).forEach((label, index) => {
182
+ filterOptions.push(label.textContent);
183
+ });
184
+
185
+ // Build up the list of searchable terms
186
+ let searchableTerms = [];
187
+ filterOptions.forEach((option, index) => {
188
+ Array.from(tableElement.querySelectorAll('td[data-label="'+option+'"]')).forEach((label, index) => {
189
+ searchableTerms[label.textContent] = label.textContent;
190
+ });
191
+ });
192
+
193
+ // Rebuild the list
194
+ let dataList = tableElement.querySelector('datalist');
195
+ dataList.innerHTML = Object.keys(searchableTerms).map(term => `<option value="${term}"></option>`).join("");
196
+ }
197
+
198
+ // On page load check if filters are needed
199
+ if(Array.from(tableElement.querySelectorAll('[data-filterable]')).length){
200
+
201
+ // Create the filter options
202
+ createFilterForm(tableElement,Array.from(tableElement.querySelectorAll('[data-filterable]')).length);
203
+
204
+ // Add event handlers for the filter options
205
+ tableElement.addEventListener('keyup', function(e){
206
+ for (var target = e.target; target && target != this; target = target.parentNode) {
207
+ if (target.matches('input[type="search"]')) {
208
+
209
+ const searchTerm = target.value;
210
+ filterTable(searchTerm)
211
+ }
212
+ }
213
+ });
214
+
215
+ tableElement.addEventListener('change', function(e){
216
+ for (var target = e.target; target && target != this; target = target.parentNode) {
217
+ if (target.matches('input[type="search"]')) {
218
+
219
+ const searchTerm = target.value;
220
+ filterTable(searchTerm)
221
+ }
222
+ }
223
+ });
224
+
225
+ tableElement.addEventListener('change', function(e){
226
+ for (var target = e.target; target && target != this; target = target.parentNode) {
227
+ if (target.matches('input[type="checkbox"]')) {
228
+
229
+ const searchTerm = tableElement.querySelector('input[type="search"]').value;
230
+ filterTable(searchTerm)
231
+ createFilterList()
232
+ }
233
+ }
234
+ });
235
+ }
236
+ // #endregion Filters
237
+
238
+ // #region Pagination
239
+ const paginateRows = function(show, page){
240
+
241
+ // Create some inline CSS to control what is viewed on the table, unline the filters we are just hiding the rable rows not removing them from the DOM.
242
+ let style = document.getElementById(randID+'_style');
243
+
244
+ if(style == null){
245
+ style = document.createElement("style");
246
+ style.setAttribute('id',randID+'_style')
247
+ }
248
+
249
+ const startShowing = (show*(page-1))+1;
250
+ const stopShowing = show*(page);
251
+
252
+ style.innerHTML = `
253
+ #${randID} tbody tr {
254
+ display: none;
255
+ }
256
+ #${randID} tbody tr:nth-child(${startShowing}),
257
+ #${randID} tbody tr:nth-child(${startShowing}) ~ tr{
258
+ display: block;
259
+ }
260
+ @media screen and (min-width: 36em) {
261
+ #${randID} tbody tr:nth-child(${startShowing}),
262
+ #${randID} tbody tr:nth-child(${startShowing}) ~ tr{
263
+ display: table-row;
264
+ }
265
+ }
266
+ #${randID} tbody tr:nth-child(${stopShowing}) ~ tr{
267
+ display: none;
268
+ }
269
+ `;
270
+
271
+ tableElement.append(style);
272
+ }
273
+
274
+ // On page load check if the table should be paginated
275
+ if(tableElement.getAttribute('data-show')){
276
+
277
+ const show = parseInt(tableElement.getAttribute('data-show'));
278
+ const page = parseInt(tableElement.getAttribute('data-page')) ? parseInt(tableElement.getAttribute('data-page')) : 1;
279
+ const totalRows = tableElement.querySelectorAll('tbody tr').length;
280
+
281
+ if(show < totalRows){
282
+ paginateRows(show,page);
283
+ createPaginationForm(randID,tableElement,show,page,totalRows);
284
+ createPaginationButttons(randID,tableElement,show,page,totalRows);
285
+
286
+ tableElement.addEventListener('change', function(e){
287
+ for (var target = e.target; target && target != this; target = target.parentNode) {
288
+ if (target.matches('.table__pagination input[type="number"]')) {
289
+
290
+ paginateRows(target.value,page);
291
+ createPaginationButttons(randID,tableElement,target.value,page,totalRows);
292
+ tableElement.setAttribute('data-show',target.value)
293
+ }
294
+ }
295
+ });
296
+
297
+ tableElement.addEventListener('click', function(e){
298
+ for (var target = e.target; target && target != this; target = target.parentNode) {
299
+ if (target.matches('.page-item:not(.active):not(.disabled) .page-link')) {
300
+
301
+ paginateRows(tableElement.getAttribute('data-show'),target.getAttribute('data-page'));
302
+ createPaginationButttons(randID,tableElement,tableElement.getAttribute('data-show'),target.getAttribute('data-page'),totalRows);
303
+ }
304
+ }
305
+ }, false);
306
+
307
+ tableElement.addEventListener('change', function(e){
308
+ for (var target = e.target; target && target != this; target = target.parentNode) {
309
+ if (target.matches('.table__pagination select')) {
310
+
311
+ paginateRows(tableElement.getAttribute('data-show'),target.value);
312
+ createPaginationButttons(randID,tableElement,tableElement.getAttribute('data-show'),target.value,totalRows);
313
+ }
314
+ }
315
+ });
316
+ }
317
+ }
318
+ // #endregion Pagination
319
+
320
+ // #region Reorderable
321
+ // Set the row thats being dragged and copy the row
322
+ function setDraggedRow(e) {
323
+ e.dataTransfer.setData("text/plain", e.target.id);
324
+ draggedRow = e.target;
325
+ e.target.classList.add('tr--dragging');
326
+ }
327
+
328
+ // Create the order column and event handler for rows
329
+ const setReorderRows = function(){
330
+
331
+ Array.from(tbody.querySelectorAll('tr')).forEach((tableRow, index) => {
332
+
333
+ // Create column if not already created
334
+ if(tableRow.querySelector('[data-label="Order"]') == null){
335
+
336
+ const orderColumn = document.createElement('th');
337
+ orderColumn.innerHTML = index + 1;
338
+ orderColumn.setAttribute('data-label','Order');
339
+ tableRow.prepend(orderColumn);
340
+ }
341
+
342
+ // Make draggable
343
+ tableRow.setAttribute('id',randID+'_row_'+(index+1));
344
+ tableRow.setAttribute('data-order',index+1);
345
+ tableRow.setAttribute('draggable','true');
346
+ tableRow.addEventListener("dragstart", setDraggedRow);
347
+ });
348
+ }
349
+
350
+ if(tableElement.getAttribute('data-reorder') && tableElement.getAttribute('data-reorder') != "false"){
351
+
352
+ // Add column heading
353
+ const orderHeading = document.createElement('th');
354
+ orderHeading.innerHTML = 'Order';
355
+ orderHeading.title = 'Click here to enable re-ordering via drag and drop';
356
+ orderHeading.classList.add('table-order-reset');
357
+ thead.querySelector('tr').prepend(orderHeading);
358
+
359
+ setReorderRows();
360
+
361
+ // Reset order button
362
+ tableElement.addEventListener('click', function(e){
363
+ for (var target = e.target; target && target != this; target = target.parentNode) {
364
+ if (target.matches('.table-order-reset')) {
365
+
366
+ // unset sort attributes
367
+ Array.from(tableElement.querySelectorAll('[data-sortable]')).forEach((col, index) => {
368
+ col.setAttribute('aria-sort','none');
369
+ });
370
+
371
+ // Save the sort options on the table element so that it can be re-sorted later
372
+ tableElement.removeAttribute('data-sort');
373
+ tableElement.removeAttribute('data-sortBy');
374
+
375
+ // Sort the table
376
+ sortTable('Order', 'ascending');
377
+
378
+ Array.from(tableElement.querySelectorAll('tbody tr')).forEach((tableRow, index) => {
379
+
380
+ tableRow.setAttribute('draggable','true');
381
+ });
382
+
383
+ break;
384
+ }
385
+ }
386
+ }, false);
387
+
388
+
389
+ document.addEventListener("dragover", function( e ) {
390
+ // prevent default to allow drop
391
+ e.preventDefault();
392
+ }, false);
393
+
394
+ document.addEventListener("dragenter", function( e ) {
395
+ // prevent default to allow drop
396
+ e.preventDefault();
397
+ e.dataTransfer.dropEffect = "move";
398
+
399
+ for (var target = e.target; target && target != this; target = target.parentNode) {
400
+ if (target.matches('[data-reorder] tbody tr')) {
401
+
402
+ target.classList.add('tr--dropable')
403
+ }
404
+ }
405
+ }, false);
406
+
407
+ document.addEventListener("dragleave", function( e ) {
408
+ // prevent default to allow drop
409
+ e.preventDefault();
410
+ for (var target = e.target; target && target != this; target = target.parentNode) {
411
+ if (target.matches('[data-reorder] tbody tr')) {
412
+
413
+ target.classList.remove('tr--dropable')
414
+ }
415
+ }
416
+ }, false);
417
+
418
+ document.addEventListener("drop", function(e) {
419
+
420
+ e.preventDefault();
421
+
422
+ for (var target = e.target; target && target != this; target = target.parentNode) {
423
+ if (target.matches('[data-reorder] tbody tr')) {
424
+
425
+ if(target.parentNode != null && draggedRow.parentNode != null && target != draggedRow){
426
+
427
+ draggedRow.parentNode.removeChild( draggedRow );
428
+
429
+ if(draggedRow.getAttribute('data-order') > target.getAttribute('data-order'))
430
+ target.parentNode.insertBefore(draggedRow, target);
431
+ else
432
+ target.parentNode.insertBefore(draggedRow, target.nextElementSibling);
433
+
434
+ // Re label the rows
435
+ Array.from(tbody.querySelectorAll('tr')).forEach((tableRowOrder, index) => {
436
+ tableRowOrder.classList.remove('tr--dragging')
437
+ tableRowOrder.classList.remove('tr--dropable')
438
+ tableRowOrder.querySelector('th').innerHTML = index + 1;
439
+ tableRowOrder.setAttribute('data-order',index+1);
440
+ });
441
+
442
+ tableElement.dispatchEvent(reorderedEvent);
443
+ }
444
+ break;
445
+ }
446
+ }
447
+ }, false);
448
+
449
+ }
450
+ // #endregion Reorderable
451
+
452
+ // Watch for the filterable event and re-sort the tbody
453
+ tableElement.addEventListener('filtered', function (e) {
454
+
455
+ if(tableElement.getAttribute('data-sortBy') && tableElement.getAttribute('data-sort'))
456
+ sortTable(tableElement.getAttribute('data-sortBy'), tableElement.getAttribute('data-sort'));
457
+
458
+ if(tableElement.getAttribute('data-show')){
459
+
460
+ const show = parseInt(tableElement.getAttribute('data-show'));
461
+ const totalRows = tableElement.querySelectorAll('tbody tr').length;
462
+ const tablePagination = tableElement.querySelector('.table__pagination');
463
+
464
+ if(tablePagination != null)
465
+ tablePagination.remove();
466
+
467
+ if(show < totalRows){
468
+
469
+ paginateRows(show,1);
470
+ createPaginationForm(randID,tableElement,show,1,totalRows);
471
+ createPaginationButttons(randID,tableElement,show,1,totalRows);
472
+ }
473
+ }
474
+
475
+ if(tableElement.getAttribute('data-reorder')){
476
+
477
+ setReorderRows();
478
+ }
479
+ }, false);
480
+
481
+ tableElement.addEventListener('sorted', function (e) {
482
+
483
+ if(tableElement.getAttribute('data-reorder')){
484
+
485
+ setReorderRows();
486
+ }
487
+ }, false);
488
+
489
+ tableElement.addEventListener('populated', function (e) {
490
+
491
+ var tableFilter = tableElement.querySelector('.table__filters')
492
+ tableFilter.remove();
493
+
494
+ var tablePagination = tableElement.querySelector('.table__pagination')
495
+ tablePagination.remove();
496
+
497
+ var newTable = tableElement.cloneNode(true);
498
+ tableElement.parentNode.replaceChild(newTable, tableElement);
499
+
500
+ table(newTable);
501
+ }, false);
502
+ }
503
+
504
+ export const createPaginationForm = function(randID,tableElement,show,page,totalRows){
505
+
506
+ const form = document.createElement("div");
507
+ form.classList.add('table__pagination');
508
+ form.classList.add('row');
509
+ form.classList.add('pt-3');
510
+ form.classList.add('pb-3');
511
+
512
+ // Create the form and create a container div to hold the pagination buttons
513
+ form.innerHTML = `<div class="col mw-fit-content mb-3">
514
+ <div class="form-control__wrapper form-control-inline mb-0">
515
+ <label for="${randID}_showing" class="form-label">Showing:</label>
516
+ <input type="number" name="${randID}_showing" id="${randID}_showing" class="form-control form-control-sm showing-input-field" placeholder="" list="${randID}_pagination" value="${show}" min="1" max="${totalRows}" />
517
+ </div>
518
+ <datalist id="${randID}_pagination">
519
+ <option value="5">5</option>
520
+ ${totalRows > 10 ? `<option value="10">10</option>` : ''}
521
+ ${totalRows > 20 ? `<option value="20">20</option>` : ''}
522
+ <option value="${totalRows}">${totalRows}</option>
523
+ </datalist>
524
+ </div>
525
+ <div class="col mw-fit-content me-auto d-flex align-items-center mb-3"><span class="label">per page</span></div>
526
+ <div class="col mw-fit-content d-sm-flex justify-content-end align-items-center" id="${randID}_paginationBtns"></div>`;
527
+
528
+ // Add after the actual table
529
+ tableElement.append(form)
530
+ }
531
+
532
+ export const createPaginationButttons = function(randID,tableElement,show,page,totalRows){
533
+
534
+ const paginationButtonsWrapper = document.getElementById(randID+'_paginationBtns')
535
+
536
+ if(paginationButtonsWrapper == null)
537
+ return false;
538
+
539
+ const numberPages = Math.ceil(totalRows / show)
540
+
541
+ if(numberPages == 1){ // Remore the buttons or dont display any if we dont need them
542
+ paginationButtonsWrapper.innerHTML = '';
543
+ }
544
+ else if(numberPages < 5){ // If less than 5 pages (which fits comfortably on mobile) we display buttons
545
+
546
+ let strButtons = '';
547
+
548
+ for (let i = 1; i <= numberPages; i++) {
549
+
550
+ if(i == page)
551
+ strButtons += `<li class="page-item active" aria-current="page"><span class="page-link">${i}</span></li>`;
552
+ else
553
+ strButtons += `<li class="page-item"><button class="page-link" data-page="${i}">${i}</button></li>`;
554
+ }
555
+
556
+ paginationButtonsWrapper.innerHTML = `<span class="pe-2 mb-3">Page: </span><ul class="pagination mb-3">
557
+ ${page == 1 ? `<li class="page-item disabled"><span class="page-link">Previous</span></li>` : `<li class="page-item"><button class="page-link" data-page="${parseInt(page)-1}">Previous</button></li>`}
558
+ ${strButtons}
559
+ ${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>`}
560
+ </ul>`;
561
+
562
+ }
563
+ else { // If more than 5 lets show a select field instead so that we dont have loads and loads of buttons
564
+
565
+ let strOptions = '';
566
+
567
+ for (let i = 1; i <= numberPages; i++) {
568
+
569
+ if(i == page)
570
+ strOptions += `<option value="${i}" selected>Page ${i}</option>`;
571
+ else
572
+ strOptions += `<option value="${i}">Page ${i}</option>`;
573
+ }
574
+
575
+ paginationButtonsWrapper.innerHTML = `
576
+ <div class="form-control__wrapper page-number mb-2">
577
+ <select class="form-select">
578
+ ${strOptions}
579
+ </select>
580
+ </div>
581
+ `;
582
+ }
583
+ }
584
+
585
+ export default table