I am using react in viewport from here: https://github.com/roderickhsiao/react-in-viewport and I set up the boiler plate so that the following line of code works:
<ViewportBlock onEnterViewport={() => console.log('enter')} onLeaveViewport={() => console.log('leave')} />
Looking at console.log it is saying enter and leave where I need it too. However, I need to have it say onEnterViewport set .Header (the css className is Header) to display:none in css, and onLeaveViewport set to display:block
Edit:
Full code:
const Block = (props: { inViewport: boolean }) => {
const { inViewport, forwardedRef } = props;
const color = inViewport ? '#217ac0' : '#ff9800';
const text = inViewport ? 'In viewport' : 'Not in viewport';
return (
<div className="viewport-block" ref={forwardedRef}>
{/* <h3>{ text }</h3>
<div style={{ width: '400px', height: '300px', background: color }} /> */}
<Link to="Header" spy={true} smooth={true} offset={-100} duration={1400}><img src={arrow} alt="arrow" className={inViewport ? 'hide' : 'Header-div2-mainnav-arrow' } /></Link>
</div>
);
};
const ViewportBlock = handleViewport(Block, /** options: {}, config: {} **/);
export const Header = () => ({
componentDidMount: function() {
Events.scrollEvent.register('begin', function(to, element) {
console.log('begin', arguments);
});
Events.scrollEvent.register('end', function(to, element) {
console.log('end', arguments);
});
scrollSpy.update();
},
componentWillUnmount: function() {
Events.scrollEvent.remove('begin');
Events.scrollEvent.remove('end');
},
scrollToBottom: function() {
scroll.scrollToBottom();
},
handleSetActive: function(to) {
console.log(to);
},
render: function() {
return (
<div className="Header">
<div className="Header-div1">
{/* background image */}
<h1 className="Header-div1-number">910-910-910</h1>
<h2 className="Header-div1-email">larryslawn#gmail.com</h2>
</div>
<div className="Header-div2">
<h1 className="Header-div2-h1"><span className="Header-div2-span">Larry's </span>Lawn Mowing</h1>
<p className="Header-div2-p">No job too big or too small, we do it all </p>
<div className="Header-div2-mainnav">
<Link to="Pricing" spy={true} smooth={true} offset={-50} duration={1200}><p>Pricing</p></Link>
<Link to="Services" spy={true} smooth={true} offset={-100} duration={1200}><p className="Header-div2-mainnav-p">Services</p></Link>
<Link to="Contact" spy={true} smooth={true} offset={-100} duration={1400}><p>Contact</p></Link>
</div>
<Block />
</div>
</div>
)
}
})
Use useState to toggle a class with display: none on the Header component:
const Example = () => {
const [inView, setInView] = useState(false)
return (
<>
<ViewportBlock
onEnterViewport={() => setInView(true)}
onLeaveViewport={() => setInView(false)}
/>
<Header className={inView ? 'hide' : '' }>Header</Header>
</>
)
}
CSS:
hide { display: none; }
Related
I'm trying to pass an object to a child element as a prop, but I get an arr[0] val instead of { id: 1, name: 'General' }.
There I bind prop value, currentRoom is a const with Object.
<input-message :currentRoom="currentRoom"/>
currentRooms value is correct there and equals {id: 1, name: 'General'}.
In child element I try to get props that way:
const props = defineProps({
currentRoom: Object
});
The whole code:
container.vue
<template>
<AppLayout title="Dashboard">
<template #header>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
Chat
</h2>
</template>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
<message-container :room="currentRoom"/>
<input-message :currentRoom="currentRoom" :customText="'blablabla'"/>
</div>
</div>
</div>
</AppLayout>
</template>
<script setup>
import AppLayout from '../../Layouts/AppLayout.vue';
import MessageContainer from "./messageContainer.vue";
import InputMessage from "./inputMessage.vue";
import {defineComponent} from "vue";
defineComponent([
AppLayout,
MessageContainer,
InputMessage
])
let chatRooms = [];
let currentRoom = [];
let messages = [];
const getRooms = () => {
axios.get('/chat/rooms')
.then( response => {
chatRooms = response.data;
setRoom(response.data[0]);
})
.catch(error => {
console.log(error);
})
}
const setRoom = (room) => {
currentRoom = room;
// If I console.log currentRoom here, it is displayed correctly!
console.log(currentRoom)
getMessages();
}
const getMessages = () => {
axios.get('/chat/rooms/' + currentRoom.id + '/messages')
.then(response => {
messages = response.data;
})
.catch(error => {
console.log(error);
});
}
getRooms();
</script>
inputMessage.vue
<template>
<div class="relative h-10 m-1">
<div style="border-top: 1px solid #e6e6e6;" class="grid grid-cols-6">
<input
type="text"
v-model="message"
#keyup.enter="sendMessage"
placeholder="Say something..."
class="col-span-5 outline-none p-1"
/>
<button
#click="sendMessage"
class="place-self-end bg-gray-500 hover:bg-blue-700 p-1 mt-1 rounded text-white">
Send
</button>
</div>
</div>
</template>
<script setup>
const props = defineProps({
currentRoom: Object
// customText: Text
});
console.log(props.currentRoom);
</script>
UPDATE
You currentRoom data property is not reactive. So, I guess, it triggers no updates to the props. You should define it this way:
const currentRoom = reactive({});
or
const currentRoom = ref({});
In case of ref() you have then change the value of the ref like this
currentRoom.value = room;
Hope it helps.
Your currentRoom is an Array. That's why you get [] in the console, when your array is empty.
Check your axios request if you get any data at all. (Browser DevTools Network Tab)
Generally, you should pass one room item to your currentRoom prop or threat your prop as array.
Like this:
<table border=1>
<tbody>
<tr v-for="(room, index) in props.currentRoom">
<td>{{index}}</td>
<td>{{room.id}}</td>
<td>{{room.name}}</td>
</tr>
</tbody>
</table>
Here is a working playground
Just do it right way and it will work.
const { createApp, ref } = Vue;
const MyComponent = {
props: {
currentRoom : {
type: Object,
default: {}
}
},
setup(props) {
console.log(`props.currentRoom: ${JSON.stringify(props.currentRoom)}`)
},
template: `<div class="MyComponent">currentRoom: {{JSON.stringify(currentRoom)}}</div>`
}
const App = {
components: {
MyComponent
},
setup() {
const rooms = ref([]);
const addRoom = () => { rooms.value.push( {id: rooms.value.length + 1, name: 'General'} ); }
return { rooms, addRoom }
}
}
const app = createApp(App)
app.mount('#app')
.MyComponent {
border: 1px solid grey;
padding: 12px;
margin: 4px;
}
<div id="app">
App.rooms: {{rooms}}<hr/>
rooms[0]: <my-component :current-room="rooms[0]"></my-component>
rooms: <my-component v-for="room in rooms" :current-room="room"></my-component>
<button type="button" #click="addRoom()">Add Room</button>
</div>
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
I am using Mantine, Next.js and Sanity. I can't change the width or height well I can't customize the image.
Here is my code.
function SinglePost({ post }: Props) {
const { classes } = useSinglePostStyles();
const serializers = {
types: {
// image: (props: any) => <img className="w-53" {...props} />,
h1: (props: any) => <h1 className={classes.h1} {...props} />,
h2: (props: any) => <h2 className={classes.h2} {...props} />,
li: ({ children }: any) => <li className={classes.list}>{children}</li>,
link: ({ href, children }: any) => (
<a href={href} className={classes.link}>
{children}
</a>
),
},
};
return (
<div className={classes.hero}>
<Container size="lg" className={classes.innerHeroSection}>
<div>
<ResponsiveHeading.H2 className={classes.postTitle}>{post.title}</ResponsiveHeading.H2>
<ResponsiveHeading.P className={classes.subHeading}>
Blog post by {post.author.name}
<br /> Published at {new Date(post.createdAt).toDateString()}
</ResponsiveHeading.P>
</div>
</Container>
{/* Right Side - Image Side */}
<Container size="md" className={classes.body}>
<PortableText
className="max-w-sm"
dataset={process.env.NEXT_PUBLIC_SANITY_DATASET!}
projectId={process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!}
content={post.body}
serializers={serializers}
/>
</Container>
</div>
);
}
Now the image that I am getting from the body is massive and breaks the responsiveness of my code.
I tried to do this:
const serializers = {
types: {
image: (props: any) => <img className="w-53" {...props} />,
},
This doesn't work.
I am having this component that can be displayed in different colors depending on prop color that component received.
const Card = ({color}) => {
return (
<p style={{color}}>Some Text</p>
)
}
I am having these global css variables:
:root{
--bg-clr-1: #E7F8F8;
--card-clr-red: #F03E3E;
--card-clr-violet: #7950F2;
--card-clr-green: #12B886;
--text-clr: #333;
}
Is there a way to pass this css variable as prop to "Card" component? Like so:
import variableStyles from '../../styles/globalVariables.css'
const App = () => {
return(
<Card color={variableStyles['--text-clr']} />
)
}
You can pass the variable string as a props, and inject that into var():
<p style={{ color: `var(${color})` }}>Some Text</p>
const Card = ({color}) => {
return (
<p style={{ color: `var(${color})` }}>Some Text</p>
)
}
class Example extends React.Component {
render() {
return (
<React.Fragment>
<Card color={'--card-clr-red'} />
<Card color={'--card-clr-violet'} />
</React.Fragment>
);
}
}
ReactDOM.render(<Example />, document.body);
:root{
--bg-clr-1: #E7F8F8;
--card-clr-red: #F03E3E;
--card-clr-violet: #7950F2;
--card-clr-green: #12B886;
--text-clr: #333;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
I have /pages/profile.js which calls the LovedOne element, passing values from props.
Debugging shows that these values are valid when passed
import React from "react";
import LovedOne from "../components/loved_one";
export const Profile = ({ loved_ones }) => {
const [session, loading] = useSession();
if (loading) return <div>loading...</div>;
if (!session) return <div>no session</div>;
return (
<Layout>
{session && (
<>
<img src={session.user.image} className="avatar" />
<h1>{session.user.name}</h1>
</>
)}
{loved_ones.map((loved_one, index) => (
<LovedOne
key={index}
firstname={loved_one.firstname}
surname={loved_one.surname}
email={loved_one.email}
/>
))}
<style jsx>{`
.avatar {
width: 220px;
border-radius: 10px;
}
`}</style>
</Layout>
);
};
However in /components/loved_one.js my props is undefined
import React, { useState, useRef } from "react";
export const LovedOne = ({ props }) => {
const [setActive, setActiveState] = useState("");
const [setHeight, setHeightState] = useState("0px");
const content = useRef();
function toggleAccordion() {
setActiveState(setActive === "" ? "active" : "");
setHeightState(
setActive === "active" ? "0px" : `${content.current.scrollHeight}px`
);
}
return (
<div>
<div className="row">
<button
className={`collection-item ${setActive}`}
onClick={toggleAccordion}
>
<i className="fas fa-plus teal-text"></i>
</button>
<div className="col s2">
{props.firstname} {props.surname}
</div>
<div className="col s2">{props.email}</div>
</div>
<div ref={content} style={{ maxHeight: `${setHeight}` }}>
<span>some stuff</span>
</div>
</div>
);
};
export default LovedOne;
I've tried passing single variables, and passing the entire loved_ones object. I get the same problem.
Any help much appreciated!
Have you tried passing props instead of {props} ?
lose brackets, try this way:
export const LovedOne = (props) => {
I have a custom Gutenberg block which has a media uploader in the editor and renders a div with a background image on the front end. I want to use the full image as background on desktop and the thumbnail as background on mobile. Is it possible to use the useMediaQuery hook to achieve this? Any guidance will be greatly appreciated.
Below is my code:
const { __ } = wp.i18n;
const { registerBlockType } = wp.blocks;
const { MediaUploadCheck, MediaUpload } = wp.blockEditor;
const { useMediaQuery } = wp.compose;
registerBlockType( 'blockset/test', {
title: __( 'test' ),
attributes: {
imgUrl: { type: 'string', default: '' },
imgMoUrl: { type: 'string', default: '' },
},
edit: (props) {
return (
<div className="content">
<span>Choose a Hero Image</span>
<MediaUploadCheck>
<MediaUpload
onSelect={ ( img ) => {
props.setAttributes( {
imgUrl: img.url,
imgMoUrl: img.sizes.thumbnail.url : '',
} );
} }
render={ ( { open } ) => {
return props.attributes.imgUrl !== ''? (
<div className={ 'hero__preview' }>
<figure className={ 'image' }>
<img
src={ props.attributes.imgUrl }
/>
</figure>
<Button
className={ 'hero__button' }
onClick={ open }
>
Select a New Image
</Button>
</div>
) : (
<div className={ 'hero__container' }>
<p className={ 'hero__description' }>
Pick a hero image from the media library.
</p>
<Button
className={ 'hero__button' }
onClick={ open }
>
Open Media Library
</Button>
</div>
);
} }
/>
</MediaUploadCheck>
</div>
);
},
save( props ) {
const isMobile = useMediaQuery( 'max-width:767px' )
const imgURL = isMobile ? props.attributes.imgMoUrl : props.attributes.imgUrl
return (
<div
className="background-image"
style={ { backgroundImage: `url(${ imgURL })` } }
></div>
);
},
} );
Solved this issue by using the tag.
<style dangerouslySetInnerHTML={ { __html: `
.background-image {background-image: url(${ props.attributes.imgUrl });}
#media (max-width: 767px) {
.background-image {background-image: url(${ props.attributes.imgMoUrl });}
}
` } }></style>