How to change body style from vue.obserable store value - javascript

It is my first time to work with Vue or Web. I have been to working with C to write firmware.
Depends on sidebar state, I would like to change padding-right value of body in App.vue.
whenever button is pushed, isNavOpen value is changed
import Vue from "vue";
export const store = Vue.observable({
isNavOpen: false
});
export const getters = {
getNavOpen: () => store.isNavOpen
}
export const mutations = {
setIsNavOpen(yesno) {
store.isNavOpen = yesno;
},
toggleNav() {
store.isNavOpen = !store.isNavOpen;
}
};
so I would like to change padding-right value of body in App.vue
<template>
<div id="app">
<nav class="main-nav">
<div class="logo">my.company</div>
<Burger></Burger>
</nav>
<Sidebar>
<ul class="sidebar-panel-nav">
<li>
Home
</li>
<li>
About
</li>
<li>
Contact
</li>
</ul>
</Sidebar>
<router-view />
</div>
</template>
<script>
import Burger from "./components/Menu/Burger.vue";
import Sidebar from "./components/Menu/Sidebar.vue";
import { store, getters, mutations } from '#/store.js'
export default {
name: "app",
components: {
Burger,
Sidebar
},
data:{
return: {
isSidebarOpen: this.$getters.getNavOpen()
}
}
};
</script>
<style>
html {
height: 100%;
overflow: hidden;
}
body {
border: 0;
margin: 0;
padding-left: 350px;
font-family: "Lato";
height: 100%;
background: rgb(101, 31, 87);
background: linear-gradient(
45deg,
rgba(101, 31, 87, 1) 0%,
rgba(225, 113, 87, 1) 48%,
rgba(249, 248, 113, 1) 100%
);
}
</style>
I read pages. However it is so hard to combine these codes for me
change style
vue sidebar tutorial
or Is any way to implmement to change padding by sidebar states?

Some thing might be missing in your second block but i assume that you successfully can use true or false value as isSideBarOpen. All you need to do is to use style binding(you can also use class binding but you need to define specific class for padding right) Here is what you need:
<div id="app" :style="(isSideBarOpen) ? 'padding-right: 10px;' : ''">
/* code */
</div>

Related

React fill SideNavBar from top to bottom with other components on the page

I want my sidenavbar to continue over the whole page but when i add the other
sections(home, contact & projects) which are just regular functional components
with a div and h1, they create their own space on the page. is there a better
solution to creating different sections on a page? i have tried rendering the components
from index and app.js but without success, i am currently rendering SidenavBar
from index.js and my sections are getting rendered from app.js.
import React from "react";
import "../Section.css";
function HomeSection() {
return (
<div className="Section" id="Home">
HomeSection
<h1>Home</h1>
</div>
);
}
export default HomeSection;
here is an example of the sections, the section.css only centers the text on the page.
function SideNavBar() {
const [titleActive, setTitleActive] = useState(HomeSection);
return (
<div className="SideNavBar">
<Stickybox>
<ul className="SideBarList">
{SideBarInfo.map((info, key) => {
return (
<li
key={key}
className="rad"
onClick={() => {
console.log(info.title + " clicked");
setTitleActive(info.title);
}}
id={titleActive === info.title ? "active" : ""}
>
<Link
onClick={() => {
console.log(info.title + " clicked");
setTitleActive(info.title);
}}
id={titleActive === info.title ? "active" : ""}
activeClass="active"
to={info.title}
spy={true}
smooth={true}
offset={50}
duration={500}
>
<div id="title">{info.title}</div>
/Link>
<Link
activeClass="active"
to={info.title}
spy={true}
smooth={true}
offset={50}
duration={500}
>
<div id="bild">{info.bild}</div>
</Link>
</li>
);
})}
</ul>
</Stickybox>
</div>
);
}
export default SideNavBar;
// this is the css for sidebar
.SideNavBar {
width: 250px;
min-height: 5000px;
background-color: rgb(47, 167, 223);
height: 100vh;
}
this is my sidenavbar component.
My sidenavbar works as intended if i remove the sections, i have tried setting a max width and transparent background for the sections so they dont overwrite the sidenavbar but they still overwrite it. what is the correct way to create different sections?
i added a red background for the home section so its easier to see what its doing.
You probably want to do something like this for your sidebar to fix it on the side and have it take up the full screen.
This will fix the sidebar in a position on the screen (0px away from the top and 0px away from the bottom).
You then define the width and you have a fixed sidebar!
Hope that helps!
.sidebar {
top: 0px;
bottom: 0px;
width: 200px;
position: fixed;
background-color: blue;
}
<div class='sidebar'></div>

