@rancher/shell 0.3.8 → 0.3.9
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/translations/en-us.yaml +28 -2
- package/babel.config.js +17 -4
- package/components/CodeMirror.vue +146 -14
- package/components/ContainerResourceLimit.vue +14 -1
- package/components/CruResource.vue +21 -5
- package/components/ExplorerProjectsNamespaces.vue +5 -1
- package/components/GroupPanel.vue +57 -0
- package/components/YamlEditor.vue +2 -2
- package/components/form/ArrayList.vue +1 -1
- package/components/form/KeyValue.vue +34 -1
- package/components/form/MatchExpressions.vue +120 -21
- package/components/form/NodeAffinity.vue +54 -4
- package/components/form/PodAffinity.vue +160 -47
- package/components/form/Tolerations.vue +40 -4
- package/components/form/__tests__/ArrayList.test.ts +3 -3
- package/components/form/__tests__/MatchExpressions.test.ts +1 -1
- package/components/nav/Header.vue +2 -0
- package/config/settings.ts +6 -1
- package/core/plugins-loader.js +0 -2
- package/edit/configmap.vue +33 -6
- package/edit/provisioning.cattle.io.cluster/AgentConfiguration.vue +326 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +1 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +60 -0
- package/mixins/chart.js +1 -1
- package/models/batch.cronjob.js +18 -3
- package/models/workload.js +1 -1
- package/package.json +2 -3
- package/pages/auth/login.vue +1 -0
- package/pages/prefs.vue +18 -2
- package/pkg/vue.config.js +0 -1
- package/plugins/codemirror.js +158 -0
- package/public/index.html +1 -1
- package/types/shell/index.d.ts +20 -1
- package/utils/create-yaml.js +105 -8
- package/utils/settings.ts +12 -0
- package/vue.config.js +2 -2
|
@@ -1611,6 +1611,27 @@ cluster:
|
|
|
1611
1611
|
additionalManifest:
|
|
1612
1612
|
title: Additional Manifest
|
|
1613
1613
|
tooltip: 'Additional Kubernetes Manifest YAML to be applied to the cluster on startup.'
|
|
1614
|
+
agentConfig:
|
|
1615
|
+
tabs:
|
|
1616
|
+
cluster: Cluster Agent
|
|
1617
|
+
fleet: Fleet Agent
|
|
1618
|
+
groups:
|
|
1619
|
+
deploymentLabels: Deployment Labels
|
|
1620
|
+
selector: Selector
|
|
1621
|
+
podAffinity: Affinity
|
|
1622
|
+
podTolerations: Tolerations
|
|
1623
|
+
podRequestsAndLimits: Requests and Limits
|
|
1624
|
+
subGroups:
|
|
1625
|
+
podAffinityAnti: Pod Affinity/Anti-Affinity
|
|
1626
|
+
nodeAffinity: Node Affinity
|
|
1627
|
+
banners:
|
|
1628
|
+
advanced: These are advanced configuration options. Generally, they should be left as-is.
|
|
1629
|
+
tolerations: Additional Pod Tolerations will be added to the default Tolerations applied by Rancher.
|
|
1630
|
+
limits: Pod Requests and Limits do not have a default configuration.
|
|
1631
|
+
windowsCompatibility: "We do not recommended removing the Node Affinity rule that prevents the <b>agent</b> from running on Windows nodes as this is not a supported configuration."
|
|
1632
|
+
affinity:
|
|
1633
|
+
default: Use default affinity rules defined by Rancher
|
|
1634
|
+
custom: Use custom affinity rules
|
|
1614
1635
|
advanced:
|
|
1615
1636
|
argInfo:
|
|
1616
1637
|
title: Additional Kubelet Args
|
|
@@ -5944,15 +5965,19 @@ workload:
|
|
|
5944
5965
|
antiAffinityTitle: Run pods on nodes without pods matching these selectors
|
|
5945
5966
|
affinityOption: Affinity
|
|
5946
5967
|
antiAffinityOption: Anti-Affinity
|
|
5968
|
+
matchFields:
|
|
5969
|
+
label: Fields
|
|
5947
5970
|
matchExpressions:
|
|
5971
|
+
label: Expressions
|
|
5948
5972
|
addRule: Add Rule
|
|
5949
5973
|
doesNotExist: is not set
|
|
5950
5974
|
exists: is set
|
|
5951
5975
|
greaterThan: ">"
|
|
5952
5976
|
in: in list
|
|
5953
|
-
inNamespaces: "
|
|
5977
|
+
inNamespaces: "Specific namespaces"
|
|
5954
5978
|
key: Key
|
|
5955
5979
|
lessThan: <
|
|
5980
|
+
matchType: Match Type
|
|
5956
5981
|
namespaces: Namespaces
|
|
5957
5982
|
notIn: not in list
|
|
5958
5983
|
operator: Operator
|
|
@@ -5968,6 +5993,7 @@ workload:
|
|
|
5968
5993
|
schedulingRules: Run pods on node(s) matching scheduling rules
|
|
5969
5994
|
specificNode: Run pods on specific node(s)
|
|
5970
5995
|
thisPodNamespace: This pod's namespace
|
|
5996
|
+
allNamespaces: All namespaces
|
|
5971
5997
|
topologyKey:
|
|
5972
5998
|
label: Topology Key
|
|
5973
5999
|
placeholder: e.g. failure-domain.beta.kubernetes.io/zone
|
|
@@ -5995,7 +6021,7 @@ workload:
|
|
|
5995
6021
|
effectOptions:
|
|
5996
6022
|
all: All
|
|
5997
6023
|
noExecute: NoExecute
|
|
5998
|
-
noSchedule: "NoSchedule
|
|
6024
|
+
noSchedule: "NoSchedule"
|
|
5999
6025
|
preferNoSchedule: PreferNoSchedule
|
|
6000
6026
|
labelKey: Label Key
|
|
6001
6027
|
operator: Operator
|
package/babel.config.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
|
|
1
|
+
module.exports = function(api) {
|
|
2
|
+
api.cache(true);
|
|
3
|
+
const presets = [
|
|
3
4
|
[
|
|
4
5
|
'@vue/cli-plugin-babel/preset',
|
|
5
6
|
{ useBuiltIns: false }
|
|
@@ -8,6 +9,18 @@ module.exports = {
|
|
|
8
9
|
'@babel/preset-env',
|
|
9
10
|
{ targets: { node: 'current' } }
|
|
10
11
|
]
|
|
11
|
-
]
|
|
12
|
-
env
|
|
12
|
+
];
|
|
13
|
+
const env = { test: { presets: [['@babel/env', { targets: { node: 'current' } }]] } };
|
|
14
|
+
|
|
15
|
+
const plugins = [];
|
|
16
|
+
|
|
17
|
+
if (process.env.NODE_ENV === 'test') {
|
|
18
|
+
plugins.push('transform-require-context');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
presets,
|
|
23
|
+
plugins,
|
|
24
|
+
env
|
|
25
|
+
};
|
|
13
26
|
};
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { KEYMAP } from '@shell/store/prefs';
|
|
3
|
+
import { _EDIT, _VIEW } from '@shell/config/query-params';
|
|
3
4
|
|
|
4
5
|
export default {
|
|
5
6
|
name: 'CodeMirror',
|
|
6
7
|
props: {
|
|
8
|
+
/**
|
|
9
|
+
* Sets the edit mode for Text Area.
|
|
10
|
+
* @values _EDIT, _VIEW
|
|
11
|
+
*/
|
|
12
|
+
mode: {
|
|
13
|
+
type: String,
|
|
14
|
+
default: _EDIT
|
|
15
|
+
},
|
|
7
16
|
value: {
|
|
8
17
|
type: String,
|
|
9
18
|
required: true,
|
|
@@ -11,14 +20,26 @@ export default {
|
|
|
11
20
|
options: {
|
|
12
21
|
type: Object,
|
|
13
22
|
default: () => {}
|
|
23
|
+
},
|
|
24
|
+
asTextArea: {
|
|
25
|
+
type: Boolean,
|
|
26
|
+
default: false
|
|
14
27
|
}
|
|
15
28
|
},
|
|
16
29
|
|
|
17
30
|
data() {
|
|
18
|
-
return {
|
|
31
|
+
return {
|
|
32
|
+
codeMirrorRef: null,
|
|
33
|
+
loaded: false
|
|
34
|
+
};
|
|
19
35
|
},
|
|
20
36
|
|
|
21
37
|
computed: {
|
|
38
|
+
|
|
39
|
+
isDisabled() {
|
|
40
|
+
return this.mode === _VIEW;
|
|
41
|
+
},
|
|
42
|
+
|
|
22
43
|
combinedOptions() {
|
|
23
44
|
const theme = this.$store.getters['prefs/theme'];
|
|
24
45
|
const keymap = this.$store.getters['prefs/get'](KEYMAP);
|
|
@@ -39,6 +60,12 @@ export default {
|
|
|
39
60
|
showCursorWhenSelecting: true,
|
|
40
61
|
};
|
|
41
62
|
|
|
63
|
+
if (this.asTextArea) {
|
|
64
|
+
out.lineNumbers = false;
|
|
65
|
+
out.tabSize = 0;
|
|
66
|
+
out.extraKeys = { Tab: false };
|
|
67
|
+
}
|
|
68
|
+
|
|
42
69
|
Object.assign(out, this.options);
|
|
43
70
|
|
|
44
71
|
return out;
|
|
@@ -58,35 +85,44 @@ export default {
|
|
|
58
85
|
methods: {
|
|
59
86
|
|
|
60
87
|
focus() {
|
|
61
|
-
if ( this.$refs.
|
|
62
|
-
this.$refs.
|
|
88
|
+
if ( this.$refs.codeMirrorRef ) {
|
|
89
|
+
this.$refs.codeMirrorRef.codemirror.focus();
|
|
63
90
|
}
|
|
64
91
|
},
|
|
65
92
|
|
|
66
93
|
refresh() {
|
|
67
|
-
if ( this.$refs.
|
|
68
|
-
this.$refs.
|
|
94
|
+
if ( this.$refs.codeMirrorRef ) {
|
|
95
|
+
this.$refs.codeMirrorRef.refresh();
|
|
69
96
|
}
|
|
70
97
|
},
|
|
71
98
|
|
|
72
|
-
onReady(
|
|
99
|
+
onReady(codeMirrorRef) {
|
|
73
100
|
this.$nextTick(() => {
|
|
74
|
-
|
|
101
|
+
codeMirrorRef.refresh();
|
|
102
|
+
this.codeMirrorRef = codeMirrorRef;
|
|
75
103
|
});
|
|
76
|
-
this.$emit('onReady',
|
|
104
|
+
this.$emit('onReady', codeMirrorRef);
|
|
77
105
|
},
|
|
78
106
|
|
|
79
107
|
onInput(newCode) {
|
|
80
108
|
this.$emit('onInput', newCode);
|
|
81
109
|
},
|
|
82
110
|
|
|
83
|
-
onChanges(
|
|
84
|
-
this.$emit('onChanges',
|
|
111
|
+
onChanges(codeMirrorRef, changes) {
|
|
112
|
+
this.$emit('onChanges', codeMirrorRef, changes);
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
onFocus() {
|
|
116
|
+
this.$emit('onFocus', true);
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
onBlur() {
|
|
120
|
+
this.$emit('onFocus', false);
|
|
85
121
|
},
|
|
86
122
|
|
|
87
123
|
updateValue(value) {
|
|
88
|
-
if ( this.$refs.
|
|
89
|
-
this.$refs.
|
|
124
|
+
if ( this.$refs.codeMirrorRef ) {
|
|
125
|
+
this.$refs.codeMirrorRef.codemirror.doc.setValue(value);
|
|
90
126
|
}
|
|
91
127
|
}
|
|
92
128
|
}
|
|
@@ -95,15 +131,21 @@ export default {
|
|
|
95
131
|
|
|
96
132
|
<template>
|
|
97
133
|
<client-only placeholder=" Loading...">
|
|
98
|
-
<div
|
|
134
|
+
<div
|
|
135
|
+
class="code-mirror"
|
|
136
|
+
:class="{['as-text-area']: asTextArea}"
|
|
137
|
+
>
|
|
99
138
|
<codemirror
|
|
100
139
|
v-if="loaded"
|
|
101
|
-
ref="
|
|
140
|
+
ref="codeMirrorRef"
|
|
102
141
|
:value="value"
|
|
103
142
|
:options="combinedOptions"
|
|
143
|
+
:disabled="isDisabled"
|
|
104
144
|
@ready="onReady"
|
|
105
145
|
@input="onInput"
|
|
106
146
|
@changes="onChanges"
|
|
147
|
+
@focus="onFocus"
|
|
148
|
+
@blur="onBlur"
|
|
107
149
|
/>
|
|
108
150
|
<div v-else>
|
|
109
151
|
Loading...
|
|
@@ -120,5 +162,95 @@ export default {
|
|
|
120
162
|
height: initial;
|
|
121
163
|
background: none
|
|
122
164
|
}
|
|
165
|
+
|
|
166
|
+
&.as-text-area {
|
|
167
|
+
min-height: 40px;
|
|
168
|
+
position: relative;
|
|
169
|
+
display: block;
|
|
170
|
+
box-sizing: border-box;
|
|
171
|
+
width: 100%;
|
|
172
|
+
padding: 10px;
|
|
173
|
+
background-color: var(--input-bg);
|
|
174
|
+
border-radius: var(--border-radius);
|
|
175
|
+
border: solid var(--border-width) var(--input-border);
|
|
176
|
+
color: var(--input-text);
|
|
177
|
+
|
|
178
|
+
&:hover {
|
|
179
|
+
border-color: var(--input-hover-border);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
&:focus, &.focus {
|
|
183
|
+
outline: none;
|
|
184
|
+
border-color: var(--outline);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.CodeMirror-wrap pre {
|
|
188
|
+
word-break: break-word;
|
|
189
|
+
}
|
|
190
|
+
.CodeMirror-code {
|
|
191
|
+
.CodeMirror-line {
|
|
192
|
+
&:not(:last-child)>span:after,
|
|
193
|
+
.cm-markdown-single-trailing-space-odd:before,
|
|
194
|
+
.cm-markdown-single-trailing-space-even:before {
|
|
195
|
+
color: var(--muted);
|
|
196
|
+
position: absolute;
|
|
197
|
+
line-height: 20px;
|
|
198
|
+
pointer-events: none;
|
|
199
|
+
}
|
|
200
|
+
&:not(:last-child)>span:after {
|
|
201
|
+
content: '↵';
|
|
202
|
+
margin-left: 2px;
|
|
203
|
+
}
|
|
204
|
+
.cm-markdown-single-trailing-space-odd:before,
|
|
205
|
+
.cm-markdown-single-trailing-space-even:before {
|
|
206
|
+
font-weight: bold;
|
|
207
|
+
content: '·';
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.CodeMirror-lines {
|
|
213
|
+
color: var(--input-text);
|
|
214
|
+
padding: 0;
|
|
215
|
+
|
|
216
|
+
.CodeMirror-line > span > span {
|
|
217
|
+
&.cm-overlay {
|
|
218
|
+
font-family: monospace;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.CodeMirror-line > span {
|
|
223
|
+
font-family: $body-font;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.CodeMirror-sizer {
|
|
228
|
+
min-height: 20px;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.CodeMirror-selected {
|
|
232
|
+
background-color: var(--primary) !important;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.CodeMirror-selectedtext {
|
|
236
|
+
color: var(--primary-text);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.CodeMirror-line::selection,
|
|
240
|
+
.CodeMirror-line > span::selection,
|
|
241
|
+
.CodeMirror-line > span > span::selection {
|
|
242
|
+
color: var(--primary-text);
|
|
243
|
+
background-color: var(--primary);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.CodeMirror-line::-moz-selection,
|
|
247
|
+
.CodeMirror-line > span::-moz-selection,
|
|
248
|
+
.CodeMirror-line > span > span::-moz-selection {
|
|
249
|
+
color: var(--primary-text);
|
|
250
|
+
background-color: var(--primary);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
123
254
|
}
|
|
255
|
+
|
|
124
256
|
</style>
|
|
@@ -26,6 +26,11 @@ export default {
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
|
|
29
|
+
handleGpuLimit: {
|
|
30
|
+
type: Boolean,
|
|
31
|
+
default: true
|
|
32
|
+
},
|
|
33
|
+
|
|
29
34
|
registerBeforeHook: {
|
|
30
35
|
type: Function,
|
|
31
36
|
default: null
|
|
@@ -180,6 +185,7 @@ export default {
|
|
|
180
185
|
:input-exponent="-1"
|
|
181
186
|
:output-modifier="true"
|
|
182
187
|
:base-unit="t('suffix.cpus')"
|
|
188
|
+
data-testid="cpu-reservation"
|
|
183
189
|
@input="updateLimits"
|
|
184
190
|
/>
|
|
185
191
|
</span>
|
|
@@ -192,6 +198,7 @@ export default {
|
|
|
192
198
|
:input-exponent="2"
|
|
193
199
|
:increment="1024"
|
|
194
200
|
:output-modifier="true"
|
|
201
|
+
data-testid="memory-reservation"
|
|
195
202
|
@input="updateLimits"
|
|
196
203
|
/>
|
|
197
204
|
</span>
|
|
@@ -207,6 +214,7 @@ export default {
|
|
|
207
214
|
:input-exponent="-1"
|
|
208
215
|
:output-modifier="true"
|
|
209
216
|
:base-unit="t('suffix.cpus')"
|
|
217
|
+
data-testid="cpu-limit"
|
|
210
218
|
@input="updateLimits"
|
|
211
219
|
/>
|
|
212
220
|
</span>
|
|
@@ -219,11 +227,15 @@ export default {
|
|
|
219
227
|
:input-exponent="2"
|
|
220
228
|
:increment="1024"
|
|
221
229
|
:output-modifier="true"
|
|
230
|
+
data-testid="memory-limit"
|
|
222
231
|
@input="updateLimits"
|
|
223
232
|
/>
|
|
224
233
|
</span>
|
|
225
234
|
</div>
|
|
226
|
-
<div
|
|
235
|
+
<div
|
|
236
|
+
v-if="handleGpuLimit"
|
|
237
|
+
class="row"
|
|
238
|
+
>
|
|
227
239
|
<span class="col span-6">
|
|
228
240
|
<UnitInput
|
|
229
241
|
v-model="limitsGpu"
|
|
@@ -231,6 +243,7 @@ export default {
|
|
|
231
243
|
:label="t('containerResourceLimit.limitsGpu')"
|
|
232
244
|
:mode="mode"
|
|
233
245
|
:base-unit="t('suffix.gpus')"
|
|
246
|
+
data-testid="gpu-limit"
|
|
234
247
|
@input="updateLimits"
|
|
235
248
|
/>
|
|
236
249
|
</span>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import isEmpty from 'lodash/isEmpty';
|
|
3
|
-
import {
|
|
3
|
+
import { createYamlWithOptions } from '@shell/utils/create-yaml';
|
|
4
4
|
import { clone, get } from '@shell/utils/object';
|
|
5
5
|
import { SCHEMA, NAMESPACE } from '@shell/config/types';
|
|
6
6
|
import ResourceYaml from '@shell/components/ResourceYaml';
|
|
@@ -101,6 +101,11 @@ export default {
|
|
|
101
101
|
default: null,
|
|
102
102
|
},
|
|
103
103
|
|
|
104
|
+
preventEnterSubmit: {
|
|
105
|
+
type: Boolean,
|
|
106
|
+
default: false,
|
|
107
|
+
},
|
|
108
|
+
|
|
104
109
|
applyHooks: {
|
|
105
110
|
type: Function,
|
|
106
111
|
default: null,
|
|
@@ -141,6 +146,11 @@ export default {
|
|
|
141
146
|
description: {
|
|
142
147
|
type: String,
|
|
143
148
|
default: ''
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
yamlModifiers: {
|
|
152
|
+
type: Object,
|
|
153
|
+
default: undefined
|
|
144
154
|
}
|
|
145
155
|
},
|
|
146
156
|
|
|
@@ -296,7 +306,7 @@ export default {
|
|
|
296
306
|
}
|
|
297
307
|
},
|
|
298
308
|
|
|
299
|
-
createResourceYaml() {
|
|
309
|
+
createResourceYaml(modifiers) {
|
|
300
310
|
const resource = this.resource;
|
|
301
311
|
|
|
302
312
|
if ( typeof this.generateYaml === 'function' ) {
|
|
@@ -306,7 +316,7 @@ export default {
|
|
|
306
316
|
const schemas = this.$store.getters[`${ inStore }/all`](SCHEMA);
|
|
307
317
|
const clonedResource = clone(resource);
|
|
308
318
|
|
|
309
|
-
const out =
|
|
319
|
+
const out = createYamlWithOptions(schemas, resource.type, clonedResource, modifiers);
|
|
310
320
|
|
|
311
321
|
return out;
|
|
312
322
|
}
|
|
@@ -317,7 +327,7 @@ export default {
|
|
|
317
327
|
await this.applyHooks(BEFORE_SAVE_HOOKS);
|
|
318
328
|
}
|
|
319
329
|
|
|
320
|
-
const resourceYaml = this.createResourceYaml();
|
|
330
|
+
const resourceYaml = this.createResourceYaml(this.yamlModifiers);
|
|
321
331
|
|
|
322
332
|
this.resourceYaml = resourceYaml;
|
|
323
333
|
this.showAsForm = false;
|
|
@@ -380,6 +390,12 @@ export default {
|
|
|
380
390
|
throw new Error(`Could not create the new namespace. ${ e.message }`);
|
|
381
391
|
}
|
|
382
392
|
}
|
|
393
|
+
},
|
|
394
|
+
|
|
395
|
+
onPressEnter(event) {
|
|
396
|
+
if (this.preventEnterSubmit) {
|
|
397
|
+
event.preventDefault();
|
|
398
|
+
}
|
|
383
399
|
}
|
|
384
400
|
}
|
|
385
401
|
};
|
|
@@ -398,7 +414,7 @@ export default {
|
|
|
398
414
|
:is="(isView? 'div' : 'form')"
|
|
399
415
|
class="create-resource-container cru__form"
|
|
400
416
|
@submit.prevent
|
|
401
|
-
@keydown.enter
|
|
417
|
+
@keydown.enter="onPressEnter($event)"
|
|
402
418
|
>
|
|
403
419
|
<div
|
|
404
420
|
v-if="hasErrors"
|
|
@@ -11,6 +11,7 @@ import MoveModal from '@shell/components/MoveModal';
|
|
|
11
11
|
import { defaultTableSortGenerationFn } from '@shell/components/ResourceTable.vue';
|
|
12
12
|
import { NAMESPACE_FILTER_ALL_ORPHANS } from '@shell/utils/namespace-filter';
|
|
13
13
|
import ResourceFetch from '@shell/mixins/resource-fetch';
|
|
14
|
+
import DOMPurify from 'dompurify';
|
|
14
15
|
|
|
15
16
|
export default {
|
|
16
17
|
name: 'ListProjectNamespace',
|
|
@@ -319,7 +320,10 @@ export default {
|
|
|
319
320
|
const row = group.rows[0];
|
|
320
321
|
|
|
321
322
|
if (row.isFake) {
|
|
322
|
-
return
|
|
323
|
+
return DOMPurify.sanitize(
|
|
324
|
+
this.t('resourceTable.groupLabel.project', { name: row.project?.nameDisplay }, true),
|
|
325
|
+
{ ALLOWED_TAGS: ['span'] }
|
|
326
|
+
);
|
|
323
327
|
}
|
|
324
328
|
|
|
325
329
|
return row.groupByLabel;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export default {
|
|
3
|
+
props: {
|
|
4
|
+
/**
|
|
5
|
+
* Label for the group
|
|
6
|
+
*/
|
|
7
|
+
label: {
|
|
8
|
+
type: String,
|
|
9
|
+
default: null
|
|
10
|
+
},
|
|
11
|
+
/**
|
|
12
|
+
* The i18n key to use for the label
|
|
13
|
+
*/
|
|
14
|
+
labelKey: {
|
|
15
|
+
type: String,
|
|
16
|
+
default: null
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
</script>
|
|
21
|
+
<template>
|
|
22
|
+
<div class="group-panel-outer">
|
|
23
|
+
<div class="group-panel">
|
|
24
|
+
<div class="group-panel-title">
|
|
25
|
+
<t
|
|
26
|
+
v-if="labelKey"
|
|
27
|
+
:k="labelKey"
|
|
28
|
+
/>
|
|
29
|
+
<template v-else-if="label">
|
|
30
|
+
{{ label }}
|
|
31
|
+
</template>
|
|
32
|
+
</div>
|
|
33
|
+
<div class="group-panel-content">
|
|
34
|
+
<slot />
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</template>
|
|
39
|
+
|
|
40
|
+
<style lang="scss" scoped>
|
|
41
|
+
.group-panel {
|
|
42
|
+
border: 1px solid var(--border);
|
|
43
|
+
border-radius: 5px;
|
|
44
|
+
padding: 10px;
|
|
45
|
+
position: relative;
|
|
46
|
+
margin-top: 10px;
|
|
47
|
+
.group-panel-title {
|
|
48
|
+
position: absolute;
|
|
49
|
+
top: -7px;
|
|
50
|
+
background-color: var(--body-bg);
|
|
51
|
+
padding: 0 5px;
|
|
52
|
+
}
|
|
53
|
+
.group-panel-content {
|
|
54
|
+
position: relative;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
</style>
|
|
@@ -86,7 +86,7 @@ export default {
|
|
|
86
86
|
},
|
|
87
87
|
|
|
88
88
|
computed: {
|
|
89
|
-
|
|
89
|
+
codeMirrorOptions() {
|
|
90
90
|
const readOnly = this.editorMode === EDITOR_MODES.VIEW_CODE;
|
|
91
91
|
|
|
92
92
|
const gutters = [];
|
|
@@ -228,7 +228,7 @@ export default {
|
|
|
228
228
|
ref="cm"
|
|
229
229
|
:class="{fill: true, scrolling: scrolling}"
|
|
230
230
|
:value="curValue"
|
|
231
|
-
:options="
|
|
231
|
+
:options="codeMirrorOptions"
|
|
232
232
|
:data-testid="componentTestid + '-code-mirror'"
|
|
233
233
|
@onInput="onInput"
|
|
234
234
|
@onReady="onReady"
|
|
@@ -10,11 +10,13 @@ import Select from '@shell/components/form/Select';
|
|
|
10
10
|
import FileSelector from '@shell/components/form/FileSelector';
|
|
11
11
|
import { _EDIT, _VIEW } from '@shell/config/query-params';
|
|
12
12
|
import { asciiLike } from '@shell/utils/string';
|
|
13
|
+
import CodeMirror from '@shell/components/CodeMirror';
|
|
13
14
|
|
|
14
15
|
export default {
|
|
15
16
|
name: 'KeyValue',
|
|
16
17
|
|
|
17
18
|
components: {
|
|
19
|
+
CodeMirror,
|
|
18
20
|
Select,
|
|
19
21
|
TextAreaAutoGrow,
|
|
20
22
|
FileSelector
|
|
@@ -139,6 +141,10 @@ export default {
|
|
|
139
141
|
type: Boolean,
|
|
140
142
|
default: false,
|
|
141
143
|
},
|
|
144
|
+
valueMarkdownMultiline: {
|
|
145
|
+
type: Boolean,
|
|
146
|
+
default: false,
|
|
147
|
+
},
|
|
142
148
|
valueMultiline: {
|
|
143
149
|
type: Boolean,
|
|
144
150
|
default: true,
|
|
@@ -245,7 +251,10 @@ export default {
|
|
|
245
251
|
data() {
|
|
246
252
|
const rows = this.getRows(this.value);
|
|
247
253
|
|
|
248
|
-
return {
|
|
254
|
+
return {
|
|
255
|
+
rows,
|
|
256
|
+
codeMirrorFocus: {},
|
|
257
|
+
};
|
|
249
258
|
},
|
|
250
259
|
|
|
251
260
|
computed: {
|
|
@@ -519,6 +528,19 @@ export default {
|
|
|
519
528
|
return this.t('detailText.binary', { n }, true);
|
|
520
529
|
},
|
|
521
530
|
get,
|
|
531
|
+
/**
|
|
532
|
+
* Update 'rows' variable with the user's input and prevents to update queue before the row model is updated
|
|
533
|
+
*/
|
|
534
|
+
onInputMarkdownMultiline(idx, value) {
|
|
535
|
+
this.rows = this.rows.map((row, i) => i === idx ? { ...row, value } : row);
|
|
536
|
+
this.queueUpdate();
|
|
537
|
+
},
|
|
538
|
+
/**
|
|
539
|
+
* Set focus on CodeMirror fields
|
|
540
|
+
*/
|
|
541
|
+
onFocusMarkdownMultiline(idx, value) {
|
|
542
|
+
this.$set(this.codeMirrorFocus, idx, value);
|
|
543
|
+
}
|
|
522
544
|
}
|
|
523
545
|
};
|
|
524
546
|
</script>
|
|
@@ -635,6 +657,16 @@ export default {
|
|
|
635
657
|
<div v-else-if="row.binary">
|
|
636
658
|
{{ binaryTextSize(row.value) }}
|
|
637
659
|
</div>
|
|
660
|
+
<CodeMirror
|
|
661
|
+
v-else-if="valueMarkdownMultiline"
|
|
662
|
+
ref="cm"
|
|
663
|
+
:class="{['focus']: codeMirrorFocus[i]}"
|
|
664
|
+
:value="row[valueName]"
|
|
665
|
+
:as-text-area="true"
|
|
666
|
+
:mode="mode"
|
|
667
|
+
@onInput="onInputMarkdownMultiline(i, $event)"
|
|
668
|
+
@onFocus="onFocusMarkdownMultiline(i, $event)"
|
|
669
|
+
/>
|
|
638
670
|
<TextAreaAutoGrow
|
|
639
671
|
v-else-if="valueMultiline"
|
|
640
672
|
v-model="row[valueName]"
|
|
@@ -750,6 +782,7 @@ export default {
|
|
|
750
782
|
&.value textarea{
|
|
751
783
|
padding: 10px 10px 10px 10px;
|
|
752
784
|
}
|
|
785
|
+
|
|
753
786
|
.text-monospace:not(.conceal) {
|
|
754
787
|
font-family: monospace, monospace;
|
|
755
788
|
}
|