I am using Strapi as a backend, and in my frontend I am using Gatsby.
I am using the values from the backend to draw a circle inside a card. The number of cards are generated from the backend value.
For now I am hard coding the three first cards in css with n:th child values with the following css code:
.svgAssesment circle {
width: 100%;
height: 100%;
fill: none;
stroke-width: 10;
stroke: rgba(255, 255, 255, 0.05);
transform: translate(5px, 5px);
stroke-linecap: round;
}
.svgAssesment circle:nth-child(2) {
stroke: #fff;
stroke-dasharray: 440px;
}
.card:nth-child(1) .svgAssesment circle:nth-child(2) {
stroke-dashoffset: calc(440px - (440px * 9) / 10);
}
.card:nth-child(2) .svgAssesment circle:nth-child(2) {
stroke-dashoffset: calc(440px - (440px * 9) / 10);
}
.card:nth-child(3) .svgAssesment circle:nth-child(2) {
stroke-dashoffset: calc(440px - (440px * 8) / 10);
}
Instead of hard coding these values, is there a way to draw them dynamically so that each circle's stroke-dashoffset is based upon the prop {item.rate} I have in my frontend.
So in the example above the hard coded values are 9,9 and 8 should be replaced with the values of {item.rate} for each of the cards generated and not just the three I have in my nth:child code.
This is my frontend code:
import React from "react"
import Title from "./Title"
import { graphql, useStaticQuery } from "gatsby"
const query = graphql`
{
allStrapiAssessment {
nodes {
title
rating {
id
topic
rate
}
}
}
}
`
const SelfAssesment = () => {
const data = useStaticQuery(query)
//console.log(data)
const {
allStrapiAssessment: { nodes: assesments },
} = data
//console.log(assesments)
const [value] = React.useState(0)
const { title, rating } = assesments[value]
console.log(title, rating)
return (
<section className="section jobs">
<Title title={title} />
<section className="assesment">
<div className="container">
{rating.map(item => {
return (
<div key={item.id} className="card">
<div className="box">
<div>
<div class="percent">
<svg className="svgAssesment">
<circle cx="70" cy="70" r="70"></circle>
<circle cx="70" cy="70" r="70"></circle>
</svg>
<div class="number">
<h2>
{item.rate}
<span>/10</span>
</h2>
</div>
</div>
</div>
</div>
<div id="buttonColor" className="text">
{item.topic}
</div>
</div>
)
})}
</div>
</section>
</section>
)
}
export default SelfAssesment
You will need to use styled-components in order to use React props in the styling.
Alternatively, you can just use inline styles for stroke-dashoffset CSS rule and delegate the rest of the styling into your CSS stylesheet. Something like:
import React from "react"
import Title from "./Title"
import { graphql, useStaticQuery } from "gatsby"
const query = graphql`
{
allStrapiAssessment {
nodes {
title
rating {
id
topic
rate
}
}
}
}
`
const SelfAssesment = () => {
const data = useStaticQuery(query)
//console.log(data)
const {
allStrapiAssessment: { nodes: assesments },
} = data
//console.log(assesments)
const [value] = React.useState(0)
const { title, rating } = assesments[value]
console.log(title, rating)
return (
<section className="section jobs">
<Title title={title} />
<section className="assesment">
<div className="container">
{rating.map(item => {
return (
<div key={item.id} className="card">
<div className="box">
<div>
<div class="percent">
<svg className="svgAssesment">
<circle cx="70" cy="70" r="70"/>
<circle cx="70" cy="70" r="70" style={{
strokeDashoffset: `calc(440px - (440px * ${item.rate}) / 10)`
}}/>
</svg>
<div class="number">
<h2>
{item.rate}
<span>/10</span>
</h2>
</div>
</div>
</div>
</div>
<div id="buttonColor" className="text">
{item.topic}
</div>
</div>
)
})}
</div>
</section>
</section>
)
}
export default SelfAssesment
Note the:
<circle cx="70" cy="70" r="70" style={{
strokeDashoffset: `calc(440px - (440px * ${item.rate}) / 10)`
}}/>
In the snippet above you are just calculating the stroke-dashoffset rule with inline styles applying the dynamic item.rate value in the second circle.
Related
I have a page on my react js where I am trying allow companies to register for my website and add their locations so that other companies come over for rentals, purchases, etc. I also need Markers if I can add them. I have gotten my Google Maps on my page, however, I can't get the Search bar (StandaloneSearchBox) on the page because it is stuck on "Loading". Here is my code:
import {useState, useEffect } from 'react'
import { GoogleMap, StandaloneSearchBox, LoadScript } from '#react-google-maps/api';
const initialState = {
company: '',
email: '',
password: '',
isMember: true,
}
const Register = () => {
const mapContainerStyle = {
height: "400px",
width: "800px"
}
const center = {
lat: -3.745,
lng: -38.523
};
const [values, setValues] = useState(initialState)
// global state and useNavigate
const handleChange = (e) => {
console.log(e.target)
}
const onSubmit = (e) => {
e.preventDefault()
console.log(e.target)
}
const handleLoad = ref => this.searchBox = ref;
const handlePlacesChanged = () => console.log(this.searchBox.getPlaces());
return (
<body class="page-template-default page page-id-13">
<header class="site-header">
<div class="container">
<h1 class="school-logo-text float-left"><strong>Direct</strong> Connection</h1>
<div class="site-header__util">
</div>
</div>
</header>
<div class="page-banner">
<div class="page-banner__bg-image" style={ { backgroundImage: "url('connection.jpg')" } }></div>
<div class="page-banner__content container container--narrow">
<h1 class="page-banner__title">Register</h1>
<div class="page-banner__intro">
<p></p>
</div>
</div>
</div>
<div class="container container--narrow page-section">
<h2 class="headline headline--tiny">Want to join and connect with companies? Sign up and get your company out there:</h2>
<label class="header">Profile Photo:</label>
<input id="image" type="file" name="profile_photo" placeholder="Photo" required="" capture></input>
<label class="company-name">Company Name</label>
<div class="company-input">
<input text="headline headline--input" placeholder="Enter Company"></input>
</div>
<label class="company-email">Email</label>
<div class="email-input">
<input text="headline headline--input" placeholder="Enter Email"></input>
</div>
<label class="company-map">Map</label>
<div class="map-input">
<LoadScript
googleMapsApiKey='API_HERE'>
<GoogleMap
id="map"
mapContainerStyle={mapContainerStyle}
center={center}
zoom={10}
>
<StandaloneSearchBox
onLoad={handleLoad}
onPlacesChanged={handlePlacesChanged}
>
<input
type="text"
placeholder="Customized your placeholder"
style={{
boxSizing: `border-box`,
border: `1px solid transparent`,
width: `240px`,
height: `32px`,
padding: `0 12px`,
borderRadius: `3px`,
boxShadow: `0 2px 6px rgba(0, 0, 0, 0.3)`,
fontSize: `14px`,
outline: `none`,
textOverflow: `ellipses`,
position: "absolute",
left: "50%",
marginLeft: "-120px"
}}
/>
</StandaloneSearchBox>
</GoogleMap>
</LoadScript>
</div>
</div>
<footer class="site-footer">
<div class="site-footer__inner container container--narrow">
<div class="group">
<div class="site-footer__col-one">
<h1 class="school-logo-text school-logo-text--alt-color">
<strong>Direct</strong> Connection
</h1>
<p><a class="site-footer__link" href="index.html">706-263-0175</a></p>
</div>
</div>
</div>
</footer>
</body>
)
}
export default Register
I have tried adding more imports like from the ScriptLoaded file in the ../api/docs folder, however, it causes the whole page to go blank. If I get rid of the StandaloneSearchBox from import {} from '#react-google-maps/api' and from the LoadScript GoogleMap code, it has it on the page no problem, only it doesn't have the Search Bar and Markers to search for an address (StandaloneSearchBox)
I think the problem is that there was no library. In order for it to display the Search Box, there has to be a library. const lib = ['places']; is setting the places as a library and the library must be added to the LoadScript code where your API key goes. Updated code should have fixed the issue here:
import {useState, useEffect } from 'react'
import { GoogleMap, LoadScript, StandaloneSearchBox } from '#react-google-maps/api'
const lib = ['places'];
const mapContainerStyle = {
height: "400px",
width: "800px"
}
const center = {
lat: -3.745,
lng: -38.523
};
const position = {
lat: 37.772,
lng: -122.214
}
const initialState = {
company: '',
email: '',
password: '',
isMember: true,
}
const Register = () => {
const [searchBox, setSearchBox] = useState(null);
const handlePlacesChanged = () => console.log(searchBox.getPlaces());
const handleLoad = ref => {
setSearchBox(ref);
};
const [values, setValues] = useState(initialState)
// global state and useNavigate
const handleChange = (e) => {
console.log(e.target)
}
const onSubmit = (e) => {
e.preventDefault()
console.log(e.target)
}
return (
<body class="page-template-default page page-id-13">
<header class="site-header">
<div class="container">
<h1 class="school-logo-text float-left"><strong>Direct</strong> Connection</h1>
<div class="site-header__util">
</div>
</div>
</header>
<div class="page-banner">
<div class="page-banner__bg-image" style={ { backgroundImage: "url('connection.jpg')" } }></div>
<div class="page-banner__content container container--narrow">
<h1 class="page-banner__title">Register</h1>
<div class="page-banner__intro">
<p></p>
</div>
</div>
</div>
<div class="container container--narrow page-section">
<h2 class="headline headline--tiny">Want to join and connect with companies? Sign up and get your company out there:</h2>
<label class="header">Profile Photo:</label>
<input id="image" type="file" name="profile_photo" placeholder="Photo" required="" capture></input>
<label class="company-name">Company Name</label>
<div class="company-input">
<input text="headline headline--input" placeholder="Enter Company"></input>
</div>
<label class="company-email">Email</label>
<div class="email-input">
<input text="headline headline--input" placeholder="Enter Email"></input>
</div>
<label class="company-map">Map</label>
<LoadScript
googleMapsApiKey='API_HERE'
libraries={lib}
>
<GoogleMap
mapContainerStyle={mapContainerStyle}
center={center}
zoom={10}
>
<>
<StandaloneSearchBox
onLoad={handleLoad}
onPlacesChanged={handlePlacesChanged}
>
<input
type="text"
placeholder="Search"
style={{
boxSizing: `border-box`,
border: `1px solid transparent`,
width: `240px`,
height: `32px`,
padding: `0 12px`,
borderRadius: `3px`,
boxShadow: `0 2px 6px rgba(0, 0, 0, 0.3)`,
fontSize: `14px`,
outline: `none`,
textOverflow: `ellipses`,
position: "absolute",
left: "50%",
marginLeft: "-120px"
}}
/>
</StandaloneSearchBox>
</>
</GoogleMap>
</LoadScript>
</div>
<footer class="site-footer">
<div class="site-footer__inner container container--narrow">
<div class="group">
<div class="site-footer__col-one">
<h1 class="school-logo-text school-logo-text--alt-color">
<strong>Direct</strong> Connection
</h1>
<p><a class="site-footer__link" href="index.html">706-263-0175</a></p>
</div>
</div>
</div>
</footer>
</body>
)
}
export default Register
I am trying to do something similar to this post but can't get it to work in react js. I tried several different methods, but didn't really get any progress. If i use transform rotate (deg, cx, cy) it moves all over the place and is hidden. When I tried the transform3d as in the post, it doesn't move at all. please help, thanks.
import React, { useState, useRef } from "react";
// import SVGStyles from "./SVGStyles";
function SVGComponent() {
const svgRef = useRef(null);
const [rotate, setRotate] = useState(45);
const [viewBox, setviewBox] = useState("0 0 24 24");
function handleChange(event) {
setRotate(event.target.value);
}
function rotateSvg() {
if (svgRef.current) {
const bbox = svgRef.current.getBBox();
const cx = bbox.x + bbox.width / 2;
const cy = bbox.y + bbox.height / 2;
// setviewBox([bbox.x, bbox.y, bbox.width, bbox.height].join(" "));
svgRef.current.style.setProperty("--r", `${rotate}deg`);
// svgRef.current.setAttribute(
// "transform",
// `rotate(${rotate}), ${cx}, ${cy}`
// );
}
}
return (
<>
<svg ref={svgRef}>
<Triangle viewBox={viewBox} style={{ border: "1px solid red" }} />
</svg>
<br />
<br />
<br />
<br />
<label>
Angle:
<input
type="range"
min="0"
max="360"
value={rotate}
onChange={handleChange}
/>
{rotate}°
</label>
<button onClick={rotateSvg}>Rotate</button>
</>
);
}
const Triangle = (props) => (
<svg
height="100%"
viewBox={props.viewBox}
fill="none"
xmlns="http://www.w3.org/2000/svg"
style={props.style}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="m13.789 1.578 9.764 19.528A2 2 0 0 1 21.763 24H2.237a2 2 0 0 1-1.789-2.894l9.764-19.528a2 2 0 0 1 3.578 0Z"
fill="#758CA3"
/>
</svg>
);
export default SVGComponent;
The css is verbatim from the post:
svg {
transform-origin: 50% 50%;
transform-box: fill-box;
transform: translate3d(25px, 25px, 0) rotate(var(--r, 0deg));
border: 1px dotted red;
}
I am trying to display loading indicator on top of the form (to overlay it) but having a hard time to do so...
I want to show loading indicator when trash / minus or plus is clicked.
I am using React.js + MobX. I do have a boolean flag, but what is difficult to achieve to actually place the loading indicator on top of the form (without corrupting the styling...)
Source code of cart:
import React, { useEffect, useContext } from "react";
import { Container, Row, Col } from "react-bootstrap";
import PromotionalCode from "./PromotionalCode";
import { observer } from "mobx-react-lite";
import { RootStoreContext } from "../../app/stores/rootStore";
import CartBody from "./CartBody";
import CartSummary from "./CartSummary";
import BasicSpinner from "../../app/common/spinners/BasicSpinner";
import Section from "../../app/common/Page/Section";
import TextContainer from "../../app/common/Page/TextContainer";
const Cart = () => {
const rootStore = useContext(RootStoreContext);
const {
cart,
total,
discount,
subtotal,
loadingCart,
loadCartOnSignIn,
loadGuestCart,
isChangingQuantity,
} = rootStore.cartStore;
const { isLoggedIn } = rootStore.userStore;
useEffect(() => {
if (isLoggedIn) {
loadCartOnSignIn();
} else {
loadGuestCart();
}
}, [loadCartOnSignIn, loadGuestCart]);
if (loadingCart)
return (
<div className="mt-5">
<BasicSpinner />
</div>
);
return (
<Section className="checkout p-0">
<TextContainer style={{ padding: "2rem 2rem 0 2rem" }}>
<h2 className="font-weight-bold">Cart</h2>
<p>The competition tickets currently added in the cart.</p>
</TextContainer>
<Container className="cart pt-0">
{loadingCart ? (
<BasicSpinner />
) : (
<div style={isChangingQuantity ? { position: "relative" } : {}}>
{cart?.cartItems && cart.cartItems.length !== 0 ? (
<>
<div className="cart__contents">
<div className="cart__contents__heading-container">
<h2>Items in your bag</h2>
</div>
<div className="cart__contents__table-wrapper">
<table className=" table cart__contents__table-wrapper__table">
<thead>
<tr>
<th scope="col">Description</th>
<th scope="col"></th>
<th scope="col"></th>
<th scope="col">Quantity</th>
<th scope="col">Price</th>
</tr>
</thead>
<CartBody />
</table>
</div>
</div>
<Container className="mt-5 ">
<Row className="justify-content-between cart__discount-and-total">
<Col md={5} className="cart__discount-and-total__discount">
{/* <PromotionalCode /> */}
</Col>
<Col className="cart__discount-and-total__total" md={6}>
<CartSummary
total={total}
subtotal={subtotal}
discount={discount.toString()}
/>
</Col>
</Row>
</Container>
</>
) : (
<div>There is no items in the cart yet.</div>
)}
</div>
)}
</Container>
</Section>
);
};
export default observer(Cart);
Desired result is something like this:
Is it even possible?
You can use absolute positioning for overlay and spinner.
Just render them inside your form, on the bottom.
.form {
// use relative on your form
position: relative;
}
.overlay {
position: absolute;
// this will stretch overlay to fill width and height of form
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.1); // some transparent grey color
}
.spinner {
position: absolute;
// this will place spinner in center of form
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
I am creating 3 cards and I need you to be able to select one of them and it has a div inside when I select it and change the css, the example that I have the selected card is the second one and I would like the same to happen with the others but that only one can be selected at the same time
import React from "react";
import "./styles.css";
export default function App() {
return (
<div className="App">
<div className="card"></div>
<div className="card cardSelect">
<div className="select">select</div>
</div>
<div className="card"></div>
</div>
);
}
.card {
width: 5rem;
height: 5rem;
background: red;
margin: 1rem;
}
.cardSelect {
box-shadow: 2px 4px 30px 0px rgba(0, 0, 0, 0.75);
}
You can achieve this using an orchestrator, or a parent component.
I have created you a simpler example at code sandbox
The active card needs to be controlled from a higher instance. In this case it is the App component it self. But you can create a Cards and Card component to encapsulate it. e.g.
<Cards>
<Card />
<Card />
<Card />
</Cards>
Here is the code of the example in the code sandbox:
import React from "react";
import "./styles.css";
const cardStyle = {
width: "5rem",
height: "5rem",
background: "red",
margin: "1rem"
};
const cardSelect = {
boxShadow: "2px 4px 30px 0px rgba(0, 0, 0, 0.75)"
};
export default function App() {
const [selected, setSelected] = React.useState(0);
return (
<div className="App">
<div
style={{ ...cardStyle, ...(selected === 0 && cardSelect) }}
onClick={() => setSelected(0)}
>
{selected === 0 && <div className="select">select</div>}
</div>
<div
style={{ ...cardStyle, ...(selected === 1 && cardSelect) }}
onClick={() => setSelected(1)}
>
{selected === 1 && <div className="select">select</div>}
</div>
<div
style={{ ...cardStyle, ...(selected === 2 && cardSelect) }}
onClick={() => setSelected(2)}
>
{selected === 2 && <div className="select">select</div>}
</div>
</div>
);
}
What you need to do is to
Add a state in the app level selection somewhere, eg. `const [selection, setSelection] = useState('card-0')
Create a component for cards, e.g.
Render that component three times <CardComponent isSelected={selection === 'card-2'} />`
Inside Cardcomponent, add that class only if it is selected. className={"card " + prop.isSelected ? "cardSelect" : ""}
QUESTION
I have a few photos saved as png files that are in the same folder as the React component and are imported correctly as well.
How and what would be a good practice way to display all the images, let's say there are 4 images, in their proper box shown in the picture below and have them be displayed side by side, along with their name/price aligned below the image.
Similar to craigslist's gallery setting when looking at posts with images.
Ex:
<img src={Logo} alt=“website logo”/>
<img src={Mogo} alt=“website mogo”/>
<img src={Jogo} alt=“website jogo”/>
<img src={Gogo} alt=“website gogo”/>
Could I do something with products.map((product, index, image?))...?
CODE
const Product = props => {
const { product, children } = props;
return (
<div className="products">
{product.name} ${product.price}
{children}
</div>
);
};
function App() {
const [products] = useState([
{ name: "Superman Poster", price: 10 },
{ name: "Spider Poster", price: 20 },
{ name: "Bat Poster", price: 30 }
]);
const [cart, setCart] = useState([]);
const addToCart = index => {
setCart(cart.concat(products[index]));
};
const calculatePrice = () => {
return cart.reduce((price, product) => price + product.price, 0);
};
return (
<div className="App">
<h2>Shopping cart example using React Hooks</h2>
<hr />
{products.map((product, index) => (
<Product key={index} product={product}>
<button onClick={() => addToCart(index)}>Add to cart</button>
</Product>
))}
YOUR CART TOTAL: ${calculatePrice()}
{cart.map((product, index) => (
<Product key={index} product={product}>
{" "}
</Product>
))}
</div>
);
}
Wrap the list of products with a div (<div className="productsContainer">), and display it as a flex with wrap.
Set the width of the items (products) to 50% or less.
To render the image, render the <img> tag as one of the children, or add it directly to the product. Also change the data to include the src.
const { useState } = React;
const Product = ({ product, children }) => (
<div className="products">
{product.name} ${product.price}
{children}
</div>
);
function App() {
const [products] = useState([
{ name: "Superman Poster", price: 10, logo: 'https://picsum.photos/150/150?1' },
{ name: "Spider Poster", price: 20, logo: 'https://picsum.photos/150/150?2' },
{ name: "Bat Poster", price: 30, logo: 'https://picsum.photos/150/150?3' }
]);
const [cart, setCart] = useState([]);
const addToCart = index => {
setCart(cart.concat(products[index]));
};
const calculatePrice = () => {
return cart.reduce((price, product) => price + product.price, 0);
};
return (
<div className="App">
<h2>Shopping cart example using React Hooks</h2>
<hr />
<div className="productsContainer">
{products.map((product, index) => (
<Product key={index} product={product}>
<img src={product.logo} alt="website logo" />
<button onClick={() => addToCart(index)}>Add to cart</button>
</Product>
))}
</div>
YOUR CART TOTAL: ${calculatePrice()}
{cart.map((product, index) => (
<Product key={index} product={product}>
{" "}
</Product>
))}
</div>
);
}
ReactDOM.render(
<App />,
root
);
* {
box-sizing: border-box;
}
.productsContainer {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.products {
display: flex;
flex-direction: column;
align-items: center;
width: 45%;
margin: 0 0 1em 0;
padding: 1em;
border: 1px solid black;
}
.products img {
margin: 0.5em 0;
}
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
Best way to display those side by side you can display it by giving css classes flex-row for horizontal view and flex-column for vertical view in the main div component
const Product = props => {
const { product, children, image } = props;
return (
<div className="products">
{product.name} ${product.price} ${product.image}
{children}
</div>
);
};
products.map((product, index, image?))...?
Something along the lines of this?