@knime/hub-features 1.2.24 → 1.3.0
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/CHANGELOG.md +18 -0
- package/package.json +6 -2
- package/src/components/Popover.vue +224 -0
- package/src/components/avatars/HubAccountAvatar.vue +30 -0
- package/src/components/avatars/HubAvatar.vue +46 -0
- package/src/components/avatars/HubGroupAvatar.vue +72 -0
- package/src/components/avatars/index.ts +1 -0
- package/src/components/versions/CreateVersionForm.vue +133 -0
- package/src/components/versions/CurrentState.vue +256 -0
- package/src/components/versions/LabelList.vue +254 -0
- package/src/components/versions/ManageVersions.vue +165 -0
- package/src/components/versions/NoVersionItem.vue +23 -0
- package/src/components/versions/VersionHistory.vue +137 -0
- package/src/components/versions/VersionItem.vue +277 -0
- package/src/components/versions/constants.ts +3 -0
- package/src/components/versions/index.ts +6 -0
- package/src/components/versions/types.ts +45 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @knime/hub-features
|
|
2
2
|
|
|
3
|
+
## 1.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- e9beadc: Port VersionManager components
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [6c99c22]
|
|
12
|
+
- @knime/components@1.20.0
|
|
13
|
+
|
|
14
|
+
## 1.2.25
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- Updated dependencies [5f3ca36]
|
|
19
|
+
- @knime/components@1.19.4
|
|
20
|
+
|
|
3
21
|
## 1.2.24
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knime/hub-features",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Vue components & composables for shared hub features",
|
|
5
5
|
"homepage": "https://knime.github.io/webapps-common/",
|
|
6
6
|
"license": "GPL 3 and Additional Permissions according to Sec. 7 (SEE the file LICENSE)",
|
|
@@ -18,16 +18,20 @@
|
|
|
18
18
|
"exports": {
|
|
19
19
|
".": {
|
|
20
20
|
"import": "./src/index.ts"
|
|
21
|
+
},
|
|
22
|
+
"./versions": {
|
|
23
|
+
"import": "./src/components/versions/index.ts"
|
|
21
24
|
}
|
|
22
25
|
},
|
|
23
26
|
"dependencies": {
|
|
27
|
+
"@floating-ui/vue": "1.1.5",
|
|
24
28
|
"@vueuse/components": "~10.11.1",
|
|
25
29
|
"@vueuse/core": "~10.11.1",
|
|
26
30
|
"@vueuse/shared": "~10.11.1",
|
|
27
31
|
"lodash-es": "4.17.21",
|
|
28
32
|
"ofetch": "^1.4.1",
|
|
29
33
|
"typescript": "^5.4.5",
|
|
30
|
-
"@knime/components": "1.
|
|
34
|
+
"@knime/components": "1.20.0",
|
|
31
35
|
"@knime/styles": "1.3.1",
|
|
32
36
|
"@knime/utils": "1.3.1"
|
|
33
37
|
},
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { type Ref, ref } from "vue";
|
|
3
|
+
import { onClickOutside } from "@vueuse/core";
|
|
4
|
+
|
|
5
|
+
import { FunctionButton } from "@knime/components";
|
|
6
|
+
import CloseIcon from "@knime/styles/img/icons/close.svg";
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
buttonTitle?: string | null;
|
|
11
|
+
useButton?: boolean;
|
|
12
|
+
buttonWithBorder?: boolean;
|
|
13
|
+
hasCloseButton?: boolean;
|
|
14
|
+
arrowPosition?: "top" | "right";
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
withDefaults(defineProps<Props>(), {
|
|
18
|
+
buttonTitle: null,
|
|
19
|
+
arrowPosition: "top",
|
|
20
|
+
useButton: true,
|
|
21
|
+
bottonWithBorder: true,
|
|
22
|
+
hasCloseButton: true,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const emit = defineEmits(["open", "close"]);
|
|
26
|
+
|
|
27
|
+
const expanded = ref(false);
|
|
28
|
+
const toggleRef: Ref<InstanceType<typeof FunctionButton> | null> = ref(null);
|
|
29
|
+
const expandedElemRef = ref<HTMLElement | null>(null);
|
|
30
|
+
|
|
31
|
+
const closeMenu = () => {
|
|
32
|
+
if (expanded.value) {
|
|
33
|
+
consola.trace("Closing popover menu");
|
|
34
|
+
emit("close");
|
|
35
|
+
expanded.value = false;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const openMenu = () => {
|
|
39
|
+
consola.trace("Opening popover menu");
|
|
40
|
+
emit("open");
|
|
41
|
+
expanded.value = true;
|
|
42
|
+
};
|
|
43
|
+
const onToggle = () => {
|
|
44
|
+
if (expanded.value) {
|
|
45
|
+
closeMenu();
|
|
46
|
+
} else {
|
|
47
|
+
openMenu();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const onFocus = () => {
|
|
51
|
+
toggleRef.value?.focus();
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
onClickOutside(expandedElemRef, closeMenu, { capture: false });
|
|
55
|
+
|
|
56
|
+
defineExpose({
|
|
57
|
+
openMenu,
|
|
58
|
+
closeMenu,
|
|
59
|
+
onToggle,
|
|
60
|
+
});
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<template>
|
|
64
|
+
<div
|
|
65
|
+
ref="wrapper"
|
|
66
|
+
:class="['popover', `arrow-${arrowPosition}`, { expanded }]"
|
|
67
|
+
:expanded="expanded"
|
|
68
|
+
@focus="onFocus"
|
|
69
|
+
>
|
|
70
|
+
<!-- use mouseup instead of click as the click event fires twice on key input in Firefox-->
|
|
71
|
+
<FunctionButton
|
|
72
|
+
v-if="useButton"
|
|
73
|
+
ref="toggleRef"
|
|
74
|
+
:title="buttonTitle"
|
|
75
|
+
class="toggle"
|
|
76
|
+
:class="{ 'with-border': buttonWithBorder }"
|
|
77
|
+
:active="expanded"
|
|
78
|
+
:disabled="disabled"
|
|
79
|
+
@mouseup.stop="onToggle"
|
|
80
|
+
@keydown.space.stop="onToggle"
|
|
81
|
+
@keydown.esc.stop="closeMenu"
|
|
82
|
+
>
|
|
83
|
+
<slot name="button" />
|
|
84
|
+
</FunctionButton>
|
|
85
|
+
<span
|
|
86
|
+
v-else
|
|
87
|
+
ref="toggleRef"
|
|
88
|
+
tabindex="0"
|
|
89
|
+
:title="buttonTitle ?? undefined"
|
|
90
|
+
:active="expanded"
|
|
91
|
+
:class="['toggle', { disabled, 'with-border': buttonWithBorder }]"
|
|
92
|
+
@mouseup.stop="onToggle"
|
|
93
|
+
@keydown.space.prevent="onToggle"
|
|
94
|
+
@keydown.esc.prevent="closeMenu"
|
|
95
|
+
>
|
|
96
|
+
<slot name="button" />
|
|
97
|
+
</span>
|
|
98
|
+
<div v-if="expanded" ref="expandedElemRef" class="content">
|
|
99
|
+
<slot name="content" />
|
|
100
|
+
<FunctionButton
|
|
101
|
+
v-if="hasCloseButton"
|
|
102
|
+
class="closer"
|
|
103
|
+
@click.stop="closeMenu"
|
|
104
|
+
>
|
|
105
|
+
<CloseIcon />
|
|
106
|
+
</FunctionButton>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</template>
|
|
110
|
+
|
|
111
|
+
<style lang="postcss" scoped>
|
|
112
|
+
.popover {
|
|
113
|
+
position: relative;
|
|
114
|
+
--popover-top-margin: 18px;
|
|
115
|
+
--popover-arrow-size: 15px;
|
|
116
|
+
|
|
117
|
+
filter: drop-shadow(0 2px 10px var(--knime-gray-dark-semi));
|
|
118
|
+
|
|
119
|
+
&.expanded {
|
|
120
|
+
z-index: 1;
|
|
121
|
+
|
|
122
|
+
&::after {
|
|
123
|
+
position: absolute;
|
|
124
|
+
content: "";
|
|
125
|
+
width: 0;
|
|
126
|
+
height: 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* transparent background to maintain focus on the popover while transitioning into it with the mouse */
|
|
130
|
+
&::before {
|
|
131
|
+
content: "";
|
|
132
|
+
position: absolute;
|
|
133
|
+
background-color: rgb(0 0 0 / 0%);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
&.arrow-top {
|
|
137
|
+
&::after {
|
|
138
|
+
top: calc(100% + var(--popover-top-margin) - var(--popover-arrow-size));
|
|
139
|
+
right: 20px;
|
|
140
|
+
border-left: var(--popover-arrow-size) solid transparent;
|
|
141
|
+
border-right: var(--popover-arrow-size) solid transparent;
|
|
142
|
+
border-bottom: var(--popover-arrow-size) solid var(--knime-white);
|
|
143
|
+
|
|
144
|
+
@media only screen and (width <= 900px) {
|
|
145
|
+
border-top: var(--popover-arrow-size) solid var(--knime-white);
|
|
146
|
+
border-bottom: 0;
|
|
147
|
+
bottom: calc(
|
|
148
|
+
100% + var(--popover-top-margin) - var(--popover-arrow-size)
|
|
149
|
+
);
|
|
150
|
+
top: auto;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
&::before {
|
|
155
|
+
top: calc(100% + var(--popover-top-margin) - var(--popover-arrow-size));
|
|
156
|
+
right: 0;
|
|
157
|
+
width: var(--popover-width, 300px);
|
|
158
|
+
height: var(--popover-arrow-size);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
&.arrow-right {
|
|
163
|
+
&::after {
|
|
164
|
+
top: 40px;
|
|
165
|
+
left: var(--popover-width, 300px);
|
|
166
|
+
width: var(--popover-arrow-size);
|
|
167
|
+
height: auto;
|
|
168
|
+
aspect-ratio: 1/2;
|
|
169
|
+
background: var(--knime-white);
|
|
170
|
+
clip-path: polygon(0 0, 100% 50%, 0 100%);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
& .content {
|
|
176
|
+
width: var(--popover-width, 300px);
|
|
177
|
+
position: absolute;
|
|
178
|
+
top: calc(
|
|
179
|
+
100% + var(--popover-top-margin) - 1px
|
|
180
|
+
); /* shift by one pixel to avoid zoom issues */
|
|
181
|
+
|
|
182
|
+
right: 0;
|
|
183
|
+
padding: 20px;
|
|
184
|
+
background: var(--knime-white);
|
|
185
|
+
|
|
186
|
+
@media only screen and (width <= 900px) {
|
|
187
|
+
bottom: calc(
|
|
188
|
+
100% + var(--popover-top-margin) - 1px
|
|
189
|
+
); /* shift by one pixel to avoid zoom issues */
|
|
190
|
+
|
|
191
|
+
top: auto;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
& .toggle {
|
|
196
|
+
display: flex;
|
|
197
|
+
|
|
198
|
+
&:focus {
|
|
199
|
+
outline: none;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
&.disabled {
|
|
203
|
+
opacity: 0.5;
|
|
204
|
+
pointer-events: none;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
& .with-border {
|
|
209
|
+
&.function-button {
|
|
210
|
+
border: 1px solid var(--knime-silver-sand);
|
|
211
|
+
|
|
212
|
+
&.active {
|
|
213
|
+
border-color: var(--theme-button-function-background-color-active);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
& .closer {
|
|
219
|
+
position: absolute;
|
|
220
|
+
top: 6px;
|
|
221
|
+
right: 6px;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
</style>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { Avatar } from "@knime/components";
|
|
3
|
+
|
|
4
|
+
import type { HubAccountAvatarData } from "./HubAvatar.vue";
|
|
5
|
+
/**
|
|
6
|
+
* Displays the avatar image for an account (i.e., team or user)
|
|
7
|
+
* or a colored placeholder. See HubAvatar.
|
|
8
|
+
*/
|
|
9
|
+
withDefaults(defineProps<HubAccountAvatarData>(), {
|
|
10
|
+
isTrusted: false,
|
|
11
|
+
});
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<Avatar
|
|
16
|
+
:image
|
|
17
|
+
:name
|
|
18
|
+
:tooltip
|
|
19
|
+
:show-text
|
|
20
|
+
:layout
|
|
21
|
+
:show-trusted="isTrusted"
|
|
22
|
+
:link-url
|
|
23
|
+
>
|
|
24
|
+
<template #overlay>
|
|
25
|
+
<slot />
|
|
26
|
+
</template>
|
|
27
|
+
</Avatar>
|
|
28
|
+
</template>
|
|
29
|
+
|
|
30
|
+
<style lang="postcss" scoped></style>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
|
|
4
|
+
import HubAccountAvatar from "./HubAccountAvatar.vue";
|
|
5
|
+
import HubGroupAvatar from "./HubGroupAvatar.vue";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Avatar component which displays an image or a colored placeholder as well as a text
|
|
9
|
+
* for accounts (i.e., teams or users) or for groups. Depending on the entity for which
|
|
10
|
+
* an avatar image should be shown, this component renders a HubGroupAvatar or HubAccountAvatar.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
type CommonAvatarData = {
|
|
14
|
+
name: string;
|
|
15
|
+
linkUrl?: string;
|
|
16
|
+
layout?: "vertical" | "horizontal";
|
|
17
|
+
showText?: "name" | "placeholder" | "none";
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type HubGroupAvatarData = CommonAvatarData & {
|
|
21
|
+
kind: "group";
|
|
22
|
+
membersCount?: number;
|
|
23
|
+
isAdminGroup?: boolean;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type HubAccountAvatarData = CommonAvatarData & {
|
|
27
|
+
kind: "account";
|
|
28
|
+
image?: { url?: string | null; altText?: string };
|
|
29
|
+
isTrusted?: boolean;
|
|
30
|
+
tooltip?: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type HubAvatarData = HubGroupAvatarData | HubAccountAvatarData;
|
|
34
|
+
|
|
35
|
+
const props = defineProps<HubAvatarData>();
|
|
36
|
+
|
|
37
|
+
const renderComponent = computed(() => {
|
|
38
|
+
return props.kind === "group" ? HubGroupAvatar : HubAccountAvatar;
|
|
39
|
+
});
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<template>
|
|
43
|
+
<Component :is="renderComponent" v-bind="props">
|
|
44
|
+
<slot />
|
|
45
|
+
</Component>
|
|
46
|
+
</template>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
|
|
4
|
+
import { Avatar } from "@knime/components";
|
|
5
|
+
import AdminGroupIcon from "@knime/styles/img/icons/team-group-admin.svg";
|
|
6
|
+
import MemberGroupIcon from "@knime/styles/img/icons/team-group.svg";
|
|
7
|
+
|
|
8
|
+
import type { HubGroupAvatarData } from "./HubAvatar.vue";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Displays the avatar image for a group.
|
|
12
|
+
* See HubAvatar for more details.
|
|
13
|
+
*/
|
|
14
|
+
const props = defineProps<HubGroupAvatarData>();
|
|
15
|
+
|
|
16
|
+
const membersCountPostfix = computed(() => {
|
|
17
|
+
if (props.membersCount === 0) {
|
|
18
|
+
return " (0 people)";
|
|
19
|
+
}
|
|
20
|
+
if (props.membersCount === 1) {
|
|
21
|
+
return " (1 person)";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return ` (${props.membersCount} people)`;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const displayText = computed(() => {
|
|
28
|
+
return `${props.name}${membersCountPostfix.value}`;
|
|
29
|
+
});
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<Avatar
|
|
34
|
+
:name="displayText"
|
|
35
|
+
:tooltip="displayText"
|
|
36
|
+
:show-text
|
|
37
|
+
:layout
|
|
38
|
+
:link-url
|
|
39
|
+
descriptor="Group"
|
|
40
|
+
>
|
|
41
|
+
<template #default>
|
|
42
|
+
<div class="container">
|
|
43
|
+
<AdminGroupIcon v-if="isAdminGroup" class="admin" />
|
|
44
|
+
<MemberGroupIcon v-else class="member" />
|
|
45
|
+
</div>
|
|
46
|
+
</template>
|
|
47
|
+
|
|
48
|
+
<template #overlay>
|
|
49
|
+
<slot />
|
|
50
|
+
</template>
|
|
51
|
+
</Avatar>
|
|
52
|
+
</template>
|
|
53
|
+
|
|
54
|
+
<style lang="postcss" scoped>
|
|
55
|
+
@import url("@knime/styles/css/mixins.css");
|
|
56
|
+
|
|
57
|
+
.container {
|
|
58
|
+
position: relative;
|
|
59
|
+
background-color: var(--knime-white);
|
|
60
|
+
display: flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
height: 100%;
|
|
64
|
+
|
|
65
|
+
& > svg {
|
|
66
|
+
@mixin svg-icon-size 24;
|
|
67
|
+
|
|
68
|
+
position: absolute;
|
|
69
|
+
stroke: var(--knime-dove-gray);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
</style>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type * from "./HubAvatar.vue";
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, nextTick, onMounted, ref } from "vue";
|
|
3
|
+
import { useTextareaAutosize } from "@vueuse/core";
|
|
4
|
+
|
|
5
|
+
import { Button, InputField, Label, TextArea } from "@knime/components";
|
|
6
|
+
|
|
7
|
+
const emit = defineEmits<{
|
|
8
|
+
create: [{ name: string; description: string }];
|
|
9
|
+
cancel: [];
|
|
10
|
+
}>();
|
|
11
|
+
|
|
12
|
+
const versionName = ref("");
|
|
13
|
+
const versionDescription = ref("");
|
|
14
|
+
|
|
15
|
+
const descriptionEditor = ref<InstanceType<typeof TextArea>>(null);
|
|
16
|
+
|
|
17
|
+
onMounted(() => {
|
|
18
|
+
useTextareaAutosize({
|
|
19
|
+
element: descriptionEditor.value?.$el.getElementsByTagName("textarea")[0],
|
|
20
|
+
input: versionDescription,
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const doValidate = ref(false);
|
|
25
|
+
|
|
26
|
+
const isVersionNameInvalid = computed(() => {
|
|
27
|
+
return versionName.value.length <= 0 && doValidate.value;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const onCreate = async () => {
|
|
31
|
+
doValidate.value = true;
|
|
32
|
+
await nextTick();
|
|
33
|
+
|
|
34
|
+
if (!isVersionNameInvalid.value) {
|
|
35
|
+
emit("create", {
|
|
36
|
+
name: versionName.value,
|
|
37
|
+
description: versionDescription.value,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<template>
|
|
44
|
+
<div class="create-version-form">
|
|
45
|
+
<Label text="Version name">
|
|
46
|
+
<InputField
|
|
47
|
+
v-model="versionName"
|
|
48
|
+
type="text"
|
|
49
|
+
required
|
|
50
|
+
:is-valid="!isVersionNameInvalid"
|
|
51
|
+
/>
|
|
52
|
+
<div v-if="isVersionNameInvalid" class="error">
|
|
53
|
+
Please enter at least 1 character.
|
|
54
|
+
</div>
|
|
55
|
+
</Label>
|
|
56
|
+
<Label text="Description">
|
|
57
|
+
<TextArea
|
|
58
|
+
ref="descriptionEditor"
|
|
59
|
+
v-model="versionDescription"
|
|
60
|
+
class="description"
|
|
61
|
+
/>
|
|
62
|
+
</Label>
|
|
63
|
+
<div class="controls">
|
|
64
|
+
<Button
|
|
65
|
+
with-border
|
|
66
|
+
compact
|
|
67
|
+
class="button cancel"
|
|
68
|
+
@click="$emit('cancel')"
|
|
69
|
+
>
|
|
70
|
+
<strong>Cancel</strong>
|
|
71
|
+
</Button>
|
|
72
|
+
<Button
|
|
73
|
+
primary
|
|
74
|
+
compact
|
|
75
|
+
class="button create"
|
|
76
|
+
:disabled="isVersionNameInvalid"
|
|
77
|
+
@click="onCreate"
|
|
78
|
+
>
|
|
79
|
+
<strong>Create</strong>
|
|
80
|
+
</Button>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</template>
|
|
84
|
+
|
|
85
|
+
<style lang="postcss" scoped>
|
|
86
|
+
.create-version-form {
|
|
87
|
+
height: 100%;
|
|
88
|
+
max-height: 100%;
|
|
89
|
+
width: 100%;
|
|
90
|
+
display: flex;
|
|
91
|
+
flex-direction: column;
|
|
92
|
+
padding: 30px 30px 0;
|
|
93
|
+
gap: 30px;
|
|
94
|
+
overscroll-behavior: none;
|
|
95
|
+
isolation: isolate;
|
|
96
|
+
overflow: auto;
|
|
97
|
+
z-index: 1;
|
|
98
|
+
background-color: var(--knime-gray-ultra-light);
|
|
99
|
+
|
|
100
|
+
& .description {
|
|
101
|
+
width: 100%;
|
|
102
|
+
max-width: unset;
|
|
103
|
+
display: flex;
|
|
104
|
+
|
|
105
|
+
& :deep(textarea) {
|
|
106
|
+
width: 100%;
|
|
107
|
+
box-sizing: content-box;
|
|
108
|
+
max-height: 200px;
|
|
109
|
+
resize: none;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
& .error {
|
|
114
|
+
position: absolute;
|
|
115
|
+
word-break: keep-all;
|
|
116
|
+
margin-top: 6px;
|
|
117
|
+
font-weight: 300;
|
|
118
|
+
font-size: 13px;
|
|
119
|
+
color: var(--theme-color-error);
|
|
120
|
+
line-height: 18px;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
& .controls {
|
|
124
|
+
border-top: 1px solid var(--knime-silver-sand);
|
|
125
|
+
display: flex;
|
|
126
|
+
margin: 0 -30px;
|
|
127
|
+
padding: 10px 20px;
|
|
128
|
+
gap: 10px;
|
|
129
|
+
justify-content: space-between;
|
|
130
|
+
margin-top: auto;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
</style>
|