]> source.dussan.org Git - nextcloud-server.git/commitdiff
fix(workflowengine): Fix multiple UI issues in workflow engine admin settings
authorJoas Schilling <coding@schilljs.com>
Wed, 10 May 2023 07:09:03 +0000 (09:09 +0200)
committerJoas Schilling <coding@schilljs.com>
Wed, 10 May 2023 09:59:29 +0000 (11:59 +0200)
Signed-off-by: Joas Schilling <coding@schilljs.com>
apps/workflowengine/src/components/Check.vue
apps/workflowengine/src/components/Checks/FileMimeType.vue
apps/workflowengine/src/components/Checks/RequestTime.vue
apps/workflowengine/src/components/Checks/RequestURL.vue
apps/workflowengine/src/components/Checks/RequestUserAgent.vue
apps/workflowengine/src/components/Checks/RequestUserGroup.vue
apps/workflowengine/src/components/Rule.vue

index 427835a7ec78732528a216ddee55a0ac4f7dd0dc..83f823838f75d45297a23580db8992a0b48d8cdf 100644 (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"
                        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.js'
 import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
 import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
+import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
+
+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;
        }
index 472edda613a69c4b83dbad3f0f47706a26e845dd..941347f0aa2922981a34374e53d5c408cb2c2dc4 100644 (file)
   -->
 <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.js'
+import NcEllipsisedOption from '@nextcloud/vue/dist/Components/NcEllipsisedOption.js'
+import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
 import valueMixin from './../../mixins/valueMixin.js'
 import { imagePath } from '@nextcloud/router'
 
 export default {
        name: 'FileMimeType',
        components: {
-               NcMultiselect,
+               NcEllipsisedOption,
+               NcSelect,
        },
        mixins: [
                valueMixin,
@@ -73,22 +75,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',
                                },
                        ],
                }
@@ -98,7 +100,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
                        }
@@ -108,18 +110,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,
                        }
                },
        },
@@ -131,7 +133,7 @@ export default {
                },
                setValue(value) {
                        if (value !== null) {
-                               this.newValue = value.pattern
+                               this.newValue = value.id
                                this.$emit('input', this.newValue)
                        }
                },
@@ -143,24 +145,30 @@ export default {
 }
 </script>
 <style scoped lang="scss">
-.multiselect, input[type='text'] {
+.v-select,
+input[type='text'] {
        width: 100%;
 }
-.multiselect::v-deep .multiselect__content-wrapper li > span,
-.multiselect::v-deep .multiselect__single {
-       display: flex;
-       white-space: nowrap;
-       overflow: hidden;
-       text-overflow: ellipsis;
+
+input[type=text] {
+       min-height: 48px;
 }
 
-.option__icon {
+.option__icon,
+.option__icon-img {
        display: inline-block;
        min-width: 30px;
-       background-position: left;
+       background-position: center;
+       vertical-align: middle;
 }
 
 .option__icon-img {
-       margin-right: 14px;
+       text-align: center;
+}
+
+.option__title {
+       display: inline-flex;
+       width: calc(100% - 36px);
+       vertical-align: middle;
 }
 </style>
index 79a91c0e544f1f7bc4bc3f8eab5de7dfb234b465..36177e6ad6546f9722a9d3d8c52bb1e4ce4b5df9 100644 (file)
                <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.js'
+import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
 import moment from 'moment-timezone'
 import valueMixin from '../../mixins/valueMixin.js'
 
@@ -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;
index 28184a52eb5d3938c21c54cee5468ab481ee1fa9..ce5e009cde9c7b7e1979a3df470bec05884d8455 100644 (file)
 
 <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.js'
+import NcEllipsisedOption from '@nextcloud/vue/dist/Components/NcEllipsisedOption.js'
+import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
 import valueMixin from '../../mixins/valueMixin.js'
 
 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>
index 1d00bdc238d47a456ffcea147cbef041365f1740..eccf76ae58c000127927ed52b7d416bd644f09e1 100644 (file)
 
 <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"
 </template>
 
 <script>
-import NcMultiselect from '@nextcloud/vue/dist/Components/NcMultiselect.js'
+import NcEllipsisedOption from '@nextcloud/vue/dist/Components/NcEllipsisedOption.js'
+import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
 import valueMixin from '../../mixins/valueMixin.js'
 
 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%;
        }
-
-       .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;
+       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 {
-               white-space: nowrap;
-               overflow: hidden;
-               text-overflow: ellipsis;
+               display: inline-flex;
+               width: calc(100% - 36px);
+               vertical-align: middle;
        }
 </style>
index cfb9c7dcc98784dd5ae6965da35d1d56e041f5af..542fa46765e17e526d178791f9435e832d11b5a8 100644 (file)
 
 <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.js'
+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>
index 6b0abed88c896ca218d04221a02505526720afa7..e641d8cffb814aa4b096f78dc9b5045a2a64e2fa 100644 (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;