Why is my SVG not appearing when passed as prop? - reactjs

I've looked through some of the other, similar questions on here, but none of the solutions seem to work for me.
I have a class component that represents a cell on a grid, and for each cell, I want there to be a specific background image that is revealed with the cell is hovered. The default component looks like this:
const Home = () => {
return (
<div id="main">
<Box title='About' svg={ BG1 } />
<Box title='Work' svg={ BG2 } />
<Box title='Portfolio' svg={ BG3 } />
<Box title='Resume' svg={ BG4 } />
</div>
);
};
I want to pass SVG components as background images, but can't seem to figure it out. I've imported each background image like this:
import { ReactComponent as BG1 } from './bg1.svg';
In the Box component, I've added state to handle some of the child elements, and the title prop seems to work out well, but the svg prop doesn't seem to work for me:
class Box extends React.Component {
constructor(props) {
super(props);
this.state = {
hovered: false,
};
}
hoverOn = e => {
this.setState({ hovered: true });
};
hoverOff = e => {
this.setState({ hovered: false });
};
render() {
const { hovered } = this.state;
const style = hovered ? { transform: 'translateY(-20%)', opacity: '.05' } : {};
return(
<section onMouseEnter={this.hoverOn} onMouseLeave={this.hoverOff}>
<a>
<div className="overlay" style={ style }></div>
<div className="bg" style={{ backgroundImage: `url(${this.props.svg})` }} ></div>
<div className="innerContent">
<h2>
{this.props.title}
</h2>
</div>
</a>
</section>
);
};
};
Whenever I inspect my developmental site, I just see <div class='bg'></div>. Any suggestions?

It's not working because BG1 is being set to the SVG file contents; not a link to the SVG file. I think you can just reference the filename in url("./bg1.svg") and it will expand it at build time. However, that probably won't work when you just pass in the filename as a prop. So - you could put the SVGs into the public folder and do something like this: {process.env.PUBLIC_URL + '/img/logo.png'}. Escape hatch doc.
With Vite you can do this: import modelGLB from './static/model.glb?url' to get the URL, but I forget the create-react-app equivalent.

Related

Access Gatsby Component from a function

I am trying to access a Gatsby component (Anime) from outside of it.
Can not figure out what instance name this would have or how to name it.
Here is my code:
import React from 'react'
import PropTypes from 'prop-types'
import PreviewCompatibleImage from '../components/PreviewCompatibleImage'
import Anime from 'react-anime';
import VisibilitySensor from 'react-visibility-sensor';
function onChange (isVisible) {
console.log('Element is now %s', isVisible ? 'visible' : 'hidden')
}
const FeatureGrid = ({ gridItems }) => (
<div className="columns is-multiline">
<VisibilitySensor onChange={onChange}>
<Anime delay={(e, i) => i * 100}
scale={[.1, .9]}
autoplay={false}>
{gridItems.map(item => (
<div key={item.text} className="column is-3">
<section className="section">
<div className="has-text-centered">
<div
style={{
width: '160px',
display: 'inline-block',
}}
>
<PreviewCompatibleImage imageInfo={item} />
</div>
</div>
<p>{item.text}</p>
</section>
</div>
))}
</Anime>
</VisibilitySensor>
</div>
)
FeatureGrid.propTypes = {
gridItems: PropTypes.arrayOf(
PropTypes.shape({
image: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
text: PropTypes.string,
})
),
}
export default FeatureGrid
I want to get the animation to trigger from the onChange function.
How do I get the name or set the name of the Anime component so I can access it from the function?
Or is there another way I should address this?
Using a Gatsby starter netlify CMS as the base, so extending on their code, but seems that const is not the route I should take.
I want the animation to trigger when it becomes visible.
Any suggestions?
According to the docs react-visibility-sensor :
You can pass a child function, which can be convenient if you don't need to store the visibility anywhere
so maybe instead of using the onchange function you can just pass the isVisible parameter, something like:
<VisibilitySensor>
{({isVisible}) =>
<Anime delay={(e, i) => i * 100}
// the rest of your codes here ...
</Anime>
}
</VisibilitySensor>
Otherwise you can convert this function to a react component and set states, etc..

React hide textarea but writable

I'm making text editor from bottom up.
I want to give the illusion that users are using textarea directly.
So I think that in one big <div/> put <textarea> and <p> and hide textarea and show value of <textarea> in <p>.
When user click word in <p>, Put cursor in appropriate textarea and write word
in textarea (<p> is used just to show result)
But when i give display: hidden for this, I cannot type anything in textarea.
How can i show illusion to user use textarea directly but not really.
class CodeEditor extends Component {
constructor(props) {
super(props);
this.state = {
textValue: ''
}
}
underline = () => {
let textVal = this.editor;
let Start = textVal.selectionStart;
let End = textVal.selectionEnd;
const oldState = this.state.textValue;
if (oldState.substring(Start,End) !== '') {
this.setState({textValue : oldState.substr(0,Start) + `<p style={{text-decoration-line:underline}}>` + oldState.substring(Start, End) + `</p>` + oldState.substr(End)})
}
};
render() {
const content = this.state.textValue;
return (
<div className={cx('text-editor')} onClick={() =>
this.editor.focus()}>
<textarea
className={cx('text-input')}
ref={ref => this.editor = ref}
value={this.state.textValue}
style={{display:'hidden'}}
onChange={(event) => {
this.setState({
textValue:event.target.value
});
}} />
<div className={cx('editor-buttons')}>
<button onClick={this.underline}> Underline </button>
</div>
<p dangerouslySetInnerHTML={{ __html: content }} />
</div>
)};
}
export default CodeEditor;
You could utilize contenteditable html5 feature:
https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable
For you all looking for an answer for this, here's an approach you can take:
1) Textarea/input has a border, so hide it with border-style: none and hide when focus with textarea:focus, input:focus { outline: none; }.
2) Change the background color to be the same as parent component.
3) Done :)

