React TinyMCE editor set bbcode content dynamically - reactjs

I am using this official component https://www.tiny.cloud/docs/integrations/react/
I want to use this method from documentation https://www.tiny.cloud/docs/api/tinymce/tinymce.editor/#setcontent in order to define bbcode as content for my editor.
But I get an error:
this.editor.setContent is not a function
Here is my code
import React, { PureComponent } from 'react';
import { Editor } from '#tinymce/tinymce-react';
/**
* Comment component.
*/
class Comment extends PureComponent {
componentDidMount() {
this.editor.setContent('[b]some[/b] html', { format: 'bbcode' });
}
render() {
return (<Editor
ref={(editor) => {
this.editor = editor;
}}
apiKey="***"
/>);
}
}
export default Comment;

I managed to make TinyMCE editor to work with bbcode.
Here is my code:
import React, { PureComponent } from 'react';
import { Editor } from '#tinymce/tinymce-react';
/**
* Comment component.
*/
class Comment extends PureComponent {
constructor(props) {
super(props);
this.state = { content: 'this is a [url=https://google.com]link[/url]' };
this.handleEditorChange = this.handleEditorChange.bind(this);
}
handleEditorChange(content) {
this.setState({ content });
}
render() {
return (<Editor
value={this.state.content}
onEditorChange={this.handleEditorChange}
apiKey="***"
init={{
menubar: '',
plugins: 'bbcode link code',
toolbar: '',
}}
/>);
}
}
export default Comment;

in tinymce-react the right way to set initial content is:
<Editor
initialValue="<p>This is the initial content of the editor</p>"
/>
Ref: https://www.tiny.cloud/docs/integrations/react/#4replacetheappjsfile
As you can see from source the method you are seeking for is not exposed in Editor component.

Related

How to change specific token style in code mirror editor?

import React, { Component } from "react";
import { render } from "react-dom";
import CodeMirror from "react-codemirror";
import "./style.css";
import "codemirror/lib/codemirror.css";
class App extends Component {
constructor() {
super();
this.state = {
name: "CodeMirror",
code: "Hello world Code Mirror"
};
}
updateCode(newCode) {
this.setState({
code: newCode
});
}
render() {
let options = {
lineNumbers: true
};
return (
<div>
<p>Start editing to see some magic happen :)</p>
<CodeMirror
value={this.state.code}
onChange={this.updateCode.bind(this)}
options={options}
/>
</div>
);
}
}
render(<App />, document.getElementById("root"));
I'm working on a tokenizer and want to highlight a specific token from the code.
How to underline or bold a specific token, like world text in this case?
Or is there any other code editor library which can highlight any substring given start and end index?
You can achieve this by using CodeMirror.overlayMode. You need to define your own mode that will parse codemirror's content and set some class to your custom tokens.
Let's say that you define your mode in the customHighlightsMode.js file:
import CodeMirror from 'codemirror';
import 'codemirror/addon/mode/overlay';
CodeMirror.defineMode("customHighlights", function (config, parserConfig) {
var myOverlay = {
token: function (stream) {
if (stream.match(/(world)/)) {
return 'custom-keyword';
} else {
stream.next();
return null;
}
}
};
return CodeMirror.overlayMode(CodeMirror.getMode(config, parserConfig.backdrop), myOverlay);
});
Then you need to set a class with styles for your tokens:
.cm-custom-keyword {
font-weight: bold;
color: red;
}
And then you need to set your mode in the CodeMirror options:
import React, { Component } from "react";
import CodeMirror from "react-codemirror";
import "codemirror/lib/codemirror.css";
import "./style.css"; // add .cm-custom-keyword class here
import "./customHighlightsMode";
class App extends Component {
constructor() {
super();
this.state = {
name: "CodeMirror",
code: "Hello world Code Mirror"
};
}
updateCode(newCode) {
this.setState({
code: newCode
});
}
render() {
let options = {
lineNumbers: true,
mode: { name: "customHighlights" },
};
return (
<div>
<p>Start editing to see some magic happen :)</p>
<CodeMirror
value={this.state.code}
onChange={this.updateCode.bind(this)}
options={options}
/>
</div>
);
}
}
Unfortunately, the most documented way of doing this (overlayMode) does not work for CodeMirror 6.
The way to do this in CodeMirror 6 is to create a custom view plugin like this.
import { MatchDecorator, ViewPlugin, Decoration } from "#codemirror/view";
let worldDeco = Decoration.mark({ class: "world" }); // This adds a className to the text that matches the regex.
let decorator = new MatchDecorator({
regexp: /(world)/g,
decoration: (m) => worldDeco,
});
export const customPlugin = ViewPlugin.define(
(view) => ({
decorations: decorator.createDeco(view),
update(u) {
this.decorations = decorator.updateDeco(u, this.decorations);
},
}),
{
decorations: (v) => v.decorations,
}
);
And then you can go ahead and change styles like so
.world {
color: #e06c75;
font-weight: bold;
}
Last step is to hookup the plugin into your Editor view along with other extensions
let view = new EditorView({
extensions: [basicSetup, customPlugin, javascript()],
parent: document.body
})

