@rancher/shell 0.3.24 → 0.3.25

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 (111) hide show
  1. package/assets/styles/themes/_light.scss +1 -1
  2. package/assets/translations/en-us.yaml +29 -7
  3. package/assets/translations/zh-hans.yaml +1 -1
  4. package/components/ClusterIconMenu.vue +143 -0
  5. package/components/CruResource.vue +7 -1
  6. package/components/ExplorerProjectsNamespaces.vue +11 -1
  7. package/components/FixedBanner.vue +17 -1
  8. package/components/Markdown.vue +1 -1
  9. package/components/Questions/__tests__/Yaml.test.ts +3 -2
  10. package/components/SortableTable/index.vue +3 -2
  11. package/components/auth/RoleDetailEdit.vue +15 -2
  12. package/components/auth/login/saml.vue +12 -1
  13. package/components/form/LabeledSelect.vue +12 -5
  14. package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
  15. package/components/form/Members/MembershipEditor.vue +6 -1
  16. package/components/form/__tests__/KeyValue.test.ts +6 -3
  17. package/components/form/__tests__/LabeledSelect.test.ts +18 -0
  18. package/components/formatter/PodsUsage.vue +11 -36
  19. package/components/formatter/PrincipalGroupBindings.vue +8 -5
  20. package/components/formatter/__tests__/PodsUsage.test.ts +36 -19
  21. package/components/nav/Group.vue +25 -27
  22. package/components/nav/Header.vue +12 -5
  23. package/components/nav/Pinned.vue +47 -0
  24. package/components/nav/TopLevelMenu.vue +233 -60
  25. package/components/nav/Type.vue +57 -3
  26. package/config/home-links.js +1 -1
  27. package/config/product/istio.js +15 -5
  28. package/config/router.js +3 -9
  29. package/config/table-headers.js +5 -6
  30. package/config/uiplugins.js +1 -0
  31. package/core/plugin-helpers.js +3 -0
  32. package/core/types.ts +6 -1
  33. package/creators/app/files/.vscode/settings.json +0 -1
  34. package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +118 -0
  35. package/detail/autoscaling.horizontalpodautoscaler/index.vue +4 -4
  36. package/detail/provisioning.cattle.io.cluster.vue +7 -5
  37. package/edit/__tests__/management.cattle.io.clusterroletemplatebinding.test.ts +58 -0
  38. package/edit/__tests__/namespace.test.ts +5 -3
  39. package/edit/management.cattle.io.clusterroletemplatebinding.vue +3 -11
  40. package/edit/namespace.vue +8 -4
  41. package/edit/provisioning.cattle.io.cluster/Basics.vue +662 -0
  42. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +6 -0
  43. package/edit/provisioning.cattle.io.cluster/DrainOptions.vue +13 -8
  44. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +11 -2
  45. package/edit/provisioning.cattle.io.cluster/MemberRoles.vue +40 -0
  46. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.tests.ts +237 -0
  47. package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.tests.ts +71 -23
  48. package/edit/provisioning.cattle.io.cluster/__tests__/DrainOptions.test.ts +52 -0
  49. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +65 -142
  50. package/edit/provisioning.cattle.io.cluster/rke2.vue +194 -598
  51. package/edit/workload/storage/__tests__/Storage.test.ts +2 -2
  52. package/edit/workload/storage/persistentVolumeClaim/__tests__/persistentvolumeclaim.test.ts +36 -0
  53. package/edit/workload/storage/persistentVolumeClaim/persistentvolumeclaim.vue +15 -7
  54. package/initialize/index.js +5 -5
  55. package/layouts/default.vue +6 -6
  56. package/layouts/home.vue +6 -2
  57. package/layouts/plain.vue +9 -2
  58. package/list/fleet.cattle.io.cluster.vue +2 -2
  59. package/list/management.cattle.io.feature.vue +1 -1
  60. package/machine-config/vmwarevsphere.vue +48 -7
  61. package/mixins/brand.js +0 -8
  62. package/mixins/child-hook.js +2 -2
  63. package/mixins/create-edit-view/impl.js +3 -3
  64. package/models/__tests__/management.cattle.io.node.ts +96 -0
  65. package/models/__tests__/node.ts +74 -0
  66. package/models/cluster/node.js +6 -5
  67. package/models/cluster.x-k8s.io.machinedeployment.js +2 -2
  68. package/models/management.cattle.io.cluster.js +22 -1
  69. package/models/management.cattle.io.clusterroletemplatebinding.js +3 -3
  70. package/models/management.cattle.io.globalrole.js +17 -2
  71. package/models/management.cattle.io.node.js +6 -4
  72. package/models/management.cattle.io.projectroletemplatebinding.js +3 -3
  73. package/models/management.cattle.io.roletemplate.js +17 -2
  74. package/package.json +2 -6
  75. package/pages/about.vue +2 -0
  76. package/pages/auth/setup.vue +5 -4
  77. package/pages/c/_cluster/monitoring/index.vue +8 -3
  78. package/pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue +9 -66
  79. package/pages/c/_cluster/uiplugins/CatalogList/CatalogUninstallDialog.vue +182 -0
  80. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +15 -32
  81. package/pages/c/_cluster/uiplugins/UninstallDialog.vue +8 -46
  82. package/pages/c/_cluster/uiplugins/index.vue +64 -64
  83. package/pages/diagnostic.vue +0 -39
  84. package/pages/home.vue +1 -1
  85. package/plugins/dashboard-store/normalize.js +4 -4
  86. package/plugins/int-number.js +5 -2
  87. package/plugins/positive-int-number.js +19 -0
  88. package/plugins/steve/__tests__/getters.spec.ts +15 -0
  89. package/plugins/steve/getters.js +22 -10
  90. package/rancher-components/Form/LabeledInput/LabeledInput.vue +0 -8
  91. package/rancher-components/Form/Radio/RadioButton.test.ts +3 -7
  92. package/store/index.js +4 -0
  93. package/store/prefs.js +1 -0
  94. package/types/shell/index.d.ts +13 -4
  95. package/utils/__tests__/cluster.test.ts +55 -0
  96. package/utils/__tests__/object.test.ts +21 -2
  97. package/utils/cluster.js +47 -1
  98. package/utils/object.js +12 -5
  99. package/utils/validators/formRules/__tests__/index.test.ts +13 -1
  100. package/utils/validators/formRules/index.ts +4 -0
  101. package/utils/validators/role-template.js +9 -1
  102. package/utils/version.js +1 -1
  103. package/yarn-error.log +16 -16
  104. package/components/ClusterProviderIconMenu.vue +0 -161
  105. package/content/docs/en-us/getting-started.md +0 -224
  106. package/content/docs/en-us/whats-new.md +0 -29
  107. package/content/docs/zh-hans/getting-started.md +0 -224
  108. package/content/docs/zh-hans/whats-new.md +0 -28
  109. package/pages/docs/_doc.vue +0 -345
  110. package/pages/docs/toc.js +0 -27
  111. package/plugins/console.js +0 -34
