master 7b6b5a874e7a cached
33 files
60.2 KB
16.4k tokens
35 symbols
1 requests
Download .txt
Repository: bear-junior/react-native-draganddrop-board
Branch: master
Commit: 7b6b5a874e7a
Files: 33
Total size: 60.2 KB

Directory structure:
gitextract_8mqcp5my/

├── .gitignore
├── README.md
├── jest/
│   └── config.json
├── package.json
└── src/
    ├── components/
    │   ├── Board/
    │   │   ├── Board.js
    │   │   └── Board.styled.js
    │   ├── Card/
    │   │   ├── Card.js
    │   │   └── Card.styled.js
    │   ├── Carousel/
    │   │   ├── Carousel.js
    │   │   └── Carousel.styled.js
    │   ├── Column/
    │   │   ├── Column.js
    │   │   └── Column.styled.js
    │   ├── EmptyColumn/
    │   │   ├── EmptyColumn.js
    │   │   ├── EmptyColumn.styled.js
    │   │   ├── EmptyColumn.test.js
    │   │   └── __snapshots__/
    │   │       └── EmptyColumn.test.js.snap
    │   ├── Icons/
    │   │   ├── Empty.js
    │   │   ├── Empty.test.js
    │   │   ├── Next.js
    │   │   ├── Next.test.js
    │   │   ├── __snapshots__/
    │   │   │   ├── Empty.test.js.snap
    │   │   │   └── Next.test.js.snap
    │   │   └── index.js
    │   ├── index.js
    │   └── lib/
    │       ├── BoardRepository.js
    │       ├── ColumnItem.js
    │       ├── Item.js
    │       ├── Mover.js
    │       ├── PositionCalculator.js
    │       └── Registry.js
    └── constants/
        ├── colors.js
        ├── deviceHelpers.js
        └── index.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
npm-debug.log
node_modules/
.DS_Store
yarn.lock

================================================
FILE: README.md
================================================
<div align="center">
  <image align="center" src="./src/assets/images/header.png"/>
  <image align="center" src="https://d25lcipzij17d.cloudfront.net/badge.svg?id=js&type=6&v=1.0.4&x2=0"/>
</div>

# Introduction 

react-native-draganddrop-board is a simple React Native library, enabling to create a scrollable board component with carousel, sortable columns and draggable cards for your iOS and Android apps.

![Move Gif](./src/assets/images/move.gif) ![MoveInColumn Gif](./src/assets/images/moveInColumn.gif) ![Scroll Gif](./src/assets/images/scroll.gif)

# Installation

Install library via `npm` or `yarn`

`npm install react-native-draganddrop-board` or `yarn add react-native-draganddrop-board`

# In Use

First you need to build and fill with data `BoardRepository`:

```js
import { BoardRepository } from 'react-native-draganddrop-board'

const data = [
  {
    id: 1,
    name: 'TO DO',
    rows: [
      {
        id: '1',
        name: 'Analyze your audience',
        description: 'Learn more about the audience to whom you will be speaking'
      },
      {
        id: '2',
        name: 'Select a topic',
        description: 'Select a topic that is of interest to the audience and to you'
      },
      {
        id: '3',
        name: 'Define the objective',
        description: 'Write the objective of the presentation in a single concise statement'
      }
    ]
  },
  {
    id: 2,
    name: 'IN PROGRESS',
    rows: [
      {
        id: '4',
        name: 'Look at drawings',
        description: 'How did they use line and shape? How did they shade?'
      },
      {
        id: '5',
        name: 'Draw from drawings',
        description: 'Learn from the masters by copying them'
      },
      {
        id: '6',
        name: 'Draw from photographs',
        description: 'For most people, it’s easier to reproduce an image that’s already two-dimensional'
      }
    ]
  },
  {
    id: 3,
    name: 'DONE',
    rows: [
      {
        id: '7',
        name: 'Draw from life',
        description: 'Do you enjoy coffee? Draw your coffee cup'
      },
      {
        id: '8',
        name: 'Take a class',
        description: 'Check your local university extension'
      }
    ]
  }
]

const boardRepository = new BoardRepository(data);
```

Then you can render the `Board`:

```jsx
import { Board } from 'react-native-draganddrop-board'

  <Board
    boardRepository={boardRepository}
    open={() => {}}
    onDragEnd={() => {}}
  />
```

# Board component

| Property | Type | Required | Description |
| :--- | :--- | :---: | :--- |
| boardRepository | `BoardRepository` | yes | object that holds data |
| boardBackground | `string` | no | board background color |
| open | `function` | yes | function invoked when item is pressed, returns item |
| onDragEnd | `function` | yes | function invoked when drag is finished, returns srcColumn, destColumn, draggedItem object|

All props from Board, Card, Column and Empty components should be added to `<Board />`

# Data update
Data can be changed within our predefined class 'boardRepository'.
'boardRepository.updateData(data)'
That way we won't have to rerender the Board and class objects.

# Card component

If you want to use default Card you should build your boardRepository with rows that have elements `id`, `name`and `description`:
Pay attention, the `id` is unique. (Rows, column)
```
  {
     id: '1',
     name: 'Analyze your audience',
     description: 'Learn more about the audience to whom you will be speaking'
  }
```

| Property | Type | Required | Description |
| :--- | :--- | :---: | :--- |
| cardNameTextColor | `string` | no | color of the first line (name) |
| cardNameFontSize | `number` | no | font size of of the first line (name) |
| cardNameFontFamily | `string` | no | font family of the first line (name) |
| cardDescriptionTextColor | `string` | no | color of the second line (description) |
| cardDescriptionFontSize | `number` | no | font size of the second line (description) |
| cardDescriptionFontFamily | `string` | no | font family of the second line (description) |
| cardIconColor | `string` | no | color of the icon (arrow) |


If you need to have another elements in rows, then you can use `cardContent` prop - it's a function that returns item element and can take another Components to fill Card.

```jsx
import { Text, View } from 'react-native'
import { Board } from 'react-native-draganddrop-board'

  <Board
    boardRepository={boardRepository}
    open={() => {}}
    onDragEnd={() => {}}
    cardContent={(item) => (<View><Text>{item.name}</Text></View>)}
  />
```

| Property | Type | Required | Description |
| :--- | :--- | :---: | :--- |
| cardBackground | `string` | no | card background color |
| cardBorderRadius | `number` | no | card border radius value |
| isCardWithShadow | `bool` | no | add shadow to card component |

# Column component

| Property | Type | Required | Description |
| :--- | :--- | :---: | :--- |
| badgeBackgroundColor | `string` | no | color of the count badge |
| badgeBorderRadius | `number` | no | count badge border radius |
| badgeHeight | `number` | no | height of the count badge |
| badgeWidth | `string` | no | width of the count badge |
| badgeTextColor | `string` | no | color of the count badge |
| badgeTextFontSize | `number` | no | font size of the count badge |
| badgeTextFontFamily | `string` | no | font family of the count badge |
| columnBackgroundColor | `string` | no | column background color |
| columnBorderRadius | `number` | no | column border radius |
| columnHeight | `number` | no | height of the column |
| columnNameTextColor | `string` | no | color of the column |
| columnNameFontSize | `number` | no | font size of the column |
| columnNameFontFamily | `string` | no | font family of the column |
| isWithCountBadge | `bool` | no | if false then the count badge is not visible |

# Empty column component


![Empty Gif](./src/assets/images/empty.gif)

You can use default empty column component:

| Property | Type | Required | Description |
| :--- | :--- | :---: | :--- |
| emptyIconColor | `string` | no | color of the icon |
| emptyTextColor | `string` | no | color of the text |
| emptyFontSize | `number` | no | font size of the text |
| emptyFontFamily | `string` | no | font family of the text |

You can also create your own empty column component: 

| Property | Type | Required | Description |
| :--- | :--- | :---: | :--- |
| emptyComponent | `function` | no | function that should return custom empty column component |

# Tech stack

React Native 0.61.4

# License

Copyright (c) 2020, Natalia Muryn

Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.


================================================
FILE: jest/config.json
================================================
{
    "preset": "react-native",
    "rootDir": "../",
    "transform": {
        "^.+\\.js$": "./node_modules/react-native/jest/preprocessor.js"
    },
    "moduleNameMapper": {
        "styled-components": "<rootDir>/node_modules/styled-components/native/dist/styled-components.native.cjs.js"
    }
}

================================================
FILE: package.json
================================================
{
  "name": "react-native-draganddrop-board",
  "version": "1.0.5",
  "description": "Drag and drop elements inside carousel",
  "main": "src/components/index.js",
  "scripts": {
    "test": "jest --config=\"./jest/config.json\""
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/bear-junior/react-native-draganddrop-board.git"
  },
  "keywords": [
    "draggable",
    "board",
    "drag",
    "and",
    "drop",
    "react",
    "native",
    "drag and drop",
    "react-native"
  ],
  "author": "Natalia Muryn",
  "license": "ISC",
  "peerDependencies": {
    "react": "16.9.0",
    "react-native": "0.61.5"
  },
  "dependencies": {
    "lodash": "^4.17.15",
    "react-timeout": "^1.2.0",
    "styled-components": "^4.4.1",
    "styled-system": "^5.1.5"
  },
  "devDependencies": {
    "@babel/core": "^7.7.4",
    "@babel/runtime": "^7.7.4",
    "@react-native-community/eslint-config": "^0.0.5",
    "babel-jest": "^24.9.0",
    "eslint": "^6.7.0",
    "jest": "^24.9.0",
    "metro-react-native-babel-preset": "^0.57.0",
    "react": "16.9.0",
    "react-native": "0.61.5",
    "react-test-renderer": "^16.12.0"
  },
  "bugs": {
    "url": "https://github.com/bear-junior/react-native-draganddrop-board/issues"
  },
  "homepage": "https://github.com/bear-junior/react-native-draganddrop-board#readme",
  "readmeFilename": "README.md"
}


