Quasar Framework (Vue) : Collapsible and mouseleave - vue.js

I'm trying to figure out a collapsible component inside the quasar.
For some reason, the mouse hover event does not work.
Please tell me how you can replace the text output area with a button when the user hovers the pointer over .
Please help! :)
<q-collapsible
opened
icon="bookmark"
:label="$t('Custom dashboards')"
#mouseleave="var2 = true" //It doesn't work :(
#mouseenter="var2 = false" //It doesn't work
>
<q-item>
<q-btn
#click="func()"
icon="add"
:label="$t('Create')"/>
</q-item>
</q-collapsible>
enter image description here

Complete HTML/Script/CSS would be expected to solve your issue. Since that is not available, I basically what you did, but expanded to solve what was supplied in the image.
#mouseleave and #mouseenter are working correctly. You just have to set and use the value to hide/show the button.
I do not believe QCollapsible support a slot in the header (correct be if I am wrong), so it might be better to use a QToolbar in conjunction with QCollapsible and QList.
// Update the variables to toggle the display
new Vue({
el: '#q-app',
data: function() {
return {
listOpen: true,
hover: false
}
},
methods: {
toggleList: function() {
this.listOpen = !this.listOpen
},
addItem: function() {
this.$q.notify('clicked')
}
},
computed: {
hovering() {
return this.listOpen && this.hover
}
}
})
// Hide the existing collapse button (maybe there is a property for this)
div.q-collapsible.q-item-division.relative-position.q-collapsible-cursor-pointer>div>div.q-item.q-item-division.relative-position {
display: none
}
<!-- Create a div with the hoverover events -->
<div style="width:500px" #mouseleave="hover = false" #mouseenter="hover = true">
<q-toolbar>
<div class="on-left">
<q-icon name="bookmark">
<q-icon>
</div>
<q-toolbar-title v-show="!hovering">
Custom Dashboard
</q-toolbar-title>
<q-btn v-show="hovering" icon="add" label="Create" class="full-width" #click="addItem"></q-btn>
<q-btn flat icon="menu" #click="toggleList"></q-btn>
</q-toolbar>
<q-collapsible opened v-model="listOpen">
<div slot="side">test</div>
<q-list>
<q-item>
<q-item-side left>
<q-icon name="dashboard">
<q-icon>
</q-item-side>
<q-item-main>
dash 2
</q-item-main>
</q-item>
<q-item>
<q-item-side left>
<q-icon name="dashboard">
<q-icon>
</q-item-side>
<q-item-main>
dash 5
</q-item-main>
</q-item>
</q-list>
</q-collapsible>
</div>
Codepen

Related

How to change styles of child component based on grid / list toggled in parent component, in vue?

I have a view in Vue project.
Home.vue
<template>
<TestLayout >
<Card/>
<Card/>
<Card/>
</TestLayout>
</template>
<script>
import TestLayout from "../components/TestLayout.vue"
import Card from "../components/Card.vue"
export default {
name: "Home",
props:{
isList:{
type: Boolean
}
},
components: {
TestLayout,
Card
},
}
</script>
The TestLayout has a section where we can display cards in list or grid view
TestLayout.vue
<template>
<div class="flex border-solid ">
<ListBulletIcon class="h-10 w-10 cursor-pointer shadow border-2 border-indigo-600 rounded-l p-2"
#click="listView = true" />
<TableCellsIcon #click="listView = false"
class="h-10 w-10 cursor-pointer shadow border-2 border-indigo-600 rounded-r p-2" />
</div>
<section
:class="[listView ? 'md:grid-cols-1 grid-cols-1' : 'md:grid-cols-4 grid-cols-2', 'rounded-md grid gap-5 col-span-full']">
<slot :listView="listView"></slot>
</section>
</template>
<script>
import {
ListBulletIcon,TableCellsIcon} from '#heroicons/vue/24/outline'
export default {
data: function () {
return {
listView: false,
}
},
components: {
ListBulletIcon,
TableCellsIcon,
},
}
}
</script>
I want to change the style of Card.vue based on whether user clicks grid view or list view icon.
For example, I want to add this style to Card.vue div tag in its template:
:class="[isList ? 'dark:bg-midnight' : 'dark:bg-red-300', 'min-h-80 w-full bg-gray-50 shadow-xl rounded-md flex flex-col']"
How will I check isList is clicked or not?
How can I achieve this?
You're half way there. After defining a slot prop (<slot :listView="listView"></slot>) you should access it in parent and pass it down to slot components.
<TestLayout>
<template v-slot="{ listView }">
<Card :isList="listView" />
<Card :isList="listView" />
</template>
</TestLayout>

Passing props dynamically to a component inside a v-for loop