@@ -1,6 +1,6 @@
1
1
  <script>
2
2
  import BrandImage from '@shell/components/BrandImage';
3
- import ClusterProviderIconMenu from '@shell/components/ClusterProviderIconMenu';
3
+ import ClusterIconMenu from '@shell/components/ClusterIconMenu';
4
4
  import IconOrSvg from '../IconOrSvg';
5
5
  import { BLANK_CLUSTER } from '@shell/store/store-types.js';
6
6
  import { mapGetters } from 'vuex';
@@ -14,13 +14,14 @@ import { LEGACY } from '@shell/store/features';
14
14
  import { SETTING } from '@shell/config/settings';
15
15
  import { filterOnlyKubernetesClusters, filterHiddenLocalCluster } from '@shell/utils/cluster';
16
16
  import { isRancherPrime } from '@shell/config/version';
17
+ import Pinned from '@shell/components/nav/Pinned';
17
18
 
18
19
  export default {
19
-
20
20
  components: {
21
21
  BrandImage,
22
- ClusterProviderIconMenu,
23
- IconOrSvg
22
+ ClusterIconMenu,
23
+ IconOrSvg,
24
+ Pinned
24
25
  },
25
26
 
26
27
  data() {
@@ -34,7 +35,9 @@ export default {
34
35
  clusterFilter: '',
35
36
  hasProvCluster,
36
37
  maxClustersToShow: MENU_MAX_CLUSTERS,
37
- emptyCluster: BLANK_CLUSTER
38
+ emptyCluster: BLANK_CLUSTER,
39
+ showPinClusters: false,
40
+ searchActive: false,
38
41
  };
39
42
  },
40
43
 
@@ -56,6 +59,24 @@ export default {
56
59
  },
57
60
  },
