mirror of https://github.com/rancher/dashboard.git
Merge branch 'master' into ingress-create
This commit is contained in:
commit
c67a37627e
Binary file not shown.
|
After Width: | Height: | Size: 186 KiB |
|
|
@ -101,5 +101,12 @@
|
||||||
|
|
||||||
--tabbed-border : #{$medium};
|
--tabbed-border : #{$medium};
|
||||||
--tabbed-container-bg : #{$darkest};
|
--tabbed-container-bg : #{$darkest};
|
||||||
|
|
||||||
|
--wm-tabs-bg : #{$medium};
|
||||||
|
--wm-tab-bg : #{$darker};
|
||||||
|
--wm-closer-hover-bg : #{$medium};
|
||||||
|
--wm-tab-active-bg : #{$darker};
|
||||||
|
--wm-body-bg : #{$darker};
|
||||||
|
--wm-border : black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -140,4 +140,12 @@ $selected: rgba($primary, .5);
|
||||||
--diff-ins-border : #{rgba($success, 0.5)};
|
--diff-ins-border : #{rgba($success, 0.5)};
|
||||||
--diff-chg-ins : #{rgba($success, 0.25)};
|
--diff-chg-ins : #{rgba($success, 0.25)};
|
||||||
--diff-chg-del : #{rgba($warning, 0.5)};
|
--diff-chg-del : #{rgba($warning, 0.5)};
|
||||||
|
|
||||||
|
--wm-tabs-bg : #{$medium};
|
||||||
|
--wm-tab-bg : #{$light};
|
||||||
|
--wm-closer-hover-bg : #{$lighter};
|
||||||
|
--wm-tab-active-bg : #{$lighter};
|
||||||
|
--wm-body-bg : #{$lighter};
|
||||||
|
--wm-border : var(--border);
|
||||||
|
--wm-tab-height : 25px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,224 @@
|
||||||
|
<script>
|
||||||
|
import { mapState } from 'vuex';
|
||||||
|
import { screenRect, boundingRect } from '@/utils/position';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return { dragOffset: 0 };
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
...mapState('wm', ['tabs', 'active', 'open', 'userHeight']),
|
||||||
|
|
||||||
|
height: {
|
||||||
|
get() {
|
||||||
|
if ( process.server ) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @TODO remember across reloads, normalize to fit into current screen
|
||||||
|
// const cached = window.localStorage.getItem('wm-height');
|
||||||
|
|
||||||
|
if ( this.userHeight ) {
|
||||||
|
return this.userHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 200;
|
||||||
|
},
|
||||||
|
|
||||||
|
set(val) {
|
||||||
|
this.$store.commit('wm/setUserHeight', val);
|
||||||
|
this.show();
|
||||||
|
|
||||||
|
return val;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
tabs() {
|
||||||
|
if ( this.open ) {
|
||||||
|
this.show();
|
||||||
|
} else {
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
if ( this.open ) {
|
||||||
|
this.show();
|
||||||
|
} else {
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
toggle() {
|
||||||
|
if ( this.open ) {
|
||||||
|
this.hide();
|
||||||
|
} else {
|
||||||
|
this.show();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
switchTo(tab) {
|
||||||
|
this.$store.commit('wm/setActive', tab);
|
||||||
|
},
|
||||||
|
|
||||||
|
show() {
|
||||||
|
document.documentElement.style.setProperty('--wm-height', `${ this.height }px`);
|
||||||
|
this.$store.commit('wm/setOpen', true);
|
||||||
|
},
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
if ( this.tabs.length ) {
|
||||||
|
document.documentElement.style.setProperty('--wm-height', `calc(var(--wm-tab-height) + 2px)`);
|
||||||
|
} else {
|
||||||
|
document.documentElement.style.setProperty('--wm-height', '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$store.commit('wm/setOpen', false);
|
||||||
|
},
|
||||||
|
|
||||||
|
dragStart(event) {
|
||||||
|
console.log('dragStart', event);
|
||||||
|
|
||||||
|
const doc = document.documentElement;
|
||||||
|
|
||||||
|
doc.addEventListener('mousemove', this.dragMove);
|
||||||
|
doc.addEventListener('touchmove', this.dragMove, true);
|
||||||
|
doc.addEventListener('mouseup', this.dragEnd);
|
||||||
|
doc.addEventListener('mouseleave', this.dragEnd);
|
||||||
|
doc.addEventListener('touchend touchcancel', this.dragEnd, true);
|
||||||
|
doc.addEventListener('touchstart', this.dragEnd, true);
|
||||||
|
|
||||||
|
const eventY = event.screenY;
|
||||||
|
const rect = boundingRect(event.target);
|
||||||
|
const offset = eventY - rect.top;
|
||||||
|
|
||||||
|
console.log(offset, eventY, rect);
|
||||||
|
|
||||||
|
this.dragOffset = offset;
|
||||||
|
},
|
||||||
|
|
||||||
|
dragMove(event) {
|
||||||
|
const rect = screenRect();
|
||||||
|
const eventY = event.screenY;
|
||||||
|
|
||||||
|
const neu = rect.height - eventY - this.dragOffset;
|
||||||
|
|
||||||
|
console.log(rect.height, '-', eventY, '-', this.dragOffset, '=', neu);
|
||||||
|
this.height = neu;
|
||||||
|
},
|
||||||
|
|
||||||
|
dragEnd(event) {
|
||||||
|
console.log('dragEnd', event);
|
||||||
|
|
||||||
|
const doc = document.documentElement;
|
||||||
|
|
||||||
|
doc.removeEventListener('mousemove', this.dragMove);
|
||||||
|
doc.removeEventListener('touchmove', this.dragMove, true);
|
||||||
|
doc.removeEventListener('mouseup', this.dragEnd);
|
||||||
|
doc.removeEventListener('mouseleave', this.dragEnd);
|
||||||
|
doc.removeEventListener('touchend touchcancel', this.dragEnd, true);
|
||||||
|
doc.removeEventListener('touchstart', this.dragEnd, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
close(tab) {
|
||||||
|
this.$store.dispatch('wm/close', tab);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="windowmanager">
|
||||||
|
<div ref="tabs" class="tabs">
|
||||||
|
<div
|
||||||
|
v-for="tab in tabs"
|
||||||
|
:key="tab"
|
||||||
|
class="tab"
|
||||||
|
:class="{'active': tab === active}"
|
||||||
|
@click="switchTo(tab)"
|
||||||
|
>
|
||||||
|
{{ tab }}
|
||||||
|
<i class="closer icon icon-x" @click="close(tab)" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="collapser"
|
||||||
|
@click="toggle"
|
||||||
|
@mousedown.prevent.stop="dragStart($event)"
|
||||||
|
@touchstart.prevent.stop="dragStart($event)"
|
||||||
|
>
|
||||||
|
<i class="icon" :class="{'icon-chevron-up': !open, 'icon-chevron-down': open}" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="body">
|
||||||
|
{{ active }} Body
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.windowmanager {
|
||||||
|
display: grid;
|
||||||
|
height: var(--wm-height, 0);
|
||||||
|
|
||||||
|
grid-template-areas:
|
||||||
|
"tabs"
|
||||||
|
"body";
|
||||||
|
|
||||||
|
grid-template-rows: var(--wm-tab-height) auto;
|
||||||
|
|
||||||
|
.tabs {
|
||||||
|
grid-area: tabs;
|
||||||
|
background-color: var(--wm-tabs-bg);
|
||||||
|
border-top: 1px solid var(--wm-border);
|
||||||
|
border-bottom: 1px solid var(--wm-border);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-content: stretch;
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
border-top: 1px solid var(--wm-border);
|
||||||
|
border-right: 1px solid var(--wm-border);
|
||||||
|
padding: 0 10px;
|
||||||
|
line-height: var(--wm-tab-height);
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
position: relative;
|
||||||
|
background-color: var(--wm-body-bg);
|
||||||
|
line-height: calc(var(--wm-tab-height) + 1px);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapser {
|
||||||
|
width: var(--wm-tab-height);
|
||||||
|
padding: 0 10px;
|
||||||
|
text-align: center;
|
||||||
|
border-left: 1px solid var(--wm-border);
|
||||||
|
line-height: var(--wm-tab-height);
|
||||||
|
height: calc(var(--wm-tab-height) + 1px);
|
||||||
|
flex-grow: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.closer {
|
||||||
|
padding: 0 0 0 10px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--wm-closer-hover-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
grid-area: body;
|
||||||
|
background-color: var(--wm-body-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { sortBy } from '@/utils/sort';
|
import { sortBy } from '@/utils/sort';
|
||||||
import { clone } from '@/utils/object';
|
import { clone } from '@/utils/object';
|
||||||
import { SCHEMA, COUNT, INGRESS } from '@/config/types';
|
import { SCHEMA, COUNT, INGRESS, API_GROUP } from '@/config/types';
|
||||||
import {
|
import {
|
||||||
isBasic,
|
isBasic,
|
||||||
isIgnored,
|
isIgnored,
|
||||||
|
|
@ -23,7 +23,15 @@ export function allTypes($store) {
|
||||||
for ( const schema of schemas ) {
|
for ( const schema of schemas ) {
|
||||||
const attrs = schema.attributes || {};
|
const attrs = schema.attributes || {};
|
||||||
const count = counts[schema.id];
|
const count = counts[schema.id];
|
||||||
const group = attrs.group;
|
const groupName = attrs.group || 'core';
|
||||||
|
const api = $store.getters['cluster/byId'](API_GROUP, groupName);
|
||||||
|
|
||||||
|
// Skip non-preferred versions
|
||||||
|
if ( api?.preferredVersion?.version ) {
|
||||||
|
if ( api.preferredVersion.version !== attrs.version ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( !attrs.kind ) {
|
if ( !attrs.kind ) {
|
||||||
// Skip the apiGroups resource
|
// Skip the apiGroups resource
|
||||||
|
|
@ -37,7 +45,7 @@ export function allTypes($store) {
|
||||||
|
|
||||||
out[schema.id] = {
|
out[schema.id] = {
|
||||||
schema,
|
schema,
|
||||||
group,
|
group: groupName,
|
||||||
id: schema.id,
|
id: schema.id,
|
||||||
label: singularLabelFor(schema),
|
label: singularLabelFor(schema),
|
||||||
namespaced: attrs.namespaced,
|
namespaced: attrs.namespaced,
|
||||||
|
|
@ -101,16 +109,6 @@ export function getTree(mode, clusterId, types, namespaces, currentType) {
|
||||||
item.route.params.cluster = clusterId;
|
item.route.params.cluster = clusterId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( item.aggregateCount ) {
|
|
||||||
let count = 0;
|
|
||||||
|
|
||||||
for ( const type of item.aggregateCount ) {
|
|
||||||
count += matchingCounts(types[type], namespaces);
|
|
||||||
}
|
|
||||||
|
|
||||||
item.count = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
const group = _ensureGroup(root, item.group, item.route);
|
const group = _ensureGroup(root, item.group, item.route);
|
||||||
|
|
||||||
group.children.push(item);
|
group.children.push(item);
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import {
|
||||||
} from '@/utils/customized';
|
} from '@/utils/customized';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CONFIG_MAP, NAMESPACE, NODE, POD, SECRET, RIO, RBAC, SERVICE, PV, PVC, INGRESS, WORKLOAD
|
CONFIG_MAP, NAMESPACE, NODE, POD, SECRET, RIO, RBAC, SERVICE, PV, PVC, INGRESS
|
||||||
} from '@/config/types';
|
} from '@/config/types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
@ -53,7 +53,7 @@ export default function() {
|
||||||
return out;
|
return out;
|
||||||
}, 99, true);
|
}, 99, true);
|
||||||
|
|
||||||
mapType('', (typeStr, match, schema) => {
|
mapType(/.*/, (typeStr, match, schema) => {
|
||||||
return schema.attributes.kind;
|
return schema.attributes.kind;
|
||||||
}, 1);
|
}, 1);
|
||||||
|
|
||||||
|
|
@ -73,6 +73,7 @@ export default function() {
|
||||||
mapGroup('certmanager.k8s.io', 'Cert Manager');
|
mapGroup('certmanager.k8s.io', 'Cert Manager');
|
||||||
mapGroup(/^gateway.solo.io(.v\d+)?$/, 'Gloo');
|
mapGroup(/^gateway.solo.io(.v\d+)?$/, 'Gloo');
|
||||||
mapGroup('gloo.solo.io', 'Gloo');
|
mapGroup('gloo.solo.io', 'Gloo');
|
||||||
|
mapGroup(/^(.*\.)?monitoring.coreos.com$/, 'Monitoring');
|
||||||
mapGroup(/^(.*\.)?tekton.dev$/, 'Tekton');
|
mapGroup(/^(.*\.)?tekton.dev$/, 'Tekton');
|
||||||
mapGroup(/^(.*\.)?rio.cattle.io$/, 'Rio');
|
mapGroup(/^(.*\.)?rio.cattle.io$/, 'Rio');
|
||||||
mapGroup(/^(.*\.)?longhorn.rancher.io$/, 'Longhorn');
|
mapGroup(/^(.*\.)?longhorn.rancher.io$/, 'Longhorn');
|
||||||
|
|
@ -208,7 +209,6 @@ export default function() {
|
||||||
name: 'c-cluster-workloads',
|
name: 'c-cluster-workloads',
|
||||||
params: { resource: 'workload' }
|
params: { resource: 'workload' }
|
||||||
},
|
},
|
||||||
aggregateCount: Object.values(WORKLOAD)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
virtualType({
|
virtualType({
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import ActionMenu from '@/components/ActionMenu';
|
||||||
import ButtonGroup from '@/components/ButtonGroup';
|
import ButtonGroup from '@/components/ButtonGroup';
|
||||||
import NamespaceFilter from '@/components/nav/NamespaceFilter';
|
import NamespaceFilter from '@/components/nav/NamespaceFilter';
|
||||||
import ClusterSwitcher from '@/components/nav/ClusterSwitcher';
|
import ClusterSwitcher from '@/components/nav/ClusterSwitcher';
|
||||||
|
import WindowManager from '@/components/nav/WindowManager';
|
||||||
import ShellSocket from '@/components/ContainerExec/ShellSocket';
|
import ShellSocket from '@/components/ContainerExec/ShellSocket';
|
||||||
import PromptRemove from '@/components/PromptRemove';
|
import PromptRemove from '@/components/PromptRemove';
|
||||||
import Group from '@/components/nav/Group';
|
import Group from '@/components/nav/Group';
|
||||||
|
|
@ -30,6 +31,7 @@ export default {
|
||||||
ButtonGroup,
|
ButtonGroup,
|
||||||
Group,
|
Group,
|
||||||
ShellSocket,
|
ShellSocket,
|
||||||
|
WindowManager
|
||||||
},
|
},
|
||||||
|
|
||||||
middleware: ['authenticated'],
|
middleware: ['authenticated'],
|
||||||
|
|
@ -212,7 +214,9 @@ export default {
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div class="wm">
|
<div class="wm">
|
||||||
|
<WindowManager />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ShellSocket />
|
<ShellSocket />
|
||||||
<ActionMenu />
|
<ActionMenu />
|
||||||
<PromptRemove />
|
<PromptRemove />
|
||||||
|
|
@ -231,8 +235,8 @@ export default {
|
||||||
"switcher main main main"
|
"switcher main main main"
|
||||||
"wm wm wm wm";
|
"wm wm wm wm";
|
||||||
|
|
||||||
grid-template-columns: var(--nav-width) auto 0px var(--header-height);
|
grid-template-columns: var(--nav-width) auto 0px var(--header-height);
|
||||||
grid-template-rows: var(--header-height) auto var(--footer-height) var(--wm-height, 0px);
|
grid-template-rows: var(--header-height) auto var(--footer-height) var(--wm-height, 0px);
|
||||||
|
|
||||||
&.back-to-rancher {
|
&.back-to-rancher {
|
||||||
grid-template-columns: var(--nav-width) auto 150px var(--header-height);
|
grid-template-columns: var(--nav-width) auto 150px var(--header-height);
|
||||||
|
|
@ -360,6 +364,5 @@ export default {
|
||||||
|
|
||||||
.wm {
|
.wm {
|
||||||
grid-area: wm;
|
grid-area: wm;
|
||||||
background-color: red;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@ export default {
|
||||||
<h1>
|
<h1>
|
||||||
Cluster: {{ cluster.nameDisplay }}
|
Cluster: {{ cluster.nameDisplay }}
|
||||||
</h1>
|
</h1>
|
||||||
<div class="text-center" style="margin-top: 200px;">
|
<div class="text-center">
|
||||||
Dashboard wheels...
|
<img src="~assets/images/i-cant-believe-its-not-wheels.png" style="width: 100%" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,7 @@ export const actions = {
|
||||||
dispatch('cluster/subscribe');
|
dispatch('cluster/subscribe');
|
||||||
|
|
||||||
const res = await allHash({
|
const res = await allHash({
|
||||||
apiGroups: dispatch('cluster/findAll', { type: API_GROUP, opt: { url: 'apiGroups' } }),
|
apiGroups: dispatch('cluster/findAll', { type: API_GROUP, opt: { url: 'apiGroups', watch: false } }),
|
||||||
counts: dispatch('cluster/findAll', { type: COUNT, opt: { url: 'counts' } }),
|
counts: dispatch('cluster/findAll', { type: COUNT, opt: { url: 'counts' } }),
|
||||||
namespaces: dispatch('cluster/findAll', { type: NAMESPACE, opt: { url: 'core.v1.namespaces' } })
|
namespaces: dispatch('cluster/findAll', { type: NAMESPACE, opt: { url: 'core.v1.namespaces' } })
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { addObject, removeObject } from '@/utils/array';
|
||||||
|
|
||||||
|
export const state = function() {
|
||||||
|
return {
|
||||||
|
tabs: [],
|
||||||
|
active: null,
|
||||||
|
open: false,
|
||||||
|
userHeight: null,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mutations = {
|
||||||
|
addTab(state, tab) {
|
||||||
|
addObject(state.tabs, tab);
|
||||||
|
state.active = tab;
|
||||||
|
state.open = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
removeTab(state, tab) {
|
||||||
|
removeObject(state.tabs, tab);
|
||||||
|
},
|
||||||
|
|
||||||
|
setOpen(state, open) {
|
||||||
|
state.open = open;
|
||||||
|
},
|
||||||
|
|
||||||
|
setActive(state, tab) {
|
||||||
|
state.active = tab;
|
||||||
|
},
|
||||||
|
|
||||||
|
setUserHeight(state, height) {
|
||||||
|
state.userHeight = height;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const actions = {
|
||||||
|
close({ state, commit }, tab) {
|
||||||
|
debugger;
|
||||||
|
// @TODO close/disconnect things hook...
|
||||||
|
let idx = state.tabs.indexOf(tab);
|
||||||
|
|
||||||
|
commit('removeTab', tab);
|
||||||
|
if ( idx >= state.tabs.length ) {
|
||||||
|
idx = state.tabs.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( idx >= 0 ) {
|
||||||
|
commit('setActive', state.tabs[idx]);
|
||||||
|
} else {
|
||||||
|
commit('setOpen', false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
open({ commit }, tab) {
|
||||||
|
commit('addTab', tab);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -394,7 +394,7 @@ function _normalizeType(type) {
|
||||||
|
|
||||||
function _addMapping(mappings, match, replace, weight, continueOnMatch) {
|
function _addMapping(mappings, match, replace, weight, continueOnMatch) {
|
||||||
if ( typeof match === 'string' ) {
|
if ( typeof match === 'string' ) {
|
||||||
match = new RegExp(escapeRegex(match), 'i');
|
match = new RegExp(`^${ escapeRegex(match) }$`, 'i');
|
||||||
}
|
}
|
||||||
|
|
||||||
mappings.push({
|
mappings.push({
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ const STATE_RECONNECTING = 'reconnecting';
|
||||||
export default class Socket extends EventTarget {
|
export default class Socket extends EventTarget {
|
||||||
url;
|
url;
|
||||||
autoReconnect = true;
|
autoReconnect = true;
|
||||||
frameTimeout = 32000;
|
frameTimeout = 35000;
|
||||||
metadata = {};
|
metadata = {};
|
||||||
|
|
||||||
// "Private"
|
// "Private"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue