How to Style the Sidebar? - react-admin

Just picked up web programming and am trying to change the color of the sidebar background. This works:
const useStyles = makeStyles(theme() => ({
drawer: {
backgroundColor:'#FFFFFF'
},
}));
const MySidebar = (props:any) => {
const classes = useStyles();
return (
<Sidebar className={classes.drawer} {...props} />
);
);
... but this doesn't:
const useStyles = makeStyles(theme() => ({
drawer: {
backgroundColor:'#FFFFFF'
},
}));
const MySidebar = (props:any) => {
const classes = useStyles();
return (
<Sidebar classes={classes} {...props} />
);
);
Why doesn't it work? And how do I find out what is the css class name to overwrite? I used Chrome's inspector tool and the css class name is "MuiDrawer-docked", so I can't figure out how it ended up to be 'drawer' in the end.
I am also trying to set the width of the sidebar when it is closed to 0 (essentially hiding it). If I put this outside in themes, it works:
sidebar: {
width: 300,
closedWidth: 0,
},
But once I put it in useStyle, it doesn't:
const useStyles = makeStyles(theme() => ({
sidebar: {
width: 300,
closedWidth: 0,
},
}));
const MySidebar = (props:any) => {
const classes = useStyles();
return (
<Sidebar className={classes.sidebar} {...props} />
);
);
What I want to achieve eventually is the combination of these two (background color change + sidebar width closed to 0), hence the use of 'classes' instead of the className.

In your second example it doesn't work because of this line:
<Sidebar classes={classes} {...props} />
classes should be className.
Also, in the first example you accessed the classes.drawer object, which actually contains the styling. Whereas in the second example you are just passing the classes object without actually specifying a key it should use.
Finally, in your last example:
const useStyles = makeStyles(theme() => ({
sidebar: {
width: 300,
closedWidth: 0,
},
}));
You are trying to use closedWidth as a css style. closedWidth does not exist as a css style.
To dynamically change the width of an element in React you probably need to use references to the dom node to change the styling on the fly.
You can find useful information about refs here

Related

react-native webview accessing variabels and functions directly

i have a simple website with a toggle function that toggles some data.
<body>
<h1>customerType: <span id="h1_element"></span></h1>
<script>
let customerType = "Public"
function toggle(){
customerType = (customerType === "Public") ? "Private" : "Public"
document.getElementById("h1_element").innerText = customerType;
}
toggle()
</script>
</body>
i then have a react-native app that can toggle the data and display the new data.
export default function Inject() {
const [customer, setCustomer] = React.useState('-');
const viewRef = React.useRef();
const postCustomer = () => viewRef.current.injectJavaScript('window.ReactNativeWebView.postMessage(customerType)');
const toggleCustomer = () => {
viewRef.current.injectJavaScript('toggle()');
}
return (
<SafeAreaView style={{ flex: 1, top: 50 }}>
<Text>WebView data: {customer}</Text>
<Button onPress={toggleCustomer} title="toggle webView data" />
<WebView
ref={viewRef}
source={{ uri: 'localhost' }}
onMessage={ event => setCustomer(event.nativeEvent.data) }
javaScriptEnabledAndroid={ true }
onLoadEnd={ postCustomer }
/>
</SafeAreaView>
);
}
I can access the function by simply using refName.current.injectJavaScript('funcName()'), but how could you access the function big project with many modules with their own script files and maybe even same function names?
I guess one way is to make the function global and then access it by window.funcName(), or bind it to a button element and then find the button with a queryselector, but is there a more direct way?
Create objects in window per functionality. Something like this:
window.test = { foo: 'bar', print: () => console.log('foobar') }
or maybe one top level object for the app to keep things clean
window.myApp = {
first: { foo: 'bar', print: () => console.log('foobar') },
second: { foo: 'bar', print: () => console.log('foobar') }
}

Using 2 Drawer Navigator in one project