How to render SVG images inside a div

I'm new to react and i hope someone can help me with this. I've searched everywhere for a solution to my problem with no luck.
Basically i want to render an array of SVG images inside a div as a backgroundImage: url().
I've managed to render my array with math.random but i want the SVG images to render in same order as in the array.
This is my code so far:
import './ListView.css';
import Green from '../Assets/ListCard/Green.svg';
import Brown from '../Assets/ListCard/Brown.svg';
import Orange from '../Assets/ListCard/Orange.svg';
import Mail from '../Assets/Icons/Mail.svg'
import Phone from '../Assets/Icons/Phone.svg'
function ListView({ userData}) {
const cardImages = [Green, Brown, Orange]; /// Array
const renderList = cardImages.map(function(image) { /// Tried to map through the array with -
return image; /// no luck
})
/// This Math.radom method works, but not the solution i want
const randomItem = cardImages[Math.floor(Math.random()*cardImages.length)];
return ( /// Below inside the div is where i want to render the images.
<div className="list-card" style={{backgroundImage: `url(${renderList})`}}>
<div className="list-card-wrapper">
<div className="list-user-image"><img src={userData.picture.medium} alt=""/></div>
<div className="list-user-info">
<div className="list-user-name">{userData.name.first} {userData.name.last}</div>
<div className="list-user-location">{userData.location.city}</div>
</div>
<div className="list-user-contact">
<img height={19} src={Mail} alt="svg"/>
<img height={19} src={Phone} alt="svg"/>
</div>
</div>
</div>
)
}
export default ListView```
you will need import image and bind it like below:
import logo from './logo.svg';
function App() {
return (
<div className="App">
<img src={logo} className="App-logo" alt="logo" />
</div>
);
}
export default App;
This is what you might be looking for:
import "./styles.css";
export default function App() {
const items = [
{ name: "first" },
{ name: "second" },
{ name: "third" },
{ name: "fourth" },
{ name: "fifth" },
{ name: "sixth" }
];
const colorArray = ["green", "brown", "orange"];
return (
<div className="App">
{items.map((item, index) => {
const classColorIndex = index % 3;
return (
<div
className={`list-card ${colorArray[classColorIndex]}`}
key={index}
>
<p>{item.name}</p>
</div>
);
})}
</div>
);
}
The main concept behind this is, that you have to focus on the index of the item and check if it the first, second, or third item (I am considering it 3 because you have 3 colors in the array). Now, according to index number, you need to add a class to that div, and using CSS, you could provide background to each div according to that array.
In this sample, I have used plain background color, but I have commented how you could use svg as well. on APP.css, and here is the css
.App {
font-family: sans-serif;
text-align: center;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.list-card {
width: 50px;
height: 50px;
display: flex;
justify-content: center;
flex-basis: 50%;
}
.list-card.green {
background: green;
/* background:url('../../Green.svg'); */
}
.list-card.brown {
background: brown;
}
.list-card.orange {
background: orange;
}
and here is the sandbox link:
https://codesandbox.io/s/stack-overflos-sample-z0yxyk?file=/src/App.js
On this example, you can see background is assigned, exactly to the array.

In Vuejs3 how to make the sticky header component change background colour when intersecting with other components?

I am just wondering, how to structure my code to make my sticky <Header /> component change its background-color when intersecting with other components (in this case with <Hero /> and <Footer />.
To make things more complicated, I use vue-router.
Link to my CodeSandbox
The structure is as follows:
App.vue
<template>
<ul class="nav">
<li>
<router-link to="/">Home</router-link>
</li>
<li>
<router-link to="/about">About</router-link>
</li>
</ul>
<Header />
<router-view />
</template>
views/Home.vue
<template>
<Child1 />
<Child2 />
<Hero />
<Child3 />
<Footer />
</template>
<script>
import Hero from "#/components/Hero";
import Child1 from "#/components/Child1";
import Child2 from "#/components/Child2";
import Child3 from "#/components/Child3";
import Footer from "#/components/Footer";
export default {
components: {
Hero,
Child1,
Child2,
Child3,
Footer,
},
};
</script>
When I follow some tutorials on yt, the authors use the IntersectionObserver, so I wanted to give it a try:
Header.vue
<template>
<section
class="header"
>
<div class="header__title"
:class="{
header__scroll: isContrastActive,
}"
>
I am an awesome header that is changing it's background colour <br />
when intersecting with other components
</div>
</section>
</template>
<script>
export default {
data() {
return {
isContrastActive: false,
observer: null
};
},
methods: {
activateObserver() {
this.observer = new IntersectionObserver(
([entry]) => {
console.log("isIntersecting: ", entry.isIntersecting);
if (!entry.isIntersecting) {
this.isContrastActive = true;
} else {
this.isContrastActive = false;
}
},
{ rootMargin: "-5% 0px 0px 0px" }
);
document
.querySelectorAll(".observer")
.forEach((el) => this.observer.observe(el));
},
},
mounted() {
this.activateObserver();
},
};
</script>
<style scoped>
.header {
position: sticky;
top: 0;
width: 100vw;
height: auto;
z-index: 10;
}
.header__title {
background-color: yellow;
color: black;
padding: 8px 4px;
text-align: center;
}
.header__scroll {
background-color: orange;
}
</style>
Also added the class="observer" to the <Hero /> and <Footer /> components.
But it doesn't seem to work.
I have read, that in vue the use of querySelector is considered to be an antipattern.
If this is the case then how I could structure my code to get what I want?
Thanks in advance
EDIT1:
I noticed, that if I change the text in the <Header /> component and save then everything seems to work just fine.
But on page refresh, it is still not working.
Think that the querySelectorAll could be the reason.
SOLUTION 1:
The most obvious solution is of course:
mounted() {
setTimeout(() => {
this.activateObserver()
}, 500)
},
but it ain't an elegant solution :/.
The code you're using makes the assumption all observed elements are already in DOM.
You need to make the observable available to all your pages (use the preferred state management solution).
Your activateObserver should become:
createObserver() {
someStore().observer = new IntersectionObserver(
([entry]) => {
if (!entry.isIntersecting) {
this.isContrastActive = true;
} else {
this.isContrastActive = false;
}
},
{ rootMargin: "-5% 0px 0px 0px" }
);
}
Then, in any page which has observed elements, use template refs to get all elements you want observed.
In onMounted you start observing them, in onBeforeUnmount you stop observing them.
const observed = ref([]);
onMounted(() => {
observed.value.forEach(someStore().observer.observe)
})
onBeforeUnmount(() => {
observed.value.forEach(someStore().observer.unobserve)
})
How you select your elements using template refs is irrelevant for this answer. The point is you need to start observing the DOM elements referenced after the component has been mounted and unobserve them before unmount.
Note: in theory, it is possible that any of your views is rendered before the observer has been added to the store. (In practice it's a rare case, so you probably don't need this).
However, if that's the case, you want to create a watcher for the observer in your pages and start observing only after you have the observer, which might be after the page has been mounted.
The principle is simple: you need both observer and observed for this to work. observer is available after it is added to store, observed are available after onMounted until onBeforeUnmount in any of the pages containing them.

How do I create a splash screen in VueJS?

I am trying to create a splash screen (loading-screen) in Vue JS that after a few seconds fades away, revealing my defaut view. I have tried several approaches but just can't get any to work. The closest is this example on CodePen But ideally the component wouldn't be inside main.js and instead inside its own component. Despite that the below code won't work.
My main.js is as below:
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
Vue.config.productionTip = false;
// FILTERS
Vue.filter('snippet', function(value) {
return value.slice(0,100);
});
Vue.component('loading-screen', {
template: '<div id="loading">Loading...</div>'
})
new Vue({
router,
store,
render: h => h(App),
data: {
isLoading: true
},
mounted () {
setTimeout(() => {
this.isLoading = false
}, 3000)
}
}).$mount("#app");
and my App.vue is as follows
<template>
<div id="app">
<loading-screen v-if="isLoading"></loading-screen>
<Top/>
<router-view/>
<PrimaryAppNav/>
</div>
</template>
<script>
import Top from './components/Top.vue'
import PrimaryAppNav from './components/PrimaryAppNav.vue'
export default {
name: 'app',
components: {
Top,
PrimaryAppNav
}
}
</script>
A LoadingScreen.vue component could look like this:
<template>
<div :class="{ loader: true, fadeout: !isLoading }">
Loading ...
</div>
</template>
<script>
export default {
name: "LoadingScreen",
props: ["isLoading"]
};
</script>
<style>
.loader {
background-color: #63ab97;
bottom: 0;
color: white;
display: block;
font-size: 32px;
left: 0;
overflow: hidden;
padding-top: 10vh;
position: fixed;
right: 0;
text-align: center;
top: 0;
}
.fadeout {
animation: fadeout 2s forwards;
}
#keyframes fadeout {
to {
opacity: 0;
visibility: hidden;
}
}
</style>
Be aware that the loader needs to be aware if loading is done to be able to fade out. You need to check that in your App as well, so it's not showing while it still needs to prepare data. Otherwise, information from the app part in the background could be leaking (for example scrollbars might be visible on the LoadingScreen). So App.vue could have a template like this:
<template>
<div id="app">
<LoadingScreen :isLoading="isLoading" />
<div v-if="!isLoading">
...your main content here...
</div>
</div>
</template>
If you want to have the LoadingScreen divs to disappear completely, you need to manage the state of the fadeout animation in the App.vue itself, making it a bit more complicated (I'd probably use two props for LoadingScreen then: isLoading and fadeout, where fadeout is a callback that gets called in LoadingScreen as soon as the fadeout animation is complete).
I have prepared a codesandbox for you with the state management inside the LoadingScreen.
This is a working App.vue with a splash screen:
<template>
<div id="app">
<v-app :light="!nav.dark" :dark="nav.dark">
<transition name="slide-fade" mode="out-in">
<router-view></router-view>
</transition>
</v-app>
<div v-if="loading" style="position:absolute; width: 100%; height:100%; top:0; left:0; z-index:10000; background-color:white">
<div style="margin-left: auto; margin-right: auto">
Loading...
</div>
</div>
</div>
</template>
<script>
export default {
name: "app",
data: () => ({
loading: true
}),
mounted() {
setTimeout(() => {
this.loading = false
}, 3000)
}
}
</script>
Note that there is a z-index trick and mounted is in App component.
You can of course create a component just for loading, so it will become:
App.vue
<template>
<div id="app">
<v-app :light="!nav.dark" :dark="nav.dark">
<transition name="slide-fade" mode="out-in">
<router-view></router-view>
</transition>
</v-app>
<loader v-if="loading"/>
</div>
</template>
<script>
import Loader from "./Loader"
export default {
name: "app",
data: () => ({
loading: true
}),
mounted() {
setTimeout(() => {
this.loading = false
}, 3000)
}
}
</script>
Loader.vue
<template>
<div style="position:absolute; width: 100%; height:100%; top:0; left:0; z-index:10000; background-color:white">
<div style="margin-left: auto; margin-right: auto">
Loading...
</div>
</div>
</template>
<script>
export default {
name: "loader"
}
</script>
After that, I strongly suggest that you use dynamic components for your router components, Top and PrimaryAppNav. Like that they will load during your splash screen. You will find how to do that very easily in my answer in this thread (only section 2 is relevant for you): here
At beginning show fullscreen splash (with class binding listens for loadedApp).
When vuejs mounted, (or your any other process completed then, change data loadedApp = true.
Then fadeoutHide style will run and hides your splash
<div class="fullscreen-splash" :class="{fadeoutHide:loadedApp}">
// splash logo etc
</div>
data() {
return {
loadedApp: false
}
},
mounted() {
this.loadedApp = true
}
.fadeoutHide {
animation: fadeoutHide .5s forwards;
}
#keyframes fadeoutHide {
to {
opacity: 0;
visibility: hidden;
}
}

Control modals using vue router

I am working in the project developed with Vue 2 with VueRouter and I am trying to work with my modals controlled by my VueRouter!
I've done the following code
Main vue component: My normal components will be loaded on the default router-view and all my modals will be loaded on the modal router-view
<div id="app">
<router-view v-if="!isLoading"></router-view>
<router-view v-if="!isLoading" name="modal"></router-view>
</div>
RoutedModals Mixing
As you can see on my beforeRouteEnter method I am checking if there is a previous "from" route (which means the user got the page navigating inside the app)... If it's I set that one as default component... if not (which means the user got directly from the URL) I set my dashboard as default and it will be opened behind my modal.
import Dashboard from 'modules/dashboard/components/Main.vue'
import { isNil } from 'lodash'
export default {
data() {
return {
canAccessDirect: true,
goBackTo: '/'
}
},
beforeRouteEnter(to, from, next) {
to.matched[0].components.default = isNil(from.matched[0]) ? Dashboard : from.matched[0].components.default
next(vm => {
if (!vm.canAccessDirect)
vm.$router.push({
name: 'dashboard.index'
})
vm.fetchRecords()
vm.goBackTo = from.path
window.jQuery(vm.$el).modal('show')
window.jQuery(vm.$el).on('hide.bs.modal', () => {
vm.$router.push(vm.goBackTo)
})
})
},
beforeRouteLeave(to, from, next) {
setTimeout(() => {
next()
}, 200)
},
methods: {
fetchRecords() {
// Do list request
}
}
}
An example of my router object: The first route will open a modal on the router-view modal and the second will open only on the default router-view
{
name: 'leads.quick-add',
path: '/leads/quick-add',
components: { modal: QuickAdd },
},
{
name: 'leads.index',
path: '/leads',
component: Main,
},
It works great! The problem comes when I access my modal URL (does not matter if it's directly or navigating) and the default component has a child component! The child component get away on that case!
There is attached some screenshots to help you out understand what happens...
Image 1
Image 2
At Image 1 we can 2 components where the number 1 is my default component on my VueRouter and the number 2 is his child!
Ar the Image 2, after clicking on the + Quotation button the modal is loaded and the component number 2 getaway!
Any ideas on how to do it keeping the others components?
Just to be clear I want to do it by routing and no calling my modal manually!
########################## Edit
I am trying to do something like that instead of check on beforeRouterEnter method:
{
name: 'leads.show.quotations.create',
path: '/leads/:id/quotations/create',
components: {
default: Show,
'default.tab': Quotations,
modal: Add
},
meta: {
requiresAuth: true
}
},
Where there is a sub-router-view but it does not work!
Thinking about possibilities I've added this issue on the github repo:
https://github.com/vuejs/vue-router/issues/1030
I did this in a project at work. It is quite simple actually, the hard part lies in mixing the jquery that you have there, and maybe the css to position a modal, since it's entry point is the router-view inside your component I recommend using the package portal-vue to move the modal content to the end of the body.
Here is a working exemple: https://codesandbox.io/s/vue-router-modal-j10t2
First create a Modal Component that receives it's child by props:
<template>
<portal to="modal">
<div class="modal-wrapper">
<div class="overlay" #click="$router.back()"></div>
<div class="modal">
<!-- you can use v-bind to pass down all props -->
<component :is="component" v-bind="$attrs"/>
</div>
</div>
</portal>
</template>
<script>
export default {
name: "Modal",
props: ["component"],
};
</script>
<style scoped>
.modal-wrapper {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
}
.modal {
position: absolute;
z-index: 1;
margin: auto;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background-color: white;
padding: 5em;
border: 1px solid #ccc;
border-radius: 1em;
box-shadow: 0 0 1em #00000033;
}
.overlay {
z-index: 1;
position: absolute;
width: 100vw;
height: 100vh;
background-color: #00000055;
}
</style>
Then you can use the props in routes to assign the content for a given route:
import Home from "./Home.vue";
import Modal from "./Modal.vue";
import Content from "./Content.vue";
const router = new VueRouter({
routes: [
{
path: "/",
component: Home,
children: [
{
path: "create",
component: Modal,
props: {
component: Content
}
}
]
}
]
});
Since the modal is a child route of '/', the Home component needs to render the router-view:
<template>
<div>
Hi i'am home
<router-view />
<router-link to="/create">open modal</router-link>
</div>
</template>
And the App.vue needs to render the portal target, so the modal goes to the end of your content (it makes positioning the modal a lot easier):
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" width="25%" />
<router-view />
<portal-target name="modal" />
</div>
</template>
<script>
export default {
name: "App",
};
</script>
And that's it

Categories

Resources