React native stylesheet multiple values in css statement - javascript

I think this question is best described by an example:
let's say I want to apply margin to an element like this:
const myView = () => <View style={styles.viewStyle}></View>
const styles = StyleSheet.create({
viewStyle: {
margin: "0 0 5 10",
},
})
Is it possible to do this without multiple margin statements?
Thanks for reading.

I don't think you can unless you write a function to do something like that.
Like this:
const CommonStyle = {
margin: (t, r, b, l) => {
marginTop: t,
marginRight: r,
marginBottom: b,
marginLeft: l,
}
}
Then in your style:
const styles = StyleSheet.create({
viewStyle: {
...CommonStyle.margin(0, 0, 5, 10),
},
})
But, in the most common case, we atmost only change margin for 2 directions. And there are serveral options to quickly styling your component when you get used to styling.
Example:
"5 10 5 10" is equal to
{
marginVertical: 5,
marginHorizontal: 10,
}
"0 0 0 5" is equal to
{
margin: 0,
marginLeft: 5,
}

Related

How to conditionally overwrite a style with a different style in React Native?

I have a PrimaryButton element that has 3 variants - primary, secondary and tertiary. As you can see in the style of the Pressable component, I set the default style based on the variant like this styles[variant] . Now I also want to make the backgroundColor of that Pressable component to turn red while it is being pressed if the button variant is tertiary. I already have access to isPressed boolean that tells me if the Pressable is pressed, however, I couldn't figure out how to change the backgroundColor to red only if the variant is tertiary.
const PrimaryButton = ({ title, variant = 'primary', wide = false, style, ...rest }) => {
const width = wide ? '100%' : undefined;
const textColor = variant === 'primary' ? colors.white : colors.primary600;
return (
<Pressable
style={({ pressed: isPressed }) => [
styles.button,
styles[variant],
{
width,
elevation: isPressed ? 5 : 0,
},
style,
]}
{...rest}
>
</Pressable>
);
};
const styles = StyleSheet.create({
button: {
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 100,
borderWidth: 1.5,
justifyContent: 'center',
alignItems: 'center',
alignSelf: 'center',
},
primary: {
backgroundColor: colors.primary600,
borderColor: colors.primary600,
},
secondary: {
backgroundColor: colors.white,
borderColor: colors.primary600,
},
tertiary: {
backgroundColor: 'transparent',
borderColor: 'transparent',
},
text: {
textAlign: 'center',
},
});
See if the following helps you. If not please do tell me what went wrong.
style = {({ pressed: isPressed }) => [
styles.button,
styles[variant],
{
width,
elevation: isPressed ? 5 : 0,
...(variant === 'tertiary') ? { backgroundColor: 'red' } : {}
},
style,
]}
check this package, very useful for this stuff.
Styles directly on field level isn't recommended anymore.
https://www.npmjs.com/package/isomorphic-style-loader
Good luck
In order to overwrite a style of a component, in this case, to change the backgroundColor to red only if the variant is tertiary, you can use the ternary operator.
It may be useful to access the defined styles to retrieve the background colors of the other buttons. To do that, you can use StyleSheet.flatten
so that you do not override the previous color style applied.
style = {
({
pressed: isPressed
}) => [
styles.button,
styles[variant],
{
width,
elevation: isPressed ? 5 : 0,
},
{
backgroundColor: isPressed && variant === 'tertiary' ?
'red' :
StyleSheet.flatten(styles[variant]).backgroundColor
},
style,
]
}
As seen in this example.

Limiting containers to three fixed sizes

