@knime/hub-features 1.28.0 → 1.29.1

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.
@@ -1,258 +0,0 @@
1
- <script setup lang="ts">
2
- import { computed } from "vue";
3
-
4
- import { Button, LocalDateTime, SubMenu } from "@knime/components";
5
- import MenuIcon from "@knime/styles/img/icons/menu-options.svg";
6
- import WrenchIcon from "@knime/styles/img/icons/wrench.svg";
7
-
8
- import HubAvatar from "../../avatars/HubAvatar.vue";
9
- import type { ItemSavepoint, WithAvatar, WithLabels } from "../types";
10
-
11
- import LabelList from "./LabelList.vue";
12
-
13
- const props = defineProps<{
14
- isSelected: boolean;
15
- hasEditCapability: boolean;
16
- hasPreviousVersion: boolean;
17
- currentStateSavepoint: ItemSavepoint & WithAvatar & WithLabels;
18
- isVersionLimitExceeded?: boolean;
19
- }>();
20
-
21
- const emit = defineEmits<{
22
- createVersion: [];
23
- discard: [];
24
- select: [];
25
- }>();
26
-
27
- const menuItems = computed(() => {
28
- return props.hasEditCapability && props.hasPreviousVersion
29
- ? [
30
- {
31
- text: "Discard",
32
- action: () => emit("discard"),
33
- },
34
- ]
35
- : [];
36
- });
37
-
38
- const onMenuItemClick = (_: Event, item: (typeof menuItems.value)[number]) => {
39
- item.action();
40
- };
41
- </script>
42
-
43
- <template>
44
- <div class="wrapper">
45
- <div
46
- class="current-state"
47
- :class="{ selected: isSelected }"
48
- @click="!isSelected && $emit('select')"
49
- >
50
- <div class="left">
51
- <div class="draft">
52
- <h6><WrenchIcon class="draft-icon" /><span>Draft</span></h6>
53
-
54
- <div class="labels">
55
- <LabelList :labels="currentStateSavepoint.labels" />
56
- </div>
57
-
58
- <span class="date">
59
- Latest edits on
60
- <LocalDateTime
61
- class="date"
62
- show-time
63
- :date="currentStateSavepoint.changes[0].createdOn"
64
- />
65
- </span>
66
- </div>
67
- </div>
68
- <div class="right">
69
- <Button
70
- v-if="hasEditCapability && !isVersionLimitExceeded"
71
- with-border
72
- compact
73
- class="create-button"
74
- @click.stop="$emit('createVersion')"
75
- >
76
- Create version
77
- </Button>
78
- <HubAvatar
79
- class="profile-picture"
80
- v-bind="currentStateSavepoint.avatar"
81
- />
82
- <div v-if="menuItems.length" class="controls">
83
- <nav>
84
- <SubMenu
85
- :items="menuItems"
86
- button-title="Options"
87
- @item-click="onMenuItemClick"
88
- >
89
- <MenuIcon class="open-icon" />
90
- </SubMenu>
91
- </nav>
92
- </div>
93
- </div>
94
- </div>
95
- </div>
96
- </template>
97
-
98
- <style lang="postcss" scoped>
99
- @import url("@knime/styles/css/mixins.css");
100
-
101
- .wrapper {
102
- position: relative;
103
- margin-left: 30px;
104
-
105
- &::before {
106
- position: absolute;
107
- top: 0;
108
- left: -22px;
109
- width: 2px;
110
- height: calc(100% + 30px);
111
- content: "";
112
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 5 5'%3E%3Ccircle cx='1' cy='1' r='.5' fill='%236E6E6E'/%3E%3C/svg%3E");
113
- background-repeat: repeat-y;
114
- background-size: 5px;
115
- }
116
-
117
- & .current-state {
118
- display: flex;
119
- gap: 10px;
120
- align-items: center;
121
- justify-content: space-between;
122
- padding: 10px;
123
- background-color: var(--knime-gray-ultra-light);
124
- transition: background-color 0.25s ease;
125
-
126
- & .left {
127
- & .draft {
128
- display: flex;
129
- flex-direction: column;
130
- gap: 3px;
131
- word-break: normal;
132
- overflow-wrap: anywhere;
133
-
134
- & h6 {
135
- display: flex;
136
- align-items: center;
137
- margin: 0;
138
- font-size: 13px;
139
- font-weight: 700;
140
- line-height: 18px;
141
- transition: color 0.25s ease;
142
-
143
- & .draft-icon {
144
- @mixin svg-icon-size 20;
145
-
146
- padding: 4px;
147
- margin-right: 4px;
148
- background: var(--knime-carrot-light);
149
- border-radius: 1000px;
150
- }
151
- }
152
-
153
- & .labels {
154
- display: flex;
155
- flex-flow: row wrap;
156
- gap: 5px;
157
- align-items: center;
158
- }
159
-
160
- & .date {
161
- margin: 0;
162
- font-size: 11px;
163
- font-weight: 300;
164
- line-height: 16px;
165
- transition: color 0.25s ease;
166
- }
167
- }
168
- }
169
-
170
- & .right {
171
- display: flex;
172
- gap: 8px;
173
- align-items: center;
174
- justify-content: flex-end;
175
-
176
- & .create-button {
177
- white-space: nowrap;
178
- }
179
-
180
- & .controls {
181
- display: flex;
182
- flex-direction: column;
183
- justify-content: center;
184
- padding-left: 5px;
185
- margin-right: -4px;
186
- border-left: 1px solid var(--knime-silver-sand);
187
- }
188
- }
189
-
190
- &::before {
191
- position: absolute;
192
- top: 12px;
193
- left: -6px;
194
- width: 12px;
195
- height: 12px;
196
- content: "";
197
- background-color: var(--knime-gray-ultra-light);
198
- transform: rotateZ(45deg);
199
- transition: background-color 0.25s ease;
200
- }
201
-
202
- &::after {
203
- position: absolute;
204
- top: 15px;
205
- left: -24px;
206
- width: 6px;
207
- height: 6px;
208
- content: "";
209
- background-color: var(--knime-masala);
210
- border-radius: 100%;
211
- box-shadow: 0 0 0 0 transparent;
212
- transition:
213
- background-color 0.25s ease,
214
- box-shadow 0.25s ease;
215
- }
216
-
217
- &:hover {
218
- cursor: pointer;
219
- box-shadow: 0 2px 10px 0 var(--knime-gray-dark-semi);
220
- }
221
-
222
- &.selected {
223
- background-color: var(--knime-cornflower-semi);
224
-
225
- & .left {
226
- & h6,
227
- & .date {
228
- color: var(--knime-cornflower-dark);
229
-
230
- & .draft-icon {
231
- background: none;
232
- stroke: var(--knime-cornflower-dark);
233
- }
234
- }
235
- }
236
-
237
- & .controls {
238
- border-left: 1px solid var(--knime-cornflower-light);
239
- }
240
-
241
- &::before {
242
- background-color: var(--knime-cornflower-semi);
243
- }
244
-
245
- &::after {
246
- background-color: var(--knime-cornflower);
247
- box-shadow:
248
- 0 0 0 3px var(--knime-porcelain),
249
- 0 0 0 5px var(--knime-cornflower);
250
- }
251
- }
252
-
253
- @media only screen and (width <= 900px) {
254
- width: calc(100vw - 100px);
255
- }
256
- }
257
- }
258
- </style>
@@ -1,253 +0,0 @@
1
- <script setup lang="ts">
2
- import { type Ref, computed, onMounted, ref } from "vue";
3
- import { useEventBus } from "@vueuse/core";
4
- import { autoUpdate, offset, useFloating } from "@floating-ui/vue";
5
- import { isEqual } from "lodash-es"; // eslint-disable-line depend/ban-dependencies
6
-
7
- import { FunctionButton } from "@knime/components";
8
- import { truncateString } from "@knime/utils";
9
-
10
- import Popover from "../../Popover.vue";
11
- import type { AssignedLabel } from "../types";
12
-
13
- type PopoverElement = HTMLElement & {
14
- closeMenu: () => void;
15
- openMenu: () => void;
16
- };
17
-
18
- const LABEL_LENGTH = 25;
19
-
20
- const props = withDefaults(
21
- defineProps<{
22
- labels: Array<AssignedLabel>;
23
- defaultLabelCount?: number;
24
- }>(),
25
- { defaultLabelCount: 3 },
26
- );
27
- const emit = defineEmits(["labelOver", "labelLeave"]);
28
-
29
- const eventBus = useEventBus("versionLabels");
30
- const eventBusKey = "versionLabelShowPopover";
31
-
32
- const showAll = ref(false);
33
- const activeLabel: Ref<AssignedLabel | null> = ref(null);
34
-
35
- const popover: Ref<PopoverElement | null> = ref(null);
36
- const floatingPanel: Ref<HTMLElement | null> = ref(null);
37
- const floatingAnchor: Ref<HTMLElement | null> = ref(null);
38
- // 40px is the offset of the arrow on the popover plus the top padding
39
- // this is necessary to compensate for the offset calculation
40
- const defaultCrossAxisOffset = -46;
41
- const defaultMainAxisOffset = 75;
42
-
43
- const createMiddleware = () => [
44
- offset({
45
- crossAxis: defaultCrossAxisOffset,
46
- mainAxis: defaultMainAxisOffset,
47
- }),
48
- ];
49
-
50
- const { floatingStyles } = useFloating(floatingAnchor, floatingPanel, {
51
- whileElementsMounted: autoUpdate,
52
- placement: "left-start",
53
- strategy: "fixed",
54
- middleware: createMiddleware(),
55
- });
56
-
57
- const popoverTopOffset = ref(0);
58
- const popoverFloatingStyles = computed(() => {
59
- const parsedTop = parseInt(floatingStyles.value.top.replace("px", ""), 10);
60
- const compensatedTop = `${parsedTop + popoverTopOffset.value}px`;
61
-
62
- return {
63
- ...floatingStyles.value,
64
- top: compensatedTop,
65
- };
66
- });
67
-
68
- const isShowMoreVisible = computed(
69
- () => props.labels.length > props.defaultLabelCount && !showAll.value,
70
- );
71
-
72
- const showMoreButtonText = computed(
73
- () => `+${props.labels.length - props.defaultLabelCount}`,
74
- );
75
-
76
- const filteredLabels = computed(() =>
77
- showAll.value ? props.labels : props.labels.slice(0, props.defaultLabelCount),
78
- );
79
-
80
- const calculateDistanceToUpperBorder = (labelElement: HTMLElement) => {
81
- const labelRect = labelElement.getBoundingClientRect();
82
- const floatingAnchorRect = floatingAnchor.value!.getBoundingClientRect();
83
-
84
- return Math.round(labelRect.top - floatingAnchorRect.top);
85
- };
86
-
87
- const isActiveLabel = (label: AssignedLabel) =>
88
- isEqual(activeLabel.value, label);
89
-
90
- const togglePopover = (label: AssignedLabel, labelElement: HTMLElement) => {
91
- if (isActiveLabel(label)) {
92
- activeLabel.value = null;
93
- popover.value?.closeMenu();
94
- } else {
95
- eventBus.emit(eventBusKey);
96
- popoverTopOffset.value = calculateDistanceToUpperBorder(labelElement);
97
-
98
- activeLabel.value = label;
99
- popover.value?.openMenu();
100
- }
101
- };
102
-
103
- const showMore = () => {
104
- showAll.value = !showAll.value;
105
- };
106
-
107
- const mouseOver = () => emit("labelOver");
108
- const mouseLeave = () => emit("labelLeave");
109
-
110
- onMounted(() => {
111
- eventBus.on((event: unknown) => {
112
- if (event === eventBusKey) {
113
- popover.value?.closeMenu();
114
- activeLabel.value = null;
115
- }
116
- });
117
- });
118
- </script>
119
-
120
- <template>
121
- <template v-if="labels.length > 0">
122
- <div class="label-list-with-popover">
123
- <div ref="floatingAnchor" class="label-list">
124
- <FunctionButton
125
- v-for="label in filteredLabels"
126
- :key="label.labelId"
127
- compact
128
- class="with-border"
129
- :active="isActiveLabel(label)"
130
- @mouseover="mouseOver"
131
- @mouseleave="mouseLeave"
132
- @click.stop="togglePopover(label, $event.currentTarget)"
133
- @keydown.esc.stop="popover?.closeMenu()"
134
- >
135
- {{ truncateString(label.label.name, LABEL_LENGTH) }}
136
- </FunctionButton>
137
-
138
- <FunctionButton
139
- v-if="isShowMoreVisible"
140
- compact
141
- class="with-border"
142
- @mouseover="mouseOver"
143
- @mouseleave="mouseLeave"
144
- @click.stop="showMore"
145
- >{{ showMoreButtonText }}</FunctionButton
146
- >
147
- </div>
148
-
149
- <div
150
- ref="floatingPanel"
151
- class="floating-panel"
152
- :style="popoverFloatingStyles"
153
- @click.stop
154
- @mouseover="mouseOver"
155
- @mouseleave="mouseLeave"
156
- >
157
- <Popover
158
- ref="popover"
159
- :use-button="false"
160
- :button-with-border="false"
161
- arrow-position="right"
162
- @close="activeLabel = null"
163
- >
164
- <template #content>
165
- <h6 class="headline">
166
- {{ activeLabel?.label.name }}
167
- </h6>
168
- <div class="panel">
169
- <div class="description">
170
- {{ activeLabel?.label.description }}
171
- </div>
172
- <div class="message">{{ activeLabel?.message }}</div>
173
- </div>
174
- </template>
175
- </Popover>
176
- </div>
177
- </div>
178
- </template>
179
- </template>
180
-
181
- <style lang="postcss" scoped>
182
- .label-list-with-popover {
183
- position: relative;
184
-
185
- & .label-list {
186
- position: relative;
187
- display: flex;
188
- flex-flow: row wrap;
189
- gap: var(--space-4);
190
- align-items: center;
191
- padding: var(--space-6) 0;
192
-
193
- & .function-button {
194
- &.with-border {
195
- padding: 2px var(--space-8);
196
- border: 1px solid var(--knime-silver-sand);
197
-
198
- &.active {
199
- border-color: var(--theme-button-function-background-color-active);
200
- }
201
- }
202
- }
203
-
204
- &:empty {
205
- display: none;
206
- }
207
- }
208
-
209
- & .floating-panel {
210
- z-index: 3;
211
- }
212
- }
213
-
214
- /* the position is set back because it interferes with the position calculation of floating UI */
215
- .popover.expanded :deep(.content) {
216
- position: initial;
217
- }
218
-
219
- .headline,
220
- .panel {
221
- line-height: 1.5;
222
- }
223
-
224
- .headline {
225
- padding-right: 18px;
226
- margin: 0;
227
- margin: 0 0 var(--space-8);
228
- font-size: 16px;
229
- font-weight: 700;
230
- }
231
-
232
- .panel {
233
- & .description {
234
- margin-bottom: var(--space-6);
235
- }
236
-
237
- & .message,
238
- & .description {
239
- font-size: 13px;
240
- font-weight: 300;
241
-
242
- &:empty {
243
- display: none;
244
- }
245
- }
246
- }
247
-
248
- @media only screen and (width <= 900px) {
249
- .label-list-with-popover {
250
- display: none;
251
- }
252
- }
253
- </style>
@@ -1,191 +0,0 @@
1
- <script setup lang="ts">
2
- import { useEventBus } from "@vueuse/core";
3
- import { throttle } from "lodash-es"; // eslint-disable-line depend/ban-dependencies
4
-
5
- import { FunctionButton } from "@knime/components";
6
- import CloseIcon from "@knime/styles/img/icons/close.svg";
7
- import HistoryIcon from "@knime/styles/img/icons/history.svg";
8
-
9
- import { CURRENT_STATE_VERSION } from "../constants";
10
- import type {
11
- ItemSavepoint,
12
- NamedItemVersion,
13
- VersionLimit,
14
- WithAvatar,
15
- WithLabels,
16
- } from "../types";
17
-
18
- import CurrentState from "./CurrentState.vue";
19
- import VersionHistory from "./VersionHistory.vue";
20
- import VersionLimitInfo from "./VersionLimitInfo.vue";
21
-
22
- type ManageVersionsProps = {
23
- hasUnversionedChanges: boolean;
24
- unversionedSavepoint?: (ItemSavepoint & WithAvatar & WithLabels) | null;
25
- currentVersion: NamedItemVersion["version"] | null;
26
- versionHistory: Array<NamedItemVersion & WithAvatar & WithLabels>;
27
- loading: boolean;
28
- hasLoadedAllVersions: boolean;
29
- hasAdminRights: boolean;
30
- hasEditCapability: boolean;
31
- versionLimit?: VersionLimit;
32
- upgradeUrl?: string;
33
- isPrivate?: boolean;
34
- };
35
-
36
- defineProps<ManageVersionsProps>();
37
-
38
- defineEmits<{
39
- close: [];
40
- loadAll: [];
41
- create: [];
42
- delete: [version: NamedItemVersion["version"]];
43
- restore: [version: NamedItemVersion["version"]];
44
- select: [version: NamedItemVersion["version"]];
45
- discardCurrentState: [];
46
- }>();
47
-
48
- const labelsEventBus = useEventBus("versionLabels");
49
-
50
- const closeLabelPopovers = throttle(() => {
51
- labelsEventBus.emit("versionLabelShowPopover");
52
- // eslint-disable-next-line no-magic-numbers
53
- }, 10000); // Arbitrary delay to reduce overhead, is automatically reset @scrollend
54
- </script>
55
-
56
- <template>
57
- <div class="manage-versions-container">
58
- <FunctionButton class="close" @click="$emit('close')">
59
- <CloseIcon />
60
- </FunctionButton>
61
-
62
- <div class="manage-versions">
63
- <div class="header">
64
- <HistoryIcon class="history-icon" /><!--
65
- -->
66
- <h4>Version history</h4>
67
- <div
68
- v-if="hasUnversionedChanges && unversionedSavepoint"
69
- class="changes"
70
- >
71
- <CurrentState
72
- :has-edit-capability="hasEditCapability"
73
- :has-previous-version="versionHistory.length > 0"
74
- :is-selected="currentVersion === CURRENT_STATE_VERSION"
75
- :current-state-savepoint="unversionedSavepoint"
76
- :is-version-limit-exceeded="
77
- versionLimit?.limit !== undefined &&
78
- versionLimit.currentUsage >= versionLimit.limit
79
- "
80
- @select="$emit('select', CURRENT_STATE_VERSION)"
81
- @create-version="$emit('create')"
82
- @discard="$emit('discardCurrentState')"
83
- />
84
- </div>
85
- </div>
86
- <div
87
- class="overflow-container"
88
- @scroll="closeLabelPopovers"
89
- @scrollend="closeLabelPopovers.cancel"
90
- >
91
- <div class="versions">
92
- <VersionHistory
93
- :selected-version="currentVersion"
94
- :version-history
95
- :loading
96
- :has-unversioned-changes
97
- :has-loaded-all-versions
98
- :has-admin-rights
99
- :has-edit-capability
100
- @delete="$emit('delete', $event)"
101
- @restore="$emit('restore', $event)"
102
- @load-all="$emit('loadAll')"
103
- @select="$emit('select', $event ?? CURRENT_STATE_VERSION)"
104
- />
105
- <VersionLimitInfo
106
- v-if="versionLimit?.limit !== undefined"
107
- class="version-limit-info"
108
- :version-limit="{
109
- limit: versionLimit.limit,
110
- currentUsage: versionLimit.currentUsage,
111
- }"
112
- :upgrade-url
113
- :is-private
114
- />
115
- </div>
116
- </div>
117
- </div>
118
- </div>
119
- </template>
120
-
121
- <style lang="postcss" scoped>
122
- @import url("@knime/styles/css/mixins.css");
123
-
124
- .manage-versions-container {
125
- position: relative;
126
- display: flex;
127
- flex-direction: column;
128
- align-items: flex-start;
129
- justify-content: flex-start;
130
- height: 100%;
131
- overscroll-behavior: none;
132
- background-color: var(--knime-gray-light-semi);
133
- isolation: isolate;
134
-
135
- & .close {
136
- position: absolute;
137
- top: 6px;
138
- right: 6px;
139
- z-index: 3;
140
- }
141
-
142
- & .manage-versions {
143
- display: flex;
144
- flex-direction: column;
145
- width: 100%;
146
- height: 100%;
147
-
148
- & .header {
149
- z-index: 2;
150
- padding: 32px 30px 30px;
151
- background-color: var(--knime-white);
152
-
153
- & h4 {
154
- display: inline;
155
- margin: 0;
156
- font-size: 22px;
157
- line-height: 26px;
158
- }
159
-
160
- & .history-icon {
161
- @mixin svg-icon-size 24;
162
-
163
- position: relative;
164
- top: 1px;
165
- margin-right: 9px;
166
- vertical-align: sub;
167
- stroke: var(--knime-masala);
168
- }
169
- }
170
-
171
- & .overflow-container {
172
- overflow-y: auto;
173
-
174
- & .versions {
175
- width: 100%;
176
- padding: 30px;
177
-
178
- & .version-limit-info {
179
- margin-top: 30px;
180
- }
181
- }
182
- }
183
-
184
- & .changes {
185
- width: 100%;
186
- padding-top: 30px;
187
- background-color: var(--knime-white);
188
- }
189
- }
190
- }
191
- </style>