react-native-sortable-list icon indicating copy to clipboard operation
react-native-sortable-list copied to clipboard

programmatically sortable list

Open adirzoari opened this issue 6 years ago • 23 comments

Im trying to "simulate" tap of user but without tap on item.. for example when I make timeout and change order so it will make the effect of the sort. I tried this way but it's not make any animation, it's only render the list

import React, { Component } from 'react';
import {
  Animated,
  Easing,
  StyleSheet,
  Text,
  Image,
  View,
  Dimensions,
  Platform,
} from 'react-native';
import SortableList from 'react-native-sortable-list';

const window = Dimensions.get('window');



export default class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      data: {
        0: {
          image: 'https://placekitten.com/200/240',
          text: 'Chloe',
        },
        1: {
          image: 'https://placekitten.com/200/201',
          text: 'Jasper',
        },
        2: {
          image: 'https://placekitten.com/200/202',
          text: 'Pepper',
        },
        3: {
          image: 'https://placekitten.com/200/203',
          text: 'Oscar',
        },
        4: {
          image: 'https://placekitten.com/200/204',
          text: 'Dusty',
        },
        5: {
          image: 'https://placekitten.com/200/205',
          text: 'Spooky',
        },
        6: {
          image: 'https://placekitten.com/200/210',
          text: 'Kiki',
        },
        7: {
          image: 'https://placekitten.com/200/215',
          text: 'Smokey',
        },
        8: {
          image: 'https://placekitten.com/200/220',
          text: 'Gizmo',
        },
        9: {
          image: 'https://placekitten.com/220/239',
          text: 'Kitty',
        },
      }
    }
  }

  componentDidMount() {
    setTimeout(() => {
      let data2 = {
          2: {
            image: 'https://placekitten.com/200/240',
            text: 'Chloe',
          },
          1: {
            image: 'https://placekitten.com/200/201',
            text: 'Jasper',
          },
          0: {
            image: 'https://placekitten.com/200/202',
            text: 'Pepper',
          },
          3: {
            image: 'https://placekitten.com/200/203',
            text: 'Oscar',
          },
          4: {
            image: 'https://placekitten.com/200/204',
            text: 'Dusty',
          },
          5: {
            image: 'https://placekitten.com/200/205',
            text: 'Spooky',
          },
          6: {
            image: 'https://placekitten.com/200/210',
            text: 'Kiki',
          },
          7: {
            image: 'https://placekitten.com/200/215',
            text: 'Smokey',
          },
          8: {
            image: 'https://placekitten.com/200/220',
            text: 'Gizmo',
          },
          9: {
            image: 'https://placekitten.com/220/239',
            text: 'Kitty',
          },
        }
        this.setState({data:data2})

    }, 3000);
}
render() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>React Native Sortable List</Text>
      <SortableList
        style={styles.list}
        contentContainerStyle={styles.contentContainer}
        data={this.state.data}
        renderRow={this._renderRow} />
    </View>
  );
}

_renderRow = ({ data, active }) => {
  return <Row data={data} active={active} />
}
}

class Row extends Component {

  constructor(props) {
    super(props);

    this._active = new Animated.Value(0);

    this._style = {
      ...Platform.select({
        ios: {
          transform: [{
            scale: this._active.interpolate({
              inputRange: [0, 1],
              outputRange: [1, 1.1],
            }),
          }],
          shadowRadius: this._active.interpolate({
            inputRange: [0, 1],
            outputRange: [2, 10],
          }),
        },

        android: {
          transform: [{
            scale: this._active.interpolate({
              inputRange: [0, 1],
              outputRange: [1, 1.07],
            }),
          }],
          elevation: this._active.interpolate({
            inputRange: [0, 1],
            outputRange: [2, 6],
          }),
        },
      })
    };
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.active !== nextProps.active) {
      Animated.timing(this._active, {
        duration: 300,
        easing: Easing.bounce,
        toValue: Number(nextProps.active),
      }).start();
    }
  }

  render() {
    const { data, active } = this.props;

    return (
      <Animated.View style={[
        styles.row,
        this._style,
      ]}>
        <Image source={{ uri: data.image }} style={styles.image} />
        <Text style={styles.text}>{data.text}</Text>
      </Animated.View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#eee',

    ...Platform.select({
      ios: {
        paddingTop: 20,
      },
    }),
  },

  title: {
    fontSize: 20,
    paddingVertical: 20,
    color: '#999999',
  },

  list: {
    flex: 1,
    width: window.width,
  },

  contentContainer: {
    ...Platform.select({
      ios: {
        paddingVertical: 30,
      },

      android: {
        paddingVertical: 0,
      }
    })
  },

  row: {
    flexDirection: 'column',
    alignItems: 'center',
    backgroundColor: '#fff',
    padding: 16,
    width: 110,
    height: 150,
    marginHorizontal: 10,
    borderRadius: 4,
    margin: 10,


    ...Platform.select({
      ios: {
        shadowColor: 'rgba(0,0,0,0.2)',
        shadowOpacity: 1,
        shadowOffset: { height: 2, width: 2 },
        shadowRadius: 2,
      },

      android: {
        elevation: 0,
        marginHorizontal: 30,
      },
    })
  },

  image: {
    width: 50,
    height: 50,
    marginBottom: 15,
    borderRadius: 25,
  },

  text: {
    fontSize: 18,
    color: '#222222',
  },
});