58
61
 
62
+ globalBannerSettings() {
63
+ const settings = this.$store.getters['management/all'](MANAGEMENT.SETTING);
64
+ const bannerSettings = settings.find((s) => s.id === SETTING.BANNERS);
65
+
66
+ if (bannerSettings) {
67
+ const parsed = JSON.parse(bannerSettings.value);
68
+ const {
69
+ showFooter, showHeader, bannerFooter, bannerHeader
70
+ } = parsed;
71
+
72
+ return {
73
+ headerFont: showHeader === 'true' ? this.pxToEm(bannerHeader.fontSize) : '0px',
74
+ footerFont: showFooter === 'true' ? this.pxToEm(bannerFooter.fontSize) : '0px'
75
+ };
76
+ }
77
+
78
+ return undefined;
79
+ },
59
80
  legacyEnabled() {
60
81
  return this.features(LEGACY);
61
82
  },
@@ -95,7 +116,9 @@ export default {
95
116
  badge: x.badge,
96
117
  isLocal: x.isLocal,
97
118
  isHarvester: x.isHarvester,
98
- pinned: true,
119
+ pinned: x.pinned,
120
+ pin: () => x.pin(),
121
+ unpin: () => x.unpin()
99
122
  };
100
123
  });
101
124
  },
@@ -107,13 +130,38 @@ export default {
107
130
 
108
131
  const sorted = sortBy(out, ['ready:desc', 'label']);
109
132
 
133
+ if (search) {
134
+ this.showPinClusters = false;
135
+ this.searchActive = !sorted.length > 0;
136
+
137
+ return sorted;
138
+ }
139
+ this.showPinClusters = true;
140
+ this.searchActive = false;
141
+
110
142
  if (sorted.length >= this.maxClustersToShow) {
111
- return sorted.slice(0, this.maxClustersToShow);
143
+ const sortedPinOut = sorted.filter((item) => !item.pinned).slice(0, this.maxClustersToShow);
144
+
145
+ return sortedPinOut;
112
146
  } else {
113
- return sorted;
147
+ return sorted.filter((item) => !item.pinned);
114
148
  }
115
149
  },
116
150
 
151
+ pinFiltered() {
152
+ const out = this.clusters.filter((item) => item.pinned);
153
+ const sorted = sortBy(out, ['ready:desc', 'label']);
154
+
155
+ return sorted;
156
+ },
157
+
158
+ pinnedClustersHeight() {
159
+ const pinCount = this.clusters.filter((item) => item.pinned).length;
160
+ const height = pinCount > 2 ? (pinCount * 43) : 90;
161
+
162
+ return `min-height: ${ height }px`;
163
+ },
164
+
117
165
  clusterFilterCount() {
118
166
  return this.clusterFilter ? this.clustersFiltered.length : this.clusters.length;
119
167
  },
@@ -208,6 +256,19 @@ export default {
208
256
  },
209
257
 