================================================
FILE: src/components/Board/Board.js
================================================
import React from 'react'
import ReactTimeout from 'react-timeout'
import {
  Animated,
  PanResponder,
  StatusBar
} from 'react-native'
import {
  func,
  object,
  string
} from 'prop-types'
import {
  colors,
  deviceWidth,
  ios,
  isX
} from '../../constants'
import Column from '../Column/Column'
import Card from '../Card/Card'
import Carousel from '../Carousel/Carousel'
import { BoardWrapper } from './Board.styled'

const MAX_RANGE = 100
const MAX_DEG = 30
let CARD_WIDTH = 0.85 * deviceWidth
const STATUSBAR_HEIGHT = ios ? (isX() ? 44 : 20) : StatusBar.currentHeight

class Board extends React.Component {
  constructor(props) {
    super(props)

    if (this.props.columnWidth) {
      CARD_WIDTH = this.props.columnWidth;
    }
    this.state = {
      boardPositionY: 0,
      rotate: new Animated.Value(0),
      pan: new Animated.ValueXY(),
      startingX: 0,
      startingY: 0,
      movingMode: false
    }

    this.varticalOffset = 0

    this.panResponder = PanResponder.create({
      onMoveShouldSetPanResponder: () => this.state.movingMode,
      onPanResponderMove: this.onPanResponderMove,
      onPanResponderRelease: this.onPanResponderRelease,
      onPanResponderTerminate: this.onPanResponderRelease
    })
  }

  componentDidMount() {
    this.val = { x: 0, y: 0 }
    // eslint-disable-next-line no-return-assign
    this.state.pan.addListener((value) => this.val = value)

  }

  componentWillUnmount() {
    this.unsubscribeFromMovingMode()
  }

  onPanResponderMove = (event, gesture) => {
    try {
      const {
        draggedItem,
        movingMode,
        pan,
        startingX
      } = this.state
      const { boardRepository } = this.props

      if (movingMode) {
        this.x = event.nativeEvent.pageX
        this.y = event.nativeEvent.pageY

        Animated.event([
          null, { dx: pan.x, dy: pan.y }
        ], {
          listener: null,
          useNativeDriver: false,
        })(event, gesture)

        if (startingX + gesture.dx < -50 && gesture.vx < 0) {
          this.carousel.snapToPrev()
        }
        if (startingX + gesture.dx + CARD_WIDTH - 50 > deviceWidth && gesture.vx > 0) {
          this.carousel.snapToNext()
        }

        const columnId = this.carousel.currentIndex
        const columnAtPosition = boardRepository.move(draggedItem, this.x, this.y, columnId)
        if (columnAtPosition) {
          const { scrolling, offset } = boardRepository.scrollingPosition(columnAtPosition, this.x, this.y, columnId)
          if (this.shouldScroll(scrolling, offset, columnAtPosition)) {
            this.scroll(columnAtPosition, draggedItem, offset)
          }
        }
      }
    } catch (error) {
      console.log("columnAtPosition", error)
    }

  }

  shouldScroll = (scrolling, offset, column) => {
    const placeToScroll = ((offset < 0
      && column.scrollOffset() > 0)
      || (offset > 0 && column.scrollOffset() < column.contentHeight()))

    return scrolling && offset !== 0 && placeToScroll
  }

  onScrollingStarted = () => {
    this.scrolling = true
  }

  onScrollingEnded = () => {
    this.scrolling = false
  }

  scroll = (column, draggedItem, anOffset) => {
    const { requestAnimationFrame, boardRepository } = this.props

    if (!this.scrolling) {
      this.onScrollingStarted()
      const scrollOffset = column.scrollOffset() + 80 * anOffset
      boardRepository.setScrollOffset(column.id(), scrollOffset)

      column.listView().scrollToOffset({ offset: scrollOffset })
    }

    boardRepository.move(draggedItem, this.x, this.y)
    const { scrolling, offset } = boardRepository.scrollingPosition(column, this.x, this.y)
    if (this.shouldScroll(scrolling, offset, column)) {
      requestAnimationFrame(() => {
        this.scroll(column, draggedItem, offset)
      })
    }
  }

  endMoving = () => {
    try {
      this.setState({ movingMode: false })
      const { draggedItem, pan, srcColumnId } = this.state
      const { boardRepository, onDragEnd } = this.props

      boardRepository.show(draggedItem.columnId(), draggedItem)
      boardRepository.notify(draggedItem.columnId(), 'reload')

      const destColumnId = draggedItem.columnId()
      pan.setValue({ x: 0, y: 0 })
      this.setState({ startingX: 0, startingY: 0 })

      return onDragEnd && onDragEnd(boardRepository.columns()[srcColumnId - 1], boardRepository.columns()[destColumnId - 1], draggedItem)

    } catch (error) {
      const { draggedItem, srcColumnId } = this.state
      const { onDragEnd } = this.props
      const destColumnId = draggedItem.columnId()
      this.setState({ movingMode: false, startingX: 0, startingY: 0 })
      console.log("endMoving", error)
      return onDragEnd && onDragEnd(boardRepository.columns()[srcColumnId - 1], boardRepository.columns()[destColumnId - 1], draggedItem)

    }
  }

  onPanResponderRelease = () => {
    const { movingMode } = this.state
    this.x = null
    this.y = null

    if (movingMode) {
      this.rotate(0)
      setTimeout(this.endMoving, 100)
    } else if (this.scrolling) {
      this.unsubscribeFromMovingMode()
    }
  }

  rotate = (toValue) => {
    const { rotate } = this.state
    Animated.spring(
      rotate,
      {
        toValue,
        friction: 5,
        useNativeDriver: true
      }
    ).start()
  }

  cancelMovingSubscription = () => {
    const { clearTimeout } = this.props

    clearTimeout(this.movingSubscription)
  }

  unsubscribeFromMovingMode = () => {
    this.cancelMovingSubscription()
  }

  onPressIn = (columnId, item, dy) => {
    const { boardPositionY } = this.state
    const {
      boardRepository,
      setTimeout
    } = this.props

    if (item.isLocked()) {
      return
    }

    if (!item || (item.isLocked() && this.scrolling)) {
      this.unsubscribeFromMovingMode()
      return
    }
    this.movingSubscription = setTimeout(() => {
      if (!item || !item.layout()) {
        return
      }
      const lastColumn = boardRepository.columns().length - 1
      const columnIndex = this.carousel.currentIndex

      let x

      if (columnIndex === 0) {
        x = 16
      } else if (columnIndex > 0 && columnIndex < lastColumn) {
        x = ((deviceWidth - (0.78 * deviceWidth) + 16) / 2)
      } else if (columnIndex === lastColumn) {
        x = deviceWidth - (0.78 * deviceWidth)
      }
      const { y } = item.layout()

      if (columnId - 1 === columnIndex) {
        boardRepository.hide(columnId, item)
        this.setState({
          movingMode: true,
          draggedItem: item,
          srcColumnId: item.columnId(),
          startingX: x,
          startingY: dy - boardPositionY - STATUSBAR_HEIGHT - (ios ? 0 : (dy - y))
        })
        this.rotate(MAX_DEG)
      }
    }, 200)
  }

  onPress = (columnId, item) => {
    const { open } = this.props
    const { movingMode } = this.state

    if (item.isLocked()) {
      return
    }

    return () => {
      this.unsubscribeFromMovingMode()

      if (item.isLocked()) {
        return
      }

      if (!movingMode) {
        const columnIndex = this.carousel.currentIndex

        if (columnId - 1 === columnIndex) {
          open(item.row())
        }
      } else {
        this.endMoving()
      }
    }
  }

  onScrollEnd = () => {
    const { boardRepository } = this.props
    boardRepository.updateColumnsLayoutAfterVisibilityChanged()
  }

  movingStyle = (zIndex) => {
    const { pan, rotate, startingX, startingY } = this.state
    const interpolatedRotateAnimation = rotate.interpolate({
      inputRange: [-MAX_RANGE, 0, MAX_RANGE],
      outputRange: [`-${MAX_DEG}deg`, '0deg', `${MAX_DEG}deg`]
    })

    return {
      position: 'absolute',
      zIndex,
      top: startingY,
      left: startingX,
      width: CARD_WIDTH - 16,
      transform: [
        { translateX: pan.x },
        { translateY: pan.y },
        { rotate: interpolatedRotateAnimation }
      ]
    }
  }

  movingTask = () => {
    const { draggedItem, movingMode } = this.state
    const zIndex = movingMode ? 1 : -1
    const data = {
      item: draggedItem,
      hidden: !movingMode,
      style: this.movingStyle(zIndex)
    }

    return this.renderWrapperRow(data)
  }

  renderWrapperRow = (data) => (
    <Card
      {...data}
      {...this.props}
      width={CARD_WIDTH}
    />
  )

  setScrollViewRef = (element) => {
    this.scrollViewRef = element
  }

  setBoardPositionY = (y) => {
    this.setState({ boardPositionY: y })
  }

  render() {
    const { movingMode } = this.state
    const {
      boardBackground,
      boardRepository,
      data
    } = this.props

    return (
      <BoardWrapper
        {...this.panResponder.panHandlers}>
        <BoardWrapper
          onLayout={(evt) => this.setBoardPositionY(evt.nativeEvent.layout.y)}
          backgroundColor={boardBackground}
        >
          <Carousel
            ref={(c) => { this.carousel = c }}
            data={boardRepository.columns()}
            onScrollEndDrag={this.onScrollEnd}
            onScroll={this.cancelMovingSubscription}
            scrollEnabled={!movingMode}
            renderItem={item => (
              <Column
                {...this.props}
                key={item.item.data().id.toString()}
                column={item.item}
                movingMode={movingMode}
                boardRepository={boardRepository}
                onPressIn={this.onPressIn}
                onPress={this.onPress}
                renderWrapperRow={this.renderWrapperRow}
                onScrollingStarted={this.onScrollingStarted}
                onScrollingEnded={this.onScrollingEnded}
                unsubscribeFromMovingMode={this.cancelMovingSubscription}
                oneColumn={boardRepository.columns().length === 1}
              />
            )}
            sliderWidth={deviceWidth}
            itemWidth={CARD_WIDTH}
            oneColumn={boardRepository.columns().length === 1}
          />

          {this.movingTask()}
        </BoardWrapper >
      </BoardWrapper>
    )
  }
}