I have 2 DrawerNavigators, one of them(PaymentHistoryDrawer) is inside the BottomTabNavigator. I bind them to two buttons - the 1st for the HamburgerMenu , the 2nd for the FilterButton inside Payment History.
export const DrawerNavigator = () => (
<Drawer.Navigator drawerContent={(props) => <Sidebar {...props} />}>
<Drawer.Screen name={SCREEN_ROOT} component={BottomTabNavigator} />
</Drawer.Navigator>
);
export const DrawerPaymentHistoryNavigator = () => (
<PaymentHistoryDrawer.Navigator
drawerContent={PaymentHistorySidebar}
drawerPosition="right"
drawerStyle={{
zIndex: 0,
width: 300
}}
drawerType="front">
<PaymentHistoryDrawer.Screen
name={SCREEN_PAYMENT_HISTORY_DRAWER}
component={HistoryScreen}
options={{ title: 'My home' }}
/>
</PaymentHistoryDrawer.Navigator>
);
I initialize them differently and bind them differently as "Drawer" for Hamburger Menu, "PaymentHistoryDrawer" for Filters. But anyway, when I click, let's say, on the Hamburger Menu button in the Bottom tab, where at the same time there is FilterButton, that calls PaymentHistoryDrawer, the PaymentHistory comes out. Why are they related to each other? how to untie?
//this code is the beginning of the above written code
type DrawerParamList = {
[SCREEN_ROOT]: undefined;
};
type PaymentHistoryDrawerParamList = {
[SCREEN_PAYMENT_HISTORY_DRAWER]: undefined;
};
export type RootScreenNavigationProp = DrawerNavigationProp<
DrawerParamList,
'SCREEN_ROOT'
>;
export type PaymentHistoryScreenNavigationProp = DrawerNavigationProp<
PaymentHistoryDrawerParamList,
'SCREEN_PAYMENT_HISTORY_DRAWER'
>;
const Drawer = createDrawerNavigator<DrawerParamList>();
const PaymentHistoryDrawer =
createDrawerNavigator<PaymentHistoryDrawerParamList>();
Binding ToggleDrawer to the FilterButton
export const FilterButton = () => {
const { toggleDrawer } =
useNavigation<PaymentHistoryScreenNavigationProp>();
return (
<FilterContainer onPress={toggleDrawer}>
<Image source={FilterIcon} />
<FilterText>Фильтры</FilterText>
</FilterContainer>
);
};
Binding ToggleDrawer to the HamburgerMenuButton
const _Menu = () => {
const { toggleDrawer } = useNavigation<RootScreenNavigationProp>();
return <Icon onPress={toggleDrawer} />;
};
export const Menu = memo(_Menu);
Additional question : ToggleDrawer is UseNavigation tool. Can it take any additional arguments? I read 2 times documentation, but didn't find anything about arguments or props. Thanks!
The answer is still secret...
I hope u're don't using drawers like this

Pass useAnimatedGestureHandler via forwardRef

I'm about to swap the old React Native Animated library with the new React Native Reanimated one to gain performance issues but I have encountered one problem I could not solve.
In all examples I found online, I saw that the GestureHandler, created with useAnimatedGestureHandler, is in the same component as the Animated.View. In reality that is sometimes not possible.
In my previous app, I just pass the GestureHandler object to the component via forwardRef but it seems React Native Reanimated is not able to do that. I don't know whether I have a syntax error or it is just a bug.
const App = () => {
const handlerRef = useAnimatedRef();
const y = useSharedValue(0);
handlerRef.current = useAnimatedGestureHandler({
onStart: (_, ctx) => {
ctx.startY = y.value;
},
onActive: ({translationX, translationY}, ctx) => {
y.value = translationY;
},
onEnd: () => {},
});
const animatedStyles = useAnimatedStyle(() => ({transform: [{translateY: withSpring(y.value)}]}));
const UsingHandlerDirect = () => (
<PanGestureHandler onGestureEvent={handlerRef.current} >
<Animated.View style={[styles.blueBox, animatedStyles]} />
</PanGestureHandler>
)
const UsingHandlerForwardRef = forwardRef(({animatedStyles}, ref) => (
<PanGestureHandler onGestureEvent={ref?.handlerRef?.current}>
<Animated.View style={[styles.redBox, animatedStyles]} />
</PanGestureHandler>
));
return (
<SafeAreaView>
<View style={styles.container}>
<UsingHandlerForwardRef ref={handlerRef} animatedStyles={animatedStyles}/>
<UsingHandlerDirect />
</View>
</SafeAreaView>
);
}
I have saved the GestureHandler in a useAnimatedRef handlerRef.current = useAnimatedGestureHandler({}) to make things more representable. Then I pass the the ref directly into the PanGestureHandler of the UsingHandlerDirect component. The result is that when I drag the blue box the box will follow the handler. So this version works.
But as soon as I pass the handlerRef to the UsingHandlerForwardRef component non of the gesture events get fired. I would expect that when I drag the red box will also follow the handler but it doesn't
Has someone an idea whether it's me or it's a bug in the library?
Cheers
I have given up on the idea to pass a ref around instead, I created a hook that connects both components with each other via context.
I created a simple hook
import { useSharedValue } from 'react-native-reanimated';
const useAppState = () => {
const sharedXValue = useSharedValue(0);
return {
sharedXValue,
};
};
export default useAppState;
that holds the shared value using useSharedValue from reanimated 2
The child component uses this value in the gestureHandler like that
const gestureHandler = useAnimatedGestureHandler({
onStart: (_, ctx) => {
ctx.startX = sharedXValue.value;
},
onActive: (event, ctx) => {
sharedXValue.value = ctx.startX + event.translationX;
},
onEnd: (_) => {
sharedXValue.value = withSpring(0);
},
});
and the Parent just consumes the hook value
const animatedStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateX: -sharedXValue.value,
},
],
};
});
I have created a workable Snack which contains the 2 components - a Child with a blue box and a Parent with a red box

