@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finema/core",
3
- "version": "2.49.0",
3
+ "version": "2.50.0",
4
4
  "configKey": "core",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
package/dist/module.mjs CHANGED
@@ -4,7 +4,7 @@ import * as lodash from 'lodash-es';
4
4
  import * as theme from '../dist/runtime/theme/index.js';
5
5
 
6
6
  const name = "@finema/core";
7
- const version = "2.49.0";
7
+ const version = "2.50.0";
8
8
 
9
9
  const nuxtAppOptions = {
10
10
  head: {
@@ -1,114 +1,114 @@
1
1
  <template>
2
- <div
3
- v-if="!isHideCaption"
4
- class="mb-4 text-gray-500"
5
- >
6
- <span class="font-bold">ผลลัพธ์ทั้งหมด:</span>
7
- จำนวน
8
- <span class="font-bold">{{ pageOptions?.totalCount || rawData.length || 0 }}</span>
9
- รายการ
10
- </div>
11
- <UTable
12
- :loading="status.isLoading"
13
- :columns="uTableCompatibleColumns"
14
- :data="rawData"
15
- v-bind="$attrs"
16
- >
17
- <template #loading-state>
18
- <div class="flex h-60 items-center justify-center">
19
- <Icon
20
- name="i-svg-spinners:180-ring-with-bg"
21
- class="text-primary size-8"
22
- />
23
- </div>
24
- </template>
25
- <template #empty-state>
26
- <Empty />
27
- </template>
28
- <template
29
- v-for="column in columns.filter((item) => !!item.type)"
30
- #[`${column.accessorKey}-cell`]="{ row }"
31
- :key="column.accessorKey"
32
- >
33
- <component
34
- :is="column.type === COLUMN_TYPES.COMPONENT ? column.component : columnTypeComponents[column.type]"
35
- v-if="column.type === COLUMN_TYPES.COMPONENT || columnTypeComponents[column.type]"
36
- :value="transformValue(column, row)"
37
- :column="column"
38
- :row="row"
39
- />
40
- </template>
41
- <template
42
- v-for="(_, slotName) of $slots"
43
- #[slotName]="slotProps"
44
- >
45
- <slot
46
- :name="slotName"
47
- v-bind="slotProps || {}"
48
- />
49
- </template>
50
- </UTable>
51
- <div
52
- v-if="!isHidePagination && pageOptions"
53
- :class="theme.paginationContainer({})"
54
- >
55
- <p
56
- :class="theme.paginationInfo({})"
57
- >
58
- {{ pageBetween }} รายการ จากทั้งหมด {{ totalCountWithComma }} รายการ
59
- </p>
60
- <Pagination
61
- v-if="pageOptions.totalPage > 1"
62
- v-model:page="page"
63
- :default-page="pageOptions?.currentPage || 1"
64
- :items-per-page="pageOptions.limit"
65
- :total="pageOptions.totalCount"
66
- @update:page="onPageChange"
67
- />
68
- </div>
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 { NumberHelper } from "#core/utils/NumberHelper";
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
- status: {
88
- type: Object,
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 pageBetween = computed(() => {
136
- const length = props.rawData?.length;
137
- if (length === 0) {
138
- return "0";
139
- }
140
- const start = (props.pageOptions.currentPage - 1) * props.pageOptions.limit + 1;
141
- const end = start + length - 1;
142
- return `${start} - ${end}`;
143
- });
144
- const transformValue = (column, row) => {
145
- return column.cell ? column.cell({
146
- row
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 PropType } from 'vue';
2
- import { type ITableOptions } from '#core/components/Table/types';
3
- declare var __VLS_25: string | number, __VLS_26: any;
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 __VLS_25>]?: (props: typeof __VLS_26) => any;
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").ExtractPropTypes<{
8
- status: {
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<import("vue").ExtractPropTypes<{
35
- status: {
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
- :columns="options.columns"
5
- :raw-data="itemsByPage"
6
- :status="options.status"
7
- :page-options="pageOptions"
8
- :is-hide-pagination="options.isHidePagination"
9
- :is-hide-caption="options.isHideCaption"
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
- type: Object,
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").ExtractPropTypes<{
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
- <UTable
15
+ <Base
16
16
  v-bind="$attrs"
17
- :loading="options.status.isLoading"
18
- :data="options.rawData"
19
- :columns="options.columns"
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="(_, slotName) of $slots"
66
- #[slotName]="slotProps"
22
+ v-for="(_, slot) of $slots"
23
+ #[slot]="slotProps"
67
24
  >
68
25
  <slot
69
- :name="slotName"
26
+ :name="slot"
70
27
  v-bind="slotProps || {}"
71
28
  />
72
29
  </template>
73
- </UTable>
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 { COLUMN_TYPES } from "#core/components/Table/types";
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 UTable from "#ui/components/Table";
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 to = (page2) => {
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:rounded-lg md:space-x-4"
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",
@@ -4,5 +4,7 @@ export declare const paginationTheme: {
4
4
  prev: string;
5
5
  next: string;
6
6
  last: string;
7
+ item: string;
8
+ ellipsis: string;
7
9
  };
8
10
  };
@@ -1,8 +1,10 @@
1
1
  export const paginationTheme = {
2
2
  slots: {
3
- first: "disabled:hidden",
4
- prev: "disabled:hidden",
5
- next: "disabled:hidden",
6
- last: "disabled:hidden"
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: "rounded-t-md rounded-b-md bg-white",
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: "mt-4 flex justify-between px-3 items-center flex-col lg:flex-row gap-4",
9
- paginationInfo: "text-sm text-gray-600",
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-[#222222] bg-white whitespace-nowrap",
12
- td: "text-[#222222]"
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.49.0",
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.3",
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",