@finema/core 2.49.0 → 2.50.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/dist/module.json +1 -1
- package/dist/module.mjs +1 -1
- package/dist/runtime/components/Table/Base.vue +119 -119
- package/dist/runtime/components/Table/Base.vue.d.ts +19 -60
- package/dist/runtime/components/Table/Pagination.vue +131 -0
- package/dist/runtime/components/Table/Pagination.vue.d.ts +18 -0
- package/dist/runtime/components/Table/Simple.vue +22 -24
- package/dist/runtime/components/Table/Simple.vue.d.ts +5 -12
- package/dist/runtime/components/Table/index.vue +11 -138
- package/dist/runtime/components/Table/types.d.ts +3 -0
- package/dist/runtime/theme/dialog.js +1 -1
- package/dist/runtime/theme/pagination.d.ts +2 -0
- package/dist/runtime/theme/pagination.js +6 -4
- package/dist/runtime/theme/table.d.ts +4 -0
- package/dist/runtime/theme/table.js +9 -5
- package/package.json +3 -3
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,114 +1,114 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
2
|
+
<div
|
|
3
|
+
:class="theme.rootWrapper({
|
|
4
|
+
class: [ui?.rootWrapper]
|
|
5
|
+
})"
|
|
6
|
+
>
|
|
7
|
+
<Pagination
|
|
8
|
+
v-if="!options.isHidePagination && !options.isHidePaginationTop"
|
|
9
|
+
:options="options"
|
|
10
|
+
:ui="ui"
|
|
11
|
+
:page="page"
|
|
12
|
+
:page-limit="pageLimit"
|
|
13
|
+
class="border-b border-[#EAECF0]"
|
|
14
|
+
@pageChange="onPageChange"
|
|
15
|
+
@search="emits('search')"
|
|
16
|
+
@pageLimitChange="changePageLimit"
|
|
17
|
+
/>
|
|
18
|
+
<UTable
|
|
19
|
+
:loading="options.status.isLoading"
|
|
20
|
+
:columns="uTableCompatibleColumns"
|
|
21
|
+
:data="options.rawData"
|
|
22
|
+
v-bind="$attrs"
|
|
23
|
+
>
|
|
24
|
+
<template #loading-state>
|
|
25
|
+
<Loader
|
|
26
|
+
:loading="true"
|
|
27
|
+
/>
|
|
28
|
+
</template>
|
|
29
|
+
<template #empty>
|
|
30
|
+
<slot
|
|
31
|
+
v-if="options.status.isLoading"
|
|
32
|
+
name="loading"
|
|
33
|
+
>
|
|
34
|
+
<Loader
|
|
35
|
+
:loading="true"
|
|
36
|
+
/>
|
|
37
|
+
</slot>
|
|
38
|
+
<slot
|
|
39
|
+
v-else-if="options.status.isError"
|
|
40
|
+
name="error"
|
|
41
|
+
>
|
|
42
|
+
<div
|
|
43
|
+
class="
|
|
44
|
+
text-error-400 flex h-[200px] items-center justify-center text-2xl
|
|
45
|
+
"
|
|
46
|
+
>
|
|
47
|
+
{{ StringHelper.getError(options.status.errorData) }}
|
|
48
|
+
</div>
|
|
49
|
+
</slot>
|
|
50
|
+
|
|
51
|
+
<slot
|
|
52
|
+
v-else
|
|
53
|
+
name="error"
|
|
54
|
+
>
|
|
55
|
+
<Empty />
|
|
56
|
+
</slot>
|
|
57
|
+
</template>
|
|
58
|
+
<template
|
|
59
|
+
v-for="column in options.columns.filter((item) => !!item.type)"
|
|
60
|
+
#[`${column.accessorKey}-cell`]="{ row }"
|
|
61
|
+
:key="column.accessorKey"
|
|
62
|
+
>
|
|
63
|
+
<component
|
|
64
|
+
:is="column.type === COLUMN_TYPES.COMPONENT ? column.component : columnTypeComponents[column.type]"
|
|
65
|
+
v-if="column.type === COLUMN_TYPES.COMPONENT || columnTypeComponents[column.type]"
|
|
66
|
+
:value="transformValue(column, row)"
|
|
67
|
+
:column="column"
|
|
68
|
+
:row="row"
|
|
69
|
+
/>
|
|
70
|
+
</template>
|
|
71
|
+
<template
|
|
72
|
+
v-for="(_, slotName) of $slots"
|
|
73
|
+
#[slotName]="slotProps"
|
|
74
|
+
>
|
|
75
|
+
<slot
|
|
76
|
+
:name="slotName"
|
|
77
|
+
v-bind="slotProps || {}"
|
|
78
|
+
/>
|
|
79
|
+
</template>
|
|
80
|
+
</UTable>
|
|
81
|
+
<Pagination
|
|
82
|
+
v-if="!options.isHidePagination && !options.isHidePaginationBottom"
|
|
83
|
+
:options="options"
|
|
84
|
+
:ui="ui"
|
|
85
|
+
:page="page"
|
|
86
|
+
:page-limit="pageLimit"
|
|
87
|
+
class="rounded-b-lg border-t border-[#EAECF0]"
|
|
88
|
+
@pageChange="onPageChange"
|
|
89
|
+
@search="emits('search')"
|
|
90
|
+
@pageLimitChange="changePageLimit"
|
|
91
|
+
/>
|
|
92
|
+
</div>
|
|
69
93
|
</template>
|
|
70
94
|
|
|
71
95
|
<script setup>
|
|
72
|
-
import { computed } from "vue";
|
|
96
|
+
import { computed, ref } from "vue";
|
|
73
97
|
import { COLUMN_TYPES } from "#core/components/Table/types";
|
|
74
98
|
import ColumnNumber from "#core/components/Table/ColumnNumber.vue";
|
|
75
99
|
import ColumnImage from "#core/components/Table/ColumnImage.vue";
|
|
76
|
-
import {
|
|
77
|
-
import { ref, useUiConfig, watch } from "#imports";
|
|
100
|
+
import { updateAppConfig, useAppConfig, useUiConfig, useWatchChange } from "#imports";
|
|
78
101
|
import ColumnDateTime from "#core/components/Table/ColumnDateTime.vue";
|
|
79
102
|
import Empty from "#core/components/Empty.vue";
|
|
80
103
|
import ColumnDate from "#core/components/Table/ColumnDate.vue";
|
|
81
104
|
import ColumnText from "#core/components/Table/ColumnText.vue";
|
|
82
|
-
import { useWatchChange } from "#core/composables/useWatch";
|
|
83
105
|
import UTable from "#ui/components/Table";
|
|
106
|
+
import Pagination from "./Pagination.vue";
|
|
84
107
|
import { tableTheme } from "#core/theme/table";
|
|
85
|
-
const emits = defineEmits(["pageChange"]);
|
|
108
|
+
const emits = defineEmits(["pageChange", "search"]);
|
|
86
109
|
const props = defineProps({
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
required: true
|
|
90
|
-
},
|
|
91
|
-
pageOptions: {
|
|
92
|
-
type: Object,
|
|
93
|
-
required: false
|
|
94
|
-
},
|
|
95
|
-
columns: {
|
|
96
|
-
type: Array,
|
|
97
|
-
// This resolves to TableColumn[]
|
|
98
|
-
required: true
|
|
99
|
-
},
|
|
100
|
-
rawData: {
|
|
101
|
-
type: Array,
|
|
102
|
-
required: true
|
|
103
|
-
},
|
|
104
|
-
isHidePagination: {
|
|
105
|
-
type: Boolean,
|
|
106
|
-
default: false
|
|
107
|
-
},
|
|
108
|
-
isHideCaption: {
|
|
109
|
-
type: Boolean,
|
|
110
|
-
default: false
|
|
111
|
-
}
|
|
110
|
+
options: { type: Object, required: true },
|
|
111
|
+
ui: { type: null, required: false }
|
|
112
112
|
});
|
|
113
113
|
const columnTypeComponents = {
|
|
114
114
|
[COLUMN_TYPES.NUMBER]: ColumnNumber,
|
|
@@ -117,39 +117,39 @@ const columnTypeComponents = {
|
|
|
117
117
|
[COLUMN_TYPES.DATE]: ColumnDate,
|
|
118
118
|
[COLUMN_TYPES.TEXT]: ColumnText
|
|
119
119
|
};
|
|
120
|
-
const page = ref(props.pageOptions?.currentPage || 1);
|
|
121
120
|
const theme = computed(() => useUiConfig(tableTheme, "table")());
|
|
121
|
+
const config = useAppConfig();
|
|
122
|
+
const page = ref(props.options.pageOptions?.currentPage || 1);
|
|
123
|
+
const pageLimit = ref(props.options.pageOptions?.limit || config.core.limit_per_page);
|
|
122
124
|
const uTableCompatibleColumns = computed(
|
|
123
|
-
() => props.columns.map((col) => ({
|
|
125
|
+
() => props.options.columns.map((col) => ({
|
|
124
126
|
...col,
|
|
125
127
|
key: col.accessorKey
|
|
126
128
|
// Use accessorKey for UTable's key property
|
|
127
129
|
}))
|
|
128
130
|
);
|
|
131
|
+
const transformValue = (column, row) => {
|
|
132
|
+
return column.cell ? column.cell({
|
|
133
|
+
row
|
|
134
|
+
}) : row.getValue(column.accessorKey);
|
|
135
|
+
};
|
|
129
136
|
useWatchChange(
|
|
130
|
-
() => props.pageOptions?.currentPage,
|
|
137
|
+
() => props.options.pageOptions?.currentPage,
|
|
131
138
|
(value) => {
|
|
132
139
|
page.value = value;
|
|
133
140
|
}
|
|
134
141
|
);
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}) : row.getValue(column.accessorKey);
|
|
142
|
+
const onPageChange = (newPage) => {
|
|
143
|
+
page.value = newPage;
|
|
144
|
+
emits("pageChange", newPage);
|
|
145
|
+
};
|
|
146
|
+
const changePageLimit = (limit) => {
|
|
147
|
+
pageLimit.value = limit;
|
|
148
|
+
updateAppConfig({
|
|
149
|
+
core: {
|
|
150
|
+
limit_per_page: limit
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
emits("search");
|
|
148
154
|
};
|
|
149
|
-
const totalCountWithComma = computed(() => {
|
|
150
|
-
return !props.pageOptions?.totalCount ? "0" : NumberHelper.withComma(props.pageOptions.totalCount);
|
|
151
|
-
});
|
|
152
|
-
watch(page, () => {
|
|
153
|
-
emits("pageChange", page.value);
|
|
154
|
-
});
|
|
155
155
|
</script>
|
|
@@ -1,67 +1,26 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { type ISimpleTableOptions, type ITableOptions } from '#core/components/Table/types';
|
|
2
|
+
import { tableTheme } from '#core/theme/table';
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
options: ITableOptions<any> | ISimpleTableOptions<any>;
|
|
5
|
+
ui?: typeof tableTheme['slots'];
|
|
6
|
+
};
|
|
7
|
+
declare var __VLS_22: {}, __VLS_29: {}, __VLS_31: {}, __VLS_45: string | number, __VLS_46: any;
|
|
4
8
|
type __VLS_Slots = {} & {
|
|
5
|
-
[K in NonNullable<typeof
|
|
9
|
+
[K in NonNullable<typeof __VLS_45>]?: (props: typeof __VLS_46) => any;
|
|
10
|
+
} & {
|
|
11
|
+
loading?: (props: typeof __VLS_22) => any;
|
|
12
|
+
} & {
|
|
13
|
+
error?: (props: typeof __VLS_29) => any;
|
|
14
|
+
} & {
|
|
15
|
+
error?: (props: typeof __VLS_31) => any;
|
|
6
16
|
};
|
|
7
|
-
declare const __VLS_component: import("vue").DefineComponent<import("vue").
|
|
8
|
-
|
|
9
|
-
type: PropType<ITableOptions["status"]>;
|
|
10
|
-
required: true;
|
|
11
|
-
};
|
|
12
|
-
pageOptions: {
|
|
13
|
-
type: PropType<ITableOptions["pageOptions"]>;
|
|
14
|
-
required: false;
|
|
15
|
-
};
|
|
16
|
-
columns: {
|
|
17
|
-
type: PropType<ITableOptions["columns"]>;
|
|
18
|
-
required: true;
|
|
19
|
-
};
|
|
20
|
-
rawData: {
|
|
21
|
-
type: PropType<ITableOptions["rawData"]>;
|
|
22
|
-
required: true;
|
|
23
|
-
};
|
|
24
|
-
isHidePagination: {
|
|
25
|
-
type: PropType<ITableOptions["isHidePagination"]>;
|
|
26
|
-
default: boolean;
|
|
27
|
-
};
|
|
28
|
-
isHideCaption: {
|
|
29
|
-
type: PropType<ITableOptions["isHideCaption"]>;
|
|
30
|
-
default: boolean;
|
|
31
|
-
};
|
|
32
|
-
}>, void, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
17
|
+
declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, void, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
18
|
+
search: (...args: any[]) => void;
|
|
33
19
|
pageChange: (...args: any[]) => void;
|
|
34
|
-
}, string, import("vue").PublicProps, Readonly<
|
|
35
|
-
|
|
36
|
-
type: PropType<ITableOptions["status"]>;
|
|
37
|
-
required: true;
|
|
38
|
-
};
|
|
39
|
-
pageOptions: {
|
|
40
|
-
type: PropType<ITableOptions["pageOptions"]>;
|
|
41
|
-
required: false;
|
|
42
|
-
};
|
|
43
|
-
columns: {
|
|
44
|
-
type: PropType<ITableOptions["columns"]>;
|
|
45
|
-
required: true;
|
|
46
|
-
};
|
|
47
|
-
rawData: {
|
|
48
|
-
type: PropType<ITableOptions["rawData"]>;
|
|
49
|
-
required: true;
|
|
50
|
-
};
|
|
51
|
-
isHidePagination: {
|
|
52
|
-
type: PropType<ITableOptions["isHidePagination"]>;
|
|
53
|
-
default: boolean;
|
|
54
|
-
};
|
|
55
|
-
isHideCaption: {
|
|
56
|
-
type: PropType<ITableOptions["isHideCaption"]>;
|
|
57
|
-
default: boolean;
|
|
58
|
-
};
|
|
59
|
-
}>> & Readonly<{
|
|
20
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
21
|
+
onSearch?: ((...args: any[]) => any) | undefined;
|
|
60
22
|
onPageChange?: ((...args: any[]) => any) | undefined;
|
|
61
|
-
}>, {
|
|
62
|
-
isHidePagination: boolean | undefined;
|
|
63
|
-
isHideCaption: boolean | undefined;
|
|
64
|
-
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
23
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
65
24
|
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
66
25
|
export default _default;
|
|
67
26
|
type __VLS_WithSlots<T, S> = T & {
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
:class="theme.paginationContainer({
|
|
4
|
+
class: [ui?.paginationContainer]
|
|
5
|
+
})"
|
|
6
|
+
>
|
|
7
|
+
<div
|
|
8
|
+
:class="theme.paginationInfoWrapper({
|
|
9
|
+
class: [ui?.paginationInfoWrapper]
|
|
10
|
+
})"
|
|
11
|
+
>
|
|
12
|
+
<USelect
|
|
13
|
+
v-if="options.pageOptions && !options.isHideLimitSelect"
|
|
14
|
+
size="lg"
|
|
15
|
+
trailing="รายการ"
|
|
16
|
+
:class="theme.paginationLimitSelect({
|
|
17
|
+
class: [ui?.paginationLimitSelect, 'cursor-pointer']
|
|
18
|
+
})"
|
|
19
|
+
:items="pageLimitItems"
|
|
20
|
+
:model-value="pageLimit"
|
|
21
|
+
@update:modelValue="emits('pageLimitChange', $event)"
|
|
22
|
+
>
|
|
23
|
+
<template #default="{ modelValue }">
|
|
24
|
+
<p
|
|
25
|
+
:class="theme.paginationLimitSelectLabel({
|
|
26
|
+
class: [ui?.paginationLimitSelectLabel]
|
|
27
|
+
})"
|
|
28
|
+
>
|
|
29
|
+
{{ modelValue }} รายการ
|
|
30
|
+
</p>
|
|
31
|
+
</template>
|
|
32
|
+
</USelect>
|
|
33
|
+
<p
|
|
34
|
+
:class="theme.paginationInfo({
|
|
35
|
+
class: [ui?.paginationInfo, '']
|
|
36
|
+
})"
|
|
37
|
+
>
|
|
38
|
+
<span v-if="options.pageOptions">{{ pageBetween }} จากทั้งหมด {{ totalCountWithComma }}</span>
|
|
39
|
+
<span v-else>ทั้งหมด {{ options.rawData.length }} รายการ</span>
|
|
40
|
+
</p>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<UPagination
|
|
44
|
+
v-if="options.pageOptions && options.pageOptions.totalPage > 1"
|
|
45
|
+
:page="page"
|
|
46
|
+
:default-page="options.pageOptions?.currentPage || 1"
|
|
47
|
+
:items-per-page="options.pageOptions.limit"
|
|
48
|
+
:total="options.pageOptions.totalCount"
|
|
49
|
+
:to="options.isRouteChange ? to : void 0"
|
|
50
|
+
show-edges
|
|
51
|
+
variant="outline"
|
|
52
|
+
color="neutral"
|
|
53
|
+
active-color="neutral"
|
|
54
|
+
active-variant="subtle"
|
|
55
|
+
@update:page="emits('pageChange', $event)"
|
|
56
|
+
/>
|
|
57
|
+
</div>
|
|
58
|
+
</template>
|
|
59
|
+
|
|
60
|
+
<script setup>
|
|
61
|
+
import { computed, NumberHelper, useUiConfig } from "#imports";
|
|
62
|
+
import UPagination from "#ui/components/Pagination";
|
|
63
|
+
import USelect from "#ui/components/Select";
|
|
64
|
+
import { tableTheme } from "../../theme/table";
|
|
65
|
+
const emits = defineEmits(["pageChange", "search", "pageLimitChange"]);
|
|
66
|
+
const props = defineProps({
|
|
67
|
+
page: { type: Number, required: true },
|
|
68
|
+
pageLimit: { type: Number, required: true },
|
|
69
|
+
options: { type: Object, required: true },
|
|
70
|
+
ui: { type: null, required: false }
|
|
71
|
+
});
|
|
72
|
+
const pageLimitItems = [
|
|
73
|
+
{
|
|
74
|
+
label: "10",
|
|
75
|
+
value: 10
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
label: "20",
|
|
79
|
+
value: 20
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
label: "30",
|
|
83
|
+
value: 30
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
label: "50",
|
|
87
|
+
value: 50
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
label: "100",
|
|
91
|
+
value: 100
|
|
92
|
+
}
|
|
93
|
+
];
|
|
94
|
+
const theme = computed(() => useUiConfig(tableTheme, "table")());
|
|
95
|
+
const pageBetween = computed(() => {
|
|
96
|
+
const rawDataLength = props.options.rawData?.length;
|
|
97
|
+
const pageOpts = props.options.pageOptions;
|
|
98
|
+
if (!rawDataLength || !pageOpts) {
|
|
99
|
+
return "0";
|
|
100
|
+
}
|
|
101
|
+
const currentPage = pageOpts.currentPage || 1;
|
|
102
|
+
const limit = pageOpts.limit;
|
|
103
|
+
if (limit <= 0) {
|
|
104
|
+
return "0";
|
|
105
|
+
}
|
|
106
|
+
const start = (currentPage - 1) * limit + 1;
|
|
107
|
+
const end = start + rawDataLength - 1;
|
|
108
|
+
return `${start} - ${end}`;
|
|
109
|
+
});
|
|
110
|
+
const totalCountWithComma = computed(() => {
|
|
111
|
+
const total = props.options.pageOptions?.totalCount;
|
|
112
|
+
if (!total || total <= 0) {
|
|
113
|
+
return "0";
|
|
114
|
+
}
|
|
115
|
+
return NumberHelper.withComma(total);
|
|
116
|
+
});
|
|
117
|
+
const to = (page) => {
|
|
118
|
+
const params = props.options.pageOptions?.request?.params || {};
|
|
119
|
+
const cleanParams = Object.fromEntries(
|
|
120
|
+
Object.entries(params).filter(
|
|
121
|
+
([key, value]) => value !== null && value !== void 0 && value !== ""
|
|
122
|
+
)
|
|
123
|
+
);
|
|
124
|
+
return {
|
|
125
|
+
query: {
|
|
126
|
+
...cleanParams,
|
|
127
|
+
page
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
</script>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ISimpleTableOptions, type ITableOptions } from '#imports';
|
|
2
|
+
import { tableTheme } from '../../theme/table.js';
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
page: number;
|
|
5
|
+
pageLimit: number;
|
|
6
|
+
options: ITableOptions<any> | ISimpleTableOptions<any>;
|
|
7
|
+
ui?: typeof tableTheme['slots'];
|
|
8
|
+
};
|
|
9
|
+
declare const _default: import("vue").DefineComponent<__VLS_Props, void, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
10
|
+
search: () => any;
|
|
11
|
+
pageChange: (page: number) => any;
|
|
12
|
+
pageLimitChange: (limit: number) => any;
|
|
13
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
14
|
+
onSearch?: (() => any) | undefined;
|
|
15
|
+
onPageChange?: ((page: number) => any) | undefined;
|
|
16
|
+
onPageLimitChange?: ((limit: number) => any) | undefined;
|
|
17
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
18
|
+
export default _default;
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<Base
|
|
3
|
-
v-bind="$attrs"
|
|
4
|
-
:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
:
|
|
10
|
-
@page-change="onPageChange"
|
|
11
|
-
>
|
|
12
|
-
<template
|
|
13
|
-
v-for="(_, slot) of $slots"
|
|
14
|
-
#[slot]="slotProps"
|
|
15
|
-
>
|
|
16
|
-
<slot
|
|
17
|
-
:name="slot"
|
|
18
|
-
v-bind="slotProps || {}"
|
|
19
|
-
/>
|
|
20
|
-
</template>
|
|
21
|
-
</Base>
|
|
2
|
+
<Base
|
|
3
|
+
v-bind="$attrs"
|
|
4
|
+
:options="{
|
|
5
|
+
...options,
|
|
6
|
+
pageOptions,
|
|
7
|
+
isHideLimitSelect: true
|
|
8
|
+
}"
|
|
9
|
+
:raw-data="itemsByPage"
|
|
10
|
+
@page-change="onPageChange"
|
|
11
|
+
>
|
|
12
|
+
<template
|
|
13
|
+
v-for="(_, slot) of $slots"
|
|
14
|
+
#[slot]="slotProps"
|
|
15
|
+
>
|
|
16
|
+
<slot
|
|
17
|
+
:name="slot"
|
|
18
|
+
v-bind="slotProps || {}"
|
|
19
|
+
/>
|
|
20
|
+
</template>
|
|
21
|
+
</Base>
|
|
22
22
|
</template>
|
|
23
23
|
|
|
24
24
|
<script setup>
|
|
@@ -27,10 +27,8 @@ import Base from "#core/components/Table/Base.vue";
|
|
|
27
27
|
import { initPageOptions } from "#core/composables/loaderPage";
|
|
28
28
|
import { useCoreConfig } from "#core/composables/useConfig";
|
|
29
29
|
const props = defineProps({
|
|
30
|
-
options: {
|
|
31
|
-
|
|
32
|
-
required: true
|
|
33
|
-
}
|
|
30
|
+
options: { type: Object, required: true },
|
|
31
|
+
ui: { type: null, required: false }
|
|
34
32
|
});
|
|
35
33
|
defineSlots();
|
|
36
34
|
const currentPage = ref(1);
|
|
@@ -1,20 +1,13 @@
|
|
|
1
|
-
import { type PropType } from 'vue';
|
|
2
1
|
import type { ISimpleTableOptions } from '#core/components/Table/types';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
options: ISimpleTableOptions<any>;
|
|
4
|
+
ui?: any;
|
|
5
|
+
};
|
|
3
6
|
type __VLS_Slots = {
|
|
4
7
|
'empty-state': () => any;
|
|
5
8
|
'loading-state': () => any;
|
|
6
9
|
};
|
|
7
|
-
declare const __VLS_component: import("vue").DefineComponent<import("vue").
|
|
8
|
-
options: {
|
|
9
|
-
type: PropType<ISimpleTableOptions<any>>;
|
|
10
|
-
required: true;
|
|
11
|
-
};
|
|
12
|
-
}>, void, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
13
|
-
options: {
|
|
14
|
-
type: PropType<ISimpleTableOptions<any>>;
|
|
15
|
-
required: true;
|
|
16
|
-
};
|
|
17
|
-
}>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
10
|
+
declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, void, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
18
11
|
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
19
12
|
export default _default;
|
|
20
13
|
type __VLS_WithSlots<T, S> = T & {
|
|
@@ -12,172 +12,45 @@
|
|
|
12
12
|
:placeholder="options.searchPlaceholder || '\u0E04\u0E49\u0E19\u0E2B\u0E32....'"
|
|
13
13
|
/>
|
|
14
14
|
</div>
|
|
15
|
-
<
|
|
15
|
+
<Base
|
|
16
16
|
v-bind="$attrs"
|
|
17
|
-
:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
:ui="ui"
|
|
17
|
+
:options="options"
|
|
18
|
+
@page-change="onPageChange"
|
|
19
|
+
@search="emits('search', q)"
|
|
21
20
|
>
|
|
22
|
-
<template #empty>
|
|
23
|
-
<slot
|
|
24
|
-
v-if="options.status.isLoading"
|
|
25
|
-
name="loading"
|
|
26
|
-
>
|
|
27
|
-
<Loader
|
|
28
|
-
:loading="true"
|
|
29
|
-
/>
|
|
30
|
-
</slot>
|
|
31
|
-
<slot
|
|
32
|
-
v-else-if="options.status.isError"
|
|
33
|
-
name="error"
|
|
34
|
-
>
|
|
35
|
-
<div
|
|
36
|
-
class="
|
|
37
|
-
text-error-400 flex h-[200px] items-center justify-center text-2xl
|
|
38
|
-
"
|
|
39
|
-
>
|
|
40
|
-
{{ StringHelper.getError(options.status.errorData) }}
|
|
41
|
-
</div>
|
|
42
|
-
</slot>
|
|
43
|
-
|
|
44
|
-
<slot
|
|
45
|
-
v-else
|
|
46
|
-
name="error"
|
|
47
|
-
>
|
|
48
|
-
<Empty />
|
|
49
|
-
</slot>
|
|
50
|
-
</template>
|
|
51
|
-
<template
|
|
52
|
-
v-for="column in options.columns.filter((item) => !!item.type)"
|
|
53
|
-
#[`${column.accessorKey}-cell`]="{ row }"
|
|
54
|
-
:key="column.accessorKey"
|
|
55
|
-
>
|
|
56
|
-
<component
|
|
57
|
-
:is="column.type === COLUMN_TYPES.COMPONENT ? column.component : columnTypeComponents[column.type]"
|
|
58
|
-
v-if="column.type === COLUMN_TYPES.COMPONENT || columnTypeComponents[column.type]"
|
|
59
|
-
:value="transformValue(column, row)"
|
|
60
|
-
:column="column"
|
|
61
|
-
:row="row"
|
|
62
|
-
/>
|
|
63
|
-
</template>
|
|
64
21
|
<template
|
|
65
|
-
v-for="(_,
|
|
66
|
-
#[
|
|
22
|
+
v-for="(_, slot) of $slots"
|
|
23
|
+
#[slot]="slotProps"
|
|
67
24
|
>
|
|
68
25
|
<slot
|
|
69
|
-
:name="
|
|
26
|
+
:name="slot"
|
|
70
27
|
v-bind="slotProps || {}"
|
|
71
28
|
/>
|
|
72
29
|
</template>
|
|
73
|
-
</
|
|
74
|
-
|
|
75
|
-
<div
|
|
76
|
-
v-if="!options.isHidePagination"
|
|
77
|
-
:class="theme.paginationContainer({
|
|
78
|
-
class: [ui?.paginationContainer]
|
|
79
|
-
})"
|
|
80
|
-
>
|
|
81
|
-
<p
|
|
82
|
-
:class="theme.paginationInfo({
|
|
83
|
-
class: [ui?.paginationInfo]
|
|
84
|
-
})"
|
|
85
|
-
>
|
|
86
|
-
{{ pageBetween }} รายการ จากทั้งหมด {{ totalCountWithComma }} รายการ
|
|
87
|
-
</p>
|
|
88
|
-
<Pagination
|
|
89
|
-
v-if="options.pageOptions.totalPage > 1"
|
|
90
|
-
v-model:page="page"
|
|
91
|
-
:default-page="options.pageOptions?.currentPage || 1"
|
|
92
|
-
:items-per-page="options.pageOptions.limit"
|
|
93
|
-
:total="options.pageOptions.totalCount"
|
|
94
|
-
:to="options.isRouteChange ? to : void 0"
|
|
95
|
-
@update:page="onPageChange"
|
|
96
|
-
/>
|
|
97
|
-
</div>
|
|
30
|
+
</Base>
|
|
98
31
|
</div>
|
|
99
32
|
</template>
|
|
100
33
|
|
|
101
34
|
<script setup>
|
|
102
35
|
import { computed } from "vue";
|
|
103
|
-
import {
|
|
104
|
-
import { _debounce, ref, useUiConfig, watch, useWatchChange, useRouter } from "#imports";
|
|
105
|
-
import { NumberHelper } from "#core/utils/NumberHelper";
|
|
106
|
-
import ColumnDate from "#core/components/Table/ColumnDate.vue";
|
|
107
|
-
import ColumnDateTime from "#core/components/Table/ColumnDateTime.vue";
|
|
108
|
-
import ColumnImage from "#core/components/Table/ColumnImage.vue";
|
|
109
|
-
import ColumnText from "#core/components/Table/ColumnText.vue";
|
|
110
|
-
import ColumnNumber from "#core/components/Table/ColumnNumber.vue";
|
|
36
|
+
import { _debounce, ref, useUiConfig, watch } from "#imports";
|
|
111
37
|
import { tableTheme } from "#core/theme/table";
|
|
112
|
-
import
|
|
38
|
+
import Base from "#core/components/Table/Base.vue";
|
|
113
39
|
const emits = defineEmits(["pageChange", "search"]);
|
|
114
40
|
const props = defineProps({
|
|
115
41
|
options: { type: Object, required: true },
|
|
116
42
|
ui: { type: null, required: false }
|
|
117
43
|
});
|
|
118
44
|
defineSlots();
|
|
119
|
-
const columnTypeComponents = {
|
|
120
|
-
[COLUMN_TYPES.NUMBER]: ColumnNumber,
|
|
121
|
-
[COLUMN_TYPES.IMAGE]: ColumnImage,
|
|
122
|
-
[COLUMN_TYPES.DATE_TIME]: ColumnDateTime,
|
|
123
|
-
[COLUMN_TYPES.DATE]: ColumnDate,
|
|
124
|
-
[COLUMN_TYPES.TEXT]: ColumnText
|
|
125
|
-
};
|
|
126
|
-
const page = ref(props.options?.pageOptions?.currentPage || 1);
|
|
127
45
|
const tableContainer = ref();
|
|
128
|
-
const router = useRouter();
|
|
129
|
-
const theme = computed(() => useUiConfig(tableTheme, "table")());
|
|
130
46
|
const q = ref(props.options?.pageOptions.search ?? "");
|
|
131
|
-
const
|
|
132
|
-
const params = props.options?.pageOptions?.request?.params || {};
|
|
133
|
-
const cleanParams = Object.fromEntries(
|
|
134
|
-
Object.entries(params).filter(
|
|
135
|
-
([key, value]) => value !== null && value !== void 0 && value !== ""
|
|
136
|
-
)
|
|
137
|
-
);
|
|
138
|
-
return {
|
|
139
|
-
query: {
|
|
140
|
-
...cleanParams,
|
|
141
|
-
page: page2
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
};
|
|
47
|
+
const theme = computed(() => useUiConfig(tableTheme, "table")());
|
|
145
48
|
watch(
|
|
146
49
|
q,
|
|
147
50
|
_debounce((value) => {
|
|
148
51
|
emits("search", value);
|
|
149
52
|
}, 500)
|
|
150
53
|
);
|
|
151
|
-
useWatchChange(() => props.options?.pageOptions?.currentPage, (value) => {
|
|
152
|
-
page.value = value;
|
|
153
|
-
});
|
|
154
|
-
const pageBetween = computed(() => {
|
|
155
|
-
const rawDataLength = props.options.rawData?.length;
|
|
156
|
-
const pageOpts = props.options.pageOptions;
|
|
157
|
-
if (!rawDataLength || !pageOpts) {
|
|
158
|
-
return "0";
|
|
159
|
-
}
|
|
160
|
-
const currentPage = pageOpts.currentPage || 1;
|
|
161
|
-
const limit = pageOpts.limit;
|
|
162
|
-
if (limit <= 0) {
|
|
163
|
-
return "0";
|
|
164
|
-
}
|
|
165
|
-
const start = (currentPage - 1) * limit + 1;
|
|
166
|
-
const end = start + rawDataLength - 1;
|
|
167
|
-
return `${start} - ${end}`;
|
|
168
|
-
});
|
|
169
|
-
const totalCountWithComma = computed(() => {
|
|
170
|
-
const total = props.options.pageOptions?.totalCount;
|
|
171
|
-
if (!total || total <= 0) {
|
|
172
|
-
return "0";
|
|
173
|
-
}
|
|
174
|
-
return NumberHelper.withComma(total);
|
|
175
|
-
});
|
|
176
|
-
const transformValue = (column, row) => {
|
|
177
|
-
return column.cell ? column.cell({
|
|
178
|
-
row
|
|
179
|
-
}) : row.getValue(column.accessorKey);
|
|
180
|
-
};
|
|
181
54
|
const onPageChange = (newPage) => {
|
|
182
55
|
if (tableContainer.value) {
|
|
183
56
|
const rect = tableContainer.value.getBoundingClientRect();
|
|
@@ -20,6 +20,9 @@ export interface IBaseTableOptions<T extends Record<string, any> = Record<string
|
|
|
20
20
|
status: IStatus;
|
|
21
21
|
columns: TableColumn<T>[];
|
|
22
22
|
isHidePagination?: boolean;
|
|
23
|
+
isHidePaginationTop?: boolean;
|
|
24
|
+
isHidePaginationBottom?: boolean;
|
|
25
|
+
isHideLimitSelect?: boolean;
|
|
23
26
|
isHideCaption?: boolean;
|
|
24
27
|
}
|
|
25
28
|
export interface ITableOptions<T extends Record<string, any> = Record<string, any>> extends IBaseTableOptions<T> {
|
|
@@ -17,7 +17,7 @@ export const dialogTheme = {
|
|
|
17
17
|
"md:!top-[50%] md:!left-[50%] md:!bottom-auto md:!right-auto",
|
|
18
18
|
"md:w-[90vw] md:max-w-[500px]",
|
|
19
19
|
"md:translate-x-[-50%] md:translate-y-[-50%]",
|
|
20
|
-
"md
|
|
20
|
+
"md:!rounded-lg md:space-x-4"
|
|
21
21
|
],
|
|
22
22
|
overlay: "fixed inset-0 bg-black/50 backdrop-blur",
|
|
23
23
|
iconWrapper: "rounded-full size-[48px] flex justify-center items-center",
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export const paginationTheme = {
|
|
2
2
|
slots: {
|
|
3
|
-
first: "
|
|
4
|
-
prev: "
|
|
5
|
-
next: "
|
|
6
|
-
last: "
|
|
3
|
+
first: "hidden",
|
|
4
|
+
prev: "",
|
|
5
|
+
next: "",
|
|
6
|
+
last: "hidden",
|
|
7
|
+
item: "max-sm:!hidden",
|
|
8
|
+
ellipsis: "max-sm:!hidden"
|
|
7
9
|
}
|
|
8
10
|
};
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
export declare const tableTheme: {
|
|
2
2
|
slots: {
|
|
3
3
|
root: string;
|
|
4
|
+
rootWrapper: string;
|
|
4
5
|
searchContainer: string;
|
|
5
6
|
captionContainer: string;
|
|
6
7
|
captionBoldText: string;
|
|
7
8
|
errorMessage: string;
|
|
8
9
|
paginationContainer: string;
|
|
10
|
+
paginationInfoWrapper: string;
|
|
9
11
|
paginationInfo: string;
|
|
12
|
+
paginationLimitSelect: string;
|
|
13
|
+
paginationLimitSelectLabel: string;
|
|
10
14
|
thead: string;
|
|
11
15
|
th: string;
|
|
12
16
|
td: string;
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
export const tableTheme = {
|
|
2
2
|
slots: {
|
|
3
|
-
root: "
|
|
3
|
+
root: "bg-white",
|
|
4
|
+
rootWrapper: "rounded-t-lg rounded-b-lg border-1 border-[#EAECF0]",
|
|
4
5
|
searchContainer: "mb-4 flex justify-end",
|
|
5
6
|
captionContainer: "hidden mb-4 text-gray-500",
|
|
6
7
|
captionBoldText: "font-bold",
|
|
7
8
|
errorMessage: "text-error-400 text-2xl h-[200px] flex justify-center items-center",
|
|
8
|
-
paginationContainer: "
|
|
9
|
-
|
|
9
|
+
paginationContainer: "flex justify-between items-center flex-row md:flex-col lg:flex-row gap-4 px-4 py-3 bg-[#F2F4F7]",
|
|
10
|
+
paginationInfoWrapper: "flex items-center justify-between gap-4",
|
|
11
|
+
paginationInfo: "text-sm font-bold text-gray-600",
|
|
12
|
+
paginationLimitSelect: "max-sm:!hidden min-w-[120px]",
|
|
13
|
+
paginationLimitSelectLabel: "font-bold text-gray-600",
|
|
10
14
|
thead: "bg-primary",
|
|
11
|
-
th: "text-[#
|
|
12
|
-
td: "text-[#
|
|
15
|
+
th: "text-[#475467] bg-white whitespace-nowrap font-medium text-xs",
|
|
16
|
+
td: "text-[#475467]"
|
|
13
17
|
},
|
|
14
18
|
variants: {
|
|
15
19
|
pinned: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finema/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.50.0",
|
|
4
4
|
"repository": "https://gitlab.finema.co/finema/ui-kit",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Finema Dev Core Team",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
],
|
|
29
29
|
"scripts": {
|
|
30
30
|
"prepack": "nuxt-module-build build",
|
|
31
|
-
"dev": "nuxi dev playground -o",
|
|
31
|
+
"dev": "bun run dev:prepare && nuxi dev playground -o",
|
|
32
32
|
"dev:build": "nuxi build playground",
|
|
33
33
|
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
|
|
34
34
|
"release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@eslint/js": "^9.26.0",
|
|
75
|
-
"@finema/eslint-config": "^2.0.
|
|
75
|
+
"@finema/eslint-config": "^2.0.4",
|
|
76
76
|
"@nuxt/devtools": "^2.6.0",
|
|
77
77
|
"@nuxt/eslint-config": "^1.4.1",
|
|
78
78
|
"@nuxt/module-builder": "^1.0.2",
|