change tagged-input-field component, allow to paste multiple values (#365)

This commit is contained in:
Christoph 2025-04-19 17:43:51 +02:00
parent 6681dfa96f
commit 1d94f6baaf
7 changed files with 304 additions and 117 deletions

View File

@ -12,6 +12,7 @@
"@fortawesome/fontawesome-free": "^6.7.2",
"@kyvg/vue3-notification": "^3.4.1",
"@popperjs/core": "^2.11.8",
"@vojtechlanka/vue-tags-input": "^3.1.1",
"bootstrap": "^5.3.5",
"bootswatch": "^5.3.5",
"flag-icons": "^7.3.2",
@ -23,8 +24,7 @@
"vue": "^3.5.13",
"vue-i18n": "^11.1.3",
"vue-prism-component": "github:h44z/vue-prism-component",
"vue-router": "^4.5.0",
"vue3-tags-input": "^1.0.12"
"vue-router": "^4.5.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.3",
@ -884,6 +884,20 @@
"vue": "^3.2.25"
}
},
"node_modules/@vojtechlanka/vue-tags-input": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@vojtechlanka/vue-tags-input/-/vue-tags-input-3.1.1.tgz",
"integrity": "sha512-GdREECH+k2pQCKdbHHh4/IxRXje3QQ8rXzXd9/6L1kzGYXqHlG1tbRoi1qC7enph67/g2nvGaZfpqLuuW+CX3g==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"vue": "3.x",
"vuedraggable": "^4.1.0"
},
"peerDependencies": {
"vue": "3.x"
}
},
"node_modules/@vue/compiler-core": {
"version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz",
@ -1070,15 +1084,6 @@
"node": ">=14"
}
},
"node_modules/click-outside-vue3": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/click-outside-vue3/-/click-outside-vue3-4.0.1.tgz",
"integrity": "sha512-sbplNecrup5oGqA3o4bo8XmvHRT6q9fvw21Z67aDbTqB9M6LF7CuYLTlLvNtOgKU6W3zst5H5zJuEh4auqA34g==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/clone-regexp": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-3.0.0.tgz",
@ -1193,6 +1198,12 @@
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"license": "MIT"
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT"
},
"node_modules/fdir": {
"version": "6.4.4",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
@ -1893,6 +1904,12 @@
"node": ">=14.0.0"
}
},
"node_modules/sortablejs": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz",
"integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==",
"license": "MIT"
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@ -2173,19 +2190,16 @@
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
"license": "MIT"
},
"node_modules/vue3-tags-input": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/vue3-tags-input/-/vue3-tags-input-1.0.12.tgz",
"integrity": "sha512-s5rG+1W3M8+be0nd9H1nv/8WLjJOO6pShgVz8ALAqOiz3tDH5QhGrDH6fzD14ZjJNRWSa3bRBSXQwHEXffPQ6g==",
"node_modules/vuedraggable": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-4.1.0.tgz",
"integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==",
"license": "MIT",
"dependencies": {
"click-outside-vue3": "^4.0.1"
},
"engines": {
"node": ">=12"
"sortablejs": "1.14.0"
},
"peerDependencies": {
"vue": "^3.0.5"
"vue": "^3.0.1"
}
}
}

View File

@ -12,6 +12,7 @@
"@fortawesome/fontawesome-free": "^6.7.2",
"@kyvg/vue3-notification": "^3.4.1",
"@popperjs/core": "^2.11.8",
"@vojtechlanka/vue-tags-input": "^3.1.1",
"bootstrap": "^5.3.5",
"bootswatch": "^5.3.5",
"flag-icons": "^7.3.2",
@ -23,8 +24,7 @@
"vue": "^3.5.13",
"vue-i18n": "^11.1.3",
"vue-prism-component": "github:h44z/vue-prism-component",
"vue-router": "^4.5.0",
"vue3-tags-input": "^1.0.12"
"vue-router": "^4.5.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.3",

View File

