@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.
Files changed (87) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/components/componentsInit.d.ts +0 -1
  3. package/dist/components/vuetifyDesignSystemLoader.d.ts +1 -2
  4. package/dist/{index-BjjXwAdJ.js → index-DCILSBLH.js} +7359 -7287
  5. package/dist/index-DCILSBLH.js.map +1 -0
  6. package/dist/index.d.ts +0 -1
  7. package/dist/index.es.js +6 -4
  8. package/dist/index.umd.js +11 -7
  9. package/dist/index.umd.js.map +1 -1
  10. package/dist/{translations-CFfpgNrx.js → translations-BNEIsfPR.js} +3 -3
  11. package/dist/{translations-CFfpgNrx.js.map → translations-BNEIsfPR.js.map} +1 -1
  12. package/dist/vue/__tests__/utils/vuetify.d.ts +5 -0
  13. package/dist/vue/form/x-file-input.vue.d.ts +10 -31
  14. package/dist/vue/form/x-form.vue.d.ts +9 -4
  15. package/dist/vue/form/x-text-field.vue.d.ts +1 -11
  16. package/dist/vue/x-alert.vue.d.ts +15 -21
  17. package/dist/vue/x-avatar.vue.d.ts +3 -3
  18. package/dist/vue/x-breadcrumb.vue.d.ts +1 -11
  19. package/dist/vue/x-btn.vue.d.ts +9 -3
  20. package/dist/vue/x-card.vue.d.ts +3 -3
  21. package/dist/vue/x-dialog.vue.d.ts +11 -15
  22. package/dist/vue/x-divider.vue.d.ts +1 -1
  23. package/dist/vue/x-img.vue.d.ts +3 -3
  24. package/dist/vue/x-load.vue.d.ts +1 -2
  25. package/dist/vue/x-menu-item.vue.d.ts +12 -16
  26. package/dist/vue/x-menu-label.vue.d.ts +16 -0
  27. package/dist/vue/x-menu.vue.d.ts +9 -17
  28. package/dist/vue/x-navigation-tree.vue.d.ts +4 -16
  29. package/dist/vue/x-search.vue.d.ts +1 -1
  30. package/dist/vue/x-tab-group.vue.d.ts +10 -5
  31. package/dist/vue/x-tab-panel.vue.d.ts +8 -13
  32. package/dist/vue/x-tab.vue.d.ts +8 -13
  33. package/dist/x-alert-C3nY6ghP.js +42 -0
  34. package/dist/x-alert-C3nY6ghP.js.map +1 -0
  35. package/dist/x-breadcrumb-C2iUd4Sf.js +22 -0
  36. package/dist/{x-breadcrumb-DBZVExfF.js.map → x-breadcrumb-C2iUd4Sf.js.map} +1 -1
  37. package/dist/x-btn-DNlhiqjD.js +48 -0
  38. package/dist/x-btn-DNlhiqjD.js.map +1 -0
  39. package/dist/x-divider-Buudf0KI.js +12 -0
  40. package/dist/x-divider-Buudf0KI.js.map +1 -0
  41. package/dist/{x-file-input-B6XyfogW.js → x-file-input-BtGTZtfq.js} +9 -9
  42. package/dist/x-file-input-BtGTZtfq.js.map +1 -0
  43. package/dist/{x-form-leyAZnAr.js → x-form-pSj3365X.js} +2 -2
  44. package/dist/{x-form-leyAZnAr.js.map → x-form-pSj3365X.js.map} +1 -1
  45. package/dist/x-menu-DDC7z1Wb.js +31 -0
  46. package/dist/x-menu-DDC7z1Wb.js.map +1 -0
  47. package/dist/x-menu-item-ByE5R9Bf.js +22 -0
  48. package/dist/x-menu-item-ByE5R9Bf.js.map +1 -0
  49. package/dist/x-menu-label-DniImc0U.js +17 -0
  50. package/dist/x-menu-label-DniImc0U.js.map +1 -0
  51. package/dist/x-navigation-tree-W6QDS4uh.js +499 -0
  52. package/dist/x-navigation-tree-W6QDS4uh.js.map +1 -0
  53. package/dist/{x-text-field-B88odaud.js → x-text-field-CAVD3OjY.js} +6 -6
  54. package/dist/{x-text-field-B88odaud.js.map → x-text-field-CAVD3OjY.js.map} +1 -1
  55. package/package.json +13 -10
  56. package/src/components/componentsInit.ts +1 -1
  57. package/src/components/vuetifyDesignSystemLoader.ts +25 -6
  58. package/src/vue/__tests__/utils/vuetify.ts +43 -0
  59. package/src/vue/__tests__/x-btn.test.ts +54 -0
  60. package/src/vue/css/style.css +2 -0
  61. package/src/vue/form/x-file-input.vue +3 -4
  62. package/src/vue/form/x-text-field.vue +3 -3
  63. package/src/vue/x-alert.vue +14 -16
  64. package/src/vue/x-breadcrumb.vue +5 -1
  65. package/src/vue/x-btn.vue +34 -1
  66. package/src/vue/x-divider.vue +1 -1
  67. package/src/vue/x-menu-item.vue +2 -2
  68. package/src/vue/x-menu-label.vue +25 -0
  69. package/src/vue/x-menu.vue +13 -6
  70. package/src/vue/x-navigation-tree.vue +132 -11
  71. package/tsconfig.json +2 -1
  72. package/{src/vue.d.ts → vitest.config.ts} +17 -7
  73. package/dist/index-BjjXwAdJ.js.map +0 -1
  74. package/dist/x-alert-Cm6mzRpR.js +0 -29
  75. package/dist/x-alert-Cm6mzRpR.js.map +0 -1
  76. package/dist/x-breadcrumb-DBZVExfF.js +0 -21
  77. package/dist/x-btn-DsACLwMY.js +0 -17
  78. package/dist/x-btn-DsACLwMY.js.map +0 -1
  79. package/dist/x-divider-7cmRuxTV.js +0 -16
  80. package/dist/x-divider-7cmRuxTV.js.map +0 -1
  81. package/dist/x-file-input-B6XyfogW.js.map +0 -1
  82. package/dist/x-menu-VerLAd9F.js +0 -32
  83. package/dist/x-menu-VerLAd9F.js.map +0 -1
  84. package/dist/x-menu-item-DGurfOCF.js +0 -22
  85. package/dist/x-menu-item-DGurfOCF.js.map +0 -1
  86. package/dist/x-navigation-tree-B8JZ_wJm.js +0 -470
  87. 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
- import { Ref, onBeforeMount, ref, watch } from "vue";
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 { type PageData } from "@xwiki/cristal-api";
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 props.treeSource.getChildNodes("")) {
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 = props.treeSource.getParentNodesId(props.currentPage);
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 props.treeSource.getChildNodes(treeItem.id);
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="single-independent"
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></style>
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
@@ -5,5 +5,6 @@
5
5
  "isolatedDeclarations": false
6
6
  },
7
7
  "extends": "./../../tsconfig.json",
8
- "include": ["./src/index.ts", "./src/**/*"]
8
+ "include": ["./src/index.ts", "./src/**/*"],
9
+ "exclude": ["**/*.spec.ts", "**/*.test.ts"]
9
10
  }
@@ -18,10 +18,20 @@
18
18
  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19
19
  */
20
20
 
21
- // vue.d.ts
22
- declare module "*.vue" {
23
- import type { DefineComponent } from "vue";
24
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
25
- const component: DefineComponent<{}, {}, any>;
26
- export default component;
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
+ );