Board.defaultProps = {
  boardBackground: colors.deepComaru
}

Board.propTypes = {
  boardBackground: string.isRequired,
  clearTimeout: func.isRequired,
  onDragEnd: func.isRequired,
  open: func.isRequired,
  requestAnimationFrame: func.isRequired,
  boardRepository: object.isRequired,
  setTimeout: func.isRequired
}

export default ReactTimeout(Board)


================================================
FILE: src/components/Board/Board.styled.js
================================================
import styled from 'styled-components/native'

const BoardWrapper = styled.View`
`

export { BoardWrapper }


================================================
FILE: src/components/Card/Card.js
================================================
import React from 'react'
import { Animated } from 'react-native'
import {
  bool,
  func,
  number,
  object,
  shape,
  string
} from 'prop-types'
import {
  colors,
  fonts,
  deviceWidth
} from '../../constants'
import { Next } from '../Icons'
import {
  CardContainer,
  CardWrapper,
  ColumnWrapper,
  IconRowWrapper,
  Paragraph,
  RowWrapper
} from './Card.styled'

const Card = ({
  cardBackground,
  cardBorderRadius,
  cardContent,
  cardDescriptionTextColor,
  cardDescriptionFontSize,
  cardDescriptionFontFamily,
  cardIconColor,
  cardNameTextColor,
  cardNameFontSize,
  cardNameFontFamily,
  hidden,
  item,
  isCardWithShadow,
  onPress,
  onPressIn,
  style
}) => {
  const styles = [style]
  if (hidden) {
    styles.push({ opacity: 0 })
  }

  return (
    <CardWrapper
      onPressIn={(evt) => onPressIn ? onPressIn(evt.nativeEvent.pageY) : {}}
      onPress={onPress}
      collapsable={false}
    >
      <Animated.View style={styles}>
        {cardContent !== undefined ? cardContent(item ? item.row() : {}) :

          <CardContainer
            backgroundColor={cardBackground}
            borderRadius={cardBorderRadius}
            elevation={isCardWithShadow ? 5 : 0}
            shadowOpacity={isCardWithShadow ? 0.1 : 0}
          >
            <RowWrapper>
              <IconRowWrapper width={deviceWidth / 2 - 28}>
                <ColumnWrapper>
                  <Paragraph
                    fontSize={cardNameFontSize}
                    fontFamily={cardNameFontFamily}
                    color={cardNameTextColor}
                  >
                    {item ? item.row().name : ''}
                  </Paragraph>
                  <Paragraph
                    fontSize={cardDescriptionFontSize}
                    fontFamily={cardDescriptionFontFamily}
                    color={cardDescriptionTextColor}
                  >
                    {item ? item.row().description : ''}
                  </Paragraph>
                </ColumnWrapper>
              </IconRowWrapper>
              <Next color={cardIconColor} />
            </RowWrapper>
          </CardContainer>
        }

      </Animated.View>
    </CardWrapper>
  )
}

Card.defaultProps = {
  cardBackground: colors.white,
  cardBorderRadius: 10,
  cardDescriptionTextColor: colors.bay,
  cardDescriptionFontSize: 14,
  cardDescriptionFontFamily: '',
  cardIconColor: colors.blurple,
  cardNameTextColor: colors.blurple,
  cardNameFontSize: 18,
  cardNameFontFamily: '',
  isCardWithShadow: true
}

Card.propTypes = {
  cardBackground: string.isRequired,
  cardBorderRadius: number.isRequired,
  cardContent: func,
  cardDescriptionTextColor: string.isRequired,
  cardDescriptionFontSize: number.isRequired,
  cardDescriptionFontFamily: string.isRequired,
  cardIconColor: string.isRequired,
  cardNameTextColor: string.isRequired,
  cardNameFontSize: number.isRequired,
  cardNameFontFamily: string.isRequired,
  hidden: bool,
  item: object,
  isCardWithShadow: bool.isRequired,
  onPress: func,
  onPressIn: func,
  style: shape({ string })
}

export default Card


================================================
FILE: src/components/Card/Card.styled.js
================================================
import styled from 'styled-components/native'
import {
  borderRadius,
  color,
  fontFamily,
  fontSize
} from 'styled-system'
import { colors } from '../../constants'

const CardContainer = styled.View`
  ${borderRadius}
  marginHorizontal: 8;
  paddingHorizontal: 0;
  paddingVertical: 0;
  width: 94.5%;
  shadow-radius: 15px;
  shadow-color: ${colors.black};
  shadow-offset: 0px 3px;
  marginTop: 4;
  marginBottom: 4;
`

const CardWrapper = styled.TouchableWithoutFeedback`
`

const ColumnWrapper = styled.View`
`

const IconRowWrapper = styled.View`
  flexDirection: row;
  alignItems: center;
`

const Paragraph = styled.Text`
  ${fontFamily};
  ${fontSize};
  ${color};
`

const RowWrapper = styled.View`
  flexDirection: row;
  alignItems: center;
  justifyContent: space-between;
`

export {
  CardContainer,
  CardWrapper,
  ColumnWrapper,
  IconRowWrapper,
  Paragraph,
  RowWrapper
}


================================================
FILE: src/components/Carousel/Carousel.js
================================================
import React, { Component } from 'react'
import { ScrollView } from 'react-native'
import {
  array,
  bool,
  func,
  number
} from 'prop-types'
import { deviceWidth, ios } from '../../constants'
import { ItemWrapper } from './Carousel.styled'

const INITIAL_ACTIVE_ITEM = 0

class Carousel extends Component {
  constructor(props) {
    super(props)

    this.activeItem = INITIAL_ACTIVE_ITEM
    this.previousActiveItem = INITIAL_ACTIVE_ITEM
    this.previousFirstItem = INITIAL_ACTIVE_ITEM
    this.previousItemsLength = INITIAL_ACTIVE_ITEM
    this.mounted = false
    this.positions = []
    this.currentContentOffset = 0
    this.scrollOffsetRef = null
  }

  componentDidMount() {
    this.mounted = true
    this.activeItem = 0

    this.initPositions(this.props)
  }

  UNSAFE_componentWillUpdate = (nextProps) => {
    this.initPositions(nextProps)
  }

  componentWillUnmount() {
    this.mounted = false
  }

  getCustomDataLength = (props = this.props) => {
    const { data } = props
    const dataLength = data && data.length

    if (!dataLength) {
      return 0
    }

    return dataLength
  }

  getCustomIndex = (index, props = this.props) => {
    const itemsLength = this.getCustomDataLength(props)

    if (!itemsLength || (!index && index !== 0)) {
      return 0
    }

    return index
  }

  get currentIndex() {
    return this.activeItem
  }

  getDataIndex = (index) => {
    const { data } = this.props
    const dataLength = data && data.length

    if (!dataLength) {
      return index
    }

    if (index >= dataLength + 1) {
      return dataLength < 1
        ? (index - 1) % dataLength
        : index - dataLength - 1
    } if (index < 1) {
      return index + dataLength - 1
    }

    return index - 1
  }

  getFirstItem = (index, props = this.props) => {
    const itemsLength = this.getCustomDataLength(props)

    if (!itemsLength || index > itemsLength - 1 || index < 0) {
      return 0
    }

    return index
  }

  getWrappedRef = () => this.carouselRef

  getKeyExtractor = (item, index) => `scrollview-item-${index}`

  getScrollOffset = event => (event && event.nativeEvent && event.nativeEvent.contentOffset
    && event.nativeEvent.contentOffset.x) || 0

  getCenter = (offset) => {
    const {
      itemWidth,
      sliderWidth
    } = this.props

    return offset + sliderWidth / 2 - (sliderWidth - itemWidth) / 2
  }

  getActiveItem = (offset) => {
    const center = this.getCenter(offset)
    const centerOffset = 20

    for (let i = 0; i < this.positions.length; i += 1) {
      const { start, end } = this.positions[i]
      if (center + centerOffset >= start && center - centerOffset <= end) {
        return i
      }
    }

    const lastIndex = this.positions.length - 1
    if (this.positions[lastIndex] && center - centerOffset > this.positions[lastIndex].end) {
      return lastIndex
    }

    return 0
  }

  initPositions = (props = this.props) => {
    const {
      data,
      itemWidth
    } = props

    if (!data || !data.length) {
      return
    }

    this.positions = []

    const firstItemMargin = 0
    data.forEach((itemData, index) => {
      this.positions[index] = {
        start: firstItemMargin + index * itemWidth + (index * 8),
        end: index * itemWidth + itemWidth + (index * 8)
      }
    })
  }

  scrollTo = (offset) => {
    const wrappedRef = this.getWrappedRef()

    wrappedRef.scrollTo({ x: offset, y: 0, animated: true })
  }

  onScroll = (event) => {
    const { onScroll } = this.props
    const scrollOffset = this.getScrollOffset(event)
    const nextActiveItem = this.getActiveItem(scrollOffset)

    const itemReached = nextActiveItem === this.itemToSnapTo
    const scrollConditions = scrollOffset >= this.scrollOffsetRef
      && scrollOffset <= this.scrollOffsetRef

    this.currentContentOffset = scrollOffset

    if (this.activeItem !== nextActiveItem && itemReached) {
      if (scrollConditions) {
        this.activeItem = nextActiveItem
      }
    }

    return onScroll && onScroll()
  }