@ -15,3 +15,85 @@ a.disabled {
.desc::after {
content: " ↓";
}
/* style the background and the text color of the input ... */
.vue-tags-input {
max-width: 100% !important;
background-color: #f7f7f9 !important;
padding: 0 0;
}
.vue-tags-input .ti-input {
padding: 0 0;
border: none !important;
transition: border-bottom 200ms ease;
}
.vue-tags-input .ti-new-tag-input {
background: transparent;
color: var(--bs-body-color);
padding: 0.75rem 1.5rem !important;
}
/* style the placeholders color across all browser */
.vue-tags-input ::-webkit-input-placeholder {
color: var(--bs-secondary-color);
}
.vue-tags-input .ti-input::placeholder {
color: var(--bs-secondary-color);
}
.vue-tags-input ::-moz-placeholder {
color: var(--bs-secondary-color);
}
.vue-tags-input :-ms-input-placeholder {
color: var(--bs-secondary-color);
}
.vue-tags-input :-moz-placeholder {
color: var(--bs-secondary-color);
}
/* default styles for all the tags */
.vue-tags-input .ti-tag {
position: relative;
background: #ffffff;
border: 2px solid var(--bs-body-color);
margin: 6px;
color: var(--bs-body-color);
}
/* the styles if a tag is invalid */
.vue-tags-input .ti-tag.ti-invalid {
background-color: #e88a74;
}
/* if the user input is invalid, the input color should be red */
.vue-tags-input .ti-new-tag-input.ti-invalid {
color: #e88a74;
}
/* if a tag or the user input is a duplicate, it should be crossed out */
.vue-tags-input .ti-duplicate span,
.vue-tags-input .ti-new-tag-input.ti-duplicate {
text-decoration: line-through;
}
/* if the user presses backspace, the complete tag should be crossed out, to mark it for deletion */
.vue-tags-input .ti-tag:after {
transition: transform .2s;
position: absolute;
content: '';
height: 2px;
width: 108%;
left: -4%;
top: calc(50% - 1px);
background-color: #000;
transform: scaleX(0);
}
.vue-tags-input .ti-deletion-mark:after {
transform: scaleX(1);
}

View File

@ -4,7 +4,7 @@ import {interfaceStore} from "@/stores/interfaces";
import {computed, ref, watch} from "vue";
import { useI18n } from 'vue-i18n';
import { notify } from "@kyvg/vue3-notification";
import Vue3TagsInput from 'vue3-tags-input';
import { VueTagsInput } from '@vojtechlanka/vue-tags-input';
import { validateCIDR, validateIP, validateDomain } from '@/helpers/validators';
import isCidr from "is-cidr";
import {isIP} from 'is-ip';
@ -38,6 +38,15 @@ const title = computed(() => {
return t("modals.interface-edit.headline-new")
})
const currentTags = ref({
Addresses: "",
Dns: "",
DnsSearch: "",
PeerDefNetwork: "",
PeerDefAllowedIPs: "",
PeerDefDns: "",
PeerDefDnsSearch: ""
})
const formData = ref(freshInterface())
// functions
@ -137,94 +146,94 @@ function close() {
function handleChangeAddresses(tags) {
let validInput = true
tags.forEach(tag => {
if(isCidr(tag) === 0) {
if(isCidr(tag.text) === 0) {
validInput = false
notify({
title: "Invalid CIDR",
text: tag + " is not a valid IP address",
text: tag.text + " is not a valid IP address",
type: 'error',
})
}
})
if(validInput) {
formData.value.Addresses = tags
formData.value.Addresses = tags.map(tag => tag.text)
}
}
function handleChangeDns(tags) {
let validInput = true
tags.forEach(tag => {
if(!isIP(tag)) {
if(!isIP(tag.text)) {
validInput = false
notify({
title: "Invalid IP",
text: tag + " is not a valid IP address",
text: tag.text + " is not a valid IP address",
type: 'error',
})
}
})
if(validInput) {
formData.value.Dns = tags
formData.value.Dns = tags.map(tag => tag.text)
}
}
function handleChangeDnsSearch(tags) {
formData.value.DnsSearch = tags
formData.value.DnsSearch = tags.map(tag => tag.text)
}
function handleChangePeerDefNetwork(tags) {
let validInput = true
tags.forEach(tag => {
if(isCidr(tag) === 0) {
if(isCidr(tag.text) === 0) {
validInput = false
notify({
title: "Invalid CIDR",
text: tag + " is not a valid IP address",
text: tag.text + " is not a valid IP address",
type: 'error',
})
}
})
if(validInput) {
formData.value.PeerDefNetwork = tags
formData.value.PeerDefNetwork = tags.map(tag => tag.text)
}
}
function handleChangePeerDefAllowedIPs(tags) {
let validInput = true
tags.forEach(tag => {
if(isCidr(tag) === 0) {
if(isCidr(tag.text) === 0) {
validInput = false
notify({
title: "Invalid CIDR",
text: tag + " is not a valid IP address",
text: tag.text + " is not a valid IP address",
type: 'error',
})
}
})
if(validInput) {
formData.value.PeerDefAllowedIPs = tags
formData.value.PeerDefAllowedIPs = tags.map(tag => tag.text)
}
}
function handleChangePeerDefDns(tags) {
let validInput = true
tags.forEach(tag => {
if(!isIP(tag)) {
if(!isIP(tag.text)) {
validInput = false
notify({
title: "Invalid IP",
text: tag + " is not a valid IP address",
text: tag.text + " is not a valid IP address",
type: 'error',
})
}
})
if(validInput) {
formData.value.PeerDefDns = tags
formData.value.PeerDefDns = tags.map(tag => tag.text)
}
}
function handleChangePeerDefDnsSearch(tags) {
formData.value.PeerDefDnsSearch = tags
formData.value.PeerDefDnsSearch = tags.map(tag => tag.text)
}
async function save() {
@ -333,11 +342,15 @@ async function del() {
<legend class="mt-4">{{ $t('modals.interface-edit.header-network') }}</legend>
<div class="form-group">
<label class="form-label mt-4">{{ $t('modals.interface-edit.ip.label') }}</label>
<vue3-tags-input class="form-control" :tags="formData.Addresses"
:placeholder="$t('modals.interface-edit.ip.placeholder')"
:add-tag-on-keys="[13, 188, 32, 9]"
:validate="validateCIDR"
@on-tags-changed="handleChangeAddresses"/>
<vue-tags-input class="form-control" v-model="currentTags.Addresses"
:tags="formData.Addresses.map(str => ({ text: str }))"
:placeholder="$t('modals.interface-edit.ip.placeholder')"
:validation="validateCIDR()"
:add-on-key="[13, 188, 32, 9]"
:save-on-key="[13, 188, 32, 9]"
:allow-edit-tags="true"
:separators="[',', ';', ' ']"
@tags-changed="handleChangeAddresses"/>
</div>
<div v-if="formData.Mode==='server'" class="form-group">
<label class="form-label mt-4">{{ $t('modals.interface-edit.listen-port.label') }}</label>
@ -345,19 +358,27 @@ async function del() {
</div>
<div v-if="formData.Mode!=='server'" class="form-group">
<label class="form-label mt-4">{{ $t('modals.interface-edit.dns.label') }}</label>
<vue3-tags-input class="form-control" :tags="formData.Dns"
:placeholder="$t('modals.interface-edit.dns.placeholder')"
:add-tag-on-keys="[13, 188, 32, 9]"
:validate="validateIP"
@on-tags-changed="handleChangeDns"/>
<vue-tags-input class="form-control" v-model="currentTags.Dns"
:tags="formData.Dns.map(str => ({ text: str }))"
:placeholder="$t('modals.interface-edit.dns.placeholder')"
:validation="validateIP()"
:add-on-key="[13, 188, 32, 9]"
:save-on-key="[13, 188, 32, 9]"
:allow-edit-tags="true"
:separators="[',', ';', ' ']"
@tags-changed="handleChangeDns"/>
</div>
<div v-if="formData.Mode!=='server'" class="form-group">
<label class="form-label mt-4">{{ $t('modals.interface-edit.dns-search.label') }}</label>
<vue3-tags-input class="form-control" :tags="formData.DnsSearch"
:placeholder="$t('modals.interface-edit.dns-search.placeholder')"
:add-tag-on-keys="[13, 188, 32, 9]"
:validate="validateDomain"
@on-tags-changed="handleChangeDnsSearch"/>
<vue-tags-input class="form-control" v-model="currentTags.DnsSearch"
:tags="formData.DnsSearch.map(str => ({ text: str }))"
:placeholder="$t('modals.interface-edit.dns-search.placeholder')"
:validation="validateDomain()"
:add-on-key="[13, 188, 32, 9]"
:save-on-key="[13, 188, 32, 9]"
:allow-edit-tags="true"
:separators="[',', ';', ' ']"
@tags-changed="handleChangeDnsSearch"/>
</div>
<div class="row">
<div class="form-group col-md-6">
@ -420,36 +441,52 @@ async function del() {
</div>
<div class="form-group">
<label class="form-label mt-4">{{ $t('modals.interface-edit.defaults.networks.label') }}</label>
<vue3-tags-input class="form-control" :tags="formData.PeerDefNetwork"
:placeholder="$t('modals.interface-edit.defaults.networks.placeholder')"
:add-tag-on-keys="[13, 188, 32, 9]"
:validate="validateCIDR"
@on-tags-changed="handleChangePeerDefNetwork"/>
<vue-tags-input class="form-control" v-model="currentTags.PeerDefNetwork"
:tags="formData.PeerDefNetwork.map(str => ({ text: str }))"
:placeholder="$t('modals.interface-edit.defaults.networks.placeholder')"
:validation="validateCIDR()"
:add-on-key="[13, 188, 32, 9]"
:save-on-key="[13, 188, 32, 9]"
:allow-edit-tags="true"
:separators="[',', ';', ' ']"
@tags-changed="handleChangePeerDefNetwork"/>
<small class="form-text text-muted">{{ $t('modals.interface-edit.defaults.networks.description') }}</small>
</div>
<div class="form-group">
<label class="form-label mt-4">{{ $t('modals.interface-edit.defaults.allowed-ip.label') }}</label>
<vue3-tags-input class="form-control" :tags="formData.PeerDefAllowedIPs"
:placeholder="$t('modals.interface-edit.defaults.allowed-ip.placeholder')"
:add-tag-on-keys="[13, 188, 32, 9]"
:validate="validateCIDR"
@on-tags-changed="handleChangePeerDefAllowedIPs"/>
<vue-tags-input class="form-control" v-model="currentTags.PeerDefAllowedIPs"
:tags="formData.PeerDefAllowedIPs.map(str => ({ text: str }))"
:placeholder="$t('modals.interface-edit.defaults.allowed-ip.placeholder')"
:validation="validateCIDR()"
:add-on-key="[13, 188, 32, 9]"
:save-on-key="[13, 188, 32, 9]"
:allow-edit-tags="true"
:separators="[',', ';', ' ']"
@tags-changed="handleChangePeerDefAllowedIPs"/>
</div>
<div class="form-group">
<label class="form-label mt-4">{{ $t('modals.interface-edit.dns.label') }}</label>
<vue3-tags-input class="form-control" :tags="formData.PeerDefDns"
:placeholder="$t('modals.interface-edit.dns.placeholder')"
:add-tag-on-keys="[13, 188, 32, 9]"
:validate="validateIP"
@on-tags-changed="handleChangePeerDefDns"/>
<vue-tags-input class="form-control" v-model="currentTags.PeerDefDns"
:tags="formData.PeerDefDns.map(str => ({ text: str }))"
:placeholder="$t('modals.interface-edit.dns.placeholder')"
:validation="validateIP()"
:add-on-key="[13, 188, 32, 9]"
:save-on-key="[13, 188, 32, 9]"
:allow-edit-tags="true"
:separators="[',', ';', ' ']"
@tags-changed="handleChangePeerDefDns"/>
</div>
<div class="form-group">
<label class="form-label mt-4">{{ $t('modals.interface-edit.dns-search.label') }}</label>
<vue3-tags-input class="form-control" :tags="formData.PeerDefDnsSearch"
:placeholder="$t('modals.interface-edit.dns-search.placeholder')"
:add-tag-on-keys="[13, 188, 32, 9]"
:validate="validateDomain"
@on-tags-changed="handleChangePeerDefDnsSearch"/>
<vue-tags-input class="form-control" v-model="currentTags.PeerDefDnsSearch"
:tags="formData.PeerDefDnsSearch.map(str => ({ text: str }))"
:placeholder="$t('modals.interface-edit.dns-search.placeholder')"
:validation="validateDomain()"
:add-on-key="[13, 188, 32, 9]"
:save-on-key="[13, 188, 32, 9]"
:allow-edit-tags="true"
:separators="[',', ';', ' ']"
@tags-changed="handleChangePeerDefDnsSearch"/>
</div>
<div class="row">
<div class="form-group col-md-6">

View File

@ -5,7 +5,7 @@ import { interfaceStore } from "@/stores/interfaces";
import { computed, ref, watch } from "vue";
import { useI18n } from 'vue-i18n';
import { notify } from "@kyvg/vue3-notification";
import Vue3TagsInput from "vue3-tags-input";
import { VueTagsInput } from '@vojtechlanka/vue-tags-input';
import { validateCIDR, validateIP, validateDomain } from '@/helpers/validators';
import isCidr from "is-cidr";
import { isIP } from 'is-ip';
@ -65,6 +65,13 @@ const title = computed(() => {
}
})
const currentTags = ref({
Addresses: "",
AllowedIPs: "",
ExtraAllowedIPs: "",
Dns: "",
DnsSearch: ""
})
const formData = ref(freshPeer())
// functions
@ -193,73 +200,73 @@ function close() {
function handleChangeAddresses(tags) {
let validInput = true
tags.forEach(tag => {
if (isCidr(tag) === 0) {
if (isCidr(tag.text) === 0) {
validInput = false
notify({
title: "Invalid CIDR",
text: tag + " is not a valid IP address",
text: tag.text + " is not a valid IP address",
type: 'error',
})
}
})
if (validInput) {
formData.value.Addresses = tags
formData.value.Addresses = tags.map(tag => tag.text)
}
}
function handleChangeAllowedIPs(tags) {
let validInput = true
tags.forEach(tag => {
if (isCidr(tag) === 0) {
if (isCidr(tag.text) === 0) {
validInput = false
notify({
title: "Invalid CIDR",
text: tag + " is not a valid IP address",
text: tag.text + " is not a valid IP address",
type: 'error',
})
}
})
if (validInput) {
formData.value.AllowedIPs.Value = tags
formData.value.AllowedIPs.Value = tags.map(tag => tag.text)
}
}
function handleChangeExtraAllowedIPs(tags) {
let validInput = true
tags.forEach(tag => {
if (isCidr(tag) === 0) {
if (isCidr(tag.text) === 0) {
validInput = false
notify({
title: "Invalid CIDR",
text: tag + " is not a valid IP address",
text: tag.text + " is not a valid IP address",
type: 'error',
})
}
})
if (validInput) {
formData.value.ExtraAllowedIPs = tags
formData.value.ExtraAllowedIPs = tags.map(tag => tag.text)
}
}
function handleChangeDns(tags) {
let validInput = true
tags.forEach(tag => {
if (!isIP(tag)) {
if (!isIP(tag.text)) {
validInput = false
notify({
title: "Invalid IP",
text: tag + " is not a valid IP address",
text: tag.text + " is not a valid IP address",
type: 'error',
})
}
})
if (validInput) {
formData.value.Dns.Value = tags
formData.value.Dns.Value = tags.map(tag => tag.text)
}
}
function handleChangeDnsSearch(tags) {
formData.value.DnsSearch.Value = tags
formData.value.DnsSearch.Value = tags.map(tag => tag.text)
}
async function save() {
@ -344,34 +351,64 @@ async function del() {
</div>
<div class="form-group">
<label class="form-label mt-4">{{ $t('modals.peer-edit.ip.label') }}</label>
<vue3-tags-input class="form-control" :tags="formData.Addresses"
:placeholder="$t('modals.peer-edit.ip.placeholder')" :add-tag-on-keys="[13, 188, 32, 9]"
:validate="validateCIDR" @on-tags-changed="handleChangeAddresses" />
<vue-tags-input class="form-control" v-model="currentTags.Addresses"
:tags="formData.Addresses.map(str => ({ text: str }))"
:placeholder="$t('modals.peer-edit.ip.placeholder')"
:validation="validateCIDR()"
:add-on-key="[13, 188, 32, 9]"
:save-on-key="[13, 188, 32, 9]"
:allow-edit-tags="true"
:separators="[',', ';', ' ']"
@tags-changed="handleChangeAddresses" />
</div>
<div class="form-group">
<label class="form-label mt-4">{{ $t('modals.peer-edit.allowed-ip.label') }}</label>
<vue3-tags-input class="form-control" :tags="formData.AllowedIPs.Value"
:placeholder="$t('modals.peer-edit.allowed-ip.placeholder')" :add-tag-on-keys="[13, 188, 32, 9]"
:validate="validateCIDR" @on-tags-changed="handleChangeAllowedIPs" />
<vue-tags-input class="form-control" v-model="currentTags.AllowedIPs"
:tags="formData.AllowedIPs.Value.map(str => ({ text: str }))"
:placeholder="$t('modals.peer-edit.allowed-ip.placeholder')"
:validation="validateCIDR()"
:add-on-key="[13, 188, 32, 9]"
:save-on-key="[13, 188, 32, 9]"
:allow-edit-tags="true"
:separators="[',', ';', ' ']"
@tags-changed="handleChangeAllowedIPs" />
</div>
<div class="form-group">
<label class="form-label mt-4">{{ $t('modals.peer-edit.extra-allowed-ip.label') }}</label>
<vue3-tags-input class="form-control" :tags="formData.ExtraAllowedIPs"
:placeholder="$t('modals.peer-edit.extra-allowed-ip.placeholder')" :add-tag-on-keys="[13, 188, 32, 9]"
:validate="validateCIDR" @on-tags-changed="handleChangeExtraAllowedIPs" />
<vue-tags-input class="form-control" v-model="currentTags.ExtraAllowedIPs"
:tags="formData.ExtraAllowedIPs.map(str => ({ text: str }))"
:placeholder="$t('modals.peer-edit.extra-allowed-ip.placeholder')"
:validation="validateCIDR()"
:add-on-key="[13, 188, 32, 9]"
:save-on-key="[13, 188, 32, 9]"
:allow-edit-tags="true"
:separators="[',', ';', ' ']"
@tags-changed="handleChangeExtraAllowedIPs" />
<small class="form-text text-muted">{{ $t('modals.peer-edit.extra-allowed-ip.description') }}</small>
</div>
<div class="form-group">
<label class="form-label mt-4">{{ $t('modals.peer-edit.dns.label') }}</label>
<vue3-tags-input class="form-control" :tags="formData.Dns.Value"
:placeholder="$t('modals.peer-edit.dns.placeholder')" :add-tag-on-keys="[13, 188, 32, 9]"
:validate="validateIP" @on-tags-changed="handleChangeDns" />
<vue-tags-input class="form-control" v-model="currentTags.Dns"
:tags="formData.Dns.Value.map(str => ({ text: str }))"
:placeholder="$t('modals.peer-edit.dns.placeholder')"
:validation="validateIP()"
:add-on-key="[13, 188, 32, 9]"
:save-on-key="[13, 188, 32, 9]"
:allow-edit-tags="true"
:separators="[',', ';', ' ']"
@tags-changed="handleChangeDns" />
</div>
<div hidden class="form-group">
<div class="form-group">
<label class="form-label mt-4">{{ $t('modals.peer-edit.dns-search.label') }}</label>
<vue3-tags-input class="form-control" :tags="formData.DnsSearch.Value"
:placeholder="$t('modals.peer-edit.dns-search.label')" :add-tag-on-keys="[13, 188, 32, 9]"
:validate="validateDomain" @on-tags-changed="handleChangeDnsSearch" />
<vue-tags-input class="form-control" v-model="currentTags.DnsSearch"
:tags="formData.DnsSearch.Value.map(str => ({ text: str }))"
:placeholder="$t('modals.peer-edit.dns-search.label')"
:validation="validateDomain()"
:add-on-key="[13, 188, 32, 9]"
:save-on-key="[13, 188, 32, 9]"
:allow-edit-tags="true"
:separators="[',', ';', ' ']"
@tags-changed="handleChangeDnsSearch" />
</div>
<div class="row">
<div class="form-group col-md-6">

View File

@ -5,7 +5,7 @@ import {interfaceStore} from "@/stores/interfaces";
import {computed, ref} from "vue";
import { useI18n } from 'vue-i18n';
import { notify } from "@kyvg/vue3-notification";
import Vue3TagsInput from "vue3-tags-input";
import { VueTagsInput } from '@vojtechlanka/vue-tags-input';
import { freshInterface } from '@/helpers/models';
const { t } = useI18n()
@ -36,6 +36,7 @@ function freshForm() {
}
}
const currentTag = ref("")
const formData = ref(freshForm())
const title = computed(() => {
@ -55,7 +56,7 @@ function close() {
}
function handleChangeUserIdentifiers(tags) {
formData.value.Identifiers = tags
formData.value.Identifiers = tags.map(tag => tag.text)
}
async function save() {
@ -89,10 +90,14 @@ async function save() {
<fieldset>
<div class="form-group">
<label class="form-label mt-4">{{ $t('modals.peer-multi-create.identifiers.label') }}</label>
<vue3-tags-input class="form-control" :tags="formData.Identifiers"
:placeholder="$t('modals.peer-multi-create.identifiers.placeholder')"
:add-tag-on-keys="[13, 188, 32, 9]"
@on-tags-changed="handleChangeUserIdentifiers"/>
<vue-tags-input class="form-control" v-model="currentTag"
:tags="formData.Identifiers.map(str => ({ text: str }))"
:placeholder="$t('modals.peer-multi-create.identifiers.placeholder')"
:add-on-key="[13, 188, 32, 9]"
:save-on-key="[13, 188, 32, 9]"
:allow-edit-tags="true"
:separators="[',', ';', ' ']"
@tags-changed="handleChangeUserIdentifiers"/>
<small class="form-text text-muted">{{ $t('modals.peer-multi-create.identifiers.description') }}</small>
</div>
<div class="form-group">

View File

@ -1,14 +1,26 @@
import isCidr from "is-cidr";
import {isIP} from 'is-ip';
export function validateCIDR(value) {
return isCidr(value) !== 0
export function validateCIDR() {
return [{
classes: 'invalid-cidr',
rule: ({ text }) => isCidr(text) === 0,
disableAdd: true,
}]
}
export function validateIP(value) {
return isIP(value)
export function validateIP() {
return [{
classes: 'invalid-ip',
rule: ({ text }) => !isIP(text),
disableAdd: true,
}]
}
export function validateDomain(value) {
return true
export function validateDomain() {
return [{
classes: 'invalid-domain',
rule: tag => tag.text.length < 3,
disableAdd: true,
}]
}