Mercurial > gemma
view client/src/components/systemconfiguration/MorphologyClassbreaks.vue @ 4282:5d6a4dd3efa1
client: improve input validation in sys-config for Bottleneck Morphology Classbreaks
* deactivate the html built-in validation
* add input validation which meets our demands
* adjust code to send only valid values.
author | Fadi Abbud <fadi.abbud@intevation.de> |
---|---|
date | Thu, 29 Aug 2019 17:12:03 +0200 |
parents | 851c0ccba59b |
children | 69f46e9c44e2 |
line wrap: on
line source
<template> <div class="d-flex flex-column py-4"> <div class="px-3"> <h6 class="font-weight-bold"><translate>Sounding Result</translate></h6> <div class="d-flex flex-wrap"> <div class="input-group mb-3 mr-2 classbreak" v-for="(value, i) in morphologyClassbreaks" :key="i" > <div class="input-group-prepend"> <button :class="[ 'btn btn-sm btn-outline-secondary', { hasColor: morphologyClassbreaks[i][1] !== '#ffffff' } ]" :style=" 'width: 28px; background-color: ' + (morphologyClassbreaks[i][1] || 'transparent') " type="button" @click="showColorPicker('sounding-' + i)" > <font-awesome-icon icon="tint" /> </button> <div class="color-picker card shadow-sm" v-if="activeColorPicker === 'sounding-' + i" > <UIBoxHeader :title="colorPickerTitle" icon="paint-brush" :actions="[ { callback: () => { morphologyClassbreaks[i][1] = '#ffffff'; activeColorPicker = null; }, icon: 'trash' }, { callback: () => { activeColorPicker = null; }, icon: 'check' } ]" /> <chrome-picker v-model="morphologyClassbreaks[i][1]" @input="color => (morphologyClassbreaks[i][1] = color.hex)" /> </div> </div> <!-- deactivate the built-in validation --> <form id="novalidatedform" novalidate /> <input v-model.number="morphologyClassbreaks[i][0]" type="number" min="0" step="0.1" form="novalidatedform" class="form-control form-control-sm" /> <div class="input-group-append"> <button class="btn btn-sm btn-outline-secondary" type="button" @click="morphologyClassbreaks.splice(i, 1)" > <font-awesome-icon icon="times" /> </button> <button class="btn btn-sm btn-outline-secondary" @click="addClassbreak(morphologyClassbreaks, i)" > <font-awesome-icon icon="plus" /> </button> </div> </div> <button v-if="!morphologyClassbreaks.length" class="btn btn-sm btn-success mb-3" @click="addClassbreak(morphologyClassbreaks, 0)" > <font-awesome-icon icon="plus" /> </button> </div> </div> <div class="mb-4 px-3"> <a @click.prevent="submitClassbreaks" :class="[ 'btn btn-info btn-sm text-white', { disabled: !checkValidity || hasNegativValue } ]" > <translate>Send</translate> </a> <a @click.prevent="resetClassbreaks" class="btn btn-outline-info btn-sm ml-2" > <translate>Reset to defaults</translate> </a> <span class="text-danger" v-if="!checkValidity"> <translate>Same value is used in multiple fields.</translate> </span> <span class="text-danger" v-if="hasNegativValue"> <translate>Negative value is not allowed.</translate> </span> </div> <div class="px-3"> <h6 class="font-weight-bold"> <translate>Sounding Result Comparison</translate> </h6> <div class="d-flex flex-wrap"> <div class="input-group mb-3 mr-2 classbreak" v-for="(value, i) in morphologyClassbreaksCompare" :key="i" > <div class="input-group-prepend"> <button :class="[ 'btn btn-sm btn-outline-secondary', { hasColor: morphologyClassbreaksCompare[i][1] !== '#ffffff' } ]" :style=" 'width: 28px; background-color: ' + (morphologyClassbreaksCompare[i][1] || 'transparent') " type="button" @click="showColorPicker('compare-' + i)" > <font-awesome-icon icon="tint" /> </button> <div class="color-picker card shadow-sm" v-if="activeColorPicker === 'compare-' + i" > <UIBoxHeader :title="colorPickerTitle" icon="paint-brush" :actions="[ { callback: () => { morphologyClassbreaksCompare[i][1] = '#ffffff'; activeColorPicker = null; }, icon: 'trash' }, { callback: () => { activeColorPicker = null; }, icon: 'check' } ]" /> <chrome-picker v-model="morphologyClassbreaksCompare[i][1]" @input=" color => (morphologyClassbreaksCompare[i][1] = color.hex) " /> </div> </div> <input v-model.number="morphologyClassbreaksCompare[i][0]" type="number" step="0.1" form="novalidatedform" class="form-control form-control-sm" /> <div class="input-group-append"> <button class="btn btn-sm btn-outline-secondary" type="button" @click="morphologyClassbreaksCompare.splice(i, 1)" > <font-awesome-icon icon="times" /> </button> <button class="btn btn-sm btn-outline-secondary" @click="addClassbreak(morphologyClassbreaksCompare, i)" > <font-awesome-icon icon="plus" /> </button> </div> </div> <button v-if="!morphologyClassbreaksCompare.length" class="btn btn-sm btn-success mb-3" @click="addClassbreak(morphologyClassbreaksCompare, 0)" > <font-awesome-icon icon="plus" /> </button> </div> </div> <div class="px-3"> <a @click.prevent="submitClassbreaksCompare" :class="[ 'btn btn-info btn-sm text-white', { disabled: !checkValidityForMCCompare } ]" > <translate>Send</translate> </a> <a @click.prevent="resetClassbreaksCompare" class="btn btn-outline-info btn-sm ml-2" > <translate>Reset to defaults</translate> </a> <span class="text-danger" v-if="!checkValidityForMCCompare"> <translate>Same value is used in multiple fields.</translate> </span> </div> </div> </template> <style lang="sass" scoped> .classbreak width: 142px .btn-outline-secondary border-color: #ccc color: #ccc &:hover:not(.hasColor) background: #eee !important .input-group-prepend .btn-outline-secondary.hasColor color: rgba(255, 255, 255, 0.5) .input-group-append .btn-outline-secondary &:hover color: #dc3545 &:last-child:hover color: #28a745 .color-picker position: absolute top: -4px left: 19px z-index: 9 overflow: hidden border-top-left-radius: 0 !important .btn border-radius: 0 !important .vc-chrome box-shadow: none /deep/ .vc-chrome-alpha-wrap display: none !important .vc-chrome-hue-wrap margin-top: 10px .vc-chrome-saturation-wrap border-radius: 0 </style> <script> /* This is Free Software under GNU Affero General Public License v >= 3.0 * without warranty, see README.md and license for details. * * SPDX-License-Identifier: AGPL-3.0-or-later * License-Filename: LICENSES/AGPL-3.0.txt * * Copyright (C) 2018 by via donau * – Österreichische Wasserstraßen-Gesellschaft mbH * Software engineering by Intevation GmbH * * Author(s): * Markus Kottländer <markus@intevation.de> */ import { mapState } from "vuex"; import { Chrome } from "vue-color"; import defaults from "./defaults"; export default { components: { "chrome-picker": Chrome }, data() { return { morphologyClassbreaks: [], morphologyClassbreaksCompare: [], activeColorPicker: null, closeColorPickerListener: null }; }, computed: { ...mapState("application", ["config"]), colorPickerTitle() { return this.$gettext("Choose color"); }, // check if the same value is used for more than one field. checkValidity() { let values = []; for (let i = 0; i < this.morphologyClassbreaks.length; i++) { values[i] = Number(this.morphologyClassbreaks[i][0]); } return new Set(values).size === values.length; }, // check if there is a negative value hasNegativValue() { return this.morphologyClassbreaks .map(m => { return m[0]; }) .some(v => v < 0); }, checkValidityForMCCompare() { let values = []; for (let i = 0; i < this.morphologyClassbreaksCompare.length; i++) { values[i] = Number(this.morphologyClassbreaksCompare[i][0]); } return new Set(values).size === values.length; } }, methods: { addClassbreak(classbreaks, i) { classbreaks.splice( i, 0, classbreaks.length > i ? [classbreaks[i][0], classbreaks[i][1]] // create new array! : [1, "#ffffff"] ); }, showColorPicker(id) { this.activeColorPicker = this.activeColorPicker === id ? null : id; }, submitClassbreaks() { this.$store .dispatch("application/saveConfig", { morphology_classbreaks: this.morphologyClassbreaks .map(cb => (cb[1] === "#ffffff" ? cb[0] : cb.join(":"))) .join(",") }) .finally(() => this.$store.dispatch("application/loadConfig")); }, submitClassbreaksCompare() { this.$store .dispatch("application/saveConfig", { morphology_classbreaks_compare: this.morphologyClassbreaksCompare .map(cb => (cb[1] === "#ffffff" ? cb[0] : cb.join(":"))) .join(",") }) .finally(() => this.$store.dispatch("application/loadConfig")); }, resetClassbreaks() { this.morphologyClassbreaks = this.parseClassbreakString( defaults.morphology_classbreaks ); }, resetClassbreaksCompare() { this.morphologyClassbreaksCompare = this.parseClassbreakString( defaults.morphology_classbreaks_compare ); }, parseClassbreakString(str) { return str .split(",") .map(cb => cb.split(":")) .map(cb => { cb[0] = Number(cb[0]); cb[1] = cb[1] || "#ffffff"; return cb; }); } }, mounted() { this.morphologyClassbreaks = this.parseClassbreakString( this.config.morphology_classbreaks ); this.morphologyClassbreaksCompare = this.parseClassbreakString( this.config.morphology_classbreaks_compare ); this.closeColorPickerListener = e => { // Escape if (e.keyCode === 27) { this.activeColorPicker = null; } }; window.addEventListener("keydown", this.closeColorPickerListener); }, destroyed() { window.removeEventListener("keydown", this.closeColorPickerListener); } }; </script>