  onScrollBeginDrag = (event) => {
    this.scrollStartOffset = this.getScrollOffset(event)
    this.scrollStartActive = this.getActiveItem(this.scrollStartOffset)
  }

  onScrollEndDrag = (event) => {
    const { onScrollEndDrag } = this.props

    if (this.carouselRef) {
      return this.onScrollEnd && this.onScrollEnd(event)
    }

    return onScrollEndDrag()
  }

  onScrollEnd = () => {
    this.scrollEndOffset = this.currentContentOffset
    this.scrollEndActive = this.getActiveItem(this.scrollEndOffset)

    this.snapScroll(this.scrollEndOffset - this.scrollStartOffset)
  }

  onLayout = () => {
    if (this.onLayoutInitDone) {
      this.initPositions()
      this.snapToItem(this.activeItem)
    } else {
      this.onLayoutInitDone = true
    }
  }

  snapScroll = (delta) => {
    if (!this.scrollEndActive && this.scrollEndActive !== 0 && ios) {
      this.scrollEndActive = this.scrollStartActive
    }

    if (this.scrollStartActive !== this.scrollEndActive) {
      this.snapToItem(this.scrollEndActive)
    } else if (delta > 0) {
      this.snapToItem(this.scrollStartActive + 1)
    } else if (delta < 0) {
      this.snapToItem(this.scrollStartActive - 1)
    } else {
      this.snapToItem(this.scrollEndActive)
    }
  }

  snapToItem = (index) => {
    const { itemWidth } = this.props
    this.activeItem = index

    if (index !== this.previousActiveItem) {
      this.previousActiveItem = index
    }

    this.itemToSnapTo = index
    this.scrollOffsetRef = this.positions[index]
      && this.positions[index].start - ((deviceWidth - itemWidth) / 2) + 8

    if (!this.scrollOffsetRef && this.scrollOffsetRef !== 0) {
      return
    }

    this.currentContentOffset = this.scrollOffsetRef < 0 ? 0 : this.scrollOffsetRef

    this.scrollTo(this.scrollOffsetRef)
  }

  snapToNext = () => {
    const { onScrollEndDrag } = this.props
    const itemsLength = this.getCustomDataLength()

    const newIndex = this.activeItem + 1
    if (newIndex > itemsLength - 1) {
      return
    }

    setTimeout(() => this.snapToItem(newIndex), 500)
    onScrollEndDrag()
  }

  snapToPrev = () => {
    const { onScrollEndDrag } = this.props
    const newIndex = this.activeItem - 1
    if (newIndex < 0) {
      return
    }
    setTimeout(() => this.snapToItem(newIndex), 500)
    onScrollEndDrag()
  }

  renderItem = ({ item, index }) => {
    const { renderItem } = this.props

    const specificProps = {
      key: this.getKeyExtractor(item, index)
    }

    return (
      <ItemWrapper
        pointerEvents="box-none"
        {...specificProps}
      >
        { renderItem({ item, index })}
      </ItemWrapper>
    )
  }

  getComponentStaticProps = () => {
    const {
      data,
      oneColumn,
      sliderWidth
    } = this.props

    const containerStyle = [
      { width: sliderWidth, flexDirection: 'row' }
    ]
    const contentContainerStyle = {
      paddingLeft: oneColumn ? 16 : 8,
      paddingTop: 8,
      paddingBottom: 8
    }

    return {
      // eslint-disable-next-line no-return-assign
      ref: c => this.carouselRef = c,
      data,
      style: containerStyle,
      contentContainerStyle,
      horizontal: true,
      scrollEventThrottle: 1,
      onScroll: this.onScroll,
      onScrollBeginDrag: this.onScrollBeginDrag,
      onScrollEndDrag: this.onScrollEndDrag,
      onLayout: this.onLayout
    }
  }

  render() {
    const props = {
      decelerationRate: 'fast',
      showsHorizontalScrollIndicator: false,
      overScrollMode: 'never',
      automaticallyAdjustContentInsets: true,
      directionalLockEnabled: true,
      pinchGestureEnabled: false,
      scrollsToTop: false,
      renderToHardwareTextureAndroid: true,
      ...this.props,
      ...this.getComponentStaticProps()
    }
    const {
      data,
      oneColumn,
      scrollEnabled
    } = this.props

    return (
      <ScrollView {...props} scrollEnabled={scrollEnabled && !oneColumn}>
        {data.map((item, index) => this.renderItem({ item, index }))}
      </ScrollView>
    )
  }
}

Carousel.propTypes = {
  data: array,
  itemWidth: number.isRequired,
  oneColumn: bool,
  onScroll: func,
  onScrollEndDrag: func,
  renderItem: func.isRequired,
  sliderWidth: number.isRequired
}

Carousel.defaultProps = {
  oneColumn: false,
  onScroll: () => { },
  onScrollEndDrag: () => { }
}

export default Carousel


================================================
FILE: src/components/Carousel/Carousel.styled.js
================================================
import styled from 'styled-components/native'

const ItemWrapper = styled.View`
`

export { ItemWrapper }


================================================
FILE: src/components/Column/Column.js
================================================
import React from 'react'
import { FlatList } from 'react-native'
import {
  bool,
  func,
  object,
  number,
  string
} from 'prop-types'
import {
  colors,
  fonts,
  deviceWidth,
  ios
} from '../../constants'
import EmptyColumn from '../EmptyColumn/EmptyColumn'
import {
  ColumnWrapper,
  ParagraphWrapper,
  Paragraph,
  RowContainer,
  RowWrapper,
  SumWrapper
} from './Column.styled'

const COLUMN_WIDTH = 0.85 * deviceWidth
const PADDING = 32
const ONE_COLUMN_WIDTH = deviceWidth - PADDING

class Column extends React.Component {
  constructor(props) {
    super(props)
  }

  componentDidMount() {
    const { column, boardRepository } = this.props

    boardRepository.addListener(column.id(), 'reload', () => this.forceUpdate())
  }

  onPressIn = (item, y) => {
    const { column, onPressIn } = this.props
    onPressIn(column.id(), item, y)
  }

  onPress = (item) => {
    const { column, onPress } = this.props

    return onPress(column.id(), item)
  }

  setItemRef = (item, ref) => {
    const { column, boardRepository } = this.props
    boardRepository.setItemRef(column.id(), item, ref)
    boardRepository.updateColumnsLayoutAfterVisibilityChanged();
  }

  updateItemWithLayout = item => () => {
    const { column, boardRepository } = this.props
    boardRepository.updateItemWithLayout(column.id(), item)
  }

  setColumnRef = (ref) => {
    const { column, boardRepository } = this.props
    boardRepository.setColumnRef(column.id(), ref)
  }

  updateColumnWithLayout = () => {
    const { column, boardRepository } = this.props

    boardRepository.updateColumnWithLayout(column.id())
  }

  renderWrapperRow = (item) => {
    const { renderWrapperRow } = this.props
    const props = {
      onPressIn: (y) => this.onPressIn(item, y),
      onPress: this.onPress(item),
      hidden: item.isHidden(),
      item
    }
    return (
      <RowWrapper
        ref={ref => this.setItemRef(item, ref)}
        collapsable={false}
        onLayout={this.updateItemWithLayout(item)}
        key={item.id.toString()}
      >
        {renderWrapperRow(props)}
      </RowWrapper>
    )
  }

  handleScroll = (event) => {
    const {
      column,
      onScrollingStarted,
      boardRepository,
      unsubscribeFromMovingMode
    } = this.props

    unsubscribeFromMovingMode()
    onScrollingStarted()

    const col = boardRepository.column(column.id())

    const liveOffset = event.nativeEvent.contentOffset.y

    this.scrollingDown = liveOffset > col.scrollOffset()
  }

  endScrolling = (event) => {
    const {
      column,
      onScrollingEnded,
      boardRepository
    } = this.props

    const currentOffset = event.nativeEvent.contentOffset.y
    const col = boardRepository.column(column.id())
    const scrollingDownEnded = this.scrollingDown && currentOffset >= col.scrollOffset()
    const scrollingUpEnded = !this.scrollingDown && currentOffset <= col.scrollOffset()

    if (scrollingDownEnded || scrollingUpEnded) {
      boardRepository.setScrollOffset(col.id(), currentOffset)
      boardRepository.updateColumnsLayoutAfterVisibilityChanged()
      onScrollingEnded()
    }
  }

  onScrollEndDrag = (event) => {
    this.endScrolling(event)
  }

  onMomentumScrollEnd = (event) => {
    const { onScrollingEnded } = this.props

    this.endScrolling(event)
    onScrollingEnded()
  }

  onContentSizeChange = (_, contentHeight) => {
    const { column, boardRepository } = this.props

    boardRepository.setContentHeight(column.id(), contentHeight)
  }

  handleChangeVisibleItems = (visibleItems) => {
    const { column, boardRepository } = this.props

    boardRepository.updateItemsVisibility(column.id(), visibleItems)
  }

  setListView = (ref) => {
    const { column, boardRepository } = this.props

    boardRepository.setListView(column.id(), ref)
  }