I'm looking at the react-grid-layout which is based on Material Design's 12 column grid. Is there a way to provide pre-defined sizes for containers to stick to the following 3 sizes: 1 full width (12 cols), half grid (6 cols) or 1/3 grid (4 cols)?
Sandbox
My guess is that when you say container, you're referring to the layout items. If that is the case, use a custom onResize method. Using the sandbox code you have from your question:
export default class ShowcaseLayout extends React.Component {
constructor(props) {
...
// add the line below
this.onResize = this.onResize.bind(this);
}
...
// add the method
onResize(layout, oldLayoutItem, layoutItem, placeholder) {
// `oldLayoutItem` contains the state of the item before the resize.
// You can modify `layoutItem` to enforce constraints.
const allowableW = this.props.cols[this.state.currentBreakpoint] - oldLayoutItem.x
if (layoutItem.w <= 4) {
layoutItem.w = 4;
placeholder.w = 4;
} else if (layoutItem.w <= 6) {
layoutItem.w = allowableW < 6 ? 4 : 6;
placeholder.w = allowableW < 6 ? 4 : 6;
} else {
layoutItem.w = allowableW < 12 ? 6 : 12;
placeholder.w = allowableW < 12 ? 6 : 12;
}
}
render() {
return (
<div>
...
<ResponsiveReactGridLayout
...
{/* bind method to component */}
onResize={this.onResize}
>
{this.generateDOM()}
</ResponsiveReactGridLayout>
</div>
);
}
}
ShowcaseLayout.propTypes = {
onLayoutChange: PropTypes.func.isRequired
};
ShowcaseLayout.defaultProps = {
...
// ensure your breakpoints have a minimum of 4 columns
cols: { lg: 12, md: 10, sm: 6, xs: 4, xxs: 4 },
};
function generateLayout() {
return _.map(_.range(0, 25), function (item, i) {
var y = Math.ceil(Math.random() * 4) + 1;
return {
x: (_.random(0, 5) * 2) % 12,
y: Math.floor(i / 6) * y,
// set item's default width to 4
w: 4,
h: y,
i: i.toString(),
static: Math.random() < 0.05
};
});
}
DEMO
I don't know much about react-grid-layout.
But you can do it by a simple change in the cols property of ShowcaseLayout.defaultProps like following.
ShowcaseLayout.defaultProps = {
className: "layout",
rowHeight: 30,
onLayoutChange: function() {},
cols: { lg: 12, md: 12, sm: 6, xs: 4, xxs: 4 },
initialLayout: generateLayout()
};
UPDATED Sandbox
Yes, you can certainly do that with react-grid-layout. In the section of their documentation labeled "Usage without Browserify/Webpack", they have this code snippet:
import { Responsive as ResponsiveGridLayout } from 'react-grid-layout';
class MyResponsiveGrid extends React.Component {
render() {
// {lg: layout1, md: layout2, ...}
const layouts = getLayoutsFromSomewhere();
return (
<ResponsiveGridLayout className="layout" layouts={layouts}
breakpoints={{lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0}}
cols={{lg: 12, md: 10, sm: 6, xs: 4, xxs: 2}}>
<div key="1">1</div>
<div key="2">2</div>
<div key="3">3</div>
</ResponsiveGridLayout>
)
}
}
You can modify the breakpoints however you like, which indicate at which screen/viewport width's the screen will consider a new size (e.g. lg vs. md vs. sm, etc.).
Once you've defined your breakpoints, define how many columns you would like each container to be at different breakpoints using the cols attribute. If you only want the three sizes you mentioned—1 full width (12 cols), ½ grid (6 cols), and ⅓ grid (4 cols)—adjust the cols attribute to something like this:
cols={{lg: 12, md: 6, sm: 6, xs: 4, xxs: 4}}
Using these redefined values, elements in this grid will be full-width on lg viewports, ½ the grid on md and sm viewports, and ⅓ the grid on xs and xxs viewports.

borderRadius doesn't work on image when resizeMode is set to 'contain'