I have a v-for loop that iterates through an array of meetings (meetings between sellers and potential buyers of used cars) and prints a card for each meeting, a basic display of who the meeting is with, what car it is about and the scheduled date. Now, I implemented a button that when clicked, opens a dialog with a Google Maps component that shows the marker for the agreed location of the meeting.
My problem is that no matter what card I click on, the dialog will always display the location of the LAST card, regardless of which has been clicked. I would think that since Im calling the component INSIDE the v-for loop it would pass props dynamically for each card, on each iteration, but that does not seem to be the case.
Here is the HTML:
<div
v-for="meeting in meetings"
:key="meeting.did"
class="col-12 col-md-6 col-lg-3 q-pa-md q-mx-xl"
>
<q-card class="my-card homeCard q-pa-md">
<q-dialog class="mapDialog flex column" v-model="mapDialog">
<MeetMapComponent
:key="componentKey"
:mapDiv="mapDiv"
:mapData="meeting.address"
:buyerName="meeting.name"
/>
</q-dialog>
<q-card-section
class="tipCardImage flex row justify-end"
:style="`background-image: url(${meeting.car.carImg})`"
>
<router-link
:to="`/user/meet/edit/${meeting.did}`"
style="text-decoration: none"
>
<q-icon
#click="fetchMeeting(meeting.did)"
name="fa-solid fa-pencil editNameIcon q-mb-sm q-ml-sm"
></q-icon>
</router-link>
<q-icon
name="fa-solid fa-trash editNameIcon q-mb-sm q-ml-sm"
#click="triggerDelete(meeting.did)"
></q-icon>
</q-card-section>
<q-card-section>
<div class="cardTitle">
<span>Encuentro Con</span> {{ truncateString(meeting.name, 30) }}
</div>
<div class="tipCardText">
<span>Agendado para el </span>
<p>{{ truncateString(meeting.date, 120) }}</p>
</div>
<div class="flex row justify-end">
<q-btn
#click="mapDialog = true"
class="text-white cardButton"
:class="{ cardButtonMobile: $q.screen.lt.md }"
>Ver Ubicación</q-btn
>
</div>
</q-card-section>
</q-card>
</div>
And here is the code for the MeetMapComponent:
<template>
<div class="meetMapContainer">
<div ref="mapDiv" style="width: 100%; height: 500px" />
<h5 class="text-center text-white">{{ props.mapData.address }}</h5>
</div>
</template>
<script setup>
import { ref } from "vue";
import { useAuthStore } from "stores/auth";
import { storeToRefs } from "pinia";
import { Loader } from "#googlemaps/js-api-loader";
const props = defineProps({
mapData: Object,
buyerName: String,
});
const GOOGLE_MAPS_API_KEY = "...";
const loader = new Loader({ apiKey: GOOGLE_MAPS_API_KEY });
const mapDiv = ref(null);
async function mapRender() {
await loader.load();
const map = new google.maps.Map(mapDiv.value, {
mapTypeId: "roadmap",
center: props.mapData.coordinates,
zoom: 13,
});
console.log(map);
new google.maps.Marker({
position: props.mapData.coordinates,
map,
title: `Encuentro con ${props.buyerName}`,
});
}
mapRender();
</script>
I will help you as much as I understand. You use the mapDialog variable to open the dialogue. But even if this variable is used in v-for, its reference does not change. For this reason, when you want to open a modal, all modals may be opened and the last one may appear because it is the last one opened. Please check the dom.
I think this method can solve the problem.
in script
const meetings = [
{
did: 'some value',
address: 'some address',
name: 'some name',
// add modal flag
showMapModal: false
}
]
template
<div
v-for="meeting in meetings"
:key="meeting.did"
class="col-12 col-md-6 col-lg-3 q-pa-md q-mx-xl"
>
<q-card class="my-card homeCard q-pa-md">
<q-dialog class="mapDialog flex column" v-model="meeting.showMapModal">
<MeetMapComponent
:key="componentKey"
:mapDiv="mapDiv"
:mapData="meeting.address"
:buyerName="meeting.name"
/>
</q-dialog>
</q-card>
</div>

VueJS - How to Enable/Disable a specific input in a v-for loop

I need to enable/disable specific inputs on button click.
My problem here is when I click my button, ALL the inputs enable/disable I am having a hard time targeting a single one.
I use props “articles” which returns the “id”, “title” and “body” of my articles.
<div v-for="(article, index) in articles" v-bind:key="article.id">
<div class="flex">
<div class="flex-1 px-2 py-2">
<input
v-model="article.title"
type="text"
:disabled="isDiabled"
class="w-full disabled:opacity-75 bg-gray-300 focus:bg-white"
/>
</div>
<div class="flex-1 px-2 py-2">
<input
v-model="article.body"
type="text"
:disabled="isDiabled"
class="w-full disabled:opacity-75 bg-gray-300 focus:bg-white"
/>
</div>
<div class="px-2 py-2">
<button
class="px-2 py-2 border-gray-400 bg-gray-800 rounded text-white hover:border-gray-300 items-end"
#click="enableEdit"
>
Edit
</button>
</div>
</div>
</div>
<script>
export default {
props: ["articles"],
data() {
return {
isDiabled: true,
};
},
methods: {
enableEdit() {
this.isDiabled = false;
},
},
};
</script>
Every article has his own button and I want only the two inputs of the button that is clicked to be enabled/disabled and not all of them.
As being pointed you need to add also that property to your articles... of course you don't need to do this in the database, still using it as a property you need also to emit this change of the flag to the parent.
You could also use a local version of the articles where you add also this isDisabled property, you fill this new variable at the creation of the component based of course on the property you received from the parent,
created(){
this.local_articles = this.articles.map(e => {
return {...e, isDisabled: true} }
)
},
this way you don't need to propagate nothing to the parent as you can handle all internally.
This fiddle gives a solution to your problem.
Make a boolean property for each article, then change the binding of disabled to that property.

