I have created a simple default page that gets rendered with content (images and text) from GraphCMS.
I can query and display the content, but I'm not sure how to make the images adjust to the view/container, as I don't know how to access the image and set a CSS class.
I'm using the plugin "gatsby-plugin-mdx" to render the following:
page.content.markdownNode.childMdx.body
Here is my DefaultPage.tsx file:
import React from "react"
import Layout from "../layout/layout"
import styled from "styled-components"
import { H1, BodyMain } from "../styles/TextStyles"
import { MDXRenderer } from "gatsby-plugin-mdx"
export default function DefaultPageTemplate({ pageContext: { page } }) {
return (
<Layout>
<Wrapper>
<HeaderWrapper>
<TextWrapper>
<TitleWrapper>{page.title}</TitleWrapper>
<SubtitleWrapper>{page.subtitle}</SubtitleWrapper>
</TextWrapper>
</HeaderWrapper>
<ContentWrapper>
<MDXRenderer>{page.content.markdownNode.childMdx.body}</MDXRenderer>
</ContentWrapper>
</Wrapper>
</Layout>
)
}
const Wrapper = styled.div``
const HeaderWrapper = styled.div`
min-height: 300px;
color: #ffffff;
background-color: #339861;
display: grid;
justify-content: center;
align-items: center;
padding: 30px;
`
const TextWrapper = styled.div`
text-align: center;
`
const TitleWrapper = styled(H1)``
const SubtitleWrapper = styled(BodyMain)``
const ContentWrapper = styled.div`
margin: 1rem;
`
Below is a screenshot of the behaviour:
Below is a screenshot of when I'm inspecting the element in the browser. It appears that the image is wrapped in a paragraph:
Can you help me to understand how to scale the image for smaller views/screens?
It seems like I was able to target the image within the gatsby-browser.js and give it a width of 100%.
my gatsby-browser.js file:
import "./src/components/layout/layout.css"
import React from "react"
import { MDXProvider } from "#mdx-js/react"
const H1 = props => (
<h1 style={{ fontSize: "60px", fontWeight: "bold" }} {...props} />
)
const H2 = props => (
<h2 style={{ fontSize: "40px", fontWeight: "bold" }} {...props} />
)
const H3 = props => (
<h2 style={{ fontSize: "30px", fontWeight: "bold" }} {...props} />
)
const H4 = props => (
<h4 style={{ fontSize: "25px", fontWeight: "bold" }} {...props} />
)
const H5 = props => (
<h4 style={{ fontSize: "20px", fontWeight: "bold" }} {...props} />
)
const H6 = props => (
<h4 style={{ fontSize: "18px", fontWeight: "bold" }} {...props} />
)
const MyParagraph = props => (
<p style={{ fontSize: "20px", lineHeight: 1.6 }} {...props} />
)
const Strong = props => <strong style={{ fontWeight: "bold" }} {...props} />
const MyImage = props => (
<img style={{ maxWidth: "100%", borderRadius: "15px" }} {...props} />
)
const components = {
h1: H1,
h2: H2,
h3: H3,
h4: H4,
h5: H5,
h6: H6,
p: MyParagraph,
strong: Strong,
img: MyImage,
}
export const wrapRootElement = ({ element }) => (
<MDXProvider components={components}>{element}</MDXProvider>
)
Any other or better recommendations/solutions?
Related
I tried to centre the typography using both textAlign and align but that did not work. Can someone help me with this ? The resulting page is below the code
import React, {useState} from 'react'
import logo from '../../images/logo.svg'
import { Typography } from '#mui/material'
import AccountCircleIcon from "#mui/icons-material/AccountCircle"
const NavBar = () => {
const StyledToolbar = styled(Toolbar)({
display: "flex",
alignItems: "center",
justifyContent: "space-between"
});
const Logo = styled("img")({
width: "12rem",
height: "auto",
});
const StyledAppBar = styled(AppBar)({
position: "sticky",
backgroundColor: "#EDEDED",
});
return (
<StyledAppBar>
<StyledToolbar>
<Logo src={logo} alt="quizy" />
<Typography variant="h5" color="black" align='center'>
Question
</Typography>
<AccountCircleIcon
style={{ color: "black" }}
/>
</StyledToolbar>
</StyledAppBar>
);
}
Page Result
It actually is centered according to its width. To center with 3 children elements you could assign Logo, Typography and Icon following CSS properties:
flex-grow: 1
flex-basis: 0
This will probably move the AccountCircleIcon a bit to the left but you can assign it margin-left: auto. You will also have to remove your assigned width of Logo. For example:
import React, {useState} from 'react'
import logo from '../../images/logo.svg'
import { Typography } from '#mui/material'
import AccountCircleIcon from "#mui/icons-material/AccountCircle"
const NavBar = () => {
const StyledToolbar = styled(Toolbar)({
display: "flex",
alignItems: "center",
justifyContent: "space-between"
});
const Logo = styled("img")({
width: "12rem",
height: "auto"
});
const StyledAppBar = styled(AppBar)({
position: "sticky",
backgroundColor: "#EDEDED",
});
return (
<StyledAppBar>
<StyledToolbar>
<div style={{ flexGrow: 1, flexBasis: 0 }}>
<Logo src={logo} alt="quizy" />
</div>
<Typography style={{ flexGrow: 1, flexBasis: 0 }} variant="h5" color="black" align='center'>
Question
</Typography>
<div style={{ flexGrow: 1, flexBasis: 0, textAlign: "right" }}>
<AccountCircleIcon
style={{ marginLeft: 'auto', color: "black" }}
/>
</div>
</StyledToolbar>
</StyledAppBar>
);
}
Initially I have three div In first div I am able to upload pdf in second div I am able upload excel file in third div I am able to upload audio file. now What I am trying to do is If I uploaded pdf file successfully then I need to disable Excel file div so I cannot upload excel file please help to achieve this
This is my code
This is App.js
import React, { useState } from "react";
import 'antd/dist/antd.css';
import './index.css';
import { Row, Col, Button, Modal, Upload, message, Divider } from 'antd';
import { FilePdfOutlined, FileExcelOutlined, AudioOutlined } from '#ant-design/icons';
import "./App.css";
const App = () => {
const [visible, setVisible] = useState(false)
const [disableDiv, setDisableDiv] = useState(disable)
const showPopUp = () => {
setVisible(true)
}
const closePopUp = () => {
setVisible(false)
}
const props = {
beforeUpload: file => {
const characterValidation = file
// setFile(characterValidation)
console.log(characterValidation)
}
}
return (
<div>
<Row>
<Col span={24}>
<Button onClick={() => showPopUp()} type="primary">Show PopUp</Button>
<Modal
visible={visible}
onCancel={closePopUp}
onOk={closePopUp}
>
<div style={{ display: "flex" }}>
<div style={{ marginLeft: "5px" }}>
<Upload accept=".pdf" {...props}>
<FilePdfOutlined style={{ fontSize: "25px", backgroundColor: "red", padding: "10px", borderRadius: "50%" }} />
<h6>Upload Pdf</h6>
</Upload>
</div>
<div style={{ marginLeft: "5px" }} disabled={disableDiv}>
<Upload accept=".xls .xlsx" {...props}>
<FileExcelOutlined style={{ fontSize: "25px", backgroundColor: "red", padding: "10px", borderRadius: "50%" }} />
<h6>Upload Excel</h6>
</Upload>
</div>
<div style={{ marginLeft: "5px" }}>
<Upload accept="audio/*" {...props}>
<AudioOutlined style={{ fontSize: "25px", backgroundColor: "red", padding: "10px", borderRadius: "50%" }} />
<h6>Upload Excel</h6>
</Upload>
</div>
</div>
</Modal>
</Col>
</Row>
</div>
)
}
export default App
This is App.css
.ant-modal-close-x > span {
display: none;
}
.ant-upload-list:before {
content:"Attachments:";
}
.ant-upload-list {
display: block;
margin-bottom: 30pt;
margin-left: 0;
font-size: 16px;
font-weight: bold;
border-top:1px solid red;
}
Ok so as far as I could understand you want to prevent the user from interacting with a specific input under a certain condition.
In your case you want to disable the Upload input component when the user has uploaded the pdf file in the previous Upload component.
One way to do that is to set an onChange event handler on the pdf Upload component and when the file is successfully uploaded you can then disable the excel Upload component.
Here is how this would look in your example (without changing how your code worked before):
import React, { useState } from "react";
import 'antd/dist/antd.css';
import './index.css';
import { Row, Col, Button, Modal, Upload, message, Divider } from 'antd';
import { FilePdfOutlined, FileExcelOutlined, AudioOutlined } from '#ant-design/icons';
import "./App.css";
const App = () => {
const [visible, setVisible] = useState(false)
const [isExcelUploadDisabled, setIsExcelUploadDisabled] = useState(false);
const showPopUp = () => {
setVisible(true)
}
const closePopUp = () => {
setVisible(false)
}
const props = {
beforeUpload: file => {
const characterValidation = file
// setFile(characterValidation)
console.log(characterValidation)
}
}
const PDFUploadProps = {
accept: '.pdf',
name: 'PDF file name',
action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
headers: {
authorization: 'authorization-text'
},
onChange(info) {
console.log(info);
if (info.file.status !== 'uploading') {
console.log('uploading');
}
if (info.file.status === 'done') {
setIsExcelUploadDisabled(true);
}
//bonus: enable the excel upload again when the user removes the file
if (info.file.status === 'removed') {
setIsExcelUploadDisabled(false);
} else if (info.file.status === 'error') {
console.error('error');
}
},
...props
};
const excelUploadProps = {
accept: '.xls, .xlsx',
name: 'excel file name',
action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
headers: {
authorization: 'authorization-text'
},
disabled: isExcelUploadDisabled
...props
};
return (
<div>
<Row>
<Col span={24}>
<Button onClick={() => showPopUp()} type="primary">Show PopUp</Button>
<Modal
visible={visible}
onCancel={closePopUp}
onOk={closePopUp}
>
<div style={{ display: "flex" }}>
<div style={{ marginLeft: "5px" }}>
<Upload {...PDFUploadProps}>
<FilePdfOutlined style={{ fontSize: "25px", backgroundColor: "red", padding: "10px", borderRadius: "50%" }} />
<h6>Upload Pdf</h6>
</Upload>
</div>
<div style={{ marginLeft: "5px" }}>
<Upload {...excelUploadProps}>
<FileExcelOutlined style={{ fontSize: "25px", backgroundColor: "red", padding: "10px", borderRadius: "50%" }} />
<h6>Upload Excel</h6>
</Upload>
</div>
<div style={{ marginLeft: "5px" }}>
<Upload accept="audio/*" {...props}>
<AudioOutlined style={{ fontSize: "25px", backgroundColor: "red", padding: "10px", borderRadius: "50%" }} />
<h6>Upload Excel</h6>
</Upload>
</div>
</div>
</Modal>
</Col>
</Row>
</div>
)
}
export default App
Here is also a minimal example of the same functionality here.
I am using the following code to generate a Tweet input box which takes in text/video/image/emoji. And they can be in different combinations.
I am not sure how to generate a tweet display box which shows the final display containing text/image/emoji ? I understand I might need to put the different inputs in an array or some sort but what after that. My current code for display side is performing nothing and I am not sure where to go from here.
I am looking for display box to be of following form after a Submit Button:
Code components/EmojiPicker.js has:
import React, {useState} from 'react'
import ReactDOM from "react-dom";
import { Picker } from "emoji-mart";
import Button from "#material-ui/core/Button";
const EmojiPicker = ({ onSelect }) => {
const [show, setShow] = useState(false);
return (
<>
<Button
onClick={() => setShow(oldState => !oldState)}
style={{ width: "30px", height: "30px", borderRadius: "4px", border: "3px solid", display: "flex", alignItems: "center", justifyContent: "center",
background: "transparent"}}>
ej
</Button>
{ReactDOM.createPortal(
show && <Picker onSelect={onSelect} />,
document.body
)}
</>
);
};
export default EmojiPicker
Code components/FileInput.js has:
import React, {useRef} from 'react'
const FileInput = ({ onChange, children }) => {
const fileRef = useRef();
const onPickFile = event => {
onChange([...event.target.files]);
};
return (
<div
style={{
width: "35px",
height: "35px",
borderRadius: "3px"
}}
onClick={() => fileRef.current.click()}
>
{children}
<input
multiple
ref={fileRef}
onChange={onPickFile}
type="file"
style={{ visibility: "hidden" }}
/>
</div>
);
};
export default FileInput
Code components/tweetboxImgInp.js as:
import React, {useState, useEffect} from 'react'
const ImgIcon = () => (
<svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0z" fill="none" />
<path d="M14 13l4 5H6l4-4 1.79 1.78L14 13zm-6.01-2.99A2 2 0 0 0 8 6a2 2 0 0 0-.01 4.01zM22 5v14a3 3 0 0 1-3 2.99H5c-1.64 0-3-1.36-3-3V5c0-1.64 1.36-3 3-3h14c1.65 0 3 1.36 3 3zm-2.01 0a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h7v-.01h7a1 1 0 0 0 1-1V5z" />
</svg>
);
export const Img = ({ file, onRemove, index }) => {
const [fileUrl, setFileUrl] = useState(null);
useEffect(() => {
if (file) {
setFileUrl(URL.createObjectURL(file));
}
}, [file]);
return fileUrl ? (
<div style={{ position: "relative", maxWidth: "230px", maxHeight: "95px" }}>
<img
style={{
display: "block",
maxWidth: "230px",
maxHeight: "95px",
width: "auto",
height: "auto"
}}
alt="pic"
src={fileUrl}
/>
<div
onClick={() => onRemove(index)}
style={{
position: "absolute",
right: 0,
top: 0,
width: "20px",
height: "20px",
borderRadius: "50%",
background: "black",
color: "white",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
x
</div>
</div>
) : null;
};
export default ImgIcon
And App.js has:
import React, { useRef, useState } from "react";
import ImgIcon, {Img} from './components/tweetboxImgInp'
import EmojiPicker from './components/EmojiPicker'
import FileInput from './components/FileInput'
import "emoji-mart/css/emoji-mart.css";
import "./styles.css";
function App() {
const [text, setText] = useState("");
const [pics, setPics] = useState([]);
const textAreaRef = useRef();
const insertAtPos = value => {
const { current: taRef } = textAreaRef;
let startPos = taRef.selectionStart;
let endPos = taRef.selectionEnd;
taRef.value =
taRef.value.substring(0, startPos) +
value.native +
taRef.value.substring(endPos, taRef.value.length);
};
return (
<div style={{display: "flex", flexDirection: "column", border: "3px solid", borderRadius: "5px", width: "600px", minHeight: "200px", padding: "20px"}} >
<div style={{ display: "flex", flexDirection: "column", flex: 1, border: "1px solid", borderRadius: "5px", margin: "0px"}}>
<textarea
ref={textAreaRef}
value={text}
style={{ flex: 1, border: "none", minHeight: "150px" }}
onChange={e => setText(e.target.value)}
/>
<div style={{ display: "flex", flexDirection: "row", flexWrap: "wrap", background: "fbfbfb"}} >
{pics.map((picFile, index) => (
<Img key={index} index={index} file={picFile} onRemove={rmIndx =>
setPics(pics.filter((pic, index) => index !== rmIndx))}/>))}
</div>
</div>
<div style={{ display: "flex", flexDirection: "row", alignItems: "center", marginTop: "20px" }}>
<div style={{ marginRight: "20px" }}>
<FileInput onChange={pics => setPics(pics)}>
{/* <ImgIcon /> */}
Tes
</FileInput>
</div>
<EmojiPicker onSelect={insertAtPos} />
</div>
</div>
);
}
export default App
Edit: I am good with the display box accepting only 1 media file, text and few emoji. It will surprise me if I am the only one in 2019 looking to do it for fun.
Working Example
Click the codesandbox button to view the demo
The tweet display component is pretty straightforward. Its a flexbox column with two parts. First part of the column contains the tweet. The second part of the column contains the list of images/media elements. Emoji is part of the text component.
Tweet Display Component
const Tweet = ({ tweet: { text, images } }) => (
<div
style={{
margin: "20px",
border: "1px solid grey",
width: "600px",
padding: "20px",
borderRadius: "3px"
}}
>
<div>{text}</div>
{images.length > 0 && (
<div
style={{
display: "flex",
flexDirection: "row",
flexWrap: "wrap",
background: "fbfbfb",
padding: "30px 0"
}}
>
{images.map((img, i) => (
<Img key={i} file={img} index={i} isSingle={images.length === 1} />
))}
</div>
)}
</div>
);
For more info checkout this css-tricks article to get more info on css flex layout
I am using React ROuter and CSS in JS for style.
I'd like to change the background color and border color for the active links child divs (navIndicator and innerIndicator)
import { css, StyleSheet } from 'aphrodite/no-important';
export default ({ task, selectedLocal, selectedScenarioId, taskId }: ITaskNavItem) => {
const isActiveNav = (match: any, location: object) => match;
const theme = getTheme();
const styles = StyleSheet.create({
navLink: {
display: 'flex',
fontSize: '12px',
textDecoration: 'none',
color: theme.palette.neutralPrimary
},
navLinkActive: {
color: theme.palette.neutralPrimary,
fontWeight: 'bold',
'.navIndicator': {
borderColor: theme.palette.themePrimary
},
'.innerIndicator': {
backgroundColor: theme.palette.themePrimary
}
},
navTitle: {
width: '100px',
textAlign: 'center',
wordBreak: 'break-word',
wordSpacing: '100px'
},
linkText: {
display: 'flex',
flexFlow: 'column',
'align-items': 'center'
},
navIndicator: {
borderRadius: '50%',
margin: '10px 0 0 0',
backgroundColor: theme.palette.white,
width: '30px',
height: '30px',
border: '2px solid',
borderColor: theme.palette.neutralPrimary,
position: 'relative'
},
innerIndicator: {
position: 'absolute',
borderRadius: '50%',
width: '20px',
height: '20px',
backgroundColor: theme.palette.neutralPrimary,
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)'
}
});
return (
<div className={css(styles.navLink)}>
<NavLink
exact
isActive={isActiveNav}
className={css(styles.navLink)}
activeClassName={css(styles.navLinkActive)}
to={`/selectedLocal/${selectedLocal}/scenarios/${selectedScenarioId}/tasks/${taskId}`}
>
<div className={css(styles.linkText)}>
<div className={css(styles.navTitle)}> {task.title}</div>
<div className={css(styles.navIndicator)}>
<div className={css(styles.innerIndicator)} />
</div>
</div>
</NavLink>
</div>
);
};
However, the navIndicator and innerIndicator colors doesn't change when nav link is active.
Wondering how to get the style working for active link?
NavLink element does not indicate to its children if it active. So I may suggest to get currecnt route from BrowserRouter component (your component should be child of BrowserRouter so NavLink works), compare path and set local isActive variable to indicate if specific route is active.
For example (not tested, just sample):
const StyledLinks: React.FunctionComponent<RouteComponentProps & ITaskNavItem> = ({ task, selectedLocal, selectedScenarioId, taskId, location }) => {
const to = '/selectedLocal/${selectedLocal}/scenarios/${selectedScenarioId}/tasks/${taskId}';
const isActive = to === location.pathname;
const styles = StyleSheet.create({
// ...
navIndicatorActive: {
borderColor: theme.palette.themePrimary
},
// ...
return (
<div className={css(styles.navLink)}>
<NavLink
exact
className={css(styles.navLink)}
activeClassName={css(styles.navLinkActive)}
to={to}
>
<div className={css(styles.linkText)}>
<div className={css(styles.navTitle)}> {task.title}</div>
<div className={isActive ? css([styles.navIndicator, styles.navIndicatorActive]) : css(styles.navIndicator)}>
<div className={css(styles.innerIndicator)} />
</div>
</div>
</NavLink>
</div>
);
}
// Wrap your component withRouter to get location prop
export default withRouter(StyledLinks);
I have a react component that renders fine but as soon as i add images it renders far slower and causes a few hundred miliseconds delay on the component rendering. Does anyone know a way to speed this up or what I am doing wrong?
class SearchResults extends React.Component {
render() {
//Individual container styling
const divStyle = {
float: 'left',
width: '80px',
height: '80px',
border: '5px solid pink',
borderRadius: '3px',
margin: '2px',
cursor: 'pointer',
backgroundColor: 'white'
};
const styleImage = {
maxWidth: '70px',
maxHeight: '70px',
margin: 'auto',
display: 'flex'
}
//Individual record
const startItem = (this.props.itemsPerPage * this.props.page) - this.props.itemsPerPage;//First item on page
const endItem = startItem + this.props.itemsPerPage;
//Slice is from/to
const all = searchableDatabase.slice(startItem, endItem).map((value, index) => {
return (
<div style={divStyle} >
{value.edit_title_normal}
{/*<img style={styleImage} src={'YourMemories/' + currentCollection.id + '/' + value.sequentialNumber + '/' + value.sequentialNumber + ' Thumbnail.jpg'} />*/}
</div>
)
});
//Main container
return (
<div>
<div style={{ clear: 'both', paddingTop: '20px', paddingBottom: '20px', textAlign: 'center' }}>
<Pagination allLength={searchableDatabase.length} page={this.props.page} itemsPerPage={this.props.itemsPerPage} />
</div>
<div>
{all}
</div>
</div>
);
}
}