@iamproperty/components 3.0.0 → 3.2.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 (161) hide show
  1. package/README.md +141 -16
  2. package/assets/.DS_Store +0 -0
  3. package/assets/css/core.min.css +1 -1
  4. package/assets/css/core.min.css.map +1 -1
  5. package/assets/css/email.min.css +1 -1
  6. package/assets/css/email.min.css.map +1 -1
  7. package/assets/css/error.min.css +1 -1
  8. package/assets/css/error.min.css.map +1 -1
  9. package/assets/css/style.min.css +1 -1
  10. package/assets/css/style.min.css.map +1 -1
  11. package/assets/favicons/manifest.json +31 -31
  12. package/assets/js/main.js +57 -57
  13. package/assets/js/modules/accordion.js +32 -32
  14. package/assets/js/modules/alert.js +56 -56
  15. package/assets/js/modules/carousel.js +101 -101
  16. package/assets/js/modules/chart.js +218 -218
  17. package/assets/js/modules/drawer.js +15 -15
  18. package/assets/js/modules/file-upload.js +48 -0
  19. package/assets/js/modules/form.js +168 -159
  20. package/assets/js/modules/helpers.js +119 -119
  21. package/assets/js/modules/modal.js +89 -89
  22. package/assets/js/modules/nav.js +28 -28
  23. package/assets/js/modules/orderablelist.js +122 -0
  24. package/assets/js/modules/table.js +585 -585
  25. package/assets/js/modules/testimonial.js +82 -82
  26. package/assets/js/modules/youtubevideo.js +145 -145
  27. package/assets/js/scripts.bundle.js +256 -242
  28. package/assets/js/scripts.bundle.js.map +1 -1
  29. package/assets/js/scripts.bundle.min.js +2 -2
  30. package/assets/js/scripts.bundle.min.js.map +1 -1
  31. package/assets/sass/_components.scss +14 -14
  32. package/assets/sass/_corefiles.scss +40 -40
  33. package/assets/sass/_fonts.scss +16 -16
  34. package/assets/sass/_forms.scss +9 -9
  35. package/assets/sass/_func.scss +12 -12
  36. package/assets/sass/_functions/functions.scss +141 -141
  37. package/assets/sass/_functions/mixins.scss +170 -170
  38. package/assets/sass/_functions/utilities.scss +143 -250
  39. package/assets/sass/_functions/variables.scss +467 -467
  40. package/assets/sass/_print.scss +61 -61
  41. package/assets/sass/_tests/colours.spec.scss +44 -44
  42. package/assets/sass/_tests/func.spec.scss +232 -232
  43. package/assets/sass/_tests/mixins.spec.scss +194 -194
  44. package/assets/sass/_tests/sass.spec.js +9 -9
  45. package/assets/sass/_tests/typography.spec.scss +35 -35
  46. package/assets/sass/components/accordion.scss +197 -197
  47. package/assets/sass/components/alert.scss +98 -98
  48. package/assets/sass/components/cardDeck.scss +107 -107
  49. package/assets/sass/components/carousel.scss +234 -234
  50. package/assets/sass/components/charts.scss +569 -569
  51. package/assets/sass/components/drawer.scss +46 -46
  52. package/assets/sass/components/header.scss +63 -63
  53. package/assets/sass/components/modal.scss +136 -136
  54. package/assets/sass/components/nav.scss +960 -918
  55. package/assets/sass/components/property-searchbar.scss +143 -143
  56. package/assets/sass/components/snapshot.scss +70 -70
  57. package/assets/sass/components/stepper.scss +164 -164
  58. package/assets/sass/components/tabs.scss +87 -87
  59. package/assets/sass/components/testimonial.scss +132 -132
  60. package/assets/sass/components/timeline.scss +95 -95
  61. package/assets/sass/core.scss +6 -6
  62. package/assets/sass/elements/buttons.scss +251 -209
  63. package/assets/sass/elements/card.scss +289 -177
  64. package/assets/sass/elements/container.scss +236 -225
  65. package/assets/sass/elements/forms.scss +262 -194
  66. package/assets/sass/elements/links.scss +97 -96
  67. package/assets/sass/elements/lists.scss +159 -112
  68. package/assets/sass/elements/panel.scss +161 -161
  69. package/assets/sass/elements/tables.scss +290 -290
  70. package/assets/sass/elements/tooltips.scss +84 -84
  71. package/assets/sass/elements/type.scss +136 -136
  72. package/assets/sass/email.scss +65 -65
  73. package/assets/sass/error.scss +4 -4
  74. package/assets/sass/foundations/brand.scss +76 -72
  75. package/assets/sass/foundations/circles.scss +74 -74
  76. package/assets/sass/foundations/icons.scss +80 -72
  77. package/assets/sass/foundations/media.scss +50 -50
  78. package/assets/sass/foundations/reboot.scss +130 -130
  79. package/assets/sass/foundations/root.scss +125 -106
  80. package/assets/sass/main.scss +7 -7
  81. package/assets/svg/icons.svg +598 -598
  82. package/assets/svg/logo.svg +49 -43
  83. package/assets/ts/main.js +56 -56
  84. package/assets/ts/main.ts +68 -68
  85. package/assets/ts/modules/accordion.js +32 -32
  86. package/assets/ts/modules/accordion.ts +43 -43
  87. package/dist/components.es.js +393 -379
  88. package/dist/components.umd.js +16 -16
  89. package/dist/style.css +1 -1
  90. package/package.json +96 -108
  91. package/src/.DS_Store +0 -0
  92. package/src/components/Accordion/Accordion.screenshot.vue +57 -57
  93. package/src/components/Accordion/Accordion.spec.js +63 -63
  94. package/src/components/Accordion/Accordion.vue +22 -22
  95. package/src/components/Accordion/AccordionItem.vue +52 -52
  96. package/src/components/Accordion/README.md +34 -34
  97. package/src/components/Alert/Alert.spec.js +49 -49
  98. package/src/components/Alert/Alert.vue +39 -39
  99. package/src/components/Alert/README.md +28 -28
  100. package/src/components/Banner/Banner.spec.js +28 -28
  101. package/src/components/Banner/Banner.vue +38 -38
  102. package/src/components/Banner/README.md +23 -23
  103. package/src/components/CardDeck/CardDeck.spec.js +99 -99
  104. package/src/components/CardDeck/CardDeck.vue +77 -77
  105. package/src/components/CardDeck/README.md +24 -24
  106. package/src/components/Carousel/Carousel.spec.js +45 -45
  107. package/src/components/Carousel/Carousel.vue +85 -85
  108. package/src/components/Carousel/README.md +19 -19
  109. package/src/components/Chart/Chart.spec.js +201 -201
  110. package/src/components/Chart/Chart.vue +88 -88
  111. package/src/components/Chart/README.md +17 -17
  112. package/src/components/Drawer/Drawer.vue +53 -53
  113. package/src/components/Drawer/README.md +22 -22
  114. package/src/components/Header/Header.spec.js +33 -33
  115. package/src/components/Header/Header.vue +38 -38
  116. package/src/components/Header/README.md +27 -27
  117. package/src/components/Modal/Modal.spec.js +22 -22
  118. package/src/components/Modal/Modal.vue +43 -43
  119. package/src/components/Modal/README.md +19 -19
  120. package/src/components/Nav/Nav.spec.js +35 -35
  121. package/src/components/Nav/Nav.vue +215 -215
  122. package/src/components/Nav/README.md +22 -22
  123. package/src/components/NoteFeed/NoteFeed.vue +79 -79
  124. package/src/components/NoteFeed/README.md +16 -16
  125. package/src/components/PropertySearchbar/PropertySearchbar.vue +204 -204
  126. package/src/components/PropertySearchbar/README.md +25 -25
  127. package/src/components/Snapshot/README.md +20 -20
  128. package/src/components/Snapshot/Snapshot.vue +32 -32
  129. package/src/components/Stepper/README.md +32 -32
  130. package/src/components/Stepper/Step.vue +28 -28
  131. package/src/components/Stepper/Stepper.spec.js +99 -99
  132. package/src/components/Stepper/Stepper.vue +33 -33
  133. package/src/components/Tabs/README.md +27 -27
  134. package/src/components/Tabs/Tab.vue +32 -32
  135. package/src/components/Tabs/Tabs.vue +77 -77
  136. package/src/components/Testimonial/README.md +25 -25
  137. package/src/components/Testimonial/Testimonial.spec.js +57 -57
  138. package/src/components/Testimonial/Testimonial.vue +60 -60
  139. package/src/components/Timeline/README.md +18 -18
  140. package/src/components/Timeline/Timeline.spec.js +17 -17
  141. package/src/components/Timeline/Timeline.vue +24 -24
  142. package/src/elements/Card/Card.vue +122 -113
  143. package/src/elements/Card/README.md +24 -24
  144. package/src/elements/FileUploads/FileUploads.vue +48 -48
  145. package/src/elements/FileUploads/README.md +24 -24
  146. package/src/elements/Input/Input.vue +272 -268
  147. package/src/elements/Input/README.md +19 -19
  148. package/src/elements/Table/README.md +62 -62
  149. package/src/elements/Table/Table.spec.js +90 -90
  150. package/src/elements/Table/Table.vue +129 -129
  151. package/src/foundations/Icon/Icon.spec.js +24 -24
  152. package/src/foundations/Icon/Icon.vue +24 -24
  153. package/src/foundations/Icon/README.md +11 -11
  154. package/src/foundations/Logo/Logo.spec.js +56 -56
  155. package/src/foundations/Logo/Logo.vue +39 -39
  156. package/src/foundations/Logo/README.md +20 -20
  157. package/src/foundations/YoutubeVideo/README.md +11 -11
  158. package/src/foundations/YoutubeVideo/YoutubeVideo.vue +24 -24
  159. package/src/helpers/strings.js +12 -12
  160. package/src/index.js +27 -27
  161. package/src/vue-shim.d.ts +6 -6
@@ -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') && 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
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