fix(workflowengine): Fix multiple UI issues in workflow engine admin settings

Signed-off-by: Joas Schilling <coding@schilljs.com>
This commit is contained in:
Joas Schilling 2023-05-10 09:09:03 +02:00
parent 7cc7984ab7
commit eea84febf7
No known key found for this signature in database
GPG key ID: 74434EFE0D2E2205
7 changed files with 182 additions and 170 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;