Rotate a MUI LinearProgress - javascript

I'm trying to make a graph chart with MUI using the LinearProgress component with some styling the basic idea is to get this to rotate 90deg
const BorderLinearProgressBottom = withStyles((theme) => ({
root: {
height: 50,
borderRadius: 5,
},
colorPrimary: {
backgroundColor:
theme.palette.grey[theme.palette.type === "light" ? 200 : 700],
},
bar: {
borderRadius: 5,
backgroundColor: "#00A99E",
},
transform: [{ rotate: "90deg" }],
}))(LinearProgress);
gets my
<BorderLinearProgressBottom
variant="determinate"
value={22}
/>
to look like this
How can I get it to rotate by 90deg?
I tried putting in the BorderLinearProgressBottom transform: [{ rotate: "90deg" }], but that did not work.
Code Sandbox

Please don't use rotate(-90deg) if you want to display the LinearProgress vertically, it will break your layout because transform only scales the element visually without changing the size, so a rotated LinearProgress still occupies the space as if it's laid out horizontally. To rotate both its appearance and size, you should have a look at the implementation of Slider for reference. But I'll write it down for you now to save time.
First off you need to swap the height and width of the ProgressBar container:
// before
height: 50, // restrict the height
width: 'auto', // expand as long as you want (inside container)
// after
width: 50, // restrict the width
height: '100%', // expand as high as you want (inside container)
Then rotate the progress bar inside. This transform works because it only transforms 'locally' (the bar position is absolute inside the container).
bar: {
// the default style uses translateX, so we need to switch the axis
transform: ({ value }) => {
return `translateY(${value}%) !important`;
}
}
And that's it. You're done. Not it will look like a vertical Slider.
Live Demo

Related

React DropDown Component on FabricJS Canvas

I am able to create a TextBox on FabricJS Canvas. But I am unable to create a Dropdown on FabricJS Canvas. There is an option to create a TextBox on FabricJS using fabric.Textbox but the same cannot be done for dropdown. Please see my codes below on how I create a Textbox on FabricJs and kindly advise on how I can do the same If I want to create a DropDown component on a FabricJS canvas.
If there any options other than using FabricJS that allows more flexibility and it is abetter option for creating complex components, kindly share a link or source to help me.
My Codes on how I create a Textbox on FabricJS canvas
export default function TextBox(props) {
useEffect(() => {
var text = new fabric.Textbox("Enter Text Here", {
left: 10,
top: 50,
width: 294,
height: 60,
fontStyle: "normal",
color: "#999999",
fontWeight: 375,
fontSize: 12,
backgroundColor: "#ffffff",
borderColor: "#ABB3BF",
padding: 20,
showTextBoxBorder: true,
textboxBorderColor: "green",
borderStyle: "solid",
});
props.canvas.add(text);
});
return null;
}
I don't think that you can do that by default in FabricJS. But you can add custom control on fabric objects, then add your submenu/dropdown component as a context menu. There are several context-menu libraries for ReactJS, like "React-Contextify".

How to custom color text and icon in TableSortText component of Material-ui?

