You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Check.vue 4.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. <template>
  2. <div v-click-outside="hideDelete" class="check" @click="showDelete">
  3. <Multiselect ref="checkSelector"
  4. v-model="currentOption"
  5. :options="options"
  6. label="name"
  7. track-by="class"
  8. :allow-empty="false"
  9. :placeholder="t('workflowengine', 'Select a filter')"
  10. @input="updateCheck" />
  11. <Multiselect v-model="currentOperator"
  12. :disabled="!currentOption"
  13. :options="operators"
  14. class="comparator"
  15. label="name"
  16. track-by="operator"
  17. :allow-empty="false"
  18. :placeholder="t('workflowengine', 'Select a comparator')"
  19. @input="updateCheck" />
  20. <component :is="currentOption.component"
  21. v-if="currentOperator && currentComponent"
  22. v-model="check.value"
  23. :disabled="!currentOption"
  24. :check="check"
  25. class="option"
  26. @input="updateCheck"
  27. @valid="(valid=true) && validate()"
  28. @invalid="!(valid=false) && validate()" />
  29. <input v-else
  30. v-model="check.value"
  31. type="text"
  32. :class="{ invalid: !valid }"
  33. :disabled="!currentOption"
  34. :placeholder="valuePlaceholder"
  35. class="option"
  36. @input="updateCheck">
  37. <Actions v-if="deleteVisible || !currentOption">
  38. <ActionButton icon="icon-close" @click="$emit('remove')" />
  39. </Actions>
  40. </div>
  41. </template>
  42. <script>
  43. import Multiselect from '@nextcloud/vue/dist/Components/Multiselect'
  44. import Actions from '@nextcloud/vue/dist/Components/Actions'
  45. import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
  46. import ClickOutside from 'vue-click-outside'
  47. export default {
  48. name: 'Check',
  49. components: {
  50. ActionButton,
  51. Actions,
  52. Multiselect,
  53. },
  54. directives: {
  55. ClickOutside,
  56. },
  57. props: {
  58. check: {
  59. type: Object,
  60. required: true,
  61. },
  62. rule: {
  63. type: Object,
  64. required: true,
  65. },
  66. },
  67. data() {
  68. return {
  69. deleteVisible: false,
  70. currentOption: null,
  71. currentOperator: null,
  72. options: [],
  73. valid: false,
  74. }
  75. },
  76. computed: {
  77. checks() {
  78. return this.$store.getters.getChecksForEntity(this.rule.entity)
  79. },
  80. operators() {
  81. if (!this.currentOption) { return [] }
  82. const operators = this.checks[this.currentOption.class].operators
  83. if (typeof operators === 'function') {
  84. return operators(this.check)
  85. }
  86. return operators
  87. },
  88. currentComponent() {
  89. if (!this.currentOption) { return [] }
  90. return this.checks[this.currentOption.class].component
  91. },
  92. valuePlaceholder() {
  93. if (this.currentOption && this.currentOption.placeholder) {
  94. return this.currentOption.placeholder(this.check)
  95. }
  96. return ''
  97. },
  98. },
  99. watch: {
  100. 'check.operator'() {
  101. this.validate()
  102. },
  103. },
  104. mounted() {
  105. this.options = Object.values(this.checks)
  106. this.currentOption = this.checks[this.check.class]
  107. this.currentOperator = this.operators.find((operator) => operator.operator === this.check.operator)
  108. if (this.check.class === null) {
  109. this.$nextTick(() => this.$refs.checkSelector.$el.focus())
  110. }
  111. this.validate()
  112. },
  113. methods: {
  114. showDelete() {
  115. this.deleteVisible = true
  116. },
  117. hideDelete() {
  118. this.deleteVisible = false
  119. },
  120. validate() {
  121. this.valid = true
  122. if (this.currentOption && this.currentOption.validate) {
  123. this.valid = !!this.currentOption.validate(this.check)
  124. }
  125. // eslint-disable-next-line vue/no-mutating-props
  126. this.check.invalid = !this.valid
  127. this.$emit('validate', this.valid)
  128. },
  129. updateCheck() {
  130. const matchingOperator = this.operators.findIndex((operator) => this.check.operator === operator.operator)
  131. if (this.check.class !== this.currentOption.class || matchingOperator === -1) {
  132. this.currentOperator = this.operators[0]
  133. }
  134. // eslint-disable-next-line vue/no-mutating-props
  135. this.check.class = this.currentOption.class
  136. // eslint-disable-next-line vue/no-mutating-props
  137. this.check.operator = this.currentOperator.operator
  138. this.validate()
  139. this.$emit('update', this.check)
  140. },
  141. },
  142. }
  143. </script>
  144. <style scoped lang="scss">
  145. .check {
  146. display: flex;
  147. flex-wrap: wrap;
  148. width: 100%;
  149. padding-right: 20px;
  150. & > *:not(.close) {
  151. width: 180px;
  152. }
  153. & > .comparator {
  154. min-width: 130px;
  155. width: 130px;
  156. }
  157. & > .option {
  158. min-width: 230px;
  159. width: 230px;
  160. }
  161. & > .multiselect,
  162. & > input[type=text] {
  163. margin-right: 5px;
  164. margin-bottom: 5px;
  165. }
  166. .multiselect::v-deep .multiselect__content-wrapper li>span,
  167. .multiselect::v-deep .multiselect__single {
  168. display: block;
  169. white-space: nowrap;
  170. overflow: hidden;
  171. text-overflow: ellipsis;
  172. }
  173. }
  174. input[type=text] {
  175. margin: 0;
  176. }
  177. ::placeholder {
  178. font-size: 10px;
  179. }
  180. button.action-item.action-item--single.icon-close {
  181. height: 44px;
  182. width: 44px;
  183. margin-top: -5px;
  184. margin-bottom: -5px;
  185. }
  186. .invalid {
  187. border: 1px solid var(--color-error) !important;
  188. }
  189. </style>