@rancher/shell 3.0.7 → 3.0.8-rc.2

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 (123) hide show
  1. package/assets/images/vendor/githubapp.svg +13 -0
  2. package/assets/styles/base/_typography.scss +1 -1
  3. package/assets/styles/global/_layout.scss +21 -35
  4. package/assets/styles/themes/_modern.scss +5 -5
  5. package/assets/translations/en-us.yaml +102 -17
  6. package/assets/translations/zh-hans.yaml +0 -4
  7. package/components/EmberPage.vue +1 -1
  8. package/components/Inactivity.vue +222 -106
  9. package/components/InstallHelmCharts.vue +2 -2
  10. package/components/Resource/Detail/CopyToClipboard.vue +1 -1
  11. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +0 -2
  12. package/components/Resource/Detail/TitleBar/index.vue +10 -6
  13. package/components/ResourceDetail/index.vue +4 -1
  14. package/components/SortableTable/index.vue +18 -2
  15. package/components/{nav/WindowManager → Window}/ContainerLogs.vue +1 -1
  16. package/components/{nav/WindowManager → Window}/ContainerLogsActions.vue +1 -0
  17. package/components/{nav/WindowManager → Window}/__tests__/ContainerLogs.test.ts +1 -1
  18. package/components/{nav/WindowManager → Window}/__tests__/ContainerShell.test.ts +2 -2
  19. package/components/fleet/FleetConfigMapSelector.vue +117 -0
  20. package/components/fleet/FleetSecretSelector.vue +127 -0
  21. package/components/fleet/__tests__/FleetConfigMapSelector.test.ts +125 -0
  22. package/components/fleet/__tests__/FleetSecretSelector.test.ts +82 -0
  23. package/components/form/FileImageSelector.vue +13 -4
  24. package/components/form/FileSelector.vue +11 -2
  25. package/components/form/ResourceLabeledSelect.vue +1 -0
  26. package/components/form/__tests__/ResourceLabeledSelect.test.ts +90 -0
  27. package/components/nav/Header.vue +34 -13
  28. package/components/{DraggableZone.vue → nav/WindowManager/PinArea.vue} +47 -80
  29. package/components/nav/WindowManager/composables/useComponentsMount.ts +70 -0
  30. package/components/nav/WindowManager/composables/useDimensionsHandler.ts +105 -0
  31. package/components/nav/WindowManager/composables/useDragHandler.ts +99 -0
  32. package/components/nav/WindowManager/composables/usePanelHandler.ts +72 -0
  33. package/components/nav/WindowManager/composables/usePanelsHandler.ts +14 -0
  34. package/components/nav/WindowManager/composables/useResizeHandler.ts +167 -0
  35. package/components/nav/WindowManager/composables/useTabsHandler.ts +51 -0
  36. package/components/nav/WindowManager/constants.ts +23 -0
  37. package/components/nav/WindowManager/index.vue +61 -575
  38. package/components/nav/WindowManager/panels/HorizontalPanel.vue +265 -0
  39. package/components/nav/WindowManager/panels/TabBodyContainer.vue +39 -0
  40. package/components/nav/WindowManager/panels/VerticalPanel.vue +308 -0
  41. package/components/templates/default.vue +4 -40
  42. package/components/templates/home.vue +31 -5
  43. package/config/product/auth.js +1 -0
  44. package/config/query-params.js +1 -0
  45. package/config/settings.ts +8 -1
  46. package/config/store.js +4 -2
  47. package/config/types.js +2 -0
  48. package/detail/pod.vue +1 -0
  49. package/dialog/AddonConfigConfirmationDialog.vue +45 -1
  50. package/directives/ui-context.ts +97 -0
  51. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +52 -11
  52. package/edit/auth/AuthProviderWarningBanners.vue +14 -1
  53. package/edit/auth/github-app-steps.vue +97 -0
  54. package/edit/auth/github-steps.vue +75 -0
  55. package/edit/auth/github.vue +94 -65
  56. package/edit/fleet.cattle.io.helmop.vue +51 -2
  57. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +15 -5
  58. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +11 -9
  59. package/edit/provisioning.cattle.io.cluster/rke2.vue +56 -9
  60. package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +28 -2
  61. package/initialize/install-directives.js +2 -0
  62. package/list/projectsecret.vue +1 -1
  63. package/machine-config/azure.vue +1 -1
  64. package/mixins/chart.js +1 -1
  65. package/models/__tests__/chart.test.ts +17 -9
  66. package/models/__tests__/compliance.cattle.io.clusterscanprofile.spec.js +30 -0
  67. package/models/catalog.cattle.io.app.js +1 -1
  68. package/models/chart.js +3 -1
  69. package/models/compliance.cattle.io.clusterscanprofile.js +1 -1
  70. package/models/management.cattle.io.authconfig.js +1 -0
  71. package/package.json +2 -2
  72. package/pages/auth/login.vue +5 -2
  73. package/pages/auth/verify.vue +1 -1
  74. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +3 -2
  75. package/pages/c/_cluster/apps/charts/chart.vue +2 -2
  76. package/pages/c/_cluster/explorer/EventsTable.vue +89 -3
  77. package/pages/c/_cluster/explorer/tools/index.vue +3 -3
  78. package/pages/c/_cluster/settings/performance.vue +12 -25
  79. package/pages/home.vue +313 -12
  80. package/plugins/axios.js +2 -1
  81. package/plugins/dashboard-store/actions.js +1 -1
  82. package/plugins/dashboard-store/resource-class.js +17 -2
  83. package/plugins/steve/steve-pagination-utils.ts +2 -2
  84. package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +5 -1
  85. package/scripts/extension/publish +1 -1
  86. package/store/auth.js +8 -3
  87. package/store/aws.js +8 -6
  88. package/store/features.js +1 -0
  89. package/store/index.js +9 -3
  90. package/store/prefs.js +6 -0
  91. package/store/ui-context.ts +86 -0
  92. package/store/wm.ts +244 -0
  93. package/types/kube/kube-api.ts +2 -1
  94. package/types/rancher/index.d.ts +1 -0
  95. package/types/resources/settings.d.ts +29 -7
  96. package/types/shell/index.d.ts +59 -0
  97. package/types/window-manager.ts +22 -0
  98. package/utils/__tests__/cluster.test.ts +379 -1
  99. package/utils/cluster.js +157 -3
  100. package/utils/dynamic-content/__tests__/config.test.ts +187 -0
  101. package/utils/dynamic-content/__tests__/index.test.ts +390 -0
  102. package/utils/dynamic-content/__tests__/info.test.ts +263 -0
  103. package/utils/dynamic-content/__tests__/new-release.test.ts +216 -0
  104. package/utils/dynamic-content/__tests__/support-notice.test.ts +262 -0
  105. package/utils/dynamic-content/__tests__/util.test.ts +235 -0
  106. package/utils/dynamic-content/config.ts +55 -0
  107. package/utils/dynamic-content/index.ts +273 -0
  108. package/utils/dynamic-content/info.ts +219 -0
  109. package/utils/dynamic-content/new-release.ts +126 -0
  110. package/utils/dynamic-content/support-notice.ts +169 -0
  111. package/utils/dynamic-content/types.d.ts +101 -0
  112. package/utils/dynamic-content/util.ts +122 -0
  113. package/utils/dynamic-importer.js +2 -2
  114. package/utils/inactivity.ts +104 -0
  115. package/utils/pagination-utils.ts +19 -4
  116. package/utils/release-notes.ts +1 -1
  117. package/assets/images/icons/document.svg +0 -3
  118. package/store/wm.js +0 -95
  119. /package/components/{nav/WindowManager → Window}/ChartReadme.vue +0 -0
  120. /package/components/{nav/WindowManager → Window}/ContainerShell.vue +0 -0
  121. /package/components/{nav/WindowManager → Window}/KubectlShell.vue +0 -0
  122. /package/components/{nav/WindowManager → Window}/MachineSsh.vue +0 -0
  123. /package/components/{nav/WindowManager → Window}/Window.vue +0 -0
