Vuetify conditional dark theme - vuejs2

I'm using Vuetify to create my social media website. The problem that I'm facing now is that I want to use dark attribute, so user can switch between normal and dark theme. The thing is that I can't use any of Vue's conditional rendering methods, as dark is not an attribute that you can bind. Below is the part of code that you use to apply dark theme:
<v-app dark>

Yes dark or light are not attributes, they are props which can take values, in this case true or false
You can find this in vuetify's documentations.
Props are the properties which are used for communication with child components. And are prefixed with : in order to distinguish it with normal properties.
Coming to the solution.
<v-app :dark="true">
or
<v-app :dark="false">
You can replace true or false with any reactive data options or computed properties to make the theme change programmatically.

To be able to persist theme with localstorage
Inside plugins/vuetify.js or plugins/vuetify.ts add this:
export default new Vuetify({
theme: {
//
dark: localStorage.getItem('theme') === 'dark',
//
}
})
then on the button you want to switch theme apply the following function:
switchTheme() {
this.$vuetify.theme.dark = !this.$vuetify.theme.dark;
localStorage.setItem('theme', this.$vuetify.theme.dark ? 'dark' : 'light');
}

As documentation says you can just update variable this.$vuetify.theme.dark:
You can manually turn dark on and off by changing this.$vuetify.theme.dark to true or false.

If a component is used as the App:
<template>
<v-app :dark="isDark"></v-app>
</template>
<script>
export default {
data () {
return {
isDark: false,
}
},
}
</script>
It can also be called from the app instance
thru its instantiated object defined property
app.__vue__.isDark = isDark
when being instantiated like :
const app = new Vue(
{
data: () => ({
isDark: false
}),
}
)
So it can be called from other component

Related

How to do a dark mode with localStorage in Vue JS + Tailwind CSS

I do a dark mode using Vue3 + Tailwind but when I reload the page or click in some component that reload something my dark mode set's to false and the light mode appears, I don't know how I can storage my data. Here's my code:
<div class="relative flex min-h-screen" :class="isDark ? 'dark' : ''">
<button class="px-2 mb-1" #click="isDark = !isDark">
--------------------
<script>
export default {
setup(){
let isDark = ref(false)
return{
isDark,
}
}
}
</script>
obs: my dark mode appears because the property 'dark:' from tailwind css in my div's/components.
You use the localStorage API (documentation)
An implementation for you would look something like this
// Click event callback on the dark mode button
toggleDarkMode(){
this.isDark = !this.isDark;
localStorage.setItem('darkMode', this.isDark);
}
// Your setup() function
setup(){
let isDark = localStorage.getItem('darkMode') == 'true'
return{
isDark,
}
}
Note: Local storage only stores strings so your Boolean value won't be of Boolean type so compare it as string version. Someone may have a way to turn it into a boolean again but this has worked fine for my projects.
Reading the Josh answer I just edit some things like the local of the variable, for instance one variable in Vue the variable should be in data().
Soo that's the code:
// The button
<button class="px-2 mb-1" #click="toggleDarkMode">
// Click event callback on the dark mode button
methods:{
toggleDarkMode(){
this.isDark = !this.isDark;
localStorage.setItem('darkMode', this.isDark);
}
}
// Your data() function
data(){
let isDark = localStorage.getItem('darkMode') == 'true'
return{
isDark,
}
}

Vue2.js - Use a custom component in custom plugin - how to pass props?

I'm trying to make a plugin just to show a modal div with some custom personalization, so i thought of a plugin with a custom component. The idea is to call the plugin that should show the modal box (the component) but i cannot understand how to pass plugin methods props to the component.
I have this plugin
import Modal from './components/Modal'
const ModalComponent = {
install (Vue) {
Vue.component('modal', Modal)
Vue.mixin({
data () {
return {
status: null,
modalMessage: null,
show: false
}
},
component: {
Modal
},
methods: {
setMessage (status, modalMessage) {
console.log(status)
console.log(modalMessage)
this.status = status
this.modalMessage = modalMessage
this.show = true
}
}
})
}
}
export default ModalComponent
i can call the plugin in my other components in this way this.setMessage(res.status, res.message)
The component is this
<template>
<b-modal
v-model="show"
centered
cancel-disabled
:title="title"
:header-bg-variant="header_bg"
><p class="my-4">{{modalMessage}}</p>
</b-modal>
</template>
<script>
export default {
name: "modal",
data() {
return {
show: false,
title: null,
modalMessage: null,
header_bg: null
}
},
props: {
show: Boolean,
title: String,
modalMessage: String,
header_bg: String
}
}
</script>
How can i tell the component to show using props from the plugin? I miss the passage to send props to it when using the plugin methods
The problem seems to be that your plugin doesn't generate any element. You're creating a method on every component using the mixin, but this will assume that every one of your components' templates has a <Modal :status="status" :modalMessage="modalMessage" :show="true" />
I would suggest a different approach...
in your main template, add the instance of the modal. Then you can use a global state of some sort. I prefer using vuex for this, since you can use the vuex state to mange the props, and use a globally available action (commit) with the desired payload. Alternatively, you can use the vue internals to modify state, but if you're already using vuex, this likely the easiest option.