  render() {
    const {
      badgeBackgroundColor,
      badgeBorderRadius,
      badgeHeight,
      badgeWidth,
      badgeTextColor,
      badgeTextFontFamily,
      badgeTextFontSize,
      column,
      columnBackgroundColor,
      columnBorderRadius,
      columnHeight,
      columnNameFontFamily,
      columnNameFontSize,
      columnNameTextColor,
      emptyComponent,
      isWithCountBadge,
      oneColumn,
      movingMode,
      boardRepository,
      columnWidth
    } = this.props

    const colElements = boardRepository.items(column.id()).length - 1

    const ColumnComponent = (
      <ColumnWrapper
        backgroundColor={columnBackgroundColor}
        borderRadius={columnBorderRadius}
        ref={this.setColumnRef}
        collapsable={false}
        onLayout={this.updateColumnWithLayout}
        columnHeight={columnHeight}
        width={oneColumn ? ONE_COLUMN_WIDTH : columnWidth?columnWidth:COLUMN_WIDTH}
        marginRight={oneColumn ? 0 : 8}
      >
        <RowContainer>
          <Paragraph
            fontSize={columnNameFontSize}
            fontFamily={columnNameFontFamily}
            color={columnNameTextColor}
          >
            {column.data().name}
          </Paragraph>
          {isWithCountBadge && <SumWrapper>
            <ParagraphWrapper
              backgroundColor={badgeBackgroundColor}
              width={badgeWidth}
              height={badgeHeight}
              borderRadius={badgeBorderRadius}
            >
              <Paragraph
                fontSize={badgeTextFontSize}
                fontFamily={badgeTextFontFamily}
                color={badgeTextColor}
                lineHeight={ios ? null : badgeTextFontSize * 1.6}
              >
                {colElements.toString()}
              </Paragraph>
            </ParagraphWrapper>
          </SumWrapper>
          }
        </RowContainer>
        {boardRepository
          .items(column.id()).length - 1 === 0 ?
          (emptyComponent
            ? emptyComponent()
            : <EmptyColumn {...this.props} marginTop={columnHeight / 3} />
          )
          : <FlatList
            data={boardRepository.items(column.id())}
            ref={this.setListView}
            onScroll={this.handleScroll}
            scrollEventThrottle={0}
            onMomentumScrollEnd={this.onMomentumScrollEnd}
            onScrollEndDrag={this.onScrollEndDrag}
            onChangeVisibleRows={this.handleChangeVisibleItems}
            renderItem={item => this.renderWrapperRow(item.item)}
            keyExtractor={item => item.row().id.toString()}
            scrollEnabled={!movingMode}
            onContentSizeChange={this.onContentSizeChange}
            showsVerticalScrollIndicator={false}
            enableEmptySections
          />
        }
      </ColumnWrapper>
    )

    return ColumnComponent
  }
}

Column.defaultProps = {
  badgeBackgroundColor: colors.blurple,
  badgeBorderRadius: 15,
  badgeHeight: 30,
  badgeWidth: 30,
  badgeTextColor: colors.white,
  badgeTextFontFamily: '',
  badgeTextFontSize: 14,
  columnBackgroundColor: colors.fallingStar,
  columnBorderRadius: 6,
  columnHeight: 650,
  columnNameTextColor: colors.blurple,
  columnNameFontFamily: '',
  columnNameFontSize: 18,
  isWithCountBadge: true,
  oneColumn: false
}

Column.propTypes = {
  badgeBackgroundColor: string.isRequired,
  badgeBorderRadius: number.isRequired,
  badgeHeight: number.isRequired,
  badgeWidth: number.isRequired,
  badgeTextColor: string.isRequired,
  badgeTextFontFamily: string.isRequired,
  badgeTextFontSize: number.isRequired,
  column: object,
  columnBackgroundColor: string.isRequired,
  columnBorderRadius: number.isRequired,
  columnHeight: number.isRequired,
  columnNameFontFamily: string.isRequired,
  columnNameFontSize: number.isRequired,
  columnNameTextColor: string.isRequired,
  emptyComponent: func,
  isWithCountBadge: bool.isRequired,
  movingMode: bool.isRequired,
  oneColumn: bool,
  onPress: func.isRequired,
  onPressIn: func.isRequired,
  onScrollingEnded: func.isRequired,
  onScrollingStarted: func.isRequired,
  renderWrapperRow: func.isRequired,
  boardRepository: object,
  unsubscribeFromMovingMode: func.isRequired
}

export default Column


================================================
FILE: src/components/Column/Column.styled.js
================================================
import styled from 'styled-components/native'
import {
  borderRadius,
  color,
  fontFamily,
  fontSize,
  marginRight,
  lineHeight
} from 'styled-system'

const ColumnWrapper = styled.View`
  paddingHorizontal: 8;
  ${borderRadius};
  maxWidth: 400;
  ${marginRight};
  ${props => `height: ${props.columnHeight}`}
`

const ParagraphWrapper = styled.View`
  alignItems: center;
  justifyContent: center;
`

const RowContainer = styled.View`
  flexDirection: row;
  alignItems: center;
  paddingVertical: 18;
  paddingHorizontal: 10;
  justifyContent:center;

`

const Paragraph = styled.Text`
  ${fontFamily};
  ${fontSize};
  ${color};
  ${lineHeight};
`

const RowWrapper = styled.View`
  opacity: 1;
`

const SumWrapper = styled.View`
  marginLeft: 8;
  alignItems: center;
  justifyContent: center;
`

export {
  ColumnWrapper,
  ParagraphWrapper,
  Paragraph,
  RowContainer,
  RowWrapper,
  SumWrapper
}


================================================
FILE: src/components/EmptyColumn/EmptyColumn.js
================================================
import React from 'react'
import {
  number,
  string
} from 'prop-types'
import {
  colors,
  fonts
} from '../../constants'
import { Empty } from '../Icons'
import {
  EmptyWrapper,
  Paragraph
} from './EmptyColumn.styled'

const EmptyColumn = ({
  emptyIconColor,
  emptyText,
  emptyTextColor,
  emptyTextFontFamily,
  emptyTextFontSize,
  marginTop
}) =>  (
  <EmptyWrapper marginTop={marginTop}>
    <Empty color={emptyIconColor} />
    <Paragraph
      color={emptyTextColor}
      fontFamily={emptyTextFontFamily}
      fontSize={emptyTextFontSize}
    >
      {emptyText}
    </Paragraph>
  </EmptyWrapper>
)

EmptyColumn.defaultProps = {
  emptyIconColor: colors.blurple,
  emptyText: 'No items',
  emptyTextColor: colors.bay,
  emptyTextFontFamily: '',
  emptyTextFontSize: 16
}

EmptyColumn.propTypes = {
  emptyIconColor: string.isRequired,
  emptyText: string.isRequired,
  emptyTextColor: string.isRequired,
  emptyTextFontFamily: string.isRequired,
  emptyTextFontSize: number.isRequired,
  marginTop: number.isRequired
}

export default EmptyColumn


================================================
FILE: src/components/EmptyColumn/EmptyColumn.styled.js
================================================
import styled from 'styled-components/native'
import {
  color,
  fontFamily,
  fontSize
} from 'styled-system'

const EmptyWrapper = styled.View`
  justifyContent: center;
  alignItems: center;
`

const Paragraph = styled.Text`
  ${fontFamily};
  ${fontSize};
  ${color};
`

export {
  EmptyWrapper,
  Paragraph
}


================================================
FILE: src/components/EmptyColumn/EmptyColumn.test.js
================================================
import React from 'react'
import renderer from 'react-test-renderer'
import EmptyColumn from './EmptyColumn'

describe('EmptyColumn component', () => {
  describe('Renders correctly', () => {
    test('it renders Default EmptyColumn', () => {
      const tree = renderer.create(
        <EmptyColumn marginTop={40} />
      ).toJSON()
      expect(tree).toMatchSnapshot()
    })
  })
})


================================================
FILE: src/components/EmptyColumn/__snapshots__/EmptyColumn.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`EmptyColumn component Renders correctly it renders Default EmptyColumn 1`] = `
<View
  marginTop={40}
  style={
    Array [
      Object {
        "alignItems": "center",
        "justifyContent": "center",
      },
    ]
  }
>
  <Image
    source={
      Object {
        "testUri": "../../../src/assets/icons/emptyBox.png",
      }
    }
    style={
      Object {
        "height": 40,
        "tintColor": "#4834d4",
        "width": 40,
      }
    }
  />
  <Text
    color="#747d8c"
    fontFamily=""
    fontSize={16}
    style={
      Array [
        Object {
          "color": "#747d8c",
          "fontSize": 16,
        },
      ]
    }
  >
    No items
  </Text>
</View>
`;


================================================
FILE: src/components/Icons/Empty.js
================================================
import React from 'react'
import { Image } from 'react-native'

export const Empty = (color) => (
  <Image
    style={{
      tintColor: color.color,
      width: 40,
      height: 40
    }}
    source={require('../../assets/icons/emptyBox.png')}
  />
)


================================================
FILE: src/components/Icons/Empty.test.js
================================================
import React from 'react'
import renderer from 'react-test-renderer'
import { Empty } from './index'

describe('Icon component', () => {
  describe('Renders correctly', () => {
    test('it renders Default Empty Icon', () => {
      const tree = renderer.create(
        <Empty
          color="#FFFFFF"
        />
      ).toJSON()
      expect(tree).toMatchSnapshot()
    })
  })
})


================================================
FILE: src/components/Icons/Next.js
================================================
import React from 'react'
import { Image } from 'react-native'

export const Next = (color) => (
  <Image
    style={{
      tintColor: color.color,
      width: 25,
      height: 25
    }}
    source={require('../../assets/icons/next.png')}
  />
)


================================================
FILE: src/components/Icons/Next.test.js
================================================
import React from 'react'
import renderer from 'react-test-renderer'
import { Next } from './index'

describe('Icon component', () => {
  describe('Renders correctly', () => {
    test('it renders Default Next Icon', () => {
      const tree = renderer.create(
        <Next
          color="#FFFFFF"
        />
      ).toJSON()
      expect(tree).toMatchSnapshot()
    })
  })
})


================================================
FILE: src/components/Icons/__snapshots__/Empty.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Icon component Renders correctly it renders Default Empty Icon 1`] = `
<Image
  source={
    Object {
      "testUri": "../../../src/assets/icons/emptyBox.png",
    }
  }
  style={
    Object {
      "height": 40,
      "tintColor": "#FFFFFF",
      "width": 40,
    }
  }
