react-codemirror2 has no CSS effect - reactjs

I added react-codemirror2 to my project but it does not load the css although I import the codemirror.css file, because it is mentioned that css should be applied into the component somehow (mentioned here), but long story short it is still rendered like this:
Code Mirror with no CSS
I really don't know what the issue can be. So here is my code:
import { connect } from 'react-redux';
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/material.css';
import { Controlled as CodeMirror } from 'react-codemirror2';
require('codemirror/mode/xml/xml');
require('codemirror/mode/javascript/javascript');
class MyComponent extends Component {
handleValueChange = value => this.props.onUpdateValue({ value });
render() {
const { shade } = this.props;
const myOptions = {
mode: 'xml',
theme: shade === 'dark' ? 'material' : 'default',
lineNumbers: true,
}
return (
<CodeMirror
id="editor"
value={this.props.value}
options={myOptions}
onBeforeChange={(editor, data, value) => {
this.handleValueChange(value);
}}
onChange={(editor, data, value) => {}}
/>
);
}
}
function mapStateToProps(state) {
return {
shade: state.muiTheme.shade,
};
}
export default connect(
mapStateToProps,
null
)(MyComponent);
I also tried to #import the css file inside the global.css file of my project (like below) but nothing's changed.
#import '/node_modules/codemirror/lib/codemirror.css';
#import '/node_modules/codemirror/theme/material.css';
I really don't know what else should be tried or what am I doing wrong, because it shouldn't be something very special. So I'm asking you, and any suggestions would be helpful.
Thanks :)

I don't know why you met the problem, but it works for me.
import React, { Component } from "react";
// Import the code mirror component.
import { Controlled as CodeMirror } from "react-codemirror2";
// The following two imports is for the theme.
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/material.css';
// This import is for the language syntax highlighting.
import 'codemirror/mode/javascript/javascript.js';
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
src: ''
}
}
render() {
let option = {
mode: 'javascript',
theme: 'material'
};
return (
<div>
<Controlled
value={this.state.src}
option={option}
onBeforeChange={(editor, data, value) => {
this.setState({ src: value });
}}
onChange={(editor, data, value) => {}}
/>
</div>
)
}
}
Maybe the problem is here:
const myOptions = {
mode: 'xml',
theme: shade === 'dark' ? 'material' : 'default',
lineNumbers: true,
}
You should set the option object in rendering function before return, or set it in constructor() and attach it to this, like this.option = {}.

Okay this is something which may only happened to me but I post my solution, maybe someday someone have same issue.
As I said the problem was not loading the css, I don't know how others handle this issue but I have to copy all styles inside the node_modules/codemirror/lib/codemirror.css into a new css file and put it in some path inside my project. And inside the global.css file of my project I just imported that new created file like #import absolute/path/to/file/codemirror.css; and it worked at least for one case and one theme. I'm sure there is better ways to connect to all css files of codemirror but for now it did the basic thing that I needed.

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

Load image from project in react chrome extension

