react-signature-canvas clears initial user input - reactjs

This signature input works but it has a quirk that has stumped us for some time now.. If the user starts signing the signature pad, as soon as they lift their finger or mouse, their input will be erased - but then the signature pad will work as expected and no longer clear the input.
If I remove this section..
if (!input.value && this.sigRef && this.sigRef.clear) {
this.clear();
}
it will no longer erase the first user input - but then if you go and view another form, this previous signature will appear (although it's not actually persisted in the database).
At this time we're requiring our users to "sign" twice in order to avoid seeing a false signature which may be confusing. Anyone know what it could be? Thanks for taking a look!
import React, { Component } from "react";
import { Button, Form } from "semantic-ui-react";
import SignatureCanvas from "react-signature-canvas";
class SignatureInput extends Component {
sigRef = {};
fieldRef = {};
state = {
trimmedDataUrl: ""
};
canvasWidth = 200;
canvasHeight = 100;
clear() {
this.sigRef.clear();
}
componentDidMount() {
const { input, enabled } = this.props;
this.setState({ trimmedDataURL: input.value.data });
this.sigRef.fromDataURL(input.value.data);
if (!enabled) {
this.sigRef.off();
}
}
render() {
const { input, placeholder, enabled } = this.props;
const { trimmedDataUrl } = this.state;
if (!input.value && this.sigRef && this.sigRef.clear) {
this.clear();
}
return (
<Form.Field
className="signature-holder"
style={{ width: "100%", height: "100%" }}
>
<div
ref={fieldRef => {
this.fieldRef = fieldRef;
if (fieldRef != null) {
this.canvasWidth = fieldRef.getBoundingClientRect().width;
}
}}
>
<label>{placeholder}</label>
<input type="hidden" name={input.name} value={trimmedDataUrl} />
<SignatureCanvas
onEnd={() => {
const imageData = this.sigRef
.getTrimmedCanvas()
.toDataURL("image/png");
this.setState({ trimmedDataURL: imageData });
input.onChange({ name: input.name, data: imageData });
}}
ref={ref => {
this.sigRef = ref;
}}
canvasProps={{
width: this.canvasWidth,
height: this.canvasHeight,
className: "signature-canvas"
}}
/>
{enabled && (
<Button
onClick={() => this.clear()}
type="button"
content="Clear"
/>
)}
</div>
</Form.Field>
);
}
}
export default SignatureInput;
CSS
.signature-holder {
position: relative;
border: 1px solid gray;
display: block;
margin: 0;
-webkit-appearance: none;
padding: 0.78571429em 1em;
background: $white;
border: 1px solid rgba(34, 36, 38, 0.15);
outline: 0;
color: rgba(0, 0, 0, 0.87);
border-radius: 0.28571429rem;
box-shadow: 0 0 0 0 transparent inset;
-webkit-transition: color 0.1s ease, border-color 0.1s ease;
transition: color 0.1s ease, border-color 0.1s ease;
font-size: 1em;
line-height: 1.2857;
resize: vertical;
label {
position: absolute;
top: 5px;
left: 5px;
color: lightgray !important;
}
button {
position: absolute;
bottom: 5px;
right: 5px;
}
}

Figured it out..
componentWillUnmount() {
this.setState({ trimmedDataURL: undefined });
this.sigRef = {};
}
And then removed the portion that checks for an input and clears it..

Related

How do I dynamically add a div where mouse was clicked in React?

I'm new to react and wonder how to do weird code stuff. I have a div component that I need to add child divs to depending on where I clicked on the div. I could do this easily in vanilla JS - here is a code sandbox of JS of what I want to do : https://codepen.io/Webasics/pen/YXXyEO
here is what I have in react so far (this is inside my App component):
const imgAdder = (e) => {
console.log(e.pageX, e.pageY)
}
<main onClick={imgAdder} </main>
$(document).ready(function() {
$(this).click(function(e) {
var x = e.pageX;
var y = e.pageY;
$('<div/>').css({
'top': y,
'left': x
}).appendTo('body');
});
});
div {
background-color: red;
width: 50px;
height: 50px;
position: absolute;
transform: translate(-50%, -50%);
/* optional */
border: 1px solid black;
/* optional */
}
h2 {
z-index: 10;
/* optional */
/* This always keeps the title on top*/
position: absolute;
}
body {
background-color: #E1E7E8;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h2>Click anywhere</h2>
Any directions would be lovely ! thank you.
function App() {
// declare array of boxes
const [boxes, setBoxes] = useState([]);
const handleClick = ({ pageX, pageY }) => {
// on every click push a new coordinate to the boxes array
setBoxes((boxes) => [...boxes, { x: pageX, y: pageY }]);
};
return (
<div className="app" onClick={handleClick}>
// display boxes
{boxes.map((box) => (
// map coordinates to left and top
<div className="box" style={{ left: box.x, top: box.y }}></div>
))}
</div>
);
}
Styles, mostly copied from the codepen
.app {
width: 100%;
height: 100vh;
}
.box {
position: absolute;
width: 50px;
height: 50px;
background: red;
transform: translate(-50%, -50%);
}
sandbox
Weird, but I like it!
https://codesandbox.io/s/elated-meadow-zuerrg?file=/src/App.js
I would simply use useEffect to register a click handler on the document and on click, add elements to a state array.
Finally, render those elements onto the page.
import { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const elements = useDynamicElements();
return (
<>
<h2>Click anywhere</h2>
{elements}
</>
);
}
const useDynamicElements = () => {
const [state, setState] = useState([]);
useEffect(() => {
const handler = (event) => {
setState((previous) => [
...previous,
<div style={{ top: event.pageY, left: event.pageX }} />
]);
};
document.addEventListener("click", handler);
return () => document.removeEventListener("click", handler);
});
return state;
};
An over simplified example in React could be like this:
This version can run in the snippets below for convenience.
const App = () => {
const [boxList, setBoxList] = React.useState([]);
const handleClick = (e) => {
if (e.target.classList.contains("btn")) {
setBoxList([]);
return;
}
setBoxList((prev) => {
const { pageX, pageY } = e;
const newBox = { left: pageX, top: pageY };
return [...prev, newBox];
});
};
return (
<div className="app" onClick={handleClick}>
<button className="btn">CLEAN UP</button>
<h2>Click anywhere</h2>
{boxList.length > 0 &&
boxList.map((box, index) => (
<div className="box" style={{ top: box.top, left: box.left }} key={index}></div>
))}
</div>
);
};
ReactDOM.render(<App />, document.querySelector("#root"));
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.app {
width: 100%;
height: 100vh;
background-color: pink;
position: relative;
}
.box {
background-color: #000;
width: 50px;
height: 50px;
position: absolute;
transform: translate(-50%, -50%);
border: 1px solid black;
}
h2 {
top: 50%;
left: 50%;
position: absolute;
transform: translate(-50%, -50%);
}
.btn {
margin: 15px;
padding: 15px;
background-color: #fff;
border: 0;
border-radius: 12px;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js"></script>

how to apply a multiconditional to style a react component?

I have a form where the borders of the inputs have 2 colors
-Grey: when the component is loaded
-blue: when the input is not empty
and I would like to apply a third color when the user presses the register button and the inputs are not empty that changes the border color to red. but I do not know how
Page
const hanleClear= () => {
setCompania(0);
setDepartamento(0);
setUnidad(0);
setLocalidad(0);
setActivoTipo(0);
setActivoTipoCatego(0);
setMarca("");
setColor("");
setModelo("");
setComponente("");
setSerial("");
setObservacion("");
};
const hanleSerial= (e) => {
setSerial(e.target.value.toUpperCase());
};
const handleSumit = function (e) {
e.preventDefault();
if (serial === 0) {
// Apply color red.
}
let form = {
idinformacion: compania,
iddepartamento: departamento,
idunidad: unidad,
};
let Data = JSON.stringify(form );
ServiceSerial.Create(Data);
};
<InputGroup
input_label={"Serial"}
input_type={"text"}
input_value={serial}
input_placeholder={"Serial"}
state_name={serial}
set_state_name={setSerial}
on_change={hanleSerial}
/>
<button onClick={hanleSumit}>Register</button>
<br />
<button onClick={hanleClear}>Limpiar</button>
<br />
Componenet
import {
Input,
Label,
InputGroupContainer,
WrapperInput,
} from "../components/FormStyled";
const InputGroup = function ({
input_label,
input_type,
input_value,
input_name,
input_placeholder,
on_change,
on_key_down,
}) {
return (
<InputGroupContainer>
<Label>{input_label}</Label>
<WrapperInput>
<Input
type={input_type}
placeholder={input_placeholder}
value={input_value}
name={input_name}
onChange={on_change}
onKeyDown={on_key_down}
className={!input_value ? "" : "Activated"}
/>
</WrapperInput>
{/* <span>{errorMessage}</span> */}
</InputGroupContainer>
);
};
export default InputGroup;
css
// LABEL
export const Label = styled.label`
font-size: 16px;
cursor: pointer;
letter-spacing: 0.8px;
color: black;
margin-top: 15px;
margin-bottom: 5px;
font-family: "Inter";
font-weight: 900;
`;
// INPUT
export const Input = styled.input`
font-size: 16px;
font-weight: normal;
font-family: "Inter";
letter-spacing: 0.3px;
width: 100%;
height: 40px;
line-height: 40px;
padding: 0px 5px 0px 10px;
border: 2px solid #ccc;
border-radius: 0px;
transition: 0.3s ease all;
border-radius: 3px;
color: black;
&:focus {
outline: none;
border: 2px solid #00aea9;
}
&.Activated {
border: 2px solid #00aea9;
}
`;
// Input Group
export const InputGroupContainer = styled.div`
margin: 15px 0px;
position: relative;
width: 100%;
`;
I can give you a general solution. If you get it, you can adapt it to your specific code.
I use a state called inputState to denote if the input is empty/filled/filled&pressed.
Then your component just composes style based on that state whenever rendering.
function ComponentABC() {
const [inputState, setInputState] = useState(1);
let inputStyle = {borderCorlor: 'grey'};
if(inputState == 1) { /* empty */
inputStyle = {borderCorlor: 'grey'};
}
else if(inputState == 2) { /* filled */
inputStyle = {borderCorlor: 'blue'};
}
else if(inputState == 3) { /* filled & pressed */
inputStyle = {borderCorlor: 'red'};
}
// when pressed button, change color
function handleRegister(e) {
if(inputValue) {
setInputState(3);
}
}
function handleInputChange(e) {
// when input is filled, change color
if(inputValue) {
setInputState(2);
}
// when input is empty, change color
else if(!inputValue) {
setInputState(1);
}
}
return (
<div>
<input style={inputStyle} onChange={handleInputChange}/>
<button onClick={handleRegister}>register</button>
</div>
);
}

How do I change the background color of a toggle button when the state changes to true?

I'm still learning the fundamentals of web development and I am stuck. I've created a toggle button in react.js but I can't seem to change the background color when the state changes to true.
Can anyone help point me in the right direction?
.ToggleSwitch {
background: #e3e8eb;
border: 3px solid #e3e8eb;
height: 2em;
width: 4em;
border-radius: 2em;
cursor: pointer;
margin-bottom: 5px;
}
.ToggleSwitch .knob {
position: relative;
width: 1.9em;
height: 1.9em;
border-radius: 50%;
left: 0em;
transition: left 0.3s ease-out;
border: 1px solid #fff;
background-color: #fff;
}
.ToggleSwitch .knob.active {
left: 2em;
background-color: #f37c22;
border: 1px solid #f37c22;
}
import React from "react";
import "./toggleButton.css";
class ToggleButton extends React.Component {
constructor(props) {
super(props);
this.state = { isToggleOn: false };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState((prevState) => ({
isToggleOn: !prevState.isToggleOn,
}));
}
render() {
return (
<div onClick={this.handleClick} className="ToggleSwitch active" >
<div className={this.state.isToggleOn ? "knob active" : "knob"} />
</div>
);
}
doSearch(props) {
const [searchInput, setSearchInput] = "";
const submitForm = (event) => {
event.preventDefault();
props.getRecipes(searchInput);
};
}
}

ReactDOM.createPortal modal is mounted on DOM but nothing is displayed on the screen

this is a typescript-next.js project. I have this Modal component:
interface ModalProps {
onCancelModal: () => void;
onAcceptModal: () => void;
acceptEnabled: boolean;
isLoading?: boolean;
title: string;
}
const Modal: React.FC<ModalProps> = (props) => {
let containerRef = useRef<HTMLDivElement | null>(null);
console.log("container", containerRef);
useEffect(() => {
const rootContainer = document.createElement("div");
const parentElem = document.querySelector("#__next");
parentElem?.insertAdjacentElement("afterend", rootContainer);
if (!containerRef.current) {
containerRef.current = rootContainer;
}
return () => rootContainer.remove();
}, []);
return containerRef.current
? ReactDOM.createPortal(
<div className="modal">
<header className="modal__header">
<h1>{props.title}</h1>
</header>
<div className="modal__content">{props.children}</div>
<div className="modal__actions">
<Button design="danger" mode="flat" onClick={props.onCancelModal}>
Cancel
</Button>
<Button
mode="raised"
onClick={props.onAcceptModal}
disabled={!props.acceptEnabled}
loading={props.isLoading}
>
Accept
</Button>
</div>
</div>,
containerRef.current
)
: null;
};
export default Modal;
I pass a custom error to ErrorHandler component:
const ErrorHandler: React.FC<ErrorHandlerProps> = (props) => (
<Fragment>
{props.error && <Backdrop onClick={props.onHandle} />}
{props.error && (
<Modal
title="An Error Occurred"
onCancelModal={props.onHandle}
onAcceptModal={props.onHandle}
acceptEnabled
>
<p>{props.error}</p>
</Modal>
)}
</Fragment>
);
However, Modal component is successfully mounted on the DOM but nothing displays on the screen.
EDIT
I have backdrop and modal components.
// css for backdrop
.backdrop {
width: 100%;
height: 100vh;
background: rgba(0, 0, 0, 0.75);
z-index: 100;
position: fixed;
left: 0;
top: 0;
transition: opacity 0.3s ease-out;
opacity: 1;
}
// css for Modal
.modal {
position: fixed;
width: 90%;
left: 5%;
top: 20vh;
background: white;
border-radius: 5px;
z-index: 200;// I changed this to 999999 but didnot solve the issue
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
}
.modal__header {
border-bottom: 2px solid #3b0062;
}
.modal__header h1 {
font-size: 1.5rem;
color: #3b0062;
margin: 1rem;
}
.modal__content {
padding: 1rem;
}
.modal__actions {
padding: 1rem;
text-align: right;
}
.modal__actions button {
margin: 0 0.5rem;
}
#media (min-width: 768px) {
.modal {
width: 40rem;
left: calc((100% - 40rem) / 2);
}
}
I found the answer after i refresh my memory. I realized that there is another .modal className on elements-styles tab. It points me to the /node_modules/bootstrap/scss/_modal.scss file which also has modal className and it was overriding my custom className.
.modal {
position: fixed;
top: 0;
left: 0;
z-index: $zindex-modal;
display: none;
width: 100%;
height: 100%;
overflow: hidden;
// Prevent Chrome on Windows from adding a focus outline. For details, see
// https://github.com/twbs/bootstrap/pull/10951.
outline: 0;
// We deliberately don't use `-webkit-overflow-scrolling: touch;` due to a
// gnarly iOS Safari bug: https://bugs.webkit.org/show_bug.cgi?id=158342
// See also https://github.com/twbs/bootstrap/issues/17695
}

How to open a multiple message box with ReactJS

I'm building a multiple chatbox messaging like Facebook's popup messenger windows. At the moment, the user can call up a chatbox, close it, minimize it, etc.
When I click on multiple users, say 3, I'm supposed to have three chatboxes pop up corresponding to those three different users. Currently, only one chatbox appears.
This screenshot illustrates what I want to achieve. On each user's button click, it's own chatbox will popup.
Here is the demo and download link for a jQuery equivalent: link.
This is the full React code which shows just one chat box:
import React, { Component, Fragment } from "react";
import { render } from "react-dom";
import axios from 'axios';
import './style.css';
class MessageBox extends Component {
constructor() {
super();
this.state = {
showBox: false,
shownToggle: true,
data: [
{ id: "1", name: "Tony" },
{ id: "2", name: "Mark" },
{ id: "3", name: "Joy" }
],
currentRec: undefined,
};
this.showBox = this.showBox.bind(this);
this.closeBox = this.closeBox.bind(this);
this.toggle = this.toggle.bind(this);
}
showBox = (i, pid, name) => {
this.setState({ currentRec: i });
console.log(`Selected record index: ${i}`);
alert(pid);
alert(name);
this.setState({ showBox: true }, () => {
document.addEventListener('click', this.closeBox);
});
}
closeBox(event) {
if (this.dropdownBox.contains(event.target)) {
this.setState({ showBox: false }, () => {
document.removeEventListener('click', this.closeBox);
});
}
}
toggle() {
this.setState({
shownToggle: !this.state.shownToggle
});
}
render() {
var hidden = {
display: this.state.shownToggle ? "block" : "none"
}
return (
<div>
<ul style={{ float: "right" }}>
{this.state.data.map((person, i) => (
<div className="chat-sidebar" key={i}>
<button onClick={() => this.showBox(i, person.id, person.name)}>Chat with {person.name}</button>
{this.state.showBox ? (
<div className="msg_box" style={{ right: '270px' }}>
<div onClick={this.toggle.bind(this)} class="msg_head">
(<b style={{ color: 'orange' }}>
{this.state.currentRec !== undefined &&
<div className="modal-body">
{this.state.data[this.state.currentRec].name}
({this.state.data[this.state.currentRec].id})
</div>
}
</b>)
Minimize
<div className="close" ref={(element) => { this.dropdownBox = element; }} style={{ color: 'white' }}>Close</div>
</div>
<div style={hidden} className="msg_wrap"><div className="msg_body">Message will appear here</div></div>
</div>) : (null)}
</div>
))}
</ul>
</div>
);
}
}
/****** Chat Popup Layout ******/
body{
background: #e5e5e5;
font-family: sans-serif;
}
.msg_box{
position: fixed;
bottom: -5px;
width: 250px;
background: white;
border-radius: 5px 5px 0px 0px;
}
.msg_head{
background: black;
color: white;
padding: 8px;
font-weight: bold;
cursor: pointer;
border-radius: 5px 5px 0px 0px;
}
.msg_body{
background: white;
height: 200px;
font-size: 12px;
padding: 15px;
overflow: auto;
overflow-x: hidden;
}
.close{
float: right;
cursor: pointer;
}
.minimize{
float: right;
cursor: pointer;
padding-right: 5px;
}
/****** Slider Layout Popup ******/
.chat-sidebar {
width: 250px;
height: 100%;
right: 0px;
top: 0px;
padding-top: 10px;
padding-bottom: 10px;
border: 1px solid #b2b2b2;
}
After series of efforts, the use of jquery and Reactjs is what solved my issue as per code below
const dataSet = this.state.data;
alert(dataSet);
if ($.inArray(pid, dataSet) != -1)
{
dataSet.splice($.inArray(pid, this.state.data), 1);
}
dataSet.unshift(pid);
var s = 270 ; // start position
var j = 260; //next position
$.each(dataSet, function( index, value ) {
if(index < 4){
$('[rel="'+value+'"]').css("right",s);
$('[rel="'+value+'"]').show();
s = s+j;
}
else{
$('[rel="'+value+'"]').hide();
}

Resources