React Native onLayout with React Hooks

I want to measure the size of a React Native View every time it renders, and save it to state. If element layout didn't change the effect should not run.
It's easy to do with a class based component, where onLayout can be used. But what do I do in a functional component where I use React Hooks?
I've read about useLayoutEffect. If that's the way to go, do you have an example of how to use it?
I made this custom hook called useDimensions. This is how far I've got:
const useDimensions = () => {
const ref = useRef(null);
const [dimensions, setDimensions] = useState({});
useLayoutEffect(
() => {
setDimensions(/* Get size of element here? How? */);
},
[ref.current],
);
return [ref, dimensions];
};
And I use the hook and add the ref to the view that I want to measure the dimensions of.
const [ref, dimensions] = useDimensions();
return (
<View ref={ref}>
...
</View>
);
I've tried to debug ref.current but didn't find anything useful there. I've also tried measure() inside the effect hook:
ref.current.measure((size) => {
setDimensions(size); // size is always 0
});
If you could like a more self-contained version of this here is a custom hook version for React Native:
const useComponentSize = () => {
const [size, setSize] = useState(null);
const onLayout = useCallback(event => {
const { width, height } = event.nativeEvent.layout;
setSize({ width, height });
}, []);
return [size, onLayout];
};
const Component = () => {
const [size, onLayout] = useComponentSize();
return <View onLayout={onLayout} />;
};
You had the right idea, it just needed a couple of tweaks... mainly, handing in the element ref and using elementRef (not elementRef.current) in the useEffect dependency array.
(Regarding useEffect vs useLayoutEffect, as you're only measuring rather than mutating the DOM then I believe useEffect is the way to go, but you can swap it out like-for-like if you need to)
const useDimensions = elementRef => {
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
useEffect(() => {
const el = elementRef.current;
setDimensions({ width: el.clientWidth, height: el.clientHeight });
}, [elementRef]);
return [dimensions];
};
Use it like this:
function App() {
const divRef = useRef(null);
const [dimensions] = useDimensions(divRef);
return (
<div ref={divRef} className="App">
<div>
width: {dimensions.width}, height: {dimensions.height}
</div>
</div>
);
}
Working codesandbox here
Edited to Add React Native version:
For React Native you can use useState with onLayout like this:
const App=()=>{
const [dimensions, setDimensions] = useState({width:0, height:0})
return (
<View onLayout={(event) => {
const {x, y, width, height} = event.nativeEvent.layout;
setDimensions({width:width, height:height});
}}>
<Text}>
height: {dimensions.height} width: {dimensions.width}
</Text>
</View>
);
}
As a refinement to
matto1990's answer, and to answer Kerkness's question - here's an example custom hook that supplies the x, y position as well as the layout size:
const useComponentLayout = () => {
const [layout, setLayout] = React.useState(null);
const onLayout = React.useCallback(event => {
const layout = event.nativeEvent.layout;
setLayout(layout);
}, [])
return [layout, onLayout]
}
const Component = () => {
const [{ height, width, x, y }, onLayout] = useComponentSize();
return <View onLayout={onLayout} />;
};

Programmatically update react navigation header title in functional component with hooks

question regarding react navigation setParams(). I asked on Reactiflux, but no one responded. I'm trying to set a title programmatically in a function component.
From another Stack Overflow thread, updating the static title retroactively, like this, works:
const Comp = props => { ... };
Comp.navigationOptions = ({ navigation }) => ({
title: 'Static Title'
});
But I need to access the component state from within the component, this does not work:
const Comp =({ navigation }) => {
const [title, setTitle] = useState('');
useEffect(() => {
navigation.setParams({ title });
}, [title]);
return ( ... );
}
If setParams() is the wrong way to do it, please enlighten me
EDIT: To add to this, when I console.log(navigation) I can see that it is changing navigation.state.params.title to the correct string, however it doesn't show up as the title.
You need to get the title param and apply it to the title:
Comp.navigationOptions = ({ navigation }) => ({
title: navigation.getParam('title', /* your default title */)
});
First you need to implement your navigation option as a function and this function needs navigation parameter as shown example in following lines :
const cameraScreenNavigator = {
'/auth/controlledCameraScreen': {
screen: ControlledCameraScreen,
navigationOptions: ({navigation}) => ({
title: navigation?.getParam('title', "Başvuru Adı"),
headerStyle: style.navigationHeaderBlue,
headerTitleStyle: {
fontWeight: 'bold',
color: COLORS.primaryText
},
}),
}
}
Then you can call setParams function in your component. This function acting as setState but react-nativagion suggests this for
setParams "setParams/setOptions etc. should only be called in useEffect/useLayoutEffect/componentDidMount/componentDidUpdate etc. Not during render or in constructor."
props.navigation.setParams({title: "New Title"})
resource : https://reactnavigation.org/docs/stack-navigator/