No More Posting New Topics!

If you have a question or an issue, please start a thread in our Github Discussions Forum.
This forum is closed for new threads/ topics.

Navigation

    Quasar Framework

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search

    Sharing my picture list component

    Show & Tell
    1
    1
    135
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • W
      walfin last edited by

      Hi, I would like to share my picture list component that allows dragging of pictures to reorder them. Dependencies are promise-file-reader, downscale and mobile-drag-drop if you want to use it on mobile. Let me know if you find it useful!

      <style>
      .separator{
      	border-radius:5px;
      	border:1px black dashed;
      }
      .pictureList{
      	display:flex;
      	flex-direction:row;
      	flex-wrap:wrap;
      }
      .picture{
      	width:max-content;
      	display:flex;
      	flex-direction:row;
      	flex-wrap:nowrap;
      }
      #newPicture{
      	width:0.1px;
      	height:0.1px;
      	opacity:0;
      	overflow:hidden;
      	position:absolute;
      	z-index:-1;
      }
      </style>
      <template>
      <div>
      	<p v-if="this.pictures.length>0">Drag the pictures to rearrange them (hold down on the picture to drag on mobile).</p>
      	<div class="pictureList">
      		<div ref="divs" v-for="(picture,index) in pictures" class="picture">
      			<q-separator vertical class="separator" @dragover="dragOverLeft(index,$event)" @dragenter="dragEnter($event)" @dragleave="dragLeaveLeft(index)" @drop="dropLeft(index,$event)" :style="leftSeparatorVisible[index]?'width:30px':'width:20px;opacity:0'"/>
      			<q-avatar draggable="true" rounded :style="dragging===index?'opacity:0.5':''" @dragstart="dragStart(index,$event)" @dragend="dragEnd">
      				<img draggable="false" :style="dragging===index?'border:2px grey dashed':''" :src="picture.src"/>
      				<q-badge color="primary" floating transparent v-ripple @click="removePicture(index)">
      					<q-icon name="far fa-times-circle"/>
      				</q-badge>
      			</q-avatar>
      			<q-separator v-if="lastItem[index]" vertical class="separator" @dragover="dragOverRight(index,$event)" @dragenter="dragEnter($event)" @dragleave="dragLeaveRight(index)" @drop="dropRight(index,$event)" :style="rightSeparatorVisible[index]?'width:30px':'width:20px;opacity:0'"/>
      		</div>
      		<input type="file" multiple id="newPicture" ref="newPicture" @change="newPictureChanged($event)" accept="image/*"/>
      		<q-btn color="primary" glossy push @click="$refs.newPicture.click()">Add Picture</q-btn>
      	</div>
      </div>
      </template>
      <script>
      import PromiseFileReader from 'promise-file-reader';
      import downscale from 'downscale';
      
      export default{
      	props:['pictures'],
      	data(){
      		return{
      			dragging:null,
      			leftSeparatorVisible:[],
      			rightSeparatorVisible:[],
      			lastItem:[]
      		};
      	},
      	watch:{
      		pictures:{
      			deep:true,
      			handler(){
      				this.leftSeparatorVisible.length=this.pictures.length;
      				this.leftSeparatorVisible.fill(false);
      				this.rightSeparatorVisible.length=this.pictures.length;
      				this.rightSeparatorVisible.fill(false);
      				this.computeLastItem();
      			}
      		}
      	},
      	methods:{
      		computeLastItem(){
      			this.lastItem.length=this.pictures.length;
      			this.lastItem.fill(false);
      			this.lastItem[this.lastItem.length-1]=true;
      			if(!this.$refs.divs)return;
      			for(let i=0;i<this.lastItem.length-1;++i){
      				if(i+1>this.$refs.divs.length)this.lastItem[i]=true;
      				else this.lastItem[i]=this.$refs.divs[i+1].getBoundingClientRect().top>this.$refs.divs[i].getBoundingClientRect().top;
      			}
      		},
      		async newPictureChanged(event){
      			this.$q.loading.show();
      			if(typeof cordova!=='undefined'&&cordova.plugins&&cordova.plugins.permissions){
      				await new Promise((resolve,reject)=>{
      					function getPermissions(){
      						let permissions=cordova.plugins.permissions;
      						permissions.requestPermission(permissions.READ_EXTERNAL_STORAGE,async status=>{
      							if(!status.hasPermission){
      								setTimeout(getPermissions.bind(this),0);
      								return;
      							}
      							this.resolve(status);
      						},()=>setTimeout(getPermissions.bind(this),0));
      					}
      					setTimeout(getPermissions.bind({resolve,reject}),0);
      				});
      			}
      			let promises=[];
      			for(let i=0;i<event.target.files.length;++i){
      				promises.push(new Promise(resolve=>{
      					let newImg=new Image();
      					newImg.onload=()=>{
      						if(newImg.width<=500||newImg.height<=500){
      							this.pictures.push({src:newImg.src});
      							resolve(this.pictures[this.pictures.length-1]);
      							return;
      						}
      						downscale(newImg.src,newImg.width>=newImg.height?500:0,newImg.height>=newImg.width?500:0).then(src=>{
      							this.pictures.push({src});
      							resolve(this.pictures[this.pictures.length-1]);
      						}).catch(error=>{
      							console.log(error);
      							this.$q.notify({message:error,timeout:2000});
      							resolve(null);
      						});
      					};
      					PromiseFileReader.readAsDataURL(event.target.files[i]).then(src=>newImg.src=src).catch(error=>{
      						console.log(error);
      						this.$q.notify({message:error,timeout:2000});
      						resolve(null);
      					});
      				}));
      			}
      			this.$emit('picturesAdded',(await Promise.all(promises)).filter(picture=>picture!==null));
      			this.$q.loading.hide();
      		},
      		removePicture(index){
      			this.$emit('pictureRemoved',this.pictures[index]);
      			this.pictures.splice(index,1);
      		},
      		dragStart(index,event){
      			event.dataTransfer.setData('pictureIndex',index);
      			this.dragging=index;
      			this.$forceUpdate();
      		},
      		dragEnd(){
      			this.dragging=null;
      			this.leftSeparatorVisible.fill(false);
      			this.rightSeparatorVisible.fill(false);
      			this.$forceUpdate();
      		},
      		dragEnter(event){
      			event.preventDefault();
      		},
      		dragOverLeft(index,event){
      			if(this.dragging===null)return;
      			event.preventDefault();
      			this.leftSeparatorVisible[index]=true;
      			this.$forceUpdate();
      		},
      		dragOverRight(index,event){
      			if(this.dragging===null)return;
      			event.preventDefault();
      			this.rightSeparatorVisible[index]=true;
      			this.$forceUpdate();
      		},
      		dragLeaveLeft(index){
      			this.leftSeparatorVisible[index]=false;
      			this.$forceUpdate();
      		},
      		dragLeaveRight(index){
      			this.rightSeparatorVisible[index]=false;
      			this.$forceUpdate();
      		},
      		dropLeft(index,event){
      			this.leftSeparatorVisible.fill(false);
      			this.rightSeparatorVisible.fill(false);
      			if(index===this.dragging){
      				this.dragging=null;
      				return;
      			}
      			this.pictures.splice(index,0,this.pictures[this.dragging]);
      			this.pictures.splice(this.dragging+(this.dragging>index?1:0),1);
      			this.dragging=null;
      		},
      		dropRight(index,event){
      			this.leftSeparatorVisible.fill(false);
      			this.rightSeparatorVisible.fill(false);
      			if(index===this.dragging){
      				this.dragging=null;
      				return;
      			}
      			this.pictures.splice(index+1,0,this.pictures[this.dragging]);
      			this.pictures.splice(this.dragging+(this.dragging>index?1:0),1);
      			this.dragging=null;
      		}
      	},
      	mounted(){
      		if(!Array.isArray(this.pictures))this.pictures=[];
      		this.leftSeparatorVisible=new Array(this.pictures.length);
      		this.leftSeparatorVisible.fill(false);
      		this.rightSeparatorVisible=new Array(this.pictures.length);
      		this.rightSeparatorVisible.fill(false);
      		this.computeLastItem();
      		window.removeEventListener('resize',this.computeLastItem);
      		window.addEventListener('resize',this.computeLastItem);
      	}
      };
      </script>
      

      mobileDragDrop.js (boot script)

      import {polyfill} from 'mobile-drag-drop';
      import {scrollBehaviourDragImageTranslateOverride} from "mobile-drag-drop/scroll-behaviour";
      
      polyfill({dragImageTranslateOverride:scrollBehaviourDragImageTranslateOverride});
      
      1 Reply Last reply Reply Quote 1
      • First post
        Last post