@@ -1,588 +1,74 @@
1
- <script>
2
- import { mapState } from 'vuex';
3
- import debounce from 'lodash/debounce';
4
- import {
5
- screenRect, boundingRect, BOTTOM, RIGHT, LEFT
6
- } from '@shell/utils/position';
7
-
8
- export default {
9
- emits: ['draggable'],
10
-
11
- data() {
12
- return {
13
- dragOffset: 0,
14
- reportedHeight: this.height,
15
- reportedWidth: this.width,
16
- component: { },
17
- };
18
- },
19
-
20
- computed: {
21
- ...mapState('wm', ['tabs', 'active', 'open', 'userHeight', 'userWidth', 'userPin']),
22
-
23
- height: {
24
- get() {
25
- if ( this.userHeight ) {
26
- return this.userHeight;
27
- }
28
-
29
- const windowHeight = window.innerHeight;
30
- let height = parseInt(window.localStorage.getItem('wm-height'), 10);
31
-
32
- if ( !height ) {
33
- height = Math.round(windowHeight / 2);
34
- }
35
- height = Math.min(height, 3 * windowHeight / 4);
36
-
37
- window.localStorage.setItem('wm-height', height);
38
-
39
- return height;
40
- },
41
-
42
- set(val) {
43
- this.$store.commit('wm/setUserHeight', val);
44
- window.localStorage.setItem('wm-height', val);
45
- this.show();
46
-
47
- return val;
48
- },
49
- },
50
-
51
- width: {
52
- get() {
53
- if (this.userWidth) {
54
- return this.userWidth;
55
- }
56
-
57
- const windowWidth = window.innerWidth;
58
- let width = parseInt(window.localStorage.getItem('wm-width'), 10);
59
-
60
- if (!width) {
61
- width = Math.round(windowWidth / 8);
62
- }
63
- width = Math.min(width, 3 * windowWidth / 4);
64
-
65
- window.localStorage.setItem('wm-width', width);
66
-
67
- return width;
68
- },
69
- set(val) {
70
- this.$store.commit('wm/setUserWidth', val);
71
- window.localStorage.setItem('wm-width', val);
72
- this.show();
73
-
74
- return val;
75
- }
76
- },
77
-
78
- pinClass() {
79
- return `pin-${ this.userPin }`;
80
- },
81
- },
82
-
83
- watch: {
84
- userPin(v) {
85
- if (this.open) {
86
- this.setWmDimensions();
87
- if (v === LEFT || v === RIGHT) {
88
- this.setReportedHeight(window.innerHeight - 55);
89
- }
90
- }
91
- },
92
-
93
- tabs() {
94
- this.toggle(true);
95
- },
96
-
97
- open(neu) {
98
- if ( neu ) {
99
- this.setReportedHeight();
100
- this.show();
101
- } else {
102
- this.hide();
103
- }
104
- },
1
+ <script setup lang="ts">
2
+ import { BOTTOM, LEFT, RIGHT } from '@shell/utils/position';
3
+ import HorizontalPanel from './panels/HorizontalPanel.vue';
4
+ import VerticalPanel from './panels/VerticalPanel.vue';
5
+ import PinArea from './PinArea.vue';
6
+ import useComponentsMount from './composables/useComponentsMount';
7
+ import useTabsHandler from './composables/useTabsHandler';
8
+ import usePanelsHandler from '@shell/components/nav/WindowManager/composables/usePanelsHandler';
9
+ import { Layout, Position } from '@shell/types/window-manager';
10
+
11
+ /**
12
+ * This component is responsible for rendering the window manager panels and tabs.
13
+ * It uses composables to manage the state and behavior of the panels and tabs.
14
+ *
15
+ * Props:
16
+ * - Layouts define which panels are enabled depending on the UI area configuration.
17
+ * - Positions define where the panels are located (bottom, left, right).
18
+ *
19
+ * Behavior:
20
+ * - Panels are conditionally rendered based on the current layout and enabled positions.
21
+ * - Tabs bodies are teleported to their respective containers based on their configuration.
22
+ * - The component for each tab is dynamically loaded and rendered within a keep-alive block.
23
+ * - The PinArea component is included to manage the dragging of tabs.
24
+ */
25
+
26
+ const props = defineProps({
27
+ layout: {
28
+ type: String as () => Layout,
29
+ default: Layout.default,
105
30
  },
106
-
107
- mounted() {
108
- this.toggle(true);
109
- this.queueUpdate = debounce(this.setReportedHeight, 250);
31
+ positions: {
32
+ type: Array as () => Position[],
33
+ default: () => [BOTTOM, LEFT, RIGHT]
110
34
  },
35
+ });
111
36
 
112
- methods: {
113
- switchTo(id) {
114
- this.$store.commit('wm/setActive', id);
115
- },
116
-
117
- toggle(reverse = false) {
118
- if ( this.dragging ) {
119
- return;
120
- }
121
-
122
- if ( this.open ^ reverse ) {
123
- this.hide();
124
- } else {
125
- this.show();
126
- }
127
- },
128
-
129
- show() {
130
- this.setWmDimensions();
131
- this.$store.commit('wm/setOpen', true);
132
- },
133
-
134
- hide() {
135
- if ( this.tabs.length ) {
136
- document.documentElement.style.setProperty('--wm-height', `calc(var(--wm-tab-height, 29px) + 2px)`);
137
- } else {
138
- document.documentElement.style.setProperty('--wm-height', '0');
139
- document.documentElement.style.setProperty('--wm-width', '0');
140
- }
141
-
142
- this.$store.commit('wm/setOpen', false);
143
- },
144
-
145
- dragYStart(event) {
146
- const doc = document.documentElement;
147
-
148
- doc.addEventListener('mousemove', this.dragYMove);
149
- doc.addEventListener('touchmove', this.dragYMove, true);
150
- doc.addEventListener('mouseup', this.dragYEnd);
151
- doc.addEventListener('mouseleave', this.dragYEnd);
152
- doc.addEventListener('touchend touchcancel', this.dragYEnd, true);
153
- doc.addEventListener('touchstart', this.dragYEnd, true);
154
-
155
- const eventY = event.screenY;
156
-
157
- const rect = boundingRect(event.target);
158
- const offset = eventY - rect.top;
159
-
160
- this.dragOffset = offset;
161
- },
162
-
163
- dragYMove(event) {
164
- const screen = screenRect();
165
- const eventY = event.screenY;
166
- const min = 50;
167
- const max = Math.round( 3 * screen.height / 4);
168
-
169
- let neu = screen.height - eventY + this.dragOffset;
170
-
171
- neu = Math.max(min, Math.min(neu, max));
172
-
173
- this.height = neu;
174
- this.dragging = true;
175
- this.queueUpdate();
176
- },
177
-
178
- dragYEnd(event) {
179
- const doc = document.documentElement;
180
-
181
- doc.removeEventListener('mousemove', this.dragYMove);
182
- doc.removeEventListener('touchmove', this.dragYMove, true);
183
- doc.removeEventListener('mouseup', this.dragYEnd);
184
- doc.removeEventListener('mouseleave', this.dragYEnd);
185
- doc.removeEventListener('touchend touchcancel', this.dragYEnd, true);
186
- doc.removeEventListener('touchstart', this.dragYEnd, true);
187
-
188
- this.setReportedHeight();
189
- setTimeout(() => {
190
- this.dragging = false;
191
- }, 100);
192
- },
193
-
194
- dragXStart(event) {
195
- const doc = document.documentElement;
196
-
197
- doc.addEventListener('mousemove', this.dragXMove);
198
- doc.addEventListener('touchmove', this.dragXMove, true);
199
- doc.addEventListener('mouseup', this.dragXEnd);
200
- doc.addEventListener('mouseleave', this.dragXEnd);
201
- doc.addEventListener('touchend touchcancel', this.dragXEnd, true);
202
- doc.addEventListener('touchstart', this.dragXEnd, true);
203
-
204
- const eventX = event.screenX;
205
- const rect = boundingRect(event.target);
206
-
207
- switch (this.userPin) {
208
- case RIGHT:
209
- this.dragOffset = eventX - rect.left;
210
- break;
211
- case LEFT:
212
- this.dragOffset = rect.right - eventX;
213
- break;
214
- }
215
- },
37
+ const { loadComponent } = useComponentsMount();
216
38
 
217
- dragXMove(event) {
218
- const screen = screenRect();
219
- const eventX = event.screenX;
220
-
221
- const min = 250;
222
- const max = Math.round(2 * screen.width / 5);
223
- let neu;
224
-
225
- switch (this.userPin) {
226
- case RIGHT:
227
- neu = screen.width - eventX + this.dragOffset;
228
- break;
229
- case LEFT:
230
- neu = eventX + this.dragOffset;
231
- break;
232
- }
233
-
234
- neu = Math.max(min, Math.min(neu, max));
235
- this.width = neu;
236
- this.dragging = true;
237
- debounce(this.setReportedWidth, 250)();
238
- },
239
-
240
- dragXEnd(event) {
241
- const doc = document.documentElement;
242
-
243
- doc.removeEventListener('mousemove', this.dragXMove);
244
- doc.removeEventListener('touchmove', this.dragXMove, true);
245
- doc.removeEventListener('mouseup', this.dragXEnd);
246
- doc.removeEventListener('mouseleave', this.dragXEnd);
247
- doc.removeEventListener('touchend touchcancel', this.dragXEnd, true);
248
- doc.removeEventListener('touchstart', this.dragXEnd, true);
249
-
250
- this.setReportedWidth();
251
- setTimeout(() => {
252
- this.dragging = false;
253
- }, 100);
254
- },
255
-
256
- setReportedHeight(height = this.height) {
257
- this.reportedHeight = height;
258
- },
259
-
260
- setReportedWidth() {
261
- this.reportedWidth = this.width;
262
- },
263
-
264
- setWmDimensions(forceValue) {
265
- switch (this.userPin) {
266
- case RIGHT:
267
- case LEFT:
268
- document.documentElement.style.setProperty('--wm-height', `${ window.innerHeight - 55 }px`);
269
- document.documentElement.style.setProperty('--wm-width', `${ forceValue || this.width }px`);
270
- break;
271
- case BOTTOM:
272
- document.documentElement.style.setProperty('--wm-height', `${ forceValue || this.height }px`);
273
- break;
274
- }
275
- },
276
-
277
- close(id) {
278
- this.$store.dispatch('wm/close', id);
279
- },
280
-
281
- componentFor(tab) {
282
- if (this.component[tab.component] === undefined) {
283
- if (this.$store.getters['type-map/hasCustomWindowComponent'](tab.component)) {
284
- this.component[tab.component] = this.$store.getters['type-map/importWindowComponent'](tab.component);
285
- } else {
286
- console.warn(`Unable to find window component for type '${ tab.component }'`); // eslint-disable-line no-console
287
- this.component[tab.component] = null;
288
- }
289
- }
290
-
291
- return this.component[tab.component];
292
- },
293
-
294
- emitDraggable(event) {
295
- this.$emit('draggable', event);
296
- },
297
-
298
- resizeVertical(arrowUp) {
299
- const resizeStep = 20;
300
- const height = arrowUp ? this.height + resizeStep : this.height - resizeStep;
301
-
302
- this.height = height;
303
-
304
- this.setWmDimensions(height);
305
- this.setReportedHeight(height);
306
- },
307
-
308
- resizeHorizontal(arrowLeft) {
309
- const resizeStep = 20;
310
- let width;
311
-
312
- if (this.userPin === 'left') {
313
- width = arrowLeft ? this.width - resizeStep : this.width + resizeStep;
314
- } else {
315
- width = arrowLeft ? this.width + resizeStep : this.width - resizeStep;
316
- }
317
-
318
- this.width = width;
319
-
320
- this.setWmDimensions(width);
321
- this.setReportedWidth(width);
322
- }
323
- }
324
- };
39
+ const { isPanelEnabled } = usePanelsHandler({ layout: props.layout, positions: props.positions });
40
+ const { tabs } = useTabsHandler();
325
41
  </script>
326
42
 
327
43
  <template>
328
- <div
329
- v-if="open"
330
- id="windowmanager"
331
- data-testid="windowmanager"
332
- class="windowmanager"
333
- :class="{[pinClass]: true}"
44
+ <HorizontalPanel
45
+ v-if="isPanelEnabled[BOTTOM]"
46
+ :position="BOTTOM"
47
+ />
48
+ <VerticalPanel
49
+ v-if="isPanelEnabled[LEFT]"
50
+ :position="LEFT"
51
+ />
52
+ <VerticalPanel
53
+ v-if="isPanelEnabled[RIGHT]"
54
+ :position="RIGHT"
55
+ />
56
+ <Teleport
57
+ v-for="{ tab, containerId } in tabs"
58
+ :key="tab.id"
59
+ :to="`#${ containerId }`"
334
60
  >
335
- <div
336
- ref="tabs"
337
- class="tabs"
338
- :class="{
339
- 'resizer-left': userPin == 'left',
340
- }"
341
- role="tablist"
342
- @keyup.right.prevent="selectNext(1)"
343
- @keyup.left.prevent="selectNext(-1)"
344
- @mousedown="emitDraggable(true)"
345
- @mouseup="emitDraggable(false)"
346
- >
347
- <div
348
- v-if="userPin == 'right'"
349
- class="resizer resizer-x"
350
- role="button"
351
- tabindex="0"
352
- :aria-label="t('wm.containerShell.resizeShellWindow', {arrow1: 'left', arrow2: 'right'})"
353
- aria-expanded="true"
354
- @mousedown.prevent.stop="dragXStart($event)"
355
- @touchstart.prevent.stop="dragXStart($event)"
356
- @keyup.left.prevent.stop="resizeHorizontal(true)"
357
- @keyup.right.prevent.stop="resizeHorizontal(false)"
358
- >
359
- <i
360
- class="icon icon-code"
361
- :alt="t('wm.containerShell.resizeShellWindow', {arrow1: 'left', arrow2: 'right'})"
362
- />
363
- </div>
364
- <div
365
- v-for="(tab, i) in tabs"
366
- :key="i"
367
- class="tab"
368
- :class="{'active': tab.id === active}"
369
- role="tab"
370
- :aria-selected="tab.id === active"
371
- :aria-label="tab.label"
372
- :aria-controls="`panel-${tab.id}`"
373
- tabindex="0"
374
- @click="switchTo(tab.id)"
375
- @keyup.enter.space="switchTo(tab.id)"
376
- >
377
- <i
378
- v-if="tab.icon"
379
- class="icon"
380
- :class="{['icon-'+ tab.icon]: true}"
381
- :alt="t('wm.containerShell.tabIcon')"
382
- />
383
- <span class="tab-label"> {{ tab.label }}</span>
384
- <i
385
- data-testid="wm-tab-close-button"
386
- class="closer icon icon-x wm-closer-button"
387
- :alt="t('wm.containerShell.closeShellTab', { tab: tab.label })"
388
- tabindex="0"
389
- :aria-label="t('windowmanager.closeTab', { tabId: tab.id })"
390
- @click.stop="close(tab.id)"
391
- @keyup.enter.space.stop="close(tab.id)"
392
- />
393
- </div>
394
- <div
395
- v-if="userPin == 'bottom'"
396
- class="resizer resizer-y"
397
- role="button"
398
- tabindex="0"
399
- :aria-label="t('wm.containerShell.resizeShellWindow', {arrow1: 'up', arrow2: 'down'})"
400
- aria-expanded="true"
401
- @mousedown.prevent.stop="dragYStart($event)"
402
- @touchstart.prevent.stop="dragYStart($event)"
403
- @click="toggle"
404
- @keyup.up.prevent.stop="resizeVertical(true)"
405
- @keyup.down.prevent.stop="resizeVertical(false)"
406
- >
407
- <i
408
- class="icon icon-sort"
409
- :alt="t('wm.containerShell.resizeShellWindow', {arrow1: 'up', arrow2: 'down'})"
410
- />
411
- </div>
412
- <div
413
- v-if="userPin == 'left'"
414
- class="resizer resizer-x resizer-align-right"
415
- role="button"
416
- tabindex="0"
417
- :aria-label="t('wm.containerShell.resizeShellWindow', {arrow1: 'left', arrow2: 'right'})"
418
- aria-expanded="true"
419
- @mousedown.prevent.stop="dragXStart($event)"
420
- @touchstart.prevent.stop="dragXStart($event)"
421
- @keyup.left.prevent.stop="resizeHorizontal(true)"
422
- @keyup.right.prevent.stop="resizeHorizontal(false)"
423
- >
424
- <i
425
- class="icon icon-code"
426
- :alt="t('wm.containerShell.resizeShellWindow', {arrow1: 'left', arrow2: 'right'})"
427
- />
428
- </div>
429
- </div>
430
- <div
431
- v-for="tab in tabs"
432
- :id="`panel-${tab.id}`"
433
- :key="tab.id"
434
- class="body"
435
- :class="{'active': tab.id === active}"
436
- draggable="false"
437
- role="tabpanel"
438
- @dragstart.prevent.stop
439
- @dragend.prevent.stop
440
- @mouseover="emitDraggable(false)"
441
- >
61
+ <keep-alive>
442
62
  <component
443
- :is="componentFor(tab)"
63
+ :is="loadComponent(tab)"
64
+ :key="tab.id"
444
65
  :tab="tab"
445
- :active="tab.id === active"
446
- :height="reportedHeight"
447
- :width="reportedWidth"
66
+ :active="true"
67
+ :height="tab.containerHeight"
68
+ :width="tab.containerWidth"
448
69
  v-bind="tab.attrs"
449
- @close="close(tab.id)"
450
70
  />
451
- </div>
452
- </div>
71
+ </keep-alive>
72
+ </Teleport>
73
+ <PinArea />
453
74
  </template>
454
-
455
- <style lang="scss" scoped>
456
- .windowmanager {
457
- display: grid;
458
- height: var(--wm-height, 0);
459
-
460
- grid-template-areas:
461
- "tabs"
462
- "body";
463
-
464
- grid-template-rows: var(--wm-tab-height) auto;
465
-
466
- .tabs, .body {
467
- max-width: 100%;
468
- }
469
-
470
- .tabs {
471
- grid-area: tabs;
472
- background-color: var(--wm-tabs-bg);
473
- border-top: 1px solid var(--wm-border);
474
- border-bottom: 1px solid var(--wm-border);
475
-
476
- display: flex;
477
- align-content: stretch;
478
-
479
- .tab {
480
- cursor: pointer;
481
- user-select: none;
482
- border-top: 1px solid var(--wm-border);
483
- border-right: 1px solid var(--wm-border);
484
- border-left: 1px solid var(--wm-border);
485
- padding: 5px 10px;
486
- overflow: hidden;
487
- text-overflow: ellipsis;
488
- margin: 0;
489
- display: flex;
490
- min-width: 0;
491
-
492
- .tab-label{
493
- overflow: hidden;
494
- text-overflow: ellipsis;
495
- }
496
-
497
- &.active {
498
- position: relative;
499
- background-color: var(--wm-body-bg);
500
- outline: 1px solid var(--wm-body-bg);
501
- z-index: 1;
502
- }
503
-
504
- &:focus-visible {
505
- @include focus-outline;
506
- outline-offset: -3px;
507
- }
508
-
509
- .closer {
510
- margin-left: 5px;
511
- border: 1px solid var(--body-text);
512
- border-radius: var(--border-radius);
513
- line-height: 12px;
514
- font-size: 10px;
515
- width: 14px;
516
- align-self: center;
517
- display: flex;
518
- justify-content: center;
519
-
520
- &:hover {
521
- border-color: var(--link-border);
522
- color: var(--link-border);
523
- }
524
-
525
- &:focus-visible {
526
- @include focus-outline;
527
- outline-offset: 1px;
528
- }
529
- }
530
- }
531
-
532
- .resizer {
533
- width: var(--wm-tab-height);
534
- padding: 0 5px;
535
- margin: 0 0 0 1px;
536
- text-align: center;
537
- border-left: 1px solid var(--wm-border);
538
- border-right: 1px solid var(--wm-border);
539
- line-height: var(--wm-tab-height);
540
- height: calc(var(--wm-tab-height) + 1px);
541
- flex-grow: 0;
542
-
543
- &:hover {
544
- background-color: var(--wm-closer-hover-bg);
545
- }
546
- }
547
-
548
- .resizer-y {
549
- cursor: ns-resize;
550
- }
551
-
552
- .resizer-x {
553
- cursor: col-resize;
554
- }
555
-
556
- .resizer-align-right {
557
- margin-left: auto;
558
- }
559
-
560
- &.resizer-left {
561
- display: flex;
562
- flex-direction: row;
563
- justify-content: space-between;
564
- }
565
- }
566
-
567
- .body {
568
- grid-area: body;
569
- background-color: var(--wm-body-bg);
570
- display: none;
571
- overflow: hidden;
572
-
573
- &.active {
574
- display: block;
575
- height: 100%;
576
- }
577
- }
578
-
579
- &.pin-right {
580
- border-left: var(--nav-border-size) solid var(--nav-border);
581
- }
582
-
583
- &.pin-left {
584
- border-right: var(--nav-border-size) solid var(--nav-border);
585
- }
586
- }
587
-
588
- </style>