If statement which determines which React element to be rendered, weird behaviour

I created a Sprite component which takes in 'icon' as a prop and determines which svg to render but I'm experiencing some weird behaviour.
I've had to resort to this method because I haven't been able to find a way to work with svg's (how to change their fill color!)
const Sprite: React.SFC<ISpriteProps> = (props: ISpriteProps) => {
const color = props.color || '#ACACAC'
let icon
if (props.icon === 'pin') {
icon = <Pin color={color} />
} else if (
props.icon === 'profile'
) {
icon = <Profile color={color} />
}
return (
<React.Fragment>
{icon}
</React.Fragment>
)
}
export default Sprite
Using the <Sprite/> in my <Navigation/> component like so
<Nav styles={this.state.styles}>
<ProfileIcon onClick={this.onProfileSelected}>
<Sprite icon='profile'
color={this.state.viewing === 'profile' ? '#5A4DB2' : ''} />
</ProfileIcon>
<LogoIcon onClick={this.onLogoSelected}>
<h1>
<img src={logo} alt="zestly" />
</h1>
</LogoIcon>
<MessagesIcon>
<img src={chat} onClick={this.onMessageSelected}
alt="messages" />
</MessagesIcon>
</Nav>
and in my <CardBlock/> component like so
const Card: React.SFC<{}> = (place) => {
return (
<CardOuter>
<CardPhoto>
<span>
<Sprite icon='pin' color='#fff' />Fitzroy</span>
</CardPhoto>
<CardDetails>
<div>
<h3>Vegie bar</h3>
<h4>7:00pm</h4>
</div>
<ProfileIcons />
</CardDetails>
</CardOuter>
)
}
For some reason the icon prop I choose to pass in to the Navigation component Sprite is determining what is rendered for the Sprite's in CardBlock as well.
E.g if I make it 'profile' in Navigation it will make all sprites render the profile icon for Sprite as well even though I specifically pass in 'pin'. If I switch it to 'pin' in Navigation, all CardBlock sprites will render the pin icon.
I don't understand, is this some weird React render quirk?
EDIT: So I thought it was something to do with stateless functional component rendering so I changed Sprite to a Component
class Sprite extends React.Component<ISpriteProps, {}> {
public render() {
const color = this.props.color || '#ACACAC'
const icons = {
'profile': Profile,
'pin': Pin
}
const ActiveIcon = icons[this.props.icon]
return (
<ActiveIcon color={color} />
)
}
}
Still no love, it's rendering ALL of them as profile icons if I pass 'profile' as icon prop to Sprite in Navigation component.
EDIT: Solved. It was something wrong with the svg's
you could use classnames library to select what class should be added to React element, including svg. And in css you give fill rule that you want. https://github.com/JedWatson/classnames

styled component computing style but not applying it

