Merge pull request #38189 from nextcloud/backport/38170/stable26

[stable26] fix(workflowengine): Fix multiple UI issues in workflow engine admin settings
This commit is contained in:
Joas Schilling 2023-05-16 18:36:32 +02:00 committed by GitHub
commit 5ea5e7b339
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 188 additions and 176 deletions

View file

@ -1,20 +1,20 @@
<template>
<div v-click-outside="hideDelete" class="check" @click="showDelete">
<NcMultiselect ref="checkSelector"
<NcSelect ref="checkSelector"
v-model="currentOption"
:options="options"
label="name"
track-by="class"
:allow-empty="false"
:clearable="false"
:placeholder="t('workflowengine', 'Select a filter')"
@input="updateCheck" />
<NcMultiselect v-model="currentOperator"
<NcSelect v-model="currentOperator"
:disabled="!currentOption"
:options="operators"
class="comparator"
label="name"
track-by="operator"
:allow-empty="false"
:clearable="false"
:placeholder="t('workflowengine', 'Select a comparator')"
@input="updateCheck" />
<component :is="currentOption.component"
@ -35,15 +35,22 @@
class="option"
@input="updateCheck">
<NcActions v-if="deleteVisible || !currentOption">
<NcActionButton icon="icon-close" @click="$emit('remove')" />
<NcActionButton :title="t('workflowengine', 'Remove filter')" @click="$emit('remove')">
<template #icon>
<CloseIcon :size="20" />
</template>
</NcActionButton>
</NcActions>
</div>
</template>
<script>
import NcMultiselect from '@nextcloud/vue/dist/Components/NcMultiselect'
import NcActions from '@nextcloud/vue/dist/Components/NcActions'
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton'
import NcSelect from '@nextcloud/vue/dist/Components/NcSelect'
import CloseIcon from 'vue-material-design-icons/Close.vue'
import ClickOutside from 'vue-click-outside'
export default {
@ -51,7 +58,10 @@ export default {
components: {
NcActionButton,
NcActions,
NcMultiselect,
NcSelect,
// Icons
CloseIcon,
},
directives: {
ClickOutside,
@ -151,45 +161,36 @@ export default {
.check {
display: flex;
flex-wrap: wrap;
align-items: flex-start; // to not stretch components vertically
width: 100%;
padding-right: 20px;
& > *:not(.close) {
width: 180px;
}
& > .comparator {
min-width: 130px;
width: 130px;
min-width: 200px;
width: 200px;
}
& > .option {
min-width: 230px;
width: 230px;
min-width: 260px;
width: 260px;
min-height: 48px;
& > input[type=text] {
min-height: 48px;
}
}
& > .multiselect,
& > .v-select,
& > .button-vue,
& > input[type=text] {
margin-right: 5px;
margin-bottom: 5px;
}
.multiselect::v-deep .multiselect__content-wrapper li>span,
.multiselect::v-deep .multiselect__single {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
input[type=text] {
margin: 0;
}
::placeholder {
font-size: 10px;
}
button.action-item.action-item--single.icon-close {
height: 44px;
width: 44px;
margin-top: -5px;
margin-bottom: -5px;
}
.invalid {
border-color: var(--color-error) !important;
}

View file

@ -22,48 +22,50 @@
<template>
<div>
<NcMultiselect :value="currentValue"
<NcSelect :value="currentValue"
:placeholder="t('workflowengine', 'Select a file type')"
label="label"
track-by="pattern"
:options="options"
:multiple="false"
:tagging="false"
:clearable="false"
@input="setValue">
<template slot="singleLabel" slot-scope="props">
<span v-if="props.option.icon" class="option__icon" :class="props.option.icon" />
<img v-else
class="option__icon-img"
:src="props.option.iconUrl"
alt="">
<span class="option__title option__title_single">{{ props.option.label }}</span>
<template #option="option">
<span v-if="option.icon" class="option__icon" :class="option.icon" />
<span v-else class="option__icon-img">
<img :src="option.iconUrl" alt="">
</span>
<span class="option__title">
<NcEllipsisedOption :name="String(option.label)" />
</span>
</template>
<template slot="option" slot-scope="props">
<span v-if="props.option.icon" class="option__icon" :class="props.option.icon" />
<img v-else
class="option__icon-img"
:src="props.option.iconUrl"
alt="">
<span class="option__title">{{ props.option.label }}</span>
<template #selected-option="selectedOption">
<span v-if="selectedOption.icon" class="option__icon" :class="selectedOption.icon" />
<span v-else class="option__icon-img">
<img :src="selectedOption.iconUrl" alt="">
</span>
<span class="option__title">
<NcEllipsisedOption :name="String(selectedOption.label)" />
</span>
</template>
</NcMultiselect>
</NcSelect>
<input v-if="!isPredefined"
type="text"
:value="currentValue.pattern"
:value="currentValue.id"
:placeholder="t('workflowengine', 'e.g. httpd/unix-directory')"
@input="updateCustom">
</div>
</template>
<script>
import NcMultiselect from '@nextcloud/vue/dist/Components/NcMultiselect'
import NcEllipsisedOption from '@nextcloud/vue/dist/Components/NcEllipsisedOption.js'
import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
import valueMixin from './../../mixins/valueMixin'
import { imagePath } from '@nextcloud/router'
export default {
name: 'FileMimeType',
components: {
NcMultiselect,
NcEllipsisedOption,
NcSelect,
},
mixins: [
valueMixin,
@ -74,22 +76,22 @@ export default {
{
icon: 'icon-folder',
label: t('workflowengine', 'Folder'),
pattern: 'httpd/unix-directory',
id: 'httpd/unix-directory',
},
{
icon: 'icon-picture',
label: t('workflowengine', 'Images'),
pattern: '/image\\/.*/',
id: '/image\\/.*/',
},
{
iconUrl: imagePath('core', 'filetypes/x-office-document'),
label: t('workflowengine', 'Office documents'),
pattern: '/(vnd\\.(ms-|openxmlformats-|oasis\\.opendocument).*)$/',
id: '/(vnd\\.(ms-|openxmlformats-|oasis\\.opendocument).*)$/',
},
{
iconUrl: imagePath('core', 'filetypes/application-pdf'),
label: t('workflowengine', 'PDF documents'),
pattern: 'application/pdf',
id: 'application/pdf',
},
],
}
@ -99,7 +101,7 @@ export default {
return [...this.predefinedTypes, this.customValue]
},
isPredefined() {
const matchingPredefined = this.predefinedTypes.find((type) => this.newValue === type.pattern)
const matchingPredefined = this.predefinedTypes.find((type) => this.newValue === type.id)
if (matchingPredefined) {
return true
}
@ -109,18 +111,18 @@ export default {
return {
icon: 'icon-settings-dark',
label: t('workflowengine', 'Custom MIME type'),
pattern: '',
id: '',
}
},
currentValue() {
const matchingPredefined = this.predefinedTypes.find((type) => this.newValue === type.pattern)
const matchingPredefined = this.predefinedTypes.find((type) => this.newValue === type.id)
if (matchingPredefined) {
return matchingPredefined
}
return {
icon: 'icon-settings-dark',
label: t('workflowengine', 'Custom mimetype'),
pattern: this.newValue,
id: this.newValue,
}
},
},
@ -132,7 +134,7 @@ export default {
},
setValue(value) {
if (value !== null) {
this.newValue = value.pattern
this.newValue = value.id
this.$emit('input', this.newValue)
}
},
@ -144,24 +146,30 @@ export default {
}
</script>
<style scoped lang="scss">
.multiselect, input[type='text'] {
width: 100%;
}
.multiselect >>> .multiselect__content-wrapper li>span,
.multiselect >>> .multiselect__single {
display: flex;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.v-select,
input[type='text'] {
width: 100%;
}
.option__icon {
display: inline-block;
min-width: 30px;
background-position: left;
}
input[type=text] {
min-height: 48px;
}
.option__icon-img {
margin-right: 14px;
}
.option__icon,
.option__icon-img {
display: inline-block;
min-width: 30px;
background-position: center;
vertical-align: middle;
}
.option__icon-img {
text-align: center;
}
.option__title {
display: inline-flex;
width: calc(100% - 36px);
vertical-align: middle;
}
</style>

View file

@ -12,15 +12,16 @@
<p v-if="!valid" class="invalid-hint">
{{ t('workflowengine', 'Please enter a valid time span') }}
</p>
<NcMultiselect v-show="valid"
<NcSelect v-show="valid"
v-model="newValue.timezone"
:clearable="false"
:options="timezones"
@input="update" />
</div>
</template>
<script>
import NcMultiselect from '@nextcloud/vue/dist/Components/NcMultiselect'
import NcSelect from '@nextcloud/vue/dist/Components/NcSelect'
import moment from 'moment-timezone'
import valueMixin from '../../mixins/valueMixin'
@ -28,7 +29,7 @@ const zones = moment.tz.names()
export default {
name: 'RequestTime',
components: {
NcMultiselect,
NcSelect,
},
mixins: [
valueMixin,
@ -112,6 +113,7 @@ export default {
width: 50%;
margin: 0;
margin-bottom: 5px;
min-height: 48px;
&.timeslot--start {
margin-right: 5px;

View file

@ -22,41 +22,43 @@
<template>
<div>
<NcMultiselect :value="currentValue"
<NcSelect :value="currentValue"
:placeholder="t('workflowengine', 'Select a request URL')"
label="label"
track-by="pattern"
group-values="children"
group-label="label"
:clearable="false"
:options="options"
:multiple="false"
:tagging="false"
@input="setValue">
<template slot="singleLabel" slot-scope="props">
<span class="option__icon" :class="props.option.icon" />
<span class="option__title option__title_single">{{ props.option.label }}</span>
<template #option="option">
<span class="option__icon" :class="option.icon" />
<span class="option__title">
<NcEllipsisedOption :name="String(option.label)" />
</span>
</template>
<template slot="option" slot-scope="props">
<span class="option__icon" :class="props.option.icon" />
<span class="option__title">{{ props.option.label }} {{ props.option.$groupLabel }}</span>
<template #selected-option="selectedOption">
<span class="option__icon" :class="selectedOption.icon" />
<span class="option__title">
<NcEllipsisedOption :name="String(selectedOption.label)" />
</span>
</template>
</NcMultiselect>
</NcSelect>
<input v-if="!isPredefined"
type="text"
:value="currentValue.pattern"
:value="currentValue.id"
:placeholder="placeholder"
@input="updateCustom">
</div>
</template>
<script>
import NcMultiselect from '@nextcloud/vue/dist/Components/NcMultiselect'
import NcEllipsisedOption from '@nextcloud/vue/dist/Components/NcEllipsisedOption.js'
import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
import valueMixin from '../../mixins/valueMixin'
export default {
name: 'RequestURL',
components: {
NcMultiselect,
NcEllipsisedOption,
NcSelect,
},
mixins: [
valueMixin,
@ -66,10 +68,9 @@ export default {
newValue: '',
predefinedTypes: [
{
label: t('workflowengine', 'Predefined URLs'),
children: [
{ pattern: 'webdav', label: t('workflowengine', 'Files WebDAV') },
],
icon: 'icon-files-dark',
id: 'webdav',
label: t('workflowengine', 'Files WebDAV'),
},
],
}
@ -86,23 +87,16 @@ export default {
},
matchingPredefined() {
return this.predefinedTypes
.map(groups => groups.children)
.flat()
.find((type) => this.newValue === type.pattern)
.find((type) => this.newValue === type.id)
},
isPredefined() {
return !!this.matchingPredefined
},
customValue() {
return {
label: t('workflowengine', 'Others'),
children: [
{
icon: 'icon-settings-dark',
label: t('workflowengine', 'Custom URL'),
pattern: '',
},
],
icon: 'icon-settings-dark',
label: t('workflowengine', 'Custom URL'),
id: '',
}
},
currentValue() {
@ -112,7 +106,7 @@ export default {
return {
icon: 'icon-settings-dark',
label: t('workflowengine', 'Custom URL'),
pattern: this.newValue,
id: this.newValue,
}
},
},
@ -125,7 +119,7 @@ export default {
setValue(value) {
// TODO: check if value requires a regex and set the check operator according to that
if (value !== null) {
this.newValue = value.pattern
this.newValue = value.id
this.$emit('input', this.newValue)
}
},
@ -137,13 +131,24 @@ export default {
}
</script>
<style scoped lang="scss">
.multiselect, input[type='text'] {
.v-select,
input[type='text'] {
width: 100%;
}
input[type='text'] {
min-height: 48px;
}
.option__icon {
display: inline-block;
min-width: 30px;
background-position: left;
background-position: center;
vertical-align: middle;
}
.option__title {
display: inline-flex;
width: calc(100% - 36px);
vertical-align: middle;
}
</style>

View file

@ -22,28 +22,25 @@
<template>
<div>
<NcMultiselect :value="currentValue"
<NcSelect :value="currentValue"
:placeholder="t('workflowengine', 'Select a user agent')"
label="label"
track-by="pattern"
:options="options"
:multiple="false"
:tagging="false"
:clearable="false"
@input="setValue">
<template slot="singleLabel" slot-scope="props">
<span class="option__icon" :class="props.option.icon" />
<!-- v-html can be used here as t() always passes our translated strings though DOMPurify.sanitize -->
<!-- eslint-disable-next-line vue/no-v-html -->
<span class="option__title option__title_single" v-html="props.option.label" />
<template #option="option">
<span class="option__icon" :class="option.icon" />
<span class="option__title">
<NcEllipsisedOption :name="String(option.label)" />
</span>
</template>
<template slot="option" slot-scope="props">
<span class="option__icon" :class="props.option.icon" />
<!-- eslint-disable-next-line vue/no-v-html -->
<span v-if="props.option.$groupLabel" class="option__title" v-html="props.option.$groupLabel" />
<!-- eslint-disable-next-line vue/no-v-html -->
<span v-else class="option__title" v-html="props.option.label" />
<template #selected-option="selectedOption">
<span class="option__icon" :class="selectedOption.icon" />
<span class="option__title">
<NcEllipsisedOption :name="String(selectedOption.label)" />
</span>
</template>
</NcMultiselect>
</NcSelect>
<input v-if="!isPredefined"
type="text"
:value="currentValue.pattern"
@ -52,13 +49,15 @@
</template>
<script>
import NcMultiselect from '@nextcloud/vue/dist/Components/NcMultiselect'
import NcEllipsisedOption from '@nextcloud/vue/dist/Components/NcEllipsisedOption.js'
import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
import valueMixin from '../../mixins/valueMixin'
export default {
name: 'RequestUserAgent',
components: {
NcMultiselect,
NcEllipsisedOption,
NcSelect,
},
mixins: [
valueMixin,
@ -67,10 +66,10 @@ export default {
return {
newValue: '',
predefinedTypes: [
{ pattern: 'android', label: t('workflowengine', 'Android client'), icon: 'icon-phone' },
{ pattern: 'ios', label: t('workflowengine', 'iOS client'), icon: 'icon-phone' },
{ pattern: 'desktop', label: t('workflowengine', 'Desktop client'), icon: 'icon-desktop' },
{ pattern: 'mail', label: t('workflowengine', 'Thunderbird & Outlook addons'), icon: 'icon-mail' },
{ id: 'android', label: t('workflowengine', 'Android client'), icon: 'icon-phone' },
{ id: 'ios', label: t('workflowengine', 'iOS client'), icon: 'icon-phone' },
{ id: 'desktop', label: t('workflowengine', 'Desktop client'), icon: 'icon-desktop' },
{ id: 'mail', label: t('workflowengine', 'Thunderbird & Outlook addons'), icon: 'icon-mail' },
],
}
},
@ -80,7 +79,7 @@ export default {
},
matchingPredefined() {
return this.predefinedTypes
.find((type) => this.newValue === type.pattern)
.find((type) => this.newValue === type.id)
},
isPredefined() {
return !!this.matchingPredefined
@ -89,7 +88,7 @@ export default {
return {
icon: 'icon-settings-dark',
label: t('workflowengine', 'Custom user agent'),
pattern: '',
id: '',
}
},
currentValue() {
@ -99,7 +98,7 @@ export default {
return {
icon: 'icon-settings-dark',
label: t('workflowengine', 'Custom user agent'),
pattern: this.newValue,
id: this.newValue,
}
},
},
@ -112,7 +111,7 @@ export default {
setValue(value) {
// TODO: check if value requires a regex and set the check operator according to that
if (value !== null) {
this.newValue = value.pattern
this.newValue = value.id
this.$emit('input', this.newValue)
}
},
@ -124,31 +123,24 @@ export default {
}
</script>
<style scoped>
.multiselect, input[type='text'] {
.v-select,
input[type='text'] {
width: 100%;
}
input[type='text'] {
min-height: 48px;
}
.multiselect .multiselect__content-wrapper li>span {
display: flex;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.multiselect::v-deep .multiselect__single {
width: 100%;
display: flex;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.option__icon {
display: inline-block;
min-width: 30px;
background-position: left;
background-position: center;
vertical-align: middle;
}
.option__title {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-flex;
width: calc(100% - 36px);
vertical-align: middle;
}
</style>

View file

@ -22,10 +22,10 @@
<template>
<div>
<NcMultiselect :value="currentValue"
<NcSelect :value="currentValue"
:loading="status.isLoading && groups.length === 0"
:options="groups"
:multiple="false"
:clearable="false"
label="displayname"
track-by="id"
@search-change="searchAsync"
@ -34,7 +34,7 @@
</template>
<script>
import NcMultiselect from '@nextcloud/vue/dist/Components/NcMultiselect'
import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
import axios from '@nextcloud/axios'
import { generateOcsUrl } from '@nextcloud/router'
@ -46,7 +46,7 @@ const status = {
export default {
name: 'RequestUserGroup',
components: {
NcMultiselect,
NcSelect,
},
props: {
value: {
@ -106,7 +106,7 @@ export default {
}
</script>
<style scoped>
.multiselect {
width: 100%;
}
.v-select {
width: 100%;
}
</style>

View file

@ -18,7 +18,7 @@
<input v-if="lastCheckComplete"
type="button"
class="check--add"
value="Add a new filter"
:value="t('workflowengine', 'Add a new filter')"
@click="onAddFilter">
</p>
</div>
@ -213,10 +213,11 @@ export default {
flex-wrap: wrap;
border-left: 5px solid var(--color-primary-element);
.trigger, .action {
.trigger,
.action {
flex-grow: 1;
min-height: 100px;
max-width: 700px;
max-width: 920px;
}
.action {
max-width: 400px;
@ -247,6 +248,9 @@ export default {
.trigger p:first-child span {
padding-top: 3px;
}
.trigger p:last-child {
padding-top: 8px;
}
.check--add {
background-position: 7px center;

4
dist/core-common.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long