Not sure if i'm using react context correcly

I've created a form in react and after some research i think that if you don't want to use an external library to manage the form, the context could be the best choice, expecially in my case where i've many nested component that compose it.
But, i'm not sure that putting a function inside my state is a good thing.
But let me give you some code:
configuration-context.js
import React from 'react'
export const ConfigurationContext = React.createContext();
ConfigurationPanel.jsx
import React, { Component } from 'react'
import { Header, Menu, Grid } from 'semantic-ui-react'
import ConfigurationSection from './ConfigurationSection.jsx'
import {ConfigurationContext} from './configuration-context.js'
class ConfigurationPanel extends Component {
constructor(props) {
super(props)
this.state = {
activeItem: '',
configuration: {
/* the configuration values */
banana: (data) => /* set the configuration values with the passed data */
}
}
}
handleItemClick = (e, { name }) => this.setState({ activeItem: name })
render() {
return (
<ConfigurationContext.Provider value={this.state.configuration}>
<Grid.Row centered style={{marginTop:'10vh'}}>
<Grid.Column width={15} >
<div className='configuration-panel'>
/* SOME BUGGED CODE */
<div className='configuration-section-group'>
{this.props.data.map((section, i) => <ConfigurationSection key={i} {...section} />)}
</div>
</div>
</Grid.Column>
</Grid.Row>
</ConfigurationContext.Provider>
)
}
}
ConfigurationItem.jsx
import React, { Component } from 'react'
import { Input, Dropdown, Radio } from 'semantic-ui-react'
import {ConfigurationContext} from './configuration-context.js'
class ConfigurationItem extends Component {
static contextType = ConfigurationContext
constructor(props) {
super(props)
}
handleChange = (e, data) => this.context.banana(data)
itemFromType = (item) =>{
switch (item.type) {
case "toggle":
return <div className='device-configuration-toggle-container'>
<label>{item.label}</label>
<Radio name={item.name} toggle className='device-configuration-toggle'onChange={this.handleChange} />
</div>
/* MORE BUGGED CODE BUT NOT INTERESTING*/
}
}
render() {
return this.itemFromType(this.props.item)
}
}
So, at the end i've a ConfigurationContext that is just a declaration, everything is inside the parent state.
The thing that i don't like is putting the banana function inside the state (it will have more logic that just logging it)
What do you think about it?
Any suggestion is appreciated.
Thanks
banana is just a regular function and you do not have to put it in the state, just do:
class ConfigurationPanel extends Component {
banana = data => console.log(data)
...
render() {
return (
<ConfigurationContext.Provider value={{banana}}>
...
}
After that you can use this.context.banana(data) as normal.

React TS: Despite passing method through wrapper, child still can't access getPage()

I'm trying to build a fetch method that can be shared to a bunch of Reader components through a higher order component. I believe I've built the HOC right, but I'm not 100% sure.
import React, { Component } from 'react';
import base from "./firebase";
export default (ChildComponent) => {
class GetPage extends Component<{},any> {
constructor(props: any) {
super(props);
this.state = {
text: "Hii"
};
}
public getPage(page: string) {
base
.fetch(page, { context: this, })
.then(data => this.setState({ text: data }));
console.log(this.state.text)
}
public render() {
return <ChildComponent getPage={this.getPage} text={...this.state.text} {...this.props}/>;
}
}
return GetPage;
};
You can see that I'm importing the HOC on the second line , but despite this, the 'Reader' component is throwing an error that 'getPage' is no where to be found.
import * as React from "react";
import GetPage from "./fetch";
class Reader extends React.Component<{},any>{
public componentWillMount() {
this.getPage('1A1');
}
public render() {
return <div{...getPage('1A1')}>{...this.state.text}</div>;
}
}
export default (GetPage(Reader));
Inside your Reader component instead of accessing this.getpage try with this.props.getpage
and I don't understand why you are doing with following:
<div{...getPage('1A1')}>

Can't find an internal method in a React container component

I'm trying to get AJAX-retrieved data into a parent React component so it can be fed down to a child component. I'm using the popular pattern for this defined here where a comment list is used as the example:
components/CommentList.js
import React from 'React';
export class CommentList extends React.Component {
constructor(props) {
super(props);
}
render() {
return <ul> {this.props.comments.map(renderComment)} </ul>;
}
renderComment({body, author}) {
return <li>{body}—{author}</li>;
}
}
components/CommentListContainer.js
import React from 'React';
import { CommentList } from './CommentList';
export class CommentListContainer extends React.Component {
constructor() {
super();
this.state = { comments: [] }
}
componentDidMount() {
$.ajax({
url: "http://get/some/api",
dataType: 'json',
success: function(comments) {
this.setState({comments: comments});
}.bind(this)
});
}
render() {
return <CommentList comments={this.state.comments} />;
}
}
index.js: the entry point for webpack
import React from 'react'
import { render } from 'react-dom'
import { CommentListContainer } from './components/CommentListContainer';
window.React = React;
render(
<CommentListContainer />,
document.getElementById('nav__react-target')
)
When doing all this, I get the following error:
Uncaught ReferenceError: renderComment is not defined
I've move the methods around as well as tweaked the importing of dependencies in various spots with no luck. Any ideas?
Thanks in advance.
You don't have unguarded references to sibling methods with ES2015 classes (as you do in Java / C#, etc.) - instead you need to explicitly reference this to get at the methods of the class:
render() {
// I changed map(renderComment) to map(this.renderComment)
return <ul>{this.props.comments.map(this.renderComment)}</ul>;
}

React and setState and autocomplete

Im using react and material ui.
This is my component
```
import React from 'react';
import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import AutoComplete from 'material-ui/AutoComplete';
// the light theme
const lightMuiTheme = getMuiTheme(lightBaseTheme);
// the autocomplete component width
const Preferencestypeahead = {
maxWidth: 600,
position:'relative',
margin:'auto'
}
export default class Preferences extends React.Component {
constructor(props) {
super(props);
this.handleUpdateInput = this.handleUpdateInput.bind();
this.state = {
dataSource: [],
};
}
handleUpdateInput(value){
this.setState({
dataSource: [
value,
value + value,
value + value + value,
],
});
};
render() {
return (
<div>
<MuiThemeProvider muiTheme={lightMuiTheme}>
<section style={Preferencestypeahead}>
<AutoComplete
hintText="Type"
dataSource={this.state.dataSource}
onUpdateInput={this.handleUpdateInput.bind(this)}
floatingLabelText="Search"
fullWidth={true}
/>
</section>
</MuiThemeProvider>
</div>
)
}
}
I keep getting setState is not defined when I type anything inside the autocomplete. Where could I be going wrong? I have also faced this problem when I tried to import the tabs as well
You need to bind to "this"
This line is the problem:
this.handleUpdateInput = this.handleUpdateInput.bind();
Change it to:
this.handleUpdateInput = this.handleUpdateInput.bind(this);
Since you are already using ES6 you could just do
...
onUpdateInput={(val) => this.handleUpdateInput(val)}
...
then you don't need to do this --> this.handleUpdateInput = this.handleUpdateInput.bind(this); in your constructor

Resources