/>
`;


================================================
FILE: src/components/Icons/__snapshots__/Next.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Icon component Renders correctly it renders Default Next Icon 1`] = `
<Image
  source={
    Object {
      "testUri": "../../../src/assets/icons/next.png",
    }
  }
  style={
    Object {
      "height": 25,
      "tintColor": "#FFFFFF",
      "width": 25,
    }
  }
/>
`;


================================================
FILE: src/components/Icons/index.js
================================================
import { Empty } from './Empty'
import { Next } from './Next'

export {
  Empty,
  Next
}


================================================
FILE: src/components/index.js
================================================
import Board from './Board/Board'
import BoardRepository from './lib/BoardRepository'

export {
  Board,
  BoardRepository
}


================================================
FILE: src/components/lib/BoardRepository.js
================================================
import { range } from 'lodash'
import Registry from './Registry'
import PositionCalculator from './PositionCalculator'
import Mover from './Mover'

class BoardRepository {
  constructor(data) {
    this.registry = new Registry(data)
    this.positionCalculator = new PositionCalculator()
    this.mover = new Mover(this.positionCalculator)
    this.listeners = {}
  }
  updateData(data) {
    this.registry.updateData(data);
    this.updateColumnsLayoutAfterVisibilityChanged();
  }
  columns = () => this.registry.columns();

  column = columnId => this.registry.column(columnId);

  items = columnId => this.registry.items(columnId);

  visibleItems = columnId => this.registry.visibleItems(columnId);

  addListener = (columnId, event, callback) => {
    const forColumn = this.listeners[columnId]
    this.listeners[columnId] = Object.assign(forColumn || {}, {
      [event]: callback
    })
  };

  notify = (columnId, event) => this.listeners[columnId][event]();

  setScrollOffset = (columnId, scrollOffset) => {
    const column = this.registry.column(columnId)
    column.setScrollOffset(scrollOffset)
  };

  setContentHeight = (columnId, contentHeight) => {
    const column = this.registry.column(columnId)
    column.setContentHeight(contentHeight)
  };

  setItemRef = (columnId, item, ref) => {
    item.setRef(ref)
  };

  setListView = (columnId, listView) => {
    const column = this.registry.column(columnId)

    return column && column.setListView(listView)
  };

  updateItemWithLayout = (columnId, item, previousItem) => {
    item.measureAndSaveLayout(previousItem)
  };

  updateLayoutAfterVisibilityChanged = columnId => {
    const items = this.items(columnId)
    const rangeArr = range(items.length)

    rangeArr.forEach(i => {
      this.updateItemWithLayout(columnId, items[i], items[i - 1])
    })
  };

  updateItemsVisibility = (columnId, visibleItemsInSections) => {
    const visibleItems = visibleItemsInSections.s1
    const items = this.items(columnId)

    this.updateLayoutAfterVisibilityChanged(columnId)

    return items.forEach(
      item => visibleItems && item.setVisible(visibleItems[item.index()]),
    )
  };

  setColumnRef = (columnId, ref) => {
    const column = this.registry.column(columnId)

    return column && column.setRef(ref)
  };

  updateColumnWithLayout = columnId => {
    const column = this.registry.column(columnId)

    return column && column.measureAndSaveLayout()
  };

  scrollingPosition = (columnAtPosition, x, y, columnId) => {
    this.positionCalculator.scrollingPosition(columnAtPosition, x, y, columnId);
  }


  updateColumnsLayoutAfterVisibilityChanged = () => {
    const columns = this.columns()

    return columns.forEach(column => {
      const columnId = column.id()
      this.updateColumnWithLayout(columnId)
      this.updateLayoutAfterVisibilityChanged(columnId)
    })
  };

  hide = (columnId, item) => {
    item.setHidden(true)
  };

  show = (columnId, item) => {
    item.setHidden(false)
  };

  showAll = () => {
    const columns = this.columns()
    columns.forEach(column => {
      const items = this.items(column.id())

      return items.forEach(item => this.show(column.id(), item))
    })
  };

  move = (draggedItem, x, y, columnId) => {
    this.mover.move(this, this.registry, draggedItem, x, y, columnId);
  }
}

export default BoardRepository


================================================
FILE: src/components/lib/ColumnItem.js
================================================
import {
  filter,
  omit,
  sortBy,
  values
} from 'lodash'
import Item from './Item'

class ColumnItem {
  constructor(attributes) {
    this.attributes = attributes
  }

  attributes = () => this.attributes

  item = itemId => this.attributes.items[itemId]

  data = () => this.attributes.data

  items = () => {
    const items = values(this.attributes.items)
    const fake = new Item({
      id: -2,
      index: 100000,
      columnId: this.id(),
      row: { id: -2, name: '' },
      hidden: true,
      locked: false,
      visible: false
    })

    return sortBy(items, item => item.index()).concat([fake])
  }

  visibleItems = columnId => filter(this.items(columnId), item => item.isVisible())

  scrollOffset = () => this.attributes.scrollOffset

  contentHeight = () => this.attributes.contentHeight

  id = () => this.attributes.id

  ref = () => this.attributes.ref

  index = () => this.attributes.index

  layout = () => this.attributes.layout

  listView = () => this.attributes.listView

  setListView = listView => (
    this.attributes.listView = listView
  )

  setScrollOffset = scrollOffset => (
    this.attributes.scrollOffset = scrollOffset
  )

  setContentHeight = contentHeight => (
    this.attributes.contentHeight = contentHeight
  )

  setRef = ref => (
    this.attributes.ref = ref
  )

  setLayout = layout => (
    this.attributes.layout = layout
  )

  measureAndSaveLayout = () => {
    const ref = this.ref()

    const measure = ref && ref.measure((ox, oy, width, height, px, py) => {
      const layout = { x: px, y: py, width, height }
      this.setLayout(layout)

    })

    return measure
  }

  setItem = (item) => {
    this.attributes.items[item.id()] = item
    item.setColumnId(this.id())
  }

  removeItem = item => (
    this.attributes.items = omit(this.attributes.items, item.id())
  )

  updateLastItemVisibility = () => {
    const visibleItems = this.visibleItems()
    const items = this.items()

    if (visibleItems.length + 1 < items.length) {
      visibleItems[visibleItems.length - 1].setVisible(false)
    }
  }
}

export default ColumnItem


================================================
FILE: src/components/lib/Item.js
================================================
class Item {
  constructor(attributes) {
    this.attributes = attributes
  }

  attributes = () => this.attributes

  ref = () => this.attributes.ref

  id = () => this.attributes.id

  row = () => this.attributes.row

  index = () => this.attributes.index

  layout = () => this.attributes.layout

  columnId = () => this.attributes.columnId

  isVisible = () => this.attributes.visible

  isHidden = () => this.attributes.hidden

  isLocked = () => this.attributes.locked

  setHidden = hidden => (
    this.attributes.hidden = hidden
  )

  setRef = ref => (
    this.attributes.ref = ref
  )

  setLayout = layout => (
    this.attributes.layout = layout
  )

  setVisible = visible => (
    this.attributes.visible = visible
  )

  setColumnId = columnId => (
    this.attributes.columnId = columnId
  )

  setIndex = index => (
    this.attributes.index = index
  )

  measureAndSaveLayout = (previousItem) => {
    const ref = this.attributes.ref
    const measure = ref && ref.measure((fx, fy, width, height, px, py) => {
      const layout = { x: px, y: py, width, height }
      this.setLayout(layout)
      if (!this.isVisible() && layout.x && layout.y && layout.width && layout.height) {
        this.setVisible(true)
      } else if (this.isVisible() && !layout.x && !layout.y && !layout.width && !layout.height) {
        this.setVisible(false)
      }
      if (this.isLocked()) {
        this.setVisible(false)
      }
      if (previousItem && previousItem.layout().y > layout.y) {
        this.setVisible(false)
      }
    })

    return measure
  }
}

export default Item


================================================
FILE: src/components/lib/Mover.js
================================================
/* eslint-disable no-unused-vars */
import { findIndex, range } from 'lodash'

class Mover {
  constructor(positionCalculator) {
    this.positionCalculator = positionCalculator
  }

  move = (boardRepository, registry, draggedItem, x, y, columnId) => {
   try {
    const fromColumnId = draggedItem.columnId()
    const columns = boardRepository.columns()
    const columnAtPosition = this.positionCalculator.columnAtPosition(columns, columnId)

    if (!columnAtPosition) {
      return
    }

    const toColumnId = columnId + 1
    if (toColumnId !== fromColumnId) {
      this.moveToOtherColumn(boardRepository, registry, fromColumnId, toColumnId, draggedItem)
    }

    const items = boardRepository.visibleItems(toColumnId)
    const itemAtPosition = this.positionCalculator
      .itemAtPosition(items, toColumnId, y, draggedItem)
    if (!itemAtPosition) {
      return columnAtPosition
    }

    const draggedId = draggedItem.id()
    const itemAtPositionId = itemAtPosition.id()

    if (draggedItem.id() === itemAtPosition.id()) {
      return columnAtPosition
    }

    this.switchItemsBetween(boardRepository, draggedItem, itemAtPosition, toColumnId)

    return columnAtPosition
     
   } catch (error) {
     console.log("move ",error)
   }
  }

  moveToOtherColumn = (boardRepository, registry, fromColumnId, toColumnId, item) => {
    registry.move(fromColumnId, toColumnId, item)

    boardRepository.notify(fromColumnId, 'reload')

    item.setVisible(true)
    item.setIndex(-1)

    const items = boardRepository.items(toColumnId)
    items.forEach(i => i.setIndex(i.index() + 1))

    const visibleItems = boardRepository.visibleItems(toColumnId)
    const rangeVisibleItems = range(0, visibleItems.length - 1)

    rangeVisibleItems
      .forEach(i => visibleItems[i].setLayout({ ...visibleItems[i + 1].layout() }))

    const lastItem = visibleItems[visibleItems.length - 1]
    const lastLayout = lastItem.layout()
    const newLastY = lastLayout.y + lastLayout.height
    lastItem.setLayout(Object.assign(lastLayout, { y: newLastY }))

    const column = registry.column(toColumnId)
    column.updateLastItemVisibility()
  }