What I'm trying to do:
I am trying to provide the user the option to provide custom styling to my EnhancedTable component by passing in a styles object containing properties such as headCellColor, headCellBackgroundColor, bodyCellColor, bodyCellBackgroundColor etc which can be used to color the cells in TableHead and TableBody.
In the TableHead component, I use a TableSortLabel in a way similar to what they've done in this material-ui docs example: https://material-ui.com/components/tables/#sorting-amp-selecting
I wish to custom color the text and the arrow icons on hover and when active based on the props provided by the user.
Let's see the colors of TableSortLabel in different situations:
The color of the text is grey initially and there is no arrow. When mouse is hovered over it, a grey arrow appears and the text turns black. On clicking it, active state is set, the grey arrow turns black and the text turns black permanently until active state is removed.
What I've tried so far:
const useStyles = makeStyles({
tableSortLabel: props => ({
backgroundColor: "blue",
color: props.headCellColor,
fill: props.headCellColor,
"&:hover": {
backgroundColor: "blue"
}
})
});
function EnhancedTableHeadCell(props) {
const { isActive, onHoverSortState, clickHandler, ...otherProps } = props;
const classes = useStyles(props.styles);
return (
<FancyTableCell styles={props.styles} {...otherProps}>
<TableSortLabel
active={isActive}
classes={{
icon: classes.tableSortLabel,
active: classes.tableSortLabel
}}
direction={onHoverSortState}
onClick={clickHandler}
>
{props.children}
</TableSortLabel>
</FancyTableCell>
);
}
This is what it looks like in the browser:
https://i.postimg.cc/fW7W2MRB/c1.jpg
The first one is a normal header, the second is on hover and the third is when clicked (active state).
From what we can observe, the text color is totally unaffected by the color css property in all the three cases (normal, hover, active). On hover, backgroundColor only affects the icon and not the text. However, we can see that backgroundColor affects the text when it is active. Everything is going as expected with the icon. The only issue is with the text.
What could I be possible doing wrong? How can I solve my problem?
What worked for me is:
const StyledTableSortLabel = withStyles((theme: Theme) =>
createStyles({
root: {
color: 'white',
"&:hover": {
color: 'white',
},
'&$active': {
color: 'white',
},
},
active: {},
icon: {
color: 'inherit !important'
},
})
)(TableSortLabel);
You can reference the following for increasing css specificity:
https://material-ui.com/customization/components/#pseudo-classes
Solution for your problem is following:
MuiTableSortLabel: {
root: {
color: textPrimary,
// if you want to have icons visible permanently
// '& $icon': {
// opacity: 1,
// color: primaryMain
// },
"&:hover": {
color: primaryMain,
'&& $icon': {
opacity: 1,
color: primaryMain
},
},
"&$active": {
color: primaryMain,
// && instead of & is a workaround for https://github.com/cssinjs/jss/issues/1045
'&& $icon': {
opacity: 1,
color: primaryMain
},
},
},
}
This restyling I use globally via my ThemeProvider, but you can of course use it individually in your single component by using "withStyles" HOC (see "BootstrapButton" in example)
I could'nt find a proper way to do it so I came up with a temporary solution overriding the material ui css.
I added this to my global css:
.MuiTableSortLabel-root.MuiTableSortLabel-active,
.MuiTableSortLabel-root:hover,
.MuiTableSortLabel-icon {
color: inherit !important;
}
Worked for me with Mui5:
sx = {
{
'&.MuiTableSortLabel-root': {
color: 'white',
},
'&.MuiTableSortLabel-root:hover': {
color: 'blue',
},
'&.Mui-active': {
color: 'blue',
},
'& .MuiTableSortLabel-icon': {
color: 'blue !important',
},
}
}
'&.MuiTableSortLabel-root' <-- no space &.
'&.Mui-active' <-- no space &.
'& .MuiTableSortLabel-icon' <-- space

Targeting specific component elements in QML

CSS makes it super easy to have on hover effects for specific buttons/elements in yhour DOM. I'm trying to create a landing page of sorts for a desktop app, but I'm trying to get around having to copy paste specific states for each object I create in my main.qml.
Heres my main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
Window
{
visible: true
width: 640
height: 480
title: qsTr("Hello World")
NavBar
{
id: mainNavigation
}
}
NavButton.qml
Rectangle
{
id: navButton
height: parent.height
width: parent.width / 3 - 10
color: "orange"
MouseArea
{
state: "unhovered"
hoverEnabled: true
height: parent.height
width: parent.width
onHoveredChanged:
{
console.log("hovered")
if(this.state == "hovered")
{
parent.state = "unhovered"
}
else
{
parent.state = "hovered"
}
}
} // end mouse area
states:
[
State
{
name: "hovered"
PropertyChanges
{
target: navButton
width: parent.width
color: "red"
}
},
State
{
name: "unhovered"
PropertyChanges
{
target: navButton
width: parent.width
color: "orange"
}
}
]
}
NavBar.qml
import QtQuick 2.0
import QtQuick.Controls 1.4
Row
{
id: navigationBar
height: parent.height / 3
width: parent.width
spacing: 15
anchors.centerIn : parent
NavButton
{
id: selectDevice
}
NavButton
{
id: deviceConfig
}
NavButton
{
id: deviceInfo
}
}
right now when I hover over one of these navButtons all three of them expand in size, as apposed to having just the one I hovered over expand and change color. I'm hoping that like with CSS and Javascript, there's a way to target the element that is the subject of the hover event.
I've already tried used parent as the target of the states, but that doesn't seem to work. I've done a bit of hunting for a solution and I don't see anything obvious that allows me to accomplish this. the only alternative I see is to add states to each individual navButton inside my navBar file.
I really hope that isn't the case, as that would be a really verbose solution to a simple problem.
What I really need is a way to target just the element the event is occurring on.
You can simply scrap all those states and whatnot, which are really not needed for such a trivial scenario:
Rectangle {
id: navButton
height: parent.height
width: parent.width / 3 - 10
color: ma.containsMouse ? "red" : "orange"
property alias hoverEnabled: ma.hoverEnabled
MouseArea {
id: ma
anchors.fill: parent
}
}
And you save yourself a bunch of code too. Then simply have hoverEnabled: true for all buttons you want to hover.
Or you can create an auxiliary type called HoverButton.qml which is just a NavButton { hoverEnabled: true } if you want to avoid setting it for each and every button.
I would also recommend to switch the nesting order of your button:
MouseArea {
id: navButton
height: parent.height
width: parent.width / 3 - 10
Rectangle {
anchors.fill: parent
color: navButton.containsMouse ? "red" : "orange"
}
}
This avoids the need to use a property alias, saves you one id and allows you to bind to the mouse area handler hooks directly, something you cannot do in your original code without redirecting the signal with an additional handler and signal.
Finally, I don't think it will be productive to look at QML as if it is HTML, QML is not HTML, and it comes with its own usage paradigms.

