This is a question on how to deal with pasted image using draftjs in the case of Japanese input mode.
Recently I am using Draft.js as text editor. Sometimes we need to insert image into the editor like copy image and paste the image into the editor area.
Everything goes well but the strange thing happens when I try to input Japanese words together with pasted image.
As the image above, After inputing Japanese words, I pressed "Enter" key to confirm the Japanese words.
But after I pressed "Enter" Key, the following Errors happens.
I think the problem is cased by the Japanese Input dialog destroyed the entity key of Draft.js
Could someone give me some hint on how to solve the problem?
My code is the following.
import React, {useState} from 'react';
import './App.css';
import {Editor, EditorState, convertFromRaw, ContentBlock} from 'draft-js';
function App() {
const initData = convertFromRaw({
blocks: [
{
key: "98pea",
text: "https://dummyimage.com/100x100/000/fff",
type: "atomic",
depth: 0,
inlineStyleRanges: [],
entityRanges: [
{
offset: 0,
length: 1,
key: 0,
},
],
data: {},
},
{
key: "16d04",
text: "testtest。",
type: "unstyled",
depth: 0,
inlineStyleRanges: [],
entityRanges: [],
data: {},
},
],
entityMap: {
0: {
type: "image",
mutability: "IMMUTABLE",
data: { src: "https://dummyimage.com/100x100/000/fff" },
},
},
});
const initState = EditorState.createWithContent(initData)
const [editorState,setEditorState] = useState(initState)
const Image = (props:any) => {
return <img src={props.src} alt="" />;
};
const Media = (props:any) => {
const entity = props.contentState.getEntity(props.block.getEntityAt(0));
const { src } = entity.getData();
const type = entity.getType();
let media;
if (type === "image") {
media = <Image src={src} />;
}
return media;
};
const myBlockRenderer = (block:ContentBlock) => {
if (block.getType() === "atomic") {
return {
component: Media,
editable: false,
};
}
return null
}
return (
<div className="App">
<div>
<Editor editorState={editorState} onChange={setEditorState} blockRendererFn={myBlockRenderer} />
</div>
</div>
);
}
export default App;
Related
The following Draftjs code is in class component. The plugins like CreateImage, Focus Plugin, and BlockDndPlugin are being imported from the DraftJS. I would be grateful if somebody can convert the class-based react components into Functional based react components...............................................................................................................................................................................................................................................................................................
import React, { Component } from 'react';
import { convertFromRaw, EditorState } from 'draft-js';
import Editor, { composeDecorators } from '#draft-js-plugins/editor';
import createImagePlugin from '#draft-js-plugins/image';
import createFocusPlugin from '#draft-js-plugins/focus';
import createBlockDndPlugin from '#draft-js-plugins/drag-n-drop';
import editorStyles from './editorStyles.module.css';
const focusPlugin = createFocusPlugin();
const blockDndPlugin = createBlockDndPlugin();
const decorator = composeDecorators(
focusPlugin.decorator,
blockDndPlugin.decorator
);
const imagePlugin = createImagePlugin({ decorator });
const plugins = [blockDndPlugin, focusPlugin, imagePlugin];
/* eslint-disable */
const initialState = {
entityMap: {
0: {
type: 'IMAGE',
mutability: 'IMMUTABLE',
data: {
src: '/images/canada-landscape-small.jpg',
},
},
},
blocks: [
{
key: '9gm3s',
text:
'You can have images in your text field which are draggable. Hover over the image press down your mouse button and drag it to another position inside the editor.',
type: 'unstyled',
depth: 0,
inlineStyleRanges: [],
entityRanges: [],
data: {},
},
{
key: 'ov7r',
text: ' ',
type: 'atomic',
depth: 0,
inlineStyleRanges: [],
entityRanges: [
{
offset: 0,
length: 1,
key: 0,
},
],
data: {},
},
{
key: 'e23a8',
text:
'You can checkout the alignment tool plugin documentation to see how to build a compatible block plugin …',
type: 'unstyled',
depth: 0,
inlineStyleRanges: [],
entityRanges: [],
data: {},
},
],
};
/* eslint-enable */
export default class CustomImageEditor extends Component {
state = {
editorState: EditorState.createWithContent(convertFromRaw(initialState)),
};
onChange = (editorState) => {
this.setState({
editorState,
});
};
focus = () => {
this.editor.focus();
};
render() {
return (
<div>
<div className={editorStyles.editor} onClick={this.focus}>
<Editor
editorState={this.state.editorState}
onChange={this.onChange}
plugins={plugins}
ref={(element) => {
this.editor = element;
}}
/>
</div>
</div>
);
}
}
You can use useState and useRef hooks in FC & Modify your component accordingly..
const CustomImageEditor = () => {
const [editorState, setEditorState] = useState(
EditorState.createWithContent(convertFromRaw(initialState))
);
const editor = useRef();
const onChange = (editorStates) => {
setEditorState(editorStates);
};
const focus = () => {
editor.current.focus();
};
return (
<div>
<div className={editorStyles.editor} onClick={focus}>
<Editor
editorState={editorState}
onChange={onChange}
plugins={plugins}
ref={editor}
/>
</div>
</div>
);
};
I don't know whether the question has been previously asked or not. I was playing around with states in react. My aim was to get a specific property of a data and based on that property I need to make changes on the appearance of a square on the screen, which I am getting upon and event call my desired task is being flawlessly being achieved but, when I am console logging the the state variable before and after the event the values are the same? I cannot see any changes in them. I have added screenshot, code and data format. I can't figure the reason.
Note: The event is being handled without any flaw the screen is changing as per my requirement
Parent Component:
import React from "react";
import boxes from "../data/boxes";
import Box from "./Box";
export default function Boxes() {
const [square, setSquare] = React.useState(boxes);
const squareBoxes = square.map((box) => {
return <Box key={box.id} on={box.on} toggle={toggle} id={box.id} />;
});
function toggle(id) {
console.log("Before Clicking:", square[id].on);
setSquare((prevSquare) => {
const newSquare = [];
for (let i = 0; i < prevSquare.length; i++) {
if (prevSquare[i].id === id) {
newSquare.push({
...prevSquare[i],
on: !prevSquare[i].on,
});
} else {
newSquare.push(prevSquare[i]);
}
}
return newSquare;
});
console.log("After Clicking:", square[id].on);
}
return (
<div className="box__main--container">
<div className="box__inner-container">
{squareBoxes}
</div>
</div>
);
}
Child Component:
import React from "react";
export default function Box(props) {
const styles = {
backgroundColor: props.on ? "#222222" : "transparent",
};
return (
<div
className="box"
style={styles}
onClick={() => props.toggle(props.id)}
></div>
);
}
Data Format:
export default [
{
id: 1,
on: false,
},
{
id: 2,
on: true,
},
{
id: 3,
on: false,
},
{
id: 4,
on: true,
},
{
id: 5,
on: false,
},
{
id: 6,
on: true,
},
];
Console Screenshot:
Image from the console
I am trying to get the chart data from graphql and render it to the DOM.
All is fine as the data is showing, but is not synchronized with it's real values.
As I had to extract the data into a unique set of arrays, the data is not synchronized with it's underlying values.
How can I improve my component, so the data is in sync with it's labels?
import React, { useEffect, useState, useMemo } from "react";
import { useQuery, gql } from "#apollo/client";
import { Line, Pie, Radar, Bar } from "react-chartjs-2";
import { Chart, registerables } from "chart.js";
Chart.register(...registerables);
const total = gql`
query GetIntell($myLimit: Int!) {
webs(
pagination: { start: 0, limit: $myLimit }
filters: { site: { eq: "nist" } }
) {
data {
id
attributes {
dateAdded
severity
}
}
}
}
`;
export default function Graph({limit}) {
const { loading, error, data } = useQuery(total, {
variables: { myLimit: limit },
});
const [chartData, setChartData] = useState({});
const myLabels = useMemo(
() => (data ? data.webs.data.map((t) => t.attributes.dateAdded) : null),
[data]
);
const myData = useMemo(
() => (data ? data.webs.data.map((t) => t.attributes.severity) : null),
[data]
);
useMemo(() => {
setChartData({
labels: [...new Set(myLabels)], <---I am altering the data with this set. How can I recover the data with it's correct values and still get the unique array of values?
datasets: [
{
label: "Nist",
data: myData,
fill: true,
},
],
});
}, [data]);
let delayed;
if (loading) return <div className="loader center"></div>;
if (error) return <p>`Error! ${error}`</p>;
return (
<div className="container">
<div className="chart">
<Bar
data={chartData}
options={{
responsive: true,
plugins: {
title: {
display: true,
text: "Severity Accumulation",
},
legend: {
display: true,
position: "top",
},
},
}}
/>
</div>
</div>
);
}
Please see my pic bellow. The chart is fine, but still incorrect as it displayes values maybe for other days.
Any feedback is well received! Also if you can see an easier way of improving my code, I would owe you a lot!
I am passing through props to my Buttons.jsx file. Each button would drop down a video. My problem is that when I click one button, all of the videos appear. I want it so that only that certain video that was clicked would show.
here is the link to repo
https://noahfarinas.github.io/stretching/
Thanks in advance!
Buttons.jsx
import { useState } from "react";
import ReactPlayer from "react-player";
export const Buttons = (props) => {
const { content } = props;
const [showVideo,setShowVideo] = useState(false);
const handleOnClick = () => setShowVideo(true);
return (
<div className="buttonContainer">
{content.map((item) => (
<div className="buttonSpace">
<button id="btn" onClick={handleOnClick}>{item.title}</button>
{showVideo ? <ReactPlayer url={item.embed} /> : null}
</div>
))}
</div>
);
};
export default Buttons;
**App.js**
import "./App.css";
import Search from "./Search";
import TitleView from "./TitleView";
import Buttons from "./Buttons";
function App() {
const TITLE = "Stretches";
const DATA = [
{
area: "upper-back",
video: "https://www.youtube.com/watch?v=bTn89EBKJdM",
},
{
area: "mid-back",
video: "https://www.youtube.com/watch?v=VnDuWC40egg",
},
{
area: "lower-back",
video: "https://www.youtube.com/watch?v=N-xqKx8oshs",
},
{
area: "hips",
video: "https://www.youtube.com/watch?v=nLuvQCTPrcY",
},
{
area: "calves",
video: "https://www.youtube.com/watch?v=37GHTaoknfw",
},
{
area: "chest",
video: "https://www.youtube.com/watch?v=NePr1XKRTLU",
},
{
area: "glute",
video: "https://www.youtube.com/watch?v=eRCpceBhcm0",
},
{
area: "foot",
video: "https://www.youtube.com/watch?v=AXSj_5pBAKw",
},
{
area: "forearm",
video: "https://www.youtube.com/watch?v=Ayhu7TzNGSQ",
},
{
area: "it band",
video: "https://www.youtube.com/watch?v=i6Psvd81Hyc",
},
{
area: "hamstring",
video: "https://www.youtube.com/watch?v=pJUwEBgxWoE",
},
{
area: "tricep",
video: "https://www.youtube.com/watch?v=SaZK9vlSmHI",
},
{
area: "lat",
video: "https://www.youtube.com/watch?v=6V5tSn9oEJg",
},
];
const BUTTONDATA = [
{
title: "back",
embed: "https://www.youtube.com/watch?v=buF1v8aiTvM",
},
{
title: "legs",
embed: "https://www.youtube.com/watch?v=UIRTPXj1Q1U",
},
{
title: "upper-body",
embed: "https://www.youtube.com/watch?v=Kpd9ik93Sxk",
},
{
title: "hips",
embed: "https://www.youtube.com/watch?v=j42sLnoMkrA",
},
];
return (
<div className="App">
<TitleView headline={TITLE} />
<Search placeholder="What hurts..." data={DATA} />
<Buttons content={BUTTONDATA} />
</div>
);
}
export default App;
The problem can be solved in two ways. One way is to have a map in app.js on the BUTTONDATA and render separate component for each item of the array. In this way each Button Component will have its own state and will show only its own contents upon button click.
The other way is to have a boolean property for each of array item in the BUTTONDATA. and then you can modify your component in the following way:
Button.js
import { useEffect, useState } from "react";
import ReactPlayer from "react-player";
const Buttons = (props) => {
const { content } = props;
const [visibilityChanged, setVisibiltyChanged] = useState(false);
useEffect(() => {}, [visibilityChanged]);
const handleOnClick = (index) => () => {
content[index].isVisible = !content[index].isVisible;
setVisibiltyChanged(!visibilityChanged);
};
return (
<div className="buttonContainer">
{content.map((item, index) => (
<div className="buttonSpace" key={index}>
<button id="btn" onClick={handleOnClick(index)}>
{item.title}
</button>
{item.isVisible ? <ReactPlayer url={item.embed} /> : null}
</div>
))}
</div>
);
};
export default Buttons;
and in app.js will have the following changes:
App.js
const BUTTONDATA = [
{
title: "back",
embed: "https://www.youtube.com/watch?v=buF1v8aiTvM",
isVisible: false
},
{
title: "legs",
embed: "https://www.youtube.com/watch?v=UIRTPXj1Q1U",
isVisible: false
},
{
title: "upper-body",
embed: "https://www.youtube.com/watch?v=Kpd9ik93Sxk",
isVisible: false
},
{
title: "hips",
embed: "https://www.youtube.com/watch?v=j42sLnoMkrA",
isVisible: false
}
];
Here is the code sandbox I created to toggle videos on button click.
I have a FlexLayout (from flexlayout-react) which contains an AceEditor (from react-ace). For testing I added a Test component as well.
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import FlexLayout from 'flexlayout-react';
import AceEditor from 'react-ace';
// display an Ace editor (here with fixed size)
const Editor = () => {
return (
<AceEditor
width="200px"
height="200px"
value="foo"
/>
);
}
// an increment button, just something simple stateful
const Test = () => {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>
{count}
</button>
</div>
);
};
// two columns for editor and test component
const model = FlexLayout.Model.fromJson({
global: {},
borders: [],
layout: {
type: 'row',
weight: 50,
children: [
{
type: 'tabset',
weight: 50,
selected: 0,
children: [
{
type: 'tab',
name: 'A',
component: 'editor',
},
],
},
{
type: 'tabset',
weight: 50,
selected: 0,
children: [
{
type: 'tab',
name: 'B',
component: 'test',
},
],
},
],
},
});
const factory = node => {
switch (node.getComponent()) {
case 'editor': {
return <Editor />;
}
case 'test': {
return <Test />;
}
default:
return null;
}
}
// display the flex layout
const Ide = () => {
return (
<FlexLayout.Layout
model={model}
factory={factory}
/>
);
};
// render everything
ReactDOM.render(
<Ide />,
document.getElementById('react-container')
);
So what's going on?
Whenever the FlexLayout state changes (focus changed, dragging the divider, changing width), the text of the Ace editor is reset to foo. In contrast, the value of Test is preserved. Without the FlexLayout, the problem goes away.
So there seems to be a curious interaction between the two components, but I'm too inexperienced with React to figure it out. How would I go about debugging this? What are common avenues of approach with such an issue? Or any concrete ideas where the error is in this specific situation?