innoboxrr-vue-datatable 1.3.3 → 2.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.
@@ -1,171 +1,183 @@
1
- <template>
2
- <div class="sm:rounded-lg overflow-x-auto">
3
- <table class="min-w-full w-full text-sm text-left text-slate-500 dark:text-slate-400 p-4 ">
4
- <thead
5
- v-if="showTableHeader"
6
- class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400 rounded-sm">
7
- <tr>
8
- <th
9
- v-for="head in dataTable.head"
10
- :key="head.id"
11
- :id="`th_${head.id}`"
12
- class="px-6 py-3"
13
- scope="col"
14
- :class="{pointer: head.sortable}"
15
- @click="sortColumn(head)">
16
- {{ head.value }}
17
- </th>
18
- <th
19
- class="uk-table-shrink"
20
- v-if="actions"></th>
21
- </tr>
22
- </thead>
23
- <tbody>
24
- <tr
25
- v-for="body in dataTable.body"
26
- :key="body.id"
27
- class="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600">
28
- <td
29
- v-for="head in dataTable.head"
30
- :key="head.id"
31
- class="px-6 py-4">
32
- <template v-if="head.component">
33
- <component
34
- :is="getComponent(head.component)"
35
- v-bind="setData(head, body)"
36
- @callback="head.callback && typeof head.callback === 'function' ? head.callback($event, body) : null" />
37
- </template>
38
- <span
39
- v-else-if="head.html"
40
- class="dark:text-white"
41
- v-html="setData(head, body)"></span>
42
- <span
43
- v-else
44
- class="dark:text-white">{{ setData(head, body) }}</span>
45
- </td>
46
- <td v-if="actions" class="uk-text-right">
47
- <button
48
- class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-2 py-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800"
49
- @click="actionButtonClicked(body.actions)">
50
- <i class="fas fa-cogs"></i>
51
- </button>
52
- <nav-dropdown-component :id="`dropdown_${body.id}`" pos="left">
53
- <li
54
- v-for="action in body.actions"
55
- :key="action.name"
56
- class="hover:bg-slate-100 dark:hover:bg-slate-600 px-2 py-1">
57
- <template v-if="action.route && !action?.link">
58
- <icon-route-component
59
- v-if="action.policy"
60
- :name="action.params.to.name"
61
- :params="{...action.params.to.params, ...extraParams}"
62
- :query="action.params.to.query ? {...action.params.to.query} : null"
63
- :icon="action.icon"
64
- :text="action.name" />
65
- <disabled-link-component
66
- v-else
67
- :icon="action.icon"
68
- :text="action.name"/>
69
- </template>
70
- <template v-else-if="action.route && action.link">
71
- <icon-link-component
72
- v-if="action.policy"
73
- :link="action.params.link"
74
- :target="action.params.target"
75
- :icon="action.icon"
76
- :text="action.name" />
77
- </template>
78
- <template v-else>
79
- <icon-link-component
80
- v-if="action.policy"
81
- :icon="action.icon"
82
- :text="action.name"
83
- @click.prevent="actionClicked(action), closeDropdown($event)" />
84
- <disabled-link-component
85
- v-else
86
- :icon="action.icon"
87
- :text="action.name"/>
88
- </template>
89
- </li>
90
- </nav-dropdown-component>
91
- </td>
92
- </tr>
93
- </tbody>
94
- </table>
95
- </div>
96
- </template>
97
-
98
- <script>
99
-
100
- import NavDropdownComponent from './NavDropdownComponent.vue'
101
- import IconRouteComponent from './IconRouteComponent.vue'
102
- import IconLinkComponent from './IconLinkComponent.vue'
103
- import DisabledLinkComponent from './DisabledLinkComponent.vue'
104
-
105
- export default {
106
- components: {
107
- NavDropdownComponent,
108
- IconRouteComponent,
109
- IconLinkComponent,
110
- DisabledLinkComponent
111
- },
112
- props: {
113
- actions: {
114
- type: Boolean,
115
- default: false
116
- },
117
- dataTable: {
118
- type: [Object, Boolean],
119
- required: true
120
- },
121
- extraParams: {
122
- type: Object,
123
- default: {}
124
- },
125
- showTableHeader: {
126
- type: Boolean,
127
- default: true,
128
- },
129
- dataTableComponents: {
130
- type: Object,
131
- default: () => ({})
132
- }
133
- },
134
- emits: ['sortColumn', 'actionButtonClicked', 'actionClicked'],
135
- setup(props) {
136
- const getComponent = (componentName) => {
137
- return props.dataTableComponents[componentName] || null;
138
- };
139
-
140
- return {
141
- getComponent,
142
- };
143
- },
144
- methods: {
145
- sortColumn(data) {
146
- this.$emit('sortColumn', data);
147
- },
148
- actionButtonClicked(data) {
149
- this.$emit('actionButtonClicked', data);
150
- },
151
- actionClicked(data) {
152
- this.$emit('actionClicked', data);
153
- },
154
- setData(head, body) {
155
- const data = JSON.parse(JSON.stringify(body));
156
- return typeof head.parser === 'function' ? head.parser(data[head.id], data) : data[head.id];
157
- },
158
- closeDropdown(event){
159
- let dropdown = event.target.closest('.uk-dropdown');
160
- UIkit.dropdown(dropdown).hide(false);
161
- }
162
- }
163
- }
164
- </script>
165
-
166
- <style>
167
- .uk-table td {
168
- padding: 12px 14px;
169
- vertical-align: middle;
170
- }
171
- </style>
1
+ <template>
2
+ <div class="sm:rounded-lg overflow-x-auto">
3
+ <table class="min-w-full w-full text-sm text-left text-slate-500 dark:text-slate-400 p-4">
4
+ <thead
5
+ v-if="showTableHeader"
6
+ class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400 rounded-sm">
7
+ <tr>
8
+ <th
9
+ v-for="head in dataTable.head"
10
+ :key="head.id"
11
+ :id="`th_${head.id}`"
12
+ class="px-6 py-3"
13
+ scope="col"
14
+ :class="{pointer: head.sortable}"
15
+ @click="emit('sortColumn', head)">
16
+ {{ head.value }}
17
+ </th>
18
+ <th
19
+ class="fe-shrink"
20
+ v-if="actions"></th>
21
+ </tr>
22
+ </thead>
23
+ <tbody>
24
+ <tr
25
+ v-for="(body, rowIndex) in dataTable.body"
26
+ :key="body.id"
27
+ class="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600">
28
+ <td
29
+ v-for="head in dataTable.head"
30
+ :key="head.id"
31
+ class="px-6 py-4">
32
+ <template v-if="head.component">
33
+ <component
34
+ :is="getComponent(head.component)"
35
+ v-bind="setData(head, body, rowIndex)"
36
+ @callback="head.callback && typeof head.callback === 'function' ? head.callback($event, body) : null" />
37
+ </template>
38
+ <span
39
+ v-else-if="head.html"
40
+ class="dark:text-white"
41
+ v-html="setData(head, body, rowIndex)"></span>
42
+ <span
43
+ v-else
44
+ class="dark:text-white">{{ setData(head, body, rowIndex) }}</span>
45
+ </td>
46
+ <td v-if="actions" class="fe-text-right">
47
+ <button
48
+ class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-2 py-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800"
49
+ @click="emit('actionButtonClicked', body.actions)">
50
+ <i class="fas fa-cogs"></i>
51
+ </button>
52
+ <NavDropdownComponent :id="`dropdown_${body.id}`" pos="left">
53
+ <li
54
+ v-for="action in body.actions"
55
+ :key="action.name"
56
+ class="hover:bg-slate-100 dark:hover:bg-slate-600 px-2 py-1">
57
+ <template v-if="action.route && !action?.link">
58
+ <IconRouteComponent
59
+ v-if="action.policy"
60
+ :name="action.params.to.name"
61
+ :params="{...action.params.to.params, ...extraParams}"
62
+ :query="action.params.to.query ? {...action.params.to.query, ...extraQuery} : {...extraQuery}"
63
+ :icon="action.icon"
64
+ :text="action.name" />
65
+ <DisabledLinkComponent
66
+ v-else
67
+ :icon="action.icon"
68
+ :text="action.name"/>
69
+ </template>
70
+ <template v-else-if="action.route && action.link">
71
+ <IconLinkComponent
72
+ v-if="action.policy"
73
+ :link="action.params.link"
74
+ :target="action.params.target"
75
+ :icon="action.icon"
76
+ :text="action.name" />
77
+ </template>
78
+ <template v-else>
79
+ <IconLinkComponent
80
+ v-if="action.policy"
81
+ :icon="action.icon"
82
+ :text="action.name"
83
+ @click.prevent="emit('actionClicked', action), closeDropdown($event)" />
84
+ <DisabledLinkComponent
85
+ v-else
86
+ :icon="action.icon"
87
+ :text="action.name"/>
88
+ </template>
89
+ </li>
90
+ </NavDropdownComponent>
91
+ </td>
92
+ </tr>
93
+ </tbody>
94
+ </table>
95
+ </div>
96
+ </template>
97
+
98
+ <script setup>
99
+
100
+ import { computed } from 'vue'
101
+
102
+ import NavDropdownComponent from './NavDropdownComponent.vue'
103
+ import IconRouteComponent from './IconRouteComponent.vue'
104
+ import IconLinkComponent from './IconLinkComponent.vue'
105
+ import DisabledLinkComponent from './DisabledLinkComponent.vue'
106
+
107
+ const props = defineProps({
108
+ actions: {
109
+ type: Boolean,
110
+ default: false
111
+ },
112
+ dataTable: {
113
+ type: [Object, Boolean],
114
+ required: true
115
+ },
116
+ // Vue 3 exige factoria en los defaults de objeto.
117
+ extraParams: {
118
+ type: Object,
119
+ default: () => ({})
120
+ },
121
+ extraQuery: {
122
+ type: Object,
123
+ default: () => ({})
124
+ },
125
+ showTableHeader: {
126
+ type: Boolean,
127
+ default: true,
128
+ },
129
+ dataTableComponents: {
130
+ type: Object,
131
+ default: () => ({})
132
+ }
133
+ })
134
+
135
+ const emit = defineEmits(['sortColumn', 'actionButtonClicked', 'actionClicked'])
136
+
137
+ const getComponent = (componentName) => props.dataTableComponents[componentName] || null
138
+
139
+ /**
140
+ * Copia aislada de cada fila, para que un parser del modelo no pueda
141
+ * mutar los datos de la tabla.
142
+ *
143
+ * Se clonaba con JSON dentro de setData(), es decir una vez por celda: con
144
+ * 20 filas y 8 columnas eran 160 clonados en cada repintado. Un clon por
145
+ * fila y repintado deja lo mismo en 20.
146
+ *
147
+ * La cache que hacia eso era un WeakMap de modulo, y eso traia dos
148
+ * problemas propios: la compartian todas las tablas de la pagina, y un
149
+ * parser que escribiera en su copia la envenenaba para el resto de la vida
150
+ * de la aplicacion. Una computed sobre el cuerpo da el mismo ahorro sin
151
+ * ninguna de las dos cosas.
152
+ */
153
+ const rows = computed(() => (props.dataTable.body ?? []).map(
154
+ (row) => JSON.parse(JSON.stringify(row))
155
+ ))
156
+
157
+ const setData = (head, body, index) => {
158
+
159
+ const data = rows.value[index] ?? body
160
+
161
+ return typeof head.parser === 'function' ? head.parser(data[head.id], data) : data[head.id]
162
+
163
+ }
164
+
165
+ const closeDropdown = (event) => {
166
+
167
+ const dropdown = event.target.closest('.uk-dropdown')
168
+
169
+ // UIkit lo aporta la aplicacion anfitriona.
170
+ if (dropdown) {
171
+ globalThis.UIkit?.dropdown(dropdown)?.hide(false)
172
+ }
173
+
174
+ }
175
+
176
+ </script>
177
+
178
+ <style>
179
+ .fe-table td {
180
+ padding: 12px 14px;
181
+ vertical-align: middle;
182
+ }
183
+ </style>
@@ -1,49 +1,39 @@
1
- <template>
2
-
3
- <a
4
- href="#"
5
- class="disabled-link block px-4 py-2 dark:hover:text-white dark:text-slate-400"
6
- :uk-tooltip="`title: ${ 'This action is not authorized' }; pos:right`">
7
-
8
- <span
9
- v-if="showIcon"
10
- class="uk-margin-small-right uk-icon"
11
- :uk-icon="icon"></span>
12
-
13
- <span>{{ text }}</span>
14
-
15
- </a>
16
-
17
- </template>
18
-
19
- <script>
20
-
21
- export default {
22
-
23
- props: {
24
-
25
- icon: {
26
- type: String,
27
- default: ''
28
- },
29
-
30
- text: {
31
- type: String,
32
- required: true
33
- }
34
-
35
- },
36
-
37
- computed: {
38
-
39
- showIcon() {
40
-
41
- return (this.icon != '' && this.icon != undefined);
42
-
43
- }
44
-
45
- }
46
-
47
- }
48
-
49
- </script>
1
+ <template>
2
+
3
+ <a
4
+ href="#"
5
+ class="disabled-link block px-4 py-2 dark:hover:text-white dark:text-slate-400"
6
+ :uk-tooltip="`title: ${ 'This action is not authorized' }; pos:right`">
7
+
8
+ <span
9
+ v-if="showIcon"
10
+ class="fe-mr-sm uk-icon"
11
+ :uk-icon="icon"></span>
12
+
13
+ <span>{{ text }}</span>
14
+
15
+ </a>
16
+
17
+ </template>
18
+
19
+ <script setup>
20
+
21
+ import { computed } from 'vue'
22
+
23
+ const props = defineProps({
24
+
25
+ icon: {
26
+ type: String,
27
+ default: ''
28
+ },
29
+
30
+ text: {
31
+ type: String,
32
+ required: true
33
+ }
34
+
35
+ })
36
+
37
+ const showIcon = computed(() => props.icon !== '' && props.icon != null)
38
+
39
+ </script>
@@ -1,79 +1,65 @@
1
- <template>
2
-
3
- <a
4
- class="block px-4 py-2 dark:hover:text-white dark:text-slate-400"
5
- :href="link"
6
- :target="target">
7
-
8
- <span
9
- class="uk-margin-small-right uk-icon"
10
- :uk-icon="iconAttr"
11
- :style="iconStyle"></span>
12
-
13
- <span :class="textClass">{{ text }}</span>
14
-
15
- </a>
16
-
17
- </template>
18
-
19
-
20
- <script>
21
-
22
- export default {
23
-
24
- props: {
25
- link: {
26
- type: String,
27
- default: '#'
28
- },
29
- text: {
30
- type: String,
31
- required: true
32
- },
33
- icon: {
34
- type: String,
35
- required: true
36
- },
37
- ratio: {
38
- type: Number,
39
- required: false,
40
- default: 1
41
- },
42
- textClass: {
43
- type: String,
44
- default: ""
45
- },
46
- target: {
47
- type: String,
48
- default: "_self"
49
- }
50
- },
51
-
52
- mounted() {
53
- this.$nextTick(() => {
54
- UIkit.update();
55
- });
56
- },
57
-
58
-
59
- computed: {
60
-
61
- iconAttr() {
62
-
63
- return `icon: ${this.icon}; ratio: ${this.ratio};`;
64
-
65
- },
66
-
67
- iconStyle() {
68
-
69
- return {
70
- fontSize: (this.ratio * 16) + 'px'
71
- }
72
-
73
- }
74
-
75
- }
76
-
77
- }
78
-
79
- </script>
1
+ <template>
2
+
3
+ <a
4
+ class="block px-4 py-2 dark:hover:text-white dark:text-slate-400"
5
+ :href="link"
6
+ :target="target">
7
+
8
+ <span
9
+ class="fe-mr-sm uk-icon"
10
+ :uk-icon="iconAttr"
11
+ :style="iconStyle"></span>
12
+
13
+ <span :class="textClass">{{ text }}</span>
14
+
15
+ </a>
16
+
17
+ </template>
18
+
19
+ <script setup>
20
+
21
+ import { computed, nextTick, onMounted } from 'vue'
22
+
23
+ const props = defineProps({
24
+ link: {
25
+ type: String,
26
+ default: '#'
27
+ },
28
+ text: {
29
+ type: String,
30
+ required: true
31
+ },
32
+ icon: {
33
+ type: String,
34
+ required: true
35
+ },
36
+ ratio: {
37
+ type: Number,
38
+ required: false,
39
+ default: 1
40
+ },
41
+ textClass: {
42
+ type: String,
43
+ default: ""
44
+ },
45
+ target: {
46
+ type: String,
47
+ default: "_self"
48
+ }
49
+ })
50
+
51
+ onMounted(() => {
52
+ nextTick(() => {
53
+ // UIkit lo aporta la aplicacion anfitriona; sin la guarda el
54
+ // componente no se puede montar en un entorno de pruebas ni en SSR.
55
+ globalThis.UIkit?.update()
56
+ })
57
+ })
58
+
59
+ const iconAttr = computed(() => `icon: ${props.icon}; ratio: ${props.ratio};`)
60
+
61
+ const iconStyle = computed(() => ({
62
+ fontSize: (props.ratio * 16) + 'px'
63
+ }))
64
+
65
+ </script>