@xy-planning-network/trees 0.6.5 → 0.6.6-rc-2
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 +2352 -2317
- package/dist/trees.umd.js +9 -9
- package/package.json +1 -2
- package/src/lib-components/lists/DataTable.vue +100 -0
- package/src/lib-components/lists/DownloadCell.vue +5 -3
- package/src/lib-components/lists/{Table.vue → DynamicTable.vue} +88 -61
- package/src/lib-components/navigation/ActionsDropdown.vue +22 -32
- package/types/composables/index.d.ts +2 -1
- 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-2",
|
|
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,100 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useTable } from "@/composables/useTable"
|
|
3
|
+
import { TableActions, TableColumns, TableRowsData } from "@/composables/table"
|
|
4
|
+
import { ActionsDropdown } from "@/lib-components"
|
|
5
|
+
|
|
6
|
+
const props = withDefaults(
|
|
7
|
+
defineProps<{
|
|
8
|
+
tableActions?: TableActions<any>
|
|
9
|
+
tableColumns: TableColumns<any>
|
|
10
|
+
tableData: TableRowsData
|
|
11
|
+
}>(),
|
|
12
|
+
{
|
|
13
|
+
tableActions: () => [],
|
|
14
|
+
}
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
const { columns, hasActions, isEmptyCellValue, rows } = useTable(
|
|
18
|
+
props.tableData,
|
|
19
|
+
props.tableColumns,
|
|
20
|
+
props.tableActions
|
|
21
|
+
)
|
|
22
|
+
</script>
|
|
23
|
+
<template>
|
|
24
|
+
<div class="flex flex-col">
|
|
25
|
+
<div class="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
|
26
|
+
<div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
|
|
27
|
+
<div
|
|
28
|
+
class="overflow-hidden border-b border-gray-200 shadow sm:rounded-lg"
|
|
29
|
+
>
|
|
30
|
+
<table class="min-w-full divide-y divide-gray-200">
|
|
31
|
+
<thead>
|
|
32
|
+
<tr>
|
|
33
|
+
<th
|
|
34
|
+
v-for="(col, idx) in columns"
|
|
35
|
+
:key="idx"
|
|
36
|
+
class="px-6 py-3 text-xs font-medium tracking-wider text-gray-900 uppercase bg-gray-50 leading-4"
|
|
37
|
+
:class="{
|
|
38
|
+
'text-left': col.alignment === 'left',
|
|
39
|
+
'text-right': col.alignment === 'right',
|
|
40
|
+
'text-center': col.alignment === 'center',
|
|
41
|
+
}"
|
|
42
|
+
>
|
|
43
|
+
{{ col.title }}
|
|
44
|
+
</th>
|
|
45
|
+
|
|
46
|
+
<!--Table Actions Header-->
|
|
47
|
+
<th
|
|
48
|
+
v-if="hasActions"
|
|
49
|
+
class="px-6 py-3 text-xs font-medium tracking-wider text-gray-900 uppercase bg-gray-50 leading-4"
|
|
50
|
+
>
|
|
51
|
+
Actions
|
|
52
|
+
</th>
|
|
53
|
+
</tr>
|
|
54
|
+
</thead>
|
|
55
|
+
<tbody class="bg-white divide-y divide-gray-200">
|
|
56
|
+
<tr v-for="(row, rowIdx) in rows" :key="rowIdx">
|
|
57
|
+
<template v-for="(cell, cellIdx) in row.cells" :key="cellIdx">
|
|
58
|
+
<component
|
|
59
|
+
:is="'td'"
|
|
60
|
+
class="px-6 py-4 text-sm text-gray-700 whitespace-nowrap leading-5"
|
|
61
|
+
:class="{
|
|
62
|
+
'text-left': cell.alignment === 'left',
|
|
63
|
+
'text-right': cell.alignment === 'right',
|
|
64
|
+
'text-center': cell.alignment === 'center',
|
|
65
|
+
}"
|
|
66
|
+
>
|
|
67
|
+
<template v-if="cell.isComponent">
|
|
68
|
+
<component :is="cell.val" />
|
|
69
|
+
</template>
|
|
70
|
+
|
|
71
|
+
<span v-else :class="cell.classNames">
|
|
72
|
+
{{ isEmptyCellValue(cell.val) ? "-" : String(cell.val) }}
|
|
73
|
+
</span>
|
|
74
|
+
</component>
|
|
75
|
+
</template>
|
|
76
|
+
|
|
77
|
+
<!--Table Actions Cell-->
|
|
78
|
+
<td
|
|
79
|
+
v-if="hasActions"
|
|
80
|
+
class="px-6 py-4 text-sm text-gray-700 whitespace-nowrap leading-5"
|
|
81
|
+
>
|
|
82
|
+
<ActionsDropdown :items="row.actions" />
|
|
83
|
+
</td>
|
|
84
|
+
</tr>
|
|
85
|
+
|
|
86
|
+
<tr v-if="rows.length === 0">
|
|
87
|
+
<td
|
|
88
|
+
:colspan="columns.length"
|
|
89
|
+
class="px-6 py-4 text-sm text-gray-700 whitespace-nowrap leading-5"
|
|
90
|
+
>
|
|
91
|
+
No items were found!
|
|
92
|
+
</td>
|
|
93
|
+
</tr>
|
|
94
|
+
</tbody>
|
|
95
|
+
</table>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</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,63 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
computed,
|
|
5
|
-
getCurrentInstance,
|
|
6
|
-
ref,
|
|
7
|
-
watch,
|
|
8
|
-
} from "vue"
|
|
2
|
+
import { computed, ref, watch } from "vue"
|
|
3
|
+
import { ActionsDropdown } from "@/lib-components"
|
|
9
4
|
import DateRangePicker from "../forms/DateRangePicker.vue"
|
|
10
5
|
import Paginator from "../navigation/Paginator.vue"
|
|
11
6
|
import BaseAPI from "../../api/base"
|
|
12
|
-
import
|
|
7
|
+
import {
|
|
8
|
+
DynamicTableAPI,
|
|
9
|
+
DynamicTableOptions,
|
|
10
|
+
TableActions,
|
|
11
|
+
TableColumns,
|
|
12
|
+
} from "@/composables/table"
|
|
13
13
|
import { useAppFlasher } from "@/composables/useFlashes"
|
|
14
14
|
import { TrailsRespPaged } from "@/api/client"
|
|
15
|
+
import { useTable } from "@/composables/useTable"
|
|
15
16
|
|
|
16
17
|
const props = withDefaults(
|
|
17
18
|
defineProps<{
|
|
18
19
|
clickable?: boolean
|
|
19
20
|
loader?: boolean
|
|
20
|
-
|
|
21
|
+
tableActions?: TableActions<any>
|
|
22
|
+
tableColumns: TableColumns<any>
|
|
23
|
+
tableOptions: DynamicTableOptions
|
|
21
24
|
}>(),
|
|
22
25
|
{
|
|
23
26
|
clickable: false,
|
|
24
27
|
loader: true,
|
|
28
|
+
tableActions: () => [],
|
|
25
29
|
}
|
|
26
30
|
)
|
|
27
31
|
|
|
28
32
|
defineEmits<{
|
|
29
|
-
(
|
|
33
|
+
(
|
|
34
|
+
e: "click:row",
|
|
35
|
+
v: (typeof tableData.value)[number],
|
|
36
|
+
c: typeof publicMethods
|
|
37
|
+
): void
|
|
30
38
|
}>()
|
|
31
39
|
|
|
40
|
+
const tableData = ref<Record<string, any>[]>([])
|
|
41
|
+
|
|
42
|
+
const { columns, hasActions, isEmptyCellValue, rows } = useTable(
|
|
43
|
+
tableData,
|
|
44
|
+
props.tableColumns,
|
|
45
|
+
props.tableActions
|
|
46
|
+
)
|
|
47
|
+
|
|
32
48
|
const currentSort = ref(
|
|
33
|
-
props.
|
|
49
|
+
props.tableOptions.defaultSort ? props.tableOptions.defaultSort : ""
|
|
34
50
|
)
|
|
35
51
|
const currentSortDirection = ref(
|
|
36
|
-
props.
|
|
37
|
-
? props.
|
|
52
|
+
props.tableOptions.defaultSortDirection
|
|
53
|
+
? props.tableOptions.defaultSortDirection
|
|
38
54
|
: "desc"
|
|
39
55
|
)
|
|
40
56
|
const dateRange = ref({
|
|
41
57
|
minDate: 0,
|
|
42
58
|
maxDate: 0,
|
|
43
59
|
})
|
|
44
|
-
|
|
60
|
+
|
|
45
61
|
const pagination = ref({
|
|
46
62
|
page: 1,
|
|
47
63
|
perPage: 10,
|
|
@@ -49,27 +65,7 @@ const pagination = ref({
|
|
|
49
65
|
totalPages: 0,
|
|
50
66
|
})
|
|
51
67
|
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
68
|
|
|
71
|
-
return ""
|
|
72
|
-
}
|
|
73
69
|
const dateRangeChanged = (newDateRange: {
|
|
74
70
|
minDate: number
|
|
75
71
|
maxDate: number
|
|
@@ -101,7 +97,7 @@ const loadAndRender = (): void => {
|
|
|
101
97
|
}
|
|
102
98
|
|
|
103
99
|
BaseAPI.get<TrailsRespPaged<unknown>>(
|
|
104
|
-
props.
|
|
100
|
+
props.tableOptions.url,
|
|
105
101
|
{ skipLoader: !props.loader },
|
|
106
102
|
params
|
|
107
103
|
).then(
|
|
@@ -112,7 +108,7 @@ const loadAndRender = (): void => {
|
|
|
112
108
|
totalItems: success.data.totalItems,
|
|
113
109
|
totalPages: success.data.totalPages,
|
|
114
110
|
}
|
|
115
|
-
|
|
111
|
+
tableData.value = success.data.items as Record<string, any>[]
|
|
116
112
|
},
|
|
117
113
|
() => {
|
|
118
114
|
useAppFlasher.genericError()
|
|
@@ -126,11 +122,11 @@ const reloadTable = (): void => {
|
|
|
126
122
|
}
|
|
127
123
|
|
|
128
124
|
const hasContent = computed((): boolean => {
|
|
129
|
-
return
|
|
125
|
+
return rows.value.length ? true : false
|
|
130
126
|
})
|
|
131
127
|
|
|
132
128
|
watch(
|
|
133
|
-
() => props.
|
|
129
|
+
() => props.tableOptions.refreshTrigger,
|
|
134
130
|
() => {
|
|
135
131
|
// This lets parent components trigger a refresh of the current page depending on external actions.
|
|
136
132
|
loadAndRender()
|
|
@@ -138,13 +134,20 @@ watch(
|
|
|
138
134
|
)
|
|
139
135
|
|
|
140
136
|
watch(
|
|
141
|
-
() => props.
|
|
137
|
+
() => props.tableOptions.reloadTrigger,
|
|
142
138
|
() => {
|
|
143
139
|
// This lets parent components trigger a reload of page 1 depending on external actions.
|
|
144
140
|
reloadTable()
|
|
145
141
|
}
|
|
146
142
|
)
|
|
147
143
|
|
|
144
|
+
const publicMethods: DynamicTableAPI = {
|
|
145
|
+
refresh: loadAndRender,
|
|
146
|
+
reset: reloadTable,
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
defineExpose(publicMethods)
|
|
150
|
+
|
|
148
151
|
// onCreated
|
|
149
152
|
loadAndRender()
|
|
150
153
|
</script>
|
|
@@ -153,7 +156,7 @@ loadAndRender()
|
|
|
153
156
|
<div
|
|
154
157
|
class="flex flex-col mb-4 space-y-4 lg:space-y-0 lg:flex-row lg:justify-between"
|
|
155
158
|
>
|
|
156
|
-
<div v-if="
|
|
159
|
+
<div v-if="tableOptions.search" class="w-full max-w-lg lg:max-w-xs">
|
|
157
160
|
<label for="search" class="sr-only">Search</label>
|
|
158
161
|
<div class="relative">
|
|
159
162
|
<div
|
|
@@ -180,7 +183,7 @@ loadAndRender()
|
|
|
180
183
|
/>
|
|
181
184
|
</div>
|
|
182
185
|
</div>
|
|
183
|
-
<div v-if="
|
|
186
|
+
<div v-if="tableOptions.dateSearch" class="w-full max-w-lg lg:max-w-xs">
|
|
184
187
|
<DateRangePicker
|
|
185
188
|
v-model="dateRange"
|
|
186
189
|
@update:model-value="dateRangeChanged"
|
|
@@ -195,11 +198,16 @@ loadAndRender()
|
|
|
195
198
|
<thead>
|
|
196
199
|
<tr>
|
|
197
200
|
<th
|
|
198
|
-
v-for="(col, idx) in
|
|
201
|
+
v-for="(col, idx) in columns"
|
|
199
202
|
:key="idx"
|
|
200
|
-
class="px-6 py-3 text-xs font-medium tracking-wider text-
|
|
203
|
+
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"
|
|
204
|
+
:class="{
|
|
205
|
+
'text-left': col.alignment === 'left',
|
|
206
|
+
'text-right': col.alignment === 'right',
|
|
207
|
+
'text-center': col.alignment === 'center',
|
|
208
|
+
}"
|
|
201
209
|
>
|
|
202
|
-
<span v-if="
|
|
210
|
+
<span v-if="col.title">{{ col.title }}</span>
|
|
203
211
|
<span
|
|
204
212
|
v-if="col.sort"
|
|
205
213
|
class="cursor-pointer"
|
|
@@ -248,37 +256,56 @@ loadAndRender()
|
|
|
248
256
|
</svg>
|
|
249
257
|
</span>
|
|
250
258
|
</th>
|
|
259
|
+
|
|
260
|
+
<!--Table Actions Header-->
|
|
261
|
+
<th
|
|
262
|
+
v-if="hasActions"
|
|
263
|
+
class="px-6 py-3 text-xs font-medium tracking-wider text-gray-900 uppercase bg-gray-50 leading-4"
|
|
264
|
+
>
|
|
265
|
+
Actions
|
|
266
|
+
</th>
|
|
251
267
|
</tr>
|
|
252
268
|
</thead>
|
|
253
269
|
|
|
254
270
|
<tbody class="bg-white">
|
|
255
271
|
<tr
|
|
256
|
-
v-for="(
|
|
257
|
-
:key="
|
|
272
|
+
v-for="(row, rowIdx) in rows"
|
|
273
|
+
:key="rowIdx"
|
|
258
274
|
:class="{ 'cursor-pointer': clickable }"
|
|
259
|
-
@click="$emit('
|
|
275
|
+
@click="$emit('click:row', row.rowData, publicMethods)"
|
|
260
276
|
>
|
|
277
|
+
<template v-for="(cell, cellIdx) in row.cells" :key="cellIdx">
|
|
278
|
+
<component
|
|
279
|
+
:is="'td'"
|
|
280
|
+
class="px-6 py-4 text-sm text-gray-700 whitespace-nowrap leading-5"
|
|
281
|
+
:class="{
|
|
282
|
+
'text-left': cell.alignment === 'left',
|
|
283
|
+
'text-right': cell.alignment === 'right',
|
|
284
|
+
'text-center': cell.alignment === 'center',
|
|
285
|
+
}"
|
|
286
|
+
>
|
|
287
|
+
<template v-if="cell.isComponent">
|
|
288
|
+
<component :is="cell.val" />
|
|
289
|
+
</template>
|
|
290
|
+
|
|
291
|
+
<span v-else :class="cell.classNames">
|
|
292
|
+
{{ isEmptyCellValue(cell.val) ? "-" : String(cell.val) }}
|
|
293
|
+
</span>
|
|
294
|
+
</component>
|
|
295
|
+
</template>
|
|
296
|
+
|
|
297
|
+
<!--Table Actions Cell-->
|
|
261
298
|
<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"
|
|
299
|
+
v-if="hasActions"
|
|
300
|
+
class="px-6 py-4 text-sm text-gray-700 whitespace-nowrap leading-5"
|
|
266
301
|
>
|
|
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>
|
|
302
|
+
<ActionsDropdown :items="row.actions" />
|
|
276
303
|
</td>
|
|
277
304
|
</tr>
|
|
278
305
|
|
|
279
306
|
<tr v-if="!hasContent">
|
|
280
307
|
<td
|
|
281
|
-
:colspan="
|
|
308
|
+
:colspan="rows.length"
|
|
282
309
|
class="px-6 py-4 text-sm text-gray-700 whitespace-nowrap border-b border-gray-200 leading-5"
|
|
283
310
|
>
|
|
284
311
|
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,6 +1,7 @@
|
|
|
1
1
|
import { default as useBaseAPI, useBaseAPIGet, useBaseAPIPut, useBaseAPIPost, useBaseAPIDelete } from "./useBaseAPI";
|
|
2
2
|
import type { UseBaseAPIOptions, UseBaseAPI } from "./useBaseAPI";
|
|
3
|
-
|
|
3
|
+
import type { DynamicTableOptions, DynamicTableAPI, TableActionItem, TableColumn, TableActions, TableCellAlignment, TableRowData, TableColumns, TableRowsData } from "./table";
|
|
4
|
+
export type { UseBaseAPIOptions, UseBaseAPI, DynamicTableOptions, DynamicTableAPI, TableActionItem, TableColumn, TableActions, TableCellAlignment, TableRowData, TableColumns, TableRowsData, };
|
|
4
5
|
export { useBaseAPI, useBaseAPIGet, useBaseAPIPut, useBaseAPIPost, useBaseAPIDelete, };
|
|
5
6
|
import { useFlashes, useAppFlashes, useAppFlasher } from "./useFlashes";
|
|
6
7
|
import type { FlashMessage, FlashType, FlashStore, Flasher } from "./useFlashes";
|
|
@@ -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
|
+
};
|