Dear genius StackOverflowians,
I am trying to write an app where users can configure questions and answers, along with defining help text for each question. I'm writing this in typescript React - which is handy when you want to define types of answers for questions.
I want to have a button next to the question that shows/hides a styled document. The button looks and works great, but the document that is hidden/shown doesn't get the generated style class that ought to be associated with it.
Here is the functional component to display the help document:
let HelpTextBody = function(props: { helpDocument: DocumentationStore }) {
return (
<div>
{props.helpDocument.toReallySimple().map(tok => {
return React.createElement(tok.tag, null, tok.content);
})}
</div>
);
};
tok comes from a custom class DocumentationStore that is pretty much a wrapper around markdown-it, a handy js library for working with md files, which I would like my users to write their helptext in (and store it that way).
So I do this (in a different module for DocumentationStore class):
toReallySimple(): MdJson[] {
let bigParsed = this.md_.parse(this.Text, null).filter(
t => return t.type == "inline" || t.type.indexOf("open") > 0
});
Later on, I style HelpTextBody with:
const StyledHelpDocument = styled(HelpTextBody)`
background-color: lightslategray;
`;
Keeping it simple now so I can just see if it's working...
I then include it in a component with the button that I export:
class HelpText extends React.Component<helpProps, helpState> {
constructor(props: helpProps) {
super(props);
this.state = {
hidden: true
};
}
swapHidden() {
this.setState({
hidden: !this.state.hidden
});
}
render() {
if (this.state.hidden) {
return (
<span>
<StyledButton
itemScope={this.state.hidden}
onClick={() => this.swapHidden()}
>
Need Help?
</StyledButton>
</span>
);
} else {
return (
<span>
<StyledButton onClick={() => this.swapHidden()}>
Hide Help
</StyledButton>
<StyledHelpDocument helpDocument={this.props.helpDocument} />
</span>
);
}
}
So I webpack it all and get stuff into the browser, and what I get back is this style tag (after clicking the button), which looks right:
<style data-styled-components="">
/* sc-component-id: sc-bdVaJa */
.sc-bdVaJa {} .gscXTZ{background:red;color:white;font-size:1em;margin:1em;padding:0.25em 1em;border:2px solid red;border-radius:3px;}.iwtdKP{background:white;color:red;font-size:1em;margin:1em;padding:0.25em 1em;border:2px solid red;border-radius:3px;}
/* sc-component-id: sc-bwzfXH */
.sc-bwzfXH {} .hAvMqj{background-color:lightslategray;}</style>
But my html for the document is missing the reference to the class (.hAvMqj I guess?)
<span>
<button class="sc-bdVaJa iwtdKP">Hide Help</button>
<div><p>Here the text is grey</p></div>
<!-- ^This^ is the StyledHelpDocument... no class!-->
</span>
So where am I going wrong? I don't understand why it generates the style, and the component's HTML renders... but the class isn't applied to the component! What do you think?
Your styled-components class isn't being applied because you're styling a custom component, but you haven't included className as a prop. Add className as an optional prop in the component you're styling, and also be sure to apply className somewhere in the render method for that component. For your case, it should be added like so:
let HelpTextBody = function(props: { helpDocument: DocumentationStore, className: string }) {
return (
<div className={props.className}>
{props.helpDocument.toReallySimple().map(tok => {
return React.createElement(tok.tag, null, tok.content);
})}
</div>
);
};

Ckeditor disable auto inline won't disable inline from being selected on page load

I'm trying to develop a simple CMS for my page. I want it to where I can edit and delete a users reply on click of a button. I got the delete functionality done so I figured for the reply functionality I would use CKeditor. What I'm struggling with is not being able to disable the autoinline feature. I can still select my div on load of the page rather than clicking a button to enable the inline feature but I don't know what I am doing wrong?
I have tried setting the feature directly in my index.html file, a custom js script file and the config.js ckeditor file but none worked. I am using Ckeditor 4.
this is the snippit of code I'm trying to use to disable inline on my div element but it's not working and I don't know why, i currently have it placed in a custom.js script file and I'm calling it from my index.html file
CKEDITOR.disableAutoInline = true;
Here is my code for my replies page:
import React from 'react';
import CKEditor from 'react-ckeditor-component';
import ForumpageService from '../../services/forumService';
import appController from '../../controllers/appController';
class Forumreplies extends React.Component {
constructor(props){
super(props);
this.elementName = "editor_" + this.props.id;
this.editReply = this.editReply.bind(this);
this.state = {
reply: '',
errorMsg: '',
isLoggedin: false,
// Ck Editor State
reply: '',
}
}
async componentDidMount(){
const topicId = this.props.match.params.topicid
const postDetails = await ForumpageService.replyDetails({topicId: topicId})
this.setState({
postDetails: postDetails[0],
topicId: topicId
})
await this.postReplies();
}
// Edit the reply
async editReply(id, e){
//CKEDITOR.inline(this.elementName);
}
async postReplies(){
const repliesData = await ForumpageService.postreplyDetails({topicId: this.state.topicId})
await this.setState({repliesData});
}
render(){
const repliesData = currentReply.map((row, index) => {
return (
<div className="row" id="reply-messages" key={index}>
<div className="col-md-8" suppressContentEditableWarning contentEditable="true" id={this.elementName}>
<p dangerouslySetInnerHTML={{__html: row.reply_message }} />
</div>
<div className="col-md-2">
{this.state.isloggedinuserId == row.reply_user_id && this.state.isLoggedin == true ? <i className="far fa-edit" onClick={this.editReply.bind(this, row.reply_id)} title="Edit this reply?"></i> : null }
</div>
</div>
})
return (
<div className="container" id="forum-replies">
<div className="row">
{repliesData}
</div>
</div>
)
}
}
export default Forumreplies;
Instead of creating a div and calling CKEDITOR.inline, you should try to use the react-ckeditor-component as its own way (not as an inline editor).
You could do something like: if the button wasn't pressed, render a div with the text content, but after the button was pressed, render a <CKEditor /> component as in the documentation
There is no documented way to set the editor as inline in this package that you are using.
EDIT:
I can see you are not using the react-ckeditor-component features, but following what you've done so far, you could set the contentEditable="true" property of the div only when the button is pressed:
<div className="col-md-8" suppressContentEditableWarning contentEditable={this.state.isEditing} id={this.elementName}>
<p dangerouslySetInnerHTML={{__html: row.reply_message }} />
</div>
And then set the isEditing to true on the onClick handler. Your component will update and then re-render with the contentEditable property set to true

Resources