How can I add remove icon on Fabric element Reactjs? - javascript

I'm using fabric in my react app. I have a button which add text to canvas. Now I want to set remove icon on top left of fabric Text and remove that text when clicked on icon Reactjs? I exactly don't know how to handle event listeners when click on canvas elements.
import React from 'react';
import { fabric } from 'fabric';
class Designer extends React.Component {
componentDidUpdate() {
console.log('componentDidUpdate ');
}
componentDidMount() {
// Make a New Canvas
this.__canvas = new fabric.Canvas('meCanvas', {
preserveObjectStacking: true,
height: 812,
width: 375,
backgroundColor: 'gray'
});
let rect = new fabric.Rect({
left: 100,
top: 150,
fill: 'red',
width: 200,
height: 200
});
this.__canvas.add(rect);
}
addText() {
let newID = new Date().getTime().toString().substr(5);
let text = new fabric.IText('текст', {
fontFamily: 'arial black',
left: 100,
top: 100,
myid: newID,
objecttype: 'text'
});
this.__canvas.add(text);
//this.addLayer(newID, 'text');
}
render() {
console.log('render');
return (
<div>
<button id="texts" onClick={() => this.addText()}>
TEXT PLEASE
</button>
<canvas id="meCanvas" ref="myFabric" onClick={console.log('onClick')} />
</div>
);
}
}
export default Designer;

Related

How to make chat like UI with chat bubbles in React JS

I have some JSON data in dummyData. I am not sure how can I place the chat bubbles on left and right according to the direction. I am using Material UI and context API. Image for the reference. I don't want to use any library other than material UI.
Currently, every chat bubble is positioned to the left. How to position bubbles according to the direction. Code so far (CodeSandbox):
import React from 'react';
import makeStyles from '#material-ui/core/styles/makeStyles';
const useStyles = makeStyles(theme => ({
container: {
bottom: 0,
position: 'fixed'
},
bubbleContainer: {
width: '100%'
},
bubble: {
border: '0.5px solid black',
borderRadius: '10px',
margin: '5px',
padding: '10px',
display: 'inline-block'
}
}));
const ChatLayout = () => {
const classes = useStyles();
const dummyData = [
{
message: '1: This should be in left',
direction: 'left'
},
{
message: '2: This should be in right',
direction: 'right'
},
{
message: '3: This should be in left again',
direction: 'left'
}
];
const chatBubbles = dummyData.map((obj, i = 0) => (
<div className={classes.bubbleContainer}>
<div key={i++} className={classes.bubble}>
<div className={classes.button}>{obj.message}</div>
</div>
</div>
));
return <div className={classes.container}>{chatBubbles}</div>;
};
export default ChatLayout;
You can create separate div of chat bubble and apply CSS. And where you are receiving messages append the bubble div to your user list.

changing the color of card in material ui conditionally?

const styles = {
card: {
minWidth: 240,
color: 'green'
},
title: {
fontSize: 14
},
pos: {
marginBottom: 12,
padding: 10,
margin: 10
}
};
function handleToggle(colorDecider) {
if (colorDecider)
styles.card.color = 'blue';
else
styles.card.color = 'red';
}
Here, I am trying to change the color of the card using handleToggle function based on the value of colorDecider. But the code doesn't change. Yet I have checked the styles.card.color using console.log, changed color is printed in the console. But, color doesn't actually change in the card
Setting a property somewhere in an object does not magically rerender the related parts of the page. If you need something stateful, move it into the related conponent's state:
class Colorful extends React.Component {
constructor(...props) {
super(...props);
this.state = { color: "red" };
}
changeColor(color) { this.setState({ color }); }
render() {
return <div
style={{ color: this.state.color }}
onClick={() => this.changeColor("blue")}
>Click me!</div>;
}
}
Agree with #jonaswilms but might add that you can also force an update like this:
function handleToggle(colorDecider) {
if (colorDecider)
styles.card.color = 'blue';
else
styles.card.color = 'red';
this.forceUpdate(); // force rerender
}
The above assumes, of course that you have bound the right this value e.g.
<div onClick={handleToggle.bind(this)} />

another case when the state of a component changes but seems that the component does not