How can I add vuetify dialog into existing application?

I created a vue dialog app/component using vue cli. It consist of a sample button to be clicked on to imitate how the dialog (What I need) will be loaded when a link on the existing application is clicked. I have a couple of issues.
When using v-app it adds the application wrapper I dont need seeing as its only the dialog I want. It creates a huge whitespace not needed. If I remove it, it errors [Vuetify] Unable to locate target [data-app] and the dialog wont load when <div #click='getInformation('USA')'></div> in the existing application is used.
Tried removing v-app and just using template but continues to error. Seems I need to still specify v-app in some way. Lost here
An example on how Im trying to pull it off but not working in App.vue
<template>
<div v-resize="onResize">
<v-dialog>
<v-card>
{{ information }}
</v-card>
</v-dialog>
</div>
</template>
<script>
export default {
data() {
return {
isMobile: false,
information: []
};
},
methods: {
onResize() {
if (window.innerWidth < 425) this.isMobile = true;
else this.isMobile = false;
},
getInformatiom(country) {
axios
.get(`${api}/${country}/info`, {
headers: {
Authorization: `token`
}
})
.then(response => {
this.information = response.data.info;
});
}
}
};
main.js
import Vue from "vue";
import App from "./App.vue";
import Vuetify from "vuetify";
import "vuetify/dist/vuetify.min.css";
Vue.use(Vuetify);
Vue.config.productionTip = false;
new Vue({
render: h => h(App)
}).$mount("#app");
Dialog component is ready to go, just having so much trouble getting it to show when its being called from the existing application. Just a note, the existing application does not use Vue, its only classic asp, Im only updating the dialog on the page to look/work better using vue/vuetify. Any help would be GREATLY APPRECIATED
You NEED the v-app element with vuetify.
Try this to only use the app when showing the dialog. Then use CSS to customise the v-app.
<v-app v-if='this.information && this.information.length'>
<v-dialog>...</v-dialog>
</v-app>
I would use the max-width prop of v-dialog, make it dynamic by adding :max-width and then have that bound to a computed property which subscribes to your screen size. I would not try to control it from an external div. See here for full list of sizing options
https://vuetifyjs.com/en/components/dialogs

Using paperjs on a vuejs project based on SFC

I have a vuejs project based on single file components. I want to add a canvas and dinamically draw things with paperjs, based on my component data. What is the proper way to do it?
Self response. A working SFC.
<template>
<canvas resize id="main-canvas">{{circle_diameter}}</canvas>
</template>
<script>
import paper from 'paper'
export default {
data: () => ({ x: 20, y: 20 }),
props: ['circle_diameter'],
methods: {
updateDrawing() {
paper.setup(document.getElementById('main-canvas'))
// Now, draw your things based on component state.
const point = new paper.Point(this.x, this.y)
const circle = new paper.Path.Circle(point, this.circle_diameter/2)
circle.fillColor = 'grey'
circle.strokeColor = 'black'
},
},
updated() {
this.updateDrawing()
},
}
</script>
EDIT
Since paperjs will render outside vue scope, drawing is not reactive until you place {{circle_diameter}} into the canvas html tags. This way, you force Vue to call update() each time a prop changes.

Nuxt/Vue: How to position custom loading component

I use nuxt and I followed this guide to make custom loading component:
https://nuxtjs.org/api/configuration-loading/#use-a-custom-loading-component
This does work, but the loader is in the same position as the original nuxt loader bar:
You can see, I really added a very big and very simple loader with a red div.
At the bottom you can see my headebar (black bar with letter «s»)
so everything is moved downwards.
What I would like to achieve is that the loader takes the position of the page content instead to keep the header and the footer in place.
Right now, it all shifts down to make space for the custom loader on top.
Is there a solution for that?
Thanks in advance
Cheers
J
Well I built a big workaround:
I set a loading component in my nuxt.config:
loading: '~/components/Loading/Loading.vue',
This component should never display anything, but has these two methods:
methods: {
start () {
this.$store.commit('ui/SHOW_LOADER')
},
finish () {
this.$store.commit('ui/HIDE_LOADER')
}
}
With those I mutate the vuex ui store.
export const mutations = {
SHOW_LOADER (state) {
state.nuxtLoader = true
},
HIDE_LOADER (state) {
state.nuxtLoader = false
}
}
nuxtLoader: false, <=> nuxtLoader: true,
Within this store I set a getter:
export const getters = {
nuxtLoader: state => state.nuxtLoader
}
And on my layouts I display <nuxt /> or the CustomLoader component (which holds an animated SVG) depending on the ui store nuxtLoader property.
<template>
<div>
<HeaderBar />
<main id="main" class="Layout__content">
<CustomLoader v-if="nuxtLoader" />
<nuxt v-else />
</main>
<Footer />
</div>
</template>
Now I give the user a feedback with a custom loader placed between headerbar and footer. 💪
I am still open for less work-aroundy and slicker solutions.
Cheers
J