I'm having issues when dynamically assigning text colors to some headings. I have a .map function that runs and outputs some divs with different heading colors. This is example of my code:
{nftInfo.map((nft, index) => (
<div
className="flex flex-col space-y-3 rounded-lg border-2 border-opacity-25 bg-white p-5 hover:bg-slate-50 dark:border-slate-500 dark:bg-gray-700"
key={index}
>
<div
className={`flex justify-center space-x-4 font-bold text-${nft.color}-600 dark:text-${nft.color}-400`}
>
<div className={`text-lg `}>
{nft.title} {nft.color} <--- This outputs the correct color
</div>
{nft.icon}
</div>
</div>
Example of JSON:
{
title: 'Dummy text 2',
description: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Maxime minima odit aspernatur aliquam deleniti in corporis omnis cupiditate optio, voluptatum quasi reiciendis dolor, nostrum nihil quaerat est, doloremque mollitia possimus.',
bottomText: 'Lorem ipsum dolor sit amet consectetur adipisicing',
buttonText: 'Generate contract',
icon: <CodeIcon className="mt-1 h-6 w-6" />,
color: 'pink'
}
Here you can see that I'm assigning text colors depending on the color I set in the JSON above.
text-${nft.color}-600 dark:text-${nft.color}-400
I can also see the string being correctly assigned in the developer tools DOM view, but when I check the CSS in the dev tools the style is not applied...
Also, if I manually add the class to the div the heading does get colored...
The CSS file generated by Tailwind will only include classes that it recognizes when it scans your code, which means that dynamically generated classes (e.g. text-${nft.color}-600) will not be included.
To ensure that your colors are added to the CSS, you could add safelist in tailwind.config.js that holds all of the color utility classes that you need (see https://tailwindcss.com/docs/content-configuration#safelisting-classes).
module.exports = {
...
safelist: [
'text-pink-600',
'dark:text-pink-400',
]
}
Another solution is to add the utility classes in an object in your component, so that they get picked up and added to the stylesheet when Tailwind parses your code. You can then look up the colors using your nft.color variable. For instance:
const colors = {
"pink": {
"light": "text-pink-600",
"dark": "dark:text-pink-400",
},
...
}
<div className={`${colors[nft.color].light} ${colors[nft.color].dark}`} />
Related
I'm working on a website with react that maps through an instructors table and displays the instructors' image, name, and bio. I'm trying to use the react-read-more-read-less component to initially show 300 characters then show a Read More link to display all the text. If I hard code the text in the component it works perfectly but when I try to pull the data in from the db, I get an error that children.substr is not a function.
Here is the code:
<ReactReadMoreReadLess
readMoreClassName="readMoreClassName"
charLimit={300}
readMoreText="Read More"
readLessText="Read Less"
>
{parse(inst.instructorBio)}
</ReactReadMoreReadLess>
If I just use {inst.instructorBio} it works find but displays the p tags p.
I even tried to write a toggleShow function and it worked but it expanded all the instructors bios and I wasn't able to figure out how to only expand the one clicked.
Pares is from a npm package I installed to render the data correctly without showing the p tags. The pagckage is html-react-parser.
First solution:
The error occurs because only text is expected as a child element. Only text has a substring (substr) feature.
My solution will require further refinement but may be a good start. Use replacement for paragraphs. Maybe this function is enough:
const convert = (html) => html.replace(/<p>/gi, "\n").replace(/<\/p>/gi, "");
In order to use \n we need to wrap the element in pre
return (
<pre>
<ReactReadMoreReadLess charLimit={5}>
{inst.instructorBio}
</ReactReadMoreReadLess>
</pre>
);
This solution preserves line breaks well, but for the rest you will have to work with styles to make lines behave like paragraphs.
For wrap text in pre:
pre {
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
Second solution for many paragraphs — own component:
The -webkit-line-clamp CSS property allows limiting of the contents of a block to the specified number of lines.
const Fragment = React.Fragment
const MoreLessText = ({children}) => {
const [more, setMore] = React.useState(false)
return(
<Fragment>
<div className={"text" + (more ? '' : ' less')} >
{children}
</div>
<span onClick={() => setMore(!more)}> {/* toggle state*/}
{ more ? 'Show Less' : 'Show More'}
</span>
</Fragment>
)
}
const App = () => {
return (
<table border="1">
<tbody>
<tr>
<td>
<MoreLessText>
<p>
1) Lorem ipsum dolor sit amet consectetur adipisicing elit. Labore fugiat cumque, aliquam minus repellat voluptates adipisci. Consectetur, voluptatem nam quasi alias minima, nihil tenetur odit, a atque deleniti maiores est?
</p>
</MoreLessText>
</td>
</tr>
<tr>
<td>
<MoreLessText>
<p>
2) Lorem ipsum dolor sit amet consectetur adipisicing elit. Labore fugiat cumque, aliquam minus repellat voluptates adipisci. Consectetur, voluptatem nam quasi alias minima, nihil tenetur odit, a atque deleniti maiores est?
</p>
</MoreLessText>
</td>
</tr>
</tbody>
</table>
)
}
ReactDOM.render(<App/>, document.getElementById("root"));
.text{
width: 300px;
}
.less {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
line-clamp: 1;
-webkit-box-orient: vertical;
}
<script src="https://unpkg.com/react#17/umd/react.development.js" crossorigin ></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.development.js" crossorigin ></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<div id="root">
I am writing a "Terms of Service" page using ReactJS and my idea is to copy the contents of the file tos-text.txt in the component at build time, to avoid fetching time when the page is opened.
I tried as follows, but with poor results:
<h2>Terms of Service</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas in scelerisque odio, sed
consequat ante. Donec lectus tortor, ullamcorper quis risus nec, cursus hendrerit libero. In hac
habitasse platea dictumst. Quisque et posuere urna. Suspendisse convallis faucibus nulla, non
egestas libero aliquet non. Donec tincidunt purus sed sem suscipit feugiat. Pellentesque rutrum
blandit gravida. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per
inceptos himenaeos. Pellentesque erat urna, lobortis sed turpis a, aliquet aliquet lorem. Class
aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nulla quis
nibh et mi ullamcorper mattis eu eget lectus.
</p>
import { Container } from 'react-bootstrap'
// Page content
import TosText from 'config/tos-text.txt'
// --- Terms of Service ---
const Tos = () => {
return (
<Container className="flex-grow-1 tos">
<div dangerouslySetInnerHTML={{ __html: TosText }} />
</Container>
)
}
export default Tos
Currently the page only shows the link to the generated txt file (/static/media/tos-text.dc220bee.txt).
EDIT:
As #jsejcksn suggested (source-assets), I've tried to install react-app-rewired, using this config-overrides.js:
module.exports = function override(config, _env) {
let rules = config.module.rules[1].oneOf
rules.splice(rules.length - 1, 0, {
test: /\.txt/,
type: 'asset/source',
})
return config
}
But when I try to start the test server, it says:
$ react-app-rewired start
Failed to compile.
Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.module.rules[1].oneOf[8].type should be one of these:
"javascript/auto" | "javascript/dynamic" | "javascript/esm" | "json" | "webassembly/experimental"
-> Module type to use for the module
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Thanks to the suggestion given to me by #jsejcksn I succeeded in my intent.
I will add the solution for anyone who needs it:
1. Install dependencies
$ yarn add react-app-rewired raw-loader
2. Create config ovverride
config-overrides.js:
module.exports = function override(config, _env) {
let rules = config.module.rules[1].oneOf
rules.splice(rules.length - 1, 0, {
test: /\.txt$/i,
use: 'raw-loader',
})
return config
}
3. Include the txt into the component
// Page text
import PageText from 'content/page.txt'
const Component = () => {
return (
<div className="text">
<div dangerouslySetInnerHTML={{ __html: PageText }} />
</div>
)
}
export default Component
(P.S. I bet there's a loader that converts the file to a ReactComponent like for SVG files, but I didn't go any further than that)
You can simply use "embedding" to display your static file within a React component.
Using <embed>:
const Tos = () => {
return (
<Container className="flex-grow-1 tos">
<h2>Terms of Service</h2>
<embed type="text/html" src={TosText} />
</Container>
)
}
Note that with this approach you can't use any markup in your imported text file -- it will not render as markup, but simply as text, that's why the title is outside the <embed> in the example above.
This should render something like:
The content will be scrollable if it doesn't fit in the default <embed> box -- but you can control its size with styles or width and height attributes.
Using <iframe>:
Move your static document to the public folder of your app, and change the extension to .html, and then link to it simply:
const Tos = () => {
return (
<Container className="flex-grow-1 tos">
<iframe title="Terms of Service" src="./tos-text.html" />
</Container>
)
}
And this should look like this:
Again, this is just default look, you can change it with styling.
You can also use <embed> with the second approach (file in the public folder):
<embed type="text/html" src="./tos-text.html" />
You can see a live example on codesandbox.
For example, let's say you have a component that you're going to reuse many times in your app.
So you set it up to receive different/unique texts, multiple h2s and multiple ps as props.
const Sets = ({ oneH, twoH, threeH, oneP, twoP, threeP }) => {
return (
<div className="sets">
<div>
<h3>{oneH}</h3>
<p>{oneP}</p>
</div>
<div>
<h3>{twoH}</h3>
<p>{twoP}</p>
</div>
<div>
<h3>{threeH}</h3>
<p>{threeP}</p>
</div>
</div>
);
};
And then you use it as such:
<Sets
oneH="Blue Skies"
twoH="Good Times"
threeH="Hotels, Etc."
oneP="Lorem ipsum dolor sit amet consectetur adipisicing elit. Corrupti, veniam voluptatibus."
twoP="Different lorem ipsum dolor sit amet consectetur adipisicing elit. Corrupti, veniam voluptatibus."
threeP="Unique lorem ipsum dolor sit amet consectetur adipisicing elit. Corrupti, veniam voluptatibus."
/>
Is there too much text in the usage of it and is it too clunky? Is there some better way?
Try breaking it up even more, and putting the text sets into a JS array.
To make the source text more readable:
const texts = [
{
h:"abc",
p:"123"
},
{
...
}
]
And then pass this into your Sets element:
<Sets texts={texts} />
And then in the Sets, iterate the texts:
const Sets = ({ texts }) => {
return (
<div className="sets">
{texts.map(({h,p}) => (
<div>
<h3>{h}</h3>
<p>{p}</p>
</div>
)
</div>
);
};
This allows you to edit the source texts really easily in one place; it can even be loaded dynamically from an external source.
The single texts prop makes it easier to pass it in as you can see.
And finally the iteration allows you to escape having to type every single prop!
I know this question has been asked a lot, but I don't really understand the answer mainly because all the answer use "Class" and I was taught to use function. I don't even understand the difference but every time I try using a class component, nothing works.
I am really new to React.js and I have a hard time understanding how it works.
I need to show and hide - <p className='service_explication more'>{props.more}</p> - on the click of a button with a smooth animation.
I've tried with basic javascript but it doesn't work.
I've tried a lot of things and now I am stuck and I don't find a good explanation to what I need.
Could you please help me ? I really want to learn but I'm having a hard time.
Thank you so much for your help.
import React, { useState } from 'react';
import '../assets/Section.scss';
import '../assets/Services.scss';
function Services(){
function showMore(event){
}
const BlocService = (props) => {
return <div className='service_sub_container'>
<h1 className='service_name'>{props.name}</h1>
<p className='service_explication'>{props.explication}</p>
<p className='service_explication more'>{props.more}</p>
<button onClik={showMore}>Plus</button>
</div>
}
return (
<>
<div className='main_container'>
<div className='section_container'>
<div className='title_intro_container'>
<h1 className='section_title'>Nos Services</h1>
<p className='section_intro'>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Sequi alias iste ducimus tenetur saepe reprehenderit quasi reiciendis ab architecto.</p>
</div>
<div className='service_container'>
<BlocService name={'Développeur Front/Web'} explication={'Lorem ipsum dolor sit amet consectetur, adipisicing elit. Sequi alias iste ducimus tenetur saepe reprehenderit quasi reiciendis ab architecto.'} />
<BlocService name={'Lead developper'} explication={'Lorem ipsum dolor sit amet consectetur, adipisicing elit. Sequi alias iste ducimus tenetur saepe reprehenderit quasi reiciendis ab architecto.'} />
<BlocService name={'Architectes Front-end'} explication={'Lorem ipsum dolor sit amet consectetur, adipisicing elit. Sequi alias iste ducimus tenetur saepe reprehenderit quasi reiciendis ab architecto.'} />
<BlocService name={'Développeur Front/Web'} explication={'Lorem ipsum dolor sit amet consectetur, adipisicing elit. Sequi alias iste ducimus tenetur saepe reprehenderit quasi reiciendis ab architecto.'} />
</div>
</div>
</div>
</>
);
}
export default Services;```
First of all, don't feel bad for ask, everyone has to learn from the bottom.
Reactjs works making "renders" of the webpage in a dynamic way, so you have 2 options depending if you want a smooth animation or just a default show/hide.
If you want to show in a easy way, you can just make a condition which allows to create or not the desired div
function Component() {
const [showed, setShowed] = useState(false)
return (
<div>
<button onClick={()=>setShowed(!showed)}>Show/Hide</button>
{showed && <div>HELLO</div>}
</div>
)
}
If you want to create smooth animations you might want to need classes. With React you can just add or remove classes with no problem and let css do the job, so you can make
function Component() {
const [showed, setShowed] = useState(false)
return (
<div>
<button onClick={()=>setShowed(!showed)}>Show/Hide</button>
<div className={showed?classShowed:classHided>HELLO</div>
</div>
)
}
I hope this helps you
Everything can be done inside the BlocService component. Using a state and conditional rendering:
import React, {useState} from 'react'
const BlocService = (props) => {
const [more, setMore] = useState(false);
const showMore = () => {
setMore(!more);
}
return (
<div className='service_sub_container'>
<h1 className='service_name'>{props.name}</h1>
<p className='service_explication'>{props.explication}</p>
{more && <p className='service_explication more'>{props.more}</p>}
<button onClick={showMore}>Plus</button>
</div>
)
}
export default BlocService;
You also have to remove showMore from Services
In a functional component you would control state by the useState hook provided by react.
import React, {useState} from 'react'
function() services {
const [show, toggleText] = useState(false)
return (
<div>
<h2>Header text</h2>
<button onClick={toggleText(!show)}>Toggle text button</button>
{show && (<p>Text to toggle</p>)}
</div>
)
}
React Documentation
I'm using the <Card> component from Semantic-UI-React. I have a group of cards displaying some random information. I have the extra prop defined which renders a button. My idea is to have this button, when clicked, toggle/expand a div to display more information. I have looked around and not been able to find much on how to achieve this.
I looked into <Accordion> from semantic ui as well, but have not been able to get it to play nicely nested inside the card component.
I created a sandbox to show what I have so far and the general look of what I explained above.
For brevity I will only post the code of one card out of the group below.
<Card color="blue">
<Card.Content header="Elliot" textAlign="center" />
<Card.Content description="'Elliot is a sound engineer living in Nashville who enjoys playing guitar and hanging with his cat.'" />
<Card.Content extra>
<Button basic circular icon size="tiny">
<Icon name="plus circle" />
</Button>
Show More
</Card.Content>
</Card>
I agree with #brandon-r that you can handle the extra content being show by handling a state object (in my case an array). What I did differently from his example was to take advantage of the <Card.Content extra> component, which handles all the styles issues.
To handle opening and closing the extra content, I used a simple reducer. I like to use the useReducer hook on those UI interactions that need a more complex state handling. Then I created three components: one that shows the extra content when opened, another that shows the button to display the content, and a third one that toggles between the two. I did it this way to be able to generalise it in the future.
Anyways, here is the link to my forked CodeSandbox with my take on the solution:
https://codesandbox.io/embed/semantic-ui-card-extra-content-toggle-kybt2
I hope it helps
Edit #1
Added style={{height: "100%"}} to the card so they mantain their size when one of the cards is opened.
Edit #2
Add picture showing a card with a long description.
<Card color="blue" style={{ height: "100%" }}>
<Card.Content header="Elliot" textAlign="center" />
<Card.Content description="'Elliot is a sound engineer living in Nashville who enjoys playing guitar and hanging with his cat.'" />
<ExtraContentAccordion
content="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ac commodo diam, et tincidunt massa. Sed aliquet tortor purus, in pulvinar enim mattis ac. Maecenas vestibulum cursus lorem, quis fermentum enim lacinia a. Ut nec feugiat nisl. Morbi finibus hendrerit diam, id iaculis nibh feugiat sed. Sed non justo turpis. Fusce neque quam, facilisis eu aliquam vitae, hendrerit nec nulla. Integer metus sapien, dictum eget viverra et, dictum in lectus. Integer vitae dolor ut libero dictum tristique eget non nunc. Suspendisse diam urna, pretium sed elementum sed, fermentum eu leo. Donec augue tortor, rhoncus id pulvinar ac, fringilla eu est. Duis et ante tristique dui molestie maximus at ut enim. Curabitur facilisis tempor lorem quis scelerisque. Maecenas enim leo, mollis at egestas in, vulputate eget risus."
onToggle={toggleCard(1)}
open={state[1]}
/>
</Card>
If you want to expand to show more content, you can keep track of which cards are expanded with some react state. In the UI, you can use the state to determine if you should render the extra content for a particular card.
EX:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import { Card, Button, Icon } from "semantic-ui-react";
import "./styles.css";
function App() {
const [expanded, setExpanded] = useState({});
const cards = [1, 2, 3, 4, 5, 6];
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<Card.Group itemsPerRow={3}>
{cards.map(cardNumber => (
<Card color="blue">
<Card.Content header="Elliot" textAlign="center" />
<Card.Content description="'Elliot is a sound engineer living in Nashville who enjoys playing guitar and hanging with his cat.'" />
<Card.Content extra>
<Button
basic
circular
icon
size="tiny"
onClick={() =>
setExpanded({
...expanded,
[cardNumber]: !expanded[cardNumber]
})
}
>
<Icon name="plus circle" />
</Button>
{expanded[cardNumber] && (
<div style={{ height: 200 }}>
Extra content expanded for card {cardNumber}
</div>
)}
Show More
</Card.Content>
</Card>
))}
</Card.Group>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Here is a short sandbox to see what it looks like: https://codesandbox.io/s/modest-mayer-t12ot