I have a react component that has a state variable:
showEditor
when showEditor is false it is supposed to show only a div with some number inside it (initially showEditor is false). If this state variable is true my react component is supposed to show a textbox and a button with "save" label inside another div -making dissapear the first div with the number-. This textbox will be used to change the number. For the first div (the one that only shows a number) I defined:
<div onClick={this.showEditorProc}>
{ this.state.showEditor ?
<div ....>
{ just the numeric value }
</div>
:
<div ....>
textBox
<button onClick={save and make show Editor false}>
Save
</button>
</div>
</div>
the function this.showEditorProc will modify the state of the showEditor variable to true and the save button and textbox components will appear (inside another div too). I created a function that will executed if the save button is clicked. This function modifies the showEditor variable to false however, I can not see the div with just the numeric value. Instead I still see the textbox with the save button. Is there something else I could be missing? Here it is the code of my component:
import React from 'react';
import ReactDOM from 'react-dom';
import NumberFormat from 'react-number-format';
export class NumericBox extends React.Component {
constructor() {
super();
this.state = {
enteredValue: '',
showNumEditor: false,
index: ''
};
this.showNumericEditor = this.showNumericEditor.bind(this);
this.handle_enteredValue_change = this.handle_enteredValue_change.bind(this);
this.saveCellInfo = this.saveCellInfo.bind(this);
this.loadBasicInformation = this.loadBasicInformation.bind(this);
}
saveCellInfo(e){
alert(this.state.index);
/* cellAuxParams = new Map([['updateCellValue', this.state.updateCellValue]]); */
console.log('numericBox.js>saveCellInfo>cellAuxParams= ------------------------------------------------ ' + 28);
console.log(this.props.cellAuxParams);
var f = this.props.cellAuxParams.get('updateCellValue');
this.setState({showNumEditor: false}, () => f(this.state.Index, '77'));
}
handle_enteredValue_change(values) {
const {formattedValue, value} = values;
// formattedValue = $2,223
// value ie, 2223
this.setState({enteredValue: value});
}
showNumericEditor()
{
this.setState({showNumEditor: true})
}
loadBasicInformation()
{
this.setState({enteredValue: this.props.enteredValue,
index: this.props.index
});
}
componentDidMount(){
this.loadBasicInformation();
}
componentWillReceiveProps(nextProps){
alert(nextProps.enteredValue);
this.setState({enteredValue: nextProps.enteredValue}, () => this.loadBasicInformation());
}
render() {
const table4controls = {
display: 'table',
width: this.props.style.width,
backgroundColor: 'white',
border: '0px solid #666666',
borderSpacing: '0px',
paddingBottom: '0em',
paddingTop: '0em'
};
const table4controls_RowStyle = {
display: 'table-row',
width: 'auto',
clear: 'both',
borderBottom: '5px'
};
const table4controls_ColsStyleA = {
float: 'left',
display: 'table-column',
width: '60px',
backgroundColor: 'white'
};
const table4controls_ColsStyleB = {
float: 'left',
display: 'table-column',
width: '20px',
backgroundColor: 'white'
};
const table4controls_ColsStyleC = {
float: 'left',
display: 'table-column',
width: '20px',
backgroundColor: 'white'
};
const btnStyle={
};
return (
<div onClick={this.showNumericEditor}>
{ this.state.showNumEditor ?
<div style ={table4controls}>
<div style={table4controls_RowStyle}>
<div style={table4controls_ColsStyleA}>
<NumberFormat style={{width: '60px'}}
value={this.state.enteredValue}
thousandSeparator={true}
prefix={this.props.prefix}
onValueChange={this.handle_enteredValue_change}
/>
</div>
<div style={table4controls_ColsStyleB}>
<button style={btnStyle} onClick={() => this.saveCellInfo(this.state.index)}>
▲
</button>
<button style={btnStyle} onClick={() => this.saveCellInfo(this.state.index)}>
▼
</button>
</div>
<div style={table4controls_ColsStyleC}>
<button style={btnStyle} onClick={(e) => {this.saveCellInfo(e, this.state.index)}}>
Save
</button>
</div>
</div>
</div>
:
<div syle={table4controls_ColsStyleA}>
{this.state.enteredValue}
</div>
}
</div>
);
}
}
You have an onClick={this.showNumericEditor} handler on the surrounding div, so when you press the save button, the click event bubbles up and invokes a this.setState({showNumEditor: true}).
To fix it, you can either restructure the rendering or call e.stopPropagation(); at the start of saveCellInfo. Also note that some of your this.saveCellInfo calls are not passing the event.

How to appendTo React Components as simple as jQuery can

As I try to get into React I find I'm running through lists of things I can do very simply in jQuery but are an absolute nightmare in React.
https://codepen.io/PocketNinjaDesign/pen/boJoEd
have modified the goal from 2 elements to nth
The goal is that nth elements on the page can popup inside of any element / component I want on the page. I achieve this the good old fashion way by adding a data attribute containing Json with an array of components to appendTo.
You can also use JS to turn a component into a ninja and passing an object through containing the list of components to appear in.
$(function() {
$('.other-ninja').Ninja({
components: ['title', 'header', 'angry']
});
});
So, imagining these are React Components now, all different kinds of components, but the ninja block(s) can be told they can appear in any components they want.
How is that possible with React without going through a ball ache of declaration and indigestion?
Here is my code for the jQuery popup appendTo script as what I want to achieve in React.
Have altered the code since the answer I received as I don't think I
was putting my question across correctly.
HTML
<div class="ninja" data-ninja='{"components": ["title", "happy", "sad", "stinky", "header", "AnotherComponent"]}'></div>
<div class="mongoose" data-ninja='{"components": ["happy", "sad", "stinky", "angry", "footer"]}'></div>
<div class="other-ninja"></div>
<h1 class="title">Getting React to work like simple jQuery :-D</h1>
<p>Where ALL HTML elements on this page represent React Components. All components being
split into different files and imported using babel es6 compiler</p>
<div class="header">
<div class="angry"></div>
</div>
<div class="content">
<ul>
<li>Just a list showing more component depth</li>
<li>
<div class="someOtherComponent">
<div class="sad"></div>
</div>
</li>
<li>Trying to show the code works regardless of where the elements are</li>
</ul>
<div class="AnotherComponent">
<div class="SomeOtherComponent">
<div class="WhatAnotherComponent"><div class="happy"></div></div>
</div>
</div>
</div>
<div class="footer">
<div class="whatever">
<div class="stinky"></div>
</div>
</div>
SCSS
Removed the SCSS as it was detracting from the main focus of markup and javascript
Script
$(function() {
function Ninja(e, options) {
var $this = $(e);
options = $.extend({}, $this.data('ninja'), options);
var componentList = options.components;
setInterval(function() {
var randomNum = Math.floor(Math.random() * componentList.length);
$this.appendTo('.' + componentList[randomNum]);
}, 1000);
}
$.fn.Ninja = function(options) {
$(this).each(function(i, e) {
Ninja(e, options);
});
};
$('[data-ninja]').each(function(i, e) {
$(e).Ninja();
});
});
$(function() {
$('.other-ninja').Ninja({
components: ['title', 'header', 'angry']
});
});
React might seem like "nightmare" when you used to use jQuery but actually using React is really easy. You just need to adjust your coding logic to suit React guidelines.
The code you shared can be implemented in React many different ways. I did a small working version for you to compare. At first-look it might seem a lot more code to achieve same simple affect but React has a lot more control over the DOM (Virtual DOM) and can be manipulated a lot more different ways. Most of the code in this example is same for all components. You just need to put consideration render method. Appending or removing a component/element can be achieved by just a simple if statement. Also component logic gives you a big flexibility and re-usability. There are a lot of good examples and tutorials online that can show you how you can solve thing in react way. A really good start point is React Docs and awesome-react.
import React from 'react';
import { render } from 'react-dom';
const styles = {
component: {
position: 'absolute',
width: 100,
height: 100
},
happy: {
position: 'absolute',
width: 100,
height: 100,
top: 100,
left: 40,
backgroundColor: 'yellow'
},
sad: {
position: 'absolute',
width: 100,
height: 100,
top: 20,
left: 180,
backgroundColor: 'blue'
},
angry: {
position: 'absolute',
width: 100,
height: 100,
top: 160,
left: 500,
backgroundColor: 'red',
},
stinky: {
position: 'absolute',
width: 100,
height: 100,
top: 210,
left: 300,
backgroundColor: 'green'
},
ninja: {
position: 'absolute',
zIndex: 100,
width: 50,
height: 50,
backgroundColor: '#000'
},
deathstar: {
position: 'absolute',
zIndex: 100,
width: 20,
height: 20,
backgroundColor: '#444'
}
};
const Ninja = () => (<div style={styles.ninja}></div>);
const DeathStar = () => (<div style={styles.deathstar}></div>);
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
types: ['happy', 'sad', 'angry', 'stinky'],
ninja: 0,
deathstar: 0
};
this.intervalNinja = null;
this.intervalDeathstart = null;
}
randomNumber() {
return Math.floor(Math.random() * (3 - 0 + 1)) + 0;
}
componentWillUnmount() {
clearInterval(this.intervalNinja);
clearInterval(this.intervalDeathstart);
this.intervalNinja = null;
this.intervalDeathstart = null;
}
componentDidMount() {
this.intervalNinja = setInterval(() => {
this.setState({
ninja: this.randomNumber()
});
}, 1082);
this.intervalDeathstart = setInterval(() => {
this.setState({
deathstar: this.randomNumber()
});
}, 987);
}
render() {
return(
<div>
{this.state.types.map((type, index) => (
<div style={styles[type]}>
{this.state.ninja === index && <Ninja />}
{this.state.deathstar === index && <DeathStar />}
</div>
))}
</div>
)
}
}
render(<App />, document.getElementById('root'));

