Victory Native BarGraph Event - react-native

I am trying to implement a bar graph that will allow the selection of bars on a bar graph. The trouble I am running into is properly setting the events. I have the following code:
<VictoryChart
containerComponent={
<Svg>
<VictoryVoronoiContainer voronoiDimension="x" />
</Svg>
}
>
<VictoryAxis fixLabelOverlap name="dateAxis" />
<VictoryAxis dependentAxis name="dailySpendAxis" />
<VictoryBar
data={dataWithCreditBarsRemoved}
events={[
{
target: 'data',
eventHandlers: {
onPressIn: (event, data) => {
return [
{
target: 'data',
eventKey: 'all',
mutation: props => {
const fill = props.style?.fill;
return fill === 'green'
? null
: { style: { fill: 'green' } };
},
},
{
target: 'data',
mutation: () => {
console.log(data.datum);
setSelectedBar(data?.datum);
return {
style: { fill: 'red' },
};
},
},
];
},
},
},
]}
name="dailySpendBars"
/>
</VictoryChart>
Doing it like this makes it difficult to press on bars that have little data.
I'd like to implement it in a way where when the graph is pressed, it selects the closest bar and sliding a finger in either direction will select the bar closest. The best example I could find was in a Nike running app. It puts in a line with a tooltip that holds data that changes when the finger slides around.
How do I implement the sliding in Victory Native? Any direction would be much appreciated!

Related

Nativebase how to customize Button color in theme without using colorScheme?