adirzoari avatar Feb 01 '19 09:02 adirzoari

@adirzoari

As the docs states, you can pass order array as follows:

componentDidMount() {
    setTimeout(() => {
        const order = [2, 1, 9, 7, 3, 0, 8, 4, 5, 6];
        this.setState({order})
    }, 3000);
}

render() {
  return (
    <View style={styles.container}>
      <SortableList
        style={styles.list}
        contentContainerStyle={styles.contentContainer}
        data={this.state.data}
        order={this.state.order}
        renderRow={this._renderRow} />
    </View>
  );
}

bureyburey avatar Feb 02 '19 07:02 bureyburey

@bureyburey thanks, that's exactly what I'm looking for!

adirzoari avatar Feb 02 '19 16:02 adirzoari

@adirzoari 🤘

bureyburey avatar Feb 02 '19 16:02 bureyburey

@bureyburey Im trying to make something like that. I changed sortingEnabled to false, each row is button, when user press on button then it takes the row to change it to first in the list.. my problem is how to pass function props for <SortableList .../> to the child(each row) when the user tab on button than it change the new order.. part of my example

     <SortableList
            sortingEnabled={false}
            style={style.list}
            contentContainerStyle={style.contentContainer}
            data={this.state.data}
            renderRow={this._renderRow}
            order={this.state.order}
            /* pass any function for example
            onPressToChangeOrder = {()=>{ make functionality to make new order}}
            */

          />

each row

`class Row extends Component {
  constructor(props) {
    super(props)
    this.state = {
      checked_id: -1,
    }

    this._active = new Animated.Value(0)

    this._style = {
      ...Platform.select({
        ios: {
          transform: [
            {
              scale: this._active.interpolate({
                inputRange: [0, 1],
                outputRange: [1, 1.1],
              }),
            },
          ],
          shadowRadius: this._active.interpolate({
            inputRange: [0, 1],
            outputRange: [2, 10],
          }),
        },

        android: {
          transform: [
            {
              scale: this._active.interpolate({
                inputRange: [0, 1],
                outputRange: [1, 1.07],
              }),
            },
          ],
          elevation: this._active.interpolate({
            inputRange: [0, 1],
            outputRange: [2, 6],
          }),
        },
      }),
    }
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.active !== nextProps.active) {
      Animated.timing(this._active, {
        duration: 300,
        easing: Easing.bounce,
        toValue: Number(nextProps.active),
      }).start()
    }
  }

  render() {
    const { data, active } = this.props
    console.log('data', data)
    if (!data) return null
    return (
      <Animated.View style={[style.row, this._style]} active={active}>
        <Button
          style={style.view_item}
          onPress={() => {
            this.setState({ checked_id: data.id })
            /* call to new order
            this.props.onPressToChangeOrder()
            */
            
          }}
        >
          <View>
            <CheckBox
              checked_id={this.state.checked_id}
              id={data.id}
              icon={'md-checkmark'}
              checked={data.checked}
              onPress={id => {
                console.log('pressed again')
                this.chooseItem(id)
              }}
              disabled={false}
            />
          </View>
          <View />
          <View>
            <Text style={style.text_item}>{data.text}</Text>
          </View>
        </Button>
      </Animated.View>
    )
  }
}
`

adirzoari avatar Feb 02 '19 17:02 adirzoari

@adirzoari

You tried passing onPressRow function as prop to SortableList?

From the docs:

onPressRow? (function) (key) => void Called when a row was pressed.

bureyburey avatar Feb 02 '19 17:02 bureyburey

yes,it's not working nothing is printing.

 {this.state.data && Object.keys(this.state.data).length > 0 && (
          <SortableList
            style={style.list}
            contentContainerStyle={style.contentContainer}
            data={this.state.data}
            renderRow={this._renderRow}
            order={this.state.order}
            onPressRow={() => {
              console.log('press row')
            }}
            /* pass any function for example
            onPressToChangeOrder = {()=>{ make functionality to make new order}}
            */
          />
        )}

adirzoari avatar Feb 02 '19 17:02 adirzoari

Do the props in the row render function has onPress function?

bureyburey avatar Feb 02 '19 18:02 bureyburey

it works only when sortingEnabled = {true}.. but I don't want the user will have option to sort.

adirzoari avatar Feb 02 '19 18:02 adirzoari

Do the props in the row render function has onPress function?

no.. how to pass it?