  switchItemsBetween = (boardRepository, draggedItem, itemAtPosition, toColumnId) => {
    draggedItem.setVisible(true)

    let items = boardRepository.visibleItems(toColumnId)
    const draggedItemI = (items).findIndex(item => item.id() === draggedItem.id())
    const itemAtPositionI = (items).findIndex(item => item.id() === itemAtPosition.id())
    let itemsRange
    if (draggedItem.index() < itemAtPosition.index()) {
      itemsRange = range(draggedItemI, itemAtPositionI)
    } else {
      itemsRange = range(itemAtPositionI, draggedItemI)
    }

    itemsRange.forEach((i) => {
      const firstItem = items[i]
      const secondItem = items[i + 1]
      this.switchItems(toColumnId, firstItem, secondItem)
      items = boardRepository.visibleItems(toColumnId)
    })

    boardRepository.notify(toColumnId, 'reload')
  }

  switchItems = (columnId, firstItem, secondItem) => {
    if (!firstItem || !secondItem) {
      return
    }

    const firstId = firstItem.id()
    const secondId = secondItem.id()
    const firstIndex = firstItem.index()
    const secondIndex = secondItem.index()
    const firstY = firstItem.layout().y
    const secondHeight = secondItem.layout().height
    const firstRef = firstItem.ref()
    const secondRef = secondItem.ref()

    firstItem.setIndex(secondIndex)
    secondItem.setIndex(firstIndex)

    firstItem.setLayout(Object.assign(firstItem.layout(), { y: firstY + secondHeight }))
    secondItem.setLayout(Object.assign(secondItem.layout(), { y: firstY }))

    firstItem.setRef(secondRef)
    secondItem.setRef(firstRef)
  }
}

export default Mover


================================================
FILE: src/components/lib/PositionCalculator.js
================================================
class PositionCalculator {
  TRESHOLD = 100

  columnAtPosition = (columns, columnId) => {
    const column = columns.find((col, index) => (
      index === columnId
    ))

    return column
  }

  scrollingPosition = (column, x, y) => {

    const layout = column.layout()
    if (layout !== undefined) {
      const upperEnd = layout.y
      const upper = y > upperEnd - this.TRESHOLD && y < upperEnd + this.TRESHOLD

      const lowerEnd = layout.y + layout.height
      const lower = y > lowerEnd - this.TRESHOLD && y < lowerEnd + this.TRESHOLD

      const offset = lower ? 1 : (upper ? -1 : 0)

      return {
        offset,
        scrolling: (lower || upper)
      }
    }else {
      console.log("scrollingPosition", layout)
    }
  }

  selectItem = (y, draggedItem, item) => {
    const layout = item.layout()
    const heightDiff = Math.abs(draggedItem.layout().height - layout.height)

    let up; let
      down
    if (heightDiff > layout.height) {
      up = y > layout.y
      down = y < layout.y + layout.height
    } else if (y < draggedItem.layout().y) {
      down = y < layout.y + layout.height - heightDiff
      up = y > layout.y
    } else {
      down = y < layout.y + layout.height
      up = y > layout.y + heightDiff
    }

    return layout && up && down
  }

  itemAtPosition = (items, columnId, y, draggedItem) => {
    let item = items.find(i => this.selectItem(y, draggedItem, i))

    const firstItem = items[0]
    if (!item && firstItem && firstItem.layout() && y <= firstItem.layout().y) {
      item = firstItem
    }

    const lastItem = items[items.length - 1]
    if (!item && lastItem && lastItem.layout() && y >= lastItem.layout().y) {
      item = lastItem
    }

    return item
  }
}

export default PositionCalculator


================================================
FILE: src/components/lib/Registry.js
================================================
import {
  range, sortBy, values
} from 'lodash'
import ColumnItem from './ColumnItem'
import Item from './Item'

class Registry {
  constructor(data) {
    this.map = {}
    if (data) {
      this.updateData(data)
    }
  }

  existingColumnAttributes = columnId => {
    const column = this.column(columnId)

    return column && column.attributes()
  };

  buildColumn = (columnIndex, columnData) => {
    const columnId = columnData.id
    const existingAttributes = this.existingColumnAttributes(columnId) || {
      id: columnId,
      index: columnIndex,
      scrollOffset: 0,
      items: {}
    }
    const { rows } = columnData
    const itemsMap = this.buildItemsMap(
      columnId,
      rows,
      existingAttributes.items,
    )

    const colItem = new ColumnItem(
      Object.assign(existingAttributes, {
        items: itemsMap,
        data: columnData
      }),
    )
    colItem.measureAndSaveLayout()
    return colItem;
  };

  existingItemAttributes = (existingItems, itemId) => {
    const item = existingItems[itemId]

    return item && item.attributes()
  };

  buildItemsMap = (columnId, rows, existingItems) => {
    const items = range(rows.length).map(index => {
      const row = rows[index]
      const { id } = row
      const existingItemAttributes =
        this.existingItemAttributes(existingItems, id) || {};
      const itemDef = new Item(
        Object.assign(existingItemAttributes, {
          id,
          index,
          columnId,
          row,
        }));

      return itemDef;
    })

    const itemsMap = {}
    items.forEach((item, index) => {
      item.measureAndSaveLayout({});
      itemsMap[item.id()] = item
    })

    return itemsMap
  };

  updateData = data => {
    this.map = {};
    const columns = range(data.length).map(columnIndex => {
      const columnData = data[columnIndex]

      return this.buildColumn(columnIndex, columnData)
    })

    columns.forEach(column => {
      this.map[column.id()] = column
    })
  };

  move = (fromColumnId, toColumnId, item) => {
    const fromColumn = this.column(fromColumnId)
    const toColumn = this.column(toColumnId)

    toColumn.setItem(item)
    fromColumn.removeItem(item)
  };

  columns = () => {
    const columns = values(this.map)
    return sortBy(columns, column => column.index())
  };

  column = columnId => this.map[columnId];

  items = columnId => {
    const column = this.column(columnId)

    return (column && column.items()) || []
  };

  visibleItems = columnId => {
    const column = this.column(columnId)

    return (column && column.visibleItems()) || []
  };

  item = (columnId, itemId) => {
    const column = this.column(columnId)

    return column && column.item(itemId)
  };
}

export default Registry


================================================
FILE: src/constants/colors.js
================================================
const colors = {
  bay: '#747d8c',
  black: '#000000',
  blurple: '#4834d4',
  deepComaru: '#30336b',
  exodusFruit: '#686de0',
  fallingStar: '#FAFAFA',
  officer: '#2C3A47',
  wildWatermelon: '#ff6b81',
  white: '#FFFFFF'
}

export { colors }


================================================
FILE: src/constants/deviceHelpers.js
================================================
import {
  Dimensions,
  Platform
} from 'react-native'

const deviceHeight = Dimensions.get('window').height

const deviceWidth = Dimensions.get('window').width

const ios = Platform.OS === 'ios'

const isX = () => Platform.OS === 'ios' && !Platform.isPad && !Platform.isTVOS
    && Dimensions.get('window').height > 800

export {
  deviceHeight,
  deviceWidth,
  ios,
  isX
}


================================================
FILE: src/constants/index.js
================================================
export * from './colors'
export * from './deviceHelpers'
Download .txt
gitextract_8mqcp5my/

├── .gitignore
├── README.md
├── jest/
│   └── config.json
├── package.json
└── src/
    ├── components/
    │   ├── Board/
    │   │   ├── Board.js
    │   │   └── Board.styled.js
    │   ├── Card/
    │   │   ├── Card.js
    │   │   └── Card.styled.js
    │   ├── Carousel/
    │   │   ├── Carousel.js
    │   │   └── Carousel.styled.js
    │   ├── Column/
    │   │   ├── Column.js
    │   │   └── Column.styled.js
    │   ├── EmptyColumn/
    │   │   ├── EmptyColumn.js
    │   │   ├── EmptyColumn.styled.js
    │   │   ├── EmptyColumn.test.js
    │   │   └── __snapshots__/
    │   │       └── EmptyColumn.test.js.snap
    │   ├── Icons/
    │   │   ├── Empty.js
    │   │   ├── Empty.test.js
    │   │   ├── Next.js
    │   │   ├── Next.test.js
    │   │   ├── __snapshots__/
    │   │   │   ├── Empty.test.js.snap
    │   │   │   └── Next.test.js.snap
    │   │   └── index.js
    │   ├── index.js
    │   └── lib/
    │       ├── BoardRepository.js
    │       ├── ColumnItem.js
    │       ├── Item.js
    │       ├── Mover.js
    │       ├── PositionCalculator.js
    │       └── Registry.js
    └── constants/
        ├── colors.js
        ├── deviceHelpers.js
        └── index.js
Download .txt
SYMBOL INDEX (35 symbols across 9 files)

