@rancher/shell 3.0.8-rc.1 → 3.0.8-rc.12

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 (345) hide show
  1. package/assets/brand/suse/banner.svg +1 -0
  2. package/assets/brand/suse/dark/banner.svg +1 -0
  3. package/assets/brand/suse/dark/login-landscape.svg +1 -0
  4. package/assets/brand/suse/dark/rancher-logo.svg +1 -1
  5. package/assets/brand/suse/favicon.png +0 -0
  6. package/assets/brand/suse/login-landscape.svg +1 -0
  7. package/assets/brand/suse/metadata.json +11 -1
  8. package/assets/brand/suse/rancher-logo.svg +1 -1
  9. package/assets/fonts/suse/suse-v2-latin-300.woff +0 -0
  10. package/assets/fonts/suse/suse-v2-latin-300.woff2 +0 -0
  11. package/assets/fonts/suse/suse-v2-latin-600.woff +0 -0
  12. package/assets/fonts/suse/suse-v2-latin-600.woff2 +0 -0
  13. package/assets/fonts/suse/suse-v2-latin-700.woff +0 -0
  14. package/assets/fonts/suse/suse-v2-latin-700.woff2 +0 -0
  15. package/assets/fonts/suse/suse-v2-latin-800.woff +0 -0
  16. package/assets/fonts/suse/suse-v2-latin-800.woff2 +0 -0
  17. package/assets/fonts/suse/suse-v2-latin-regular.woff +0 -0
  18. package/assets/fonts/suse/suse-v2-latin-regular.woff2 +0 -0
  19. package/assets/images/content/README.md +5 -0
  20. package/assets/images/content/cloud-native.svg +84 -0
  21. package/assets/images/content/dark/cloud-native.svg +21 -0
  22. package/assets/images/content/dark/shield.svg +59 -0
  23. package/assets/images/content/dark/suse.svg +10 -0
  24. package/assets/images/content/shield.svg +59 -0
  25. package/assets/images/content/suse.svg +10 -0
  26. package/assets/styles/base/_typography.scss +1 -0
  27. package/assets/styles/fonts/_fontstack.scss +53 -1
  28. package/assets/styles/global/_cards.scss +0 -3
  29. package/assets/styles/global/_layout.scss +21 -35
  30. package/assets/styles/themes/_dark.scss +1 -1
  31. package/assets/styles/themes/_light.scss +1 -1
  32. package/assets/styles/themes/_modern.scss +11 -3
  33. package/assets/styles/themes/_suse.scss +116 -24
  34. package/assets/translations/en-us.yaml +94 -10
  35. package/components/AutoscalerCard.vue +113 -0
  36. package/components/AutoscalerTab.vue +94 -0
  37. package/components/BackLink.vue +8 -0
  38. package/components/BannerGraphic.vue +36 -21
  39. package/components/BrandImage.vue +17 -6
  40. package/components/ClusterIconMenu.vue +1 -1
  41. package/components/ClusterProviderIcon.vue +1 -1
  42. package/components/Cron/CronExpressionEditor.vue +1 -1
  43. package/components/Cron/CronExpressionEditorModal.vue +1 -1
  44. package/components/Drawer/Chrome.vue +2 -6
  45. package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +4 -9
  46. package/components/Drawer/ResourceDetailDrawer/YamlTab.vue +3 -8
  47. package/components/Drawer/ResourceDetailDrawer/composables.ts +3 -4
  48. package/components/Drawer/ResourceDetailDrawer/index.vue +4 -9
  49. package/components/Drawer/ResourceDetailDrawer/types.ts +17 -0
  50. package/components/Drawer/types.ts +3 -0
  51. package/components/DynamicContent/DynamicContentBanner.vue +102 -0
  52. package/components/DynamicContent/DynamicContentCloseButton.vue +42 -0
  53. package/components/DynamicContent/DynamicContentIcon.vue +132 -0
  54. package/components/DynamicContent/DynamicContentPanel.vue +112 -0
  55. package/components/DynamicContent/content.ts +78 -0
  56. package/components/EmberPage.vue +1 -1
  57. package/components/IconOrSvg.vue +2 -2
  58. package/components/PaginatedResourceTable.vue +2 -6
  59. package/components/PopoverCard.vue +192 -0
  60. package/components/Questions/__tests__/index.test.ts +159 -0
  61. package/components/Resource/Detail/CopyToClipboard.vue +4 -1
  62. package/components/Resource/Detail/FetchLoader/composables.ts +18 -4
  63. package/components/Resource/Detail/Metadata/Annotations/index.vue +2 -2
  64. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +1 -1
  65. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +4 -0
  66. package/components/Resource/Detail/Metadata/KeyValueRow.vue +1 -1
  67. package/components/Resource/Detail/Metadata/Labels/index.vue +2 -2
  68. package/components/Resource/Detail/Metadata/composables.ts +9 -9
  69. package/components/Resource/Detail/Metadata/index.vue +3 -3
  70. package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +2 -19
  71. package/components/Resource/Detail/ResourcePopover/__tests__/ResourcePopoverCard.test.ts +0 -29
  72. package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +132 -150
  73. package/components/Resource/Detail/ResourcePopover/index.vue +54 -159
  74. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +0 -2
  75. package/components/Resource/Detail/TitleBar/composables.ts +2 -1
  76. package/components/Resource/Detail/TitleBar/index.vue +10 -6
  77. package/components/Resource/Detail/composables.ts +12 -0
  78. package/components/ResourceDetail/Masthead/latest.vue +29 -0
  79. package/components/ResourceDetail/index.vue +4 -1
  80. package/components/ResourceList/Masthead.vue +1 -1
  81. package/components/ResourceTable.vue +1 -1
  82. package/components/SortableTable/index.vue +2 -1
  83. package/components/Tabbed/__tests__/index.test.ts +86 -0
  84. package/components/{nav/WindowManager → Window}/ContainerLogs.vue +1 -1
  85. package/components/{nav/WindowManager → Window}/ContainerLogsActions.vue +1 -0
  86. package/components/{nav/WindowManager → Window}/__tests__/ContainerLogs.test.ts +1 -1
  87. package/components/{nav/WindowManager → Window}/__tests__/ContainerShell.test.ts +2 -2
  88. package/components/__tests__/AutoscalerCard.test.ts +154 -0
  89. package/components/__tests__/AutoscalerTab.test.ts +125 -0
  90. package/components/__tests__/PopoverCard.test.ts +204 -0
  91. package/components/auth/SelectPrincipal.vue +24 -6
  92. package/components/auth/__tests__/SelectPrincipal.test.ts +119 -0
  93. package/components/auth/login/ldap.vue +3 -3
  94. package/components/form/NodeScheduling.vue +2 -2
  95. package/components/formatter/Autoscaler.vue +97 -0
  96. package/components/formatter/InternalExternalIP.vue +198 -24
  97. package/components/formatter/__tests__/Autoscaler.test.ts +156 -0
  98. package/components/formatter/__tests__/InternalExternalIP.test.ts +133 -0
  99. package/components/google/util/__tests__/formatter.test.ts +47 -0
  100. package/components/google/util/formatter.ts +5 -2
  101. package/components/nav/Group.vue +21 -5
  102. package/components/nav/Header.vue +37 -17
  103. package/components/nav/NamespaceFilter.vue +13 -1
  104. package/components/nav/NotificationCenter/index.vue +2 -1
  105. package/components/nav/TopLevelMenu.helper.ts +16 -6
  106. package/components/nav/TopLevelMenu.vue +4 -2
  107. package/components/nav/Type.vue +8 -3
  108. package/components/{DraggableZone.vue → nav/WindowManager/PinArea.vue} +47 -80
  109. package/components/nav/WindowManager/composables/useComponentsMount.ts +70 -0
  110. package/components/nav/WindowManager/composables/useDimensionsHandler.ts +105 -0
  111. package/components/nav/WindowManager/composables/useDragHandler.ts +99 -0
  112. package/components/nav/WindowManager/composables/usePanelHandler.ts +72 -0
  113. package/components/nav/WindowManager/composables/usePanelsHandler.ts +14 -0
  114. package/components/nav/WindowManager/composables/useResizeHandler.ts +167 -0
  115. package/components/nav/WindowManager/composables/useTabsHandler.ts +51 -0
  116. package/components/nav/WindowManager/constants.ts +23 -0
  117. package/components/nav/WindowManager/index.vue +61 -575
  118. package/components/nav/WindowManager/panels/HorizontalPanel.vue +265 -0
  119. package/components/nav/WindowManager/panels/TabBodyContainer.vue +39 -0
  120. package/components/nav/WindowManager/panels/VerticalPanel.vue +308 -0
  121. package/components/nav/__tests__/Type.test.ts +59 -0
  122. package/components/templates/default.vue +4 -40
  123. package/components/templates/home.vue +31 -5
  124. package/components/templates/plain.vue +30 -4
  125. package/components/templates/standalone.vue +1 -1
  126. package/composables/useI18n.ts +10 -1
  127. package/composables/useInterval.ts +15 -0
  128. package/config/__test__/uiplugins.test.ts +309 -0
  129. package/config/labels-annotations.js +9 -1
  130. package/config/product/explorer.js +3 -1
  131. package/config/product/manager.js +20 -9
  132. package/config/router/navigation-guards/clusters.js +3 -3
  133. package/config/router/navigation-guards/products.js +1 -1
  134. package/config/router/routes.js +10 -2
  135. package/config/settings.ts +2 -1
  136. package/config/store.js +4 -2
  137. package/config/table-headers.js +8 -0
  138. package/config/types.js +9 -0
  139. package/config/uiplugins.js +46 -2
  140. package/config/version.js +1 -1
  141. package/core/__test__/extension-manager-impl.test.js +236 -0
  142. package/core/extension-manager-impl.js +21 -4
  143. package/core/plugin-helpers.ts +4 -2
  144. package/core/plugins-loader.js +2 -2
  145. package/core/types-provisioning.ts +8 -1
  146. package/detail/pod.vue +1 -0
  147. package/detail/provisioning.cattle.io.cluster.vue +19 -7
  148. package/dialog/DeveloperLoadExtensionDialog.vue +13 -4
  149. package/dialog/RollbackWorkloadDialog.vue +2 -5
  150. package/dialog/SearchDialog.vue +1 -0
  151. package/directives/ui-context.ts +103 -0
  152. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +2 -2
  153. package/edit/auth/__tests__/oidc.test.ts +26 -0
  154. package/edit/auth/github.vue +5 -0
  155. package/edit/auth/oidc.vue +5 -1
  156. package/edit/autoscaling.horizontalpodautoscaler/index.vue +1 -0
  157. package/edit/cloudcredential.vue +1 -1
  158. package/edit/configmap.vue +1 -0
  159. package/edit/constraints.gatekeeper.sh.constraint/index.vue +1 -0
  160. package/edit/fleet.cattle.io.gitrepo.vue +0 -10
  161. package/edit/fleet.cattle.io.helmop.vue +6 -6
  162. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  163. package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +1 -0
  164. package/edit/logging-flow/index.vue +1 -0
  165. package/edit/logging.banzaicloud.io.output/index.vue +1 -0
  166. package/edit/management.cattle.io.fleetworkspace.vue +1 -1
  167. package/edit/management.cattle.io.project.vue +1 -0
  168. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +4 -1
  169. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +2 -1
  170. package/edit/monitoring.coreos.com.prometheusrule/index.vue +1 -0
  171. package/edit/monitoring.coreos.com.receiver/index.vue +2 -1
  172. package/edit/monitoring.coreos.com.route.vue +1 -1
  173. package/edit/namespace.vue +1 -0
  174. package/edit/networking.istio.io.destinationrule/index.vue +1 -0
  175. package/edit/networking.k8s.io.ingress/index.vue +1 -0
  176. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -0
  177. package/edit/networking.k8s.io.networkpolicy/index.vue +1 -0
  178. package/edit/node.vue +1 -0
  179. package/edit/persistentvolume/index.vue +27 -22
  180. package/edit/persistentvolume/plugins/awsElasticBlockStore.vue +13 -14
  181. package/edit/persistentvolume/plugins/azureDisk.vue +49 -48
  182. package/edit/persistentvolume/plugins/azureFile.vue +15 -14
  183. package/edit/persistentvolume/plugins/cephfs.vue +15 -14
  184. package/edit/persistentvolume/plugins/cinder.vue +15 -14
  185. package/edit/persistentvolume/plugins/csi.vue +18 -16
  186. package/edit/persistentvolume/plugins/fc.vue +13 -14
  187. package/edit/persistentvolume/plugins/flexVolume.vue +15 -14
  188. package/edit/persistentvolume/plugins/flocker.vue +1 -3
  189. package/edit/persistentvolume/plugins/gcePersistentDisk.vue +13 -14
  190. package/edit/persistentvolume/plugins/glusterfs.vue +15 -14
  191. package/edit/persistentvolume/plugins/hostPath.vue +40 -39
  192. package/edit/persistentvolume/plugins/iscsi.vue +13 -14
  193. package/edit/persistentvolume/plugins/local.vue +1 -3
  194. package/edit/persistentvolume/plugins/longhorn.vue +23 -22
  195. package/edit/persistentvolume/plugins/nfs.vue +15 -14
  196. package/edit/persistentvolume/plugins/photonPersistentDisk.vue +1 -14
  197. package/edit/persistentvolume/plugins/portworxVolume.vue +15 -14
  198. package/edit/persistentvolume/plugins/quobyte.vue +15 -14
  199. package/edit/persistentvolume/plugins/rbd.vue +15 -14
  200. package/edit/persistentvolume/plugins/scaleIO.vue +15 -14
  201. package/edit/persistentvolume/plugins/storageos.vue +15 -14
  202. package/edit/persistentvolume/plugins/vsphereVolume.vue +1 -3
  203. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +32 -5
  204. package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.test.ts +35 -0
  205. package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +155 -0
  206. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +21 -21
  207. package/edit/provisioning.cattle.io.cluster/index.vue +28 -18
  208. package/edit/provisioning.cattle.io.cluster/rke2.vue +50 -16
  209. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +107 -5
  210. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +92 -4
  211. package/edit/secret/index.vue +1 -1
  212. package/edit/service.vue +9 -4
  213. package/edit/serviceaccount.vue +1 -0
  214. package/edit/storage.k8s.io.storageclass/index.vue +1 -0
  215. package/edit/workload/index.vue +2 -1
  216. package/edit/workload/mixins/workload.js +1 -1
  217. package/initialize/App.vue +4 -4
  218. package/initialize/install-directives.js +2 -0
  219. package/initialize/install-plugins.js +19 -2
  220. package/list/provisioning.cattle.io.cluster.vue +15 -2
  221. package/machine-config/amazonec2.vue +42 -135
  222. package/machine-config/components/EC2Networking.vue +490 -0
  223. package/machine-config/components/__tests__/EC2Networking.test.ts +148 -0
  224. package/machine-config/components/__tests__/utils/vpcSubnetMockData.js +294 -0
  225. package/machine-config/digitalocean.vue +11 -0
  226. package/machine-config/google.vue +1 -1
  227. package/mixins/__tests__/brand.spec.ts +2 -2
  228. package/mixins/__tests__/chart.test.ts +21 -0
  229. package/mixins/brand.js +1 -7
  230. package/mixins/chart.js +7 -1
  231. package/mixins/create-edit-view/index.js +5 -0
  232. package/models/__tests__/chart.test.ts +33 -4
  233. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +112 -5
  234. package/models/chart.js +25 -13
  235. package/models/cluster/node.js +13 -6
  236. package/models/cluster.x-k8s.io.machine.js +10 -20
  237. package/models/cluster.x-k8s.io.machinedeployment.js +5 -1
  238. package/models/management.cattle.io.cluster.js +21 -3
  239. package/models/management.cattle.io.kontainerdriver.js +1 -0
  240. package/models/provisioning.cattle.io.cluster.js +249 -33
  241. package/package.json +8 -7
  242. package/pages/auth/login.vue +41 -5
  243. package/pages/auth/setup.vue +1 -1
  244. package/pages/auth/verify.vue +3 -3
  245. package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +135 -0
  246. package/pages/c/_cluster/apps/charts/chart.vue +33 -15
  247. package/pages/c/_cluster/apps/charts/index.vue +11 -13
  248. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  249. package/pages/c/_cluster/explorer/index.vue +8 -6
  250. package/pages/c/_cluster/manager/hostedprovider/index.vue +220 -0
  251. package/pages/c/_cluster/settings/brand.vue +1 -1
  252. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +7 -0
  253. package/pages/c/_cluster/uiplugins/catalogs.vue +147 -0
  254. package/pages/c/_cluster/uiplugins/index.vue +126 -184
  255. package/pages/home.vue +14 -4
  256. package/pkg/auto-import.js +3 -3
  257. package/pkg/dynamic-importer.lib.js +5 -1
  258. package/pkg/import.js +1 -1
  259. package/plugins/dashboard-client-init.js +3 -0
  260. package/plugins/dashboard-store/getters.js +19 -2
  261. package/plugins/dashboard-store/model-loader.js +1 -1
  262. package/plugins/dashboard-store/resource-class.js +10 -6
  263. package/plugins/dynamic-content.js +13 -0
  264. package/plugins/i18n.js +8 -0
  265. package/plugins/plugin.js +2 -2
  266. package/plugins/steve/__tests__/steve-pagination-utils.test.ts +333 -0
  267. package/plugins/steve/steve-class.js +1 -1
  268. package/plugins/steve/steve-pagination-utils.ts +39 -20
  269. package/plugins/steve/subscribe.js +17 -9
  270. package/plugins/subscribe-events.ts +4 -2
  271. package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
  272. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +6 -34
  273. package/rancher-components/Pill/RcStatusBadge/index.ts +0 -1
  274. package/rancher-components/Pill/RcStatusBadge/types.ts +1 -1
  275. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +5 -28
  276. package/rancher-components/Pill/RcStatusIndicator/types.ts +2 -1
  277. package/rancher-components/Pill/types.ts +0 -1
  278. package/rancher-components/RcDropdown/RcDropdownItem.vue +1 -0
  279. package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +5 -1
  280. package/rancher-components/RcIcon/RcIcon.test.ts +51 -0
  281. package/rancher-components/RcIcon/RcIcon.vue +46 -0
  282. package/rancher-components/RcIcon/index.ts +1 -0
  283. package/rancher-components/RcIcon/types.ts +160 -0
  284. package/rancher-components/utils/status.test.ts +67 -0
  285. package/rancher-components/utils/status.ts +77 -0
  286. package/scripts/typegen.sh +1 -0
  287. package/store/__tests__/catalog.test.ts +1 -1
  288. package/store/action-menu.js +8 -0
  289. package/store/auth.js +4 -4
  290. package/store/catalog.js +6 -0
  291. package/store/features.js +1 -0
  292. package/store/i18n.js +3 -3
  293. package/store/index.js +40 -19
  294. package/store/notifications.ts +51 -4
  295. package/store/plugins.js +7 -3
  296. package/store/prefs.js +6 -6
  297. package/store/type-map.js +7 -7
  298. package/store/ui-context.ts +86 -0
  299. package/store/wm.ts +244 -0
  300. package/types/notifications/index.ts +27 -3
  301. package/types/shell/index.d.ts +80 -4
  302. package/types/store/__tests__/pagination.types.spec.ts +137 -0
  303. package/types/store/pagination.types.ts +157 -9
  304. package/types/store/subscribe-events.types.ts +8 -1
  305. package/types/store/subscribe.types.ts +1 -0
  306. package/types/window-manager.ts +24 -0
  307. package/utils/__tests__/object.test.ts +19 -0
  308. package/utils/__tests__/provider.test.ts +98 -0
  309. package/utils/__tests__/selector-typed.test.ts +263 -0
  310. package/utils/__tests__/version.test.ts +19 -1
  311. package/utils/autoscaler-utils.ts +7 -0
  312. package/utils/back-off.ts +3 -3
  313. package/utils/brand.ts +29 -0
  314. package/utils/chart.js +18 -0
  315. package/utils/color.js +1 -1
  316. package/utils/dynamic-content/__tests__/announcement.test.ts +498 -0
  317. package/utils/dynamic-content/__tests__/info.test.ts +21 -9
  318. package/utils/dynamic-content/announcement.ts +142 -0
  319. package/utils/dynamic-content/example.json +40 -0
  320. package/utils/dynamic-content/index.ts +6 -2
  321. package/utils/dynamic-content/info.ts +44 -2
  322. package/utils/dynamic-content/new-release.ts +1 -1
  323. package/utils/dynamic-content/notification-handler.ts +48 -0
  324. package/utils/dynamic-content/types.d.ts +53 -1
  325. package/utils/dynamic-importer.js +2 -2
  326. package/utils/favicon.js +4 -4
  327. package/utils/object.js +20 -2
  328. package/utils/pagination-utils.ts +2 -2
  329. package/utils/pagination-wrapper.ts +13 -9
  330. package/utils/provider.ts +14 -0
  331. package/utils/scroll.js +7 -0
  332. package/utils/selector-typed.ts +6 -2
  333. package/utils/settings.ts +15 -0
  334. package/utils/unit-tests/pagination-utils.spec.ts +8 -8
  335. package/utils/validators/machine-pool.ts +13 -3
  336. package/utils/version.js +15 -0
  337. package/vue.config.js +3 -3
  338. package/assets/images/icons/document.svg +0 -3
  339. package/plugins/nuxt-client-init.js +0 -3
  340. package/store/wm.js +0 -95
  341. /package/components/{nav/WindowManager → Window}/ChartReadme.vue +0 -0
  342. /package/components/{nav/WindowManager → Window}/ContainerShell.vue +0 -0
  343. /package/components/{nav/WindowManager → Window}/KubectlShell.vue +0 -0
  344. /package/components/{nav/WindowManager → Window}/MachineSsh.vue +0 -0
  345. /package/components/{nav/WindowManager → Window}/Window.vue +0 -0
