@iamproperty/components 3.1.0 → 3.4.4

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