@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.
- package/assets/images/vendor/githubapp.svg +13 -0
- package/assets/styles/base/_typography.scss +1 -1
- package/assets/styles/global/_layout.scss +21 -35
- package/assets/styles/themes/_modern.scss +5 -5
- package/assets/translations/en-us.yaml +102 -17
- package/assets/translations/zh-hans.yaml +0 -4
- package/components/EmberPage.vue +1 -1
- package/components/Inactivity.vue +222 -106
- package/components/InstallHelmCharts.vue +2 -2
- package/components/Resource/Detail/CopyToClipboard.vue +1 -1
- package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +0 -2
- package/components/Resource/Detail/TitleBar/index.vue +10 -6
- package/components/ResourceDetail/index.vue +4 -1
- package/components/SortableTable/index.vue +18 -2
- package/components/{nav/WindowManager → Window}/ContainerLogs.vue +1 -1
- package/components/{nav/WindowManager → Window}/ContainerLogsActions.vue +1 -0
- package/components/{nav/WindowManager → Window}/__tests__/ContainerLogs.test.ts +1 -1
- package/components/{nav/WindowManager → Window}/__tests__/ContainerShell.test.ts +2 -2
- package/components/fleet/FleetConfigMapSelector.vue +117 -0
- package/components/fleet/FleetSecretSelector.vue +127 -0
- package/components/fleet/__tests__/FleetConfigMapSelector.test.ts +125 -0
- package/components/fleet/__tests__/FleetSecretSelector.test.ts +82 -0
- package/components/form/FileImageSelector.vue +13 -4
- package/components/form/FileSelector.vue +11 -2
- package/components/form/ResourceLabeledSelect.vue +1 -0
- package/components/form/__tests__/ResourceLabeledSelect.test.ts +90 -0
- package/components/nav/Header.vue +34 -13
- package/components/{DraggableZone.vue → nav/WindowManager/PinArea.vue} +47 -80
- package/components/nav/WindowManager/composables/useComponentsMount.ts +70 -0
- package/components/nav/WindowManager/composables/useDimensionsHandler.ts +105 -0
- package/components/nav/WindowManager/composables/useDragHandler.ts +99 -0
- package/components/nav/WindowManager/composables/usePanelHandler.ts +72 -0
- package/components/nav/WindowManager/composables/usePanelsHandler.ts +14 -0
- package/components/nav/WindowManager/composables/useResizeHandler.ts +167 -0
- package/components/nav/WindowManager/composables/useTabsHandler.ts +51 -0
- package/components/nav/WindowManager/constants.ts +23 -0
- package/components/nav/WindowManager/index.vue +61 -575
- package/components/nav/WindowManager/panels/HorizontalPanel.vue +265 -0
- package/components/nav/WindowManager/panels/TabBodyContainer.vue +39 -0
- package/components/nav/WindowManager/panels/VerticalPanel.vue +308 -0
- package/components/templates/default.vue +4 -40
- package/components/templates/home.vue +31 -5
- package/config/product/auth.js +1 -0
- package/config/query-params.js +1 -0
- package/config/settings.ts +8 -1
- package/config/store.js +4 -2
- package/config/types.js +2 -0
- package/detail/pod.vue +1 -0
- package/dialog/AddonConfigConfirmationDialog.vue +45 -1
- package/directives/ui-context.ts +97 -0
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +52 -11
- package/edit/auth/AuthProviderWarningBanners.vue +14 -1
- package/edit/auth/github-app-steps.vue +97 -0
- package/edit/auth/github-steps.vue +75 -0
- package/edit/auth/github.vue +94 -65
- package/edit/fleet.cattle.io.helmop.vue +51 -2
- package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +15 -5
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +11 -9
- package/edit/provisioning.cattle.io.cluster/rke2.vue +56 -9
- package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +28 -2
- package/initialize/install-directives.js +2 -0
- package/list/projectsecret.vue +1 -1
- package/machine-config/azure.vue +1 -1
- package/mixins/chart.js +1 -1
- package/models/__tests__/chart.test.ts +17 -9
- package/models/__tests__/compliance.cattle.io.clusterscanprofile.spec.js +30 -0
- package/models/catalog.cattle.io.app.js +1 -1
- package/models/chart.js +3 -1
- package/models/compliance.cattle.io.clusterscanprofile.js +1 -1
- package/models/management.cattle.io.authconfig.js +1 -0
- package/package.json +2 -2
- package/pages/auth/login.vue +5 -2
- package/pages/auth/verify.vue +1 -1
- package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +3 -2
- package/pages/c/_cluster/apps/charts/chart.vue +2 -2
- package/pages/c/_cluster/explorer/EventsTable.vue +89 -3
- package/pages/c/_cluster/explorer/tools/index.vue +3 -3
- package/pages/c/_cluster/settings/performance.vue +12 -25
- package/pages/home.vue +313 -12
- package/plugins/axios.js +2 -1
- package/plugins/dashboard-store/actions.js +1 -1
- package/plugins/dashboard-store/resource-class.js +17 -2
- package/plugins/steve/steve-pagination-utils.ts +2 -2
- package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +5 -1
- package/scripts/extension/publish +1 -1
- package/store/auth.js +8 -3
- package/store/aws.js +8 -6
- package/store/features.js +1 -0
- package/store/index.js +9 -3
- package/store/prefs.js +6 -0
- package/store/ui-context.ts +86 -0
- package/store/wm.ts +244 -0
- package/types/kube/kube-api.ts +2 -1
- package/types/rancher/index.d.ts +1 -0
- package/types/resources/settings.d.ts +29 -7
- package/types/shell/index.d.ts +59 -0
- package/types/window-manager.ts +22 -0
- package/utils/__tests__/cluster.test.ts +379 -1
- package/utils/cluster.js +157 -3
- package/utils/dynamic-content/__tests__/config.test.ts +187 -0
- package/utils/dynamic-content/__tests__/index.test.ts +390 -0
- package/utils/dynamic-content/__tests__/info.test.ts +263 -0
- package/utils/dynamic-content/__tests__/new-release.test.ts +216 -0
- package/utils/dynamic-content/__tests__/support-notice.test.ts +262 -0
- package/utils/dynamic-content/__tests__/util.test.ts +235 -0
- package/utils/dynamic-content/config.ts +55 -0
- package/utils/dynamic-content/index.ts +273 -0
- package/utils/dynamic-content/info.ts +219 -0
- package/utils/dynamic-content/new-release.ts +126 -0
- package/utils/dynamic-content/support-notice.ts +169 -0
- package/utils/dynamic-content/types.d.ts +101 -0
- package/utils/dynamic-content/util.ts +122 -0
- package/utils/dynamic-importer.js +2 -2
- package/utils/inactivity.ts +104 -0
- package/utils/pagination-utils.ts +19 -4
- package/utils/release-notes.ts +1 -1
- package/assets/images/icons/document.svg +0 -3
- package/store/wm.js +0 -95
- /package/components/{nav/WindowManager → Window}/ChartReadme.vue +0 -0
- /package/components/{nav/WindowManager → Window}/ContainerShell.vue +0 -0
- /package/components/{nav/WindowManager → Window}/KubectlShell.vue +0 -0
- /package/components/{nav/WindowManager → Window}/MachineSsh.vue +0 -0
- /package/components/{nav/WindowManager → Window}/Window.vue +0 -0
|
@@ -67,18 +67,19 @@ export default {
|
|
|
67
67
|
const shellShortcut = '(Ctrl+`)';
|
|
68
68
|
|
|
69
69
|
return {
|
|
70
|
-
authInfo:
|
|
71
|
-
show:
|
|
72
|
-
showTooltip:
|
|
73
|
-
isUserMenuOpen:
|
|
74
|
-
isPageActionMenuOpen:
|
|
75
|
-
kubeConfigCopying:
|
|
70
|
+
authInfo: {},
|
|
71
|
+
show: false,
|
|
72
|
+
showTooltip: false,
|
|
73
|
+
isUserMenuOpen: false,
|
|
74
|
+
isPageActionMenuOpen: false,
|
|
75
|
+
kubeConfigCopying: false,
|
|
76
76
|
searchShortcut,
|
|
77
77
|
shellShortcut,
|
|
78
78
|
LOGGED_OUT,
|
|
79
|
-
navHeaderRight:
|
|
80
|
-
extensionHeaderActions:
|
|
81
|
-
|
|
79
|
+
navHeaderRight: null,
|
|
80
|
+
extensionHeaderActions: getApplicableExtensionEnhancements(this, ExtensionPoint.ACTION, ActionLocation.HEADER, this.$route),
|
|
81
|
+
extensionActionsEnabled: {},
|
|
82
|
+
ctx: this
|
|
82
83
|
};
|
|
83
84
|
},
|
|
84
85
|
|
|
@@ -252,6 +253,7 @@ export default {
|
|
|
252
253
|
handler(neu) {
|
|
253
254
|
if (neu) {
|
|
254
255
|
this.extensionHeaderActions = getApplicableExtensionEnhancements(this, ExtensionPoint.ACTION, ActionLocation.HEADER, neu);
|
|
256
|
+
this.updateExtensionActionsEnabled();
|
|
255
257
|
|
|
256
258
|
this.navHeaderRight = this.$plugin?.getDynamic('component', 'NavHeaderRight');
|
|
257
259
|
}
|
|
@@ -368,7 +370,7 @@ export default {
|
|
|
368
370
|
});
|
|
369
371
|
},
|
|
370
372
|
|
|
371
|
-
handleExtensionAction(action, event) {
|
|
373
|
+
async handleExtensionAction(action, event) {
|
|
372
374
|
const fn = action.invoke;
|
|
373
375
|
const opts = {
|
|
374
376
|
event,
|
|
@@ -377,7 +379,7 @@ export default {
|
|
|
377
379
|
product: this.currentProduct.name,
|
|
378
380
|
cluster: this.currentCluster,
|
|
379
381
|
};
|
|
380
|
-
const enabled =
|
|
382
|
+
const enabled = await this.isActionEnabled(action);
|
|
381
383
|
|
|
382
384
|
if (fn && enabled) {
|
|
383
385
|
fn.apply(this, [opts, [], { $route: this.$route }]);
|
|
@@ -393,7 +395,25 @@ export default {
|
|
|
393
395
|
}
|
|
394
396
|
|
|
395
397
|
return null;
|
|
396
|
-
}
|
|
398
|
+
},
|
|
399
|
+
|
|
400
|
+
async updateExtensionActionsEnabled() {
|
|
401
|
+
for (const [i, action] of this.extensionHeaderActions.entries()) {
|
|
402
|
+
this.extensionActionsEnabled[i] = await this.isActionEnabled(action);
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
|
|
406
|
+
async isActionEnabled(action) {
|
|
407
|
+
if (action.enabled === undefined) {
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (typeof action.enabled === 'function') {
|
|
412
|
+
return await action.enabled(this.ctx);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return action.enabled;
|
|
416
|
+
},
|
|
397
417
|
}
|
|
398
418
|
};
|
|
399
419
|
</script>
|
|
@@ -642,7 +662,7 @@ export default {
|
|
|
642
662
|
:key="`${action.label}${i}`"
|
|
643
663
|
v-clean-tooltip="handleExtensionTooltip(action)"
|
|
644
664
|
v-shortkey="action.shortcutKey"
|
|
645
|
-
:disabled="
|
|
665
|
+
:disabled="!extensionActionsEnabled[i]"
|
|
646
666
|
type="button"
|
|
647
667
|
class="btn header-btn role-tertiary"
|
|
648
668
|
:data-testid="`extension-header-action-${ action.labelKey || action.label }`"
|
|
@@ -842,6 +862,7 @@ export default {
|
|
|
842
862
|
.side-menu-logo {
|
|
843
863
|
align-items: center;
|
|
844
864
|
display: flex;
|
|
865
|
+
height: 55px;
|
|
845
866
|
margin-right: 8px;
|
|
846
867
|
max-width: 200px;
|
|
847
868
|
padding: 12px 0;
|
|
@@ -1,105 +1,71 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import { mapState } from 'vuex';
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { onMounted } from 'vue';
|
|
4
3
|
import { BOTTOM, CENTER, LEFT, RIGHT } from '@shell/utils/position';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
computed: {
|
|
28
|
-
|
|
29
|
-
...mapState('wm', ['userPin']),
|
|
30
|
-
|
|
31
|
-
pin: {
|
|
32
|
-
get(): Zone {
|
|
33
|
-
return this.userPin;
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
set(pin: Zone) {
|
|
37
|
-
if (pin === CENTER) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
window.localStorage.setItem('wm-pin', pin as string);
|
|
41
|
-
this.$store.commit('wm/setUserPin', pin);
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
methods: {
|
|
48
|
-
|
|
49
|
-
onDragStart() {
|
|
50
|
-
this.drag.active = true;
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
onDragOver(event: DragEvent, zone: Zone) {
|
|
54
|
-
this.drag.zone = zone;
|
|
55
|
-
if (zone !== CENTER) {
|
|
56
|
-
event.preventDefault();
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
onDragEnd() {
|
|
61
|
-
this.pin = this.drag.zone;
|
|
62
|
-
this.drag = {
|
|
63
|
-
active: false,
|
|
64
|
-
zone: CENTER,
|
|
65
|
-
};
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
}
|
|
4
|
+
import useDragHandler from './composables/useDragHandler';
|
|
5
|
+
import { Z_INDEX } from './constants';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* This component is responsible for rendering the pin area used during tab dragging.
|
|
9
|
+
*
|
|
10
|
+
* Behavior:
|
|
11
|
+
* - Enable drag areas for each position (left, right, bottom, center) when dragging is active.
|
|
12
|
+
* - Highlights the area where the tab can be pinned.
|
|
13
|
+
* - Uses z-index variables to ensure proper layering of drag areas.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const {
|
|
17
|
+
dragOverPositionsActive, pin, pinArea, lockedPositions, onDragPositionOver
|
|
18
|
+
} = useDragHandler();
|
|
19
|
+
|
|
20
|
+
onMounted(() => {
|
|
21
|
+
Object.keys(Z_INDEX).forEach((key) => {
|
|
22
|
+
document.documentElement.style.setProperty(
|
|
23
|
+
`--drag-area-${ key.toLowerCase().replaceAll('_', '-') }-z-index`, (Z_INDEX as any)[key].toString()
|
|
24
|
+
);
|
|
25
|
+
});
|
|
69
26
|
});
|
|
70
27
|
</script>
|
|
71
28
|
|
|
72
29
|
<template>
|
|
73
|
-
<div
|
|
30
|
+
<div
|
|
31
|
+
v-if="dragOverPositionsActive"
|
|
32
|
+
class="pin-area-container"
|
|
33
|
+
>
|
|
74
34
|
<span
|
|
75
|
-
v-if="
|
|
35
|
+
v-if="pinArea != pin"
|
|
76
36
|
class="pin-effect-area"
|
|
77
|
-
:class="
|
|
37
|
+
:class="pinArea"
|
|
78
38
|
/>
|
|
79
39
|
<span
|
|
80
40
|
class="drag-area center"
|
|
81
|
-
@dragover="
|
|
41
|
+
@dragover="onDragPositionOver($event, CENTER)"
|
|
82
42
|
/>
|
|
83
43
|
<span
|
|
44
|
+
v-if="!lockedPositions.includes(RIGHT)"
|
|
84
45
|
class="drag-area right"
|
|
85
|
-
@dragover="
|
|
46
|
+
@dragover="onDragPositionOver($event, RIGHT)"
|
|
86
47
|
/>
|
|
87
48
|
<span
|
|
88
|
-
|
|
89
|
-
|
|
49
|
+
v-if="!lockedPositions.includes(LEFT)"
|
|
50
|
+
class="drag-area left"
|
|
51
|
+
@dragover="onDragPositionOver($event, LEFT)"
|
|
90
52
|
/>
|
|
91
53
|
<span
|
|
92
|
-
|
|
93
|
-
|
|
54
|
+
v-if="!lockedPositions.includes(BOTTOM)"
|
|
55
|
+
class="drag-area bottom"
|
|
56
|
+
@dragover="onDragPositionOver($event, BOTTOM)"
|
|
94
57
|
/>
|
|
95
58
|
</div>
|
|
96
59
|
</template>
|
|
97
60
|
|
|
98
61
|
<style lang='scss' scoped>
|
|
62
|
+
.pin-area-container {
|
|
63
|
+
display: contents;
|
|
64
|
+
}
|
|
99
65
|
|
|
100
66
|
.pin-effect-area {
|
|
101
67
|
position: absolute;
|
|
102
|
-
z-index:
|
|
68
|
+
z-index: var(--drag-area-pin-effect-z-index);
|
|
103
69
|
width: 0;
|
|
104
70
|
height: 0;
|
|
105
71
|
border-style: hidden;
|
|
@@ -142,10 +108,8 @@ export default defineComponent({
|
|
|
142
108
|
}
|
|
143
109
|
}
|
|
144
110
|
|
|
145
|
-
// ToDo make height and width as input variable
|
|
146
111
|
.drag-area {
|
|
147
112
|
position: absolute;
|
|
148
|
-
z-index: 999;
|
|
149
113
|
width: 0;
|
|
150
114
|
height: 0;
|
|
151
115
|
opacity: 0;
|
|
@@ -155,7 +119,7 @@ export default defineComponent({
|
|
|
155
119
|
right: 0;
|
|
156
120
|
width: 100%;
|
|
157
121
|
height: 100%;
|
|
158
|
-
z-index:
|
|
122
|
+
z-index: var(--drag-area-center-z-index);
|
|
159
123
|
}
|
|
160
124
|
|
|
161
125
|
&.right {
|
|
@@ -163,6 +127,7 @@ export default defineComponent({
|
|
|
163
127
|
right: 0;
|
|
164
128
|
width: 300px;
|
|
165
129
|
height: 100%;
|
|
130
|
+
z-index: var(--drag-area-right-z-index);
|
|
166
131
|
}
|
|
167
132
|
|
|
168
133
|
&.left {
|
|
@@ -170,12 +135,14 @@ export default defineComponent({
|
|
|
170
135
|
left: 0;
|
|
171
136
|
width: 300px;
|
|
172
137
|
height: 100%;
|
|
138
|
+
z-index: var(--drag-area-left-z-index);
|
|
173
139
|
}
|
|
174
140
|
|
|
175
141
|
&.bottom {
|
|
176
142
|
bottom: 0;
|
|
177
143
|
height: 270px;
|
|
178
144
|
width: 100%;
|
|
145
|
+
z-index: var(--drag-area-bottom-z-index);
|
|
179
146
|
}
|
|
180
147
|
}
|
|
181
148
|
</style>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ref,
|
|
3
|
+
markRaw,
|
|
4
|
+
} from 'vue';
|
|
5
|
+
import { useStore } from 'vuex';
|
|
6
|
+
|
|
7
|
+
const warn = (msg: string, ...args: any[]) => {
|
|
8
|
+
console.warn(`[wm] ${ msg } ${ args?.reduce((acc, v) => `${ acc } '${ v }'`, '') }`); /* eslint-disable-line no-console */
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* This composable is responsible for loading the tabs body components.
|
|
13
|
+
*
|
|
14
|
+
* - It supports loading components from Rancher extensions as well as from the TypeMap (defined in shell/components/Window directory).
|
|
15
|
+
* - The component is cached after the first load to optimize performance.
|
|
16
|
+
*
|
|
17
|
+
* Loading a component from extension:
|
|
18
|
+
*
|
|
19
|
+
* // Register the component in the extension index file:
|
|
20
|
+
* plugin.register('component', 'TestComponent', defineAsyncComponent(() => import('./pages/TestComponent.vue')));
|
|
21
|
+
*
|
|
22
|
+
* // Use the component in a tab by specifying its name and extensionId:
|
|
23
|
+
* store.dispatch('wm/open', {
|
|
24
|
+
* id: PRODUCT_NAME,
|
|
25
|
+
* extensionId: PRODUCT_NAME,
|
|
26
|
+
* label: 'Label',
|
|
27
|
+
* component: 'TestComponent',
|
|
28
|
+
* position: 'bottom',
|
|
29
|
+
* layouts: [
|
|
30
|
+
* Layout.default,
|
|
31
|
+
* Layout.home
|
|
32
|
+
* ],
|
|
33
|
+
* showHeader: false,
|
|
34
|
+
* }, { root: true });
|
|
35
|
+
*
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
export default () => {
|
|
39
|
+
const store = useStore();
|
|
40
|
+
|
|
41
|
+
const components = ref<any>({});
|
|
42
|
+
|
|
43
|
+
function loadComponent(tab: { component?: string, extensionId?: string }) {
|
|
44
|
+
const { component: name, extensionId } = tab || {};
|
|
45
|
+
|
|
46
|
+
if (!name) {
|
|
47
|
+
warn('component name not provided');
|
|
48
|
+
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!components.value[name]) {
|
|
53
|
+
if (!!extensionId) {
|
|
54
|
+
warn(`loading component from extension`, name, extensionId);
|
|
55
|
+
components.value[name] = markRaw((store as any).$extension?.getDynamic('component', name));
|
|
56
|
+
} else if (store.getters['type-map/hasCustomWindowComponent'](name)) {
|
|
57
|
+
warn(`loading component from TypeMap`, name);
|
|
58
|
+
components.value[name] = markRaw(store.getters['type-map/importWindowComponent'](name));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!components.value[name]) {
|
|
63
|
+
warn(`component not found for`, name);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return components.value[name];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return { loadComponent };
|
|
70
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { computed } from 'vue';
|
|
2
|
+
import debounce from 'lodash/debounce';
|
|
3
|
+
import { useStore } from 'vuex';
|
|
4
|
+
import { BOTTOM, LEFT, RIGHT } from '@shell/utils/position';
|
|
5
|
+
import { Position } from '@shell/types/window-manager';
|
|
6
|
+
import { CSS_KEY } from '../constants';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* This composable is responsible for handling the dimensions of the window manager panels.
|
|
10
|
+
*/
|
|
11
|
+
export default (props: { position: Position }) => {
|
|
12
|
+
const store = useStore();
|
|
13
|
+
|
|
14
|
+
const height = computed({
|
|
15
|
+
get() {
|
|
16
|
+
const panelHeight = store.state.wm.panelHeight[props.position];
|
|
17
|
+
|
|
18
|
+
if (panelHeight) {
|
|
19
|
+
return panelHeight;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const windowHeight = window.innerHeight;
|
|
23
|
+
let height = panelHeight ? parseInt(panelHeight, 10) : 0;
|
|
24
|
+
|
|
25
|
+
if ( !height ) {
|
|
26
|
+
height = Math.round(windowHeight / 2);
|
|
27
|
+
}
|
|
28
|
+
height = Math.min(height, 3 * windowHeight / 4);
|
|
29
|
+
|
|
30
|
+
setDimensions({ height });
|
|
31
|
+
|
|
32
|
+
return height;
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
set(height) {
|
|
36
|
+
setDimensions({ height });
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const width = computed({
|
|
41
|
+
get() {
|
|
42
|
+
const panelWidth = store.state.wm.panelWidth[props.position];
|
|
43
|
+
|
|
44
|
+
if (panelWidth) {
|
|
45
|
+
return panelWidth;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const windowWidth = window.innerWidth;
|
|
49
|
+
let width = panelWidth ? parseInt(panelWidth, 10) : 0;
|
|
50
|
+
|
|
51
|
+
if (!width) {
|
|
52
|
+
width = Math.round(windowWidth / 8);
|
|
53
|
+
}
|
|
54
|
+
width = Math.min(width, 3 * windowWidth / 4);
|
|
55
|
+
|
|
56
|
+
setDimensions({ width });
|
|
57
|
+
|
|
58
|
+
return width;
|
|
59
|
+
},
|
|
60
|
+
set(width) {
|
|
61
|
+
setDimensions({ width });
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
function openPanel(position: Position) {
|
|
66
|
+
setCssVariable(position, position === BOTTOM ? height.value : width.value);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function closePanel(position: Position) {
|
|
70
|
+
setCssVariable(position, 0);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function setDimensions(args: { width?: number, height?: number } = {}) {
|
|
74
|
+
const h = Number(args.height || height.value) || 0;
|
|
75
|
+
const w = Number(args.width || width.value) || 0;
|
|
76
|
+
|
|
77
|
+
switch (props.position) {
|
|
78
|
+
case RIGHT:
|
|
79
|
+
case LEFT:
|
|
80
|
+
setCssVariable(props.position, w);
|
|
81
|
+
debouncedSetPanelWidth(props.position, w);
|
|
82
|
+
break;
|
|
83
|
+
case BOTTOM:
|
|
84
|
+
setCssVariable(BOTTOM, h);
|
|
85
|
+
debouncedSetPanelHeight(props.position, h);
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const debouncedSetPanelWidth = debounce((position, width) => {
|
|
91
|
+
store.commit('wm/setPanelWidth', { position, width });
|
|
92
|
+
}, 250);
|
|
93
|
+
|
|
94
|
+
const debouncedSetPanelHeight = debounce((position, height) => {
|
|
95
|
+
store.commit('wm/setPanelHeight', { position, height });
|
|
96
|
+
}, 250);
|
|
97
|
+
|
|
98
|
+
function setCssVariable(position: Position, value: number) {
|
|
99
|
+
document.documentElement.style.setProperty(CSS_KEY[position as keyof typeof CSS_KEY], `${ value }px`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
width, height, setDimensions, openPanel, closePanel,
|
|
104
|
+
};
|
|
105
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { useStore } from 'vuex';
|
|
2
|
+
import { Position, Tab } from '@shell/types/window-manager';
|
|
3
|
+
import { computed, ref } from 'vue';
|
|
4
|
+
import { CENTER } from '@shell/utils/position';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* This composable is responsible for handling the drag-and-drop behavior of tabs within the window manager.
|
|
8
|
+
*
|
|
9
|
+
* dragOverPositionsActive: A reactive reference indicating whether a drag operation is currently over the position areas.
|
|
10
|
+
* pinArea: A reactive reference representing the current position area being hovered over during a drag operation.
|
|
11
|
+
*
|
|
12
|
+
* Both of these references are shared across all instances of this composable (used by Horizontal and Vertical Panels)
|
|
13
|
+
* to maintain consistent drag-and-drop state.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const dragOverPositionsActive = ref(false);
|
|
17
|
+
const pinArea = ref(CENTER as Position);
|
|
18
|
+
|
|
19
|
+
export default (props?: { position: Position }) => {
|
|
20
|
+
const store = useStore();
|
|
21
|
+
|
|
22
|
+
const lockedPositions = computed(() => (store.state.wm.lockedPositions || []) as Position[]);
|
|
23
|
+
const lockedPosition = computed(() => props?.position ? lockedPositions.value.includes(props.position) : false);
|
|
24
|
+
|
|
25
|
+
const pin = computed({
|
|
26
|
+
get(): Position {
|
|
27
|
+
return store.state.wm.userPin;
|
|
28
|
+
},
|
|
29
|
+
set(pin: Position) {
|
|
30
|
+
if (pin !== CENTER) {
|
|
31
|
+
store.commit('wm/setUserPin', pin);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const dragOverTabBarActive = ref(false);
|
|
37
|
+
|
|
38
|
+
function onDragPositionStart(value: { event: DragEvent, tab: Tab }) {
|
|
39
|
+
value.event.dataTransfer?.setData('application/json', JSON.stringify({
|
|
40
|
+
position: value.tab.position,
|
|
41
|
+
tabId: value.tab.id
|
|
42
|
+
}));
|
|
43
|
+
dragOverPositionsActive.value = true;
|
|
44
|
+
dragOverTabBarActive.value = true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function onDragPositionOver(event: DragEvent, position: Position) {
|
|
48
|
+
pinArea.value = position;
|
|
49
|
+
if (position !== CENTER) {
|
|
50
|
+
event.preventDefault();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function onDragPositionEnd(value: { event: DragEvent, tab: Tab }) {
|
|
55
|
+
pin.value = pinArea.value;
|
|
56
|
+
if (pinArea.value !== CENTER) {
|
|
57
|
+
store.commit('wm/switchTab', { tabId: value.tab.id, targetPosition: pinArea.value });
|
|
58
|
+
}
|
|
59
|
+
dragOverPositionsActive.value = false;
|
|
60
|
+
pinArea.value = CENTER;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function onTabBarDragOver(event: DragEvent) {
|
|
64
|
+
dragOverTabBarActive.value = true;
|
|
65
|
+
event.preventDefault();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function onTabBarDragLeave() {
|
|
69
|
+
dragOverTabBarActive.value = false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function onTabBarDrop(event: DragEvent) {
|
|
73
|
+
dragOverTabBarActive.value = false;
|
|
74
|
+
const data = event.dataTransfer?.getData('application/json');
|
|
75
|
+
|
|
76
|
+
if (data) {
|
|
77
|
+
const { tabId } = JSON.parse(data);
|
|
78
|
+
|
|
79
|
+
store.commit('wm/switchTab', { tabId, targetPosition: props?.position });
|
|
80
|
+
} else {
|
|
81
|
+
console.warn('No data found in drag event'); // eslint-disable-line no-console
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
dragOverPositionsActive,
|
|
87
|
+
dragOverTabBarActive,
|
|
88
|
+
pinArea,
|
|
89
|
+
pin,
|
|
90
|
+
lockedPositions,
|
|
91
|
+
lockedPosition,
|
|
92
|
+
onTabBarDragOver,
|
|
93
|
+
onTabBarDragLeave,
|
|
94
|
+
onTabBarDrop,
|
|
95
|
+
onDragPositionStart,
|
|
96
|
+
onDragPositionOver,
|
|
97
|
+
onDragPositionEnd
|
|
98
|
+
};
|
|
99
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { computed, onMounted, onBeforeUnmount } from 'vue';
|
|
2
|
+
import { useStore } from 'vuex';
|
|
3
|
+
import { Position, Tab } from '@shell/types/window-manager';
|
|
4
|
+
import useResizeHandler from '../composables/useResizeHandler';
|
|
5
|
+
import useDimensionsHandler from '../composables/useDimensionsHandler';
|
|
6
|
+
import useDragHandler from '../composables/useDragHandler';
|
|
7
|
+
import useTabsHandler from '../composables/useTabsHandler';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* This composable is responsible for managing the state and behavior of the window manager panel.
|
|
11
|
+
*/
|
|
12
|
+
export default (props: { position: Position }) => {
|
|
13
|
+
const store = useStore();
|
|
14
|
+
|
|
15
|
+
const tabs = computed(() => store.getters['wm/tabs'].filter((t: Tab) => t.position === props.position));
|
|
16
|
+
|
|
17
|
+
const isTabsHeaderEnabled = computed(() => tabs.value.every((t: Tab) => t.showHeader));
|
|
18
|
+
|
|
19
|
+
const {
|
|
20
|
+
activeTab, setTabActive, onTabReady, onTabClose, onPanelClose
|
|
21
|
+
} = useTabsHandler();
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
height, width, setDimensions, openPanel, closePanel
|
|
25
|
+
} = useDimensionsHandler({ position: props.position });
|
|
26
|
+
|
|
27
|
+
const {
|
|
28
|
+
mouseResizeYStart, keyboardResizeY, mouseResizeXStart, keyboardResizeX
|
|
29
|
+
} = useResizeHandler({ position: props.position, setDimensions });
|
|
30
|
+
|
|
31
|
+
const {
|
|
32
|
+
dragOverPositionsActive,
|
|
33
|
+
dragOverTabBarActive,
|
|
34
|
+
onTabBarDragOver,
|
|
35
|
+
onTabBarDragLeave,
|
|
36
|
+
onTabBarDrop,
|
|
37
|
+
onDragPositionStart,
|
|
38
|
+
onDragPositionEnd,
|
|
39
|
+
lockedPosition
|
|
40
|
+
} = useDragHandler({ position: props.position });
|
|
41
|
+
|
|
42
|
+
onMounted(() => openPanel(props.position));
|
|
43
|
+
|
|
44
|
+
onBeforeUnmount(() => {
|
|
45
|
+
closePanel(props.position);
|
|
46
|
+
onPanelClose(props.position);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
tabs,
|
|
51
|
+
activeTab,
|
|
52
|
+
isTabsHeaderEnabled,
|
|
53
|
+
height,
|
|
54
|
+
width,
|
|
55
|
+
dragOverPositionsActive,
|
|
56
|
+
dragOverTabBarActive,
|
|
57
|
+
setTabActive,
|
|
58
|
+
onTabReady,
|
|
59
|
+
onTabClose,
|
|
60
|
+
onPanelClose,
|
|
61
|
+
mouseResizeXStart,
|
|
62
|
+
mouseResizeYStart,
|
|
63
|
+
keyboardResizeX,
|
|
64
|
+
keyboardResizeY,
|
|
65
|
+
onTabBarDragOver,
|
|
66
|
+
onTabBarDragLeave,
|
|
67
|
+
onTabBarDrop,
|
|
68
|
+
onDragPositionStart,
|
|
69
|
+
onDragPositionEnd,
|
|
70
|
+
lockedPosition
|
|
71
|
+
};
|
|
72
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { computed } from 'vue';
|
|
2
|
+
import { useStore } from 'vuex';
|
|
3
|
+
import { Layout, Position, Tab } from '@shell/types/window-manager';
|
|
4
|
+
|
|
5
|
+
export default (props?: { positions: Position[], layout: Layout }) => {
|
|
6
|
+
const store = useStore();
|
|
7
|
+
|
|
8
|
+
const isPanelEnabled = computed(() => (props?.positions || []).reduce((acc, pos) => ({
|
|
9
|
+
...acc,
|
|
10
|
+
[pos]: store.state.wm.open[pos] && !store.state.wm.tabs.find((t: Tab) => !t.layouts.includes(props?.layout || Layout.default) && t.position === pos),
|
|
11
|
+
}), {} as Record<Position, boolean>));
|
|
12
|
+
|
|
13
|
+
return { isPanelEnabled };
|
|
14
|
+
};
|