adirzoari avatar Feb 02 '19 18:02 adirzoari

row component

class Row extends Component {
  constructor(props) {
    super(props)
    this.state = {
      checked_id: -1,
    }

    this._active = new Animated.Value(0)

    this._style = {
      ...Platform.select({
        ios: {
          transform: [
            {
              scale: this._active.interpolate({
                inputRange: [0, 1],
                outputRange: [1, 1.1],
              }),
            },
          ],
          shadowRadius: this._active.interpolate({
            inputRange: [0, 1],
            outputRange: [2, 10],
          }),
        },

        android: {
          transform: [
            {
              scale: this._active.interpolate({
                inputRange: [0, 1],
                outputRange: [1, 1.07],
              }),
            },
          ],
          elevation: this._active.interpolate({
            inputRange: [0, 1],
            outputRange: [2, 6],
          }),
        },
      }),
    }
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.active !== nextProps.active) {
      Animated.timing(this._active, {
        duration: 300,
        easing: Easing.bounce,
        toValue: Number(nextProps.active),
      }).start()
    }
  }

  render() {
    const { data, active } = this.props
    console.log('data', data)
    if (!data) return null
    return (
      <Animated.View style={[this._style, style.view_item]} active={active}>
        <Button
          style={style.view_item}
          onPress={() => {
            // this.setState({ checked_id: data.id })
            /* call to new order
            this.props.onPressToChangeOrder()
            */
          }}
        >
          <View>
            <CheckBox
              checked_id={this.state.checked_id}
              id={data.id}
              icon={'md-checkmark'}
              checked={data.checked}
              onPress={id => {
                console.log('pressed again')
                this.chooseItem(id)
              }}
              disabled={false}
            />
          </View>
          <View />
          <View>
            <Text style={style.text_item}>{data.text}</Text>
          </View>
        </Button>
      </Animated.View>
    )
  }
}

adirzoari avatar Feb 02 '19 18:02 adirzoari

Do the props in the row render function has onPress function?

no.. how to pass it?

Nevermind, my bad. The onPressRow should suffice

bureyburey avatar Feb 02 '19 18:02 bureyburey

it works only when sortingEnabled={true} and when I remove button so this way

`   <Animated.View style={[this._style, style.view_item]} active={active}>
        <View>
          <CheckBox
            checked_id={this.state.checked_id}
            id={data.id}
            icon={'md-checkmark'}
            checked={data.checked}
            onPress={id => {
              console.log('pressed again')
              this.chooseItem(id)
            }}
            disabled={false}
          />
        </View>
        <View />
        <View>
          <Text style={style.text_item}>{data.text}</Text>
        </View>
      </Animated.View>
    )
  }`

but it's not what I'm looking for... I don't want user have option to sort..

adirzoari avatar Feb 02 '19 18:02 adirzoari

@adirzoari try this:

          <SortableList
            style={styles.list}
            contentContainerStyle={styles.contentContainer}
            data={this.state.data}
            renderRow={this._renderRow}
            order={this.state.order}
            sortingEnabled={true}
            manuallyActivateRows={true}
            onPressRow={(key) => {
              this.setState({order: [key, ...Object.keys(this.state.data).filter((i) => i != key)]})
            }}
          />
  • the ordering could use some more work on it .....

bureyburey avatar Feb 02 '19 18:02 bureyburey

but in my case I want sortingEnabled={false} because I don't want user change order by sort it.

adirzoari avatar Feb 02 '19 19:02 adirzoari

@adirzoari in my attempt to re-create it you cannot re-organize by dragging, only by clicking on a row will it move to the top https://snack.expo.io/rJIMsvQEN

bureyburey avatar Feb 02 '19 19:02 bureyburey

@bureyburey Awesome!!! that's exactly what I want! thank your for your time! I appreciate!

adirzoari avatar Feb 02 '19 19:02 adirzoari

@adirzoari Glad i could help :)

bureyburey avatar Feb 02 '19 19:02 bureyburey

is there any way to make separator between each item? like in flatlist

adirzoari avatar Feb 02 '19 19:02 adirzoari

Add view with border? Or border on the Row component?

bureyburey avatar Feb 02 '19 19:02 bureyburey

umm sounds good.. thanks. you can close this issue

adirzoari avatar Feb 02 '19 19:02 adirzoari

last thing, in row class, can i know what the key press? because i want to make when i press on view(not in checkbox) so it change checkbox style.

adirzoari avatar Feb 02 '19 19:02 adirzoari

The key is passed to the renderRow function, so just pass it along to the Row component

_renderRow = ({ data, active, key }) => {
  return <Row data={data} active={active} key={key}/>
}

bureyburey avatar Feb 02 '19 21:02 bureyburey

I mean to the chosen row key. for example if I press on item num 3 so I want only the checkbox or item num 3 will be active (without press on checkbox button)

adirzoari avatar Feb 02 '19 21:02 adirzoari