Allow placeholder and validation without custom vue component
Signed-off-by: Julius Härtl <jus@bitgrid.net>
This commit is contained in:
parent
69ac169fd9
commit
1742f97acf
|
@ -6,9 +6,9 @@
|
|||
<Multiselect :disabled="!currentOption" v-model="currentOperator" :options="operators"
|
||||
label="name" track-by="operator" :allow-empty="false"
|
||||
:placeholder="t('workflowengine', 'Select a comparator')" @input="updateCheck" />
|
||||
<component :is="currentOption.component" v-if="currentOperator && currentComponent" v-model="check.value" :disabled="!currentOption" />
|
||||
<input v-else v-model="check.value" type="text"
|
||||
@input="updateCheck" :disabled="!currentOption">
|
||||
<component :is="currentOption.component" v-if="currentOperator && currentComponent" v-model="check.value" :disabled="!currentOption" :check="check" @valid="valid=true && validate()" @invalid="valid=false && validate()" />
|
||||
<input v-else v-model="check.value" type="text" :class="{ invalid: !valid }"
|
||||
@input="updateCheck" :disabled="!currentOption" :placeholder="valuePlaceholder">
|
||||
<Actions>
|
||||
<ActionButton v-if="deleteVisible || !currentOption" icon="icon-delete" @click="$emit('remove')" />
|
||||
</Actions>
|
||||
|
@ -34,6 +34,10 @@ export default {
|
|||
check: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
rule: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
@ -41,7 +45,8 @@ export default {
|
|||
deleteVisible: false,
|
||||
currentOption: null,
|
||||
currentOperator: null,
|
||||
options: []
|
||||
options: [],
|
||||
valid: true,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -56,6 +61,11 @@ export default {
|
|||
if (!this.currentOption) { return [] }
|
||||
const currentComponent = this.Checks[this.currentOption.class].component
|
||||
return currentComponent
|
||||
},
|
||||
valuePlaceholder() {
|
||||
if (this.currentOption && this.currentOption.placeholder) {
|
||||
return this.currentOption.placeholder(this.check)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
@ -63,6 +73,11 @@ export default {
|
|||
this.currentOption = this.Checks[this.check.class]
|
||||
this.currentOperator = this.operators.find((operator) => operator.operator === this.check.operator)
|
||||
},
|
||||
watch: {
|
||||
'check.operator': function () {
|
||||
this.validate()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showDelete() {
|
||||
this.deleteVisible = true
|
||||
|
@ -70,12 +85,27 @@ export default {
|
|||
hideDelete() {
|
||||
this.deleteVisible = false
|
||||
},
|
||||
validate() {
|
||||
if (this.currentOption && this.currentOption.validate) {
|
||||
if(this.currentOption.validate(this.check)) {
|
||||
this.valid = true
|
||||
} else {
|
||||
this.valid = false
|
||||
}
|
||||
}
|
||||
this.$store.dispatch('setValid', { rule: this.rule, valid: this.rule.valid && this.valid })
|
||||
return this.valid
|
||||
},
|
||||
updateCheck() {
|
||||
if (this.check.class !== this.currentOption.class) {
|
||||
this.currentOperator = this.operators[0]
|
||||
}
|
||||
this.check.class = this.currentOption.class
|
||||
this.check.operator = this.currentOperator.operator
|
||||
|
||||
if (!this.validate()) {
|
||||
return
|
||||
}
|
||||
this.$emit('update', this.check)
|
||||
}
|
||||
}
|
||||
|
@ -107,4 +137,7 @@ export default {
|
|||
margin-top: -5px;
|
||||
margin-bottom: -5px;
|
||||
}
|
||||
.invalid {
|
||||
border: 1px solid var(--color-error) !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
</p>
|
||||
<p v-for="check in rule.checks">
|
||||
<span>{{ t('workflowengine', 'and') }}</span>
|
||||
<Check :check="check" @update="updateRule" @remove="removeCheck(check)" />
|
||||
<Check :check="check" :rule="rule" @update="updateRule" @remove="removeCheck(check)" />
|
||||
</p>
|
||||
<p>
|
||||
<span />
|
||||
|
@ -74,7 +74,7 @@ export default {
|
|||
return this.$store.getters.getOperationForRule(this.rule)
|
||||
},
|
||||
ruleStatus() {
|
||||
if (this.error) {
|
||||
if (this.error || !this.rule.valid) {
|
||||
return {
|
||||
title: t('workflowengine', 'The configuration is invalid'),
|
||||
class: 'icon-close-white invalid',
|
||||
|
|
|
@ -1,47 +1,104 @@
|
|||
<template>
|
||||
<input type="text" v-model="test">
|
||||
<div>
|
||||
|
||||
<multiselect
|
||||
:value="currentValue"
|
||||
placeholder="Select a file type"
|
||||
label="label"
|
||||
track-by="pattern"
|
||||
:options="options" :multiple="false" :tagging="false" @input="setValue">
|
||||
<template slot="singleLabel" slot-scope="props">
|
||||
<span class="option__icon" :class="props.option.icon"></span>
|
||||
<span class="option__title option__title_single">{{ props.option.label }}</span>
|
||||
</template>
|
||||
<template slot="option" slot-scope="props">
|
||||
<span class="option__icon" :class="props.option.icon"></span>
|
||||
<span class="option__title">{{ props.option.label }}</span>
|
||||
</template>
|
||||
</multiselect>
|
||||
<input type="text" :value="currentValue.pattern" @input="updateCustom"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Multiselect } from 'nextcloud-vue'
|
||||
|
||||
export default {
|
||||
name: 'SizeValue',
|
||||
name: 'FileMimeType',
|
||||
components: {
|
||||
Multiselect
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
test: 'test',
|
||||
value: '',
|
||||
predefinedTypes: [
|
||||
{
|
||||
icon: 'icon-picture',
|
||||
label: 'Images',
|
||||
label: t('workflowengine', 'Images'),
|
||||
pattern: '/image\\/.*/'
|
||||
},
|
||||
{
|
||||
icon: 'icon-category-office',
|
||||
label: 'Office documents',
|
||||
label: t('workflowengine', 'Office documents'),
|
||||
pattern: '/(vnd\\.(ms-|openxmlformats-).*))$/'
|
||||
},
|
||||
{
|
||||
icon: 'icon-filetype-file',
|
||||
label: 'PDF documents',
|
||||
label: t('workflowengine', 'PDF documents'),
|
||||
pattern: 'application/pdf'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
options() {
|
||||
return [...this.predefinedTypes, this.customValue]
|
||||
},
|
||||
customValue() {
|
||||
const matchingPredefined = this.predefinedTypes.find((type) => this.value.pattern === type.pattern)
|
||||
return {
|
||||
icon: 'icon-settings-dark',
|
||||
label: t('workflowengine', 'Custom pattern'),
|
||||
pattern: '',
|
||||
}
|
||||
},
|
||||
currentValue() {
|
||||
const matchingPredefined = this.predefinedTypes.find((type) => this.value === type.pattern)
|
||||
if (matchingPredefined) {
|
||||
return matchingPredefined
|
||||
}
|
||||
return {
|
||||
icon: 'icon-settings-dark',
|
||||
label: t('workflowengine', 'Custom pattern'),
|
||||
pattern: this.value,
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
validateRegex(string) {
|
||||
var regexRegex = /^\/(.*)\/([gui]{0,3})$/
|
||||
var result = regexRegex.exec(string)
|
||||
return result !== null
|
||||
},
|
||||
setValue (value) {
|
||||
// TODO: check if value requires a regex and set the check operator according to that
|
||||
if (value !== null) {
|
||||
this.value = value.pattern
|
||||
}
|
||||
},
|
||||
updateCustom (event) {
|
||||
console.log(event)
|
||||
this.value = event.target.value
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.multiselect::v-deep .multiselect__single {
|
||||
display: flex;
|
||||
}
|
||||
input, .multiselect {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -40,6 +40,36 @@ const FileChecks = Object.values(OCA.WorkflowEngine.Plugins).map((plugin) => {
|
|||
|
||||
|
||||
// new way of registering checks
|
||||
|
||||
const validateRegex = function(string) {
|
||||
var regexRegex = /^\/(.*)\/([gui]{0,3})$/
|
||||
var result = regexRegex.exec(string)
|
||||
return result !== null
|
||||
}
|
||||
|
||||
FileChecks.push({
|
||||
class: 'OCA\\WorkflowEngine\\Check\\FileName',
|
||||
name: t('workflowengine', 'File name'),
|
||||
operators: [
|
||||
{ operator: 'is', name: t('workflowengine', 'is') },
|
||||
{ operator: '!is', name: t('workflowengine', 'is not') },
|
||||
{ operator: 'matches', name: t('workflowengine', 'matches') },
|
||||
{ operator: '!matches', name: t('workflowengine', 'does not match') }
|
||||
],
|
||||
placeholder: (check) => {
|
||||
if (check.operator === 'matches' || check.operator === '!matches') {
|
||||
return '/^dummy-.+$/i'
|
||||
}
|
||||
return 'filename.txt'
|
||||
},
|
||||
validate: (check) => {
|
||||
if (check.operator === 'matches' || check.operator === '!matches') {
|
||||
return validateRegex(check.value)
|
||||
}
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
FileChecks.push({
|
||||
class: 'OCA\\WorkflowEngine\\Check\\FileMimeType',
|
||||
name: t('workflowengine', 'File MIME type'),
|
||||
|
|
|
@ -58,7 +58,7 @@ const store = new Vuex.Store({
|
|||
},
|
||||
mutations: {
|
||||
addRule(state, rule) {
|
||||
state.rules.push(rule)
|
||||
state.rules.push({ ...rule, valid: true })
|
||||
},
|
||||
updateRule(state, rule) {
|
||||
const index = state.rules.findIndex((item) => rule.id === item.id)
|
||||
|
@ -129,6 +129,10 @@ const store = new Vuex.Store({
|
|||
await confirmPassword()
|
||||
await axios.delete(getApiUrl(`/${rule.id}`))
|
||||
context.commit('removeRule', rule)
|
||||
},
|
||||
setValid (context, { rule, valid }) {
|
||||
rule.valid = valid
|
||||
context.commit('updateRule', rule)
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
|
|
|
@ -16,6 +16,8 @@ import {Operators} from './services/Operation';
|
|||
* The component should handle the v-model directive properly,
|
||||
* so it needs a value property to receive data and emit an input
|
||||
* event once the data has changed
|
||||
* @property {callable} placeholder - Return a placeholder of no custom component is used
|
||||
* @property {callable} validate - validate a check if no custom component is used
|
||||
**/
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue