How to open a multiple message box with ReactJS - 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();
}

Related

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);
};
}
}

Input checkbox didn't work what I expected

I tried to make an input checkbox when I click the input checkbox, it should be displayed a check image like this.
However, it didn't show the checkbox and I am not sure how to check that the input box was checked or not. Could you help me with what part do I missed and where is something wrong?
I really appreciate your help!
This is CSS inputl and label part
.colors {
display: flex;
flex-direction: column;
span {
margin-bottom: 20px;
}
.colorLists {
margin-left: 10px;
display: flex;
flex-wrap: wrap;
.colorLayout {
display: flex;
flex-direction: column;
position: relative;
width: 33%;
height: 80px;
flex-shrink: 0;
align-items: center;
justify-content: center;
.checkboxLabel {
background-color: beige;
border: 1px solid #ccc;
border-radius: 50%;
cursor: pointer;
height: 28px;
left: 0;
position: absolute;
top: 40;
width: 28px;
&:after {
border: 2px solid #fff;
border-top: none;
border-right: none;
content: '';
height: 6px;
left: 7px;
opacity: 0;
position: absolute;
top: 8px;
transform: rotate(-45deg);
width: 12px;
// opacity: 0.2;
}
}
input[type='checkbox'] {
visibility: hidden;
}
input[type='checkbox']:checked {
& + label {
background-color: beige;
border-color: beige;
&:after {
opacity: 1;
}
}
}
.productColor {
margin-top: 70px;
font-size: 13px;
margin-right: 21px;
}
}
}
}
.sizes {
.HorizontalLine {
margin-top: 25px;
}
.span {
}
.sizeLists {
margin-top: 20px;
margin-bottom: 20px;
button {
margin: 5px;
width: 44px;
height: 32px;
background-color: white;
border: 1px solid silver;
border-radius: 15%;
}
}
}
This is js part
<div className="colors">
<span>색상</span>
<ul className="colorLists">
{COLOR_LISTS.map((color, idx) => {
return (
<li className="colorLayout" key={idx}>
<input type="checkbox" />
<label
className="checkboxLabel"
for="checkbox"
style={{ backgroundColor: color.colorProps }}
/>
<span className="productColor">{color.color_name}</span>
</li>
);
})}
</ul>
</div>
In react you have to set the htmlFor property for the label instead of for.
The value should be the same as the id from the input.
Then you can add a value property for the input which is used for adding/removing the item in the list of selected items.
For this purpose a handleChange function can be defined.
const [selectedItems, setSelectedItems] = useState([]);
function handleChange(e) {
let newSelected = [];
if (selectedItems.includes(e.target.value)) {
newSelected = selectedItems.filter((item) => item !== e.target.value);
} else {
newSelected = [...selectedItems, e.target.value];
}
setSelectedItems(newSelected);
}
return (
<div className="colors">
<span>색상</span>
<ul className="colorLists">
{COLOR_LISTS.map((color, idx) => {
return (
<li className="colorLayout" key={idx}>
<input
onChange={handleChange}
type="checkbox"
id={idx}
value={color.color_name}
checked={selectedItems.includes(color.color_name)}
/>
<label
className="checkboxLabel"
htmlFor={idx}
style={{ backgroundColor: color.colorProps }}
/>
<span className="productColor">{color.color_name}</span>
</li>
);
})}
</ul>
</div>
);
EDIT: Since you are using a class component it can be rewrittenlike this:
export default class CheckboxListComponent extends Component {
constructor(props) {
super(props);
this.state = { selectedItems: [] };
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
let newSelected = [];
if (this.state.selectedItems.includes(e.target.value)) {
newSelected = this.state.selectedItems.filter(
(item) => item !== e.target.value
);
} else {
newSelected = [...this.state.selectedItems, e.target.value];
}
this.setState({ selectedItems: newSelected });
}
render() {
return (
<div className="colors">
<span>색상</span>
<ul className="colorLists">
{COLOR_LISTS.map((color, idx) => {
return (
<li className="colorLayout" key={idx}>
<input
onChange={this.handleChange}
type="checkbox"
id={idx}
value={color.color_name}
checked={this.state.selectedItems.includes(color.color_name)}
/>
<label
className="checkboxLabel"
htmlFor={idx}
style={{ backgroundColor: color.colorProps }}
/>
<span className="productColor">{color.color_name}</span>
</li>
);
})}
</ul>
</div>
);
}
}
You must tell react that your input is checked so that your CSS will apply it. selected ids must be kept in a place for future existence check. in the following code, I named this array selecetedIdx.
You also need to add idx on selection(via onChange event handler) or wrap them all in form and add them via extra dom attribute.
class Main extends Component {
// initialize selectedIdx with [] in your state (esp constructor)
// e.g. this.state = {/* rest of state, */ selectedIdx: []}
render() {
return (
{COLOR_LISTS.map((color, idx) => {
return (
// ...
<input
type="checkbox"
checked={selectedIdx.includes(idx)}
onChange={() => this.setState(state => ({
selectedIdx: [...state.selectedIdx, idx]
}))}
/>
// ...
)}
)
}
Your checkbox element needs name and value properties and would normally be a child of the <form> element.

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 can I change the styles of the react-toastify popup message?

I want to add my own custom style to the react-toastify message popup, depending on whether its success or error. So far I tried the following approach:
toastify.js
import { toast, Slide } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { css } from "glamor";
toast.configure({
position: toast.POSITION.BOTTOM_RIGHT,
autoClose: 3000,
transition: Slide,
pauseOnFocusLoss: false,
className: css({
backgroundColor: 'red',
}),
bodyClassName: css({
backgroundColor: 'blue',
height: '100%',
width: '100%',
})
});
export default function (message, type, styles = {}) {
switch (type) {
case type === 'success':
toast.success(message);
break;
case type === 'error':
toast.error(message);
break;
case type === 'info':
toast.info(message);
break;
case type === 'warn':
toast.warn(message);
break;
default:
toast(message);
break;
}
}
This is a function in which I define what type of message style toastify shows based on the type param. Then I call this function like this:
export default function ({params}) {
...
async function deleteTodo (id) {
try {
const res = await axios.delete(http://localhost:8000/api/delete-task/${id});
toastifyMessage(res.data, 'success');
} catch (error) {
errorInfo(toastifyMessage(error, 'error'));
}
}
return (
<li className="menu-item">
<i
className="fas fa-trash"
onClick={() => deleteTask(task._id)}
></i>
</li>
);
}
And this is what I get:
I still get that white background. All I want is to remove the default styles and add my own for success and error.
for custom style react-toastify
css
.Toastify__toast--error {
border: 1px solid #EB5757;
border-radius: 50px !important;
background: #FAE1E2 !important;
}
.Toastify__toast--error::after {
content : url('../assets/images/svg/closeToast.svg'); // Your svg Path
position: absolute;
color: #333333;
font-size: 15px;
font-weight: 700;
left:265px;
padding-top: 14px !important;
}
.Toastify__toast--error::before {
content: url("../assets/images/svg/errorToast.svg");// Your svg Path
position:relative;
z-index:100000;
left:12px;
top:6px;
}
.Toastify__toast--success {
border: 1px solid #3A9EA5 !important;
border-radius: 50px !important;
background: #F0F9FA !important;
}
.Toastify__toast--success::before {
content: url("../assets/images/svg/successToast.svg");// Your svg Path
position:relative;
z-index:100000;
left:12px;
top:6px;
}
.Toastify__toast--success::after {
content : url('../assets/images/svg/closeToast.svg');// Your svg Path
position: absolute;
color: #333333;
font-size: 15px;
font-weight: 700;
left:265px;
padding-top: 14px !important;
}
.Toastify__toast--warning {
border: 1px solid #E78326 !important;
border-radius: 50px !important;
background: #FADFC5 !important;
}
.Toastify__toast--warning::before {
content: url("../assets/images/svg/warnToast.svg");// Your svg Path
position:relative;
z-index:100000;
left:12px;
top:6px;
}
.Toastify__toast--warning::after {
content : url('../assets/images/svg/closeToast.svg'); // Your svg Path
position: absolute;
color: #E78326;
font-size: 15px;
font-weight: 700;
left:265px;
padding-top: 14px !important;
}
.Toastify__toast-body {
color: #444C63 ;
font-size: 16px;
padding-left: 20px;
line-height: 20px;
padding: 0px;
padding-top: 25px;
width: 100%;
font-weight: 400;
margin-left: 25px !important;
}
.Toastify__toast > button> svg {
display: none;
}
In my case (also a React app) I only needed to change:
background color of warning and error
progress bar color
font
I found this to be the easiest & quickest solution. In my app's CSS file I overwrite the background-property in the default classes. I also define my own classes for toast body and the progress bar:
/* https://fkhadra.github.io/react-toastify/how-to-style/ */
.Toastify__toast--warning {
background: #FFE8BC !important;
}
.Toastify__toast--error {
background: #FCA7A9 !important;
}
.toastBody {
font-family: "Atlas Grotesk Web", Arial, Helvetica, sans-serif;
color: #10171D; /* #10171D */
font-size: 0.875rem !important;
}
.toastProgress {
background: #333F48 !important;
}
To use my classes:
<ToastContainer
progressClassName="toastProgress"
bodyClassName="toastBody"
/>
Easest way to define a custom method, which can take the type of notification, content, and toast options. With the type of notification, you can pass different classNames to your custom content and override root toast component styles. Typescript example:
export enum NOTIFICATIONS_TYPES {
SUCCESS = 'success',
ERROR = 'error',
}
const NotificationStringContent: React.FunctionComponent<{
content: string;
}> = ({ content }) => (
<Typography component="p" variant="text-200">
{content}
</Typography>
);
export const showNotification = (
type: NOTIFICATIONS_TYPES,
content: string | React.ReactElement,
options: ToastOptions = {}
) => {
const toastContent = typeof content === 'string' ? (
<NotificationStringContent content={content} />
) : (
content
);
toast(toastContent, {
className: classnames(styles.toast, {
[styles.toastSuccess]: type === NOTIFICATIONS_TYPES.SUCCESS,
[styles.toastError]: type === NOTIFICATIONS_TYPES.ERROR,
}),
...options,
});
};
const NotificationContainer: React.FunctionComponent<{}> = () => (
<ToastContainer
position="bottom-left"
hideProgressBar
closeButton={<NotificationCloseButton />}
closeOnClick={false}
pauseOnHover
/>
);
export default NotificationContainer;
import { toast } from "react-toastify";
// promise is a function that returns a promise
export const withToast = (promise) => {
toast.promise(
promise,
{
pending: {
render() {
return (
<div className="p-6 py-2 bg-green-400">
<p className="mb-2">Your transaction is being processed.</p>
<p>Hang tight... Just few more moments.</p>
</div>
);
},
icon: false,
},
success: {
render({ data }) {
return (
<div>
<p className="font-bold">
Tx: {data.transactionHash.slice(0, 20)}...
</p>
<p>Has been succesfuly processed.</p>
</div>
);
},
// other options
icon: "🟢",
},
error: {
render({ data }) {
// When the promise reject, data will contains the error
return <div>{data.message ?? "Transaction has failed"}</div>;
},
},
},
// configuration
{
closeButton: true,
}
);
};
When you need to use it:
withToast(_purchase({ Id, value }));
Depending on _purchase, your app will show different messages with different styles

Why is git pages rendering my page different then in local?

I'm rather new to whole react thing, going through courses on FCC. My problem come from CSS/SCSS itself. I tried ordinary CSS and SCSS. My page is done in React. The page is also hosted on codepen. All three versions are the same, codepen, git and local and yet CSS is somewhat different between them. My previous projects who are also hosted on these platforms and in local have also the same CSS but they look identical. Difference between local and codepen's version isn't that drastic, but between local>codepen>github is more than noticeable as you descend. Do GitHub ignores/renders? or what, React differently? This question should also apply to codepen.
My codepen
My git page
My repoToProject
Image of localhost:
html from codepen:
<div id="root"></div>
scss from codepen:
$position: absolute;
$percentage: 100%;
$color: whitesmoke;
$car: auto;
#import url("//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-glyphicons.css");
.btn1 {
position: $position;
bottom: -30px;
left: 51.3%;
z-index: 999;
background-color: $color;
border: 1px solid gray;
color: aqua;
}
#root {
width: $percentage;
}
body,
html {
height: $percentage;
}
body {
background-color: $color;
background-image: url("../images/react1.png");
background-repeat: no-repeat;
background-size: $car;
background-position: center bottom;
overflow: hidden;
}
.grid-container {
display: grid;
grid-template-columns: 50% 50%;
background-color: $color;
padding: 10px;
grid-gap: 15px;
width: $percentage;
grid-template-rows: 30px 25px 98%;
}
.grid-item {
text-align: center;
}
#inner2 {
padding-left: 20px;
padding-right: 25px;
border-bottom: 15px solid #d6e9c6;
padding-bottom: 55px;
background-color: #fcf8e3;
width: $percentage;
margin-left: $car;
margin-right: $car;
position: $position;
top: 0px;
left: 0px;
height: $percentage;
min-height: 20%;
max-height: $percentage;
}
#editor {
width: $percentage;
background-color: white;
resize: none;
color: #495057;
border: 1px solid #ced4da;
border-radius: 0.25rem;
overflow-y: $car;
max-height: $percentage;
min-height: 40px;
margin-bottom: 40px;
}
#preview {
width: 98.9%;
background-color: white;
border-radius: 0.25rem;
border: 1px solid #ced4da;
overflow-y: $car;
max-height: $percentage;
min-height: 40px;
margin-bottom: 40px;
}
#item1,
#item2 {
font-family: "Russo One";
font-style: oblique;
font-weight: bold;
font-size: 2em;
margin-bottom: 10px;
padding-bottom: 0px;
width: $percentage;
background-color: #fcf8e3;
min-height: 50px;
border-bottom: none;
padding-top: 10px;
}
.insignia {
letter-spacing: 5px;
-webkit-transition: letter-spacing 1s;
transition: letter-spacing 1s;
}
.insignia:hover {
letter-spacing: 13px;
cursor: pointer;
}
.ui-resizable-s {
cursor: row-resize;
}
textarea:focus,
input:focus {
outline: none;
}
#arrow {
background-color: #dff0d8;
width: $percentage;
height: 15px;
position: $position;
bottom: -12px;
padding-left: 0px;
padding-right: 0px;
font-size: 1.5em;
border-bottom: 1px solid #d6e9c6;
text-align: center;
}
.glyphicon {
top: -4px;
left: 4px;
color: gray;
-ms-transform: scale(1, 0.6); /* IE 9 */
-webkit-transform: scale(1, 0.6); /* Safari */
transform: scale(1, 0.6);
}
#media screen and (max-height: 600px) {
#inner2 {
height: 90vh !important;
}
}
#eraser {
text-align: center;
grid-column: 1 / 3;
z-index: 2;
line-height: 10px;
margin-left: $car;
margin-right: $car;
}
/*Additional styling*/
td,
th {
border: 2px solid #224b4b;
padding-left: 5px;
padding-right: 5px;
}
.label {
position: $position;
top: -10px;
left: 0px;
min-width: $percentage;
z-index: 999;
}
.preview-editor {
position: fixed;
top: 55px;
left: 0px;
min-width: $percentage;
height: $percentage;
z-index: 999;
}
h1 {
border-bottom: 2px solid #224b4b;
}
h2 {
border-bottom: 1px solid #224b4b;
}
code {
background-color: #d6e9c6;
color: #e83e8c !important;
}
blockquote {
border-left: 2px solid black;
padding-left: 5px;
margin-left: 25px;
}
#media only screen and (max-width: 768px) {
img {
width: 100%;
}
#ggED {
text-align: left;
}
#ggPrev {
text-align: right;
}
.insignia,
.insignia:hover {
letter-spacing: 0px;
font-size: 1em;
}
}
js from codepen:
var renderer = new marked.Renderer();
renderer.link = function(href, title, text) {
return (
'<a target="_blank" href="' +
href +
'" title="' +
title +
'">' +
text +
"</a>"
);
};
marked.setOptions({
breaks: true,
renderer: renderer,
sanitize: true
});
class DisplayMessages extends React.Component {
constructor(props) {
super(props);
this.state = {
markdown: defaultMarkdown,
erase: false,
goFull: false,
headViewKlasa: "grid-item",
headEdKlasa: "grid-item",
editorKlasa: "",
previewKlasa: "",
stilPreview: {},
stilEditor: {},
attr: "Click on me for fullscreen",
inner2H: "",
h2Inner: false
};
this.handleChange = this.handleChange.bind(this);
this.eraseFields = this.eraseFields.bind(this);
this.inner2Height = this.inner2Height.bind(this);
}
eraseFields() {
this.setState({
erase: true
});
if (this.state.erase === false) {
this.setState({
markdown: ""
});
}
if (this.state.erase === true) {
this.setState({
markdown: defaultMarkdown,
erase: !this.state.erase
});
}
}
componentDidMount() {
this.node = ReactDOM.findDOMNode(this);
$(this.node).resizable({
handles: "s",
minHeight: 170
});
document
.querySelector(".ui-resizable-handle.ui-resizable-s")
.setAttribute(
"title",
"Double click on me or pull me down to full height"
);
}
inner2Height() {
if (this.state.h2Inner === false) {
this.setState({
inner2H: "100%",
h2Inner: true
});
}
if (this.state.h2Inner === true) {
this.setState({
inner2H: "",
h2Inner: false
});
}
}
fullScreen(clicked_id) {
if (clicked_id === "ggEd" && this.state.goFull === false) {
this.setState({
headEdKlasa: this.state.headEdKlasa + " label",
attr: "Click again to go back!",
editorKlasa: "preview-editor",
stilPreview: { display: "none" },
stilEditor: { paddingTop: "0px" },
goFull: true
});
}
if (clicked_id === "ggEd" && this.state.goFull === true) {
this.setState({
headEdKlasa: this.state.headEdKlasa.substr(0, 9),
attr: "Click on me for fullscreen",
editorKlasa: "",
stilPreview: { display: "block" },
stilEditor: { paddingTop: "10px" },
goFull: !this.state.goFull
});
}
if (clicked_id === "ggPrev" && this.state.goFull === false) {
this.setState({
headViewKlasa: this.state.headViewKlasa + " label",
attr: "Click again to go back!",
previewKlasa: "preview-editor",
stilEditor: { display: "none" },
stilPreview: { paddingTop: "0px" },
goFull: true
});
}
if (clicked_id === "ggPrev" && this.state.goFull === true) {
this.setState({
headViewKlasa: this.state.headViewKlasa.substr(0, 9),
attr: "Click on me for fullscreen",
previewKlasa: "",
stilEditor: { display: "block" },
stilPreview: { paddingTop: "10px" },
goFull: !this.state.goFull
});
}
}
handleChange(event) {
this.setState({
markdown: event.target.value
});
}
render() {
const btnText = this.state.erase ? "Populate" : "Erase";
const handleClick = e => this.fullScreen(e.target.id);
return (
<div
id="inner2"
className="grid-container animated zoomIn"
style={{ height: this.state.inner2H }}
onDoubleClick={this.inner2Height}
>
<EditorHead
id={"item1"}
style={this.state.stilEditor}
className={this.state.headEdKlasa}
onClick={handleClick}
title={this.state.attr}
/>
<PreviewHead
id={"item2"}
style={this.state.stilPreview}
className={this.state.headViewKlasa}
onClick={handleClick}
title={this.state.attr}
/>
<BtnEraser
id={"eraser"}
onClick={this.eraseFields}
type={"button"}
className={"btn btn-danger btn-lg"}
title={"Erase & populate both fields"}
value={btnText}
/>
<Editor
id={"editor"}
onChange={this.handleChange}
className={this.state.editorKlasa}
value={this.state.markdown}
placeholder={"Enter ... some kind a text!? ..."}
title={
"This is rather obvious isn't it? It's editor window Sherlock :D"
}
/>
<Preview
id={"preview"}
className={this.state.previewKlasa}
dangerouslySetInnerHTML={{
__html: marked(this.state.markdown, { renderer: renderer })
}}
title={"It's a preview window, Sherlock ;)"}
/>
<Arrow id={"arrow"} />
</div>
);
}
}
/*class Inner2 extends React.Component{
render(){
return (
<div id={this.props.id} className={this.props.className} style={this.props.style} onDoubleClick={this.props.onDoubleClick}>Editor:</div>
);
}
}*/
class EditorHead extends React.Component {
render() {
return (
<h1
id={this.props.id}
style={this.props.style}
className={this.props.className}
onClick={this.props.onClick}
>
<span className="insignia" title={this.props.title} id="ggEd">
Editor:
</span>
</h1>
);
}
}
class PreviewHead extends React.Component {
render() {
return (
<h1
id={this.props.id}
style={this.props.style}
className={this.props.className}
onClick={this.props.onClick}
>
<span className="insignia" title={this.props.title} id="ggPrev">
Previewer:
</span>
</h1>
);
}
}
class BtnEraser extends React.Component {
render() {
return (
<button
id={this.props.id}
onClick={this.props.onClick}
type={this.props.type}
className={this.props.className}
title={this.props.title}
>
{this.props.value}
</button>
);
}
}
class Editor extends React.Component {
render() {
return (
<textarea
id={this.props.id}
onChange={this.props.onChange}
className={this.props.className}
value={this.props.value}
placeholder={this.props.placeholder}
title={this.props.title}
/>
);
}
}
class Preview extends React.Component {
render() {
return (
<div
id={this.props.id}
className={this.props.className}
dangerouslySetInnerHTML={this.props.dangerouslySetInnerHTML}
title={this.props.title}
/>
);
}
}
class Arrow extends React.Component {
render() {
return (
<div id={this.props.id}>
<Glyph className={"glyphicon glyphicon-align-justify"} />
</div>
);
}
}
class Glyph extends React.Component {
render() {
return <span className={this.props.className} />;
}
}
ReactDOM.render(<DisplayMessages />, document.getElementById("root"));

Resources