I am trying to build a react chrome extension. I started off with the simple project found here. In its structure, the popup is contained in Modal.js and I would like to include an image in it which is contained in the project. However, to access that I must use chrome.runtime.getURL("image_file.png"), which I cannot access in Modal.js, but only in content.js. How should I get the image properly to Modal.js?
This is all activated when the browser action button is pressed, which calls this function within content.js:
function main() {
const extensionOrigin = 'chrome-extension://' + chrome.runtime.id
if (!location.ancestorOrigins.contains(extensionOrigin)) {
fetch(chrome.runtime.getURL('index.html'))
.then((response) => response.text())
.then((html) => {
const styleStashHTML = html.replace(
/\/static\//g,
`${extensionOrigin}/static/`
)
$(styleStashHTML).appendTo('body')
})
.catch((error) => {
console.warn(error)
})
}
}
The content of index.html is:
<div id="modal-window"></div>
but when the html is returned from the fetch it has expanded in the build to:
<div id="modal-window"></div><script src="/static/js/runtime-main.0d1674f1.js"></script><script src="/static/js/2.021a85b4.chunk.js"></script><script src="/static/js/main.d80831e3.chunk.js"></script>
It is unclear to me how index.js is getting called but it is, which finds the div in index.html and replaces it with the modal object as follows:
import React from 'react'
import ReactDOM from 'react-dom'
import Modal from './Components/Modal'
ReactDOM.render(<Modal />, document.getElementById('modal-window'))
My current implementation of modal.js is as follows. Obviously the img src won't work as it is right now (needing to use chrome.runtime.getURL):
import React from 'react'
const Modal = () => {
return <img src="icon48.png" alt="icon48"></img>
}
export default Modal
How would I actually be able to get the image src from chrome.runtime.getURL?
Figured this out. I don't know if this is the best solution, but it is working for me.
Since I need to access chrome.runtime.getURL I need to do that from the content script. But I need the value of that in my component which doesn't have access to the chrome api. So I message between them through window events. Here is some example code:
ExampleComponent.js
import React, { Component } from 'react'
let imgSrc = 'file.png'
// Note that this listener is outside the Component
window.addEventListener('ToComponent', (msg) => {
imgSrc = msg.detail
})
class ExampleComponent extends Component {
constructor(props) {
super(props)
// Note that this is occurring inside the Component constructor
var event = new CustomEvent('FromComponent')
window.dispatchEvent(event)
this.state = {
imgSrc: imgSrc,
// etc.
}
}
render() {
return (
<img src={this.state.imgSrc} alt=""></img>
)
}
}
content.js:
window.addEventListener('FromComponent', () => {
const imgSrc = chrome.runtime.getURL('file.png')
// Send response
var event = new CustomEvent('ToComponent', { detail: imgSrc })
window.dispatchEvent(event)
})
Content scripts can use chrome.runtime.getUrl().
https://developer.chrome.com/docs/extensions/mv3/content_scripts/
You'll need to declare the images in your manifest.json file using web_accessable_resources.
For example, mine are all copied to a img folder:
"web_accessible_resources": [
{
"resources": ["/img/*"],
"matches": ["https://*"]
}
if using webpack, you'll need to copy them to your build as well:
new CopyWebpackPlugin({
patterns: [
{
from: 'src/assets/img',
to: 'img',
force: true,
},
],
}),
Then in the component call getUrl and include it in render's return:
const logo = chrome.runtime.getURL('img/logo.png');
return ( <img src={ logo } className="app-logo" alt="logo" /> );

Is this only possible with external URLs and not local?

I'm trying to make a photo gallery using react-images, the URLs are correct but the photos themselves are not loading into my web app. I get the broken image icon when switching themodalIsOpen:false to true.
Ive tried looking up examples of the same problems and alternatives, like if the component was configured right or if I am extending it right in the class.
import React, { Component } from 'react';
import Carousel, { Modal, ModalGateway } from 'react-images';
import blksmith from '../images/gallery/illustration/Blacksmith.jpg';
import mage from '../images/gallery/illustration/Mage.jpg';
const images =
[
{
src:{blksmith}
} ,
{
src:{mage}
}
];
class illuGallery extends Component {
state = { modalIsOpen: false }
toggleModal = () => {
this.setState(state => ({ modalIsOpen: !state.modalIsOpen }));
}
render() {
const { modalIsOpen } = this.state;
return (
<ModalGateway>
{modalIsOpen ? (
<Modal onClose={this.toggleModal}>
<Carousel
views={images}
/>
</Modal>
) : null}
</ModalGateway>
);
}
}
export default illuGallery;
This is in the actual gallery.js file, the web page that renders the gallery.
import React from 'react';
import Layout from "../components/layout";
import IlluPhotos from "../components/illustrationGallery";
import SEO from "../components/seo";
import './gallery.scss';
const GalleryPage = () => {
return (
<Layout>
<div style={{width:'100%',height:'250px'}}>
<SEO title="Gallery" />
<IlluPhotos/>
</div>
</Layout>
)
}
export default GalleryPage;
I am seeking some feedback on how to get this to work and what I did wrong, or what I should explore more.
So I ended up adding the pictures I wanted for the gallery to the public folder as mentioned farther down in this post
Since the https://localhost:8000 was appearing in front of the links to the images I wanted to use.
Thank you all for helping me find the answer!!
You don't need to import images.
According to react-images documentation, you just need to pass path to image as a string to <Carousel> component, like in this example below:
import React from 'react';
import Carousel from 'react-images';
const images = [{ src: 'path/to/image-1.jpg' }, { src: 'path/to/image-2.jpg' }];
class Component extends React.Component {
render() {
return <Carousel views={images} />;
}
}

React TinyMCE editor set bbcode content dynamically

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.

Webpack theme loader

I'd like to accomplish the following structure:
button.core.jsx
button.theme-a.jsx
button.theme-b.jsx
To take React as an example, I'd like to do the following in button.core.jsx:
import React from 'react';
import Themed from './button.[theme]';
export default class Button extends React.Component {
render() {
if (Themed) {
return <Themed />;
}
return <button>default button</button>;
}
}
In other words, I want to define a theme in my webpack.config.js and load that file if it exists. If it does't, render the default behaviour. I think this would be a very powerful setup!
I've been searching around for making a custom loader, but no success yet. Can anyone point me in the right direction?
I've got this working with writing a custom "resolver":
const ThemeResolver = {
apply: function(resolver) {
resolver.plugin('file', function(req, cb) {
if (req.request.indexOf('[theme]') == -1) {
return cb();
}
const defaultFile = req.request.replace('[theme]', 'Default');
const themedFile = req.request.replace('[theme]', process.env.THEME);
req.request = themedFile;
this.doResolve(['file'], req, (err) => {
if (!err) {
return cb();
}
req.request = defaultFile;
this.doResolve(['file'], req, cb);
})
});
}
};
module.exports = {
// ...
plugins: [
new webpack.ResolverPlugin([ThemeResolver]),
]
// ...
};
It tries to resolve a file with [theme] in its path into a path with the theme defined as a environment variable. If it fails, it'll fallback to a default file instead. This way I can require a themed file like so:
import Presentation from './button-[theme]'
The main component turned out to be a bit different than in my question, but I'm actually pretty content with it:
import React from 'react';
import Presentation from './button-[theme]';
export default class Button extends React.Component {
onClick = (e) => console.log('some logic');
render() {
return <Presentation onClick={ this.onClick } />;
}
}
The logic of this button-component can live inside of button.core.jsx, while the presentation will be handled by one of these components:
THEME=theme-a npm start // button-[theme] resolves to button-theme-a.jsx
THEME=theme-c npm start // button-[theme] resolves to button-default.jsx
Disclaimer: I didn't use this in a large scale or production environment yet, but it seems to work in a small POC. Please let me know if I'm doing something unwise!

Resources