@xen-orchestra/web-core 0.56.0 → 0.57.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/lib/assets/css/base.pcss +5 -0
- package/lib/components/input-wrapper/VtsInputWrapper.vue +2 -2
- package/lib/components/layout/VtsLayoutSidebar.vue +116 -43
- package/lib/components/menu/MenuItem.vue +9 -1
- package/lib/components/menu/MenuList.vue +4 -2
- package/lib/components/menu/VtsActionsMenu.vue +24 -24
- package/lib/components/modal/VtsActionModal.vue +44 -9
- package/lib/components/modal/VtsBlockedModal.vue +1 -1
- package/lib/components/modal/VtsDeleteModal.vue +1 -1
- package/lib/components/overlay/VtsOverlayButton.vue +35 -0
- package/lib/components/overlay/VtsOverlayCancelButton.vue +16 -0
- package/lib/components/overlay/VtsOverlayConfirmButton.vue +25 -0
- package/lib/components/overlay/VtsOverlayList.vue +82 -0
- package/lib/components/progress-bar/VtsProgressBar.vue +5 -3
- package/lib/components/select/VtsSelect.vue +0 -3
- package/lib/components/space-card/VtsSpaceCard.vue +1 -1
- package/lib/components/table/cells/VtsProgressBarCell.vue +1 -1
- package/lib/components/table/cells/VtsUserNameCell.vue +29 -0
- package/lib/components/task/VtsQuickTaskButton.vue +1 -1
- package/lib/components/task/VtsQuickTaskList.vue +1 -1
- package/lib/components/tree/VtsTreeItem.vue +4 -4
- package/lib/components/ui/head-bar/UiHeadBar.vue +1 -1
- package/lib/components/ui/input/UiInput.vue +53 -9
- package/lib/components/ui/query-search-bar/UiQuerySearchBar.vue +1 -15
- package/lib/components/ui/quick-task-item/UiQuickTaskItem.vue +1 -1
- package/lib/components/ui/quick-task-panel/UiQuickTaskPanel.vue +1 -1
- package/lib/components/ui/task-item/UiTaskItem.vue +14 -6
- package/lib/components/ui/task-list/UiTaskList.vue +1 -1
- package/lib/components/ui/text-area/UiTextarea.vue +12 -14
- package/lib/components/vif-connection-toggle-modal/VtsVifConnectionToggleModal.vue +43 -0
- package/lib/composables/table/multi-select.composable.md +33 -0
- package/lib/layouts/CoreLayout.vue +74 -62
- package/lib/locales/en.json +63 -2
- package/lib/locales/fr.json +63 -2
- package/lib/packages/form-validation/custom-rules/ip-addresses.rule.ts +15 -0
- package/lib/packages/form-validation/custom-rules/ip.regex.ts +26 -0
- package/lib/packages/form-validation/custom-rules/ipv4-or-cidr.rule.ts +2 -3
- package/lib/packages/form-validation/index.ts +2 -0
- package/lib/packages/hide-permanently/README.md +60 -0
- package/lib/packages/hide-permanently/types.ts +3 -0
- package/lib/packages/hide-permanently/use-hide-permanently.ts +15 -0
- package/lib/packages/overlay/OverlayComponent.vue +18 -0
- package/lib/packages/overlay/README.md +285 -0
- package/lib/packages/overlay/create-event-handler.ts +80 -0
- package/lib/packages/overlay/injection-keys.ts +3 -0
- package/lib/packages/overlay/is-thenable.ts +3 -0
- package/lib/packages/overlay/symbols.ts +5 -0
- package/lib/packages/overlay/types.ts +94 -0
- package/lib/packages/overlay/use-overlay-escape.ts +34 -0
- package/lib/packages/overlay/use-overlay-store.ts +33 -0
- package/lib/packages/overlay/use-overlay-trigger.ts +33 -0
- package/lib/packages/overlay/use-overlay.ts +99 -0
- package/lib/packages/progress/use-progress.ts +12 -2
- package/lib/packages/sidebar/index.ts +2 -0
- package/lib/packages/sidebar/sidebar.store.ts +55 -0
- package/lib/packages/sidebar/types.ts +21 -0
- package/lib/packages/sidebar/use-sidebar-resize.ts +47 -0
- package/lib/packages/sidebar/use-sidebar-responsive-expand.ts +19 -0
- package/lib/tables/column-definitions/user-name-column.ts +10 -0
- package/lib/tables/column-sets/server-columns.ts +2 -2
- package/lib/tables/column-sets/user-columns.ts +16 -0
- package/lib/types/object.type.ts +9 -1
- package/lib/types/task.type.ts +5 -12
- package/lib/utils/injection-keys.util.ts +3 -1
- package/lib/utils/ip-address.utils.ts +7 -0
- package/lib/utils/sr.utils.ts +4 -0
- package/package.json +2 -1
- package/lib/stores/sidebar.store.ts +0 -79
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<UiTableCell>
|
|
3
3
|
<div class="container">
|
|
4
|
-
<VtsProgressBar :current :total
|
|
4
|
+
<VtsProgressBar :current :total no-ruler class="progress" />
|
|
5
5
|
<span>{{ n(percentage / 100, { maximumFractionDigits: 0, style: 'percent' }) }}</span>
|
|
6
6
|
</div>
|
|
7
7
|
</UiTableCell>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<UiTableCell>
|
|
3
|
+
<div class="user-name">
|
|
4
|
+
<UiUserLogo size="small" />
|
|
5
|
+
<UiLink :href size="medium">
|
|
6
|
+
{{ content }}
|
|
7
|
+
</UiLink>
|
|
8
|
+
</div>
|
|
9
|
+
</UiTableCell>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script setup lang="ts">
|
|
13
|
+
import UiLink from '@core/components/ui/link/UiLink.vue'
|
|
14
|
+
import UiTableCell from '@core/components/ui/table-cell/UiTableCell.vue'
|
|
15
|
+
import UiUserLogo from '@core/components/ui/user-logo/UiUserLogo.vue'
|
|
16
|
+
|
|
17
|
+
defineProps<{
|
|
18
|
+
content: string
|
|
19
|
+
href?: string
|
|
20
|
+
}>()
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<style scoped lang="postcss">
|
|
24
|
+
.user-name {
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
gap: 0.8rem;
|
|
28
|
+
}
|
|
29
|
+
</style>
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
import VtsBackdrop from '@core/components/backdrop/VtsBackdrop.vue'
|
|
20
20
|
import UiButtonIcon from '@core/components/ui/button-icon/UiButtonIcon.vue'
|
|
21
21
|
import UiQuickTaskPanel from '@core/components/ui/quick-task-panel/UiQuickTaskPanel.vue'
|
|
22
|
+
import type { Task } from '@core/components/ui/task-item/UiTaskItem.vue'
|
|
22
23
|
import { vTooltip } from '@core/directives/tooltip.directive'
|
|
23
|
-
import type { Task } from '@core/types/task.type.ts'
|
|
24
24
|
import { unrefElement, watchArray, whenever } from '@vueuse/core'
|
|
25
25
|
import placementJs from 'placement.js'
|
|
26
26
|
import { computed, nextTick, ref } from 'vue'
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
<script lang="ts" setup>
|
|
28
28
|
import UiLoader from '@core/components/ui/loader/UiLoader.vue'
|
|
29
29
|
import UiQuickTaskItem from '@core/components/ui/quick-task-item/UiQuickTaskItem.vue'
|
|
30
|
-
import type { Task } from '@core/
|
|
30
|
+
import type { Task } from '@core/components/ui/task-item/UiTaskItem.vue'
|
|
31
31
|
import { useI18n } from 'vue-i18n'
|
|
32
32
|
import { DynamicScroller, DynamicScrollerItem } from 'vue-virtual-scroller'
|
|
33
33
|
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
</template>
|
|
7
7
|
|
|
8
8
|
<script lang="ts" setup>
|
|
9
|
+
import { useLeftSidebarStore } from '@core/packages/sidebar'
|
|
9
10
|
import type { TreeNodeId } from '@core/packages/tree/types.ts'
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { IK_TREE_ITEM_EXPANDED, IK_TREE_ITEM_HAS_CHILDREN } from '@core/utils/injection-keys.util'
|
|
11
|
+
import { useUiStore } from '@core/stores/ui.store.ts'
|
|
12
|
+
import { IK_TREE_ITEM_EXPANDED, IK_TREE_ITEM_HAS_CHILDREN } from '@core/utils/injection-keys.util.ts'
|
|
13
13
|
import { onBeforeMount, onBeforeUpdate, provide, ref, toRef, useSlots } from 'vue'
|
|
14
14
|
|
|
15
15
|
const props = defineProps<{
|
|
@@ -23,7 +23,7 @@ defineSlots<{
|
|
|
23
23
|
sublist?(): any
|
|
24
24
|
}>()
|
|
25
25
|
|
|
26
|
-
const sidebar =
|
|
26
|
+
const sidebar = useLeftSidebarStore()
|
|
27
27
|
const uiStore = useUiStore()
|
|
28
28
|
const hasChildrenState = ref(false)
|
|
29
29
|
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
<!--
|
|
1
|
+
<!-- v9 -->
|
|
2
2
|
<template>
|
|
3
|
-
<div :class="toVariants({ accent, disabled })" class="ui-input" @click.self="focus()">
|
|
3
|
+
<div :class="toVariants({ accent, disabled, selected })" class="ui-input" @click.self="focus()">
|
|
4
|
+
<span v-if="slots.prefix || prefix" class="prefix">
|
|
5
|
+
<slot name="prefix">
|
|
6
|
+
{{ prefix }}
|
|
7
|
+
</slot>
|
|
8
|
+
</span>
|
|
4
9
|
<span v-if="readonly" class="typo-body-regular input text-ellipsis">
|
|
5
10
|
<input ref="inputRef" class="readonly-input" readonly type="text" />
|
|
6
11
|
{{ modelValue }}
|
|
@@ -19,14 +24,19 @@
|
|
|
19
24
|
/>
|
|
20
25
|
<UiButtonIcon
|
|
21
26
|
v-if="!disabled && modelValue && clearable"
|
|
22
|
-
icon="fa:xmark"
|
|
23
|
-
accent="brand"
|
|
24
27
|
size="small"
|
|
28
|
+
icon="action:close-cancel-clear"
|
|
29
|
+
accent="brand"
|
|
25
30
|
@click="clear()"
|
|
26
31
|
/>
|
|
27
32
|
<slot v-if="slots['right-icon'] || rightIcon" name="right-icon">
|
|
28
33
|
<VtsIcon :name="rightIcon" size="medium" class="right-icon" />
|
|
29
34
|
</slot>
|
|
35
|
+
<span v-if="slots.suffix || suffix" class="suffix">
|
|
36
|
+
<slot name="suffix">
|
|
37
|
+
{{ suffix }}
|
|
38
|
+
</slot>
|
|
39
|
+
</span>
|
|
30
40
|
</div>
|
|
31
41
|
</template>
|
|
32
42
|
|
|
@@ -58,12 +68,14 @@ const {
|
|
|
58
68
|
id?: string
|
|
59
69
|
required?: boolean
|
|
60
70
|
disabled?: boolean
|
|
71
|
+
selected?: boolean
|
|
61
72
|
readonly?: boolean
|
|
62
73
|
detached?: boolean
|
|
63
74
|
type?: InputType
|
|
64
|
-
icon?: IconName
|
|
65
75
|
rightIcon?: IconName
|
|
66
76
|
clearable?: boolean
|
|
77
|
+
prefix?: string
|
|
78
|
+
suffix?: string
|
|
67
79
|
}>()
|
|
68
80
|
|
|
69
81
|
const emit = defineEmits<{
|
|
@@ -74,6 +86,8 @@ const modelValue = defineModel<string | number | undefined>({ required: true })
|
|
|
74
86
|
|
|
75
87
|
const slots = defineSlots<{
|
|
76
88
|
'right-icon'?(): any
|
|
89
|
+
prefix?(): any
|
|
90
|
+
suffix?(): any
|
|
77
91
|
}>()
|
|
78
92
|
|
|
79
93
|
function handleInput(event: Event) {
|
|
@@ -135,6 +149,31 @@ defineExpose({ focus })
|
|
|
135
149
|
color: var(--color-brand-txt-base);
|
|
136
150
|
}
|
|
137
151
|
|
|
152
|
+
.prefix,
|
|
153
|
+
.suffix {
|
|
154
|
+
display: flex;
|
|
155
|
+
align-items: center;
|
|
156
|
+
justify-content: center;
|
|
157
|
+
align-self: stretch;
|
|
158
|
+
background-color: var(--color-neutral-background-secondary);
|
|
159
|
+
padding: 0.8rem 1.6rem;
|
|
160
|
+
color: var(--color-neutral-txt-secondary);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.prefix {
|
|
164
|
+
border-end-start-radius: 0.4rem;
|
|
165
|
+
border-start-start-radius: 0.4rem;
|
|
166
|
+
border-inline-end: 0.1rem solid var(--color-neutral-border);
|
|
167
|
+
margin-inline-start: -1.6rem;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.suffix {
|
|
171
|
+
border-end-end-radius: 0.4rem;
|
|
172
|
+
border-start-end-radius: 0.4rem;
|
|
173
|
+
border-inline-start: 0.1rem solid var(--color-neutral-border);
|
|
174
|
+
margin-inline-end: -1.6rem;
|
|
175
|
+
}
|
|
176
|
+
|
|
138
177
|
&.disabled .right-icon {
|
|
139
178
|
color: var(--color-neutral-txt-secondary);
|
|
140
179
|
}
|
|
@@ -163,12 +202,9 @@ defineExpose({ focus })
|
|
|
163
202
|
/* VARIANT */
|
|
164
203
|
|
|
165
204
|
&.accent--brand {
|
|
205
|
+
--selected-color: var(--color-brand-item-base);
|
|
166
206
|
border-color: var(--color-neutral-border);
|
|
167
207
|
|
|
168
|
-
&:focus-within {
|
|
169
|
-
border-color: var(--color-brand-item-base);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
208
|
&:hover {
|
|
173
209
|
border-color: var(--color-brand-item-hover);
|
|
174
210
|
}
|
|
@@ -184,6 +220,7 @@ defineExpose({ focus })
|
|
|
184
220
|
}
|
|
185
221
|
|
|
186
222
|
&.accent--warning {
|
|
223
|
+
--selected-color: var(--color-warning-item-base);
|
|
187
224
|
border-color: var(--color-warning-item-base);
|
|
188
225
|
|
|
189
226
|
&:hover {
|
|
@@ -201,6 +238,7 @@ defineExpose({ focus })
|
|
|
201
238
|
}
|
|
202
239
|
|
|
203
240
|
&.accent--danger {
|
|
241
|
+
--selected-color: var(--color-danger-item-base);
|
|
204
242
|
border-color: var(--color-danger-item-base);
|
|
205
243
|
|
|
206
244
|
&:hover {
|
|
@@ -216,5 +254,11 @@ defineExpose({ focus })
|
|
|
216
254
|
background-color: var(--color-neutral-background-disabled);
|
|
217
255
|
}
|
|
218
256
|
}
|
|
257
|
+
|
|
258
|
+
&:focus-within,
|
|
259
|
+
&.selected {
|
|
260
|
+
border-color: var(--selected-color);
|
|
261
|
+
border-width: 0.2rem;
|
|
262
|
+
}
|
|
219
263
|
}
|
|
220
264
|
</style>
|
|
@@ -10,24 +10,12 @@
|
|
|
10
10
|
type="text"
|
|
11
11
|
accent="brand"
|
|
12
12
|
:aria-label="uiStore.isSmall ? t('query-search-bar:label') : undefined"
|
|
13
|
-
:icon="!uiStore.isSmall ? 'fa:magnifying-glass' : undefined"
|
|
14
13
|
:placeholder="t('query-search-bar:placeholder')"
|
|
15
14
|
/>
|
|
16
15
|
<template v-if="!uiStore.isSmall">
|
|
17
|
-
<UiButton size="medium" accent="brand" variant="
|
|
16
|
+
<UiButton size="medium" accent="brand" variant="secondary" type="submit" class="action-button">
|
|
18
17
|
{{ t('action:search') }}
|
|
19
18
|
</UiButton>
|
|
20
|
-
<VtsDivider type="stretch" />
|
|
21
|
-
<UiButton
|
|
22
|
-
v-tooltip="t('coming-soon!')"
|
|
23
|
-
size="medium"
|
|
24
|
-
accent="brand"
|
|
25
|
-
variant="secondary"
|
|
26
|
-
disabled
|
|
27
|
-
class="action-button"
|
|
28
|
-
>
|
|
29
|
-
{{ t('query-search-bar:use-query-builder') }}
|
|
30
|
-
</UiButton>
|
|
31
19
|
</template>
|
|
32
20
|
|
|
33
21
|
<!-- Mobile icons: search + filter -->
|
|
@@ -39,11 +27,9 @@
|
|
|
39
27
|
</template>
|
|
40
28
|
|
|
41
29
|
<script lang="ts" setup>
|
|
42
|
-
import VtsDivider from '@core/components/divider/VtsDivider.vue'
|
|
43
30
|
import UiButton from '@core/components/ui/button/UiButton.vue'
|
|
44
31
|
import UiButtonIcon from '@core/components/ui/button-icon/UiButtonIcon.vue'
|
|
45
32
|
import UiInput from '@core/components/ui/input/UiInput.vue'
|
|
46
|
-
import { vTooltip } from '@core/directives/tooltip.directive'
|
|
47
33
|
import { useUiStore } from '@core/stores/ui.store'
|
|
48
34
|
import { uniqueId } from '@core/utils/unique-id.util'
|
|
49
35
|
import { ref } from 'vue'
|
|
@@ -34,7 +34,7 @@ import VtsIcon from '@core/components/icon/VtsIcon.vue'
|
|
|
34
34
|
import VtsQuickTaskList from '@core/components/task/VtsQuickTaskList.vue'
|
|
35
35
|
import UiButtonIcon from '@core/components/ui/button-icon/UiButtonIcon.vue'
|
|
36
36
|
import UiTag from '@core/components/ui/tag/UiTag.vue'
|
|
37
|
-
import type { Task } from '@core/
|
|
37
|
+
import type { Task } from '@core/components/ui/task-item/UiTaskItem.vue'
|
|
38
38
|
import { useToggle } from '@vueuse/core'
|
|
39
39
|
import { computed } from 'vue'
|
|
40
40
|
import { useI18n } from 'vue-i18n'
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
<script lang="ts" setup>
|
|
16
16
|
import VtsQuickTaskList from '@core/components/task/VtsQuickTaskList.vue'
|
|
17
17
|
import VtsQuickTaskTabBar, { type TaskTab } from '@core/components/task/VtsQuickTaskTabBar.vue'
|
|
18
|
+
import type { Task } from '@core/components/ui/task-item/UiTaskItem.vue'
|
|
18
19
|
import { useUiStore } from '@core/stores/ui.store'
|
|
19
|
-
import type { Task } from '@core/types/task.type.ts'
|
|
20
20
|
import { computed, ref } from 'vue'
|
|
21
21
|
|
|
22
22
|
const props = defineProps<{
|
|
@@ -21,10 +21,13 @@
|
|
|
21
21
|
</div>
|
|
22
22
|
|
|
23
23
|
<div class="main-content">
|
|
24
|
-
<div v-if="task.name" class="content-left">
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
<div v-if="task.nameParts || task.name" class="content-left">
|
|
25
|
+
<template v-if="task.nameParts">
|
|
26
|
+
<template v-for="(part, index) in task.nameParts" :key="index">
|
|
27
|
+
<UiLink size="medium" :to="part.to">{{ part.text }}</UiLink>
|
|
28
|
+
</template>
|
|
29
|
+
</template>
|
|
30
|
+
<UiLink v-else size="small">{{ task.name }}</UiLink>
|
|
28
31
|
<div v-if="shouldShowInfos || hasSubTasks" class="infos">
|
|
29
32
|
<UiCounter v-if="hasSubTasks" :value="subTasksCount" accent="brand" variant="secondary" size="small" />
|
|
30
33
|
<UiInfo v-if="hasInfos" accent="info" />
|
|
@@ -41,7 +44,7 @@
|
|
|
41
44
|
<UiCircleProgressBar :accent="progressAccent" size="small" :value="progress" />
|
|
42
45
|
</div>
|
|
43
46
|
<div class="actions">
|
|
44
|
-
<UiButtonIcon icon="fa:eye" size="
|
|
47
|
+
<UiButtonIcon icon="fa:eye" size="small" accent="brand" @click="emit('select', task.id)" />
|
|
45
48
|
</div>
|
|
46
49
|
</div>
|
|
47
50
|
</div>
|
|
@@ -61,6 +64,7 @@ import UiLink from '@core/components/ui/link/UiLink.vue'
|
|
|
61
64
|
import UiTaskList from '@core/components/ui/task-list/UiTaskList.vue'
|
|
62
65
|
import { useTimeAgo } from '@core/composables/locale-time-ago.composable.ts'
|
|
63
66
|
import { vTooltip } from '@core/directives/tooltip.directive'
|
|
67
|
+
import type { TaskObjectSegment, TaskStatus } from '@core/types/task.type.ts'
|
|
64
68
|
import { logicOr } from '@vueuse/math'
|
|
65
69
|
import { computed } from 'vue'
|
|
66
70
|
import { useI18n } from 'vue-i18n'
|
|
@@ -69,9 +73,13 @@ export type Task = {
|
|
|
69
73
|
id: string
|
|
70
74
|
infos?: { data: unknown; message: string }[]
|
|
71
75
|
name?: string
|
|
76
|
+
nameParts?: TaskObjectSegment[]
|
|
72
77
|
progress?: number
|
|
78
|
+
tag?: string
|
|
79
|
+
userName?: string
|
|
80
|
+
start?: number
|
|
73
81
|
end?: number
|
|
74
|
-
status:
|
|
82
|
+
status: TaskStatus
|
|
75
83
|
subtasks?: Task[]
|
|
76
84
|
warnings?: { data: unknown; message: string }[]
|
|
77
85
|
}
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
|
|
17
17
|
<script lang="ts" setup>
|
|
18
18
|
import UiTaskItem from '@core/components/ui/task-item/UiTaskItem.vue'
|
|
19
|
+
import type { Task } from '@core/components/ui/task-item/UiTaskItem.vue'
|
|
19
20
|
import { useCollection } from '@core/packages/collection'
|
|
20
|
-
import type { Task } from '@core/types/task.type.ts'
|
|
21
21
|
|
|
22
22
|
const { tasks, depth = 0 } = defineProps<{
|
|
23
23
|
tasks: Task[]
|
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
<!--
|
|
1
|
+
<!-- v5 -->
|
|
2
2
|
<template>
|
|
3
|
-
<div class="ui-textarea" :class="toVariants({ accent:
|
|
3
|
+
<div class="ui-textarea" :class="toVariants({ accent: isExceedingMaxCharacters ? 'danger' : accent })">
|
|
4
4
|
<UiLabel v-if="slots.default" :accent="labelAccent" :required :href :for="id">
|
|
5
5
|
<slot />
|
|
6
6
|
</UiLabel>
|
|
7
7
|
<textarea v-bind="attrs" :id ref="textarea" v-model="model" :disabled class="textarea" />
|
|
8
8
|
<UiCharacterLimit v-if="maxCharacters" :count="model.trim().length" :max="maxCharacters" />
|
|
9
|
+
<UiInfo v-if="slots.info" accent="info">
|
|
10
|
+
<slot name="info" />
|
|
11
|
+
</UiInfo>
|
|
9
12
|
<UiInfo v-if="isExceedingMaxCharacters" accent="danger">
|
|
10
13
|
{{ t('field:exceeds-max-characters', { max: maxCharacters }) }}
|
|
11
14
|
</UiInfo>
|
|
12
|
-
<UiInfo v-if="slots.
|
|
13
|
-
<slot name="
|
|
15
|
+
<UiInfo v-if="slots.message && labelAccent !== 'neutral'" :accent="labelAccent">
|
|
16
|
+
<slot name="message" />
|
|
14
17
|
</UiInfo>
|
|
15
18
|
</div>
|
|
16
19
|
</template>
|
|
@@ -46,6 +49,7 @@ const model = defineModel<string>({ required: true })
|
|
|
46
49
|
const slots = defineSlots<{
|
|
47
50
|
default?(): any
|
|
48
51
|
info?(): any
|
|
52
|
+
message?(): any
|
|
49
53
|
}>()
|
|
50
54
|
|
|
51
55
|
const { t } = useI18n()
|
|
@@ -58,16 +62,10 @@ const { focused } = useFocus(textAreaElement)
|
|
|
58
62
|
|
|
59
63
|
// WIP: To update when using VeeValidate and custom validation rules
|
|
60
64
|
const isExceedingMaxCharacters = computed(() =>
|
|
61
|
-
maxCharacters !== undefined ? model.value.trim().length > maxCharacters : false
|
|
65
|
+
maxCharacters !== undefined ? !focused.value && model.value.trim().length > maxCharacters : false
|
|
62
66
|
)
|
|
63
67
|
|
|
64
|
-
const hasMaxCharactersError = computed(() => !focused.value && isExceedingMaxCharacters.value)
|
|
65
|
-
|
|
66
68
|
const labelAccent = computed(() => {
|
|
67
|
-
if (hasMaxCharactersError.value) {
|
|
68
|
-
return 'danger'
|
|
69
|
-
}
|
|
70
|
-
|
|
71
69
|
return accent === 'brand' ? 'neutral' : accent
|
|
72
70
|
})
|
|
73
71
|
</script>
|
|
@@ -101,8 +99,8 @@ const labelAccent = computed(() => {
|
|
|
101
99
|
}
|
|
102
100
|
|
|
103
101
|
&:focus:not(:active) {
|
|
104
|
-
border-width: 0.2rem;
|
|
105
102
|
border-color: var(--color-brand-item-base);
|
|
103
|
+
box-shadow: inset 0 0 0 0.1rem var(--color-brand-item-base);
|
|
106
104
|
}
|
|
107
105
|
|
|
108
106
|
&:disabled {
|
|
@@ -125,8 +123,8 @@ const labelAccent = computed(() => {
|
|
|
125
123
|
}
|
|
126
124
|
|
|
127
125
|
&:focus:not(:active) {
|
|
128
|
-
border-width: 0.2rem;
|
|
129
126
|
border-color: var(--color-warning-item-base);
|
|
127
|
+
box-shadow: inset 0 0 0 0.1rem var(--color-warning-item-base);
|
|
130
128
|
}
|
|
131
129
|
|
|
132
130
|
&:disabled {
|
|
@@ -149,8 +147,8 @@ const labelAccent = computed(() => {
|
|
|
149
147
|
}
|
|
150
148
|
|
|
151
149
|
&:focus:not(:active) {
|
|
152
|
-
border-width: 0.2rem;
|
|
153
150
|
border-color: var(--color-danger-item-base);
|
|
151
|
+
box-shadow: inset 0 0 0 0.1rem var(--color-danger-item-base);
|
|
154
152
|
}
|
|
155
153
|
|
|
156
154
|
&:disabled {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<VtsModal accent="info" icon="status:info-picto" dismissible>
|
|
3
|
+
<template #title>
|
|
4
|
+
{{ connection.title }}
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<template v-if="action === CONNECTION_ACTION.DISCONNECT" #content>
|
|
8
|
+
{{ t('vif-disconnect-info') }}
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<template #buttons>
|
|
12
|
+
<VtsModalCancelButton>{{ t('action:go-back') }}</VtsModalCancelButton>
|
|
13
|
+
<VtsModalConfirmButton>
|
|
14
|
+
{{ connection.action }}
|
|
15
|
+
</VtsModalConfirmButton>
|
|
16
|
+
</template>
|
|
17
|
+
</VtsModal>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<script lang="ts" setup>
|
|
21
|
+
import VtsModal from '@core/components/modal/VtsModal.vue'
|
|
22
|
+
import VtsModalCancelButton from '@core/components/modal/VtsModalCancelButton.vue'
|
|
23
|
+
import VtsModalConfirmButton from '@core/components/modal/VtsModalConfirmButton.vue'
|
|
24
|
+
import { useMapper } from '@core/packages/mapper'
|
|
25
|
+
import { CONNECTION_ACTION, type ConnectionAction } from '@core/types/connection.ts'
|
|
26
|
+
import { useI18n } from 'vue-i18n'
|
|
27
|
+
|
|
28
|
+
const { action, count } = defineProps<{
|
|
29
|
+
action: ConnectionAction
|
|
30
|
+
count: number
|
|
31
|
+
}>()
|
|
32
|
+
|
|
33
|
+
const { t } = useI18n()
|
|
34
|
+
|
|
35
|
+
const connection = useMapper(
|
|
36
|
+
() => action,
|
|
37
|
+
() => ({
|
|
38
|
+
connect: { title: t('vif-connect-title', { n: count }), action: t('action:connect-n-vifs', { n: count }) },
|
|
39
|
+
disconnect: { title: t('vif-disconnect-title', { n: count }), action: t('action:disconnect-n-vifs', { n: count }) },
|
|
40
|
+
}),
|
|
41
|
+
'connect'
|
|
42
|
+
)
|
|
43
|
+
</script>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# useMultiSelect composable
|
|
2
|
+
|
|
3
|
+
```vue
|
|
4
|
+
<template>
|
|
5
|
+
<table>
|
|
6
|
+
<thead>
|
|
7
|
+
<tr>
|
|
8
|
+
<th>
|
|
9
|
+
<input type="checkbox" v-model="areAllSelected" />
|
|
10
|
+
</th>
|
|
11
|
+
<th>Name</th>
|
|
12
|
+
</tr>
|
|
13
|
+
</thead>
|
|
14
|
+
<tbody>
|
|
15
|
+
<tr v-for="item in items">
|
|
16
|
+
<td>
|
|
17
|
+
<input type="checkbox" :value="item.id" v-model="selected" />
|
|
18
|
+
</td>
|
|
19
|
+
<td>{{ item.name }}</td>
|
|
20
|
+
</tr>
|
|
21
|
+
</tbody>
|
|
22
|
+
</table>
|
|
23
|
+
|
|
24
|
+
<!-- You can use something else than a "Select All" checkbox -->
|
|
25
|
+
<button @click="areAllSelected = !areAllSelected">Toggle all selected</button>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<script lang="ts" setup>
|
|
29
|
+
import useMultiSelect from './multi-select.composable'
|
|
30
|
+
|
|
31
|
+
const { selected, areAllSelected } = useMultiSelect()
|
|
32
|
+
</script>
|
|
33
|
+
```
|