Updated seasonedinput to also handle two-way binded value prop. This changes is reflected most all places that seaoned-input is used
. Fuck, also added the new ResultsList which replaces MoviesList
This commit is contained in:
@@ -1,386 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class='movies-list' v-if="!error">
|
||||
<header class='list-header'>
|
||||
<h2 class='header__title'>{{ listTitle }}</h2>
|
||||
|
||||
<router-link class='header__view-more'
|
||||
:to="'/list/' + list.route"
|
||||
v-if='shortList'>
|
||||
View All</router-link>
|
||||
|
||||
<div v-else style="line-height: 0;">
|
||||
<span class='header__result-count' v-if="totalResults">{{ resultCount }} results</span>
|
||||
<loading-placeholder v-else :count="1" lineClass='short nomargin'></loading-placeholder>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- <ul class="filter">
|
||||
<li class="filter-item" v-for="(item, index) in results" @click="applyFilter(item, index)" :class="{'active': item === selectedRelaseType}">{{ item.title }}</li>
|
||||
</ul> -->
|
||||
|
||||
<ul class='results'>
|
||||
<movies-list-item v-for='movie in results' :movie="movie" :shortList="shortList"></movies-list-item>
|
||||
</ul>
|
||||
|
||||
<loader v-if="loader" />
|
||||
|
||||
<div class='end-section' v-if="!shortList">
|
||||
<seasoned-button v-if="currentPage < totalPages" @click="loadMore">load more</seasoned-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else style="display: flex; height: 50vh; width: 100%; justify-content: center; align-items: center;">
|
||||
|
||||
<h1 v-if="error">{{ error }}</h1>
|
||||
<h1 v-else>Unable to load list: {{ listTitle }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import storage from '@/storage.js'
|
||||
import MoviesListItem from '@/components/MoviesListItem.vue'
|
||||
import SeasonedButton from '@/components/ui/SeasonedButton.vue'
|
||||
import LoadingPlaceholder from '@/components/ui/LoadingPlaceholder.vue'
|
||||
import Loader from '@/components/ui/Loader.vue'
|
||||
import { searchTmdb, getTmdbListByPath } from '@/api.js'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
shortList: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
propList: Object
|
||||
},
|
||||
components: { MoviesListItem, SeasonedButton, LoadingPlaceholder, Loader },
|
||||
data() {
|
||||
return {
|
||||
listTitle: 'No listname found',
|
||||
results: [],
|
||||
currentPage: 1,
|
||||
totalResults: 0,
|
||||
totalPages: -1,
|
||||
fetchingResults: false,
|
||||
error: undefined,
|
||||
loader: false,
|
||||
|
||||
filters: {
|
||||
status: {
|
||||
elms: ['all', 'requested', 'downloading', 'downloaded'],
|
||||
selected: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
resultCount() {
|
||||
return this.totalResults.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " ")
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
if (this.propList) {
|
||||
this.list = this.propList
|
||||
}
|
||||
|
||||
this.setPageFromUrlQuery()
|
||||
this.parseURI()
|
||||
},
|
||||
mounted() {
|
||||
setTimeout(() => {
|
||||
if (this.results.length === 0 && this.error === undefined) {
|
||||
this.loader = true
|
||||
}
|
||||
}, 200)
|
||||
},
|
||||
methods: {
|
||||
setPageFromUrlQuery() {
|
||||
if (this.$route.query.page)
|
||||
this.currentPage = this.$route.query.page
|
||||
console.log('url page param found', this.currentPage)
|
||||
},
|
||||
getListByName(name) {
|
||||
return storage.homepageLists.filter(list => list.route === name)[0]
|
||||
},
|
||||
parseURI() {
|
||||
const currentRouteName = this.$route.name
|
||||
|
||||
// route name is list - we are in a list view
|
||||
if (currentRouteName === 'list') {
|
||||
const nameParam = this.$route.params.name
|
||||
if (this.getListByName(nameParam)) {
|
||||
this.list = this.getListByName(nameParam)
|
||||
this.listTitle = this.list.title
|
||||
this.fetchListitems()
|
||||
} else {
|
||||
this.error = `Unable to load list: `
|
||||
}
|
||||
} // route name is search - we are searcing
|
||||
else if (currentRouteName === 'search') {
|
||||
if (this.$route.query.query) {
|
||||
this.query = decodeURIComponent(this.$route.query.query)
|
||||
this.listTitle = 'Search results: ' + this.query
|
||||
this.fetchSearchItems()
|
||||
} else {
|
||||
this.error = 'Search query is not defined, please try again'
|
||||
}
|
||||
|
||||
} // no matched route found - using prop to fetch list items
|
||||
else {
|
||||
this.listTitle = this.list.title
|
||||
this.fetchListitems()
|
||||
}
|
||||
|
||||
document.title = this.listTitle
|
||||
},
|
||||
// TODO these should receive a path not get it from list instance
|
||||
fetchListitems() {
|
||||
getTmdbListByPath(this.list.path, this.currentPage)
|
||||
.then(this.parseResponse)
|
||||
.catch(error => {
|
||||
console.error(error)
|
||||
this.error = 'Network error'
|
||||
})
|
||||
},
|
||||
fetchSearchItems() {
|
||||
searchTmdb(this.query, this.currentPage)
|
||||
.then(this.parseResponse)
|
||||
},
|
||||
|
||||
// TODO what parts are modular and what parts do we want the component to deal with
|
||||
// if we pass in some object and then as we initialize we set to local variables.
|
||||
// This way we call the http-api from outside and pass the response in to the component[0]
|
||||
// Could also parse the response we are requesting then return a clean object we can
|
||||
// pass down[1].
|
||||
|
||||
// [0] if this is done we should also take the page, total pages, total results and
|
||||
// the list of results. Maybe also the title of the list or use local title as fallback?
|
||||
// [1] an issue with this that duplicate code will be needed for doing the same with
|
||||
// url params and paths.
|
||||
// (What if we eliminated folder based routes and implemented the routes in hashes
|
||||
// with single page applications today the navigation is simple enought that it
|
||||
// would maybe not be needed to have a path-route but a hash-local.storage
|
||||
// implementation; would allow sharing and remembering paths is just silly for most
|
||||
// Single-Page-Applications that are tightly scoped applications)
|
||||
parseResponse(response) {
|
||||
const data = response.data
|
||||
if (data.page > data.total_pages) {
|
||||
console.error('You have reached the end')
|
||||
this.error = 'You have reached the end'
|
||||
return
|
||||
}
|
||||
|
||||
if (this.results.length) {
|
||||
this.results.push(...data.results)
|
||||
} else {
|
||||
this.results = this.shortList ? data.results.slice(0,12) : data.results
|
||||
}
|
||||
this.page = data.page
|
||||
this.totalPages = data.total_pages
|
||||
this.totalResults = data.total_results || data.results.length
|
||||
|
||||
this.loader = false
|
||||
|
||||
console.info(`Response from list: ${this.listTitle}`, { results: this.results, page: this.page, totalPages: this.totalPages, totalResults: this.totalResults })
|
||||
},
|
||||
loadMore(){
|
||||
this.currentPage++;
|
||||
|
||||
console.log('path and name:', this.$route.path, this.$route.name)
|
||||
let url = ''
|
||||
|
||||
if (this.$route.path.includes('list'))
|
||||
url = `/#${this.$route.path}?page=${this.currentPage}`
|
||||
else if (this.$route.path.includes('search'))
|
||||
url = `/#/search?query=${this.query}&page=${this.currentPage}`
|
||||
|
||||
console.log('new url', url)
|
||||
window.history.replaceState({}, 'foo', url)
|
||||
|
||||
this.parseURI()
|
||||
},
|
||||
// sort() {
|
||||
// console.log(this.showFilters)
|
||||
// },
|
||||
// toggleFilter(item, index){
|
||||
// this.showFilter = this.showFilter ? false : true;
|
||||
// // this.results = this.results.filter(result => result.status != 'downloaded')
|
||||
// },
|
||||
// applyFilter(item, index) {
|
||||
// this.filter = item;
|
||||
// this.filters.status.selected = index;
|
||||
// console.log('applied query filter: ', item, index)
|
||||
// this.fetchCategory()
|
||||
// }
|
||||
},
|
||||
watch: {
|
||||
$route: function () {
|
||||
console.log('updated route')
|
||||
this.results = false
|
||||
this.currentPage = 1
|
||||
this.setPageFromUrlQuery()
|
||||
this.parseURI()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import './src/scss/media-queries';
|
||||
@import './src/scss/variables';
|
||||
|
||||
.movies-list {
|
||||
& ul:last-of-type {
|
||||
padding-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
&:first-of-type header {
|
||||
padding-top: 1.75rem;
|
||||
}
|
||||
|
||||
header {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 12px;
|
||||
|
||||
&.sticky {
|
||||
background-color: $background-color-secondary;
|
||||
|
||||
position: sticky;
|
||||
position: -webkit-sticky;
|
||||
top: $header-size;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 18px;
|
||||
font-weight: 300;
|
||||
text-transform: capitalize;
|
||||
line-height: 18px;
|
||||
margin: 0;
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
.view-more {
|
||||
font-size: 13px;
|
||||
font-weight: 300;
|
||||
letter-spacing: .5px;
|
||||
color: $text-color-70;
|
||||
text-decoration: none;
|
||||
transition: color .5s ease;
|
||||
cursor: pointer;
|
||||
|
||||
&:after{
|
||||
content: " →";
|
||||
}
|
||||
&:hover{
|
||||
color: $text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.result-count {
|
||||
font-size: 13px;
|
||||
font-weight: 300;
|
||||
letter-spacing: .5px;
|
||||
color: $text-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.results {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
&.shortList > li {
|
||||
display: none;
|
||||
|
||||
&:nth-child(-n+4) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.fullwidth-button {
|
||||
width: 100%;
|
||||
margin: 1rem 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@include tablet-min {
|
||||
header {
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
.results.shortList > li:nth-child(-n+6) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@include tablet-landscape-min {
|
||||
header {
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
.results.shortList > li:nth-child(-n+8) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@include desktop-min {
|
||||
.results.shortList > li:nth-child(-n+12) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@include desktop-lg-min {
|
||||
header {
|
||||
padding-left: 1.75rem;
|
||||
}
|
||||
.results.shortList > li:nth-child(-n+16) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
// .shutter {
|
||||
// $height: 36px;
|
||||
// height: $height;
|
||||
// width: 100%;
|
||||
// background-color: $background-color-secondary;
|
||||
// position: absolute;
|
||||
// margin-bottom: -$height;
|
||||
|
||||
// position: -webkit-sticky; /* Safari */
|
||||
// position: sticky;
|
||||
// top: $header-size;
|
||||
// z-index: 4;
|
||||
|
||||
// @include tablet-min{
|
||||
// background-color: blue;
|
||||
// height: 23px 15px;
|
||||
// }
|
||||
// @include tablet-landscape-min{
|
||||
// background-color: orange;
|
||||
// height: 30px;
|
||||
// }
|
||||
// @include desktop-min{
|
||||
// background-color: navajowhite;
|
||||
// height: 34px;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
.form__group-input {
|
||||
padding: 10px 5px 10px 15px;
|
||||
margin-left: 0;
|
||||
height: 38px;
|
||||
width: 150px;
|
||||
font-size: 15px;
|
||||
@include desktop-min {
|
||||
width: 200px;
|
||||
font-size: 17px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -2,12 +2,13 @@
|
||||
<section>
|
||||
<h1>Register new user</h1>
|
||||
|
||||
<seasoned-input text="username" icon="Email" type="username"
|
||||
@inputValue="setValue('username', $event)" />
|
||||
<seasoned-input text="password" icon="Keyhole" type="password"
|
||||
@inputValue="setValue('password', $event)" @enter="requestNewUser"/>
|
||||
<seasoned-input text="repeat password" icon="Keyhole" type="password"
|
||||
@inputValue="setValue('password', $event)" @enter="requestNewUser"/>
|
||||
<seasoned-input placeholder="username" icon="Email" type="username" :value.sync="username" />
|
||||
|
||||
<seasoned-input placeholder="password" icon="Keyhole" type="password"
|
||||
:value.sync="password" @enter="requestNewUser"/>
|
||||
|
||||
<seasoned-input placeholder="repeat password" icon="Keyhole" type="password"
|
||||
:value.sync="passwordRepeat" @enter="requestNewUser"/>
|
||||
|
||||
<seasoned-button @click="requestNewUser">Register</seasoned-button>
|
||||
|
||||
@@ -18,29 +19,26 @@
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import storage from '@/storage.js'
|
||||
import SeasonedButton from '@/components/ui/SeasonedButton.vue'
|
||||
import SeasonedInput from '@/components/ui/SeasonedInput.vue'
|
||||
import SeasonedMessages from '@/components/ui/SeasonedMessages.vue'
|
||||
|
||||
export default {
|
||||
components: { SeasonedButton, SeasonedInput, SeasonedMessages },
|
||||
data(){
|
||||
return{
|
||||
data() {
|
||||
return {
|
||||
messages: [],
|
||||
username: undefined,
|
||||
password: undefined,
|
||||
passwordRepeat: undefined
|
||||
username: null,
|
||||
password: null,
|
||||
passwordRepeat: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
requestNewUser(){
|
||||
let username = this.username
|
||||
let password = this.password
|
||||
let password_re = this.passwordRepeat
|
||||
let { username, password, passwordRepeat } = this
|
||||
|
||||
let verifyCredentials = this.checkCredentials(username, password, passwordRepeat);
|
||||
|
||||
let verifyCredentials = this.checkCredentials(username, password, password_re);
|
||||
|
||||
if (verifyCredentials.verified) {
|
||||
axios.post(`https://api.kevinmidboe.com/api/v1/user`, {
|
||||
username: username,
|
||||
@@ -65,19 +63,25 @@ export default {
|
||||
this.messages.push({ type: 'warning', title: 'Parse error', message: verifyCredentials.reason })
|
||||
}
|
||||
},
|
||||
checkCredentials(username, password, password_re) {
|
||||
if (password !== password_re) {
|
||||
checkCredentials(username, password, passwordRepeat) {
|
||||
if (!username || username.length === 0) {
|
||||
return {
|
||||
verified: false,
|
||||
reason: 'Fill inn username'
|
||||
}
|
||||
}
|
||||
else if (!password || !passwordRepeat) {
|
||||
return {
|
||||
verified: false,
|
||||
reason: "Fill inn both password fields"
|
||||
}
|
||||
}
|
||||
else if (password !== passwordRepeat) {
|
||||
return {
|
||||
verified: false,
|
||||
reason: 'Passwords do not match'
|
||||
}
|
||||
}
|
||||
else if (username === undefined) {
|
||||
return {
|
||||
verified: false,
|
||||
reason: 'Please insert username'
|
||||
}
|
||||
}
|
||||
else {
|
||||
return {
|
||||
verified: true,
|
||||
@@ -85,9 +89,6 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
setValue(l, t) {
|
||||
this[l] = t
|
||||
},
|
||||
logOut(){
|
||||
localStorage.clear();
|
||||
eventHub.$emit('setUserStatus');
|
||||
|
||||
68
src/components/ResultsList.vue
Normal file
68
src/components/ResultsList.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<ul class="results" :class="{'shortList': shortList}">
|
||||
<movies-list-item v-for='movie in results' :movie="movie" />
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import MoviesListItem from '@/components/MoviesListItem'
|
||||
|
||||
export default {
|
||||
components: { MoviesListItem },
|
||||
props: {
|
||||
results: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
shortList: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './src/scss/media-queries';
|
||||
|
||||
.results {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
&.shortList > li {
|
||||
display: none;
|
||||
|
||||
&:nth-child(-n+4) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include tablet-min {
|
||||
.results.shortList > li:nth-child(-n+6) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@include tablet-landscape-min {
|
||||
.results.shortList > li:nth-child(-n+8) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@include desktop-min {
|
||||
.results.shortList > li:nth-child(-n+10) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@include desktop-lg-min {
|
||||
.results.shortList > li:nth-child(-n+16) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -6,22 +6,24 @@
|
||||
<span class="settings__info">Sign in to your plex account to get information about recently added movies and to see your watch history</span>
|
||||
|
||||
<form class="form">
|
||||
<seasoned-input text="plex username" icon="Email"
|
||||
@inputValue="setValue('plexUsername', $event)"/>
|
||||
<seasoned-input text="plex password" icon="Keyhole" type="password"
|
||||
@inputValue="setValue('plexPassword', $event)"/>
|
||||
<seasoned-input placeholder="plex username" icon="Email" :value.sync="plexUsername"/>
|
||||
<seasoned-input placeholder="plex password" icon="Keyhole" type="password"
|
||||
:value.sync="plexPassword" @submit="authenticatePlex" />
|
||||
|
||||
<seasoned-button @click="authenticatePlex">link plex account</seasoned-button>
|
||||
|
||||
<seasoned-messages :messages.sync="messages" />
|
||||
</form>
|
||||
|
||||
<hr class='setting__divider'>
|
||||
|
||||
<h3 class='settings__header'>Change password</h3>
|
||||
<form class="form">
|
||||
<seasoned-input text="new password" icon="Keyhole" type="password"
|
||||
@inputValue="setValue('newPass', $event)"/>
|
||||
<seasoned-input text="repeat new password" icon="Keyhole" type="password"
|
||||
@inputValue="setValue('newPassConfirm', $event)"/>
|
||||
<seasoned-input placeholder="new password" icon="Keyhole" type="password"
|
||||
:value.sync="newPassword" />
|
||||
|
||||
<seasoned-input placeholder="repeat new password" icon="Keyhole" type="password"
|
||||
:value.sync="newPasswordRepeat" />
|
||||
|
||||
<seasoned-button @click="changePassword">change password</seasoned-button>
|
||||
</form>
|
||||
@@ -45,11 +47,12 @@
|
||||
import storage from '@/storage.js'
|
||||
import SeasonedInput from '@/components/ui/SeasonedInput.vue'
|
||||
import SeasonedButton from '@/components/ui/SeasonedButton.vue'
|
||||
import SeasonedMessages from '@/components/ui/SeasonedMessages.vue'
|
||||
|
||||
import { plexAuthenticate } from '@/api.js'
|
||||
|
||||
export default {
|
||||
components: { SeasonedInput, SeasonedButton },
|
||||
components: { SeasonedInput, SeasonedButton, SeasonedMessages },
|
||||
data(){
|
||||
return{
|
||||
userLoggedIn: '',
|
||||
@@ -73,11 +76,12 @@ export default {
|
||||
|
||||
plexAuthenticate(username, password)
|
||||
.then((resp) => {
|
||||
let data = resp.data;
|
||||
console.log('response from plex:', data.user)
|
||||
let data = resp.data;
|
||||
this.messages.push({ type: 'success', title: 'Authenticated with plex', message: 'Successfully linked plex account with seasoned request' })
|
||||
// console.log('response from plex:', data.user)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log('error: ', error)
|
||||
this.messages.push({ type: 'error', title: 'Something went wrong', message: error.message })
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
<section>
|
||||
<h1>Sign in</h1>
|
||||
|
||||
<seasoned-input text="username" icon="Email" type="username"
|
||||
@inputValue="setValue('username', $event)" />
|
||||
<seasoned-input text="password" icon="Keyhole" type="password"
|
||||
@inputValue="setValue('password', $event)" @enter="signin"/>
|
||||
<seasoned-input placeholder="username" icon="Email" type="username" :value.sync="username" />
|
||||
<seasoned-input placeholder="password" icon="Keyhole" type="password" :value.sync="password" @enter="signin"/>
|
||||
|
||||
<seasoned-button @click="signin">sign in</seasoned-button>
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
|
||||
<div class="editQuery" v-if="editSearchQuery">
|
||||
|
||||
<seasonedInput text="Torrent query" icon="_torrents" @inputValue="(val) => editedSearchQuery = val" @enter="fetchTorrents(editedSearchQuery)" />
|
||||
<seasonedInput placeholder="Torrent query" icon="_torrents" :value.sync="editedSearchQuery" @enter="fetchTorrents(editedSearchQuery)" />
|
||||
|
||||
<div style="height: 45px; width: 5px;"></div>
|
||||
|
||||
|
||||
@@ -1,29 +1,37 @@
|
||||
<template>
|
||||
<div class="group" :class="{ completed: value.length > 0 }">
|
||||
<div class="group" :class="{ completed: value }">
|
||||
<svg class="group__input-icon"><use v-bind="{'xlink:href':'#icon' + icon}"></use></svg>
|
||||
<input class="group__input" :type="tempType || type" ref="plex_username"
|
||||
v-model="value" :placeholder="text" @keyup.enter="submit" @input="handleInput" />
|
||||
<input class="group__input" :type="tempType || type" @input="handleInput" v-model="inputValue"
|
||||
:placeholder="placeholder" @keyup.enter="submit" />
|
||||
|
||||
<i v-if="value.length > 0 && type === 'password'" @click="toggleShowPassword" class="group__input-show noselect">show</i>
|
||||
<i v-if="value && type === 'password'" @click="toggleShowPassword" class="group__input-show noselect">show</i>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
text: { type: String },
|
||||
placeholder: { type: String },
|
||||
icon: { type: String },
|
||||
type: { type: String }
|
||||
type: { type: String, default: 'text' },
|
||||
value: { type: String, default: undefined }
|
||||
},
|
||||
data() {
|
||||
return { value: '', tempType: undefined }
|
||||
return {
|
||||
inputValue: undefined,
|
||||
tempType: undefined
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
submit(event) {
|
||||
this.$emit('enter')
|
||||
},
|
||||
handleInput(value) {
|
||||
this.$emit('inputValue', this.value)
|
||||
handleInput(event) {
|
||||
if (this.value !== undefined) {
|
||||
this.$emit('update:value', this.inputValue)
|
||||
} else {
|
||||
this.$emit('change', this.inputValue, event)
|
||||
}
|
||||
},
|
||||
toggleShowPassword() {
|
||||
if (this.tempType === 'text') {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<transition-group name="fade">
|
||||
<div class="message" v-for="message in messages" :class="message.type || 'warning'" :key="message">
|
||||
<div class="message" v-for="(message, index) in reversedMessages" :class="message.type || 'warning'" :key="index">
|
||||
<span class="pinstripe"></span>
|
||||
<div>
|
||||
<h2>{{ message.title || defaultTitles[message.type] }}</h2>
|
||||
@@ -31,6 +31,11 @@ export default {
|
||||
localMessages: [...this.messages]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
reversedMessages() {
|
||||
return [...this.messages].reverse()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clicked(e) {
|
||||
const removedMessage = [...this.messages].filter(mes => mes !== e)
|
||||
|
||||
Reference in New Issue
Block a user