@xy-planning-network/trees 0.6.5 → 0.6.6-rc-1
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/README.md +0 -22
- package/dist/trees.es.js +2539 -2498
- package/dist/trees.umd.js +9 -9
- package/package.json +1 -2
- package/src/lib-components/lists/DataTable.vue +99 -0
- package/src/lib-components/lists/DownloadCell.vue +5 -3
- package/src/lib-components/lists/{Table.vue → DynamicTable.vue} +87 -61
- package/src/lib-components/navigation/ActionsDropdown.vue +22 -32
- package/types/composables/nav.d.ts +12 -0
- package/types/composables/table.d.ts +52 -22
- package/types/composables/useFlashes.d.ts +5 -0
- package/types/composables/useTable.d.ts +42 -0
- package/types/lib-components/index.d.ts +5 -5
- package/types/lib-components/lists/DataTable.vue.d.ts +33 -0
- package/types/lib-components/lists/DownloadCell.vue.d.ts +0 -3
- package/types/lib-components/lists/DynamicTable.vue.d.ts +54 -0
- package/types/lib-components/lists/StaticTable.vue.d.ts +7 -4
- package/types/lib-components/lists/StaticTableActionSlot.vue.d.ts +27 -0
- package/types/lib-components/navigation/ActionsDropdown.vue.d.ts +3 -11
- package/types/lib-components/navigation/ActionsDropdownCallback.vue.d.ts +18 -0
- package/types/lib-components/navigation/ActionsDropdownEmit.vue.d.ts +22 -0
- package/src/lib-components/lists/StaticTable.vue +0 -83
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xy-planning-network/trees",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.6-rc-1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "github:xy-planning-network/trees",
|
|
@@ -48,7 +48,6 @@
|
|
|
48
48
|
"deepmerge": "^4.3.1",
|
|
49
49
|
"eslint": "^8.38.0",
|
|
50
50
|
"eslint-plugin-vue": "^9.10.0",
|
|
51
|
-
"mitt": "^2.1.0",
|
|
52
51
|
"postcss": "^8.4.21",
|
|
53
52
|
"prettier": "^2.8.7",
|
|
54
53
|
"tailwindcss": "^3.2.1",
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useTable } from "@/composables/useTable"
|
|
3
|
+
import { TableActions, TableColumns, TableRowsData } from "@/composables/table"
|
|
4
|
+
|
|
5
|
+
const props = withDefaults(
|
|
6
|
+
defineProps<{
|
|
7
|
+
tableActions?: TableActions<any>
|
|
8
|
+
tableColumns: TableColumns<any>
|
|
9
|
+
tableData: TableRowsData
|
|
10
|
+
}>(),
|
|
11
|
+
{
|
|
12
|
+
tableActions: () => [],
|
|
13
|
+
}
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
const { columns, hasActions, isEmptyCellValue, rows } = useTable(
|
|
17
|
+
props.tableData,
|
|
18
|
+
props.tableColumns,
|
|
19
|
+
props.tableActions
|
|
20
|
+
)
|
|
21
|
+
</script>
|
|
22
|
+
<template>
|
|
23
|
+
<div class="flex flex-col">
|
|
24
|
+
<div class="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
|
25
|
+
<div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
|
|
26
|
+
<div
|
|
27
|
+
class="overflow-hidden border-b border-gray-200 shadow sm:rounded-lg"
|
|
28
|
+
>
|
|
29
|
+
<table class="min-w-full divide-y divide-gray-200">
|
|
30
|
+
<thead>
|
|
31
|
+
<tr>
|
|
32
|
+
<th
|
|
33
|
+
v-for="(col, idx) in columns"
|
|
34
|
+
:key="idx"
|
|
35
|
+
class="px-6 py-3 text-xs font-medium tracking-wider text-gray-900 uppercase bg-gray-50 leading-4"
|
|
36
|
+
:class="{
|
|
37
|
+
'text-left': col.alignment === 'left',
|
|
38
|
+
'text-right': col.alignment === 'right',
|
|
39
|
+
'text-center': col.alignment === 'center',
|
|
40
|
+
}"
|
|
41
|
+
>
|
|
42
|
+
{{ col.title }}
|
|
43
|
+
</th>
|
|
44
|
+
|
|
45
|
+
<!--Table Actions Header-->
|
|
46
|
+
<th
|
|
47
|
+
v-if="hasActions"
|
|
48
|
+
class="px-6 py-3 text-xs font-medium tracking-wider text-gray-900 uppercase bg-gray-50 leading-4"
|
|
49
|
+
>
|
|
50
|
+
Actions
|
|
51
|
+
</th>
|
|
52
|
+
</tr>
|
|
53
|
+
</thead>
|
|
54
|
+
<tbody class="bg-white divide-y divide-gray-200">
|
|
55
|
+
<tr v-for="(row, rowIdx) in rows" :key="rowIdx">
|
|
56
|
+
<template v-for="(cell, cellIdx) in row.cells" :key="cellIdx">
|
|
57
|
+
<component
|
|
58
|
+
:is="'td'"
|
|
59
|
+
class="px-6 py-4 text-sm text-gray-700 whitespace-nowrap leading-5"
|
|
60
|
+
:class="{
|
|
61
|
+
'text-left': cell.alignment === 'left',
|
|
62
|
+
'text-right': cell.alignment === 'right',
|
|
63
|
+
'text-center': cell.alignment === 'center',
|
|
64
|
+
}"
|
|
65
|
+
>
|
|
66
|
+
<template v-if="cell.isComponent">
|
|
67
|
+
<component :is="cell.val" />
|
|
68
|
+
</template>
|
|
69
|
+
|
|
70
|
+
<span v-else :class="cell.classNames">
|
|
71
|
+
{{ isEmptyCellValue(cell.val) ? "-" : String(cell.val) }}
|
|
72
|
+
</span>
|
|
73
|
+
</component>
|
|
74
|
+
</template>
|
|
75
|
+
|
|
76
|
+
<!--Table Actions Cell-->
|
|
77
|
+
<td
|
|
78
|
+
v-if="hasActions"
|
|
79
|
+
class="px-6 py-4 text-sm text-gray-700 whitespace-nowrap leading-5"
|
|
80
|
+
>
|
|
81
|
+
<ActionsDropdown :items="row.actions" />
|
|
82
|
+
</td>
|
|
83
|
+
</tr>
|
|
84
|
+
|
|
85
|
+
<tr v-if="rows.length === 0">
|
|
86
|
+
<td
|
|
87
|
+
:colspan="columns.length"
|
|
88
|
+
class="px-6 py-4 text-sm text-gray-700 whitespace-nowrap leading-5"
|
|
89
|
+
>
|
|
90
|
+
No items were found!
|
|
91
|
+
</td>
|
|
92
|
+
</tr>
|
|
93
|
+
</tbody>
|
|
94
|
+
</table>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</template>
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
import { DownloadIcon } from "@heroicons/vue/solid"
|
|
3
3
|
defineProps<{
|
|
4
4
|
propsData: Record<string, unknown>
|
|
5
|
-
attribute: string
|
|
6
5
|
}>()
|
|
7
6
|
</script>
|
|
8
7
|
<template>
|
|
9
|
-
<a
|
|
10
|
-
<DownloadIcon
|
|
8
|
+
<a href="#">
|
|
9
|
+
<DownloadIcon
|
|
10
|
+
class="h-6 w-6 group-hover:text-gray-500 transition text-right"
|
|
11
|
+
text-anchor="end"
|
|
12
|
+
/>
|
|
11
13
|
</a>
|
|
12
14
|
</template>
|
|
@@ -1,47 +1,62 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
ComponentPublicInstance,
|
|
4
|
-
computed,
|
|
5
|
-
getCurrentInstance,
|
|
6
|
-
ref,
|
|
7
|
-
watch,
|
|
8
|
-
} from "vue"
|
|
2
|
+
import { computed, ref, watch } from "vue"
|
|
9
3
|
import DateRangePicker from "../forms/DateRangePicker.vue"
|
|
10
4
|
import Paginator from "../navigation/Paginator.vue"
|
|
11
5
|
import BaseAPI from "../../api/base"
|
|
12
|
-
import
|
|
6
|
+
import {
|
|
7
|
+
DynamicTableAPI,
|
|
8
|
+
DynamicTableOptions,
|
|
9
|
+
TableActions,
|
|
10
|
+
TableColumns,
|
|
11
|
+
} from "@/composables/table"
|
|
13
12
|
import { useAppFlasher } from "@/composables/useFlashes"
|
|
14
13
|
import { TrailsRespPaged } from "@/api/client"
|
|
14
|
+
import { useTable } from "@/composables/useTable"
|
|
15
15
|
|
|
16
16
|
const props = withDefaults(
|
|
17
17
|
defineProps<{
|
|
18
18
|
clickable?: boolean
|
|
19
19
|
loader?: boolean
|
|
20
|
-
|
|
20
|
+
tableActions?: TableActions<any>
|
|
21
|
+
tableColumns: TableColumns<any>
|
|
22
|
+
tableOptions: DynamicTableOptions
|
|
21
23
|
}>(),
|
|
22
24
|
{
|
|
23
25
|
clickable: false,
|
|
24
26
|
loader: true,
|
|
27
|
+
tableActions: () => [],
|
|
25
28
|
}
|
|
26
29
|
)
|
|
27
30
|
|
|
28
31
|
defineEmits<{
|
|
29
|
-
(
|
|
32
|
+
(
|
|
33
|
+
e: "click:row",
|
|
34
|
+
v: (typeof tableData.value)[number],
|
|
35
|
+
c: typeof publicMethods
|
|
36
|
+
): void
|
|
30
37
|
}>()
|
|
31
38
|
|
|
39
|
+
const tableData = ref<Record<string, any>[]>([])
|
|
40
|
+
|
|
41
|
+
const { columns, hasActions, isEmptyCellValue, rows } = useTable(
|
|
42
|
+
tableData,
|
|
43
|
+
props.tableColumns,
|
|
44
|
+
props.tableActions
|
|
45
|
+
)
|
|
46
|
+
|
|
32
47
|
const currentSort = ref(
|
|
33
|
-
props.
|
|
48
|
+
props.tableOptions.defaultSort ? props.tableOptions.defaultSort : ""
|
|
34
49
|
)
|
|
35
50
|
const currentSortDirection = ref(
|
|
36
|
-
props.
|
|
37
|
-
? props.
|
|
51
|
+
props.tableOptions.defaultSortDirection
|
|
52
|
+
? props.tableOptions.defaultSortDirection
|
|
38
53
|
: "desc"
|
|
39
54
|
)
|
|
40
55
|
const dateRange = ref({
|
|
41
56
|
minDate: 0,
|
|
42
57
|
maxDate: 0,
|
|
43
58
|
})
|
|
44
|
-
|
|
59
|
+
|
|
45
60
|
const pagination = ref({
|
|
46
61
|
page: 1,
|
|
47
62
|
perPage: 10,
|
|
@@ -49,27 +64,7 @@ const pagination = ref({
|
|
|
49
64
|
totalPages: 0,
|
|
50
65
|
})
|
|
51
66
|
const query = ref("")
|
|
52
|
-
const cellValue = (
|
|
53
|
-
item: Record<string, any>,
|
|
54
|
-
col: TableTypes.Column
|
|
55
|
-
): string => {
|
|
56
|
-
if (col.key) {
|
|
57
|
-
// NOTE(dlk): supports dot notation for nested keys
|
|
58
|
-
return col.key.split(".").reduce((o, i) => o[i], item as any)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (col.presenter) {
|
|
62
|
-
// TODO: discuss this pattern. Current usage can be replaced with modules.
|
|
63
|
-
// https://v3.vuejs.org/api/composition-api.html#getcurrentinstance
|
|
64
|
-
const internalInstance = getCurrentInstance()
|
|
65
|
-
return col.presenter(
|
|
66
|
-
item,
|
|
67
|
-
internalInstance?.proxy as ComponentPublicInstance
|
|
68
|
-
)
|
|
69
|
-
}
|
|
70
67
|
|
|
71
|
-
return ""
|
|
72
|
-
}
|
|
73
68
|
const dateRangeChanged = (newDateRange: {
|
|
74
69
|
minDate: number
|
|
75
70
|
maxDate: number
|
|
@@ -101,7 +96,7 @@ const loadAndRender = (): void => {
|
|
|
101
96
|
}
|
|
102
97
|
|
|
103
98
|
BaseAPI.get<TrailsRespPaged<unknown>>(
|
|
104
|
-
props.
|
|
99
|
+
props.tableOptions.url,
|
|
105
100
|
{ skipLoader: !props.loader },
|
|
106
101
|
params
|
|
107
102
|
).then(
|
|
@@ -112,7 +107,7 @@ const loadAndRender = (): void => {
|
|
|
112
107
|
totalItems: success.data.totalItems,
|
|
113
108
|
totalPages: success.data.totalPages,
|
|
114
109
|
}
|
|
115
|
-
|
|
110
|
+
tableData.value = success.data.items as Record<string, any>[]
|
|
116
111
|
},
|
|
117
112
|
() => {
|
|
118
113
|
useAppFlasher.genericError()
|
|
@@ -126,11 +121,11 @@ const reloadTable = (): void => {
|
|
|
126
121
|
}
|
|
127
122
|
|
|
128
123
|
const hasContent = computed((): boolean => {
|
|
129
|
-
return
|
|
124
|
+
return rows.value.length ? true : false
|
|
130
125
|
})
|
|
131
126
|
|
|
132
127
|
watch(
|
|
133
|
-
() => props.
|
|
128
|
+
() => props.tableOptions.refreshTrigger,
|
|
134
129
|
() => {
|
|
135
130
|
// This lets parent components trigger a refresh of the current page depending on external actions.
|
|
136
131
|
loadAndRender()
|
|
@@ -138,13 +133,20 @@ watch(
|
|
|
138
133
|
)
|
|
139
134
|
|
|
140
135
|
watch(
|
|
141
|
-
() => props.
|
|
136
|
+
() => props.tableOptions.reloadTrigger,
|
|
142
137
|
() => {
|
|
143
138
|
// This lets parent components trigger a reload of page 1 depending on external actions.
|
|
144
139
|
reloadTable()
|
|
145
140
|
}
|
|
146
141
|
)
|
|
147
142
|
|
|
143
|
+
const publicMethods: DynamicTableAPI = {
|
|
144
|
+
refresh: loadAndRender,
|
|
145
|
+
reset: reloadTable,
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
defineExpose(publicMethods)
|
|
149
|
+
|
|
148
150
|
// onCreated
|
|
149
151
|
loadAndRender()
|
|
150
152
|
</script>
|
|
@@ -153,7 +155,7 @@ loadAndRender()
|
|
|
153
155
|
<div
|
|
154
156
|
class="flex flex-col mb-4 space-y-4 lg:space-y-0 lg:flex-row lg:justify-between"
|
|
155
157
|
>
|
|
156
|
-
<div v-if="
|
|
158
|
+
<div v-if="tableOptions.search" class="w-full max-w-lg lg:max-w-xs">
|
|
157
159
|
<label for="search" class="sr-only">Search</label>
|
|
158
160
|
<div class="relative">
|
|
159
161
|
<div
|
|
@@ -180,7 +182,7 @@ loadAndRender()
|
|
|
180
182
|
/>
|
|
181
183
|
</div>
|
|
182
184
|
</div>
|
|
183
|
-
<div v-if="
|
|
185
|
+
<div v-if="tableOptions.dateSearch" class="w-full max-w-lg lg:max-w-xs">
|
|
184
186
|
<DateRangePicker
|
|
185
187
|
v-model="dateRange"
|
|
186
188
|
@update:model-value="dateRangeChanged"
|
|
@@ -195,11 +197,16 @@ loadAndRender()
|
|
|
195
197
|
<thead>
|
|
196
198
|
<tr>
|
|
197
199
|
<th
|
|
198
|
-
v-for="(col, idx) in
|
|
200
|
+
v-for="(col, idx) in columns"
|
|
199
201
|
:key="idx"
|
|
200
|
-
class="px-6 py-3 text-xs font-medium tracking-wider text-
|
|
202
|
+
class="px-6 py-3 text-xs font-medium tracking-wider text-gray-900 uppercase border-b border-gray-200 bg-gray-50 leading-4"
|
|
203
|
+
:class="{
|
|
204
|
+
'text-left': col.alignment === 'left',
|
|
205
|
+
'text-right': col.alignment === 'right',
|
|
206
|
+
'text-center': col.alignment === 'center',
|
|
207
|
+
}"
|
|
201
208
|
>
|
|
202
|
-
<span v-if="
|
|
209
|
+
<span v-if="col.title">{{ col.title }}</span>
|
|
203
210
|
<span
|
|
204
211
|
v-if="col.sort"
|
|
205
212
|
class="cursor-pointer"
|
|
@@ -248,37 +255,56 @@ loadAndRender()
|
|
|
248
255
|
</svg>
|
|
249
256
|
</span>
|
|
250
257
|
</th>
|
|
258
|
+
|
|
259
|
+
<!--Table Actions Header-->
|
|
260
|
+
<th
|
|
261
|
+
v-if="hasActions"
|
|
262
|
+
class="px-6 py-3 text-xs font-medium tracking-wider text-gray-900 uppercase bg-gray-50 leading-4"
|
|
263
|
+
>
|
|
264
|
+
Actions
|
|
265
|
+
</th>
|
|
251
266
|
</tr>
|
|
252
267
|
</thead>
|
|
253
268
|
|
|
254
269
|
<tbody class="bg-white">
|
|
255
270
|
<tr
|
|
256
|
-
v-for="(
|
|
257
|
-
:key="
|
|
271
|
+
v-for="(row, rowIdx) in rows"
|
|
272
|
+
:key="rowIdx"
|
|
258
273
|
:class="{ 'cursor-pointer': clickable }"
|
|
259
|
-
@click="$emit('
|
|
274
|
+
@click="$emit('click:row', row.rowData, publicMethods)"
|
|
260
275
|
>
|
|
276
|
+
<template v-for="(cell, cellIdx) in row.cells" :key="cellIdx">
|
|
277
|
+
<component
|
|
278
|
+
:is="'td'"
|
|
279
|
+
class="px-6 py-4 text-sm text-gray-700 whitespace-nowrap leading-5"
|
|
280
|
+
:class="{
|
|
281
|
+
'text-left': cell.alignment === 'left',
|
|
282
|
+
'text-right': cell.alignment === 'right',
|
|
283
|
+
'text-center': cell.alignment === 'center',
|
|
284
|
+
}"
|
|
285
|
+
>
|
|
286
|
+
<template v-if="cell.isComponent">
|
|
287
|
+
<component :is="cell.val" />
|
|
288
|
+
</template>
|
|
289
|
+
|
|
290
|
+
<span v-else :class="cell.classNames">
|
|
291
|
+
{{ isEmptyCellValue(cell.val) ? "-" : String(cell.val) }}
|
|
292
|
+
</span>
|
|
293
|
+
</component>
|
|
294
|
+
</template>
|
|
295
|
+
|
|
296
|
+
<!--Table Actions Cell-->
|
|
261
297
|
<td
|
|
262
|
-
v-
|
|
263
|
-
|
|
264
|
-
class="px-6 py-4 text-sm text-gray-700 whitespace-nowrap border-b border-gray-200 leading-5"
|
|
265
|
-
:class="col.class"
|
|
298
|
+
v-if="hasActions"
|
|
299
|
+
class="px-6 py-4 text-sm text-gray-700 whitespace-nowrap leading-5"
|
|
266
300
|
>
|
|
267
|
-
<
|
|
268
|
-
:is="col.component"
|
|
269
|
-
v-if="col.component"
|
|
270
|
-
:props-data="item"
|
|
271
|
-
:current-user="tableData.currentUser"
|
|
272
|
-
:attribute="col.key"
|
|
273
|
-
:items="col.items"
|
|
274
|
-
></component>
|
|
275
|
-
<div v-else v-text="cellValue(item, col)"></div>
|
|
301
|
+
<ActionsDropdown :items="row.actions" />
|
|
276
302
|
</td>
|
|
277
303
|
</tr>
|
|
278
304
|
|
|
279
305
|
<tr v-if="!hasContent">
|
|
280
306
|
<td
|
|
281
|
-
:colspan="
|
|
307
|
+
:colspan="rows.length"
|
|
282
308
|
class="px-6 py-4 text-sm text-gray-700 whitespace-nowrap border-b border-gray-200 leading-5"
|
|
283
309
|
>
|
|
284
310
|
No items were found!
|
|
@@ -1,47 +1,36 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/vue"
|
|
3
3
|
import { DotsVerticalIcon } from "@heroicons/vue/solid"
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import User from "@/composables/user"
|
|
4
|
+
import { computed, isRef } from "vue"
|
|
5
|
+
import type { ActionMenuItem } from "@/composables/nav"
|
|
7
6
|
|
|
8
7
|
const props = defineProps<{
|
|
9
|
-
|
|
10
|
-
items: TableTypes.MenuItem[]
|
|
11
|
-
propsData: any
|
|
8
|
+
items: ActionMenuItem[]
|
|
12
9
|
}>()
|
|
13
10
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const show = (item: TableTypes.MenuItem): boolean => {
|
|
21
|
-
if (!item.show) return true
|
|
22
|
-
return item.show(props.propsData, props.currentUser)
|
|
23
|
-
}
|
|
11
|
+
const menuItems = computed(() => {
|
|
12
|
+
return props.items.filter((item) => {
|
|
13
|
+
if (item.show === undefined) {
|
|
14
|
+
return true
|
|
15
|
+
}
|
|
24
16
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (!item.show) {
|
|
28
|
-
hasActionItems.value = true
|
|
29
|
-
return
|
|
17
|
+
if (isRef<boolean>(item.show)) {
|
|
18
|
+
return item.show.value
|
|
30
19
|
}
|
|
31
20
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
hasActionItems.value = true
|
|
35
|
-
return
|
|
21
|
+
if (typeof item.show === "boolean") {
|
|
22
|
+
return item.show
|
|
36
23
|
}
|
|
37
|
-
|
|
24
|
+
|
|
25
|
+
return item.show()
|
|
26
|
+
})
|
|
38
27
|
})
|
|
39
28
|
</script>
|
|
40
29
|
<template>
|
|
41
30
|
<Menu as="div" class="relative flex justify-end items-center">
|
|
42
31
|
<MenuButton
|
|
43
32
|
class="w-8 h-8 bg-white inline-flex items-center justify-center text-gray-700 rounded-full hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
44
|
-
:disabled="
|
|
33
|
+
:disabled="menuItems.length === 0"
|
|
45
34
|
>
|
|
46
35
|
<span class="sr-only">Open options</span>
|
|
47
36
|
<DotsVerticalIcon class="w-5 h-5" aria-hidden="true" />
|
|
@@ -58,17 +47,18 @@ onMounted(() => {
|
|
|
58
47
|
class="z-10 mx-3 origin-top-right absolute right-7 top-0 w-48 mt-1 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 divide-y divide-gray-200 focus:outline-none"
|
|
59
48
|
>
|
|
60
49
|
<div class="py-1">
|
|
61
|
-
<template v-for="(item, idx) in
|
|
62
|
-
<MenuItem v-
|
|
50
|
+
<template v-for="(item, idx) in menuItems" :key="idx">
|
|
51
|
+
<MenuItem v-slot="{ active }: { active: boolean }" as="div">
|
|
63
52
|
<button
|
|
64
53
|
type="submit"
|
|
65
54
|
:class="[
|
|
66
55
|
active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',
|
|
67
56
|
'block w-full text-left px-4 py-2 text-sm font-semibold',
|
|
68
57
|
]"
|
|
69
|
-
@click="
|
|
70
|
-
|
|
71
|
-
|
|
58
|
+
@click="item.callback"
|
|
59
|
+
>
|
|
60
|
+
{{ item.label }}
|
|
61
|
+
</button>
|
|
72
62
|
</MenuItem>
|
|
73
63
|
</template>
|
|
74
64
|
</div>
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ComputedRef } from "vue";
|
|
2
|
+
import { Ref } from "vue";
|
|
1
3
|
import { Component, RenderFunction } from "vue";
|
|
2
4
|
export interface Item {
|
|
3
5
|
icon?: Component | RenderFunction;
|
|
@@ -11,3 +13,13 @@ export interface Pagination {
|
|
|
11
13
|
totalItems: number;
|
|
12
14
|
totalPages: number;
|
|
13
15
|
}
|
|
16
|
+
export interface ActionMenuItem {
|
|
17
|
+
label: string;
|
|
18
|
+
callback: (...args: any[]) => void;
|
|
19
|
+
show?: ((...args: any[]) => boolean) | Ref<boolean> | ComputedRef<boolean> | boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface ActionMenuEmit {
|
|
22
|
+
id: string;
|
|
23
|
+
label: string;
|
|
24
|
+
show?: ((...args: any[]) => boolean) | Ref<boolean> | ComputedRef<boolean> | boolean;
|
|
25
|
+
}
|
|
@@ -1,17 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
export interface
|
|
4
|
-
display: string;
|
|
5
|
-
class?: string;
|
|
6
|
-
key?: string;
|
|
7
|
-
presenter?(row: any, instance: ComponentPublicInstance): any;
|
|
8
|
-
component?: Component;
|
|
9
|
-
items?: Array<MenuItem>;
|
|
10
|
-
sort?: string;
|
|
11
|
-
}
|
|
12
|
-
export interface Dynamic {
|
|
13
|
-
currentUser: User;
|
|
14
|
-
columns: Array<Column>;
|
|
1
|
+
import { ComputedRef, Ref, VNodeChild } from "vue";
|
|
2
|
+
import { ActionMenuItem } from "../composables/nav";
|
|
3
|
+
export interface DynamicTableOptions {
|
|
15
4
|
dateSearch?: boolean;
|
|
16
5
|
defaultSort?: string;
|
|
17
6
|
defaultSortDirection?: string;
|
|
@@ -20,13 +9,54 @@ export interface Dynamic {
|
|
|
20
9
|
search?: boolean;
|
|
21
10
|
url: string;
|
|
22
11
|
}
|
|
23
|
-
export interface
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
12
|
+
export interface DynamicTableAPI {
|
|
13
|
+
/**
|
|
14
|
+
* Force refresh the table data with the current api params state
|
|
15
|
+
* @returns void
|
|
16
|
+
*/
|
|
17
|
+
refresh: () => void;
|
|
18
|
+
/**
|
|
19
|
+
* Reset the table data back to page 1 and load
|
|
20
|
+
* @returns void
|
|
21
|
+
*/
|
|
22
|
+
reset: () => void;
|
|
27
23
|
}
|
|
28
|
-
export interface
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
export interface TableActionItem<T = TableRowData> extends ActionMenuItem {
|
|
25
|
+
callback: (rowData: T, rowIndex: number) => void;
|
|
26
|
+
show?: ((rowData: T, rowIndex: number) => boolean) | Ref<boolean> | ComputedRef<boolean> | boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface TableColumn<T = TableRowData> {
|
|
29
|
+
actions?: TableActions<T>;
|
|
30
|
+
/**
|
|
31
|
+
* The alignment the table cell should have
|
|
32
|
+
*/
|
|
33
|
+
alignment?: TableCellAlignment;
|
|
34
|
+
/**
|
|
35
|
+
* Class names to wrap your column data in
|
|
36
|
+
* This field is ignored when a render method is defined
|
|
37
|
+
*/
|
|
38
|
+
classNames?: string;
|
|
39
|
+
/**
|
|
40
|
+
* The text to display as the column header
|
|
41
|
+
*/
|
|
42
|
+
title: string;
|
|
43
|
+
/**
|
|
44
|
+
* The property key your column data maps to
|
|
45
|
+
*/
|
|
46
|
+
key: keyof T;
|
|
47
|
+
/**
|
|
48
|
+
* A render method for formatting the output of your columns data
|
|
49
|
+
* This may include returning a custom component using the vue h method
|
|
50
|
+
*/
|
|
51
|
+
render?: (rowData: T, rowIndex: number) => string | number | boolean | VNodeChild;
|
|
52
|
+
/**
|
|
53
|
+
* A sorting identifier
|
|
54
|
+
* Only used on DynamicTable
|
|
55
|
+
*/
|
|
56
|
+
sort?: string;
|
|
32
57
|
}
|
|
58
|
+
export type TableActions<T = TableRowData> = TableActionItem<T>[];
|
|
59
|
+
export type TableCellAlignment = "left" | "center" | "right";
|
|
60
|
+
export type TableRowData = Record<string, any>;
|
|
61
|
+
export type TableColumns<T = TableRowData> = TableColumn<T>[];
|
|
62
|
+
export type TableRowsData = TableRowData[];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Ref } from "vue";
|
|
2
|
+
import { TableColumns, TableRowsData, TableActions } from "./table";
|
|
3
|
+
export declare const useTable: (rowData: TableRowsData | Ref<TableRowsData>, cols: TableColumns, acts: TableActions) => {
|
|
4
|
+
columns: import("vue").ComputedRef<{
|
|
5
|
+
alignment: import("./table").TableCellAlignment;
|
|
6
|
+
actions?: {
|
|
7
|
+
callback: (rowData: import("./table").TableRowData, rowIndex: number) => void;
|
|
8
|
+
show?: boolean | ((rowData: import("./table").TableRowData, rowIndex: number) => boolean) | undefined;
|
|
9
|
+
label: string;
|
|
10
|
+
}[] | undefined;
|
|
11
|
+
classNames?: string | undefined;
|
|
12
|
+
title: string;
|
|
13
|
+
key: string;
|
|
14
|
+
render?: ((rowData: import("./table").TableRowData, rowIndex: number) => import("vue").VNodeChild) | undefined;
|
|
15
|
+
sort?: string | undefined;
|
|
16
|
+
}[]>;
|
|
17
|
+
hasActions: import("vue").ComputedRef<boolean>;
|
|
18
|
+
isEmptyCellValue: (v: unknown) => boolean;
|
|
19
|
+
rows: import("vue").ComputedRef<{
|
|
20
|
+
actions: {
|
|
21
|
+
callback: () => void;
|
|
22
|
+
show: boolean | undefined;
|
|
23
|
+
label: string;
|
|
24
|
+
}[];
|
|
25
|
+
rowData: import("./table").TableRowData;
|
|
26
|
+
cells: {
|
|
27
|
+
isComponent: boolean;
|
|
28
|
+
val: any;
|
|
29
|
+
alignment: import("./table").TableCellAlignment;
|
|
30
|
+
actions?: {
|
|
31
|
+
callback: (rowData: import("./table").TableRowData, rowIndex: number) => void;
|
|
32
|
+
show?: boolean | ((rowData: import("./table").TableRowData, rowIndex: number) => boolean) | undefined;
|
|
33
|
+
label: string;
|
|
34
|
+
}[] | undefined;
|
|
35
|
+
classNames?: string | undefined;
|
|
36
|
+
title: string;
|
|
37
|
+
key: string;
|
|
38
|
+
render?: ((rowData: import("./table").TableRowData, rowIndex: number) => import("vue").VNodeChild) | undefined;
|
|
39
|
+
sort?: string | undefined;
|
|
40
|
+
}[];
|
|
41
|
+
}[]>;
|
|
42
|
+
};
|