feat(xo6): update input version (#10009)

This commit is contained in:
Alina D
2026-07-24 11:47:22 +02:00
committed by GitHub
parent 316122a683
commit b0194b2c5f
8 changed files with 66 additions and 22 deletions

View File

@@ -4,7 +4,7 @@
<div class="inputs">
<UiInput v-if="newFilter.isAdvanced" v-model="filterContent" accent="brand" class="advanced-input" />
<template v-else>
<VtsSelect :id="builderPropertySelectId" accent="brand" :icon="currentFilterIcon" />
<VtsSelect :id="builderPropertySelectId" accent="brand" />
<VtsSelect v-if="hasComparisonSelect" :id="builderComparisonSelectId" accent="brand" />
<VtsSelect v-if="currentFilter?.type === 'enum'" :id="builderValueSelectId" accent="brand" />
<UiInput v-else-if="hasValueInput" v-model="newFilter.builder.value" accent="brand" />
@@ -20,7 +20,6 @@
<script lang="ts" setup>
import UiActionButton from '@/components/ui/UiActionButton.vue'
import { buildComplexMatcherNode } from '@/libs/complex-matcher.utils'
import { getFilterIcon } from '@/libs/utils'
import type { Filter, FilterComparisons, FilterComparisonType, Filters, FilterType, NewFilter } from '@/types/filter'
import VtsSelect from '@core/components/select/VtsSelect.vue'
import UiInput from '@core/components/ui/input/UiInput.vue'
@@ -104,8 +103,6 @@ function getDefaultComparisonType(property: string) {
const currentFilter = computed<Filter | undefined>(() => availableFilters[builderProperty.value])
const currentFilterIcon = computed(() => getFilterIcon(currentFilter.value))
const hasValueInput = computed(() =>
currentFilter.value ? ['string', 'number'].includes(currentFilter.value?.type) : false
)

View File

@@ -98,7 +98,7 @@
<VtsKeyValueList>
<VtsKeyValueRow>
<template #label>
<VtsSelect :id="localeSelectId" icon="fa:earth-americas" accent="brand" />
<VtsSelect :id="localeSelectId" accent="brand" />
</template>
<template #value>
<UiLink size="medium" href="https://translate.vates.tech/engage/xen-orchestra/">

View File

@@ -6,13 +6,19 @@
prop('accent').enum('brand', 'warning', 'danger').required().preset('brand').widget(),
prop('id').str().widget(),
prop('required').bool().widget(),
prop('selected').bool().widget(),
prop('readonly').bool().widget(),
prop('type').enum('text', 'number', 'password', 'search').preset('text').widget(),
prop('rightIcon').widget(
choice({ label: 'Search', value: 'fa:magnifying-glass' }, { label: 'AngleDown', value: 'fa:angle-down' })
),
prop('clearable').bool().widget(),
prop('disabled').bool().widget().help('Not defined as component prop'),
slot('rightIcon').help('Can be used in place of rightIcon prop'),
prop('prefix').str().widget(),
prop('suffix').str().widget(),
prop('disabled').bool().widget(),
slot('right-icon').help('Can be used in place of rightIcon prop'),
slot('prefix').help('Can be used in place of prefix prop'),
slot('suffix').help('Can be used in place of suffix prop'),
]"
>
<UiInput placeholder="Enter your search" v-bind="properties" />

View File

@@ -51,10 +51,10 @@ const unsortedMessages = useArrayMap(
})
)
const messages = useRanked(unsortedMessages, ({ accent }) => accent, ['danger', 'warning', 'success', 'info'])
const messages = useRanked(unsortedMessages, ({ accent }) => accent, ['muted', 'info', 'success', 'warning', 'danger'])
const labelAccent = useMapper<InfoAccent, LabelAccent>(
() => messages.value[0]?.accent,
() => messages.value.at(-1)?.accent,
{
info: 'neutral',
success: 'neutral',

View File

@@ -4,7 +4,6 @@
ref="triggerRef"
:accent
:disabled="isDisabled"
:icon
:model-value="selectedLabel"
:placeholder
:required="isRequired"
@@ -47,7 +46,6 @@ import VtsOption from '@core/components/select/VtsOption.vue'
import UiDropdown from '@core/components/ui/dropdown/UiDropdown.vue'
import UiDropdownList from '@core/components/ui/dropdown/UiDropdownList.vue'
import UiInput from '@core/components/ui/input/UiInput.vue'
import type { IconName } from '@core/icons'
import {
type FormSelect,
type FormSelectId,
@@ -62,7 +60,6 @@ import { useI18n } from 'vue-i18n'
const { accent, id } = defineProps<{
accent: 'brand' | 'warning' | 'danger'
id: TSelectId
icon?: IconName
}>()
defineSlots<{

View File

@@ -1,6 +1,11 @@
<!-- v5 -->
<!-- v9 -->
<template>
<div :class="toVariants({ accent, disabled })" class="ui-input" @click.self="focus()">
<div :class="toVariants({ accent, disabled, selected })" class="ui-input" @click.self="focus()">
<span v-if="slots.prefix || prefix" class="prefix">
<slot name="prefix">
{{ prefix }}
</slot>
</span>
<span v-if="readonly" class="typo-body-regular input text-ellipsis">
<input ref="inputRef" class="readonly-input" readonly type="text" />
{{ modelValue }}
@@ -19,14 +24,19 @@
/>
<UiButtonIcon
v-if="!disabled && modelValue && clearable"
icon="fa:xmark"
accent="brand"
size="small"
icon="action:close-cancel-clear"
accent="brand"
@click="clear()"
/>
<slot v-if="slots['right-icon'] || rightIcon" name="right-icon">
<VtsIcon :name="rightIcon" size="medium" class="right-icon" />
</slot>
<span v-if="slots.suffix || suffix" class="suffix">
<slot name="suffix">
{{ suffix }}
</slot>
</span>
</div>
</template>
@@ -58,12 +68,14 @@ const {
id?: string
required?: boolean
disabled?: boolean
selected?: boolean
readonly?: boolean
detached?: boolean
type?: InputType
icon?: IconName
rightIcon?: IconName
clearable?: boolean
prefix?: string
suffix?: string
}>()
const emit = defineEmits<{
@@ -74,6 +86,8 @@ const modelValue = defineModel<string | number | undefined>({ required: true })
const slots = defineSlots<{
'right-icon'?(): any
prefix?(): any
suffix?(): any
}>()
function handleInput(event: Event) {
@@ -135,6 +149,31 @@ defineExpose({ focus })
color: var(--color-brand-txt-base);
}
.prefix,
.suffix {
display: flex;
align-items: center;
justify-content: center;
align-self: stretch;
background-color: var(--color-neutral-background-secondary);
padding: 0.8rem 1.6rem;
color: var(--color-neutral-txt-secondary);
}
.prefix {
border-end-start-radius: 0.4rem;
border-start-start-radius: 0.4rem;
border-inline-end: 0.1rem solid var(--color-neutral-border);
margin-inline-start: -1.6rem;
}
.suffix {
border-end-end-radius: 0.4rem;
border-start-end-radius: 0.4rem;
border-inline-start: 0.1rem solid var(--color-neutral-border);
margin-inline-end: -1.6rem;
}
&.disabled .right-icon {
color: var(--color-neutral-txt-secondary);
}
@@ -163,12 +202,9 @@ defineExpose({ focus })
/* VARIANT */
&.accent--brand {
--selected-color: var(--color-brand-item-base);
border-color: var(--color-neutral-border);
&:focus-within {
border-color: var(--color-brand-item-base);
}
&:hover {
border-color: var(--color-brand-item-hover);
}
@@ -184,6 +220,7 @@ defineExpose({ focus })
}
&.accent--warning {
--selected-color: var(--color-warning-item-base);
border-color: var(--color-warning-item-base);
&:hover {
@@ -201,6 +238,7 @@ defineExpose({ focus })
}
&.accent--danger {
--selected-color: var(--color-danger-item-base);
border-color: var(--color-danger-item-base);
&:hover {
@@ -216,5 +254,11 @@ defineExpose({ focus })
background-color: var(--color-neutral-background-disabled);
}
}
&:focus-within,
&.selected {
border-color: var(--selected-color);
border-width: 0.2rem;
}
}
</style>

View File

@@ -10,7 +10,6 @@
type="text"
accent="brand"
:aria-label="uiStore.isSmall ? t('query-search-bar:label') : undefined"
:icon="!uiStore.isSmall ? 'fa:magnifying-glass' : undefined"
:placeholder="t('query-search-bar:placeholder')"
/>
<template v-if="!uiStore.isSmall">

View File

@@ -46,6 +46,7 @@
- [REST API] Expose `/rest/v0/acl-roles/:id/groups` (PR [#10085](https://github.com/vatesfr/xen-orchestra/pull/10085))
- [XO6] Fix some design inconsistency between pages (PR [#10109](https://github.com/vatesfr/xen-orchestra/pull/10109))
- [XO6/Users] Add Users Table (PR [#10029](https://github.com/vatesfr/xen-orchestra/pull/10029))
- [XO6] Input fields now support prefix/suffix sections, and display validation messages ordered by severity (PR [#10009](https://github.com/vatesfr/xen-orchestra/pull/10009))
### Bug fixes