@@ -0,0 +1,265 @@
1
+ <script setup lang="ts">
2
+ import { PropType } from 'vue';
3
+ import { BOTTOM } from '@shell/utils/position';
4
+ import { Position } from '@shell/types/window-manager';
5
+ import TabBodyContainer from './TabBodyContainer.vue';
6
+ import usePanelHandler from '../composables/usePanelHandler';
7
+
8
+ const props = defineProps({
9
+ position: {
10
+ type: String as PropType<Position>,
11
+ default: BOTTOM,
12
+ },
13
+ });
14
+
15
+ const {
16
+ tabs,
17
+ activeTab,
18
+ isTabsHeaderEnabled,
19
+ dragOverPositionsActive,
20
+ dragOverTabBarActive,
21
+ setTabActive,
22
+ onTabReady,
23
+ onTabClose,
24
+ mouseResizeYStart,
25
+ keyboardResizeY,
26
+ onTabBarDragOver,
27
+ onTabBarDragLeave,
28
+ onTabBarDrop,
29
+ onDragPositionStart,
30
+ onDragPositionEnd,
31
+ lockedPosition,
32
+ } = usePanelHandler({ position: props.position });
33
+ </script>
34
+
35
+ <template>
36
+ <div
37
+ id="horizontal-window-manager"
38
+ data-testid="horizontal-window-manager"
39
+ class="horizontal-window-manager wm"
40
+ :class="{
41
+ 'drag-start': dragOverPositionsActive,
42
+ 'drag-end': !dragOverPositionsActive,
43
+ 'tabs-header-enabled': isTabsHeaderEnabled,
44
+ }"
45
+ >
46
+ <div
47
+ v-if="isTabsHeaderEnabled"
48
+ :class="['tabs', { 'tab-bar-highlight': dragOverTabBarActive }]"
49
+ role="tablist"
50
+ @dragover="onTabBarDragOver"
51
+ @dragleave="onTabBarDragLeave"
52
+ @drop="onTabBarDrop"
53
+ >
54
+ <div
55
+ v-for="(tab, i) in tabs"
56
+ :key="i"
57
+ class="tab"
58
+ :class="{
59
+ 'active': tab.id === activeTab[props.position],
60
+ 'draggable': !lockedPosition,
61
+ }"
62
+ :draggable="tab.id === activeTab[props.position] && !lockedPosition"
63
+ role="tab"
64
+ :aria-selected="tab.id === activeTab[props.position]"
65
+ :aria-label="tab.label"
66
+ :aria-controls="`panel-${tab.id}`"
67
+ tabindex="0"
68
+ @click="setTabActive({ position: props.position, id: tab.id })"
69
+ @keyup.enter.space="setTabActive({ position: props.position, id: tab.id })"
70
+ @dragstart="onDragPositionStart({ event: $event, tab })"
71
+ @dragend="onDragPositionEnd({ event: $event, tab })"
72
+ >
73
+ <i
74
+ v-if="tab.icon"
75
+ class="icon"
76
+ :class="{
77
+ ['icon-'+ tab.icon]: true,
78
+ }"
79
+ :alt="t('wm.tabIcon')"
80
+ />
81
+ <span
82
+ class="tab-label"
83
+ >
84
+ {{ tab.label }}
85
+ </span>
86
+ <i
87
+ data-testid="wm-tab-close-button"
88
+ class="closer icon icon-x wm-closer-button"
89
+ :alt="t('wm.closeTab', { tabId: tab.label })"
90
+ tabindex="0"
91
+ :aria-label="t('wm.closeTab', { tabId: tab.id })"
92
+ @click.stop="onTabClose(tab.id)"
93
+ @keyup.enter.space.stop="onTabClose(tab.id)"
94
+ />
95
+ </div>
96
+ <div
97
+ class="resizer resizer-y"
98
+ role="button"
99
+ tabindex="0"
100
+ :aria-label="t('wm.resize', {arrow1: 'up', arrow2: 'down'})"
101
+ aria-expanded="true"
102
+ @mousedown.prevent.stop="mouseResizeYStart($event)"
103
+ @touchstart.prevent.stop="mouseResizeYStart($event)"
104
+ @keyup.up.prevent.stop="keyboardResizeY(true)"
105
+ @keyup.down.prevent.stop="keyboardResizeY(false)"
106
+ >
107
+ <i
108
+ class="icon icon-sort"
109
+ :alt="t('wm.resize', {arrow1: 'up', arrow2: 'down'})"
110
+ />
111
+ </div>
112
+ </div>
113
+ <TabBodyContainer
114
+ v-for="tab in tabs"
115
+ v-show="tab.id === activeTab[props.position]"
116
+ :id="tab.id"
117
+ :key="`${ props.position }-${ tab.id }`"
118
+ :position="props.position"
119
+ @ready="onTabReady({ tab, containerId: $event })"
120
+ />
121
+ </div>
122
+ </template>
123
+
124
+ <style lang="scss" scoped>
125
+ .horizontal-window-manager {
126
+ display: grid;
127
+ // z-index: var(--drag-area-wm-z-index);
128
+
129
+ grid-template-areas:
130
+ "body";
131
+ grid-template-rows: auto;
132
+
133
+ &.tabs-header-enabled {
134
+ grid-template-areas:
135
+ "tabs"
136
+ "body";
137
+
138
+ grid-template-rows: var(--wm-tab-height) auto;
139
+ }
140
+
141
+ &.vm {
142
+ height: var(--wm-height, 0);
143
+ }
144
+
145
+ .tabs, .body {
146
+ max-width: 100%;
147
+ }
148
+
149
+ .tabs {
150
+ grid-area: tabs;
151
+ background-color: var(--wm-tabs-bg);
152
+ border-top: 1px solid var(--wm-border);
153
+ border-bottom: 1px solid var(--wm-border);
154
+
155
+ display: flex;
156
+ align-content: stretch;
157
+
158
+ .tab {
159
+ cursor: pointer;
160
+ user-select: none;
161
+ border-top: 1px solid var(--wm-border);
162
+ border-right: 1px solid var(--wm-border);
163
+ border-left: 1px solid var(--wm-border);
164
+ padding: 5px 10px;
165
+ overflow: hidden;
166
+ text-overflow: ellipsis;
167
+ margin: 0;
168
+ display: flex;
169
+ min-width: 0;
170
+
171
+ .tab-label {
172
+ overflow: hidden;
173
+ text-overflow: ellipsis;
174
+ }
175
+
176
+ &.active {
177
+ position: relative;
178
+ background-color: var(--wm-body-bg);
179
+ outline: 1px solid var(--wm-body-bg);
180
+ z-index: 1;
181
+ }
182
+
183
+ &.draggable {
184
+ cursor: grab;
185
+ }
186
+
187
+ &:focus-visible {
188
+ @include focus-outline;
189
+ outline-offset: -3px;
190
+ }
191
+
192
+ .closer {
193
+ margin-left: 5px;
194
+ border: 1px solid var(--body-text);
195
+ border-radius: var(--border-radius);
196
+ line-height: 12px;
197
+ font-size: 10px;
198
+ width: 14px;
199
+ align-self: center;
200
+ display: flex;
201
+ justify-content: center;
202
+ cursor: pointer;
203
+
204
+ &:hover {
205
+ border-color: var(--link-border);
206
+ color: var(--link-border);
207
+ }
208
+
209
+ &:focus-visible {
210
+ @include focus-outline;
211
+ outline-offset: 1px;
212
+ }
213
+ }
214
+ }
215
+
216
+ .resizer {
217
+ width: var(--wm-tab-height);
218
+ padding: 0 5px;
219
+ margin: 0 0 0 1px;
220
+ text-align: center;
221
+ border-left: 1px solid var(--wm-border);
222
+ border-right: 1px solid var(--wm-border);
223
+ line-height: var(--wm-tab-height);
224
+ height: calc(var(--wm-tab-height) + 1px);
225
+ flex-grow: 0;
226
+
227
+ &:hover {
228
+ background-color: var(--wm-closer-hover-bg);
229
+ }
230
+ }
231
+
232
+ .resizer-y {
233
+ cursor: ns-resize;
234
+ }
235
+ }
236
+
237
+ .body {
238
+ grid-area: body;
239
+ background-color: var(--wm-body-bg);
240
+ display: none;
241
+ overflow: hidden;
242
+
243
+ &.active {
244
+ display: block;
245
+ height: 100%;
246
+ }
247
+ }
248
+ }
249
+
250
+ .drag-start {
251
+ z-index: var(--drag-area-wm-z-index);
252
+ opacity: 0.5;
253
+ transition: opacity .3s ease;
254
+ }
255
+
256
+ .drag-end {
257
+ opacity: 1;
258
+ }
259
+
260
+ .tab-bar-highlight {
261
+ transition: height .5s ease;
262
+ background-image: linear-gradient(to top, var(--drag-over-inner-bg), var(--drag-over-outer-bg));
263
+ border-style: dashed hidden hidden hidden;
264
+ }
265
+ </style>
@@ -0,0 +1,39 @@
1
+ <template>
2
+ <div
3
+ :id="contentId"
4
+ class="body active teleport-target"
5
+ role="tabpanel"
6
+ />
7
+ </template>
8
+
9
+ <script setup>
10
+ import { onMounted, defineProps, defineEmits } from 'vue';
11
+
12
+ /**
13
+ * This component serves as a container for the body of a tab within a window manager panel.
14
+ *
15
+ * Props:
16
+ * - id: The unique identifier for the tab.
17
+ * - position: The position of the panel (e.g., left, right, bottom).
18
+ *
19
+ * Behavior:
20
+ * - On mounting, it emits a 'ready' event with the generated content ID for the tab body.
21
+ * This is necessary for teleporting the tab content to the correct location in the DOM, ONLY when the body container DIV is mounted.
22
+ */
23
+ const props = defineProps({
24
+ id: {
25
+ type: String,
26
+ required: true
27
+ },
28
+ position: {
29
+ type: String,
30
+ required: true
31
+ }
32
+ });
33
+
34
+ const emit = defineEmits(['ready']);
35
+
36
+ const contentId = `wm-panel-body-${ props.position }-${ props.id.replace(/[^a-zA-Z0-9_-]/g, '-') }`;
37
+
38
+ onMounted(() => emit('ready', contentId));
39
+ </script>
@@ -0,0 +1,308 @@
1
+ <script setup lang="ts">
2
+ import { RIGHT, LEFT } from '@shell/utils/position';
3
+ import { PropType } from 'vue';
4
+ import { Position } from '@shell/types/window-manager';
5
+ import TabBodyContainer from './TabBodyContainer.vue';
6
+ import usePanelHandler from '../composables/usePanelHandler';
7
+
8
+ const props = defineProps({
9
+ position: {
10
+ type: String as PropType<Position>,
11
+ default: RIGHT,
12
+ },
13
+ });
14
+
15
+ const {
16
+ tabs,
17
+ activeTab,
18
+ isTabsHeaderEnabled,
19
+ dragOverPositionsActive,
20
+ dragOverTabBarActive,
21
+ setTabActive,
22
+ onTabReady,
23
+ onTabClose,
24
+ mouseResizeXStart,
25
+ keyboardResizeX,
26
+ lockedPosition,
27
+ onTabBarDragOver,
28
+ onTabBarDragLeave,
29
+ onTabBarDrop,
30
+ onDragPositionStart,
31
+ onDragPositionEnd,
32
+ } = usePanelHandler({ position: props.position });
33
+ </script>
34
+
35
+ <template>
36
+ <div
37
+ id="vertical-window-manager"
38
+ data-testid="vertical-window-manager"
39
+ class="vertical-window-manager"
40
+ :class="{
41
+ 'wm-vr': props.position === RIGHT,
42
+ 'wm-vl': props.position === LEFT,
43
+ 'drag-start': dragOverPositionsActive,
44
+ 'drag-end': !dragOverPositionsActive,
45
+ 'tabs-header-enabled': isTabsHeaderEnabled,
46
+ }"
47
+ >
48
+ <div
49
+ v-if="isTabsHeaderEnabled"
50
+ :class="[
51
+ 'tabs',
52
+ {
53
+ 'tab-bar-highlight': dragOverTabBarActive,
54
+ 'resizer-left': props.position === LEFT,
55
+ }
56
+ ]"
57
+ role="tablist"
58
+ @dragover="onTabBarDragOver"
59
+ @dragleave="onTabBarDragLeave"
60
+ @drop="onTabBarDrop"
61
+ >
62
+ <div
63
+ v-if="props.position === RIGHT"
64
+ class="resizer resizer-x"
65
+ role="button"
66
+ tabindex="0"
67
+ :aria-label="t('wm.resize', {arrow1: 'left', arrow2: 'right'})"
68
+ aria-expanded="true"
69
+ @mousedown.prevent.stop="mouseResizeXStart($event)"
70
+ @touchstart.prevent.stop="mouseResizeXStart($event)"
71
+ @keyup.left.prevent.stop="keyboardResizeX(true)"
72
+ @keyup.right.prevent.stop="keyboardResizeX(false)"
73
+ >
74
+ <i
75
+ class="icon icon-code"
76
+ :alt="t('wm.resize', {arrow1: 'left', arrow2: 'right'})"
77
+ />
78
+ </div>
79
+ <div
80
+ v-for="(tab, i) in tabs"
81
+ :key="i"
82
+ class="tab"
83
+ :class="{
84
+ 'active': tab.id === activeTab[props.position],
85
+ 'draggable': !lockedPosition,
86
+ }"
87
+ :draggable="tab.id === activeTab[props.position] && !lockedPosition"
88
+ role="tab"
89
+ :aria-selected="tab.id === activeTab[props.position]"
90
+ :aria-label="tab.label"
91
+ :aria-controls="`panel-${tab.id}`"
92
+ tabindex="0"
93
+ @click="setTabActive({ position: props.position, id: tab.id })"
94
+ @keyup.enter.space="setTabActive({ position: props.position, id: tab.id })"
95
+ @dragstart="onDragPositionStart({ event: $event, tab })"
96
+ @dragend="onDragPositionEnd({ event: $event, tab })"
97
+ >
98
+ <i
99
+ v-if="tab.icon"
100
+ class="icon"
101
+ :class="{
102
+ ['icon-'+ tab.icon]: true,
103
+ }"
104
+ :alt="t('wm.tabIcon')"
105
+ />
106
+ <span
107
+ class="tab-label"
108
+ >
109
+ {{ tab.label }}
110
+ </span>
111
+ <i
112
+ data-testid="wm-tab-close-button"
113
+ class="closer icon icon-x wm-closer-button"
114
+ :alt="t('wm.closeTab', { tabId: tab.label })"
115
+ tabindex="0"
116
+ :aria-label="t('wm.closeTab', { tabId: tab.id })"
117
+ @click.stop="onTabClose(tab.id)"
118
+ @keyup.enter.space.stop="onTabClose(tab.id)"
119
+ />
120
+ </div>
121
+ <div
122
+ v-if="props.position === LEFT"
123
+ class="resizer resizer-x resizer-align-right"
124
+ role="button"
125
+ tabindex="0"
126
+ :aria-label="t('wm.resize', {arrow1: 'left', arrow2: 'right'})"
127
+ aria-expanded="true"
128
+ @mousedown.prevent.stop="mouseResizeXStart($event)"
129
+ @touchstart.prevent.stop="mouseResizeXStart($event)"
130
+ @keyup.left.prevent.stop="keyboardResizeX(true)"
131
+ @keyup.right.prevent.stop="keyboardResizeX(false)"
132
+ >
133
+ <i
134
+ class="icon icon-code"
135
+ :alt="t('wm.resize', {arrow1: 'left', arrow2: 'right'})"
136
+ />
137
+ </div>
138
+ </div>
139
+ <TabBodyContainer
140
+ v-for="tab in tabs"
141
+ v-show="tab.id === activeTab[props.position]"
142
+ :id="tab.id"
143
+ :key="`${ props.position }-${ tab.id }`"
144
+ :position="props.position"
145
+ @ready="onTabReady({ tab, containerId: $event })"
146
+ />
147
+ </div>
148
+ </template>
149
+
150
+ <style lang="scss" scoped>
151
+ .vertical-window-manager {
152
+ display: grid;
153
+ // z-index: var(--drag-area-wm-z-index);
154
+
155
+ grid-template-areas:
156
+ "body";
157
+ grid-template-rows: auto;
158
+
159
+ &.tabs-header-enabled {
160
+ grid-template-areas:
161
+ "tabs"
162
+ "body";
163
+
164
+ grid-template-rows: var(--wm-tab-height) auto;
165
+ }
166
+
167
+ &.wm-vr {
168
+ width: var(--wm-vr-width, 0);
169
+ border-left: var(--nav-border-size) solid var(--nav-border);
170
+ }
171
+
172
+ &.wm-vl {
173
+ width: var(--wm-vl-width, 0);
174
+ border-right: var(--nav-border-size) solid var(--nav-border);
175
+ }
176
+
177
+ .tabs, .body {
178
+ max-width: 100%;
179
+ }
180
+
181
+ .tabs {
182
+ grid-area: tabs;
183
+ background-color: var(--wm-tabs-bg);
184
+ border-top: 1px solid var(--wm-border);
185
+ border-bottom: 1px solid var(--wm-border);
186
+
187
+ display: flex;
188
+ align-content: stretch;
189
+
190
+ .tab {
191
+ cursor: pointer;
192
+ user-select: none;
193
+ border-top: 1px solid var(--wm-border);
194
+ border-right: 1px solid var(--wm-border);
195
+ border-left: 1px solid var(--wm-border);
196
+ padding: 5px 10px;
197
+ overflow: hidden;
198
+ text-overflow: ellipsis;
199
+ margin: 0;
200
+ display: flex;
201
+ min-width: 0;
202
+
203
+ .tab-label {
204
+ overflow: hidden;
205
+ text-overflow: ellipsis;
206
+ }
207
+
208
+ &.active {
209
+ position: relative;
210
+ background-color: var(--wm-body-bg);
211
+ outline: 1px solid var(--wm-body-bg);
212
+ z-index: 1;
213
+ }
214
+
215
+ &.draggable {
216
+ cursor: grab;
217
+ }
218
+
219
+ &:focus-visible {
220
+ @include focus-outline;
221
+ outline-offset: -3px;
222
+ }
223
+
224
+ .closer {
225
+ margin-left: 5px;
226
+ border: 1px solid var(--body-text);
227
+ border-radius: var(--border-radius);
228
+ line-height: 12px;
229
+ font-size: 10px;
230
+ width: 14px;
231
+ align-self: center;
232
+ display: flex;
233
+ justify-content: center;
234
+ cursor: pointer;
235
+
236
+ &:hover {
237
+ border-color: var(--link-border);
238
+ color: var(--link-border);
239
+ }
240
+
241
+ &:focus-visible {
242
+ @include focus-outline;
243
+ outline-offset: 1px;
244
+ }
245
+ }
246
+ }
247
+
248
+ .resizer {
249
+ width: var(--wm-tab-height);
250
+ padding: 0 5px;
251
+ margin: 0 0 0 1px;
252
+ text-align: center;
253
+ border-left: 1px solid var(--wm-border);
254
+ border-right: 1px solid var(--wm-border);
255
+ line-height: var(--wm-tab-height);
256
+ height: calc(var(--wm-tab-height) + 1px);
257
+ flex-grow: 0;
258
+
259
+ &:hover {
260
+ background-color: var(--wm-closer-hover-bg);
261
+ }
262
+ }
263
+
264
+ .resizer-x {
265
+ cursor: col-resize;
266
+ }
267
+
268
+ .resizer-align-right {
269
+ margin-left: auto;
270
+ }
271
+
272
+ &.resizer-left {
273
+ display: flex;
274
+ flex-direction: row;
275
+ justify-content: space-between;
276
+ }
277
+ }
278
+
279
+ .body {
280
+ grid-area: body;
281
+ background-color: var(--wm-body-bg);
282
+ display: none;
283
+ overflow: hidden;
284
+
285
+ &.active {
286
+ display: block;
287
+ height: 100%;
288
+ }
289
+ }
290
+ }
291
+
292
+ .drag-start {
293
+ z-index: var(--drag-area-wm-z-index);
294
+ opacity: 0.5;
295
+ transition: opacity .3s ease;
296
+ }
297
+
298
+ .drag-end {
299
+ opacity: 1;
300
+ }
301
+
302
+ .tab-bar-highlight {
303
+ transition: height .5s ease;
304
+ background-image: linear-gradient(to top, var(--drag-over-inner-bg), var(--drag-over-outer-bg));
305
+ border-style: dashed hidden hidden hidden;
306
+ }
307
+
308
+ </style>
@@ -250,6 +250,65 @@ describe('component: Type', () => {
250
250
  });
