@vc-shell/vc-app-skill 2.0.10-pr247.f47cc74 → 2.0.11
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/package.json +1 -1
- package/runtime/VERSION +1 -1
- package/runtime/knowledge/docs/_BUILD_HASH.md +1 -1
- package/runtime/knowledge/docs/core/blade-navigation/table-query-state/useTableQueryState.docs.md +95 -0
- package/runtime/knowledge/docs/core/composables/useAppInsights/useAppInsights.docs.md +1 -1
- package/runtime/knowledge/docs/core/composables/useBlade/useBlade.docs.md +34 -9
- package/runtime/knowledge/docs/core/composables/usePlatformLocaleSync/usePlatformLocaleSync.docs.md +34 -0
- package/runtime/knowledge/docs/core/composables/useResponsive/useResponsive.docs.md +1 -1
- package/runtime/knowledge/docs/core/composables/useSlowNetworkDetection/useSlowNetworkDetection.docs.md +1 -1
- package/runtime/knowledge/docs/core/composables/useUser/useUser.docs.md +1 -1
- package/runtime/knowledge/docs/core/notifications/composables/useBladeNotifications.docs.md +184 -0
- package/runtime/knowledge/docs/core/notifications/composables/useBroadcastFilter.docs.md +117 -0
- package/runtime/knowledge/docs/core/notifications/composables/useNotificationContext.docs.md +150 -0
- package/runtime/knowledge/docs/core/notifications/composables/useNotificationStore.docs.md +113 -0
- package/runtime/knowledge/docs/core/notifications/notifications.docs.md +1 -1
- package/runtime/knowledge/docs/modules/assets/assets-details.docs.md +123 -0
- package/runtime/knowledge/docs/modules/assets-manager/assets-manager.docs.md +1 -1
- package/runtime/knowledge/docs/shell/dashboard/draggable-dashboard/dashboard-widget-skeleton.docs.md +33 -0
- package/runtime/knowledge/docs/ui/components/organisms/vc-data-table/vc-data-table.docs.md +67 -10
- package/runtime/knowledge/docs/ui/composables/useDataTablePagination.docs.md +44 -23
- package/runtime/knowledge/docs/ui/composables/useDataTableSort.docs.md +16 -8
|
@@ -29,7 +29,6 @@ import { useDataTablePagination } from "@vc-shell/framework";
|
|
|
29
29
|
const pagination = useDataTablePagination({
|
|
30
30
|
pageSize: 20,
|
|
31
31
|
totalCount: computed(() => searchResult.value?.totalCount ?? 0),
|
|
32
|
-
onPageChange: ({ skip }) => loadItems({ ...searchQuery.value, skip }),
|
|
33
32
|
});
|
|
34
33
|
```
|
|
35
34
|
|
|
@@ -37,6 +36,8 @@ const pagination = useDataTablePagination({
|
|
|
37
36
|
<VcDataTable :items="items" :total-count="pagination.totalCount" :pagination="pagination" @pagination-click="pagination.goToPage" />
|
|
38
37
|
```
|
|
39
38
|
|
|
39
|
+
> **Note:** Let `@pagination-click` only update the page (`goToPage`) and load from one watcher that reads `pagination.skip` together with sort and search — see the recipe below. `onPageChange` (load on each page change) still works for simple tables, but do not combine it with that watcher or the page loads twice.
|
|
40
|
+
|
|
40
41
|
## API
|
|
41
42
|
|
|
42
43
|
### Parameters (Options)
|
|
@@ -49,15 +50,16 @@ const pagination = useDataTablePagination({
|
|
|
49
50
|
|
|
50
51
|
### Returns (`reactive()` object)
|
|
51
52
|
|
|
52
|
-
| Property | Type | Description
|
|
53
|
-
| ------------- | ------------------------ |
|
|
54
|
-
| `currentPage` | `number` | Current 1-based page number (writable)
|
|
55
|
-
| `pages` | `number` (readonly) | Total number of pages
|
|
56
|
-
| `skip` | `number` (readonly) | Current skip offset for API calls
|
|
57
|
-
| `pageSize` | `number` (readonly) | Resolved page size
|
|
58
|
-
| `totalCount` | `number` (readonly) | Resolved total item count
|
|
59
|
-
| `goToPage` | `(page: number) => void` | Navigate to page; fires `onPageChange`
|
|
60
|
-
| `
|
|
53
|
+
| Property | Type | Description |
|
|
54
|
+
| ------------- | ------------------------ | ----------------------------------------------------------------------- |
|
|
55
|
+
| `currentPage` | `number` | Current 1-based page number (writable) |
|
|
56
|
+
| `pages` | `number` (readonly) | Total number of pages |
|
|
57
|
+
| `skip` | `number` (readonly) | Current skip offset for API calls |
|
|
58
|
+
| `pageSize` | `number` (readonly) | Resolved page size |
|
|
59
|
+
| `totalCount` | `number` (readonly) | Resolved total item count |
|
|
60
|
+
| `goToPage` | `(page: number) => void` | Navigate to page; fires `onPageChange` |
|
|
61
|
+
| `setPage` | `(page: number) => void` | Seed the page WITHOUT firing `onPageChange` — use for pull-restore seed |
|
|
62
|
+
| `reset` | `() => void` | Reset to page 1; does NOT fire `onPageChange` |
|
|
61
63
|
|
|
62
64
|
All properties are auto-unwrapped by `reactive()` — no `.value` access needed in script or template.
|
|
63
65
|
|
|
@@ -65,30 +67,43 @@ All properties are auto-unwrapped by `reactive()` — no `.value` access needed
|
|
|
65
67
|
|
|
66
68
|
```vue
|
|
67
69
|
<script setup lang="ts">
|
|
68
|
-
import { ref, computed } from "vue";
|
|
69
|
-
import { useDataTablePagination, useDataTableSort } from "@vc-shell/framework";
|
|
70
|
+
import { ref, computed, watch } from "vue";
|
|
71
|
+
import { useDataTablePagination, useDataTableSort, useTableQueryState } from "@vc-shell/framework";
|
|
70
72
|
|
|
71
73
|
// Data layer
|
|
72
74
|
const searchResult = ref<{ results: Item[]; totalCount: number }>();
|
|
73
|
-
const searchQuery = ref({ take: 20, skip: 0 });
|
|
74
75
|
|
|
75
|
-
async function loadItems(query?:
|
|
76
|
-
|
|
77
|
-
searchResult.value = await api.search(searchQuery.value);
|
|
76
|
+
async function loadItems(query: { sort?: string; keyword?: string; skip?: number }) {
|
|
77
|
+
searchResult.value = await api.search({ take: 20, ...query });
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
//
|
|
80
|
+
// State only — no self-loading callback.
|
|
81
81
|
const pagination = useDataTablePagination({
|
|
82
82
|
pageSize: 20,
|
|
83
83
|
totalCount: computed(() => searchResult.value?.totalCount ?? 0),
|
|
84
|
-
onPageChange: ({ skip }) => loadItems({ skip }),
|
|
85
84
|
});
|
|
86
|
-
|
|
87
|
-
// Sort state
|
|
88
85
|
const { sortField, sortOrder, sortExpression } = useDataTableSort({
|
|
89
86
|
initialField: "createdDate",
|
|
90
87
|
initialDirection: "DESC",
|
|
91
88
|
});
|
|
89
|
+
const searchValue = ref<string>();
|
|
90
|
+
|
|
91
|
+
// Restore from the URL, then load once. setPage seeds the page without loading.
|
|
92
|
+
const restored = useTableQueryState("product-list").read();
|
|
93
|
+
if (restored.sort) {
|
|
94
|
+
const [field, dir] = restored.sort.split(":");
|
|
95
|
+
sortField.value = field;
|
|
96
|
+
sortOrder.value = dir === "DESC" ? -1 : 1;
|
|
97
|
+
}
|
|
98
|
+
if (restored.search) searchValue.value = restored.search;
|
|
99
|
+
if (restored.page) pagination.setPage(restored.page);
|
|
100
|
+
|
|
101
|
+
// One loader reading sort + search + page.
|
|
102
|
+
watch(
|
|
103
|
+
() => ({ sort: sortExpression.value, keyword: searchValue.value || undefined, skip: pagination.skip }),
|
|
104
|
+
(query) => loadItems(query),
|
|
105
|
+
{ immediate: true },
|
|
106
|
+
);
|
|
92
107
|
</script>
|
|
93
108
|
|
|
94
109
|
<template>
|
|
@@ -97,6 +112,12 @@ const { sortField, sortOrder, sortExpression } = useDataTableSort({
|
|
|
97
112
|
:total-count="pagination.totalCount"
|
|
98
113
|
:pagination="pagination"
|
|
99
114
|
@pagination-click="pagination.goToPage"
|
|
115
|
+
@search="
|
|
116
|
+
(kw) => {
|
|
117
|
+
searchValue = kw;
|
|
118
|
+
pagination.setPage(1);
|
|
119
|
+
}
|
|
120
|
+
"
|
|
100
121
|
v-model:sort-field="sortField"
|
|
101
122
|
v-model:sort-order="sortOrder"
|
|
102
123
|
>
|
|
@@ -130,7 +151,6 @@ export function useOffers() {
|
|
|
130
151
|
const pagination = useDataTablePagination({
|
|
131
152
|
pageSize: 20,
|
|
132
153
|
totalCount: computed(() => searchResult.value?.totalCount ?? 0),
|
|
133
|
-
onPageChange: ({ skip }) => loadOffers({ ...searchQuery.value, skip }),
|
|
134
154
|
});
|
|
135
155
|
|
|
136
156
|
return {
|
|
@@ -141,7 +161,7 @@ export function useOffers() {
|
|
|
141
161
|
}
|
|
142
162
|
```
|
|
143
163
|
|
|
144
|
-
|
|
164
|
+
The blade owns the single loader watch (reading `pagination.skip` with sort/search) and binds:
|
|
145
165
|
|
|
146
166
|
```vue
|
|
147
167
|
<VcDataTable :total-count="pagination.totalCount" :pagination="pagination" @pagination-click="pagination.goToPage" />
|
|
@@ -150,7 +170,8 @@ Blade then simply binds:
|
|
|
150
170
|
## Details
|
|
151
171
|
|
|
152
172
|
- **`reactive()` return**: The composable wraps its return in `reactive()`, so all `Ref`/`ComputedRef` properties are auto-unwrapped. Access with `pagination.pages` (not `pagination.pages.value`). This allows the object to be passed directly as `:pagination` prop to VcDataTable without intermediate conversion.
|
|
153
|
-
- **Event callback, not load**: `onPageChange` is an event notification (like VueUse's `useOffsetPagination`). The composable does not
|
|
173
|
+
- **Event callback, not load**: `onPageChange` is an event notification (like VueUse's `useOffsetPagination`). The composable does not fetch data. With a single loader watcher you usually omit it and read `pagination.skip` from the watcher instead.
|
|
174
|
+
- **`setPage` vs `goToPage`**: `goToPage(n)` updates the page and fires `onPageChange`; `setPage(n)` updates it without the callback. Use `setPage` to seed the page from a URL restore so the seed itself does not load.
|
|
154
175
|
- **No auto-clamp**: When `totalCount` decreases (e.g. after deletion), `currentPage` is NOT automatically clamped. Call `reset()` or `goToPage(1)` explicitly after mutations.
|
|
155
176
|
- **reset() is silent**: `reset()` sets `currentPage = 1` but does NOT fire `onPageChange`. This prevents accidental double-fetches when the consumer resets pagination during a reload.
|
|
156
177
|
- **Pure without callback**: Omit `onPageChange` and the composable works as pure state — useful for unit tests or when the consumer prefers to watch properties reactively.
|
|
@@ -84,14 +84,16 @@ const { sortField, sortOrder, sortExpression, resetSort } = useDataTableSort({
|
|
|
84
84
|
|
|
85
85
|
const items = ref([]);
|
|
86
86
|
const loading = ref(false);
|
|
87
|
+
const currentPage = ref(1);
|
|
88
|
+
const PAGE_SIZE = 20;
|
|
87
89
|
|
|
88
|
-
async function loadItems() {
|
|
90
|
+
async function loadItems(query: { sort?: string; skip?: number }) {
|
|
89
91
|
loading.value = true;
|
|
90
92
|
try {
|
|
91
93
|
const response = await api.searchProducts({
|
|
92
|
-
sort:
|
|
93
|
-
skip: 0,
|
|
94
|
-
take:
|
|
94
|
+
sort: query.sort, // e.g. "createdDate:DESC" or undefined
|
|
95
|
+
skip: query.skip ?? 0,
|
|
96
|
+
take: PAGE_SIZE,
|
|
95
97
|
});
|
|
96
98
|
items.value = response.results;
|
|
97
99
|
} finally {
|
|
@@ -99,8 +101,13 @@ async function loadItems() {
|
|
|
99
101
|
}
|
|
100
102
|
}
|
|
101
103
|
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
+
// Load from one watcher that reads sort together with search and page, so changing
|
|
105
|
+
// several at once is a single request. { immediate: true } also does the first load.
|
|
106
|
+
watch(
|
|
107
|
+
() => ({ sort: sortExpression.value, skip: (currentPage.value - 1) * PAGE_SIZE }),
|
|
108
|
+
(query) => loadItems(query),
|
|
109
|
+
{ immediate: true },
|
|
110
|
+
);
|
|
104
111
|
</script>
|
|
105
112
|
|
|
106
113
|
<template>
|
|
@@ -134,12 +141,13 @@ watch(sortExpression, () => loadItems(), { immediate: true });
|
|
|
134
141
|
- **Sort expression format**: `sortExpression` returns `"field:DIRECTION"` when both `sortField` and a non-zero `sortOrder` are set, otherwise `undefined`. This format is directly compatible with VirtoCommerce Platform search endpoints.
|
|
135
142
|
- **Reset behavior**: `resetSort()` restores `sortField` and `sortOrder` to the values passed at construction time. If no initial options were provided, both are cleared.
|
|
136
143
|
- **Numeric order convention**: `VcDataTable` uses PrimeVue's numeric sort order convention (`1`/`-1`/`0`). This composable encapsulates the mapping so call sites never need to handle the numeric values directly.
|
|
137
|
-
- **Stateless regarding data**: The composable only manages the sort field and direction.
|
|
144
|
+
- **Stateless regarding data**: The composable only manages the sort field and direction. Read `sortExpression` from the same loader watcher that reads search and page, instead of giving sort its own `watch(sortExpression, load)`. See VcDataTable → URL query persistence.
|
|
138
145
|
|
|
139
146
|
## Tips
|
|
140
147
|
|
|
141
148
|
- Always mark columns as `sortable` in your `<VcColumn>` definitions for the sort indicator icon to appear.
|
|
142
|
-
- Use `{ immediate: true }` on the
|
|
149
|
+
- Use `{ immediate: true }` on the combined loader watcher to trigger the initial (or URL-restored) data load with the correct sort on mount.
|
|
150
|
+
- On reload, seed `sortField`/`sortOrder` from `useTableQueryState(stateKey).read().sort` (`"field:DIR"`) before the loader watcher, so restore costs a single request.
|
|
143
151
|
- Call `resetSort()` alongside any "clear all filters" action to keep sort state consistent with the rest of the filter UI.
|
|
144
152
|
|
|
145
153
|
## Related
|