@vc-shell/create-vc-app 2.0.10-pr242.2e7f6b3 → 2.0.10-pr242.bfb451e

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.
@@ -14,7 +14,6 @@
14
14
  :pagination="{ currentPage, pages }"
15
15
  :searchable="true"
16
16
  :state-key="'<%- ModuleNameScreamingSnake %>'"
17
- @search="onSearch"
18
17
  @row-click="onRowClick"
19
18
  @pagination-click="onPaginationClick"
20
19
  >
@@ -26,7 +25,7 @@
26
25
  </template>
27
26
 
28
27
  <script setup lang="ts">
29
- import { useBlade, useDataTableSort, useTableQueryState, type IBladeToolbar } from "@vc-shell/framework";
28
+ import { useBlade, useDataTableSort, useTableQueryState, useFunctions, type IBladeToolbar } from "@vc-shell/framework";
30
29
  import { VcBlade, VcDataTable, VcColumn } from "@vc-shell/framework/ui";
31
30
  import { computed, ref, watch } from "vue";
32
31
  import use<%- ModuleNamePascalCase %>List from "../composables/useList";
@@ -45,6 +44,7 @@ defineBlade({
45
44
 
46
45
  const { t } = useI18n({ useScope: "global" });
47
46
  const { openBlade, exposeToChildren } = useBlade();
47
+ const { debounce } = useFunctions();
48
48
 
49
49
  const PAGE_SIZE = 20;
50
50
 
@@ -55,59 +55,39 @@ const { sortField, sortOrder, sortExpression } = useDataTableSort({
55
55
 
56
56
  const { data, loading, totalCount, getItems } = use<%- ModuleNamePascalCase %>List();
57
57
 
58
- const searchValue = ref<string>(); // live search box (v-model)
59
- const appliedKeyword = ref<string>(); // applied filter — drives the loader
58
+ const searchValue = ref<string>();
60
59
  const currentPage = ref(1);
61
-
62
60
  const pages = computed(() => Math.ceil(totalCount.value / PAGE_SIZE) || 0);
63
61
 
64
- // Pull-restore: read the URL-persisted view state and seed our refs synchronously,
65
- // BEFORE the loader watch below — so a reload costs exactly one request.
62
+ // Restore sort/search/page from the URL, then load once below.
66
63
  const restored = useTableQueryState("<%- ModuleNameScreamingSnake %>").read();
67
64
  if (restored.sort) {
68
65
  const [field, direction] = restored.sort.split(":");
69
66
  sortField.value = field;
70
67
  sortOrder.value = direction === "DESC" ? -1 : 1;
71
68
  }
72
- if (restored.search !== undefined) {
73
- searchValue.value = restored.search;
74
- appliedKeyword.value = restored.search;
75
- }
76
- if (restored.page) {
77
- currentPage.value = restored.page;
78
- }
69
+ if (restored.search) searchValue.value = restored.search;
70
+ if (restored.page) currentPage.value = restored.page;
79
71
 
80
- // Single loader — the ONLY trigger that calls the API. Vue batches synchronous
81
- // changes to several criteria into one flush → one request, even on restore.
82
- watch(
83
- () => ({
72
+ function load() {
73
+ return getItems({
84
74
  sort: sortExpression.value,
85
- keyword: appliedKeyword.value || undefined,
75
+ keyword: searchValue.value || undefined,
86
76
  skip: (currentPage.value - 1) * PAGE_SIZE,
87
77
  take: PAGE_SIZE,
88
- }),
89
- (query) => getItems(query),
90
- { immediate: true },
91
- );
92
-
93
- // Handlers only update state — they never load. VcDataTable debounces @search, so
94
- // reacting to appliedKeyword coalesces fast typing into a single request.
95
- const onSearch = (keyword: string) => {
96
- appliedKeyword.value = keyword;
97
- currentPage.value = 1;
98
- };
78
+ });
79
+ }
80
+
81
+ // One loader for sort/search/page. Debounced so typing doesn't fetch per keystroke.
82
+ load();
83
+ watch(searchValue, () => (currentPage.value = 1));
84
+ watch([sortExpression, searchValue, currentPage], debounce(load, 300));
99
85
 
100
86
  const onPaginationClick = (page: number) => {
101
87
  currentPage.value = page;
102
88
  };
103
89
 
104
- const reload = () =>
105
- getItems({
106
- sort: sortExpression.value,
107
- keyword: appliedKeyword.value || undefined,
108
- skip: (currentPage.value - 1) * PAGE_SIZE,
109
- take: PAGE_SIZE,
110
- });
90
+ const reload = () => load();
111
91
 
112
92
  const bladeToolbar = ref<IBladeToolbar[]>([
113
93
  {
@@ -34,7 +34,6 @@
34
34
  :total-label="$t('SAMPLE_APP.PAGES.LIST.TABLE.TOTALS')"
35
35
  :total-count="totalCount"
36
36
  state-key="SAMPLE_APP"
37
- @search="onSearchList"
38
37
  @row-click="onItemClick"
39
38
  @pagination-click="onPaginationClick"
40
39
  >
@@ -76,7 +75,7 @@
76
75
 
77
76
  <script lang="ts" setup>
78
77
  import { computed, ref, watch } from "vue";
79
- import { IBladeToolbar, useBlade, usePopup, useDataTableSort, useTableQueryState } from "@vc-shell/framework";
78
+ import { IBladeToolbar, useBlade, usePopup, useDataTableSort, useTableQueryState, useFunctions } from "@vc-shell/framework";
80
79
  import type { TableAction } from "@vc-shell/framework";
81
80
  import { VcColumn, VcDataTable, VcBlade } from "@vc-shell/framework/ui";
82
81
  import { useI18n } from "vue-i18n";
@@ -97,6 +96,7 @@ defineBlade({
97
96
  const { t } = useI18n({ useScope: "global" });
98
97
  const { param, openBlade, exposeToChildren } = useBlade();
99
98
  const { showConfirmation } = usePopup();
99
+ const { debounce } = useFunctions();
100
100
 
101
101
  const PAGE_SIZE = 20;
102
102
 
@@ -110,30 +110,22 @@ const { getItems, removeItems, data, loading, totalCount, pages } = useList({
110
110
  pageSize: PAGE_SIZE,
111
111
  });
112
112
 
113
- const searchValue = ref<string>(); // live search box (v-model)
114
- const appliedKeyword = ref<string>(); // applied filter — drives the loader
113
+ const searchValue = ref<string>();
115
114
  const currentPage = ref(1);
116
115
  const selectedItemId = ref<string>();
117
116
  const selectedItems = ref<MockedItem[]>([]);
118
117
 
119
118
  const selectedIds = computed(() => selectedItems.value.map((item) => item.id).filter(Boolean) as string[]);
120
119
 
121
- // Pull-restore: read the URL-persisted view state and seed our refs synchronously,
122
- // BEFORE the loader watch below — so a page reload costs exactly one request with
123
- // the full criteria, instead of several uncoordinated loads.
120
+ // Restore sort/search/page from the URL, then load once below.
124
121
  const restored = useTableQueryState("SAMPLE_APP").read();
125
122
  if (restored.sort) {
126
123
  const [field, direction] = restored.sort.split(":");
127
124
  sortField.value = field;
128
125
  sortOrder.value = direction === "DESC" ? -1 : 1;
129
126
  }
130
- if (restored.search !== undefined) {
131
- searchValue.value = restored.search;
132
- appliedKeyword.value = restored.search;
133
- }
134
- if (restored.page) {
135
- currentPage.value = restored.page;
136
- }
127
+ if (restored.search) searchValue.value = restored.search;
128
+ if (restored.page) currentPage.value = restored.page;
137
129
 
138
130
  watch(
139
131
  param,
@@ -143,36 +135,25 @@ watch(
143
135
  { immediate: true },
144
136
  );
145
137
 
146
- // Single loader — the ONLY place that calls the API. Vue batches synchronous changes
147
- // to several criteria into one flush, so sort + search + page changing together still
148
- // yields one request.
149
- watch(
150
- () => ({
138
+ function load() {
139
+ return getItems({
151
140
  sort: sortExpression.value,
152
- keyword: appliedKeyword.value || undefined,
141
+ keyword: searchValue.value || undefined,
153
142
  skip: (currentPage.value - 1) * PAGE_SIZE,
154
- }),
155
- (query) => getItems(query),
156
- { immediate: true },
157
- );
143
+ });
144
+ }
158
145
 
159
- // Handlers only update state they never load. VcDataTable debounces @search, so
160
- // reacting to appliedKeyword coalesces fast typing into a single request.
161
- const onSearchList = (keyword: string) => {
162
- appliedKeyword.value = keyword;
163
- currentPage.value = 1;
164
- };
146
+ // One loader for sort/search/page. Debounced so typing doesn't fetch per keystroke.
147
+ load();
148
+ watch(searchValue, () => (currentPage.value = 1));
149
+ watch([sortExpression, searchValue, currentPage], debounce(load, 300));
165
150
 
166
151
  const clearSearch = () => {
167
152
  searchValue.value = "";
168
- appliedKeyword.value = "";
169
- currentPage.value = 1;
170
153
  };
171
154
 
172
155
  const addItem = () => {
173
- openBlade({
174
- name: "SampleDetails",
175
- });
156
+ openBlade({ name: "SampleDetails" });
176
157
  };
177
158
 
178
159
  const onPaginationClick = (page: number) => {
@@ -203,13 +184,7 @@ const title = computed(() => t("SAMPLE_APP.PAGES.LIST.TITLE"));
203
184
 
204
185
  const reload = async () => {
205
186
  selectedItems.value = [];
206
- // Explicit user refresh — the criteria are unchanged, so the loader watch would
207
- // not fire; reload calls the API directly with the current combined criteria.
208
- await getItems({
209
- sort: sortExpression.value,
210
- keyword: appliedKeyword.value || undefined,
211
- skip: (currentPage.value - 1) * PAGE_SIZE,
212
- });
187
+ await load();
213
188
  };
214
189
 
215
190
  const onItemClick = (event: { data: MockedItem; index: number; originalEvent: Event }) => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vc-shell/create-vc-app",
3
3
  "description": "Application scaffolding",
4
- "version": "2.0.10-pr242.2e7f6b3",
4
+ "version": "2.0.10-pr242.bfb451e",
5
5
  "type": "module",
6
6
  "bin": "./dist/index.js",
7
7
  "files": [
@@ -16,7 +16,7 @@
16
16
  "devDependencies": {
17
17
  "@types/ejs": "^3.1.5",
18
18
  "@types/prompts": "^2.4.4",
19
- "@vc-shell/ts-config": "2.0.10-pr242.2e7f6b3",
19
+ "@vc-shell/ts-config": "2.0.10-pr242.bfb451e",
20
20
  "copyfiles": "^2.4.1",
21
21
  "cross-env": "^7.0.3",
22
22
  "shx": "^0.3.4",