I really struggle with this simple thing.
By default the button color is primary.600 which is not the real primary color. So I just want it to be primary.400 (the real primary).
As there is nearly no documentation on this and to my regret no IDE auto-completion with typescript, I am asking your lights on how to solve the problem
Here is what I have tried so far:
export const theme = extendTheme({
components: {
Button: {
// I tried this after checking directly in ts file for extendTheme implementation
baseStyle: () => ({
bg: 'red.500',
backgroundColor: 'red.500',
})
// as well as
baseStyle: {
bg: 'red.500',
backgroundColor: 'red.500',
}
// also tried with hex colors with no success
},
// Also tried the code in this example: https://github.com/GeekyAnts/NativeBase/blob/v3.1.0/src/theme/components/button.ts#L72 with no success
variants: {
solid(props: Dict) {
const { colorScheme: c } = props;
let bg = `${c}.400`
bg = mode(bg, `${c}.400`)(props);
if (props.isDisabled) {
bg = mode(`muted.300`, `muted.500`)(props);
}
const styleObject = {
_web: {
outlineWidth: 0,
},
bg,
_hover: {
bg: mode(`${c}.600`, `${c}.500`)(props),
},
_pressed: {
bg: mode(`${c}.700`, `${c}.600`)(props),
},
};
return styleObject;
}
}
});
Also tried the code in this example: https://github.com/GeekyAnts/NativeBase/blob/v3.1.0/src/theme/components/button.ts#L72 with no success
As far as I can tell, there is no way to define the specific color, only a colorScheme. This may be because the button as designed in NativeBase relies on a set of colors, not a single one (normal, disabled, hovering, pressed, etc.).
That being said I can see two approaches to achieving your goal.
define a custom color scene and connect it to the button in the theme configuration:
const theme = extendTheme({
colors: {
custom: {
50: '#ecfeff',
100: '#67e8f9',
200: '#22d3ee',
300: '#06b6d4',
400: '#0891b2',
500: '#0e7490',
600: '#155e75',
700: '#164e63',
800: '#174e63',
900: '#184e63',
},
},
components: {
Button: {
defaultProps: {
colorScheme: 'custom',
},
},
},
});
Simply override the primary theme (this could of course have side effects if you use the primary color in other components)
const theme = extendTheme({
colors: {
primary: {
50: '#ecfeff',
100: '#67e8f9',
200: '#22d3ee',
300: '#06b6d4',
400: '#0891b2',
500: '#0e7490',
600: '#155e75',
700: '#164e63',
800: '#174e63',
900: '#184e63',
},
},
});
baseStyle: {
rounded: 'md',
bg: '#ffcc00',
backgroundColor: '#fc0',
},
Using bg and backgroundColor seems to work

React Native: How to implement 2 columns of swipping cards?

I am trying to implement a scrollable list of cards in 2 columns. The cards should be swipe-able left or right out of the screen to be removed.
Basically, it should be like how the Chrome app is showing the list of tabs currently, which can be swiped away to be closed. See example image here.
I am able to implement the list of cards in 2 columns using FlatList. However, I have trouble making the cards swipe-able. I tried react-tinder-card but it cannot restrict swiping up and down and hence the list becomes not scrollable. react-native-deck-swiper also does not work well with list.
Any help is appreciated. Thank you!
I am going to implement a component that satisfies the following requirements:
Create a two column FlatList whose items are your cards.
Implement a gesture handling that recognizes swipeLeft and swipeRight actions which will remove the card that was swiped.
The swipe actions should be animated, meaning we have some kind of drag of the screen behavior.
I will use the basic react-native FlatList with numColumns={2} and react-native-swipe-list-view to handle swipeLeftand swipeRight actions as well as the desired animations.
I will implement a fire and forget action, thus after removing an item, it is gone forever. We will implement a restore mechanism later if we want to be able to restore removed items.
My initial implementation works as follows:
Create a FlatList with numColumns={2} and some additional dummy styling to add some margins.
Create state using useState which holds an array of objects that represent our cards.
Implement a function that removes an item from the state provided an id.
Wrap the item to be rendered in a SwipeRow.
Pass the removeItem function to the swipeGestureEnded prop.
import React, { useState } from "react"
import { FlatList, SafeAreaView, Text, View } from "react-native"
import { SwipeRow } from "react-native-swipe-list-view"
const data = [
{
id: "0",
title: "Title 1",
},
{
id: "1",
title: "Title 2",
},
{
id: "2",
title: "Title 3",
},
{
id: "3",
title: "Title 4",
},
{
id: "4",
title: "Title 5",
},
{
id: "5",
title: "Title 6",
},
{
id: "6",
title: "Title 7",
},
{
id: "7",
title: "Title 8",
},
]
export function Test() {
const [cards, setCards] = useState(data)
const [removed, setRemoved] = useState([])
function removeItem(id) {
let previous = [...cards]
let itemToRemove = previous.find((x) => x.id === id)
setCards(previous.filter((c) => c.id !== id))
setRemoved([...removed, itemToRemove])
}
return (
<SafeAreaView style={{ margin: 20 }}>
<FlatList
data={cards}
numColumns={2}
keyExtractor={(item) => item.id}
renderItem={({ index, item }) => (
<SwipeRow swipeGestureEnded={() => removeItem(item.id)}>
<View />
<View style={{ margin: 20, borderWidth: 1, padding: 20 }}>
<Text>{item.title}</Text>
</View>
</SwipeRow>
)}
/>
</SafeAreaView>
)
}
Notice that we need some kind of property in our objects in order to determine which one we want to remove. I have used a basic id property here, which is quite common using FlatList. If you are retrieving your data from an API which does not provide the same id, then we could just do some preprocessing (normalization) first and add the id prop ourselves.
The initial view looks as follows.
Swiping, let's say the item with 'Title 6' to the right or to the left removes it.
It might be desired to implement the following feature as well.
If the item is in the first column, then only swiping to the left will remove the item.
If the item is in the second column, then only swiping to the right will remove the item.
This is easily implemented using the index param which is passed to the renderItem function and the vx prop of the gestureState passed to the swipeGestureEnded function.
Here is fully working implementation.
import React, { useState } from "react"
import { FlatList, SafeAreaView, Text, View } from "react-native"
import { SwipeRow } from "react-native-swipe-list-view"
const data = [
{
id: "0",
title: "Title 1",
},
{
id: "1",
title: "Title 2",
},
{
id: "2",
title: "Title 3",
},
{
id: "3",
title: "Title 4",
},
{
id: "4",
title: "Title 5",
},
{
id: "5",
title: "Title 6",
},
{
id: "6",
title: "Title 7",
},
{
id: "7",
title: "Title 8",
},
]
export function Test() {
const [cards, setCards] = useState(data)
const [removed, setRemoved] = useState([])
function removeItem(id) {
let previous = [...cards]
let itemToRemove = previous.find((x) => x.id === id)
setCards(previous.filter((c) => c.id !== id))
setRemoved([...removed, itemToRemove])
}
return (
<SafeAreaView style={{ margin: 20 }}>
<FlatList
data={cards}
numColumns={2}
keyExtractor={(item) => item.id}
renderItem={({ index, item }) => (
<SwipeRow
swipeGestureEnded={(key, event) => {
if (event.gestureState.vx < 0) {
if (index % 2 === 0) {
removeItem(item.id)
}
} else if (event.gestureState.vx >= 0) {
if (index % 2 === 1) {
removeItem(item.id)
}
}
}}
disableLeftSwipe={index % 2 === 1}
disableRightSwipe={index % 2 === 0}>
<View />
<View style={{ margin: 20, borderWidth: 1, padding: 20 }}>
<Text>{item.title}</Text>
</View>
</SwipeRow>
)}
/>
</SafeAreaView>
)
}
Since the index is zero based in a FlatList, an item is in the second column if and only if index % 2 === 1 (e.g. an item with index 3 is always in the second column and thus not divisible by 2), on the other hand an item is in the first column if and only if index % 2 === 0 that is index is divisible by 2.
There are several callback function props in the SwipeRowComponent that should be fired in certain situations. However, most of them did not work in my setup and I still have no clue why. I got it to work by using the event.gestureState.vx property which is negative if we swipe to the left and positive (including zero) if we swipe to the right.
It might be desired to implement an undo button as it is quite common in this kind of functionalities. This can be done as follows:
Implement a second state which represents a Queue that holds lastly removed items. The undo button then just pops the lastly removed item.
Here is a fully working implementation with a dummy undo button that achieves exactly that.
import React, { useState } from "react"
import { Button, FlatList, SafeAreaView, Text, View } from "react-native"
import { SwipeRow } from "react-native-swipe-list-view"
const data = [
{
id: "0",
title: "Title 1",
},
{
id: "1",
title: "Title 2",
},
{
id: "2",
title: "Title 3",
},
{
id: "3",
title: "Title 4",
},
{
id: "4",
title: "Title 5",
},
{
id: "5",
title: "Title 6",
},
{
id: "6",
title: "Title 7",
},
{
id: "7",
title: "Title 8",
},
]
export function Test() {
const [cards, setCards] = useState(data)
const [removed, setRemoved] = useState([])
function removeItem(id) {
let previous = [...cards]
let itemToRemove = previous.find((x) => x.id === id)
setCards(previous.filter((c) => c.id !== id))
setRemoved([...removed, itemToRemove])
}
function undoRemove() {
if (removed && removed.length > 0) {
let itemToUndo = removed[removed.length - 1]
setCards([...cards, itemToUndo])
setRemoved(removed.filter((c) => c.id !== itemToUndo.id))
}
}
return (
<SafeAreaView style={{ margin: 20 }}>
<FlatList
data={cards}
numColumns={2}
keyExtractor={(item) => item.id}
renderItem={({ index, item }) => (
<SwipeRow
swipeGestureEnded={(key, event) => {
if (event.gestureState.vx < 0) {
if (index % 2 === 0) {
removeItem(item.id)
}
} else if (event.gestureState.vx >= 0) {
if (index % 2 === 1) {
removeItem(item.id)
}
}
}}
disableLeftSwipe={index % 2 === 1}
disableRightSwipe={index % 2 === 0}>
<View />
<View style={{ margin: 20, borderWidth: 1, padding: 20 }}>
<Text>{item.title}</Text>
</View>
</SwipeRow>
)}
/>
<Button onPress={undoRemove} title="Undo" />
</SafeAreaView>
)
}
Notice that my undo button just appends the removed item to the end of the list. If you want to keep the initial index, then you need to save the old index and push the item to the correct position.
Here is workin snack of my last implementation.

Update table when user clicked on pagination buttons

I have a vuetify table. Right now, I call API for all records and load all records into my table. I might not see the performance differences now because it's less than 1000. When I have 10,000,000 this might not work anymore. I need to only query for the first 10 on page loads, and my BE API already supports that. Then, I will make calls to API to get the next/previous 10 only when the user clicked on the pagination buttons.
data() {
return {
headers: [
{
text: 'Name',
align: 'start',
sortable: false,
value: 'name',
width: '20%'
},
{ text: 'Link Count', value: 'count', width: '10%' },
{ text: 'URL', value: 'details', width: '50%' },
{ text: 'Actions', value: 'id', width: '20%' }
],
items: []
}
},
and I set this.items to the response from API below :
axios.defaults.headers['Content-Type'] = 'application/json'
axios
.post(window.MTS_URL, vc_url_group)
.then((response) => {
this.items = response.data.groups
})
Can someone please get me started?
It's highly depends on your backend implementation, but there are some common points:
You should define v-data-table this way:
<v-data-table
:headers="headers"
:items="desserts"
:items-per-page="10"
:server-items-length="totalDesserts"
:options.sync="options"
></v-data-table>
So you need to operate with three variables:
desserts as a [1..10] rows on your current page,
totalDesserts as a total amount of rows (10,000,000 in your case)
options as a synchronize point of your pagination
These variables comes from an example of official docs.
After that, in order to track user click on pagination buttons, you need to define a deep watcher (it should be deep in order to react to changes in nested props):
watch: {
options: {
handler() {
this.getDessertsFromApi();
},
deep: true
},
},
And then - your API call method:
methods: {
async getDessertsFromApi() {
this.loading = true;
const { sortBy, sortDesc, page, itemsPerPage } = this.options;
// Your axios call should be placed instead
await this.simulateApiCall({
size: itemsPerPage,
page: page - 1,
})
.then((response) => {
this.desserts = response.data.content;
this.totalDesserts = response.data.totalElements;
})
.finally(() => {
this.loading = false;
});
},
}
Test this at CodePen with simulated API calls.

Overriding the theme CSS of Box

I'm trying to develop the front end of an application and I wanted to theme some boxes.
I pulled an example from here:
// theme.js
import { extendTheme } from "#chakra-ui/react"
const theme = extendTheme({
components: {
Button: {
// 1. We can update the base styles
baseStyle: {
fontWeight: "bold", // Normally, it is "semibold"
},
// 2. We can add a new button size or extend existing
sizes: {
xl: {
h: "56px",
fontSize: "lg",
px: "32px",
},
},
// 3. We can add a new visual variant
variants: {
"with-shadow": {
bg: "red.400",
boxShadow: "0 0 2px 2px #efdfde",
},
// 4. We can override existing variants
solid: (props) => ({
bg: props.colorMode === "dark" ? "red.300" : "red.500",
}),
},
},
},
})
export default theme
and adapted it to this test-code
const theme = extendTheme({
components: {
Box: {
// 1. We can update the base styles
baseStyle: {
},
// 2. We can add a new button size or extend existing
sizes: {
},
// 3. We can add a new visual variant
variants: {
Mini: {
fontWeight: "bold", // Normally, it is "semibold"
textDecoration: "underline",
boxShadow: "0px 30px 5px -20px rgba(0,0,0,0.75)"
}
},
},
},
})
The idea is that I can say <Box variant="Mini">, but that's not actually working. None of the styles have any affect on the box or it's content.
If I change Box: to Text: and add a Text element <Text variant="Mini">test</Text>, the styles are applied. It's also not working for Grid but does for Button
What am I doing wrong?
I do know that Box has a boxShadow property, but it's very limited.
I still haven't figured out how to modify Box with CSS, partly because the answer I'm about to give doesn't list a file for Box.
There's a note on the page
If you're curious as to what component styles you can override, please reference the default component style files.
When I wanted to modify Drawer, I found the same issue. I went to that link and opened drawer.ts. In there I found this function
const baseStyle: PartsStyleFunction<typeof parts> = (props) => ({
overlay: baseStyleOverlay,
dialogContainer: baseStyleDialogContainer,
dialog: baseStyleDialog(props),
header: baseStyleHeader,
closeButton: baseStyleCloseButton,
body: baseStyleBody,
footer: baseStyleFooter,
})
Which gives me some useful keys. I then found of I modified my theme styles like so, I could modify facets as expected.
import { extendTheme } from '#chakra-ui/react';
let theme = extendTheme({
components: {
Drawer: {
baseStyle: {
dialog: {
bg: "blue",
},
closeButton: {
bg: "hotpink",
},
header: {
bg: "red",
},
},
},
}
});
read the documentation in layer styles, i had the same problem with Grid component and also tried the same as you, thanks for the answer
https://chakra-ui.com/docs/theming/component-style#usestyleconfig-api
https://chakra-ui.com/docs/features/text-and-layer-styles

How do I disable interaction on a Highcharts chart?

In my react native application I have a Highcharts chart displaying some bar chart info.
I want to display the legend, so people know what the chart is showing, but I want to disable the functionality that hides bars when the legend is touched.
Additionally the bars themselves fade in and out when you press them... I have disabled the 'tooltip' but the fading still happens, how can I stop this?
If I could render to a static image that would be ideal!
just for info the code is
let Highcharts = "Highcharts";
const conf = {
chart: {
type: "column",
animation: false,
marginRight: 10,
dateFormat: "dd/mm/YYYY"
},
title: {
text: "Spread Events"
},
xAxis: {
type: "datetime",
tickPixelInterval: 50
},
yAxis: {
title: {
text: "Spread"
},
plotLines: [
{
value: 0,
width: 1,
color: "#808080"
}
]
},
tooltip: { enabled: false },
legend: {
enabled: true
},
exporting: {
enabled: false
},
plotOptions: {
column: {
pointPadding: 0.2,
borderWidth: 0
}
},
series: FieldStore.graphData.slice()
};
To disable hiding series on legend click, return false from legendItemClick event function.
To disable a tooltip and the fading effect on series hover set enableMouseTracking to false. If you want to also turn off the fading effect on legend hover, change opacity in inactive state:
plotOptions: {
series: {
enableMouseTracking: false,
states: {
inactive: {
opacity: 1
}
},
events: {
legendItemClick: function() {
return false;
}
}
}
}
Live demo: http://jsfiddle.net/BlackLabel/gjkprbto/
API Reference:
https://api.highcharts.com/highcharts/series.bar.enableMouseTracking
https://api.highcharts.com/highcharts/series.bar.events.legendItemClick
https://api.highcharts.com/highcharts/series.bar.states.inactive.opacity