How to use React TransitionMotion willEnter()

Using React Motion's TransitionMotion, I want to animate 1 or more boxes in and out. When a box enters the view, it's width and height should go from 0 pixels to 200 pixels and it's opacity should go from 0 to 1. When the box leaves the view, the reverse should happen (width/height = 0, opacity = 0)
I have tried to solve this problem here http://codepen.io/danijel/pen/RaboxO but my code is unable to transition the box in correctly. The box's style jumps immediately to a width/height of 200 pixels instead of transitioning in.
What is wrong with the code?
let Motion = ReactMotion.Motion
let TransitionMotion = ReactMotion.TransitionMotion
let spring = ReactMotion.spring
let presets = ReactMotion.presets
const Demo = React.createClass({
getInitialState() {
return {
items: []
}
},
componentDidMount() {
let ctr = 0
setInterval(() => {
ctr++
console.log(ctr)
if (ctr % 2 == 0) {
this.setState({
items: [{key: 'b', width: 200, height: 200, opacity: 1}], // fade box in
});
} else {
this.setState({
items: [], // fade box out
});
}
}, 1000)
},
willLeave() {
// triggered when c's gone. Keeping c until its width/height reach 0.
return {width: spring(0), height: spring(0), opacity: spring(0)};
},
willEnter() {
return {width: 0, height: 0, opacity: 1};
},
render() {
return (
<TransitionMotion
willEnter={this.willEnter}
willLeave={this.willLeave}
defaultStyles={this.state.items.map(item => ({
key: item.key,
style: {
width: 0,
height: 0,
opacity: 0
},
}))}
styles={this.state.items.map(item => ({
key: item.key,
style: {
width: item.width,
height: item.height,
opacity: item.opacity
},
}))}
>
{interpolatedStyles =>
<div>
{interpolatedStyles.map(config => {
return <div key={config.key} style={{...config.style, backgroundColor: 'yellow'}}>
<div className="label">{config.style.width}</div>
</div>
})}
</div>
}
</TransitionMotion>
);
},
});
ReactDOM.render(<Demo />, document.getElementById('app'));
As per the documentation of styles under the TransitionMotion section (and I don't claim to have understood all of it entirely :)):
styles: ... an array of TransitionStyle ...
The key thing to note here is that there are 2 types of style objects that this library deals with (or at least this TransitionMotion part of it) and it calls them TransitionStyle and TransitionPlainStyle.
The previous values passed into styles attribute were of TransitionPlainStyle. Changing them to TransitionStyle magically starts animating the Enter sequence.
You can read more about 2 different types mentioned above over here.
styles={this.state.items.map(item => ({
key: item.key,
style: {
width: spring(item.width),
height: spring(item.height),
opacity: spring(item.opacity)
}
}))}
Forked codepen demo.
Again, I do not fully understand the inner workings of it just yet. I just know that your styles had to be changed in the above way to make it work.
I will be happy if someone can educate me on this as well.
Hope this helps.

React - change state right after previous state change was rendered

I wanna make cool box grow animation (expand) when user clicks on it and I want to do it following way:
user clicks on expand button -> get div dimensions and top/left positions via ref, store it in state and assign div's style to these values
changed expanded state variable and change div's position to fixed, also change left, top values and width, height css values
My problem is in initial div expand click. It seems that both state's changes are rendered in one cycle so I don't see smooth animation on first expand click. I've tried to do it via setState callback, also tried to update expanded in componentDidUpdate method once div dimensions are in state, nothing worked except delaying expanded set via setTimeout.
Code example via setState callbacks
if (chartsExpanded.get(chart) === "collapsed-end" || !chartsExpanded.get(chart)) {
this.setState({
chartsProportions: chartsProportions.set(
chart,
Map({
left: chartProportions.left,
top: chartProportions.top,
width: chartProportions.width,
height: chartProportions.height
})
)
}, () => {
this.setState({
chartsExpanded: chartsExpanded.set(chart, "expanded")
})
})
}
...
<div
className={`box customers-per-sources-count ${
customersPerSourcesCount.loading ? "loading" : ""
} ${
chartsExpanded.get("customersPerSourcesCount")
? chartsExpanded.get("customersPerSourcesCount")
: "collapsed-end"
}`}
ref={el => {
this.chartRefs["customersPerSourcesCount"] = el
}}
style={{
left: chartsProportions.getIn(["customersPerSourcesCount", "left"], "auto"),
top: chartsProportions.getIn(["customersPerSourcesCount", "top"], "auto"),
width: chartsProportions.getIn(["customersPerSourcesCount", "width"], "100%"),
height: chartsProportions.getIn(["customersPerSourcesCount", "height"], "100%")
}}
>
How can I achieve that style from chartsProportions will be rendered before class based on expanded value is changed? I don't want to use setTimeout nor want to update all charts proportions onScroll event etc.
You just need to pass setState a function instead of an object, to ensure the state changes are applied in order:
this.setState(previousState => ({
previousChange: "value"
}))
this.setState(previousState => ({
afterPreviousChange: previousState.previousChange
}))
https://reactjs.org/docs/react-component.html#setstate
Another option might be to pass a callback to setState that runs after the state changes have been applied, like:
this.setState({ someChange: "value" }, () => this.setState({
otherChange: "value"
}))
CSS transitions could help with this too.
Using React state to animate properties is not the right way to do it. State updates will always get batched and you generally don't want to re-render your entire component 60 times per second
Store 'expanded' boolean in your state, and change element's class accordingly. Use css to add animations between two states
handleClick = () => {
this.setState({ expanded: !this.state.expanded })
}
render() {
return (
<div
className={`box ${this.state.expanded ? 'expanded' : ''}`}
onCLick={this.handleClick}
/>
)
}
in your css
.box {
width: 100px;
height: 100px;
transition: width 2s;
}
.expanded {
width: 300px;
}
added based on comments:
What you want to do is:
set position: fixed to your element. This would snap it to the top of the screen instantly, so you need to pick the right top and left values so that position fixed starts off where it was when position was static (default). For that you can use element.getBoundingClientRect()
calculate desired top and left attributes that would make your element appear in the middle of a screen, and apply them
very important: between step 1 and 2 browser has to render the page to apply position and initial top and left values, in order to have something to start animation from. It won't be able to do that if we apply both of these styles synchronously one after another, as page will not render until JS stack frame is clear. Wrap stage 2 logic in setTimeout which will make sure that browser renders at least once with styles applied at stage 1
rough working example:
class Example extends React.Component {
constructor(props) {
super(props)
this.state = {
expanded: false,
style: {}
}
}
handleClick = (e) => {
if (!this.state.expanded) {
const r = e.target.getBoundingClientRect()
const style = {
top: r.y,
left: r.x,
}
this.setState({
expanded: !this.state.expanded,
style
})
setTimeout(() => {
this.setState({
style: {
top: (window.innerHeight / 2) - 50,
left: (window.innerWidth / 2) - 50,
}
})
})
} else {
this.setState({
expanded: false,
style: {}
})
}
}
render() {
return (
<div className={'container'}>
<div className={'empty'} />
<div className={'empty'} />
<div className={'empty'} />
<div
onClick={this.handleClick}
className={`box ${this.state.expanded ? 'expanded' : ''}`}
style={this.state.style}
/>
</div>
)
}
}
and styles.css
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
.container {
height: 200vh;
}
.empty {
height: 100px;
width: 100px;
border: 1px solid gray;
}
.box {
height: 100px;
width: 100px;
border: 3px solid red;
transition: all 0.5s;
}
.expanded {
position: fixed;
}

Categories

Resources