I've been trying to build an Image carousel with a bunch of randomly selected images. I wanted to maintain their aspect ratios so I set the resizeMode to 'contain'. Somehow that step leads to the loss of any set borderRadius! What could be the reason? And if that step doesn't work at all, any other ideas on how to maintain the correct aspect ratio + get the corners rounded?
Thanks a lot for your help!
here's the code:
import React, { useCallback, memo, useRef, useState } from "react";
import {
FlatList,
View,
Dimensions,
Text,
StyleSheet,
Image,
} from "react-native";
const images = [
Image1,
Image2,
Image3,
Image4,
Image5,
Image6,
Image7,
Image8,
Image9,
Image10,
Image11,
Image12,
Image13,
Image14,
Image15,
Image16,
Image17,
Image18,
Image19,
Image20,
Image21,
Image22,
Image23,
Image24,
Image25,
Image26,
Image27,
Image28,
Image29,
Image30,
Image31,
Image32,
Image33,
Image34,
Image35,
Image36,
Image37,
Image38,
Image39,
Image40,
Image41,
]
const { width: windowWidth, height: windowHeight } = Dimensions.get("window");
const randomImage = () =>
images[Math.floor(Math.random() * images.length)];
const styles = StyleSheet.create({
slide: {
height: windowHeight,
width: windowWidth,
//justifyContent: "center",
alignItems: "center",
},
slideImage: {
height: '70%',
width: '90%',
borderRadius: 20,
marginTop: 20,
},
slideTitle: {
fontSize: 24,
marginTop: 0,
},
slideSubtitle: {
fontSize: 18,
marginTop: 10,
},
pagination: {
position: "absolute",
bottom: 8,
justifyContent: "center",
flexDirection: "row",
marginBottom: 12
},
paginationDot: {
width: 8,
height: 8,
borderRadius: 4,
marginHorizontal: 2,
},
paginationDotActive: { backgroundColor: "lightblue" },
paginationDotInactive: { backgroundColor: "gray" },
carousel: {},
});
const slideList = Array.from({ length: 999 }).map((_, i) => {
return {
id: i,
image: randomImage,
title: `This is the title ${i + 1}!`,
subtitle: `This is the subtitle ${i + 1}!`,
};
});
const Slide = memo(function Slide({ data }) {
return (
<View style={styles.slide}>
<Image resizeMode = 'contain' source = {randomImage()} style={styles.slideImage}></Image>
<Text style={styles.slideTitle}>{data.title}</Text>
<Text style={styles.slideSubtitle}>{data.subtitle}</Text>
</View>
);
});
function Pagination({ index }) {
return (
<View style={styles.pagination} pointerEvents="none">
{slideList.map((_, i) => {
return (
<View
key={i}
style={[
styles.paginationDot,
index === i
? styles.paginationDotActive
: styles.paginationDotInactive,
]}
/>
);
})}
</View>
);
}
export default function Carousel() {
const [index, setIndex] = useState(0);
const indexRef = useRef(index);
indexRef.current = index;
const onScroll = useCallback((event) => {
const slideSize = event.nativeEvent.layoutMeasurement.width;
const index = event.nativeEvent.contentOffset.x / slideSize;
const roundIndex = Math.round(index);
const distance = Math.abs(roundIndex - index);
// Prevent one pixel triggering setIndex in the middle
// of the transition. With this we have to scroll a bit
// more to trigger the index change.
const isNoMansLand = 0.4 < distance;
if (roundIndex !== indexRef.current && !isNoMansLand) {
setIndex(roundIndex);
}
}, []);
const flatListOptimizationProps = {
initialNumToRender: 0,
maxToRenderPerBatch: 1,
removeClippedSubviews: true,
scrollEventThrottle: 16,
windowSize: 2,
keyExtractor: useCallback(s => String(s.id), []),
getItemLayout: useCallback(
(_, index) => ({
index,
length: windowWidth,
offset: index * windowWidth,
}),
[]
),
};
const renderItem = useCallback(function renderItem({ item }) {
return <Slide data={item} />;
}, []);
return (
<>
<FlatList
data={slideList}
style={styles.carousel}
renderItem={renderItem}
pagingEnabled
horizontal
showsHorizontalScrollIndicator={false}
bounces={false}
onScroll={onScroll}
{...flatListOptimizationProps}
/>
<Pagination index={index}></Pagination>
</>
);
}
``
Actually borderRadius works but you can't see it because of an incorrect ratio.
If your image has a 16:9 ratio, for example, 1600x900 dimensions, then you need to set width and height with the same ratio.
<Image
source={ 1600x900 }
resizeMode="contain"
style={{
width: 300,
height: 300,
borderRadius: 15,
backgroundColor: 'red'
}} />
The result will be:
Because the image has width and height 300, ie 1:1 ratio. If you modify width and height like 320 and 180, ie 16:9, then the image fills all the space and borders will be visible.
Another workaround is to wrap your image with view that hides the overflow
<View
style={{
width: 300,
height: 300,
borderRadius: 150,
overflow: 'hidden',
}}
>
<Image
source={item.image}
style={{
width: 300,
height: 300,
}}
resizeMode='contain'
/>
</View>

React Spring - useTrail delay issues

