Change div's "perceived window width" - css

I want to make the content of a div respond to its size, much like if
it was an iframe, but I can't use one.
My intent is to make the content adapt as if it was in a real smaller
screen so the user can preview the div content on mobile devices.
The iframe works, but it breaks react's diff algorithm, so I can't change the frame contents using the parent props anymore.
So, I want to be able to resize a div which will make it's contents responde
to it's media queries like if they are on a smaller window.
const Index = ({format}) => {
const classes = useStyles ({format})
// if I use iframe instead of div, the breakpoints works,
// but it breaks a lot of app logic
return <div className={classes.root}>
<ChildrenToPerceiveBreakpoint/>
</div>
}
const useStyles = makeStyles(theme => ({
root: props => ({
overflow: props?.format ? 'auto' : 'visible',
maxWidth: props?.format === 'mobile'
? 'min(414px, 100%)'
: props?.format === 'tablet'
? 'min(768px, 100%)'
: '100%',
maxHeight: props?.format === 'mobile'
? 'min(736px, 100%)'
: props?.format === 'tablet'
? 'min(1024px, 100%)'
: '100%',
margin: '0% auto',
marginTop: props?.format === 'mobile'
&& theme.breakpoints.down('md')
? '5%'
: '0%',
}),
}))
I'm using material-ui and jss for the styling.
Thx!

Related

Why is element height different in Safari than it is in Chrome, Firefox?

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:

StickyHeader Row is hindering the drop down list view

I have made my row header, of material UI Table component, sticky using the stickyHeader attribute
<Table stickyHeader className={classes.table}></Table>
there are two drop-downs displayed above this table, these are implemented using the ReactSelect component
const DropDown = props => (
<div className={[props.divClasses, props.error ? 'error-class' : ''].join(' ')}>
<ReactSelect
{...props}
classNamePrefix="normal-select"
disabled={props.disabled ? props.disabled : false}
multi={props.multi}
placeholder={props.placeholder ? props.placeholder : 'Select'}
closeOnSelect={props.closeOnSelect}
clearable={props.clearable}
searchable={props.searchable}
options={props.options ? props.options : []}
value={props.simpleValue ? props.options.filter(
({ value }) => value === props.value) : props.value}
isLoading={props.isLoading}
className={` ${props.className ? props.className : ''}`}
onChange={option => props.onChange(props.property, props.simpleValue ? option?.value : option)}
onBlur={props.onBlur}/>
{props.error && <FormHelperText style={{ color: '#f44336' }}>{props.error}</FormHelperText>}
</div>
);
because of being sticky, the header of the table component is now corrupting the view of the drop downs
currently, I am getting,
the expected behavior is,
kindly help me in this regard.
The reason of this overlapping is because mui table's sticky header's default z-index value is 2 and is greater than react-select menu's default z-index value 1.
So, you need to increase z-index of react-select component's menu property to some value that is greater than 2 like this:
const customStyles = {
menu: (provided, state) => ({
...provided,
zIndex: 3
})
};
<ReactSelect
value={selectedOption}
onChange={setSelectedOption}
options={options}
styles={customStyles}
/>
You can take a look at this sandbox for a live working example of this usage.

How to use #media to change width in prop styled components

I am not sure if what they called but I have a component which takes its style as an object with its props.
const PricingSection = ({
secDesc,
}) => {
return (
<Text
{...secDesc}
content={intl.formatMessage({ id: 'packages.description' })}
/>
);
};
PricingSection.propTypes = {
secDesc: PropTypes.object
};
PricingSection.defaultProps = {
secDesc: {
width: '50%',
m: 'auto',
textAlign: 'center',
pt: '20px',
color: '#6a7a8d',
lineHeight: '1.5rem',
},
}
I want to apply different witdh for mobile devices. I know how to use #media tag in css but I dont know where to write #media in this component or how achieve what I want.
Instead of passing style object, it would be better if you apply a class to the component for easy maintenance. It'll also solve your problem for width for different size devices as you can add media query for the class in its css file.
Another suggestion would be to use styled-components. They provide great support for adding media queries inside the component file.
Refer:styled-components
you can use 'react-device-detect' and pass the different width from parent component like this:
enter code here
import isMobile from 'react-device-detect'
secDesc: {
width: isMobile ? '50%' : 'your value',
m: 'auto',
textAlign: 'center',
pt: '20px',
color: '#6a7a8d',
lineHeight: '1.5rem',
}
< PricingSection
secDesc={secDesc}
/>

React Native:Positioning does not work and images positioned below viewing area

