I am currently building a ellipsis components for vue3, here's part of it's code
<template>
<XTooltip
v-if="getTooltipsDisabled() === false"
ref="tooltipRef"
:content="tooltipContent"
>
<span
ref="content"
:class="ellipsisStyleRef.valueOf()"
:style="ellipsisClassRef"
#click="handleClickRef"
>
<slot></slot>
</span>
</XTooltip>
<span
v-else
ref="content"
:class="ellipsisStyleRef.valueOf()"
:style="ellipsisClassRef"
#click="handleClickRef"
>
<slot></slot>
</span>
</template>
const getTooltipsDisabled = (): boolean => {
const content = ref<HTMLElement | null>(null);
let tooltipsDisabled = false;
const { value: outer } = content;
console.log('TEST');
if (outer) {
tooltipsDisabled = outer.scrollHeight <= outer.offsetHeight;
}
return tooltipsDisabled;
};
So the main problem right now is I am using getTooltipsDisabled() to judge if Tooltip should be rendered, but if I did this, it will keep trigger the function again and again until it hits maximum recursive, I wonder why it is and how to avoid this ?
my guess is that when we render the tooltip, it will also render the ref:content, which will make the function start all over again, but I am not sure if this is the reason.
I have the following problem with state usage on below code snippet.
<div class="featured-card-container">
<swiper class="swiper" :options="swiperOption">
<template v-for="(strategy, index) in strategiesCards">
<swiper-slide :key="'slider-' + index">
<strategy-card
:data="strategy"
#click.native="showModalStrategies()"
/>
</swiper-slide>
<strategies-modal
:key="'modal-' + index"
:dialog.sync="modalStrategies"
:data="strategy"
/>
</template>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
So basically, all I need to is triggering a modal from its responsible card. I just kind of having a hard time how to settle this.
My current approach is the above code.
But I know the problem would come that, the modalStrategies data state is being used by many components. So it makes them like quickly changing all the existing modal even I clicked only one card.
How I can I fixed this? Is there any workaround?
Thank you so much everyone. I really appreciate your effort to try understanding my problems.
<template v-for="(strategy, index) in strategiesCards">
<swiper-slide :key="'slider-' + index">
<strategy-card
:data="strategy"
#click.native="showModalStrategies(strategy)"
/>
</swiper-slide>
<strategies-modal
:key="'modal-' + index"
:dialog.sync="isShowModal"
:data="strategy"
/>
</template>
data() {
return {
modalStrategy: null,
currentStrategy: null
}
},
computed: {
isShowModal: {
get() {
return this.modalStrategy === this.currentStrategy
},
// https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier
set(val) {
if(!val) {
this.modalStrategy = null
}
}
}
},
methods: {
showModalStrategies(strategy) {
this.currentStrategy= strategy
this.modalStrategy = strategy
}
}
I'm trying to create a custom play/pause button for my audio items which I did like so...
<div v-for="(post, p) in post_list">
<!-- ... -->
<!-- ... -->
<!-- ... -->
<v-avatar v-if="!is_played" color="#663399" size="42" class="mx-2"
#click="playMe('custom-wave-aud-'+p)"> <!-- is_played = true -->
<v-icon dark> mdi-play </v-icon>
</v-avatar>
<v-avatar v-if="is_played" color="#663399" size="42" class="mx-2"
#click="pauseMe('custom-wave-aud-'+p)"> <!-- is_played = false-->
<v-icon dark> mdi-pause </v-icon>
</v-avatar>
</div>
The above code toggles from pause icon to play icon on click. The problem is that, it affects all play/pause button for all items since the is_played is assigned for all the items.
What I want is that is_played should only affect one item and should only be exclusive to the item that I toggle play/pause button.
EDITED: Here's the play/pause function
playMe(c, index) {
document.getElementsByClassName(c)[0].play();
this.is_played = true;
},
pauseMe(c, index) {
document.getElementsByClassName(c)[0].pause();
this.is_played = false;
},
As explained in comments, you have to reference the index of the current post in is_played. Otherwise all the buttons will change when is_played changes. Here's some sample code which should do that:
<div v-for="(post, p) in post_list" :key="p">
<v-avatar v-if="is_played !== p"
color="#663399"
size="42"
class="mx-2"
#click="playMe('custom-wave-aud-' + p, p)">
<v-icon dark>mdi-play</v-icon>
</v-avatar>
<v-avatar v-if="is_played === p"
color="#663399"
size="42"
class="mx-2"
#click="pauseMe('custom-wave-aud-' + p, p)">
<v-icon dark>mdi-pause</v-icon>
</v-avatar>
</div>
data: () => ({
is_played: null
}),
methods: {
playMe(className, index) {
if (this.is_played !== null) {
// pause the currently playing one first
this.pauseMe(className, this.is_played);
}
this.is_played = index;
document.getElementsByClassName(className)[0].play();
},
pauseMe(className, index) {
this.is_played = null;
document.getElementsByClassName(className)[0].pause();
}
}
Note: the above code assumes you don't want more than one of the wavs to play at one time. If you do want to allow playing multiple at the same time, you'll have to change is_playing to an array and change the condition to check if current index is contained in the array. Obviously, you'll want to add/remove the index to the array when playing/pausing.
If you need more help, please provide a runnable minimal reproducible example.
I am working on a personal project where I need to get status of all devices within a unit. This status along with device name is returned in array from function deviceStatus(). If all the devices are ON, the home icon beside unit.id would turn green. If all devices within that unit are OFF, the home icon would turn red. This code works very well as shown below.
I am looking for help to display array returned from deviceStatus() as text box pop up when mouse hovers over the 'home' icon. I am very confused about mouseover event, any help would be highly appreciated.
<template>
<div>
<div class="d-flex flex-row align-items-center py-2 px-2">
<h1 class="display-1 unit-status m-0">{{ unit ? unit.id : null }}</h1>
<font-awesome-icon
icon="home"
:style="deviceStyling"
class="tab-icon mx-2"
size="lg"
id="unit-info"/>
</div>
</template>
<script>
export default {
computed: {
deviceStatus() {
const device = this.unitDvc(this.currentSite.id);
return device.map(dv => {
const status = this.deviceStat(dv.device_id);
return { name: dv.name, status };
});
},
deviceStyling() {
var turnedOn = null;
var turnedOff = null;
for (var i = 0; i < this.deviceStatus.length; i++) {
if (this.deviceStatus[i]['state'] == 'on') {
turnedOn = turnedOn + 1;
} else {
turnedOff = turnedOff + 1;
}
}
if (turnedOn == this.deviceStatus.length) {
return { 'color': `green` };
} else if (turnedOff == this.deviceStatus.length) {
return { 'color': `red` };
}
}
},
</script>
Be sure to have installed the v-tooltip dependency
npm install --save v-tooltip
and added to your app
import Vue from 'vue'
import VTooltip from 'v-tooltip'
Vue.use(VTooltip)
Then you can add the directive to your component:
<font-awesome-icon
v-tooltip="'Status is ' + deviceStatus.name"
icon="home"
:style="deviceStyling"
class="tab-icon mx-2"
size="lg"
id="unit-info"/>
As you mentioned you are using HTML and JavaScript, You could use the simple attribute of HTML i.e TITLE. It works simply as mouseover on that.
More Info: Title - MDN
I want to dynamically control the visibility of the (<) and (>) arrows in the Vuetify carousel component.
For example, so that the final right arrow on the last item disappears, or so that I can use internal buttons or other interactivity within the carousel-item content to replace the buttons dynamically. (I know the continuous prop can do the simple end case).
The documentation for the next-icon and prev-icon prop is bool or string and the default says $next.
Name next-icon
Type boolean | string
Default $next
Description Icon used for the "next" button if show-arrows is true
I can make the icon button disappear by setting it to false, but true doesn't make it reappear.
I'm guessing the string value is the icon name (like md-arrow-right?) but the documentation doesn't say what the default is, and that doesn't work. I'm guessing that "off" is setting the prop to false and "on" is restoring it to the icon name.
I also don't understand what $next means, and this isn't explained in the page. It errors if you use that as a value. Everything else seems to evaluate to false.
I'm guessing it's something like this:
<template>
<v-carousel v-model="stepNo" :show-arrows="show.arrows" :next-icon="show.nextArrow" height="auto" light>
<!-- ... -->
</template>
<script>
export default {
data: () => {
return {
stepNo: 0,
show: {
arrows: true,
nextArrow: "md-arrow-right",
},
}
},
watch: {
stepNo: function(newStep, oldStep) {
// some logic here, for example
this.nextArrow = (newStep === 4) ? "md-arrow-right" : false;
},
},
//...
}
</script>
UPDATE
One of my mistakes was md-arrow-right should be mdi-arrow-right (missing the i), or actually mdi-chevron-right as noted by tony19. So I can now set it to a literal icon OK.
But setting it to $next or $prev still doesn't work - it displays either nothing, and empty circle, or a $ sign which is actually the word $next. And this seems to "break" the binding and setting it to a literal icon after this, fails until reloading the page.
<i aria-hidden="true" class="v-icon notranslate material-icons theme--light" style="font-size: 36px;">$next</i>
I think that you can achieve the behavior you wanted without relying on documentation if it doesn't provide what you need.
Just inspect the left and right arrow of the carousel component and get the DOM Node by selector.
Then you are ready to do what you want with the elements.
For exemple:
const nextButton = document.querySelector('.v-window__next button');
const prevButton = document.querySelector('.v-window__prev button');
(Maybe instead of document you can use the $el inside your component)
Now you can do whatever you want with your elements.
To show/hide dynamically:
nextButton.style.display = 'None'; // Hide
nextButton.style.display = 'Block'; // Show
To navigate:
nextButton.click(); // Go next.
prevButton.click(); // Go prev.
Vue is just JavaScript at the end, no magic ;)
BTW, you could try this directly in the browser console on the link you provided for the carousel.
The icon visibility should be restored when setting it to $next (as seen in demo code snippet below).
About $next...
For all icons in the framework, Vuetify uses v-icon to render the icon specified by name. Icon names are mapped to an iconset (default is Material Design Icons). The mapped icon names are identified by the $ prefix, and remapped during icon rendering.
For instance, the mdi preset maps $prev to mdi-chevron-left and $next to mdi-chevron-right; and the fa (Font Awesome) preset maps $prev to fas fa-chevron-left and $next to fas fa-chevron-right.
Literal icon names (without the $ prefix) could also be explicitly used. For example, you could specify mdi-arrow-expand-right instead of $next in v-icon.
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
nextIcon: '$next',
prevIcon: '$prev',
nextIconEnabled: true,
prevIconEnabled: true,
colors: [
'indigo',
'warning',
'pink darken-2',
'red lighten-1',
'deep-purple accent-4',
],
slides: [
'First',
'Second',
'Third',
'Fourth',
'Fifth',
],
}
},
watch: {
nextIconEnabled(nextIconEnabled) {
if (nextIconEnabled) {
this.nextIcon = this._lastNextIcon
} else {
this._lastNextIcon = this.nextIcon
this.nextIcon = false
}
},
prevIconEnabled(prevIconEnabled) {
if (prevIconEnabled) {
this.prevIcon = this._lastPrevIcon
} else {
this._lastPrevIcon = this.prevIcon
this.prevIcon = false
}
}
}
})
.controls {
display: flex;
flex-direction: column;
}
<script src="https://unpkg.com/vue#2.6.11/dist/vue.min.js"></script>
<script src="https://unpkg.com/vuetify#2.2.8/dist/vuetify.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/#mdi/font#4.x/css/materialdesignicons.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons">
<link rel="stylesheet" href="https://unpkg.com/vuetify#2.2.8/dist/vuetify.min.css">
<div id="app">
<v-app id="inspire">
<div class="controls">
<label>Toggle next-icon
<input type="checkbox" v-model="nextIconEnabled">
</label>
<label>next-icon:
<input v-model="nextIcon" placeholder="icon name"/>
</label>
<label>Toggle prev-icon
<input type="checkbox" v-model="prevIconEnabled">
</label>
<label>prev-icon:
<input v-model="prevIcon" placeholder="icon name"/>
</label>
</div>
<v-carousel
height="400"
hide-delimiter-background
:prev-icon="prevIcon"
:next-icon="nextIcon"
>
<v-carousel-item
v-for="(slide, i) in slides"
:key="i"
>
<v-sheet
:color="colors[i]"
height="100%"
>
<v-row
class="fill-height"
align="center"
justify="center"
>
<div class="display-3">{{ slide }} Slide</div>
</v-row>
</v-sheet>
</v-carousel-item>
</v-carousel>
</v-app>
</div>
A simple typo in the icon name:
nextArrow: "md-arrow-right",
should be
nextArrow: "mdi-arrow-right",
I keep making this mistake because I get the icon names by searching https://materialdesignicons.com/ where the icon names do not have the mdi- prefix and I so often get it wrong when manually adding md- for just material design.
There some ways to have more control over the carousel component
To programaticaly control if the arrows will be showed, you can delegate this to a variable
continuous=false will do the job hiding the arrows on the begining/end of the elements list
And to determine wich element will be active, you can use v-model
<v-carousel
:show-arrows=arrows
:progress=false
:continuous=false
v-model="item"
hide-delimiter-background
>
<v-carousel-item
v-for="n in 15"
:key="n"
>
<v-card>
{{item}}
<v-btn
text
#click="nextItem"
>
Next Item
</v-btn>
<v-btn
text
#click="showHideArrows"
>
showHideArrows
</v-btn>
</v-card>
</v-carousel-item>
</v-carousel>
nextItem(): will change the current active item
showHideArrows(): will toggle the arrow's state
data: () => ({
arrows: false,
item: 0,
}),
methods: {
nextItem() {
console.log('next');
this.item += 1;
},
showHideArrows() {
this.arrows = !this.arrows;
console.log(this.arrows);
},
},