I'm trying to create a floating action button by using vuetify -vspeed dial. I created a logic to style my button whenever it's clicked and it's working perfect, it collapses and expands whenever i click on it. However, if i try to click the focus area of the action buttons, it breaks it and close the buttons. How can i prevent that? When I click on button, it's fine - I use click.stop to make it persistent but if i click to the area right next to button, it closes the buttons which breaks my logic for styling. Here's my code
Test.Vue
<template>
<v-card :class="{create: backgroundColor }">
<v-speed-dial
:bottom="true"
:right="true"
:direction="direction"
:transition="transition"
fixed
>
<template v-slot:activator>
<v-btn
:class="{is_active:isActive}"
color="#C6002B"
fab
dark
#click=toggleButton
x-large
>
<v-icon>{{isActive? 'mdi-close' : 'mdi-account-circle'}}</v-icon><span>{{isActive ? "EXPANDED" : ''}}</span>
</v-btn>
</template>
<v-btn
v-if="finalProp"
:class="{alignLeft:isActive}"
fab
dark
large
#click.stop="$emit('func1')"
color="white" >
<v-icon color="#F0BE85">mdi-pencil</v-icon>
</v-btn>
<v-btn
v-if="thirdProp"
:class="{alignLeft:isActive}"
fab
dark
large
#click.stop="$emit('func2')"
color="white">
>
<v-icon color="purple">mdi-delete</v-icon>
</v-btn>
<v-btn
:class="{alignLeft:isActive}"
v-if="secondProp"
fab
dark
large
#click.stop="$emit('func3')"
color="white">
>
<v-icon color="green">mdi-plus</v-icon>
</v-btn>
<v-btn
v-if="firstProp"
:class="{alignLeft:isActive}"
fab
dark
large
#click.stop="$emit('func4')"
color="white">
>
<v-icon color="red">home</v-icon>
</v-btn>
</v-speed-dial>
</v-card>
</template>
<script>
export default {
name: 'FloatingButton',
props: {
firstProp: Boolean,
secondProp: Boolean,
thirdProp: Boolean,
finalProp: Boolean
},
data: () => ({
direction: 'top',
fab: false,
right: true,
bottom: true,
transition: 'scale-transition',
isActive: false,
backgroundColor: false,
check:true
}),
methods: {
toggleButton:function() {
this.isActive = !this.isActive
this.backgroundColor = !this.backgroundColor
}
},
}
</script>
<style scoped>
.is_active {
min-width:120px
/* width: 380px;
height: 70px;
border-radius: 36px;
margin:5px; */
}
.is_active span {
font-size: 18px;
letter-spacing: 0px;
}
.create {
min-width: 100%;
min-height: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 4;
background:rgba(0,0,0,0.4);
color:rgba(0,0,0,0.8);
}
}
</style>
App. vue
<template>
<v-app>
<Test :firstProp=a :secondProp=b :thirdProp=c :lastProp=d />
</v-app>
</template>
<script>
import Test from './components/Test'
export default {
name: 'App',
components: {
Test
},
data(){
return{
a:true,
b:true,
c:true,
d:true
}
}
};
</script>
I don't see what's wrong. Can you check this reproduction? I mostly only changed the casing of attributes on <Test> element.
Vue.component('Test', {
template: `
<v-card :class="{create: backgroundColor }">
<v-speed-dial
:bottom="true"
:right="true"
:direction="direction"
:transition="transition"
fixed
>
<template v-slot:activator>
<v-btn
:class="{is_active:isActive}"
color="#C6002B"
fab
dark
#click="toggleButton"
x-large
>
<v-icon>{{isActive? 'mdi-close' : 'mdi-account-circle'}}</v-icon>
</v-btn>
</template>
<v-btn
v-if="finalProp"
:class="{alignLeft:isActive}"
fab
dark
large
#click.stop="$emit('func1')"
color="white" >
<v-icon color="#F0BE85">mdi-pencil</v-icon>
</v-btn>
<v-btn
v-if="thirdProp"
:class="{alignLeft:isActive}"
fab
dark
large
#click.stop="$emit('func2')"
color="white">
>
<v-icon color="purple">mdi-delete</v-icon>
</v-btn>
<v-btn
:class="{alignLeft:isActive}"
v-if="secondProp"
fab
dark
large
#click.stop="$emit('func3')"
color="white">
>
<v-icon color="green">mdi-plus</v-icon>
</v-btn>
<v-btn
v-if="firstProp"
:class="{alignLeft:isActive}"
fab
dark
large
#click.stop="$emit('func4')"
color="white">
>
<v-icon color="red">home</v-icon>
</v-btn>
</v-speed-dial>
</v-card>
`,
props: {
firstProp: Boolean,
secondProp: Boolean,
thirdProp: Boolean,
finalProp: Boolean
},
data: () => ({
direction: 'top',
fab: false,
right: true,
bottom: true,
transition: 'scale-transition',
isActive: false,
backgroundColor: false,
check:true
}),
methods: {
toggleButton: function() {
this.isActive = !this.isActive
this.backgroundColor = !this.backgroundColor
}
},
})
Vue.config.productionTip = false
new Vue({
el: '#app',
vuetify: new Vuetify(),
data(){
return{
a:true,
b:true,
c:true,
d:true
}
}
});
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<style scoped>
.is_active {
/*min-width:120px
width: 380px;
height: 70px;
border-radius: 36px;
margin:5px; */
}
.is_active span {
font-size: 18px;
letter-spacing: 0px;
}
.create {
min-width: 100%;
min-height: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 4;
background:rgba(0,0,0,0.4);
color:rgba(0,0,0,0.8);
}
}
</style>
</head>
<body>
<div id="app">
<v-app>
<Test :first-prop="a" :second-prop="b" :third-prop="c" :last-prop="d" />
</v-app>
</div>
</body>
</html>
Related
I started a fresh vue3 project with vuetify3 via vue cli.
$ vue create playground-vuelidate
$ cd playground-vuelidate
$ code .
$ vue add vuetify
$ npm run serve
FormInput.vue
<template>
<v-form>
<v-container>
<v-row no-gutters>
<v-col cols="12" sm="12">
<v-combobox :items="select1" label="Select1" chips></v-combobox>
</v-col>
<v-col cols="12" sm="12">
<v-text-field
outline
v-model="input1"
:rules="rules"
label="Input1"
></v-text-field>
</v-col>
<v-col cols="12" sm="12">
<v-text-field
outline
v-model="input2"
:rules="rules"
label="Input2"
></v-text-field>
</v-col>
<v-col cols="12" sm="12">
<v-text-field outline v-model="input3" label="Input3"></v-text-field>
<v-textarea
v-model="v$.textarea1.$model"
:class="status(v$.textarea1)"
label="textarea1"
></v-textarea>
<pre>{{ $v }}</pre>
</v-col>
</v-row>
</v-container>
</v-form>
</template>
<script>
import { maxLength, required } from "#vuelidate/validators";
import { useVuelidate } from "#vuelidate/core";
export default {
name: "UsersPage",
data: () => ({
select1: ["Foo", "Bar", "Fizz", "Buzz"],
input1: "",
input2: "222",
input3: "333",
textarea1: "12345678910",
rules: [
(value) => !!value || "Required.",
(value) => (value || "").length <= 20 || "Max 20 characters",
],
}),
setup: () => ({ v$: useVuelidate() }),
validations() {
return {
textarea1: {
required,
minlength: maxLength(20),
},
};
},
methods: {
status(validation) {
return {
error: validation.$error,
dirty: validation.$dirty,
};
},
},
};
</script>
<style>
input {
border: 1px solid silver;
border-radius: 4px;
background: white;
padding: 5px 10px;
}
.dirty {
border-color: #5a5;
background: #efe;
}
.dirty:focus {
outline-color: #8e8;
}
.error {
border-color: red;
background: #fdd;
}
.error:focus {
outline-color: #f99;
}
</style>
App.vue
<template>
<v-card class="mx-auto" max-width="1000">
<FormInput />
<v-card-actions>
<v-btn text color="gray accent-4"> Cancel </v-btn>
<v-btn color="primary"> Submit </v-btn>
</v-card-actions>
</v-card>
</template>
<script>
import FormInput from "./components/FormInput.vue";
export default {
name: "App",
components: {
FormInput,
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
It yields this result
It supposes to look like this.
Official Doc
Notes
I have a select, inputs, and textarea all 3 of them the outline CSS doesn't seem to work or applied. Is it a known Vuetify bug ?
Should be
<v-text-field
outlined
></v-text-field>
not
<v-text-field
outline
></v-text-field>
This is based from vuetify for vue3
https://next.vuetifyjs.com/en/components/text-fields/
I'm facing a problem to prevent the toggle for the v-switch and show an alert message. Here is
Codepen example
If I add CSS to prevent the toggle, then click event is not working
.v-input--switch {
pointer-events: none;
}
Also, if I add disabled prop to switch, then click event is not working
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
switch1: true,
snackbar: false
}
},
methods:{
showALert(){
this.snackbar = true;
}
}
})
.v-input--switch .v-input--switch__thumb {
color: black !important;
}
.v-input--switch .v-input--switch__thumb.primary--text {
color: black !important;
}
.v-input--switch .v-input--switch__track.primary--text {
color: rgba(0, 0, 0, 0.38) !important;
}
.v-input--switch.v-input--is-disabled:not(.v-input--is-dirty) .v-input--switch__thumb {
color: black !important;
}
.v-input--switch .v-input--switch__thumb:after {
content: "£";
color: #fff;
}
//.v-input--switch {
// pointer-events: none;
//}
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#5.x/css/materialdesignicons.min.css" rel="stylesheet"/>
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.3.14/dist/vuetify.min.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.3.14/dist/vuetify.min.js"></script>
<div id="app">
<v-app id="inspire">
<v-container class="px-0" fluid>
<v-switch :ripple="false" flat #change="showALert"></v-switch>
<v-snackbar v-model="snackbar" bottom right>
This has been set to Historical Mode
<template v-slot:action="{ attrs }">
<v-icon v-bind="attrs" color="white" small #click="snackbar = false">
mdi-close
</v-icon>
</template>
</v-snackbar>
</v-container>
</v-app>
</div>
Can someone help me with this? Any solutions would be appreciated.
You can use the readonly prop of the v-switch which prevents toggling of the switch when clicked. You can also add #click directly on v-switch to handle whatever behavior you want to happen when the switch is clicked like showing an alert or validating a value before toggling the switch programmatically.
<v-switch #click="showALert" readonly></v-switch>
I am wanting to use the vuetify speeddial component in my app, but I am getting wierd positioning errors, what I am wanting is the button to be clicked and then the additional options slide to the left of it,
As you can see from the image above they are positioned on the left but far to far from the original "parent" button, I have attemped to make a codesandbox but the results are inconclusive.
https://codesandbox.io/s/vuetify-playground-forked-gwtkc?file=/src/components/SpeedDial.vue
And finally here is my component code,
<template>
<v-speed-dial
v-model="fab"
direction="left"
transition="slide-x-reverse-transition"
>
<template v-slot:activator>
<v-btn
v-model="fab"
color="blue darken-2"
dark
fab
>
<v-icon v-if="fab">mdi-close</v-icon>
<v-icon v-else>mdi-account-circle</v-icon>
</v-btn>
</template>
<v-btn fab dark small color="green">
<v-icon>mdi-pencil</v-icon>
</v-btn>
<v-btn fab dark small color="indigo">
<v-icon>mdi-plus</v-icon>
</v-btn>
<v-btn fab dark small color="red">
<v-icon>mdi-delete</v-icon>
</v-btn>
</v-speed-dial>
</template>
<script>
export default {
name: 'QuickSpeedDial',
data() {
return {
fab: false,
tabs: null
}
},
computed: {
activeFab () {
switch (this.tabs) {
case 'one': return { class: 'purple', icon: 'account_circle' }
case 'two': return { class: 'red', icon: 'edit' }
case 'three': return { class: 'green', icon: 'keyboard_arrow_up' }
default: return {}
}
},
}
}
</script>
You need to add a style as it says on the official website.
...
<style>
.v-speed-dial {
position: absolute;
}
.v-btn--floating {
position: relative;
}
</style>
...
I have a problem related to Vue.js probs (I guess). I have a chart which fetches data in the created() from an API which looks like this:
<template>
<div>
<highcharts if="data.length > 0" :options="chartOptions" ></highcharts>
</div>
</template>
<script>
import { Chart } from "highcharts-vue";
import Highcharts from "highcharts";
import exportingInit from "highcharts/modules/exporting";
exportingInit(Highcharts);
export default {
props: {
partsdata: {
type: Array
}
},
created() {
this.$http.get('http://127.0.0.1:5000/data/')
.then(response => {
var result = JSON.parse(response.data)
return result
}).then(data => {
var result = data.Fare
console.log(result) // eslint-disable-line no-console
this.chartOptions.series[0].data = [] // Array wieder zurücksetzen
for(var key in result) {
this.chartOptions.series[0].data.push(result[key]) // eslint-disable-line no-console
}
})
},
components: {
highcharts: Chart
},
data() {
return {
data: [],
chartOptions: {
title: {
text: 'Title aligned left',
align: 'left',
x: 0
},
series: [{
type: "column",
data: [] // sample data
}]
}
}
}
};
</script>
<style scoped>
.btn-primary {
padding: 10px 20px;
background: white;
color: black;
margin-top: 15px;
margin-bottom: 15px;
border-radius: 5px;
}
</style>
This is my parent component, where I use my Charts, which works fine.
<template>
<v-container grid-list-md text-xs-center>
<v-layout row wrap>
<v-flex xs6>
<v-card dark color="white">
<v-card-text class="px-0">
<Highcharts></Highcharts>
</v-card-text>
</v-card>
</v-flex>
<v-flex xs6>
<v-card dark color="white">
<v-card-text class="px-0">
<Highcharts></Highcharts>
</v-card-text>
</v-card>
</v-flex>
<v-flex xs6>
<v-card dark color="white">
<v-card-text class="px-0">
<Highcharts></Highcharts>
</v-card-text>
</v-card>
</v-flex>
<v-flex xs6>
<v-card dark color="white">
<v-card-text class="px-0">
<Highcharts></Highcharts>
</v-card-text>
</v-card>
</v-flex>
</v-layout>
</v-container>
</template>
<script>
import Highcharts from "./Header.vue"
export default {
components: {
Highcharts: Highcharts
}
}
</script>
<style scoped>
</style>
However, what I want is to fetch the data from different Endpoints without creating a ton of new components. Is there a way to use the created() lifecycle hook as props or are there any other clever solutions how to archieve this?
I need a way to use list of v-card from Vuetify like Bootstrap Card Columns
Possible solution:
Pardon me, I couldn't plunkr the code :(
This is what I did.
Update: Unfortunately this messes up with the v-ripple directive
<template>
<v-container grid-list-md>
<div class="v-card-columns">
<v-card tile v-for="post in posts" :key="post.id">
<v-card-title primary-title>
<h3 headline>
{{post.title}}
</h3>
</v-card-title>
<v-card-text>
{{post.body}}
</v-card-text>
</v-card>
</div>
</v-container>
</template>
<script>
import axios from "axios";
export default {
name: "All",
data() {
return {
posts: []
}
},
mounted() {
axios
.get('https://jsonplaceholder.typicode.com/posts')
.then(res => {
this.posts = res.data
})
}
}
</script>
<style scoped>
.v-card-columns .v-card {
margin-bottom: 0.75rem;
}
#media (min-width: 576px) {
.v-card-columns {
-webkit-column-count: 3;
-moz-column-count: 3;
column-count: 3;
-webkit-column-gap: 1.25rem;
-moz-column-gap: 1.25rem;
column-gap: 1.25rem;
orphans: 1;
widows: 1;
}
.v-card-columns .v-card {
display: inline-block;
width: 100%;
}
}
</style>
Pardon me, I couldn't plunkr the code :(
This is what I did.
Update: Unfortunately this messes up with the v-ripple directive
<template>
<v-container grid-list-md>
<div class="v-card-columns">
<v-card tile v-for="post in posts" :key="post.id">
<v-card-title primary-title>
<h3 headline>
{{post.title}}
</h3>
</v-card-title>
<v-card-text>
{{post.body}}
</v-card-text>
</v-card>
</div>
</v-container>
</template>
<script>
import axios from "axios";
export default {
name: "All",
data() {
return {
posts: []
}
},
mounted() {
axios
.get('https://jsonplaceholder.typicode.com/posts')
.then(res => {
this.posts = res.data
})
}
}
</script>
<style scoped>
.v-card-columns .v-card {
margin-bottom: 0.75rem;
}
#media (min-width: 576px) {
.v-card-columns {
-webkit-column-count: 3;
-moz-column-count: 3;
column-count: 3;
-webkit-column-gap: 1.25rem;
-moz-column-gap: 1.25rem;
column-gap: 1.25rem;
orphans: 1;
widows: 1;
}
.v-card-columns .v-card {
display: inline-block;
width: 100%;
}
}
</style>