On android emulator, my React Native 0.62.2 app displays uploaded images within an accordion whose display area is warped with <View> which is styled with width and height. The image is warped with which is <Image> with cache ability. The problem is that the image is positioned way below beyond the viewing boundary and is not visible when open the accordion.
Here is the render code in accordion which is provided with both width and height as canvas:
return (
<>
<TouchableWithoutFeedback onPress={() => toggleListItem()}>
<View style={styles.titleContainer}>
<Text>{title}</Text>
<Animated.View style={{ transform: [{ rotateZ: arrowAngle }] }}>
<Icon name="chevron-down-outline" size={20} />
</Animated.View>
</View>
</TouchableWithoutFeedback>
<Animated.View style={[styles.bodyBackground, { height: bodyHeight }]}>
<View
style={(absPosition ? styles.bodyContainerAbs : styles.bodyContainerCenter), (screenSize ? {width:screenSize.width, height:screenSize.height, flex:1} : null)}
onLayout={event => {
if (screenSize) {
setBodySectionHeight(screenSize.height);
} else {
setBodySectionHeight(event.nativeEvent.layout.height);
};
console.log("layout : ", event.nativeEvent.layout);
}
}> //<<<===here is <View> warping the image area. Both width and height are set.
{children} //<<<===images uploaded displayed here
</View>
</Animated.View>
</>
);
const styles = StyleSheet.create({
bodyBackground: {
backgroundColor: '#EFEFEF',
overflow: 'hidden',
},
bodyContainerCenter: { //<<<==here is the style used for display images
padding: 1,
paddingLeft: 0,
justifyContent: 'center',
alignItems: 'center',
},
bodyContainerAbs: {
padding: 5,
paddingLeft: 10,
position: 'absolute',
bottom: 0,
},
});
Here is the image render code:
const displayImg = (img_source, width, ht, index) => {
if (img_source && img_source!==[] && img_source!=={}) {
return (
<ImageZoom cropWidth={screen_width}
cropHeight={screen_ht}
imageWidth={width}
imageHeight={ht}
enableSwipeDown={true}
style={{padding:1}}
>
<FastImage
source={{uri:img_source}}
resizeMode={FastImage.resizeMode.contain}
style={{ //<<<===image width and height are set
width:width,
height:ht,
position:"absolute" //<<<===does not work
}}
/>
</ImageZoom>
);
} else {
return null;
};
};
When there is one image, it is display with full screen width. For 2 images, then they are displayed side by side occupying half of the screen width:
import { Col, Row, Grid } from 'react-native-easy-grid';
const DisplayImages = ({pics}) => {
if (!pics || pics===[] || pics==={}) return null;
var len = pics.length,
if (len > 0) setImgAccordOpen(true);
switch(len) {
case 0:
return;
case 1: //<<<=== one image is uploaded
return (
<React.Fragment>
{displayImg(pics[0].path, screen_width*full, screen_width*full, 0)}
</React.Fragment>
);
case 2: //<<<=== case of 2 images uploaded
return (
<Grid style={{position:"absolute", paddingTop:0,paddingLeft:0}}>
<Row style={{paddingTop:0}}>
<Col style={{position:"absolute", top:0,left:0, paddingVertical:0}} > //<<==positioned at [0,0] on up left corner.
{displayImg(pics[0].path, screen_width*half, screen_width*half, 0)}
</Col>
<Col style={{position:"absolute", top:0,left:Math.ceil((screen_width-20)/2), paddingTop:0}}> //<<==positioned at [0, middle of screen]
{displayImg(pics[1].path, screen_width*half, screen_width*half, 1)}
</Col>
</Row>
</Grid>
);
.....
Here is the screen shot when one image is uploaded. The image is not positioned at absolute
Here is the screen shot when 2 images are uploaded. The first image is not within the viewing area and not visible at all.
How to position images right within viewing area?
The problem is the <ImageZoom> is not compatible with <FastImage> and only show a portion of image or positioning images way below the viewing screen. After removing <ImageZoom>, then the position of the images is back to normal as style dictated. Another module #dudigital/react-native-zoomable-view works fine with <FastImage>.

Alignment Issue with tabBarLabel and Icon in react-navigation-material-bottom-tabs

I am using createMaterialBottomTabNavigator from React Navigation for one of my project. If i use tabBarIcon and tabBarLabel together icon is overlapping with text. Need help to fix the issue.
Here's what i tried -
const MainNavigator = createMaterialBottomTabNavigator({
Offers: {
screen: OffersNavigator,
navigationOptions:{
tabBarIcon: (
{focused}) => {
return <IconButton icon="tag" style={{marginBottom: 5}} color={focused ? '#2E98FF' : '#000'} size={20}/>
},
tabBarLabel: 'Offers',
},
},
Search: SearchNavigator,
Settings: SettingsNavigator,
});
Margin Icon like this. It will work perfectly.
<IconButton icon="tag" style={{marginTop: -3}} color={focused ? '#2E98FF' : '#000'} size={20}/>

Resources