Last active
February 3, 2019 16:05
-
-
Save rodrigopedra/db023f59d7a19cb8f78bf92eaeb0bdff to your computer and use it in GitHub Desktop.
Vue Dynamic Assets Manager (Answer to https://laracasts.com/discuss/channels/vue/use-vue-or-not-to-use-it)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const AssetsManager = { | |
name : 'AssetsManager', | |
template : '#assets-manager-template', | |
components : { ImageForm, LinkForm }, | |
data () { | |
return { | |
showImageForm : false, | |
showLinkForm : false, | |
resolveCallback : null, | |
rejectCallback : null, | |
assets : [], | |
}; | |
}, | |
methods : { | |
addAsset ( type ) { | |
if ( type === 'link' ) { | |
return this.addLink(); | |
} | |
if ( type === 'image' ) { | |
return this.addImage(); | |
} | |
return Promise.reject( 'Invalid asset' ); | |
}, | |
addLink () { | |
this.showLinkForm = true; | |
this.$nextTick( () => { | |
this.$refs.linkForm.focus(); | |
} ); | |
return new Promise( ( resolve, reject ) => { | |
this.resolveCallback = resolve; | |
this.rejectCallback = reject; | |
} ); | |
}, | |
addImage () { | |
this.showImageForm = true; | |
this.$nextTick( () => { | |
this.$refs.imageForm.focus(); | |
} ); | |
return new Promise( ( resolve, reject ) => { | |
this.resolveCallback = resolve; | |
this.rejectCallback = reject; | |
} ); | |
}, | |
saveImage ( url ) { | |
const asset = { | |
id : 'image_' + Date.now(), | |
type : 'image', | |
url : url, | |
}; | |
// Here you could upload the image to the server | |
// and resolve the form callback after uploading | |
this.assets.push( asset ); | |
this.resolveCallback( asset ); | |
this.showImageForm = false; | |
}, | |
saveLink ( link ) { | |
const asset = { | |
id : 'link_' + Date.now(), | |
type : 'link', | |
label : link.label, | |
url : link.url, | |
}; | |
this.assets.push( asset ); | |
// Here you could upload the link info to the server | |
// and resolve the form callback after uploading | |
this.resolveCallback( asset ); | |
this.showLinkForm = false; | |
}, | |
cancel () { | |
this.rejectCallback(); | |
this.showLinkForm = false; | |
this.showImageForm = false; | |
}, | |
removeAsset ( asset ) { | |
if (!confirm('Remove this asset?')) return; | |
this.assets = this.assets.filter( ( item ) => item.id !== asset.id ); | |
this.$emit( 'remove', asset ); | |
}, | |
}, | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Dashboard = { | |
name : 'Dashboard', | |
components : { AssetsManager, ImagesList, LinksList }, | |
template : '#dashboard-template', | |
data () { | |
return { | |
disabled : false, | |
items : [], | |
}; | |
}, | |
mounted () { | |
this.hydrate(); | |
}, | |
methods : { | |
addLinkList () { | |
this.items.push( { | |
id : 'links_' + Date.now(), | |
type : 'links-list', | |
assets : [], | |
} ); | |
}, | |
addImagesList () { | |
this.items.push( { | |
id : 'images_' + Date.now(), | |
type : 'images-list', | |
assets : [], | |
} ); | |
}, | |
addAsset ( item ) { | |
this.disabled = true; | |
const assetType = item.type === 'links-list' | |
? 'link' | |
: 'image'; | |
this.$refs.assets | |
.addAsset( assetType, item.id ) | |
.then( ( asset ) => { | |
item.assets.push( asset ); | |
this.disabled = false; | |
} ) | |
.catch( () => { | |
this.disabled = false; | |
} ); | |
}, | |
removeAsset ( asset ) { | |
this.items.forEach( ( item ) => { | |
item.assets = item.assets.filter( ( item ) => item.id !== asset.id ); | |
} ); | |
}, | |
hydrate () { | |
// Here you could fetch current assets from the server | |
this.addLinkList(); | |
this.addImagesList(); | |
}, | |
} | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const ImageForm = { | |
name : 'ImageForm', | |
template : '#image-form-template', | |
data () { | |
return { | |
image : null, | |
}; | |
}, | |
mounted () { | |
this.image = null; | |
}, | |
methods : { | |
submit () { | |
const url = `http://lorempixel.com/100/75/${this.image}/`; | |
this.$emit( 'submit', url ); | |
}, | |
cancel () { | |
this.$emit( 'cancel' ); | |
}, | |
focus () { | |
this.$nextTick( () => { | |
this.$refs.autofocus.focus(); | |
} ); | |
}, | |
}, | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const ImagesList = { | |
name : 'ImagesList', | |
template : '#images-list-template', | |
props : { | |
item : { type : Object, required : true }, | |
disabled : { type : Boolean, default : false }, | |
}, | |
methods : { | |
addImage () { | |
this.$emit( 'add' ); | |
}, | |
} | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Dashboard</title> | |
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css"> | |
<!-- development version, includes helpful console warnings --> | |
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> | |
<!-- components --> | |
<script src="image-form.js"></script> | |
<script src="link-form.js"></script> | |
<script src="assets-manager.js"></script> | |
<script src="links-list.js"></script> | |
<script src="images-list.js"></script> | |
<script src="dashboard.js"></script> | |
</head> | |
<body> | |
<div id="app"></div> | |
<script> | |
const app = new Vue({ | |
components: {Dashboard}, | |
render: (h) => h('Dashboard') | |
}); | |
document.addEventListener('DOMContentLoaded', () => app.$mount('#app')); | |
</script> | |
<script type="text/x-template" id="dashboard-template"> | |
<div class="container"> | |
<h1>Dashboard</h1> | |
<div class="row"> | |
<div class="col-4"> | |
<assets-manager | |
ref="assets" | |
@remove="(asset) => removeAsset(asset)"></assets-manager> | |
</div> | |
<div class="col-8"> | |
<p class="text-right"> | |
<button | |
class="btn btn-primary" | |
type="button" | |
:disabled="disabled" | |
@click="addLinkList()"> | |
Add Links List | |
</button> | |
<button | |
class="btn btn-warning" | |
type="submit" | |
:disabled="disabled" | |
@click="addImagesList()"> | |
Add Images List | |
</button> | |
</p> | |
<hr> | |
<component | |
class="mb-3" | |
:is="item.type" | |
:item="item" | |
:disabled="disabled" | |
:key="item.id" | |
@add="addAsset(item)" | |
v-for="item in items"></component> | |
</div> | |
</div> | |
</div> | |
</script> | |
<script type="text/x-template" id="links-list-template"> | |
<div class="card"> | |
<div class="card-header bg-primary d-flex align-items-center text-white"> | |
<h4 class="mb-0">Links</h4> | |
<button | |
class="ml-auto btn btn-sm btn-success" | |
type="button" | |
:disabled="disabled" | |
@click="addLink()"> | |
add link | |
</button> | |
</div> | |
<ul class="list-group list-group-flush"> | |
<li class="list-group-item text-center text-muted" v-if="item.assets.length === 0"> | |
<em>no links yet</em> | |
</li> | |
<li | |
class="list-group-item" | |
:key="link.id" | |
v-for="link in item.assets"> | |
<a :href="link.url" target="_blank">{{ link.label }}</a> | |
</li> | |
</ul> | |
</div> | |
</script> | |
<script type="text/x-template" id="images-list-template"> | |
<div class="card"> | |
<div class="card-header bg-warning d-flex align-items-center"> | |
<h4 class="mb-0">Images</h4> | |
<button | |
class="ml-auto btn btn-sm btn-success" | |
type="button" | |
:disabled="disabled" | |
@click="addImage()"> | |
add image | |
</button> | |
</div> | |
<ul class="list-group list-group-flush"> | |
<li class="list-group-item text-center text-muted" v-if="item.assets.length === 0"> | |
<em>no images yet</em> | |
</li> | |
<li | |
class="list-group-item" | |
:key="image.id" | |
v-for="image in item.assets"> | |
<img :src="image.url" class="d-block" style="max-height: 75px;"> | |
</li> | |
</ul> | |
</div> | |
</script> | |
<script type="text/x-template" id="assets-manager-template"> | |
<div> | |
<div class="card"> | |
<div class="card-header d-flex align-items-center"> | |
<h4 class="mb-0">Assets</h4> | |
</div> | |
<ul class="list-group list-group-flush"> | |
<li class="list-group-item text-center text-muted" v-if="assets.length === 0"> | |
<em>no assets yet</em> | |
</li> | |
<li | |
class="list-group-item d-flex align-items-center" | |
:key="asset.id" | |
v-for="asset in assets"> | |
<span class="badge badge-primary mr-2" v-if="asset.type == 'link'">link</span> | |
<span class="badge badge-info mr-2" v-if="asset.type == 'image'">image</span> | |
<strong class="flex-grow-1 text-truncate mr-2" :title="asset.url">{{ asset.url }}</strong> | |
<button | |
type="button" | |
class="btn btn-sm btn-outline-danger ml-auto" | |
@click="removeAsset(asset)">remove</button> | |
</li> | |
</ul> | |
</div> | |
<image-form | |
class="mt-3" | |
ref="imageForm" | |
v-if="showImageForm" | |
@submit="(url) => saveImage(url)" | |
@cancel="cancel()"></image-form> | |
<link-form | |
class="mt-3" | |
ref="linkForm" | |
v-if="showLinkForm" | |
@submit="(link) => saveLink(link)" | |
@cancel="cancel()"></link-form> | |
</div> | |
</script> | |
<script type="text/x-template" id="image-form-template"> | |
<form @submit.prevent="submit()" @reset="cancel()"> | |
<div class="card"> | |
<h4 class="card-header">Add an image</h4> | |
<div class="card-body"> | |
<div class="form-group"> | |
<select class="form-control" ref="autofocus" v-model="image" required> | |
<option :value="null">Choose an image...</option> | |
<option value="animals">Animal</option> | |
<option value="city">City</option> | |
<option value="food">Food</option> | |
<option value="nature">Nature</option> | |
<option value="people">People</option> | |
</select> | |
</div> | |
</div> | |
<div class="card-footer text-right"> | |
<button type="reset" class="btn btn-sm btn-link">cancel</button> | |
<button type="submit" class="btn btn-sm btn-success">Add</button> | |
</div> | |
</div> | |
</form> | |
</script> | |
<script type="text/x-template" id="link-form-template"> | |
<form @submit.prevent="submit()" @reset="cancel()"> | |
<div class="card"> | |
<h4 class="card-header">Add a link</h4> | |
<div class="card-body"> | |
<div class="form-group"> | |
<label for="link-label">Label</label> | |
<input | |
type="text" | |
class="form-control" | |
id="link-label" | |
required | |
ref="autofocus" | |
v-model="label"> | |
</div> | |
<div class="form-group"> | |
<label for="link-label">URL</label> | |
<input type="url" class="form-control" id="link-label" required v-model="url"> | |
</div> | |
</div> | |
<div class="card-footer text-right"> | |
<button type="reset" class="btn btn-sm btn-link">cancel</button> | |
<button type="submit" class="btn btn-sm btn-success">Add</button> | |
</div> | |
</div> | |
</form> | |
</script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const LinkForm = { | |
name : 'LinkForm', | |
template : '#link-form-template', | |
data () { | |
return { | |
label : null, | |
url : null, | |
}; | |
}, | |
mounted () { | |
this.label = null; | |
this.image = null; | |
}, | |
methods : { | |
submit () { | |
this.$emit( 'submit', { label : this.label, url : this.url } ); | |
}, | |
cancel () { | |
this.$emit( 'cancel' ); | |
}, | |
focus () { | |
this.$nextTick( () => { | |
this.$refs.autofocus.focus(); | |
} ); | |
}, | |
}, | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const LinksList = { | |
name : 'LinksList', | |
template : '#links-list-template', | |
props : { | |
item : { type : Object, required : true }, | |
disabled : { type : Boolean, default : false }, | |
}, | |
methods : { | |
addLink () { | |
this.$emit( 'add' ); | |
}, | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment