innoboxrr-vue-datatable 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js ADDED
@@ -0,0 +1,3 @@
1
+ import DataTable from './src/DataTable.vue';
2
+
3
+ export default DataTable;
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "innoboxrr-vue-datatable",
3
+ "version": "1.0.0",
4
+ "description": "Datatable para vue3",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/innoboxrr/vue-datatable.git"
12
+ },
13
+ "keywords": [
14
+ "vue3",
15
+ "datatable",
16
+ "table",
17
+ "crud"
18
+ ],
19
+ "author": "Homero Raul Vargas Cruz",
20
+ "license": "MIT",
21
+ "bugs": {
22
+ "url": "https://github.com/innoboxrr/vue-datatable/issues"
23
+ },
24
+ "homepage": "https://github.com/innoboxrr/vue-datatable#readme"
25
+ }
@@ -0,0 +1,545 @@
1
+ <template>
2
+
3
+ <div>
4
+
5
+ <div v-if="showTopbar">
6
+
7
+ <!-- Tools -->
8
+ <div class="uk-card uk-card-body">
9
+
10
+ <div uk-grid>
11
+
12
+ <!-- Actions -->
13
+ <div class="uk-width-expand@m" v-if="hasActions">
14
+
15
+ <!-- Action Button-->
16
+ <button
17
+ class="uk-button button uk-button-large uk-border-rounded uk-box-shadow-medium uk-box-shadow-hover-small white-text uk-text-bold"
18
+ @click="actionButtonClicked(crudActions)">
19
+
20
+ Acciones
21
+
22
+ </button>
23
+
24
+ <!-- Dropdown Actions-->
25
+ <nav-dropdown-component id="actionCrudDropdown" pos="right">
26
+
27
+ <li v-for="action in crudActions" :key="action.id">
28
+
29
+ <template v-if="action.route">
30
+
31
+ <icon-route-component
32
+ v-if="action.policy"
33
+ :name="action.params.to.name"
34
+ :params="{...action.params.to.params, ...extraParams}"
35
+ :icon="action.icon"
36
+ :text="action.name" />
37
+
38
+ <disabled-link-component
39
+ v-else
40
+ :icon="action.icon"
41
+ :text="action.name"/>
42
+
43
+ </template>
44
+
45
+ <template v-else>
46
+
47
+ <icon-link-component
48
+ v-if="action.policy"
49
+ :icon="action.icon"
50
+ :text="action.name"
51
+ @click="actionClicked(action) , closeDropdown($event)" />
52
+
53
+ <disabled-link-component
54
+ v-else
55
+ :icon="action.icon"
56
+ :text="action.name"/>
57
+
58
+ </template>
59
+
60
+ </li>
61
+
62
+ </nav-dropdown-component>
63
+
64
+ </div>
65
+
66
+ <div v-if="hasFilter" class="uk-width-auto@m">
67
+
68
+ <div class="uk-grid-divider uk-child-width-expand uk-text-center" uk-grid>
69
+
70
+ <div>
71
+
72
+ <a
73
+ class="uk-text-right cursor"
74
+ :uk-tooltip="`title: ${'Update results'}`"
75
+ @click="updateFilters">
76
+
77
+ <i class="fas fa-sync tools-icon"></i>
78
+
79
+ </a>
80
+
81
+ </div>
82
+
83
+ <!-- Filter -->
84
+ <div>
85
+
86
+ <a
87
+ class="uk-text-right cursor"
88
+ uk-toggle="target: .filter-form; animation: uk-animation-scale-up;"
89
+ :uk-tooltip="`title: Buscar">
90
+
91
+ <i class="fas fa-sliders-h tools-icon"></i>
92
+
93
+ </a>
94
+
95
+ </div>
96
+
97
+ </div>
98
+
99
+ </div>
100
+
101
+ </div>
102
+
103
+ </div>
104
+
105
+ <!-- Filter Form-->
106
+ <div
107
+ v-if="hasFilter"
108
+ class="filter-form uk-card uk-card-body uk-padding-remove-top"
109
+ hidden>
110
+
111
+ <slot name="filterForm"></slot>
112
+
113
+ </div>
114
+
115
+ </div>
116
+
117
+ <!-- Table -->
118
+ <div
119
+ class="uk-container uk-container-expand"
120
+ :class="{
121
+ 'ptb-20': showTopbar
122
+ }">
123
+
124
+ <div
125
+ class="uk-card uk-card-body uk-border-rounded uk-overflow-auto uk-padding-small"
126
+ :class="{
127
+ 'uk-card-default': cardWrapper
128
+ }">
129
+
130
+ <data-table-component
131
+ :actions="hasActions"
132
+ :data-table="dataTable"
133
+ :extra-params="extraParams"
134
+ @sortColumn="sortColumn"
135
+ @actionButtonClicked="actionButtonClicked"
136
+ @actionClicked="actionClicked" />
137
+
138
+ <select-pagination-component
139
+ :meta="pagination.meta"
140
+ :links="pagination.links"
141
+ @updatePage="updatePage" />
142
+
143
+ </div>
144
+
145
+ </div>
146
+
147
+ </div>
148
+
149
+ </template>
150
+
151
+ <script>
152
+
153
+ import NavDropdownComponent from './components/NavDropdownComponent'
154
+ import IconRouteComponent from './components/IconRouteComponent'
155
+ import IconLinkComponent from './components/IconLinkComponent'
156
+ import DisabledLinkComponent from './components/DisabledLinkComponent'
157
+ import DataTableComponent from './components/DataTableComponent'
158
+ import SelectPaginationComponent from './components/SelectPaginationComponent'
159
+
160
+ export default {
161
+
162
+ components: {
163
+
164
+ NavDropdownComponent,
165
+
166
+ IconRouteComponent,
167
+
168
+ IconLinkComponent,
169
+
170
+ DisabledLinkComponent,
171
+
172
+ DataTableComponent,
173
+
174
+ SelectPaginationComponent
175
+
176
+ },
177
+
178
+ props: {
179
+
180
+ dataUrl: {
181
+ type: String,
182
+ required: true
183
+ },
184
+
185
+ model: {
186
+ type: Object,
187
+ required: true
188
+ },
189
+
190
+ policyUrl: {
191
+ type: String,
192
+ required: true
193
+ },
194
+
195
+ showTopbar:{
196
+ type: Boolean,
197
+ default: true
198
+ },
199
+
200
+ hasActions: {
201
+ type: Boolean,
202
+ default: true,
203
+ },
204
+
205
+ hasFilter: {
206
+ type: Boolean,
207
+ default: true,
208
+ },
209
+
210
+ formFilters: {
211
+ type: Object,
212
+ default: {}
213
+ },
214
+
215
+ externalFilters: {
216
+ type: Object,
217
+ default: {}
218
+ },
219
+
220
+ extraParams: {
221
+ type: Object,
222
+ default: {}
223
+ },
224
+
225
+ hideColumns: {
226
+ type: Array,
227
+ default: []
228
+ },
229
+
230
+ cardWrapper: {
231
+ type: Boolean,
232
+ default: true
233
+ }
234
+
235
+ },
236
+
237
+ mounted() {
238
+
239
+ this.fetchData();
240
+
241
+ },
242
+
243
+ data() {
244
+
245
+ return {
246
+
247
+ crudActions: this.model.crudActions(),
248
+
249
+ dataTable: {
250
+
251
+ head: this.dataTableHead(),
252
+
253
+ body: []
254
+
255
+ },
256
+
257
+ pagination: {
258
+
259
+ meta: [],
260
+
261
+ links: []
262
+
263
+ },
264
+
265
+ sort: this.model.dataTableSort(),
266
+
267
+ orderBy: 'id',
268
+
269
+ page: 1,
270
+
271
+ fetchDataAttempts: 0,
272
+
273
+ fetchPoliciesAttempts: 0,
274
+
275
+ }
276
+
277
+ },
278
+
279
+ watch: {
280
+
281
+ formFilters: {
282
+
283
+ handler(val, oldVal) {
284
+
285
+ this.page = 1;
286
+
287
+ this.updateFilters();
288
+
289
+ },
290
+
291
+ deep: true
292
+
293
+ },
294
+
295
+ externalFilters: {
296
+
297
+ handler(val, oldVal) {
298
+
299
+ if(!_.isEqual(val, oldVal))
300
+ this.updateFilters();
301
+
302
+ },
303
+
304
+ deep: true
305
+
306
+ }
307
+
308
+ },
309
+
310
+ methods: {
311
+
312
+ dataTableHead() {
313
+
314
+ // NOTA: En este punto puede existir una oportunidad para sobreescribir las columnas del datatable
315
+
316
+ let cols = this.model.dataTableHead();
317
+
318
+ let rm = this.hideColumns;
319
+
320
+ for( let i = cols.length - 1; i >= 0; i--) {
321
+
322
+ for( let j = 0; j < rm.length; j++) {
323
+
324
+ if(cols[i] && (cols[i].id === rm[j].id)){
325
+
326
+ cols.splice(i, 1);
327
+
328
+ }
329
+
330
+ }
331
+
332
+ }
333
+
334
+ return cols;
335
+
336
+ },
337
+
338
+ fetchData() {
339
+
340
+ axios.post(this.dataUrl, this.getFilters()).then( res => {
341
+
342
+ this.fetchDataAttempts = 0;
343
+
344
+ this.dataTable.body = res.data.data;
345
+
346
+ this.pagination.meta = res.data.meta;
347
+
348
+ this.pagination.links = res.data.links;
349
+
350
+ }).catch(error => {
351
+
352
+ console.log(error);
353
+
354
+ if(error.response.status == 403) {
355
+
356
+ // this.$router.push({name: "NotAuthorized" });
357
+
358
+ } else {
359
+
360
+ if(this.fetchDataAttempts <= 3) {
361
+
362
+ setTimeout( () => {
363
+
364
+ ++this.fetchDataAttempts;
365
+
366
+ this.fetchData();
367
+
368
+ }, 1500);
369
+
370
+ }
371
+
372
+ }
373
+
374
+ });
375
+
376
+ },
377
+
378
+ getFilters() {
379
+
380
+ let formFilters = this.formFilters;
381
+
382
+ let params = {
383
+ _token: csrf_token,
384
+ managed: true,
385
+ except_view_any: true,
386
+ }
387
+
388
+ let order = {
389
+ orderBy: this.orderBy,
390
+ orderMode: this.sort[this.orderBy]
391
+ }
392
+
393
+ let page = {
394
+ page: this.page
395
+ }
396
+
397
+ let filters = {
398
+ ...params,
399
+ ...formFilters,
400
+ ...this.externalFilters,
401
+ ...order,
402
+ ...page
403
+ }
404
+
405
+ return filters;
406
+
407
+ },
408
+
409
+ updateFilters() {
410
+
411
+ // permite las restricciones de consulta estén disponible en el estado global de la aplicación
412
+ this.$store.commit('admin/setCrudFilters', this.getFilters());
413
+
414
+ this.fetchData();
415
+
416
+ },
417
+
418
+ sortColumn(data) {
419
+
420
+ if(data.sortable == true) {
421
+
422
+ let column_id = data.id;
423
+
424
+ // Determinar la columan que va a ordenar los resultados
425
+ this.orderBy = column_id;
426
+
427
+ // Encontrar el orden actual
428
+ let order = (this.sort[column_id] == 'asc') ? 'desc' : 'asc';
429
+
430
+ // Determinar el nuevo orden para la columna dada
431
+ this.sort[column_id] = order;
432
+
433
+ // Solicitar los datos
434
+ this.updateFilters();
435
+
436
+ }
437
+
438
+ },
439
+
440
+ updatePage(page) {
441
+
442
+ this.page = page;
443
+
444
+ this.updateFilters();
445
+
446
+ },
447
+
448
+ actionClicked(action) {
449
+
450
+ this.model[action.callback](action.params).then( res => {
451
+
452
+ UIkit.notification({
453
+ message: res.message,
454
+ status: 'success'
455
+ });
456
+
457
+ this.updateFilters();
458
+
459
+ }).catch( error => {
460
+
461
+ UIkit.notification({
462
+ message: error.message,
463
+ status: 'danger'
464
+ });
465
+
466
+ });
467
+
468
+ },
469
+
470
+ actionButtonClicked(actions) {
471
+
472
+ // Recuperar el id
473
+ let id = actions[0].params.id;
474
+
475
+ axios.post(this.policyUrl, {
476
+
477
+ _token: csrf_token,
478
+
479
+ id: id
480
+
481
+ }).then(res => {
482
+
483
+ this.fetchPoliciesAttempts = 0;
484
+
485
+ let policies = res.data;
486
+
487
+ actions.forEach( action => {
488
+
489
+ if(policies[action.id]) {
490
+
491
+ action.policy = true;
492
+
493
+ }
494
+
495
+ });
496
+
497
+ }).catch(error => {
498
+
499
+ if(this.fetchPoliciesAttempts <= 3) {
500
+
501
+ setTimeout( () => {
502
+
503
+ ++this.fetchPoliciesAttempts;
504
+
505
+ this.actionButtonClicked(actions);
506
+
507
+ }, 1500);
508
+
509
+ } else {
510
+
511
+ setTimeout( () => {
512
+
513
+ this.fetchPoliciesAttempts = 0;
514
+
515
+ }, 3000);
516
+
517
+ }
518
+
519
+ });
520
+
521
+ },
522
+
523
+ closeDropdown(event){
524
+
525
+ let dropdown = event.target.closest('.uk-dropdown');
526
+
527
+ UIkit.dropdown(dropdown).hide(false);
528
+
529
+ }
530
+
531
+ }
532
+
533
+ }
534
+
535
+ </script>
536
+
537
+ <style scoped>
538
+
539
+ .tools-icon {
540
+ font-size: 30px;
541
+ color: var(--secondary-color);
542
+ padding: 10px 15px;
543
+ }
544
+
545
+ </style>
@@ -0,0 +1,194 @@
1
+ <template>
2
+
3
+ <div class="uk-overflow-auto">
4
+
5
+ <table class="uk-table uk-table-divider">
6
+
7
+ <thead>
8
+
9
+ <tr>
10
+
11
+ <th
12
+ v-for="head in dataTable.head"
13
+ :key="head.id"
14
+ :id="`th_${head.id}`"
15
+ @click="sortColumn(head)"
16
+ :class="{pointer: head.sortable}">
17
+
18
+ {{ head.value }}
19
+
20
+ </th>
21
+
22
+ <th
23
+ class="uk-table-shrink"
24
+ v-if="actions"></th>
25
+
26
+ </tr>
27
+
28
+ </thead>
29
+
30
+ <tbody>
31
+
32
+ <tr v-for="body in dataTable.body" :key="body.id">
33
+
34
+ <td v-for="head in dataTable.head" :key="head.id">
35
+
36
+ <span
37
+ v-if="head.html"
38
+ v-html="setData(head, body)"></span>
39
+
40
+ <span v-else>{{ setData(head, body) }}</span>
41
+
42
+ </td>
43
+
44
+ <td v-if="actions">
45
+
46
+ <button
47
+ class="uk-button button"
48
+ @click="actionButtonClicked(body.actions)">
49
+
50
+ <i class="fas fa-cogs"></i>
51
+
52
+ </button>
53
+
54
+ <nav-dropdown-component :id="`dropdown_${body.id}`" pos="left">
55
+
56
+ <li v-for="action in body.actions">
57
+
58
+ <template v-if="action.route">
59
+
60
+ <icon-route-component
61
+ v-if="action.policy"
62
+ :name="action.params.to.name"
63
+ :params="{...action.params.to.params, ...extraParams}"
64
+ :icon="action.icon"
65
+ :text="action.name" />
66
+
67
+ <disabled-link-component
68
+ v-else
69
+ :icon="action.icon"
70
+ :text="action.name"/>
71
+
72
+ </template>
73
+
74
+ <template v-else>
75
+
76
+ <icon-link-component
77
+ v-if="action.policy"
78
+ :icon="action.icon"
79
+ :text="action.name"
80
+ @click="actionClicked(action), closeDropdown($event)" />
81
+
82
+ <disabled-link-component
83
+ v-else
84
+ :icon="action.icon"
85
+ :text="action.name"/>
86
+
87
+ </template>
88
+
89
+ </li>
90
+
91
+ </nav-dropdown-component>
92
+
93
+ </td>
94
+
95
+ </tr>
96
+
97
+ </tbody>
98
+
99
+ </table>
100
+
101
+ </div>
102
+
103
+ </template>
104
+
105
+ <script>
106
+
107
+ import NavDropdownComponent from './NavDropdownComponent'
108
+ import IconRouteComponent from './IconRouteComponent'
109
+ import IconLinkComponent from './IconLinkComponent'
110
+ import DisabledLinkComponent from './DisabledLinkComponent'
111
+
112
+ export default {
113
+
114
+ components: {
115
+
116
+ NavDropdownComponent,
117
+
118
+ IconRouteComponent,
119
+
120
+ IconLinkComponent,
121
+
122
+ DisabledLinkComponent
123
+
124
+ },
125
+
126
+ props: {
127
+
128
+ actions: {
129
+ type: Boolean,
130
+ default: false
131
+ },
132
+
133
+ dataTable: {
134
+ type: [Object, Boolean],
135
+ required: true
136
+ },
137
+
138
+ extraParams: {
139
+ type: Object,
140
+ default: {}
141
+ },
142
+
143
+ },
144
+
145
+ emits: ['sortColumn', 'actionButtonClicked', 'actionClicked'],
146
+
147
+ methods: {
148
+
149
+ sortColumn(data) {
150
+
151
+ this.$emit('sortColumn', data);
152
+
153
+ },
154
+
155
+ actionButtonClicked(data) {
156
+
157
+ this.$emit('actionButtonClicked', data);
158
+
159
+ },
160
+
161
+ actionClicked(data) {
162
+
163
+ this.$emit('actionClicked', data);
164
+
165
+ },
166
+
167
+ setData(head, body) {
168
+
169
+ return (typeof head.parser === 'function') ? head.parser(body[head.id]) : body[head.id]
170
+
171
+ },
172
+
173
+ closeDropdown(event){
174
+
175
+ let dropdown = event.target.closest('.uk-dropdown');
176
+
177
+ UIkit.dropdown(dropdown).hide(false);
178
+
179
+ }
180
+
181
+ }
182
+
183
+ }
184
+
185
+ </script>
186
+
187
+ <style>
188
+
189
+ .uk-table td {
190
+ padding: 12px 14px;
191
+ vertical-align: middle;
192
+ }
193
+
194
+ </style>
@@ -0,0 +1,46 @@
1
+ <template>
2
+
3
+ <a href="#" class="disabled-link" :uk-tooltip="`title: ${ 'This action is not authorized' }; pos:right`">
4
+
5
+ <span
6
+ v-if="showIcon"
7
+ class="uk-margin-small-right uk-icon"
8
+ :uk-icon="icon"></span>
9
+
10
+ <span>{{ text }}</span>
11
+
12
+ </a>
13
+
14
+ </template>
15
+
16
+ <script>
17
+
18
+ export default {
19
+
20
+ props: {
21
+
22
+ icon: {
23
+ type: String,
24
+ default: ''
25
+ },
26
+
27
+ text: {
28
+ type: String,
29
+ required: true
30
+ }
31
+
32
+ },
33
+
34
+ computed: {
35
+
36
+ showIcon() {
37
+
38
+ return (this.icon != '' && this.icon != undefined);
39
+
40
+ }
41
+
42
+ }
43
+
44
+ }
45
+
46
+ </script>
@@ -0,0 +1,69 @@
1
+ <template>
2
+
3
+ <a :href="link" :target="target">
4
+
5
+ <span
6
+ class="uk-margin-small-right uk-icon"
7
+ :uk-icon="iconAttr"
8
+ :style="iconStyle"></span>
9
+
10
+ <span :class="textClass">{{ text }}</span>
11
+
12
+ </a>
13
+
14
+ </template>
15
+
16
+
17
+ <script>
18
+
19
+ export default {
20
+
21
+ props: {
22
+ link: {
23
+ type: String,
24
+ default: '#'
25
+ },
26
+ text: {
27
+ type: String,
28
+ required: true
29
+ },
30
+ icon: {
31
+ type: String,
32
+ required: true
33
+ },
34
+ ratio: {
35
+ type: Number,
36
+ required: false,
37
+ default: 1
38
+ },
39
+ textClass: {
40
+ type: String,
41
+ default: ""
42
+ },
43
+ target: {
44
+ type: String,
45
+ default: "_self"
46
+ }
47
+ },
48
+
49
+ computed: {
50
+
51
+ iconAttr() {
52
+
53
+ return `icon: ${this.icon}; ratio: ${this.ratio};`;
54
+
55
+ },
56
+
57
+ iconStyle() {
58
+
59
+ return {
60
+ fontSize: (this.ratio * 16) + 'px'
61
+ }
62
+
63
+ }
64
+
65
+ }
66
+
67
+ }
68
+
69
+ </script>
@@ -0,0 +1,78 @@
1
+ <template>
2
+
3
+ <router-link :to="pathObject">
4
+
5
+ <span
6
+ class="uk-margin-small-right uk-icon"
7
+ :uk-icon="iconAttr"
8
+ :style="iconStyle"></span>
9
+
10
+ <span :class="textClass">{{ text }}</span>
11
+
12
+ </router-link>
13
+
14
+ </template>
15
+
16
+
17
+ <script>
18
+
19
+ export default {
20
+
21
+ props: {
22
+ name: {
23
+ type: String,
24
+ required: true
25
+ },
26
+ params: {
27
+ type: Object,
28
+ default: {}
29
+ },
30
+ text: {
31
+ type: String,
32
+ required: true
33
+ },
34
+ icon: {
35
+ type: String,
36
+ required: true
37
+ },
38
+ ratio: {
39
+ type: Number,
40
+ required: false,
41
+ default: 1
42
+ },
43
+ textClass: {
44
+ type: String,
45
+ default: ""
46
+ },
47
+ },
48
+
49
+ data() {
50
+ return {
51
+ pathObject: {
52
+ name: this.name,
53
+ params: this.params
54
+ }
55
+ }
56
+ },
57
+
58
+ computed: {
59
+
60
+ iconAttr() {
61
+
62
+ return `icon: ${this.icon}; ratio: ${this.ratio};`;
63
+
64
+ },
65
+
66
+ iconStyle() {
67
+
68
+ return {
69
+ fontSize: (this.ratio * 16) + 'px'
70
+ }
71
+
72
+ }
73
+
74
+ }
75
+
76
+ }
77
+
78
+ </script>
@@ -0,0 +1,60 @@
1
+ <template>
2
+
3
+ <div :uk-dropdown="options">
4
+
5
+ <ul class="uk-nav uk-dropdown-nav">
6
+
7
+ <slot></slot>
8
+
9
+ </ul>
10
+
11
+ </div>
12
+
13
+ </template>
14
+
15
+ <script>
16
+
17
+ export default {
18
+
19
+ props: {
20
+ pos: {
21
+ type: String,
22
+ default: "bottom-left"
23
+ },
24
+ mode: {
25
+ type: String,
26
+ default: "click"
27
+ },
28
+ offset: {
29
+ type: Number,
30
+ default: 0
31
+ },
32
+ animation: {
33
+ type: String,
34
+ default: "uk-animation-slide-top-small"
35
+ },
36
+ duration: {
37
+ type: Number,
38
+ default: 500
39
+ }
40
+ },
41
+
42
+ computed: {
43
+
44
+ options () {
45
+
46
+ return `
47
+ pos: ${this.pos};
48
+ mode: ${this.mode};
49
+ offset: ${this.offset};
50
+ animation: ${this.animation};
51
+ duration: ${this.duration};
52
+ `;
53
+
54
+ }
55
+
56
+ }
57
+
58
+ }
59
+
60
+ </script>
@@ -0,0 +1,110 @@
1
+ <template>
2
+
3
+ <ul v-if="length > 3" class="uk-pagination uk-flex-center" uk-margin>
4
+
5
+ <li v-if="firstLink != null">
6
+
7
+ <a href="#" @click="$emit('go', firstLink)">
8
+
9
+ <span uk-pagination-previous></span>
10
+
11
+ </a>
12
+
13
+ </li>
14
+
15
+ <li
16
+ v-for="link in middleLinks"
17
+ :key="link.label"
18
+ :class="{
19
+ 'uk-disabled': link.url == null,
20
+ 'uk-active': link.active,
21
+ }">
22
+
23
+ <a href="#" @click="$emit('go', link.url)">
24
+
25
+ {{ link.label }}
26
+
27
+ </a>
28
+
29
+ </li>
30
+
31
+ <li v-if="lastLink != null">
32
+
33
+ <a href="#" @click="$emit('go', lastLink)">
34
+
35
+ <span uk-pagination-next></span>
36
+
37
+ </a>
38
+
39
+ </li>
40
+
41
+ </ul>
42
+
43
+ </template>
44
+
45
+ <script>
46
+
47
+ export default {
48
+
49
+ props: {
50
+ links: {
51
+ type: Array,
52
+ required: true,
53
+ }
54
+ },
55
+
56
+ emits: ['go'],
57
+
58
+ computed: {
59
+
60
+ length() {
61
+
62
+ return this.links.length;
63
+
64
+ },
65
+
66
+ lastLinkIndex() {
67
+
68
+ return this.length - 1;
69
+
70
+ },
71
+
72
+ firstLink() {
73
+
74
+ return (this.length > 0) ? this.links[0].url : null;
75
+
76
+ },
77
+
78
+ lastLink() {
79
+
80
+ return (this.length > 0 && this.links[this.lastLinkIndex] != undefined) ?
81
+ this.links[this.lastLinkIndex].url :
82
+ null;
83
+
84
+ },
85
+
86
+ middleLinks() {
87
+
88
+ let auxLinks = this.links;
89
+
90
+ let middleLinks = auxLinks.slice(1, -1);
91
+
92
+ return middleLinks;
93
+
94
+ },
95
+
96
+ }
97
+
98
+ }
99
+
100
+ </script>
101
+
102
+ <style scoped>
103
+
104
+ .uk-pagination > .uk-active > * {
105
+ color: #666;
106
+ font-weight: 600;
107
+ border-bottom: 3px solid var(--primary-color);
108
+ }
109
+
110
+ </style>
@@ -0,0 +1,133 @@
1
+ <template>
2
+
3
+ <div class="pagination" uk-grid>
4
+
5
+ <div class="uk-width-auto">
6
+
7
+ <ul class="uk-pagination uk-flex-left uk-margin-medium-top" uk-margin>
8
+
9
+ <li>
10
+
11
+ <span v-if="meta.total > 0">{{ 'Showing' }} {{ meta.from }} {{ 'to' }} {{ meta.to }} {{ 'of' }} {{ meta.total }} {{ 'entries' }}</span>
12
+
13
+ <span v-else>{{ ('No results found') }}</span>
14
+
15
+ </li>
16
+
17
+ </ul>
18
+
19
+ </div>
20
+
21
+ <div class="uk-width-expand">
22
+
23
+ <ul class="uk-pagination uk-flex-right uk-margin-medium-top" uk-margin>
24
+
25
+ <!-- Prev -->
26
+ <li v-if="current_page > 1">
27
+
28
+ <a href="#" @click="prevPage()">
29
+
30
+ <span uk-pagination-previous></span>
31
+
32
+ </a>
33
+
34
+ </li>
35
+
36
+ <li>
37
+ <select
38
+ class="uk-select"
39
+ @change="updatePage()"
40
+ v-model="current_page">
41
+
42
+ <option
43
+ v-for="page in meta.last_page"
44
+ :key="'page_' + page"
45
+ :value="page">{{ page }}</option>
46
+
47
+ </select>
48
+ </li>
49
+
50
+ <!-- Next -->
51
+ <li v-if="current_page < meta.last_page">
52
+
53
+ <a href="#" @click="nextPage()">
54
+
55
+ <span uk-pagination-next></span>
56
+
57
+ </a>
58
+
59
+ </li>
60
+
61
+ </ul>
62
+
63
+ </div>
64
+
65
+ </div>
66
+
67
+ </template>
68
+
69
+ <script>
70
+
71
+ export default {
72
+
73
+ props: {
74
+
75
+ meta: {
76
+ type: Object,
77
+ required: true
78
+ },
79
+
80
+ links: {
81
+ type: Object,
82
+ required: true
83
+ }
84
+
85
+ },
86
+
87
+ emits: ['updatePage'],
88
+
89
+ updated() {
90
+
91
+ this.current_page = this.meta.current_page;
92
+
93
+ },
94
+
95
+ data() {
96
+
97
+ return {
98
+
99
+ current_page: this.meta.current_page,
100
+
101
+ }
102
+
103
+ },
104
+
105
+ methods: {
106
+
107
+ prevPage(){
108
+
109
+ this.current_page--;
110
+
111
+ this.updatePage();
112
+
113
+ },
114
+
115
+ nextPage(){
116
+
117
+ this.current_page++;
118
+
119
+ this.updatePage();
120
+
121
+ },
122
+
123
+ updatePage() {
124
+
125
+ this.$emit('updatePage', this.current_page);
126
+
127
+ }
128
+
129
+ }
130
+
131
+ }
132
+
133
+ </script>