@xwiki/cristal-dsvuetify 0.10.0 → 0.12.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/CHANGELOG.md +27 -0
- package/dist/components/componentsInit.d.ts +0 -1
- package/dist/components/vuetifyDesignSystemLoader.d.ts +1 -2
- package/dist/{index-BjjXwAdJ.js → index-DCILSBLH.js} +7359 -7287
- package/dist/index-DCILSBLH.js.map +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.es.js +6 -4
- package/dist/index.umd.js +11 -7
- package/dist/index.umd.js.map +1 -1
- package/dist/{translations-CFfpgNrx.js → translations-BNEIsfPR.js} +3 -3
- package/dist/{translations-CFfpgNrx.js.map → translations-BNEIsfPR.js.map} +1 -1
- package/dist/vue/__tests__/utils/vuetify.d.ts +5 -0
- package/dist/vue/form/x-file-input.vue.d.ts +10 -31
- package/dist/vue/form/x-form.vue.d.ts +9 -4
- package/dist/vue/form/x-text-field.vue.d.ts +1 -11
- package/dist/vue/x-alert.vue.d.ts +15 -21
- package/dist/vue/x-avatar.vue.d.ts +3 -3
- package/dist/vue/x-breadcrumb.vue.d.ts +1 -11
- package/dist/vue/x-btn.vue.d.ts +9 -3
- package/dist/vue/x-card.vue.d.ts +3 -3
- package/dist/vue/x-dialog.vue.d.ts +11 -15
- package/dist/vue/x-divider.vue.d.ts +1 -1
- package/dist/vue/x-img.vue.d.ts +3 -3
- package/dist/vue/x-load.vue.d.ts +1 -2
- package/dist/vue/x-menu-item.vue.d.ts +12 -16
- package/dist/vue/x-menu-label.vue.d.ts +16 -0
- package/dist/vue/x-menu.vue.d.ts +9 -17
- package/dist/vue/x-navigation-tree.vue.d.ts +4 -16
- package/dist/vue/x-search.vue.d.ts +1 -1
- package/dist/vue/x-tab-group.vue.d.ts +10 -5
- package/dist/vue/x-tab-panel.vue.d.ts +8 -13
- package/dist/vue/x-tab.vue.d.ts +8 -13
- package/dist/x-alert-C3nY6ghP.js +42 -0
- package/dist/x-alert-C3nY6ghP.js.map +1 -0
- package/dist/x-breadcrumb-C2iUd4Sf.js +22 -0
- package/dist/{x-breadcrumb-DBZVExfF.js.map → x-breadcrumb-C2iUd4Sf.js.map} +1 -1
- package/dist/x-btn-DNlhiqjD.js +48 -0
- package/dist/x-btn-DNlhiqjD.js.map +1 -0
- package/dist/x-divider-Buudf0KI.js +12 -0
- package/dist/x-divider-Buudf0KI.js.map +1 -0
- package/dist/{x-file-input-B6XyfogW.js → x-file-input-BtGTZtfq.js} +9 -9
- package/dist/x-file-input-BtGTZtfq.js.map +1 -0
- package/dist/{x-form-leyAZnAr.js → x-form-pSj3365X.js} +2 -2
- package/dist/{x-form-leyAZnAr.js.map → x-form-pSj3365X.js.map} +1 -1
- package/dist/x-menu-DDC7z1Wb.js +31 -0
- package/dist/x-menu-DDC7z1Wb.js.map +1 -0
- package/dist/x-menu-item-ByE5R9Bf.js +22 -0
- package/dist/x-menu-item-ByE5R9Bf.js.map +1 -0
- package/dist/x-menu-label-DniImc0U.js +17 -0
- package/dist/x-menu-label-DniImc0U.js.map +1 -0
- package/dist/x-navigation-tree-W6QDS4uh.js +499 -0
- package/dist/x-navigation-tree-W6QDS4uh.js.map +1 -0
- package/dist/{x-text-field-B88odaud.js → x-text-field-CAVD3OjY.js} +6 -6
- package/dist/{x-text-field-B88odaud.js.map → x-text-field-CAVD3OjY.js.map} +1 -1
- package/package.json +13 -10
- package/src/components/componentsInit.ts +1 -1
- package/src/components/vuetifyDesignSystemLoader.ts +25 -6
- package/src/vue/__tests__/utils/vuetify.ts +43 -0
- package/src/vue/__tests__/x-btn.test.ts +54 -0
- package/src/vue/css/style.css +2 -0
- package/src/vue/form/x-file-input.vue +3 -4
- package/src/vue/form/x-text-field.vue +3 -3
- package/src/vue/x-alert.vue +14 -16
- package/src/vue/x-breadcrumb.vue +5 -1
- package/src/vue/x-btn.vue +34 -1
- package/src/vue/x-divider.vue +1 -1
- package/src/vue/x-menu-item.vue +2 -2
- package/src/vue/x-menu-label.vue +25 -0
- package/src/vue/x-menu.vue +13 -6
- package/src/vue/x-navigation-tree.vue +132 -11
- package/tsconfig.json +2 -1
- package/{src/vue.d.ts → vitest.config.ts} +17 -7
- package/dist/index-BjjXwAdJ.js.map +0 -1
- package/dist/x-alert-Cm6mzRpR.js +0 -29
- package/dist/x-alert-Cm6mzRpR.js.map +0 -1
- package/dist/x-breadcrumb-DBZVExfF.js +0 -21
- package/dist/x-btn-DsACLwMY.js +0 -17
- package/dist/x-btn-DsACLwMY.js.map +0 -1
- package/dist/x-divider-7cmRuxTV.js +0 -16
- package/dist/x-divider-7cmRuxTV.js.map +0 -1
- package/dist/x-file-input-B6XyfogW.js.map +0 -1
- package/dist/x-menu-VerLAd9F.js +0 -32
- package/dist/x-menu-VerLAd9F.js.map +0 -1
- package/dist/x-menu-item-DGurfOCF.js +0 -22
- package/dist/x-menu-item-DGurfOCF.js.map +0 -1
- package/dist/x-navigation-tree-B8JZ_wJm.js +0 -470
- package/dist/x-navigation-tree-B8JZ_wJm.js.map +0 -1
|
@@ -18,13 +18,27 @@ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
|
|
18
18
|
02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
|
19
19
|
-->
|
|
20
20
|
<script setup lang="ts">
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Navigation Tree implemented using Vuetify's VTreeView component.
|
|
23
|
+
* In order to use the initial component as a proper Navigation Tree for
|
|
24
|
+
* Cristal, a few changes were made:
|
|
25
|
+
* - The only node activated matches the current page, or the clicked link if
|
|
26
|
+
* the component has a custom clickAction. We want to use actual links so
|
|
27
|
+
* that the user can click them normally (to e.g., open them in a new tab).
|
|
28
|
+
* So the default behavior of activating a node by clicking anywhere on the
|
|
29
|
+
* item was disabled. Default hover effects, such as darkening or changing
|
|
30
|
+
* the cursor on items, were also disabled.
|
|
31
|
+
*/
|
|
32
|
+
import { inject, onBeforeMount, ref, watch } from "vue";
|
|
22
33
|
import { VTreeview } from "vuetify/labs/VTreeview";
|
|
23
|
-
import {
|
|
34
|
+
import type { CristalApp, PageData } from "@xwiki/cristal-api";
|
|
35
|
+
import type { DocumentService } from "@xwiki/cristal-document-api";
|
|
24
36
|
import type {
|
|
25
|
-
NavigationTreeSource,
|
|
26
37
|
NavigationTreeNode,
|
|
38
|
+
NavigationTreeSource,
|
|
39
|
+
NavigationTreeSourceProvider,
|
|
27
40
|
} from "@xwiki/cristal-navigation-tree-api";
|
|
41
|
+
import type { Ref } from "vue";
|
|
28
42
|
|
|
29
43
|
type TreeItem = {
|
|
30
44
|
id: string;
|
|
@@ -36,6 +50,15 @@ type TreeItem = {
|
|
|
36
50
|
|
|
37
51
|
type OnClickAction = (node: NavigationTreeNode) => void;
|
|
38
52
|
|
|
53
|
+
const cristal: CristalApp = inject<CristalApp>("cristal")!;
|
|
54
|
+
const documentService: DocumentService = cristal
|
|
55
|
+
.getContainer()
|
|
56
|
+
.get<DocumentService>("DocumentService");
|
|
57
|
+
const treeSource: NavigationTreeSource = cristal
|
|
58
|
+
.getContainer()
|
|
59
|
+
.get<NavigationTreeSourceProvider>("NavigationTreeSourceProvider")
|
|
60
|
+
.get();
|
|
61
|
+
|
|
39
62
|
const rootNodes: Ref<Array<TreeItem>> = ref([]);
|
|
40
63
|
const tree: Ref<VTreeview | undefined> = ref(undefined);
|
|
41
64
|
|
|
@@ -43,13 +66,12 @@ const activatedNodes: Ref<Array<string>> = ref(new Array<string>());
|
|
|
43
66
|
const expandedNodes: Ref<Array<string>> = ref(new Array<string>());
|
|
44
67
|
|
|
45
68
|
const props = defineProps<{
|
|
46
|
-
treeSource: NavigationTreeSource;
|
|
47
69
|
clickAction?: OnClickAction;
|
|
48
70
|
currentPage?: PageData;
|
|
49
71
|
}>();
|
|
50
72
|
|
|
51
73
|
onBeforeMount(async () => {
|
|
52
|
-
for (const node of await
|
|
74
|
+
for (const node of await treeSource.getChildNodes("")) {
|
|
53
75
|
rootNodes.value.push({
|
|
54
76
|
id: node.id,
|
|
55
77
|
title: node.label,
|
|
@@ -61,6 +83,9 @@ onBeforeMount(async () => {
|
|
|
61
83
|
if (props.currentPage !== undefined) {
|
|
62
84
|
await expandTree();
|
|
63
85
|
}
|
|
86
|
+
|
|
87
|
+
documentService.registerDocumentChangeListener("delete", onDocumentDelete);
|
|
88
|
+
documentService.registerDocumentChangeListener("update", onDocumentUpdate);
|
|
64
89
|
});
|
|
65
90
|
|
|
66
91
|
watch(
|
|
@@ -73,7 +98,7 @@ watch(
|
|
|
73
98
|
);
|
|
74
99
|
|
|
75
100
|
async function expandTree() {
|
|
76
|
-
const newExpandedNodes =
|
|
101
|
+
const newExpandedNodes = treeSource.getParentNodesId(props.currentPage);
|
|
77
102
|
let i;
|
|
78
103
|
let currentNodes = rootNodes.value;
|
|
79
104
|
for (i = 0; i < newExpandedNodes.length - 1; i++) {
|
|
@@ -113,7 +138,7 @@ async function expandTree() {
|
|
|
113
138
|
|
|
114
139
|
async function lazyLoadChildren(item: unknown) {
|
|
115
140
|
const treeItem = item as TreeItem;
|
|
116
|
-
const childNodes = await
|
|
141
|
+
const childNodes = await treeSource.getChildNodes(treeItem.id);
|
|
117
142
|
for (const child of childNodes) {
|
|
118
143
|
treeItem.children?.push({
|
|
119
144
|
id: child.id,
|
|
@@ -128,32 +153,112 @@ async function lazyLoadChildren(item: unknown) {
|
|
|
128
153
|
treeItem.children = undefined;
|
|
129
154
|
}
|
|
130
155
|
}
|
|
156
|
+
|
|
157
|
+
function clearSelection() {
|
|
158
|
+
// Clicking on a node would activate it and this can't be disabled easily.
|
|
159
|
+
// With this listener, we ensure that only the first activated node stays
|
|
160
|
+
// active.
|
|
161
|
+
if (activatedNodes.value.length > 1) {
|
|
162
|
+
activatedNodes.value.pop();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function onDocumentDelete(page: PageData) {
|
|
167
|
+
const parents = treeSource.getParentNodesId(page);
|
|
168
|
+
let currentItems: TreeItem[] | undefined = rootNodes.value;
|
|
169
|
+
while (currentItems) {
|
|
170
|
+
for (const i of currentItems.keys()) {
|
|
171
|
+
if (currentItems[i].id == parents[0]) {
|
|
172
|
+
if (parents.length == 1) {
|
|
173
|
+
currentItems.splice(i, 1);
|
|
174
|
+
return;
|
|
175
|
+
} else {
|
|
176
|
+
currentItems = currentItems[i].children;
|
|
177
|
+
parents.shift();
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function onDocumentUpdate(page: PageData) {
|
|
186
|
+
const parents = treeSource.getParentNodesId(page);
|
|
187
|
+
let currentParent: string | undefined = undefined;
|
|
188
|
+
let currentItems: TreeItem[] | undefined = rootNodes.value;
|
|
189
|
+
|
|
190
|
+
while (currentItems) {
|
|
191
|
+
for (const i of currentItems.keys()) {
|
|
192
|
+
if (currentItems[i].id == parents[0]) {
|
|
193
|
+
if (parents.length == 1) {
|
|
194
|
+
// Page update
|
|
195
|
+
const newItems = await treeSource.getChildNodes(
|
|
196
|
+
currentParent ? currentParent : "",
|
|
197
|
+
);
|
|
198
|
+
for (const newItem of newItems) {
|
|
199
|
+
if (newItem.id == currentItems[i].id) {
|
|
200
|
+
currentItems[i].title = newItem.label;
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
} else {
|
|
205
|
+
currentParent = currentItems[i].id;
|
|
206
|
+
currentItems = currentItems[i].children;
|
|
207
|
+
parents.shift();
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// New page
|
|
214
|
+
const newItems = await treeSource.getChildNodes(
|
|
215
|
+
currentParent ? currentParent : "",
|
|
216
|
+
);
|
|
217
|
+
newItemsLoop: for (const newItem of newItems) {
|
|
218
|
+
for (const i of currentItems!.keys()) {
|
|
219
|
+
if (newItem.id == currentItems![i].id) {
|
|
220
|
+
continue newItemsLoop;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
currentItems!.push({
|
|
224
|
+
id: newItem.id,
|
|
225
|
+
title: newItem.label,
|
|
226
|
+
href: newItem.url,
|
|
227
|
+
children: newItem.has_children ? [] : undefined,
|
|
228
|
+
_location: newItem.location,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
131
233
|
</script>
|
|
132
234
|
|
|
133
235
|
<template>
|
|
134
236
|
<v-treeview
|
|
135
237
|
ref="tree"
|
|
136
|
-
v-model:activated="activatedNodes"
|
|
137
238
|
v-model:opened="expandedNodes"
|
|
239
|
+
density="compact"
|
|
240
|
+
:activated="activatedNodes"
|
|
138
241
|
:items="rootNodes"
|
|
139
242
|
:load-children="lazyLoadChildren"
|
|
140
243
|
activatable
|
|
141
|
-
active-strategy="
|
|
244
|
+
active-strategy="independent"
|
|
142
245
|
item-value="id"
|
|
143
246
|
open-strategy="multiple"
|
|
247
|
+
@update:activated="clearSelection"
|
|
144
248
|
>
|
|
145
249
|
<template #title="{ item }: { item: any }">
|
|
146
250
|
<a
|
|
147
251
|
v-if="props.clickAction"
|
|
148
252
|
:href="item.href"
|
|
149
253
|
@click.prevent="
|
|
254
|
+
activatedNodes = [item.id];
|
|
150
255
|
clickAction!({
|
|
151
256
|
id: item.id,
|
|
152
257
|
label: item.title,
|
|
153
258
|
location: item._location,
|
|
154
259
|
url: item.href,
|
|
155
260
|
has_children: item.children !== undefined,
|
|
156
|
-
})
|
|
261
|
+
});
|
|
157
262
|
"
|
|
158
263
|
>{{ item.title }}</a
|
|
159
264
|
>
|
|
@@ -162,4 +267,20 @@ async function lazyLoadChildren(item: unknown) {
|
|
|
162
267
|
</v-treeview>
|
|
163
268
|
</template>
|
|
164
269
|
|
|
165
|
-
<style scoped
|
|
270
|
+
<style scoped>
|
|
271
|
+
.v-list {
|
|
272
|
+
background: none;
|
|
273
|
+
}
|
|
274
|
+
:deep(a) {
|
|
275
|
+
text-decoration: none;
|
|
276
|
+
color: var(--cr-base-text-color);
|
|
277
|
+
}
|
|
278
|
+
/* Disable hover on items. */
|
|
279
|
+
:deep(.v-list-item__overlay) {
|
|
280
|
+
--v-hover-opacity: 0;
|
|
281
|
+
}
|
|
282
|
+
/* Disable hand cursor on items, since we disable the default click action. */
|
|
283
|
+
:deep(.v-list-item--link) {
|
|
284
|
+
cursor: default;
|
|
285
|
+
}
|
|
286
|
+
</style>
|
package/tsconfig.json
CHANGED
|
@@ -18,10 +18,20 @@
|
|
|
18
18
|
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
import localConfig from "./vite.config";
|
|
22
|
+
import { defineConfig, mergeConfig } from "vitest/config";
|
|
23
|
+
import defaultConfig from "@xwiki/cristal-dev-config/vitest-vue.config";
|
|
24
|
+
|
|
25
|
+
export default mergeConfig(
|
|
26
|
+
mergeConfig(defaultConfig, localConfig),
|
|
27
|
+
defineConfig({
|
|
28
|
+
test: {
|
|
29
|
+
server: {
|
|
30
|
+
deps: {
|
|
31
|
+
// see https://github.com/vitest-dev/vitest/discussions/2677#discussioncomment-7035566
|
|
32
|
+
inline: ["vuetify"],
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
}),
|
|
37
|
+
);
|