FILE: src/components/Board/Board.js
  constant MAX_RANGE (line 24) | const MAX_RANGE = 100
  constant MAX_DEG (line 25) | const MAX_DEG = 30
  constant CARD_WIDTH (line 26) | let CARD_WIDTH = 0.85 * deviceWidth
  constant STATUSBAR_HEIGHT (line 27) | const STATUSBAR_HEIGHT = ios ? (isX() ? 44 : 20) : StatusBar.currentHeight
  class Board (line 29) | class Board extends React.Component {
    method constructor (line 30) | constructor(props) {
    method componentDidMount (line 55) | componentDidMount() {
    method componentWillUnmount (line 62) | componentWillUnmount() {
    method render (line 334) | render() {

FILE: src/components/Carousel/Carousel.js
  constant INITIAL_ACTIVE_ITEM (line 12) | const INITIAL_ACTIVE_ITEM = 0
  class Carousel (line 14) | class Carousel extends Component {
    method constructor (line 15) | constructor(props) {
    method componentDidMount (line 28) | componentDidMount() {
    method componentWillUnmount (line 39) | componentWillUnmount() {
    method currentIndex (line 64) | get currentIndex() {
    method render (line 318) | render() {

FILE: src/components/Column/Column.js
  constant COLUMN_WIDTH (line 26) | const COLUMN_WIDTH = 0.85 * deviceWidth
  constant PADDING (line 27) | const PADDING = 32
  constant ONE_COLUMN_WIDTH (line 28) | const ONE_COLUMN_WIDTH = deviceWidth - PADDING
  class Column (line 30) | class Column extends React.Component {
    method constructor (line 31) | constructor(props) {
    method componentDidMount (line 35) | componentDidMount() {
    method render (line 160) | render() {

FILE: src/components/lib/BoardRepository.js
  class BoardRepository (line 6) | class BoardRepository {
    method constructor (line 7) | constructor(data) {
    method updateData (line 13) | updateData(data) {

FILE: src/components/lib/ColumnItem.js
  class ColumnItem (line 9) | class ColumnItem {
    method constructor (line 10) | constructor(attributes) {

FILE: src/components/lib/Item.js
  class Item (line 1) | class Item {
    method constructor (line 2) | constructor(attributes) {

FILE: src/components/lib/Mover.js
  class Mover (line 4) | class Mover {
    method constructor (line 5) | constructor(positionCalculator) {

FILE: src/components/lib/PositionCalculator.js
  class PositionCalculator (line 1) | class PositionCalculator {

FILE: src/components/lib/Registry.js
  class Registry (line 7) | class Registry {
    method constructor (line 8) | constructor(data) {
Condensed preview — 33 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (66K chars).
[
  {
    "path": ".gitignore",
    "chars": 47,
    "preview": "npm-debug.log\nnode_modules/\n.DS_Store\nyarn.lock"
  },
  {
    "path": "README.md",
    "chars": 7316,
    "preview": "<div align=\"center\">\n  <image align=\"center\" src=\"./src/assets/images/header.png\"/>\n  <image align=\"center\" src=\"https:/"
  },
  {
    "path": "jest/config.json",
    "chars": 301,
    "preview": "{\n    \"preset\": \"react-native\",\n    \"rootDir\": \"../\",\n    \"transform\": {\n        \"^.+\\\\.js$\": \"./node_modules/react-nati"
  },
  {
    "path": "package.json",
    "chars": 1370,
    "preview": "{\n  \"name\": \"react-native-draganddrop-board\",\n  \"version\": \"1.0.5\",\n  \"description\": \"Drag and drop elements inside caro"
  },
  {
    "path": "src/components/Board/Board.js",
    "chars": 10377,
    "preview": "import React from 'react'\nimport ReactTimeout from 'react-timeout'\nimport {\n  Animated,\n  PanResponder,\n  StatusBar\n} fr"
  },
  {
    "path": "src/components/Board/Board.styled.js",
    "chars": 108,
    "preview": "import styled from 'styled-components/native'\n\nconst BoardWrapper = styled.View`\n`\n\nexport { BoardWrapper }\n"
  },
  {
    "path": "src/components/Card/Card.js",
    "chars": 3085,
    "preview": "import React from 'react'\nimport { Animated } from 'react-native'\nimport {\n  bool,\n  func,\n  number,\n  object,\n  shape,\n"
  },
  {
    "path": "src/components/Card/Card.styled.js",
    "chars": 899,
    "preview": "import styled from 'styled-components/native'\nimport {\n  borderRadius,\n  color,\n  fontFamily,\n  fontSize\n} from 'styled-"
  },
  {
    "path": "src/components/Carousel/Carousel.js",
    "chars": 8398,
    "preview": "import React, { Component } from 'react'\nimport { ScrollView } from 'react-native'\nimport {\n  array,\n  bool,\n  func,\n  n"
  },
  {
    "path": "src/components/Carousel/Carousel.styled.js",
    "chars": 106,
    "preview": "import styled from 'styled-components/native'\n\nconst ItemWrapper = styled.View`\n`\n\nexport { ItemWrapper }\n"
  },
  {
    "path": "src/components/Column/Column.js",
    "chars": 8047,
    "preview": "import React from 'react'\nimport { FlatList } from 'react-native'\nimport {\n  bool,\n  func,\n  object,\n  number,\n  string\n"
  },
  {
    "path": "src/components/Column/Column.styled.js",
    "chars": 912,
    "preview": "import styled from 'styled-components/native'\nimport {\n  borderRadius,\n  color,\n  fontFamily,\n  fontSize,\n  marginRight,"
  },
  {
    "path": "src/components/EmptyColumn/EmptyColumn.js",
    "chars": 1067,
    "preview": "import React from 'react'\nimport {\n  number,\n  string\n} from 'prop-types'\nimport {\n  colors,\n  fonts\n} from '../../const"
  },
  {
    "path": "src/components/EmptyColumn/EmptyColumn.styled.js",
    "chars": 315,
    "preview": "import styled from 'styled-components/native'\nimport {\n  color,\n  fontFamily,\n  fontSize\n} from 'styled-system'\n\nconst E"
  },
  {
    "path": "src/components/EmptyColumn/EmptyColumn.test.js",
    "chars": 387,
    "preview": "import React from 'react'\nimport renderer from 'react-test-renderer'\nimport EmptyColumn from './EmptyColumn'\n\ndescribe('"
  },
  {
    "path": "src/components/EmptyColumn/__snapshots__/EmptyColumn.test.js.snap",
    "chars": 740,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`EmptyColumn component Renders correctly it renders Default EmptyCol"
  },
  {
    "path": "src/components/Icons/Empty.js",
    "chars": 254,
    "preview": "import React from 'react'\nimport { Image } from 'react-native'\n\nexport const Empty = (color) => (\n  <Image\n    style={{\n"
  },
  {
    "path": "src/components/Icons/Empty.test.js",
    "chars": 384,
    "preview": "import React from 'react'\nimport renderer from 'react-test-renderer'\nimport { Empty } from './index'\n\ndescribe('Icon com"
  },
  {
    "path": "src/components/Icons/Next.js",
    "chars": 249,
    "preview": "import React from 'react'\nimport { Image } from 'react-native'\n\nexport const Next = (color) => (\n  <Image\n    style={{\n "
  },
  {
    "path": "src/components/Icons/Next.test.js",
    "chars": 381,
    "preview": "import React from 'react'\nimport renderer from 'react-test-renderer'\nimport { Next } from './index'\n\ndescribe('Icon comp"
  },
  {
    "path": "src/components/Icons/__snapshots__/Empty.test.js.snap",
    "chars": 332,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Icon component Renders correctly it renders Default Empty Icon 1`] "
  },
  {
    "path": "src/components/Icons/__snapshots__/Next.test.js.snap",
    "chars": 327,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Icon component Renders correctly it renders Default Next Icon 1`] ="
  },
  {
    "path": "src/components/Icons/index.js",
    "chars": 90,
    "preview": "import { Empty } from './Empty'\nimport { Next } from './Next'\n\nexport {\n  Empty,\n  Next\n}\n"
  },
  {
    "path": "src/components/index.js",
    "chars": 125,
    "preview": "import Board from './Board/Board'\nimport BoardRepository from './lib/BoardRepository'\n\nexport {\n  Board,\n  BoardReposito"
  },
  {
    "path": "src/components/lib/BoardRepository.js",
    "chars": 3361,
    "preview": "import { range } from 'lodash'\nimport Registry from './Registry'\nimport PositionCalculator from './PositionCalculator'\ni"
  },
  {
    "path": "src/components/lib/ColumnItem.js",
    "chars": 2113,
    "preview": "import {\n  filter,\n  omit,\n  sortBy,\n  values\n} from 'lodash'\nimport Item from './Item'\n\nclass ColumnItem {\n  constructo"
  },
  {
    "path": "src/components/lib/Item.js",
    "chars": 1593,
    "preview": "class Item {\n  constructor(attributes) {\n    this.attributes = attributes\n  }\n\n  attributes = () => this.attributes\n\n  r"
  },
  {
    "path": "src/components/lib/Mover.js",
    "chars": 3761,
    "preview": "/* eslint-disable no-unused-vars */\nimport { findIndex, range } from 'lodash'\n\nclass Mover {\n  constructor(positionCalcu"
  },
  {
    "path": "src/components/lib/PositionCalculator.js",
    "chars": 1769,
    "preview": "class PositionCalculator {\n  TRESHOLD = 100\n\n  columnAtPosition = (columns, columnId) => {\n    const column = columns.fi"
  },
  {
    "path": "src/components/lib/Registry.js",
    "chars": 2761,
    "preview": "import {\n  range, sortBy, values\n} from 'lodash'\nimport ColumnItem from './ColumnItem'\nimport Item from './Item'\n\nclass "
  },
  {
    "path": "src/constants/colors.js",
    "chars": 245,
    "preview": "const colors = {\n  bay: '#747d8c',\n  black: '#000000',\n  blurple: '#4834d4',\n  deepComaru: '#30336b',\n  exodusFruit: '#6"
  },
  {
    "path": "src/constants/deviceHelpers.js",
    "chars": 378,
    "preview": "import {\n  Dimensions,\n  Platform\n} from 'react-native'\n\nconst deviceHeight = Dimensions.get('window').height\n\nconst dev"
  },
  {
    "path": "src/constants/index.js",
    "chars": 57,
    "preview": "export * from './colors'\nexport * from './deviceHelpers'\n"
  }
]

About this extraction

This page contains the full source code of the bear-junior/react-native-draganddrop-board GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 33 files (60.2 KB), approximately 16.4k tokens, and a symbol index with 35 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!