251
251
  });
252
252
 
253
+ describe('should respect highlightRoute prop', () => {
254
+ it('should not use active class when highlightRoute is false even if route is active', () => {
255
+ const wrapper = shallowMount(Type as any, {
256
+ props: { type: defaultRouteTypeProp, highlightRoute: false },
257
+
258
+ global: {
259
+ directives: { cleanHtml: (identity) => identity },
260
+
261
+ mocks: {
262
+ $store: storeMock, $router: routerMock, $route: routeMock
263
+ },
264
+ stubs: { routerLink: createChildRenderingRouterLinkStub() },
265
+ },
266
+ });
267
+
268
+ const elementWithSelector = wrapper.find(`.${ activeClass }`);
269
+
270
+ expect(elementWithSelector.exists()).toBe(false);
271
+ });
272
+
273
+ it('should not use exact active class when highlightRoute is false even if route is exact active', () => {
274
+ const wrapper = shallowMount(Type as any, {
275
+ props: { type: defaultRouteTypeProp, highlightRoute: false },
276
+
277
+ global: {
278
+ directives: { cleanHtml: (identity) => identity },
279
+
280
+ mocks: {
281
+ $store: storeMock, $router: routerMock, $route: routeMock
282
+ },
283
+ stubs: { routerLink: createChildRenderingRouterLinkStub({ isExactActive: true }) },
284
+ },
285
+ });
286
+
287
+ const elementWithSelector = wrapper.find(`.${ exactActiveClass }`);
288
+
289
+ expect(elementWithSelector.exists()).toBe(false);
290
+ });
291
+
292
+ it('should use active class when highlightRoute is true (default) and route is active', () => {
293
+ const wrapper = shallowMount(Type as any, {
294
+ props: { type: defaultRouteTypeProp, highlightRoute: true },
295
+
296
+ global: {
297
+ directives: { cleanHtml: (identity) => identity },
298
+
299
+ mocks: {
300
+ $store: storeMock, $router: routerMock, $route: routeMock
301
+ },
302
+ stubs: { routerLink: createChildRenderingRouterLinkStub() },
303
+ },
304
+ });
305
+
306
+ const elementWithSelector = wrapper.find(`.${ activeClass }`);
307
+
308
+ expect(elementWithSelector.exists()).toBe(true);
309
+ });
310
+ });
311
+
253
312
  describe('should handle the favorite icon appropriately', () => {
254
313
  it('should show favorite icon if mouse is over and type is favorite', async() => {
255
314
  const wrapper = shallowMount(Type as any, {