Currently been using react-spring to do our animations in our app; unfortunately, animation is not something I excel in and the design our designer gave us for our new logo is leaving me stumped on implementation. It is pretty easy with plain old JS but implementing it in react-spring has proved a challenge that I can not get past.
The end goal for the animation is to look like this:
https://codepen.io/darylginn/pen/GRqZxBZ
Currently, I am up to this stage:
import React from "react";
import { useTrail, animated } from "react-spring";
const Loader: React.FC = () => {
// Animations
const paths = [
{
id: 1,
color: "#466FB5",
d: "M90.6672 33H16L53.3336 96.4409L90.6672 33Z",
},
{
id: 2,
color: "#0093D3",
d: "M53.3347 96.4443H128.002L90.6683 33.0034L53.3347 96.4443Z",
},
{
id: 3,
color: "#53BFA2",
d: "M128.001 96.3701H53.3336L90.6672 159.811L128.001 96.3701Z",
},
{
id: 4,
color: "#93C83F",
d: "M90.6675 159.892H165.335L128.001 96.4417L90.6675 159.892Z",
},
{
id: 5,
color: "#58B647",
d: "M165.334 159.892H90.6664L128 223.333L165.334 159.892Z",
},
{
id: 6,
color: "#F2E90B",
d: "M202.667 96.4436H128L165.334 159.894L202.667 96.4436Z",
},
{
id: 7,
color: "#FBB12C",
d: "M128.001 96.4443H202.668L165.335 33.0034L128.001 96.4443Z",
},
{
id: 8,
color: "#FF5E8D",
d: "M240 33H165.333L202.666 96.4409L240 33Z",
},
];
const trail = useTrail(paths.length, {
from: {
scale: 1,
},
to: async (next: any) => {
while (1) {
await next({ scale: 1.5 });
await next({ scale: 1 });
}
},
});
return (
<div style={{ width: "200px", height: "200px" }}>
<svg
width="72"
height="72"
viewBox="0 0 256 256"
fill="none"
xmlns="http://www.w3.org/2000/svg"
style={{ overflow: "visible" }}>
{trail.map(({ scale }, index) => {
const path = paths[index];
return (
<animated.path
key={path.id}
fill={path.color}
d={path.d}
style={{
transformOrigin: "center",
transform: scale.interpolate((s: any) => `scale(${s})`),
}}
/>
);
})}
</svg>
</div>
);
};
The main issue I am at now is the scale of each triangle in the SVG needs to happen one after another, but nothing I do with the useTrail make this happen.
I did try adding a delay like this to the useTrail
delay: (key: any) => key * 200
But the delay doesn't even seem to make a difference. If someone could help make sure each triangle finish it sequences before the next one starts that would be great.
Bonus if you can also help me add the colour change as seen in the original design (see link).
I have tried posting in the react-spring community but got no replies
I would change the useTrail to one useSpring for all the triangles. If you change the x value from 0 to 8, then you can use interpolate and a range for each triangle to change. For example for the second triangle you can use range:[0,1,1.5,2,8],output:[1,1,1.5,1,1]. It means that when x is between 1 and 2 it will change the scale from 1 to 1.5 to 1 and all other places it will remain 1.
const props = useSpring({
from: {
x: 0
},
config: {
duration: 4000
},
to: async (next: any) => {
while (1) {
await next({ x: 8 });
await next({ reset: true });
}
}
});
I also added the color interpolation.
{paths.map((path, index) => {
const colors = [];
for (let i = 0; i < 8; i++) {
colors.push(paths[(i + index) % 8].color);
}
return (
<animated.path
key={path.id}
fill={path.color}
d={path.d}
style={{
transformOrigin: "center",
transform: props.x
.interpolate({
range: [0, index, index + 0.5, index + 1, 8],
output: [1, 1, 1.5, 1, 1]
})
.interpolate((x) => `scale(${x})`),
fill: props.x.interpolate({
range: [0, 1, 2, 3, 4, 5, 6, 7, 8],
output: colors
})
}}
/>
);
})}
I have an example. There is some glitch with the center triangle. But I think you get the idea.
Example: https://codesandbox.io/s/animate-triangles-sequentially-with-interpolating-and-range-oer84

How to get the object in StyleSheet.create?(React Native)

const styles = StyleSheet.create({
container : {
flex : 1,
backgroundColor : config.getColor('bg'),
},
title : {
marginLeft : 80,
marginTop : 30,
height : 35,
width : 100,
borderRadius : 17,
borderWidth : 1,
borderColor : config.getColor('theme'),
fontSize : 17,
color : config.getColor('theme')
}
});
when I console.log styles.title,I got a number.so how to convert it to a object?
You can use the flatten method.
Example:
StyleSheet.flatten(styles.title)
Jean's answer is very good. But I use this pattern to not repeat StyleSheet.flatten every time.
import { StyleSheet } from 'react-native'
const styles = StyleSheet.create({
container: {
flex: 1,
display: 'flex',
justifyContent: 'space-between',
padding: 20,
},
})
export default Object.keys(styles).reduce((newObject, key) => ({
...newObject,
[key]: StyleSheet.flatten(styles[key])
}), {})

Categories

Resources