210
258
  methods: {
259
+ /**
260
+ * Converts a pixel value to an em value based on the default font size.
261
+ * @param {number} elementFontSize - The font size of the element in pixels.
262
+ * @param {number} [defaultFontSize=14] - The default font size in pixels.
263
+ * @returns {string} The converted value in em units.
264
+ */
265
+ pxToEm(elementFontSize, defaultFontSize = 14) {
266
+ const lineHeightInPx = 2 * parseInt(elementFontSize);
267
+ const lineHeightInEm = lineHeightInPx / defaultFontSize;
268
+
269
+ return `${ lineHeightInEm }em`;
270
+ },
271
+
211
272
  handler(e) {
212
273
  if (e.keyCode === KEY.ESCAPE ) {
213
274
  this.hide();
@@ -242,7 +303,7 @@ export default {
242
303
  return {
243
304
  content: this.shown ? null : item,
244
305
  placement: 'right',
245
- popperOptions: { modifiers: { preventOverflow: { enabled: false } } }
306
+ popperOptions: { modifiers: { preventOverflow: { enabled: false }, hide: { enabled: false } } }
246
307
  };
247
308
  } else {
248
309
  return { content: undefined };
@@ -263,6 +324,10 @@ export default {
263
324
  data-testid="side-menu"
264
325
  class="side-menu"
265
326
  :class="{'menu-open': shown, 'menu-close':!shown}"
327
+ :style="{'marginBottom':
328
+ globalBannerSettings?.footerFont,
329
+ 'marginTop':
330
+ globalBannerSettings?.headerFont}"
266
331
  tabindex="-1"
267
332
  >
268
333
  <div class="title">
@@ -306,7 +371,6 @@ export default {
306
371
  {{ t('nav.home') }}
307
372
  </div>
308
373
  </nuxt-link>
309
-
310
374
  <div
311
375
  v-if="showClusterSearch"
312
376
  class="clusters-search"
@@ -375,41 +439,101 @@ export default {
375
439
  <div
376
440
  ref="clusterList"
377
441
  class="clusters"
442
+ :style="pinnedClustersHeight"
378
443
  >
379
444
  <div
380
- v-for="c in clustersFiltered"
381
- :key="c.id"
382
- @click="hide()"
445
+ v-if="showPinClusters && pinFiltered.length"
446
+ class="clustersPinned"
383
447
  >
384
- <nuxt-link
385
- v-if="c.ready"
386
- :data-testid="`menu-cluster-${ c.id }`"
387
- class="cluster selector option"
388
- :to="{ name: 'c-cluster-explorer', params: { cluster: c.id } }"
448
+ <div
449
+ v-for="c in pinFiltered"
450
+ :key="c.id"
451
+ @click="hide()"
389
452
  >
390
- <ClusterProviderIconMenu
391
- v-tooltip="getTooltipConfig(c.label)"
392
- :cluster="c"
393
- class="rancher-provider-icon"
394
- />
395
- <div class="cluster-name">
396
- {{ c.label }}
397
- </div>
398
- </nuxt-link>
399
- <span
400
- v-else
401
- class="option cluster selector disabled"
453
+ <nuxt-link
454
+ v-if="c.ready"
455
+ :data-testid="`menu-cluster-${ c.id }`"
456
+ class="cluster selector option"
457
+ :to="{ name: 'c-cluster-explorer', params: { cluster: c.id } }"
458
+ >
459
+ <ClusterIconMenu
460
+ v-tooltip="getTooltipConfig(c.label)"
461
+ :cluster="c"
462
+ class="rancher-provider-icon"
463
+ />
464
+ <div class="cluster-name">
465
+ {{ c.label }}
466
+ </div>
467
+ <Pinned
468
+ :cluster="c"
469
+ />
470
+ </nuxt-link>
471
+ <span
472
+ v-else
473
+ class="option cluster selector disabled"
474
+ >
475
+ <ClusterIconMenu
476
+ v-tooltip="getTooltipConfig(c.label)"
477
+ :cluster="c"
478
+ class="rancher-provider-icon"
479
+ />
480
+ <div class="cluster-name">{{ c.label }}</div>
481
+ <Pinned
482
+ :cluster="c"
483
+ />
484
+ </span>
485
+ </div>
486
+ <div
487
+ v-if="clustersFiltered.length > 0"
488
+ class="category-title"
402
489
  >
403
- <ClusterProviderIconMenu
404
- v-tooltip="getTooltipConfig(c.label)"
405
- :cluster="c"
406
- class="rancher-provider-icon"
407
- />
408
- <div class="cluster-name">{{ c.label }}</div>
409
- </span>
490
+ <hr>
491
+ </div>
492
+ </div>
493
+ <div class="clustersList">
494
+ <div
495
+ v-for="c in clustersFiltered"
496
+ :key="c.id"
497
+ @click="hide()"
498
+ >
499
+ <nuxt-link
500
+ v-if="c.ready"
501
+ :data-testid="`menu-cluster-${ c.id }`"
502
+ class="cluster selector option"
503
+ :to="{ name: 'c-cluster-explorer', params: { cluster: c.id } }"
504
+ >
505
+ <ClusterIconMenu
506
+ v-tooltip="getTooltipConfig(c.label)"
507
+ :cluster="c"
508
+ class="rancher-provider-icon"
509
+ />
510
+ <div class="cluster-name">
511
+ {{ c.label }}
512
+ </div>
513
+ <Pinned
514
+ :class="{'showPin': c.pinned}"
515
+ :cluster="c"
516
+ />
517
+ </nuxt-link>
518
+ <span
519
+ v-else
520
+ class="option cluster selector disabled"
521
+ >
522
+ <ClusterIconMenu
523
+ v-tooltip="getTooltipConfig(c.label)"
524
+ :cluster="c"
525
+ class="rancher-provider-icon"
526
+ />
527
+ <div class="cluster-name">{{ c.label }}</div>
528
+ <Pinned
529
+ :class="{'showPin': c.pinned}"
530
+ :cluster="c"
531
+ />
532
+ </span>
533
+ </div>
410
534
  </div>
411
535
  <div
412
- v-if="clustersFiltered.length === 0 && shown"
536
+ v-if="(clustersFiltered.length === 0 || pinFiltered.length === 0) && searchActive"
413
537
  class="none-matching"
414
538
  >
415
539
  {{ t('nav.search.noResults') }}
@@ -573,25 +697,25 @@ export default {
573
697
  $option-padding-left: 14px;
574
698
  $option-height: $icon-size + $option-padding + $option-padding;
575
699
 
576
- .menu {
577
- position: absolute;
578
- width: $app-bar-collapsed-width;
579
- height: 54px;
580
- top: 0;
581
- grid-area: menu;
582
- cursor: pointer;
583
- display: flex;
584
- align-items: center;
585
- justify-content: center;
700
+ .side-menu {
701
+ .menu {
702
+ position: absolute;
703
+ width: $app-bar-collapsed-width;
704
+ height: 54px;
705
+ top: 0;
706
+ grid-area: menu;
707
+ cursor: pointer;
708
+ display: flex;
709
+ align-items: center;
710
+ justify-content: center;
586
711
 
587
- .menu-icon {
588
- width: 25px;
589
- height: 25px;
590
- fill: var(--header-btn-text);
712
+ .menu-icon {
713
+ width: 25px;
714
+ height: 25px;
715
+ fill: var(--header-btn-text);
716
+ }
591
717
  }
592
- }
593
718
 
594
- .side-menu {
595
719
  position: fixed;
596
720
  top: 0;
597
721
  left: 0px;
@@ -636,7 +760,7 @@ export default {
636
760
  svg {
637
761
  width: 25px;
638
762
  height: 25px;
639
- margin-left: 10px;
763
+ margin-left: 9px;
640
764
  }
641
765
  }
642
766
  .home-text {
@@ -664,8 +788,23 @@ export default {
664
788
  font-weight: 500;
665
789
  }
666
790
 
791
+ .pin {
792
+ font-size: 16px;
793
+ margin-left: auto;
794
+ display: none;
795
+ color: var(--body-text);
796
+ &.showPin {
797
+ display: block;
798
+ }
799
+ }
800
+
667
801
  &:hover {
668
802
  text-decoration: none;
803
+
804
+ .pin {
805
+ display: block;
806
+ color: var(--darker-text);
807
+ }
669
808
  }
670
809
  &.disabled {
671
810
  background: transparent;
@@ -676,6 +815,10 @@ export default {
676
815
  filter: grayscale(1);
677
816
  color: var(--muted);
678
817
  }
818
+
819
+ .pin {
820
+ cursor: pointer;
821
+ }
679
822
  }
680
823
 
681
824
  &:focus {
@@ -729,6 +872,11 @@ export default {
729
872
  &.disabled {
730
873
  background: transparent;
731
874
  color: var(--muted);
875
+
876
+ > .pin {
877
+ color:var(--default-text);
878
+ display: block;
879
+ }
732
880
  }
733
881
  }
734
882
 
@@ -749,7 +897,7 @@ export default {
749
897
  > input {
750
898
  background-color: transparent;
751
899
  margin-bottom: 8px;
752
- padding-right: 34px;
900
+ padding-right: 35px;
753
901
  }
754
902
  > i {
755
903
  position: absolute;
@@ -779,10 +927,6 @@ export default {
779
927
  .clusters {
780
928
  overflow-y: auto;
781
929
 
782
- @media screen and (max-height: 720px) {
783
- min-height: 80px;
784
- }
785
-
786
930
  a, span {
787
931
  margin: 0;
788
932
  }
@@ -846,6 +990,24 @@ export default {
846
990
  padding: 8px
847
991
  }
848
992
 
993
+ .clustersPinned {
994
+ .category {
995
+ &-title {
996
+ margin: 8px 0;
997
+ margin-left: 16px;
998
+ hr {
999
+ margin: 0;
1000
+ width: 94%;
1001
+ transition: all 0.5s ease-in-out;
1002
+ max-width: 100%;
1003
+ }
1004
+ }
1005
+ }
1006
+ .pin {
1007
+ display: block;
1008
+ }
1009
+ }
1010
+
849
1011
  .category {
850
1012
  display: flex;
851
1013
  flex-direction: column;
@@ -907,6 +1069,17 @@ export default {
907
1069
  }
908
1070
  }
909
1071
  }
1072
+
1073
+ .clustersPinned {
1074
+ .category {
1075
+ &-title {
1076
+ hr {
1077
+ width: 40px;
1078
+ }
1079
+ }
1080
+ }
1081
+ }
1082
+
910
1083
  .footer {
911
1084
  margin: 20px 15px;
912
1085
 
@@ -1,6 +1,7 @@
1
1
  <script>
2
2
  import Favorite from '@shell/components/nav/Favorite';
3
3
  import { FAVORITE, USED } from '@shell/store/type-map';
4
+ import { linkActiveClass } from '@shell/config/router';
4
5
 
5
6
  const showFavoritesFor = [FAVORITE, USED];
6
7
 
@@ -27,12 +28,65 @@ export default {
27
28
 
28
29
  data() {
29
30
  return {
30
- near: false,
31
- over: false,
31
+ near: false,
32
+ over: false,
33
+ menuPath: this.type.route ? this.$router.resolve(this.type.route)?.route?.path : undefined,
34
+ linkActiveClass
32
35
  };
33
36
  },
34
37
 
35
38
  computed: {
39
+ isCurrent() {
40
+ // This is required to avoid scenarios where fragments break vue routers location matching
41
+ // For example, the following fails
42
+ // Curruent Path /c/c-m-hzqf4tqt/explorer/members#project-membership
43
+ // Menu Path /c/c-m-hzqf4tqt/explorer/members
44
+ // vue-router exact-path="true" fixes this (https://v3.router.vuejs.org/api/#exact-path),
45
+ // but fails when the the current path is a child (for instance a resource detail page)
46
+
47
+ // Scenarios to consider
48
+ // - Fragement world
49
+ // Curruent Path /c/c-m-hzqf4tqt/explorer/members#project-membership
50
+ // Menu Path /c/c-m-hzqf4tqt/explorer/members
51
+ // - Similar current paths
52
+ // /c/c-m-hzqf4tqt/fleet/fleet.cattle.io.bundlenamespacemapping
53
+ // /c/c-m-hzqf4tqt/fleet/fleet.cattle.io.bundle
54
+ // - Other menu items that appear in current menu item
55
+ // /c/c-m-hzqf4tqt/fleet
56
+ // /c/c-m-hzqf4tqt/fleet/management.cattle.io.fleetworkspace
57
+
58
+ // If there's no hash the n-link will determine it's linkActiveClass correctly, so avoid this faff
59
+ const invalidHash = !this.$route.hash;
60
+ // Lets be super safe
61
+ const invalidProps = !this.menuPath || !this.$route.path;
62
+
63
+ if (invalidHash || invalidProps) {
64
+ return false;
65
+ }
66
+
67
+ // We're kind of, but in a fixing way, copying n-link --> vue-router link see vue-router/src/components/link.js & vue-router/src/util/route.js
68
+ // We're only going to compare the path and ignore query and fragment
69
+
70
+ if (this.type.exact) {
71
+ return this.$route.path === this.menuPath;
72
+ }
73
+
74
+ const currentPath = this.$route.path.split('/');
75
+ const menuPath = this.menuPath.split('/');
76
+
77
+ if (menuPath.length > currentPath.length) {
78
+ return false;
79
+ }
80
+
81
+ for (let i = 0; i < menuPath.length; i++) {
82
+ if (menuPath[i] !== currentPath[i]) {
83
+ return false;
84
+ }
85
+ }
86
+
87
+ return true;
88
+ },
89
+
36
90
  showFavorite() {
37
91
  return ( this.type.mode && this.near && showFavoritesFor.includes(this.type.mode) );
38
92
  },
@@ -80,7 +134,7 @@ export default {
80
134
  :to="type.route"
81
135
  tag="li"
82
136
  class="child nav-type"
83
- :class="{'root': isRoot, [`depth-${depth}`]: true}"
137
+ :class="{'root': isRoot, [`depth-${depth}`]: true, [linkActiveClass]: isCurrent}"
84
138
  :exact="type.exact"
85
139
  >
86
140
  <a
@@ -26,7 +26,7 @@ const DEFAULT_LINKS = [
26
26
  },
27
27
  {
28
28
  key: 'getStarted',
29
- value: '/docs/getting-started',
29
+ value: 'https://ranchermanager.docs.rancher.com/getting-started/overview',
30
30
  enabled: true,
31
31
  },
32
32
  ];
@@ -1,4 +1,4 @@
1
- import { AGE, NAME as NAME_HEADER, STATE } from '@shell/config/table-headers';
1
+ import { AGE, NAME as NAME_HEADER, NAMESPACE as NAMESPACE_HEADER, STATE } from '@shell/config/table-headers';
2
2
  import { ISTIO } from '@shell/config/types';
3
3
  import { DSL, IF_HAVE } from '@shell/store/type-map';
4
4
 
@@ -14,9 +14,10 @@ export function init(store) {
14
14
  } = DSL(store, NAME);
15
15
 
16
16
  product({
17
- ifHaveGroup: /^(.*\.)*istio\.io$/,
18
- ifHave: IF_HAVE.NOT_V1_ISTIO,
19
- icon: 'istio',
17
+ ifHaveGroup: /^(.*\.)*istio\.io$/,
18
+ ifHave: IF_HAVE.NOT_V1_ISTIO,
19
+ icon: 'istio',
20
+ showNamespaceFilter: true,
20
21
  });
21
22
 
22
23
  virtualType({
@@ -42,7 +43,9 @@ export function init(store) {
42
43
  'networking.istio.io.envoyfilter',
43
44
  'networking.istio.io.serviceentry',
44
45
  'networking.istio.io.sidecar',
45
- 'networking.istio.io.workloadentrie',
46
+ 'networking.istio.io.proxyconfig',
47
+ 'networking.istio.io.workloadentry',
48
+ 'networking.istio.io.workloadgroup',
46
49
  ], 'Networking');
47
50
 
48
51
  basicType([
@@ -58,9 +61,16 @@ export function init(store) {
58
61
  'security.istio.io.requestauthentication',
59
62
  ], 'Security');
60
63
 
64
+ basicType([
65
+ 'install.istio.io.istiooperator',
66
+ 'telemetry.istio.io.telemetry',
67
+ 'extensions.istio.io.wasmplugin',
68
+ ], 'Advanced');
69
+
61
70
  headers(ISTIO.VIRTUAL_SERVICE, [
62
71
  STATE,
63
72
  NAME_HEADER,
73
+ NAMESPACE_HEADER,
64
74
  {
65
75
  name: 'gateways',
66
76
  label: 'Gateways',
package/config/router.js CHANGED
@@ -6,13 +6,15 @@ import scrollBehavior from '../utils/router.scrollBehavior.js';
6
6
 
7
7
  const emptyFn = () => {};
8
8
 
9
+ export const linkActiveClass = 'nuxt-link-active';
10
+
9
11
  Vue.use(Router);
10
12
 
11
13
  export const routerOptions = {
12
14
  mode: 'history',
13
15
  // Note: router base comes from the ROUTER_BASE env var
14
16
  base: process.env.routerBase || '/',
15
- linkActiveClass: 'nuxt-link-active',
17
+ linkActiveClass,
16
18
  linkExactActiveClass: 'nuxt-link-exact-active',
17
19
  scrollBehavior,
18
20
 
@@ -72,10 +74,6 @@ export const routerOptions = {
72
74
  path: '/auth/verify',
73
75
  component: () => interopDefault(import('../pages/auth/verify.vue' /* webpackChunkName: "pages/auth/verify" */)),
74
76
  name: 'auth-verify'
75
- }, {
76
- path: '/docs/toc',
77
- component: () => interopDefault(import('../pages/docs/toc.js' /* webpackChunkName: "pages/docs/toc" */)),
78
- name: 'docs-toc'
79
77
  }, {
80
78
  path: '/rio/mesh',
81
79
  component: () => interopDefault(import('../pages/rio/mesh.vue' /* webpackChunkName: "pages/rio/mesh" */)),
@@ -84,10 +82,6 @@ export const routerOptions = {
84
82
  path: '/c/:cluster',
85
83
  component: () => interopDefault(import('../pages/c/_cluster/index.vue' /* webpackChunkName: "pages/c/_cluster/index" */)),
86
84
  name: 'c-cluster'
87
- }, {
88
- path: '/docs/:doc?',
89
- component: () => interopDefault(import('../pages/docs/_doc.vue' /* webpackChunkName: "pages/docs/_doc" */)),
90
- name: 'docs-doc'
91
85
  }, {
92
86
  path: '/c/:cluster/apps',
93
87
  component: () => interopDefault(import('../pages/c/_cluster/apps/index.vue' /* webpackChunkName: "pages/c/_cluster/apps/index" */)),
@@ -993,13 +993,12 @@ export const UI_PLUGIN_CATALOG = [
993
993
  name: 'image',
994
994
  sort: ['image'],
995
995
  labelKey: 'plugins.manageCatalog.headers.image.label',
996
- value: 'deploymentImage'
996
+ value: 'image'
997
997
  },
998
998
  {
999
- name: 'cacheState',
1000
- sort: ['cacheState'],
1001
- labelKey: 'plugins.manageCatalog.headers.cacheState.label',
1002
- value: 'cacheState',
1003
- formatter: 'ExtensionCache'
999
+ name: 'repository',
1000
+ sort: ['repository'],
1001
+ labelKey: 'plugins.manageCatalog.headers.repository.label',
1002
+ value: 'repo.metadata.name'
1004
1003
  }
1005
1004
  ];
@@ -49,6 +49,7 @@ export const UI_PLUGIN_CHART_ANNOTATIONS = {
49
49
  UI_VERSION: 'catalog.cattle.io/ui-version',
50
50
  EXTENSIONS_HOST: 'catalog.cattle.io/ui-extensions-host',
51
51
  DISPLAY_NAME: 'catalog.cattle.io/display-name',
52
+ HIDDEN_BUILTIN: 'catalog.cattle.io/ui-hidden-builtin',
52
53
  };
53
54
 
54
55
  // Extension catalog labels
@@ -85,6 +85,9 @@ function checkExtensionRouteBinding($route, locationConfig, context) {
85
85
  // also handle "mode" in a separate way because it mainly depends on query params
86
86
  } else if (param === 'mode') {
87
87
  res = checkRouteMode($route, locationConfigParam);
88
+ } else if (param === 'resource') {
89
+ // Match exact resource but also allow resource of '*' to match any resource
90
+ res = (params[param] && locationConfigParam === '*') || locationConfigParam === params[param];
88
91
  } else if (param === 'context') {
89
92
  // Need all keys and values to match
90
93
  res = isEqual(locationConfigParam, context);
package/core/types.ts CHANGED
@@ -102,7 +102,7 @@ export type Action = {
102
102
  icon?: string;
103
103
  multiple?: boolean;
104
104
  enabled?: Function | boolean;
105
- invoke: (opts: ActionOpts, resources: any[]) => void | boolean | Promise<boolean>;
105
+ invoke: (opts: ActionOpts, resources: any[], globals?: any) => void | boolean | Promise<boolean>;
106
106
  };
107
107
 
108
108
  /** Definition of a panel (options that can be passed when defining an extension panel enhancement) */
@@ -567,3 +567,8 @@ export interface IPlugin {
567
567
  */
568
568
  DSL(store: any, productName: string): DSLReturnType;
569
569
  }
570
+
571
+ // Internal interface
572
+ // Built-in extensions may use this, but external extensions should not, as this is subject to change
573
+ // Defined as any for now
574
+ export type IInternal = any;