I'm creating a skills chart in a React component, where each bar starts with a short width and then it expands to a specified width after 0.5 second. The width is related to the skill level, defined in the following array:
const skills = [
{ skillName: 'JavaScript', level: 10, color: 'bca538' },
{ skillName: 'HTML', level: 9, color: 'af4336' },
{ skillName: 'CSS', level: 9, color: '2f81b7' },
]
The chart is represented in the following code:
<div className="chart__bars">
{skills.map((skill, index) => {
const { skillName, level, color } = skill
const { scale } = this.state
return (
<div
className={'chart__bars__item'}
key={skillName}
style={{
background: `#${color}`,
height: `${(100 / skills.length) * (index + 1)}%`,
width: `${scale ? level * 10 : 30}%`,
zIndex: skills.length - index,
}}
>
<h4
style={{
opacity: `${scale ? 1 : 0}`,
}}
>
{skillName}
</h4>
</div>
)
})}
</div>
After the component is mounted, it triggers a state change after 0.5 second, which should then expand each bar (logic for this is inside the style property in the code above). Here's the initial state:
state = {
scale: false,
}
And here's where I change it:
componentDidMount() {
setInterval(() => {
this.setState({ scale: true })
}, 500)
}
It works fine on the browser, but not on mobile. Using the devtools I can see the width being updated, but it won't expand. If I uncheck the tick box for the width, and then check it again, then the width expands (which should happen automatically).
The working example is on my website: https://marcelcruz.io.
Any thoughts would be much appreciated!
Thanks.
Related
I'm trying to create a "collapsible text" react component that allows a user-determined number of lines to be displayed. I'm using the line-clamp CSS property to do this, for the most part. On the JavaScript side of things, I want to selectively render a button that toggles the effect based on whether the content to be shown is greater in height than the number of lines to be shown multiplied by their line height. This is working fairly well in Firefox and Chrome. I can get the line height of the element after it's rendered, and I can multiply that by the number of lines that the user wants shown to approximate the height of the content. I can use that to set a min-height CSS property, and I can compare that value against the scroll height of the content. The problem is, I'm getting the same value for the scroll height of the content in Firefox and Chrome, but NOT in Safari.
const CollapsibleText = ({
text,
linesToShow,
markdown = false,
containerStyles,
textStyles,
buttonStyles,
handleScroll,
}) => {
const [isActive, setIsActive] = useState(false)
const contentRef = useRef(null)
const [displayButton, setDisplayButton] = useState(false)
const [contentMinimumHeight, setContentMinimumHeight] = useState(null)
const windowSize = useWindowSize()
const linesShown = windowSize.mobile
? parseInt(linesToShow[0])
: parseInt(linesToShow[1])
const handleToggleIsActive = () => {
if (isActive && handleScroll) {
handleScroll()
}
setIsActive(!isActive)
}
useEffect(() => {
const contentLineHeight = parseInt(
window
.getComputedStyle(contentRef.current, null)
.getPropertyValue('line-height')
)
const contentHeight = contentRef.current.scrollHeight
// Adding 5 here to offset rounding that seems to occur in actual pixel values of rendered output, value of 5 is arbitrary.
const linesToShowHeight = contentLineHeight * linesShown + 5
setContentMinimumHeight(linesToShowHeight)
console.log('Content height: ', contentHeight)
console.log('Lines to show height: ', linesToShowHeight)
if (contentHeight > linesToShowHeight) {
setDisplayButton(true)
}
if (contentHeight < linesToShowHeight) {
setDisplayButton(false)
}
}, [windowSize])
return (
<div sx={{ ...containerStyles }}>
<div
sx={{
display: '-webkit-box',
'-webkit-line-clamp': !isActive ? linesToShow : 'none',
lineClamp: !isActive ? linesToShow : 'none',
'-webkit-box-orient': 'vertical',
overflow: 'hidden',
outline: '2px solid red',
textOverflow: 'ellipsis',
minHeight: contentMinimumHeight,
'&:first-child': {
marginTop: '0px',
},
}}
>
{markdown ? (
<div
ref={contentRef}
sx={{
...textStyles,
'& *:first-child': {
marginTop: '0px',
// marginBottom: '0px',
},
}}
dangerouslySetInnerHTML={{
__html: text,
}}
/>
) : (
<p
ref={contentRef}
sx={{
whiteSpace: 'pre-wrap',
...textStyles,
}}
>
{text}{' '}
</p>
)}
</div>
{displayButton && (
<Button
variant="viewMore"
sx={{ marginTop: '20px', ...buttonStyles }}
onClick={handleToggleIsActive}
>
{!isActive ? 'Read more' : 'Read Less'}{' '}
<ChevronDown
styles={{ transform: !isActive ? 'none' : 'rotate(180deg)' }}
/>
</Button>
)}
</div>
)
}
export default CollapsibleText
To determine the content height, I'm using a ref and grabbing the scroll height of the element in question. I get the same value in Chrome and Firefox, but a larger value in Safari, which breaks my ability to selectively render the "toggle" button I'm trying to implement.
I'm comparing the "line height" (with my admittedly very simplistic algorithm) against the content height to determine whether or not to render the button. I get the same measurement in all three browsers, so as far as I can tell Safari is just measuring the scroll height of the content differently than in Chrome and Firefox.
I seemingly have to use a min height because the line-clamp property causes the content to collapse to zero in Safari. Setting the min height allows things to work more or less as predicted where the content is more than the user-defined lines to show prop given to the component.
Screenshots of the console logs for the SAME content in each browser:
Firefox:
Chrome:
Safari:
I am trying to show a basic tooltip when the user clicks on an event in the calendar with a few event details but the tooltip gets covered by the next row.
Currently, I'm trying to use the slotPropGetter prop to change the style of a cell to include z-index: -1 and inline-styling the tooltip with z-index: 1000 but to no avail.
Here's my current component:
export default() =>{
const eventStyleGetter = ({ color }) => {
const style = {
backgroundColor: color,
opacity: 0.8,
zindex: "6",
position:"relative",
};
return {
style: style,
};
};
const slotStyleGetter = () =>{
const style = {
position: "relative",
zindex:0,
height:"100px",
}
return{
style: style
}
}
const CalendarElement = (
<Calendar
localizer={localizer}
defaultDate={new Date()}
events={events}
style={{ height: 500 }}
eventPropGetter={(event) => eventStyleGetter(event)}
slotPropGetter={(slot) => slotStyleGetter(slot)}
onSelectSlot={(e) => handleSlotSelect(e)}
selectable
popup
components={{
event: EventPopover,
}}
/>
);
return(
{CalendarElement}
)
}
The issue isn't the cell z-index, but that of your tooltip. What are you using to render your tooltip? Under the hood, RBC has react-bootstrap#^0.32.4, which uses react-overlay#^0.8.0. If you use react-overlay to create your tooltips, you can portal them, which should automatically handle your z-index issue.
The way to correctly implement this is to use Reactstrap/Bootstrap Popover (based on Popperjs) rather than plain CSS. It worked in this case.
I am having a problem with ngStyle after updating to angular 7. In angular 5 its working fine.
Using ngStyle I am dynamically applying style with Css grid.
but dont know why 'grid-column': '1 / span 2' is not working.
here is my code
<div class="widthHeight100"
[ngStyle]="getStyleForGroup(group)">
<div *ngFor="let subGroup of group?.childs"
[ngStyle]="getStyleForSubGroup(subGroup)">
In typescript
Grid container:
getStyleForGroup(mdg: MdgSimple) {
let style: any = {
'width': '100%',
'height': '100%'
};
if (!isNullOrUndefined(mdg)) {
if (!isNullOrUndefined(mdg.childs)) {
if (mdg.childs.length > 0) {
style = {
'display': 'grid',
'grid-template-rows': this.getGridTemplateCount(mdg.childs, true),
'grid-template-columns': this.getGridTemplateCount(mdg.childs, false),
'grid-column-gap': '4px',
'grid-row-gap': '4px',
'justify-items': 'start',
'align-items': 'start',
'padding': '5px'
};
}
}
}
return style;
}
Childs:
getStyleForSubGroup(mdsg: MdsgSimple) {
let style: any = {
'width': '100%',
'height': '100%'
};
if (!isNullOrUndefined(mdsg) && !isNullOrUndefined(mdsg.layout)) {
style = {
'grid-row': this.getCssRowInfo(mdsg),
'grid-column': this.getCssColumnInfo(mdsg),
'height': this.getHeight(mdsg),
'width': this.getWidth(mdsg),
'min-width': this.getMinWidth(mdsg),
'max-width': this.getMaxWidth(mdsg),
'min-height': this.getMinHeight(mdsg),
'max-height': this.getMaxHeight(mdsg),
};
}
return style;
}
But controls are overlapping each other. I have checked using chrome css element inspector grid area showing unsafe.
But its all working fine in angular 5 version.
example 1 / span 2 also not working
here is screen shot for error.
Any suggestion please ?
I have the exact same issue. ngStyle is replacing this:
{
'grid-column': (width > 1 ? column + ' / ' + (column + width) : column + ''),
'grid-row': '' + row
}
with this:
style="grid-area: 1 / unsafe / auto / unsafe
when width > 1
HTML
<p [ngStyle]="myStyles">
Hello World!
</p>
TS
myStyles = {
'color': 'red',
'font-size': '20px',
'font-weight': 'bold'
}
I just write very basic code, Try this I hope it'll help you out. Thanks
Not long ago here I recieved answer, how increase each img as member of array. Somehow the same principle don't wokrs for z-index (increaced img should lie on top of the rest), though console displays that z-index changed. Why?
class Article extends React.Component{
constructor(props) {
super(props)
this.state = {showIncreaced: null}
this.getImgStyle = this.getImgStyle.bind(this);
this.increase = this.increase.bind(this);
}
increase (incId) {
this.setState({showIncreaced: incId})
}
getImgStyle (id) {
return {
width: '20vw',
marginRight: '0.5vw',
marginLeft: '0.5vw',
zIndex: this.state.showIncreaced === id ? '10' : '0',
transform: this.state.showIncreaced === id ? 'scale(1.5, 1.5)' : 'scale(1, 1)'
};
}
render(){
const TipStyle={
marginBottom: '10px'
}
return(
<div style={TipStyle}>
<h2 style={{marginBottom: '1px'}}>{this.props.name}</h2>
<div>
{[1,2,3].map((id) => {
return <img style={this.getImgStyle(id)} src={this.props[`img${id}`]} onMouseOver={this.increase.bind(this, id)} onMouseOut={this.increase} />
})}
</div>
</div>
);
}
}
https://jsfiddle.net/Nata_Hamster/fbs3m4jL/
add position: 'relative', to the object returned by getImgStyle because z-index works only when postion is set to something else than static (its default value). The easiest way is to use relative because the element is still part of the document flow.
https://jsfiddle.net/fbs3m4jL/7/
This is because the position of your image elements are static by default.
If you update the image position to say position:absolute;, then the zIndex values will work as expected. The catch with this is that you need to position the images with left coordinates so that they sit next to each other. Here is an updated version of getImgStyle that illustrates the concept:
getImgStyle (id) {
return {
position:'absolute', // Set absolute position
left: `${(id-1) * 100}px`, // Calculate a left coordinate for image
width: '20vw',
marginRight: '0.5vw',
marginLeft: '0.5vw',
zIndex: this.state.showIncreaced === id ? '10' : '0',
transform: this.state.showIncreaced === id ? 'scale(1.5, 1.5)' : 'scale(1, 1)'
};
}
Here is a working jsFiddle if you'd like to see it in action
I am trying to build a SPFx webpart containing a ChoiceGroup. When I set the css style to ms-sm12 the choices are aligned vertical:
Show assigned to:
o anyone
* me
o nobody
I like them to align horizontal in one row:
Show assigned to: o anyone * me o nobody
When I set the style to ms-sm6, it aligns them "mixed":
The Slider and Toggle are set to ms-sm3
Show assigned to: o anyone
* me
o nobody
With ms-sm4 it looks like:
With ms-sm3, ms-sm2, ms-sm1 it looks like (the title getting more and more squashed and all options in one column:
How can I force / encourage the options to be rendered horizontal?
Follow the steps given below :
1) Create New .scss file
ex: fabric.scss and paste this class in it.
.inlineflex .ms-ChoiceField{
display: inline-block;
}
2) In your component give refernece like:
import './fabric.scss';
3) Add component and apply class.
<ChoiceGroup
className="inlineflex"
label='Pick one icon'
options={ [
{
key: 'day',
text: 'Day'
},
{
key: 'week',
text: 'Week'
},
{
key: 'month',
text: 'Month'
}
] }
/>
Another option that doesn't require adding a CSS is to apply the style directly to the control:
set the flexContainer style to display:flex.
you will notice a space might be needed at the end of the options, I added a non-breaking space as \u00A0
<ChoiceGroup selectedKey={valueType}
styles={{ flexContainer: { display: "flex" } }} options={[
{ key: 'specific', text: 'selected\u00A0\u00A0' },
{ key: 'relative', text: 'relative' }]} />
done!
add styles property to ChoiceGroup styles={{ flexContainer: { display: "flex" } }}
add styles property to options styles: { choiceFieldWrapper: { display: 'inline-block', margin: '0 0 0 10px' }}
done!
const options: IChoiceGroupOption[] = [
{ key: 'conforme', text: 'Conforme'},
{ key: 'noConforme', text: 'No conforme', styles:{field: { marginLeft: "15px"}}}
];
<ChoiceGroup styles={{ flexContainer: { display: "flex" } }} options={options} />