React onClick event does not fire setState

I am trying to build an image gallery in React.js, everything went smoothly until now.In gallery I am creating Thumbnail objects - on click this will fire "mini gallery" with all pictures from particular project and description for project. However to get back to main gallery I am creating "CLOSE" button within "mini gallery" with an attached handler.Thumbnail click works, however Close Button does not. Please see code attached below.I will be very grateful for any help!
This is Main Gallery:
import React from 'react';
import Thumbnail from '../components/Thumbnail';
export default class Drawings extends React.Component {
render () {
const linkPrefix = "./life/";
const imageS = ".800.jpg";
const imageL = ".jpg";
const lifePics = [
{
name: "One",
filename: [
"lifedrawing1",
],
descr: "one",
},
{
name: "Two",
filename: [
"lifedrawing2",
"lifedrawing2ed",
"lifedrawing2ed2",
],
descr: "two",
},
{
name: "Three",
filename: [
"lifedrawing3",
],
descr: "three",
},
]
return (
<div id="Drawings" className="container row around wrap">
{lifePics.map(
(picture, i) =>
<Thumbnail
key={i}
linkPrefix={linkPrefix}
filename={picture.filename}
imageS={imageS}
imageL={imageL}
/>
)}
</div>
);
}
}
This is each Thumbnail:
import React from 'react';
export default class Thumbnail extends React.Component {
constructor(props) {
super(props);
this.state = {
viewerDisplay: "hidden",
};
}
thumbnailClick(event) {
this.setState({
viewerDisplay: "visible",
});
}
closeViewer(event) {
this.setState({
viewerDisplay: "hidden",
});
}
render () {
const thumbnailStyle = {
width: '45%',
height: '300px',
backgroundImage: 'url('+this.props.linkPrefix + this.props.filename[0]+this.props.imageS+')',
backgroundSize: 'cover',
marginBottom: '10px',
cursor: 'pointer',
};
var viewerStyle = {
position: "absolute",
top: "300px",
right: "50px",
bottom: "10px",
left: "50px",
visibility: this.state.viewerDisplay,
background: "black",
cursor: "auto",
};
const viewerColStyle = {
width: "50%",
height: "100%",
}
return (
<div
className="thumbnail container col between"
style={thumbnailStyle}
onClick={this.thumbnailClick.bind(this)}
>
<div
id="Viewer"
className="viewer container row between"
style={viewerStyle}
>
<div
id="PicList"
className="container col around"
style={viewerColStyle}
>
Thumbnails
{//map function for thumbnails of particular gallery
}
</div>
<div
id="ProjectDescr"
className="container col around"
style={viewerColStyle}
>
Project Descr
</div>
<button
onClick={this.closeViewer.bind(this)}
>CLOSE</button>
</div>
</div>
);
}
}
you should add event.stopPropagation() to the closeViewer function to prevent propagation of the click event to Thumbnail div element
closeViewer(event) {
event.stopPropagation();
this.setState({
viewerDisplay: "hidden",
});
}
Here is an example without stopPropagation
<body>
<div onclick="clickDiv()">
<button onclick="clickButton()">Test</button>
</div>
<script>
function clickButton() {
alert('clickButton');
}
function clickDiv() {
alert('clickDiv');
}
</script>
</body>
Here is an example with stopPropagation
<body>
<div onclick="clickDiv()">
<button onclick="clickButton(event)">Test</button>
</div>
<script>
function clickButton(e) {
e.stopPropagation();
alert('clickButton');
}
function clickDiv() {
alert('clickDiv');
}
</script>
</body>
You should bind your click handler in the constructor or make it an arrow function in order to pass context:
thumbnailClick = (event) => {
this.setState({
viewerDisplay: "visible",
});
}

Categories

Resources