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>
Related
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; }
My sidebar in the responsive mode not working correctly, i'm use #media for controller width of page, when is responsive i use position:absolute for sidebar button stay in up of content, i created a state for onclick is active change this position:relative but is not working, help please. The page in the mode normal funciton correctly, and mode responsive (Ctrl + shift + I) too but i click in the button the problemn happens.
Sidebar.js
export default class Menu extends Component {
constructor(props) {
super(props);
this.state = {
classStyle: "sidebar"
};
}
// handleSidebar(value) {
// this.setState = ({ classStyle : value });
// }
handleSidebar = (value) => {
this.setState = ({ classStyle: value });
}
render() {
return (
<div className={this.state.classStyle}>
<Navbar bg="light" variant="light" sticky="top" expand="lg">
<Navbar.Toggle aria-controls="navbarSupportedContent" onClick={() => handleSidebar("sidebarR")} />
<Navbar.Collapse id="navbarSupportedContent">
Index.css
#media (max-width: 600px)
{
.sidebar
{
position: absolute;
}
.sidebarR
{
position: relative;
}
}
Please try this one, it is working
Replace this function in your component
handleSidebar = () => {
console.log("clicked");
this.setState({ classStyle: "sidebarR" });
}
If you want toggle class then use below function,
handleSidebar = () => {
console.log("clicked");
const classStyle = this.state.classStyle == "sidebar" ? "sidebarR" : "sidebar";
this.setState({ classStyle: classStyle });
}
Running Component without Navbar Component,
import React,{Component} from 'react';
import './Menu.css';
export default class Menu extends Component {
state = {
classStyle: "sidebar"
};
handleSidebar = () => {
console.log("clicked");
this.setState({ classStyle: "sidebarR" });
}
render() {
return (
<div className={this.state.classStyle}>
<p onClick={() => this.handleSidebar()} >Menu</p>
</div>
)
}
}
When you call handleSidebar on onClick, you need to use this
Navbar.Toggle aria-controls="navbarSupportedContent" onClick={() => this.handleSidebar("sidebarR")} />
Save function while creating a block is not re-rendering in the front end. I made a component for the save which should re-render on state change but it is not. Edit function is working fine for admin.
Basically the setState function is not working for me.
I tried to enqueue the style but it also didn't worked for me.
My Save.js :
const { Component } = wp.element;
import './MyCss.css';
const { Icon } = wp.components;
import unsplash from './unsplash';
export class Save extends React.Component {
constructor(props) {
super(props)
this.state = {
images: [],
currentIndex: 0,
translateValue: 0,
translateValueSmall: 0
}
}
async componentDidMount(){
const response = await unsplash.get('/search/photos',{
params:{query: "cat"},
});
response.data.results.map(result=>{
this.setState(prevState => ({
images: [...prevState.images, result.urls.thumb]
}))
});
}
goToPrevSlide(){
console.log("previous slide");
if(this.state.currentIndex === 0)
return;
this.setState(prevState => ({
currentIndex: prevState.currentIndex - 1,
translateValue: prevState.translateValue + this.slideWidth(),
translateValueSmall: prevState.translateValueSmall + this.slideWidthSmall()/2
}))
}
goToNextSlide(){
if(this.state.currentIndex === this.state.images.length - 1) {
return this.setState({
currentIndex: 0,
translateValue: 0,
translateValueSmall: 0
})
}
this.setState(prevState => ({
currentIndex: prevState.currentIndex + 1,
translateValue: prevState.translateValue + -(this.slideWidth()),
translateValueSmall: prevState.translateValueSmall + -(this.slideWidthSmall())/2
}));
}
slideWidth(){
return document.querySelector('.slide').clientWidth
}
slideWidthSmall(){
return document.querySelector('.sliders').clientWidth
}
render() {
return (
<div className="box" onClick={()=>this.goToPrevSlide()}>
<div className="slider">
<div className="slider-wrapper"
style={{
transform: `translateX(${this.state.translateValue}px)`,
transition: 'transform ease-out 0.95s'
}}>
{
this.state.images.map((image, i) => (
<Slide key={i} image={image} />
))
}
</div>
</div>
<div className="sliders">
<LeftArrow
goToPrevSlide={()=>this.goToPrevSlide()}
/>
<RightArrow
goToNextSlide={()=>this.goToNextSlide()}
/>
<div className="chaitali"
style={{
transform: `translateX(${this.state.translateValueSmall}px)`,
transition: 'transform ease-out 0.95s'
}}>
{
this.state.images.map((image, i) => (
<Slide key={i} image={image} />
))
}
</div>
</div>
</div>
);
}
}
const Slide = ({ image }) => {
const styles = {
backgroundImage: `url(${image})`,
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
backgroundPosition: '50% 60%'
}
return <div className="slide" style={styles}></div>
}
const LeftArrow = (props) => {
return (
<div onClick={props.goToPrevSlide}>
<Icon className="back arrow" icon="arrow-left"/>
</div>
);
}
const RightArrow = (props) => {
return (
<div onClick={props.goToNextSlide}>
<Icon className="next arrow" icon="arrow-right"/>
</div>
);
}
I have an issue in my react application. I'm using styled-components for using styling (CSS-in-JS).
The issue:
When the user performs a specific action, a layer over the complete page should be displayed. I've created for this an seperate component. But the layer is not working as expected (see the image). Layer 1 should cover the complete page. Layer 2 should be in the middle of the page.
See my code of the component:
import React, { Component, Fragment } from 'react';
import styled from 'styled-components';
import axios from 'axios';
const uuidv4 = require('uuid/v4');
const RefreshLink = styled.a`
text-decoration: underline;
cursor: pointer;
color: #155724;
&:hover {
color: #155724;
}
`
const Background = styled.div`
position:fixed;
width:100%;
height:100%;
background-color:#aeaeae;
opacity:0.5;
z-index:10000;
`
const PopUp = styled.div`
position:fixed;
z-index:10001;
left:50%;
margin-left:-25%;
width:450px;
top:50%;
margin-top:-25%;
`
class UpdatingFreightsInfo extends Component {
_isMounted = false;
signal = axios.CancelToken.source();
constructor(props) {
super(props);
this.state = {
freightsInUpdateProcess: false,
hasFreightsBeenInUpdateStatusSincePageLoad: false,
intervalId: -1,
freightsUpdating: [],
};
this.checkForUpdatingFreights = this.checkForUpdatingFreights.bind(this);
}
componentDidMount() {
this._isMounted = true;
this.getUpdatingFreightsInfo();
}
componentWillUnmount() {
this.signal.cancel();
clearInterval(this.state.intervalId);
this._isMounted = false;
}
componentDidUpdate(prevProps) {
if (this.props.updateTrigger !== prevProps.updateTrigger) {
this.checkForUpdatingFreights();
}
}
getUpdatingFreightsInfo() {
this.checkForUpdatingFreights();
let intervalId = setInterval(() => {
this.checkForUpdatingFreights();
},30000);
this.setState({
intervalId: intervalId
});
}
checkForUpdatingFreights = async () => {
try {
const response = await axios.get('../data/get/json/freightsCurrentlyUpdating', {
cancelToken: this.signal.token,
})
.then((response) => {
console.log(response);
if(response != undefined && response != null) {
if (this._isMounted) {
if(response.data.length > 0) {
this.setState({
freightsUpdating: response.data,
freightsInUpdateProcess: true,
hasFreightsBeenInUpdateStatusSincePageLoad: true,
});
}
else {
this.setState({
freightsUpdating: [],
freightsInUpdateProcess: false,
});
}
}
}
})
.catch(function (error) {
console.log(error);
});
}
catch(err) {
if (axios.isCancel(err)) {
console.log('Error: ', err.message); // => prints: Api is being canceled
} else {
}
}
}
render() {
return (
(this.state.freightsInUpdateProcess || (this.state.hasFreightsBeenInUpdateStatusSincePageLoad && !this.state.freightsInUpdateProcess )) &&
<Fragment>
<Background key={uuidv4()}></Background>
<PopUp key={uuidv4()}>
<div className="container-fluid">
<div className="row">
<div className="col-12 col-sm-12 col-md-12 col-lg-12">
{
this.state.freightsInUpdateProcess &&
<div className="alert alert-warning text-center" role="alert">
<h4 className="alert-heading">Updating freights in process</h4>
<p className="mb-0">{ this.state.freightsUpdating.length } freight entries are currently being updated.</p>
</div>
}
{
this.state.hasFreightsBeenInUpdateStatusSincePageLoad && !this.state.freightsInUpdateProcess &&
<div className="alert alert-success text-center" role="alert">
<h4 className="alert-heading">Updating freights finished</h4>
<p className="mb-0">
The update process has been finished.
<br />
<span className="fa fa-refresh"></span> <RefreshLink href="/" target="_self">Please refresh the page</RefreshLink>
</p>
</div>
}
</div>
</div>
</div>
</PopUp>
</Fragment>
);
}
}
export default UpdatingFreightsInfo;
Is it because the component is being nested in other components? It seems like that, but I thought, when using CSS
position: fixed with combination of left and top
that this code is independent from the components. And also strange that in PopUp it seems to work (almost) correctly.
I have a problem with a custom block which send me an error when I reload the edition page.
I don't understand what the problem is. In regards of the error, actual and expected are the same.
Here the error :
Block validation: Block validation failed for namespace/nottomiss ({object}).
Expected:
<div class="wp-block-utopiales-nottomiss"><p>label test</p><p>label test</p></div>
Actual:
<div class="wp-block-utopiales-nottomiss"><p>label test</p><p>title test</p></div>
Here my code :
const { registerBlockType } = wp.blocks;
const { __ } = wp.i18n;
const { PanelBody, TextControl } = wp.components;
const { BlockControls, InspectorControls, RichText } = wp.editor;
const { createElement, Fragment } = wp.element
registerBlockType( 'namespace/nottomiss', {
title: __( 'Nottomiss' ),
description: __('My description'),
icon: 'star-filled',
category: 'widgets',
supports: { align: true, alignWide: true },
attributes: {
label: {
type: 'string',
source: 'html',
selector: 'p',
},
title: {
type: 'string',
source: 'html',
selector: 'p',
},
},
edit: function( props ) {
const { label, title } = props.attributes;
function onChangeLabel( newLabel ) {
props.setAttributes( { label: newLabel } );
}
function onChangeTitle( newTitle ) {
props.setAttributes( { title: newTitle } );
}
return (
<Fragment>
<BlockControls>
</BlockControls>
<InspectorControls>
<PanelBody title={ __( 'List' ) }>
</PanelBody>
</InspectorControls>
<RichText
identifier="label"
tagName="p"
placeholder=""
value={ label }
onChange={ onChangeLabel }
/>
<RichText
identifier="title"
tagName="p"
placeholder=""
value={ title }
onChange={ onChangeTitle }
/>
</Fragment>
);
},
save: function( props ) {
const { label, title } = props.attributes;
return (
<div>
<RichText.Content
tagName="p"
value={ label }
/>
<RichText.Content
tagName="p"
value={ title }
/>
</div>
);
},
} );
Thanks in advance for your answer,
The selectors are how the editor pulls the data from the saved html, and currently your selectors aren't targeting the content. You could change your selectors to something like this:
attributes: {
label: {
type: 'string',
source: 'html',
selector: '.label'
},
title: {
type: 'string',
source: 'html',
selector: '.title'
}
}
And you could change your save function to this:
save: function(props) {
const { label, title } = props.attributes
return (
<div>
<div className="label">
<RichText.Content
value={ label }
/>
</div>
<div className="title">
<RichText.Content
value={ title }
/>
</div>
</div>
)
}