I want to use this library for my react js project https://www.npmjs.com/package/html-to-image.
In my case I want to use a var that contain a simple div and export It as an Image, but there's a problem: my var (node) is a JSX.Element and the "toPng" method of the library accept as a parameter an HTMLElement.
I know that the doc of the library suggests to use methods like this to get an HTMLElement: var node = document.getElementById('my-node') but in my case I can't do something like this because my node isn't in the document.
So there's a way to convert a JSX.Element to an HTMLElement? Thanks in advance ;)
To do that, use renderToStaticMarkup from the react-dom/server library.
import { renderToStaticMarkup } from "react-dom/server"
const output = document.createElement('p')
const staticElement = renderToStaticMarkup(reactElement)
output.innerHTML = staticElement
Question might be old, but while looking for a solution for this I found an elegant way to render a hidden element as an image.
render() {
return (
<div>
<div
id="image-template"
className="d-none"
style={{
width: "200px",
height: "200px",
display: "flex",
justifyContent: "center",
alignItems: "center",
color: "white",
fontSize: "10px",
textAlign: "center",
backgroundColor: "green",
}}
>
<span style={{ margin: "20px" }}>{"Hey, an image rendered but not visible!"}</span>
</div>
</div>
);
}
With the class d-none (from bootstrap) the element is hidden to the user, so it does not matter where you place it. d-none in bootstrap is basically display: none !important;.
Now add a function to create an image:
async createImage() {
const template = document.getElementById("image-template").cloneNode(true);
template.classList.remove("d-none");
const base64Image = await htmlToImage.toPng(template, {
height: 200,
width: 200,
});
// Do whatever you want to do with your image
}
The element is cloned and the d-none/display: none removed to be able to render it with htmlToImage. The result is the base64 data you can use whereever you want.
Related
I am trying to override the CSS property position on the .MuiDataGrid-columnsContainer. When looking at the https://material-ui.com/api/data-grid/#css you can see there is a rule for root but there isn't a rule for .MuiDataGrid-columnsContainer. I have read the documentation on overriding the CSS properties but I can't seem to get it to work for that class.
Normally I would leverage the rule and do the following
const useStyles = makeStyles({
paper: {
background: '#010310',
}
});
Then return something like this
const classes = useStyles();
return (
<DataGrid classes={{ paper: classes.paper }} />
);
However, when I replace the word paper in the makeStyles and in the return to be columnsContainer it doesn't work.
Possible Reasons It's Not Working
Based on the documentation on the CSS for datagrid (link above), the only Rule is root and since the .MuiDataGrid-columnsContainer doesn't have a Rule then I need to do it differently. Unfortunately, I haven't found another way that has worked.
I appreciate any help I get, Thank you!
You can give inline style to the class that you want to override and give it an !important flag.
For example if the class you want to override is .MuiDataGrid-columnsContainer then you can simply do this .
.MuiDataGrid-columnsContainer{
position: relative !important; // to override the default css property
}
For a local overriding of MuiDataGrid-columnsContainer, as you can see on api doc, you have to pass your css to root class. So:
<DataGrid classes={{ root: classes.root }} />
Now if you want to override MuiDataGrid-columnsContainer in particular, in your makeStyles write something like:
const useStyles = makeStyles({
root: {
"&.MuiDataGrid-columnsContainer":{
background: '#010310',
}
}
});
Thats it.
root: {
"& .MuiDataGrid-root": {
color: `#fff`
},
"& .MuiIconButton-label": {
color: `#fff`
}
},
I put this "& .MuiDataGrid-root" in my main layout and it works very well.
const useStyles = makeStyles(() => ({
root: {
display: 'flex',
"& .MuiDataGrid-root": {
color: `#fff`
},
"& .MuiIconButton-label": {
color: `#fff`
}
},
appBar: {
backgroundColor:'#181745',
color:theme.palette.primary.main,
zIndex: theme.zIndex.drawer + 1,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
},
}))
In my React app I have a form page for a book which has an option to upload a cover photo. I decided to use material-ui-dropzone (https://yuvaleros.github.io/material-ui-dropzone/) which works great, but I ran into one issue. The preview does not look very good when only one file is allowed.
I would like it to be centered and as big as possible, but I am really struggling. Relevant code:
const useStyles = makeStyles(() => ({
dropZone: {
height: '100%',
fullWidth: 'true',
},
previewContainer: {
container: 'true',
width: '100%',
height: '100%',
},
preview: {
//width: '100%',
//height: '100%',
//item: 'true',
xs: '12',
},
previewImg: {
//height: '100%',
//width: '100%',
},
}));
const classes = useStyles();
<DropzoneArea
acceptedFiles={['image/*']}
dropzoneClass={classes.dropZone}
previewGridClasses={{
container: classes.previewContainer,
item: classes.preview,
image: classes.previewImg,
}}
dropzoneText={'Upload book cover image'}
filesLimit={1}
onChange={(files) => formikProps.setFieldValue('coverImage', files)}
showAlerts={false}
/>
I have been trying a bunch of things, but so far the only way I even managed to get an actual visual effect was by adding some padding to the preview class. Any help would be very much appreciated.
I ended up resolving the issue after snooping around the source code of the component so I thought I would post the solution here in case someone runs into this in the future. As it turns out the dropzone component does not actually apply the item style to the image (even though the docs say it should). I did find a way around this, you can actually provide a custom getPreviewIcon function to the component and you can add your style there.
<DropzoneArea
acceptedFiles={['image/*']}
dropzoneClass={classes.dropZone}
previewGridClasses={{
item: classes.preview,
}}
getPreviewIcon={(file) => {
if (file.file.type.split('/')[0] === 'image')
return (
<img className={classes.previewImg} role="presentation" src={file.data} />
);
}}
dropzoneText={'Upload book cover image'}
filesLimit={1}
onChange={(files) => formikProps.setFieldValue('coverImage', files[0])}
showAlerts={false}
/>
I am trying to reference data attributes from JSS, test for them, and apply the styles if the test is true. However, when I run the code below, it does not apply "color:green" and I have no idea why!
import React from "react";
import { createUseStyles } from "react-jss";
const useStyles = createUseStyles({
statusDiv: {
backgroundColor: "lightGrey",
padding: "10px",
fontWeight: "bold",
'[data-status="OPEN"]': {
// no effect ..?
color: "green",
},
},
});
const App = () => {
const classes = useStyles();
return (
<div data-status="OPEN" className={classes.statusDiv}>
OPEN
</div>
);
};
export default App;
That's an interesting case.
What you can do is:
export default {
'#global' : {
'div.testClass[data-custom="value"]': {
color: 'red',
border: '1px, solid navy'
}
}
}
This assumes a JSS plug-in #global installed, but this is something I think is quite useful to have.
More about the plug-in #global - here
And if you want to address a data- attribute like in your edited example 0 you should do:
testClass: {
'[data-custom="value"]': {
//styles for the custom data
}
}
And in the markup you'll have (depends on the way you extracting the class):
testClass.classes['[data-custom="value"]']
... styles for the custom data will be applied by the above.
The way you wrote in your last edit will not work. You should add a small bit though. To work the code example you provided:
...
fontWeight: "bold",
'& [data-status="OPEN"]': {
// no effect ..?
color: "green",
},
The & require a nesting plug-in setup - here is more about it.
Thanks to the amazing support from #Vladyn, the following code demonstrates this working perfectly :D
import React from "react";
import { create } from "jss";
import { JssProvider, createUseStyles } from "react-jss";
import nested from "jss-plugin-nested";
const jss = create();
jss.use(nested());
const useStyles = createUseStyles({
statusDiv: {
backgroundColor: "lightGrey",
padding: "10px",
fontWeight: "bold",
'&[data-status="OPEN"]': {
color: "green",
},
'&[data-status="CLOSED"]': {
color: "red",
},
},
});
const App = () => {
const classes = useStyles();
const status = "OPEN";
return (
<JssProvider jss={jss}>
<div data-status={status} className={classes.statusDiv}>
{status}
</div>
</JssProvider>
);
};
export default App;
If your JS looks like this:
<div data-customAttribute={value} className={classes.testClass}></div>
Your CSS will look like:
.testClass[data-customAttribute="value"] {
// your styles
}
Now, I have component like this:
code of it:
import React from "react";
import {withStyles} from "material-ui/styles";
import Settings from "material-ui-icons/Settings";
import Button from "material-ui/Button";
const styles = {
button: {
color: "primary",
height: 95,
width: 95,
disableRipple: "true",
focusRipple: "true",
},
icon: {
height: 35,
width: 35,
display: "block",
float: "none",
},
text: {
height: 35,
width: 35,
display: "block",
float: "none",
marginTop: 10,
},
};
/* eslint-disable react/prop-types */
const IconedLabel = ({classes}) => (
<section>
<Button className={classes.iconButton} variant="raised" color="primary">
<Settings className={classes.icon}/>
<div className={classes.text}>Message</div>
</Button>
</section>
);
export default withStyles(styles)(IconedLabel);
But need to button, that in top part contains icon and text message in bottom.
I use reactjs and material-ui lib from here https://material-ui-next.com/demos/buttons/
The Button component uses flexbox to control the layout/alignment of content. To align the content vertically (so the icon is above the text), you can simply change the flex-direction to column.
This style needs to be applied to an element inside the button component, not to the root element. You can use the classes property to override all of the styles in a component.
In this case, you want to add flexDirection: column to the label class.
Documentation on class overrides in material ui v1
Here's a working example. Hope it helps.
const [React, ReactDOM, Button, Settings, withStyles] = [window.React, window.ReactDOM, window['material-ui'].Button, ({className}) => <i className={`material-icons ${className}`}>settings</i>, window['material-ui'].withStyles]
// Ignore code above this line
const styles = theme => ({
button: {
height: 95, // setting height/width is optional
},
label: {
// Aligns the content of the button vertically.
flexDirection: 'column'
},
icon: {
fontSize: '32px !important',
marginBottom: theme.spacing.unit
}
})
const CustomButton = ({ classes }) => (
<Button
/* Use classes property to inject custom styles */
classes={{ root: classes.button, label: classes.label }}
variant="raised"
color="primary"
disableRipple={true}
>
<Settings className={classes.icon} />
Message
</Button>
)
const WrappedCustomButton = withStyles(styles)(CustomButton)
ReactDOM.render(<WrappedCustomButton />, document.querySelector('#root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script><script src="https://unpkg.com/material-ui#1.0.0-beta.40/umd/material-ui.production.min.js"></script><link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"><div id="root" />
A (potentially bad) solution would simply be:
.MuiIconButton-label {
flex-direction: column
}
I say bad, because you might want to use it in it's standard format elsewhere.
What I opted to do was add a class name nav-bar-icon-wrapper to the IconButton & set the flex direction in it's parent:
.nav-bar-icon-wrapper {
flex-direction: column
}
.MuiIconButton-label {
flex-direction: inherit
}
If I run into instance later where I want the icon/label button to be standard, I'll just add a new class default-icon-wrapper and css that handles that:
.default-icon-wrapper {
flex-direction: row
}
FWIW:
I preach the BEM http://getbem.com/introduction/ convention AND that whenever you make a component, you add an optional modifier prop.
I have functions in a shared dir that looks these:
export function BEMifyThis(modifier) {
return (klass) => BEMify(klass, modifier)
}
export function BEMify(klass, modifier=false) {
if (modifier) {
klass += ` ${klass}-${modifier}`
}
return klass
}
Then I use that everywhere in my component so the user can access the component elements as a group or individually using their modifiers.
import {BEMifyThis} from '../shared/bem'
const BEMify = BEMifyThis(this.props.modifier)
className={"navbar__menu_item")}
becomes
className={BEMify("navbar__menu_item")}
so something like navbar__menu_item becomes navbar__menu_item navbar__menu_item-logout
I'm wondering how to align text in Draft.js just like on the picture below.
I have searched this several days, but I haven't found the solution.
After reading the source code, I found a way for it. Using blockRenderMap, you can add some custom block types like this:
const blockRenderMap: Record<string, DraftBlockRenderConfig> = {
'header-one-right': {
element: 'h1',
wrapper: <StyleHOC style={{ ...blockStylesMap['header-one'], display: 'flex', justifyContent: 'flex-end' }} />,
},
'header-two-right': {
element: 'h2',
wrapper: <StyleHOC style={{ ...blockStylesMap['header-two'], display: 'flex', justifyContent: 'flex-end' }} />,
},
'header-three-right': {
element: 'h3',
wrapper: <StyleHOC style={{ ...blockStylesMap['header-three'], display: 'flex', justifyContent: 'flex-end' }} />,
},
'unstyled-right': {
element: 'div',
wrapper: <StyleHOC style={{ ...blockStylesMap['unstyled'], display: 'flex', justifyContent: 'flex-end' }} />,
},
};
I use flex to avoid wasting time to find a away to override the internal style .public-DraftStyleDefault-ltr.
StyleHOC is quite simple:
const StyleHOC: React.FC<Props> = ({ style, children }) => {
const childrenWithStyle = React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
return React.cloneElement(child, { style });
}
return child;
});
return <>{childrenWithStyle}</>;
};
And then you can toggle the blockType using RichUtils.toggleBlockType(editorState, blockType).
The Editor component has a div with the class .public-DraftStyleDefault-ltr. This controls the text alignment of each paragraph that you write. As you create more paragraphs, more of these divs are created to contain the text. The text is wrapped in a span element and .public-DraftStyleDefault-ltr has a default alignment of text-align: left.
I created some css classes for text-align: left, text-align: center, text-align: right and text-align: justify and added this basic for loop to my component for creating text alignment buttons.
const textBlock = document.querySelectorAll(".public-DraftStyleDefault-ltr");
for (let i = 0; i < textBlock.length; i++) {
textBlock[i].classList.toggle(this.props.style);
}
this.props.style is the name of the css class that determines the text-alignment I wanted.
It is a pretty basic fix since this way when you click align right say, the whole document is aligned right. I am planning to work on this so only the selected text is aligned so should be able to update this answer soon. Hope it helps in some way
I tried to make almost the same thing. But my trouble was in text-align property, which was correctly set to block span, but .public-DraftStyleDefault-ltr doesn't react to it.
So, I have made next decision, which get the first div child, and copy it's params:
const paragraphs: any = document.querySelectorAll(".public-DraftStyleDefault-ltr");
for (let i = 0; i < paragraphs.length; i++) {
const paragraph = paragraphs.item(i);
if (paragraph) {
const firstItem = paragraph.querySelectorAll('*').item(0);
// Apply to the parent the first child style
paragraph.style.textAlign = firstItem.style.textAlign;
}
}
To change block alignment you can:
1- set alignment data
const modifiedBlockState = Modifier.setBlockData(editorState.getCurrentContent(),
editorState.getSelection(),Map({align:'align-center'}));
setEditorState(EditorState.push(modifiedBlockState,'change-block-data'));
2- use it in styling function
/*
JSX
blockStyleFn={ block => block.getData().get('align')
this will return 'align-center|left|right' which will be assigned as a classname
*/
<Editor blockStyleFn={ block => block.getData().get('align')} .../>
//CSS
.align-center div{
text-align: center;
}
.align-right div{
text-align: right;
}
.....