Vue2Leaflet : how to display the tooltip of a specific marker

I'm using nuxt-leaflet (with Vue2Leaflet) and I'm wondering how to display the tooltip of a specific marker after cliquing on a button ("Display tooltip") in my template vue file ?
<template>
<div>
<button #click="displayTooltipOfMarker(x)">Display tooltip</button>
<div id="map-wrap" style="height: 500px; width:100%">
<no-ssr>
<l-map :zoom="10" :center="positionInitiale">
<l-tile-layer url="http://{s}.tile.osm.org/{z}/{x}/{y}.png"></l-tile-layer>
<l-marker :icon="customIcon" :lat-lng="positionMarqueurHome"></l-marker>
<l-marker
v-for="marker in marqueurs"
:key="marker.id"
:lat-lng.sync="marker.position"
#click="alert(marker)"
>
<l-popup :content="marker.tooltip"/>
<l-tooltip :content="marker.tooltip"/>
</l-marker>
</l-map>
</no-ssr>
</div>
</div>
</template>
Is it possible ?
To open/close Tooltip on external event (like button is your case) the folowing solution could be considered:
get access to Leaflet marker object via $refs attribute:
<l-marker
v-for="(marker, index) in markers"
:key="index"
ref="markersRef"
:lat-lng="marker.position"
>
<l-popup :content="marker.name"/>
</l-marker>
and save it into array:
mounted: function() {
this.$nextTick(() => {
this.markerObjects = this.$refs.markersRef.map(ref => ref.mapObject);
});
}
Once the external event is triggered (e.g. button click) the tooltip is getting displayed like this:
<button #click="displayTooltip(1)">Display</button>
displayTooltip(selectedIndex) {
this.markerObjects[selectedIndex].openTooltip();
}
Here is a demo for your reference

How to pass an image src into a modal dialog in Vue (in order to zoom the clicked image)?

What I'm trying to do is to zoom an image on click, the idea is:
you click on an image
dialog of width=85vw opens up with the image you just clicked on inside of it (so the image is displayed almost fullscreen now)
I cannot think of a better way of "zooming" an image on click, but to open it in a modal dialog (if there's an easier way, please let me know).
Code:
<v-dialog v-model="dialog" max-width="85vw" >
<img :src="img1" alt="" width="100%" #click.stop="dialog=false">
</v-dialog>
<img :src="img1" width="500px" #click.stop="dialog = true">
<img :src="img2" width="500px" #click.stop="dialog = true">
<img :src="img3" width="500px" #click.stop="dialog = true">
export default {
data() {
img1: "../../src/assets/pexels-photo-373912.jpg",
img2: "../../src/assets/pexels-photo-373912.jpg",
img3: "../../src/assets/pexels-photo-373912.jpg"
}
}
The problem is, it's not opening any clicked image in a dialog, just the one you hard coded in there, in this example it will always open img1 no matter what image you click.
I don't know how to pass the :src into the dialog dynamically - the :src of the image you clicked.
P.S. v-dialog is a component from Vuetify.js library
Question:
Is there an obviously better way of doing it?
If not really, how do I make this method to work and display the image I clicked in the modal dialog?
You need a variable to hold which image is selected. When you click on an image, it should set the variable to the url for that image. When you click on the dialog image, it should unset the variable.
The dialog should show when the variable is set and otherwise be hidden.
For simplicity, I'm not using an actual dialog, just a div. It looks like you would use it for the dialog's v-model rather than using the v-if I use.
new Vue({
el: '#app',
data: {
selectedImage: null,
images: [
'http://via.placeholder.com/400x300?text=image%201',
'http://via.placeholder.com/600x400?text=image%202',
'http://via.placeholder.com/500x500?text=image%203'
]
},
methods: {
zoom(url) {
console.log("Zoom", url);
this.selectedImage = url;
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<div v-if="selectedImage" max-width="85vw">
<img :src="selectedImage" alt="" width="100%" #click.stop="selectedImage = null">
<hr>
</div>
<div v-for="url in images">
<img :src="url" width="100px" #click="zoom(url)">
</div>
</div>
to click distributed images across the page define a property and change it with click event
in template
<v-dialog v-model="dialog" max-width="60%" #keydown.esc="cancel">
<v-card>
<v-img :src="pic" alt="" contain/>
</v-card>
</v-dialog>
<v-img
src="require(#/assets/clinic/1.jpeg)"
alt=""
contain
#click="openPic(require('#/assets/clinic/1.jpeg'))"//this part is important you have to require image
/>
in script
data() {
return {
pic: "",
dialog: false
}
},
methods: {
openPic(image) {
this.dialog = true
this.pic = image
},
cancel() {
this.dialog = false
}
}