Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/marius-wieschollek/passwords-webextension.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src/vue
diff options
context:
space:
mode:
Diffstat (limited to 'src/vue')
-rw-r--r--src/vue/App/Popup.vue7
-rw-r--r--src/vue/Components/Form/InputField.vue18
-rw-r--r--src/vue/Components/Form/SliderField.vue9
-rw-r--r--src/vue/Components/Icon.vue1
-rw-r--r--src/vue/Components/List/Item/Menu/PasswordMenu.vue3
-rw-r--r--src/vue/Components/List/Item/Password.vue26
-rw-r--r--src/vue/Components/Password/CustomProperty.vue244
-rw-r--r--src/vue/Components/Password/Property.vue224
-rw-r--r--src/vue/Components/Password/View.vue253
9 files changed, 774 insertions, 11 deletions
diff --git a/src/vue/App/Popup.vue b/src/vue/App/Popup.vue
index 7030c45..14bcee6 100644
--- a/src/vue/App/Popup.vue
+++ b/src/vue/App/Popup.vue
@@ -112,8 +112,11 @@
.send({type: 'popup.status.set', payload: {tab}});
},
searchEvent($event) {
- this.search.query = $event;
- this.$refs.tabs.setActive('search');
+ var pwdViews = document.getElementsByClassName("password-view")
+ if(pwdViews.length === 0) {
+ this.search.query = $event;
+ this.$refs.tabs.setActive('search');
+ }
}
}
};
diff --git a/src/vue/Components/Form/InputField.vue b/src/vue/Components/Form/InputField.vue
index 65b3e94..e902f54 100644
--- a/src/vue/Components/Form/InputField.vue
+++ b/src/vue/Components/Form/InputField.vue
@@ -1,9 +1,21 @@
<template>
- <input :type="type"
+ <textarea v-if="type === 'textarea'"
+ :value="value"
+ :placeholder="getPlaceholder"
+ :title="getTitle"
+ :readonly="readonly"
+ filled
+ auto-grow
+ v-on="listeners"
+ @input="handleInput"
+ @change="handleChange"/>
+ <input v-else
+ :type="type"
:value="value"
:checked="isChecked"
:placeholder="getPlaceholder"
:title="getTitle"
+ :readonly="readonly"
v-on="listeners"
@input="handleInput"
@change="handleChange"/>
@@ -34,6 +46,10 @@
title : {
type : String,
default: ''
+ },
+ readonly : {
+ type : Boolean,
+ default: false
}
},
diff --git a/src/vue/Components/Form/SliderField.vue b/src/vue/Components/Form/SliderField.vue
index 729c113..e1acbf4 100644
--- a/src/vue/Components/Form/SliderField.vue
+++ b/src/vue/Components/Form/SliderField.vue
@@ -6,6 +6,7 @@
ref="checkbox"
:id="id"
:name="name"
+ :readonly="readonly"
v-model="model"
v-on="listeners" />
</span>
@@ -31,6 +32,10 @@
name : {
type : String,
default: undefined
+ },
+ readonly: {
+ type : Boolean,
+ default: false
}
},
@@ -66,7 +71,9 @@
methods: {
toggleSwitch() {
- this.model = !this.model;
+ if(this.readonly === false) {
+ this.model = !this.model;
+ }
}
},
diff --git a/src/vue/Components/Icon.vue b/src/vue/Components/Icon.vue
index cdbc5b4..b8f6a2f 100644
--- a/src/vue/Components/Icon.vue
+++ b/src/vue/Components/Icon.vue
@@ -50,7 +50,6 @@
let icon = this.hover && this.hoverIcon !== null ? this.hoverIcon:this.icon,
font = this.hover && this.hoverFont !== null ? this.hoverFont:this.font,
style = font === 'solid' ? 'fas':'far';
-
if(this.spin) style += ' fa-spin';
return `icon icon-${icon} font-${font} ${style} fa-${icon}`;
diff --git a/src/vue/Components/List/Item/Menu/PasswordMenu.vue b/src/vue/Components/List/Item/Menu/PasswordMenu.vue
index 93b0ef8..f343694 100644
--- a/src/vue/Components/List/Item/Menu/PasswordMenu.vue
+++ b/src/vue/Components/List/Item/Menu/PasswordMenu.vue
@@ -4,6 +4,9 @@
<icon icon="globe-europe" font="solid" slot="before"/>
<icon class="option" icon="clipboard" slot="after" @click.stop="$emit('copy', 'url')"/>
</translate>
+ <translate tag="div" class="menu-item" say="PasswordItemViewEdit" @click="$emit('toggleEntry')">
+ <icon icon="file-alt" font="solid" slot="before"/>
+ </translate>
<translate tag="div" class="menu-item" say="PasswordItemToTrash" @click="moveToTrash">
<icon icon="trash" font="solid" slot="before"/>
</translate>
diff --git a/src/vue/Components/List/Item/Password.vue b/src/vue/Components/List/Item/Password.vue
index cffad61..c0dd21c 100644
--- a/src/vue/Components/List/Item/Password.vue
+++ b/src/vue/Components/List/Item/Password.vue
@@ -1,6 +1,6 @@
<template>
<li class="item password-item">
- <div class="item-main" :class="{'has-menu':showMenu}">
+ <div class="item-main" :class="{'has-menu':(showMenu || showEntry ? true : false)}">
<div class="label" @click="sendPassword()" :title="title">
<favicon :password="password.getId()" :size="22" v-if="favicon"/>
{{ label }}
@@ -8,11 +8,12 @@
<div class="options">
<icon icon="user" hover-icon="clipboard" @click="copy('username')" draggable="true" @dragstart="drag($event, 'username')"/>
<icon icon="key" font="solid" hover-icon="clipboard" hover-font="regular" @click="copy('password')" draggable="true" @dragstart="drag($event, 'password')"/>
- <icon icon="ellipsis-h" font="solid" @click="showMenu = !showMenu"/>
+ <icon icon="ellipsis-h" font="solid" @click="toggleMenu()"/>
</div>
<icon :class="securityClass" icon="shield-alt" font="solid"/>
</div>
- <password-menu :show="showMenu" :password="password" v-on:copy="copy($event)" v-on:delete="$emit('delete', password)"/>
+ <password-menu :show="showMenu" :password="password" v-on:copy="copy($event)" v-on:delete="$emit('delete', password)" v-on:toggleEntry="toggleEntry()"/>
+ <password-view v-if="showEntry" :password="password" v-on:toggleEntry="toggleEntry()"/>
</li>
</template>
@@ -28,9 +29,10 @@
import PasswordSettingsManager from '@js/Manager/PasswordSettingsManager';
import Translate from "@vue/Components/Translate";
import PasswordMenu from "@vue/Components/List/Item/Menu/PasswordMenu";
+ import PasswordView from '@vue/Components/Password/View';
export default {
- components: {PasswordMenu, Translate, Favicon, Icon},
+ components: {PasswordMenu, Translate, Favicon, Icon, PasswordView},
props : {
password: {
type: Password
@@ -47,8 +49,9 @@
data() {
return {
- active : true,
- showMenu: false
+ active : true,
+ showMenu : false,
+ showEntry: false
};
},
@@ -127,6 +130,17 @@
drag(event, property) {
let data = this.password.getProperty(property);
event.dataTransfer.setData('text/plain', data);
+ },
+ toggleMenu() {
+ if(this.showEntry === true) {
+ this.showEntry = false;
+ } else {
+ this.showMenu = !this.showMenu;
+ }
+ },
+ toggleEntry() {
+ this.showMenu = false;
+ this.showEntry = !this.showEntry;
}
}
};
diff --git a/src/vue/Components/Password/CustomProperty.vue b/src/vue/Components/Password/CustomProperty.vue
new file mode 100644
index 0000000..1b3cd0d
--- /dev/null
+++ b/src/vue/Components/Password/CustomProperty.vue
@@ -0,0 +1,244 @@
+<template>
+ <div :class="classList" v-if="showField">
+ <div :class="'property-label' + activeClassName">
+ <label v-if="!editable">{{label}}</label>
+ <input-field v-else :class="labelClassName" v-model="label"/>
+ <select-field :class="activeClassName" v-model="type" :options="customTypeOptions" :disabled="!editable"/>
+ </div>
+ <div class="property-value">
+ <a v-if="field.type === 'url' && !editable" :href="value">{{text}}</a>
+ <input-field v-else :class="activeClassName" @click="copy(field.name)" :type="getInputType" v-model="value" :readonly="!editable"/>
+ <div class="password-eye">
+ <icon v-if="field.type === 'secret'" @click="plainText = !plainText" :icon="passwordIcon" font="solid"/>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script>
+ import InputField from '@vue/Components/Form/InputField';
+ import ToastService from '@js/Services/ToastService';
+ import MessageService from '@js/Services/MessageService';
+ import ErrorManager from '@js/Manager/ErrorManager';
+ import SelectField from '@vue/Components/Form/SelectField';
+ import Icon from "@vue/Components/Icon";
+
+ export default {
+ components: { Icon, InputField, SelectField },
+ props : {
+ field : {
+ type : Object
+ },
+ editable : {
+ type : Boolean
+ }
+ },
+
+ data() {
+ return {
+ value : this.field.value,
+ label : this.field.label,
+ type : this.field.type,
+ plainText: false
+ };
+ },
+
+ computed: {
+ customTypeOptions() {
+ return [
+ {
+ id : 'text',
+ label: 'PasswordCustomFieldsTypeText'
+ },
+ {
+ id : 'secret',
+ label: 'PasswordCustomFieldsTypeSecret'
+ },
+ {
+ id : 'email',
+ label: 'PasswordCustomFieldsTypeEmail'
+ },
+ {
+ id : 'url',
+ label: 'PasswordCustomFieldsTypeUrl',
+ }
+ ];
+ },
+ labelClassName() {
+ return "label" + (this.editable === true ? " active": "");
+ },
+ activeClassName() {
+ return this.editable === true ? " active": "";
+ },
+ showField() {
+ if(this.field.type === "file") return false;
+ if(this.field.type === "data") return false;
+ if(this.editable || this.value !== '') {
+ return true;
+ }
+ return false;
+ },
+ label() {
+ return this.field.label;
+ },
+ classList() {
+ var result = "password-view-customproperty";
+ if(!this.editable) {
+ result += " readonly";
+ }
+ if(this.field.type !== "url" && this.editable === false) {
+ result += " allow-copy";
+ }
+ return result;
+ },
+ passwordIcon() {
+ if(this.plainText) {
+ return "eye-slash";
+ }
+ return "eye";
+ },
+ getInputType() {
+ if(this.field.type === "secret" && this.plainText !== true) {
+ return "password";
+ }
+ return "text";
+ }
+ },
+
+ methods: {
+ copy() {
+ if(this.editable) return;
+ var type = (this.field.type === "secret" ? "password":"text");
+ let data = this.field.value;
+ MessageService.send({type: 'clipboard.write', payload: {type: type, value: data}}).catch(ErrorManager.catch);
+
+ ToastService.success(['PasswordPropertyCopied', this.field.label])
+ .catch(ErrorManager.catch);
+ },
+ },
+ watch : {
+ value(value) {
+ if(value === undefined || null) return;
+ this.field.value = value;
+ this.$emit('updateField');
+ },
+ label(label) {
+ if(label === undefined || null) return;
+ this.field.label = label;
+ this.$emit('updateField');
+ },
+ type(type) {
+ if(type === undefined || null) return;
+ this.field.type = type;
+ this.$emit('updateField');
+ },
+ editable(value) {
+ if(value === false) {
+ this.plainText = false;
+ }
+ }
+ }
+
+ };
+</script>
+
+<style lang="scss">
+.password-view-customproperty {
+ padding : .5rem;
+ cursor : initial;
+
+ .property-label {
+ display : flex;
+ flex-direction : row;
+ justify-content : space-between;
+ line-height : 1rem;
+
+ &.active {
+ line-height : 2rem;
+ margin-bottom : .25rem;
+ }
+
+ .input-select {
+ padding : 0;
+ position : relative;
+ top : -.25rem;
+
+ select {
+ padding : 0 1.5rem 0 0;
+ }
+
+ &.active{
+ padding : .25rem;
+ top : 0;
+
+ select {
+ padding : .25rem 1.75rem .25rem .25rem;
+ }
+ }
+ }
+
+ label {
+ font-weight : 550;
+ }
+
+ }
+
+
+ .property-value {
+ display : flex;
+ flex-direction : row;
+ position : relative;
+ }
+
+ .password-eye {
+ width : 0;
+ cursor : pointer;
+
+ .icon {
+ position : absolute;
+ right : 1rem;
+ top : .7rem;
+ }
+ }
+
+ .input-select {
+ margin-left : .25rem;
+ }
+
+ input {
+ width : 100%;
+ padding : .25rem;
+ box-sizing : border-box;
+ border-radius : 3px;
+ border : none;
+ line-height : 2rem;
+ background-color : var(--element-hover-bg-color);
+ color : var(--element-fg-color);
+ }
+
+ input.active, .label.active, .input-select.active {
+ box-shadow : 0 0 0 1px var(--element-active-fg-color);
+
+ }
+
+ a {
+ width : 100%;
+ padding : .25rem;
+ line-height : 2rem;
+ background-color : var(--element-hover-bg-color);
+ color : var(--element-active-fg-color);
+ }
+
+ .readonly {
+ box-shadow : none;
+ border : none;
+ }
+
+ &.allow-copy {
+ input:hover, input:active {
+ cursor : pointer;
+ border : none;
+ }
+ }
+}
+</style> \ No newline at end of file
diff --git a/src/vue/Components/Password/Property.vue b/src/vue/Components/Password/Property.vue
new file mode 100644
index 0000000..5e81a82
--- /dev/null
+++ b/src/vue/Components/Password/Property.vue
@@ -0,0 +1,224 @@
+<template>
+ <div :class="classList" v-if="canEdit || value !== ''">
+ <div v-if="field.type === 'checkbox'" class="password-checkbox">
+ <label class="property-label">{{ label }}</label>
+ <slider-field v-model="value" :readonly="!canEdit" :class="activeClassName"/>
+ </div>
+ <div v-else>
+ <label class="property-label">{{ label }}</label>
+ <div class="property-value">
+ <a v-if="field.type === 'url' && !canEdit" :href="value">{{text}}</a>
+ <input-field v-else-if="field.type === 'datetime' || field.type === 'folder'" v-model="text" :readonly="true" class="readonly"/>
+ <input-field v-else v-model="value" :type="getInputType" @click="copyProperty(field.name)" @dblclick="copyNotes(field.name)" :readonly="!canEdit" :class="activeClassName"/>
+ <div class="password-eye">
+ <icon v-if="field.type === 'password'" @click="plainText = !plainText" :icon="passwordIcon" font="solid"/>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script>
+ import Password from 'passwords-client/src/Model/Password/Password';
+ import InputField from '@vue/Components/Form/InputField';
+ import SliderField from '@vue/Components/Form/SliderField';
+ import ToastService from '@js/Services/ToastService';
+ import MessageService from '@js/Services/MessageService';
+ import ErrorManager from '@js/Manager/ErrorManager';
+ import LocalisationService from '@js/Services/LocalisationService';
+ import Icon from "@vue/Components/Icon";
+
+ export default {
+ components: { Icon, InputField, SliderField },
+ props : {
+ password : {
+ type : Password
+ },
+ field : {
+ type : JSON
+ },
+ editable : {
+ type : Boolean
+ }
+ },
+
+ data() {
+ return {
+ value : this.password.getProperty(this.field.name),
+ plainText: false,
+ folder: undefined
+ };
+ },
+
+ async mounted() {
+ var response = await MessageService.send({type: 'folder.show', payload: this.value});
+ var payload = response.getPayload();
+ if(payload !== undefined && payload !== null ) {
+ this.folder = payload;
+ }
+ },
+
+ computed: {
+ canEdit() {
+ if(this.editable && this.field.editable) {
+ return true;
+ }
+ return false;
+ },
+ label() {
+ return LocalisationService.translate(`Label${this.field.name.capitalize()}`);
+ },
+ text() {
+ var type = this.field.type;
+ if(type === 'datetime') {
+ return new Date(this.value * 1000).toLocaleString();
+ }
+ if(type === 'folder') {
+ if(this.folder === undefined) {
+ return this.value;
+ }
+
+ return this.folder.getLabel();
+ }
+ return this.value;
+ },
+ classList() {
+ var result = "password-view-property";
+ if(!this.canEdit) {
+ result += " readonly";
+ }
+ if(this.field.allowCopy && this.editable === false) {
+ result += " allow-copy";
+ }
+ return result;
+ },
+ getInputType() {
+ if(this.field.type === "textarea") {
+ return "textarea";
+ }
+ if(this.field.type === "password" && this.plainText !== true) {
+ return "password";
+ }
+ return "text";
+ },
+ activeClassName() {
+ return this.editable === true ? " active": "";
+ },
+ passwordIcon() {
+ if(this.plainText) {
+ return "eye-slash";
+ }
+ return "eye";
+ }
+ },
+
+ methods: {
+ copy(property) {
+ let data = this.password.getProperty(property);
+ MessageService.send({type: 'clipboard.write', payload: {type: this.field.type, value: data}}).catch(ErrorManager.catch);
+
+ let label = LocalisationService.translate(`Label${property.capitalize()}`);
+ ToastService.success(['PasswordPropertyCopied', label])
+ .catch(ErrorManager.catch);
+ },
+ copyProperty(property) {
+ if(this.field.allowCopy == false || this.editable === true || property === 'notes') return;
+ this.copy(property);
+ },
+ copyNotes(property) {
+ if(this.editable === true || property !== 'notes') return;
+ this.copy(property);
+ }
+ },
+
+ watch : {
+ value(value) {
+ if(value === undefined || value === null) return;
+ this.$emit('updateField', this.field.name, value);
+ },
+ editable(value) {
+ if(value === false) {
+ this.plainText = false;
+ }
+ }
+ }
+
+ };
+</script>
+
+<style lang="scss">
+.password-view-property {
+ padding : .5rem;
+ cursor : initial;
+
+ padding : .5rem;
+ cursor : initial;
+
+ .property-label {
+ display : block;
+ font-weight : 550;
+ line-height : 1rem;
+ }
+
+ .property-value {
+ display : flex;
+ flex-direction : row;
+ position : relative;
+ }
+
+ .password-eye {
+ width : 0;
+ cursor : pointer;
+
+ .icon {
+ position : absolute;
+ right : 1rem;
+ top : .7rem;
+ }
+ }
+
+ input, textarea {
+ width : 100%;
+ padding : .25rem;
+ box-sizing : border-box;
+ border-radius : 3px;
+ border : none;
+ line-height : 2rem;
+ background-color : var(--element-hover-bg-color);
+ color : var(--element-fg-color);
+ scrollbar-width : thin;
+ }
+
+ input.active, textarea.active, .label.active {
+ box-shadow : 0 0 0 1px var(--element-active-fg-color);
+
+ }
+
+ a {
+ width : 100%;
+ padding : .25rem;
+ line-height : 2rem;
+ background-color : var(--element-hover-bg-color);
+ color : var(--element-active-fg-color);
+ }
+
+ .readonly {
+ box-shadow : none;
+ border : none;
+ }
+
+ &.allow-copy {
+ input:hover, input:active {
+ cursor : pointer;
+ border : none;
+ }
+ }
+
+ .password-checkbox {
+ display : flex;
+ flex-direction : row;
+ justify-content : space-between;
+ padding-right : 1rem;
+ }
+}
+</style> \ No newline at end of file
diff --git a/src/vue/Components/Password/View.vue b/src/vue/Components/Password/View.vue
new file mode 100644
index 0000000..57aafdf
--- /dev/null
+++ b/src/vue/Components/Password/View.vue
@@ -0,0 +1,253 @@
+<template>
+ <div class="item-menu password-view">
+ <div class="password-header">
+ <div class="left-space"/>
+ <div class="badge-container">
+ <icon :class="securityClass" icon="shield-alt" font="solid"/>
+ <icon class="favorite" @click="updateFavorite()" icon="star" :font="favoriteIconSolid"/>
+ <icon :icon="sharedIcon" font="solid"/>
+ </div>
+ <div class="action-container">
+ <icon icon="save" @click="save()" v-if="editable"/>
+ <icon icon="edit" @click="editable = !editable" v-if="!editable"/>
+ </div>
+ </div>
+ <div class="password-view-item">
+ <property :password="password" :editable="editable" :field="field" v-for="field in defaultFields" :key="field" v-on:updateField="updateField"/>
+ <custom-property :field="field" :editable="editable" v-for="field in customFields" :key="field" v-on:updateField="updateCustomField"/>
+ </div>
+ </div>
+</template>
+
+<script>
+ import Icon from "@vue/Components/Icon";
+ import Password from "passwords-client/src/Model/Password/Password";
+ import Property from '@vue/Components/Password/Property';
+ import CustomProperty from '@vue/Components/Password/CustomProperty';
+ import MessageService from "@js/Services/MessageService";
+
+ export default {
+ components: {Icon, Property, CustomProperty},
+ props : {
+ password: {
+ type: Password
+ }
+ },
+
+ data() {
+ return {
+ editable : false,
+ defaultFields : this.getDefaultFields(),
+ customFields : this.getCustomFields(),
+ updatedFields : {}
+ }
+ },
+
+ computed: {
+ securityClass() {
+ let types = ['secure', 'warn', 'bad'];
+
+ return `security ${types[this.password.getStatus()]}`;
+ },
+ favoriteIconSolid() {
+ if(this.password.getProperty('favorite') === true) {
+ return "solid";
+ }
+ return "reqular";
+ },
+ sharedIcon() {
+ if(this.password.getProperty('shared') === true) {
+ return "users";
+ }
+ return "user-shield";
+ },
+ showNewCustomField() {
+ if(this.allowNewCustomField()) {
+ return true;
+ }
+ return false;
+ }
+ },
+
+ methods : {
+ getDefaultFields() {
+ var fields = [];
+ for (var property in this.password.getProperties()) {
+ if(property === "password") {
+ fields.push(this.getFieldObject(property, "password", true, true));
+ }
+ if(property === "edited" || property === "created") {
+ fields.push(this.getFieldObject(property, "datetime", false, false));
+ }
+ if(property === "label") {
+ fields.push(this.getFieldObject(property, "text", true, false));
+ }
+ if(property === "folder") {
+ fields.push(this.getFieldObject(property, "folder", false, false));
+ }
+ if(property === "notes") {
+ fields.push(this.getFieldObject(property, "textarea", true, true));
+ }
+ if(property === "url") {
+ fields.push(this.getFieldObject(property, "url", true, true));
+ }
+ if(property === "username") {
+ fields.push(this.getFieldObject(property, "text", true, true));
+ }
+ if(property === "hidden") {
+ fields.push(this.getFieldObject(property, "checkbox", true, false));
+ }
+ }
+ return fields;
+ },
+ getFieldObject(property, type, editable, allowCopy) {
+ return {
+ name: property,
+ type: type,
+ editable: editable,
+ allowCopy: allowCopy,
+ }
+ },
+ getCustomFields() {
+ var result = this.password.getProperty('customFields');
+ result.push(this.getNewCustomField());
+ return result;
+ },
+ getNewCustomField() {
+ return (
+ {
+ label : "",
+ value : "",
+ type : "text"
+ }
+ )
+ },
+ allowNewCustomField() {
+ if(this.customFields === undefined
+ || this.customFields.length >= 20) return false;
+ if(this.updatedFields !== undefined
+ && this.updatedFields.customFields !== undefined
+ && this.updatedFields.customFields.length >=20) return false;
+ return true;
+ },
+ async updateFavorite() {
+ var data = {
+ id: this.password.getId(),
+ favorite: !this.password.getFavorite()
+ }
+ var result = await MessageService.send({type: 'password.update', payload: {data: data}});
+ if(result.getPayload().success === true) {
+ this.password.setProperties(result.getPayload().data);
+ }
+ },
+ save() {
+ if(Object.keys(this.updatedFields).length !== 0) {
+ this.removeEmptyCustomFields();
+ this.updatedFields.id = this.password.getId();
+ MessageService.send({type: 'password.update', payload: {data: this.updatedFields}});
+ this.updatedFields = {};
+ }
+ this.editable = false;
+ },
+ updateField(field, value) {
+ this.updatedFields[field] = value;
+ },
+ updateCustomField() {
+ this.updatedFields.customFields = this.customFields;
+ if(!this.allowNewCustomField()) return;
+ var emptyFieldAvailable = false;
+ this.updatedFields.customFields.forEach((e) => {
+ if(e.label === "" && e.value === "" && e.type !== "data" && e.type !== "file") {
+ emptyFieldAvailable = true;
+ }
+ })
+ if(!emptyFieldAvailable) {
+ this.customFields.push(this.getNewCustomField());
+ }
+ },
+ removeEmptyCustomFields(){
+ if(this.updatedFields.customFields === undefined) return;
+ this.updatedFields.customFields.forEach((e) => {
+ if(e.label === "" && e.value === "" && e.type !== "data" && e.type !== "file") {
+ var i = this.updatedFields.customFields.indexOf(e)
+ this.updatedFields.customFields.splice(i, 1);
+ }
+ })
+ }
+ }
+ };
+</script>
+
+<style lang="scss">
+.item-menu.password-view {
+ background-color : var(--element-hover-bg-color);
+ color : var(--element-hover-fg-color);
+
+ .password-header {
+ line-height : 3rem;
+ display : flex;
+ justify-content: space-between;
+ margin-bottom : -1rem;
+
+ .left-space{
+ width : 3rem
+ }
+ .icon {
+ width : 3rem;
+ display : inline-block;
+ text-align : center;
+ }
+
+ .badge-container {
+ .security {
+ &.secure {
+ color : var(--success-bg-color)
+ }
+
+ &.warn {
+ color : var(--warning-bg-color)
+ }
+
+ &.bad {
+ color : var(--error-bg-color)
+ }
+ }
+
+ .favorite {
+ cursor : pointer;
+ color : var(--warning-bg-color);
+ font-size :calc(var(--font-size) + 1rem);
+ position : relative;
+ bottom : -.25rem;
+ }
+ }
+
+ .action-container {
+ cursor : pointer;
+
+ .icon:hover {
+ background-color : var(--button-hover-bg-color);
+ color : var(--button-hover-fg-color);
+ }
+ }
+ }
+
+
+ .view-item {
+ line-height : 3rem;
+ cursor : pointer;
+ display : flex;
+
+ .icon {
+ text-align : center;
+ width : 3rem;
+ display : inline-block;
+ }
+
+ &:hover {
+ background-color : var(--element-active-hover-bg-color);
+ color : var(--element-active-hover-fg-color);
+ }
+ }
+}
+</style> \ No newline at end of file