├── .watchmanconfig
├── src
├── tableTop.png
├── button.js
├── graphic.js
├── introContent.js
└── inputOverlay.js
├── .babelrc
├── README.md
├── App.test.js
├── app.json
├── .gitignore
├── package.json
├── App.js
└── .flowconfig
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/src/tableTop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atticoos/react-native-todo-empty-state-transition/HEAD/src/tableTop.png
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"],
3 | "env": {
4 | "development": {
5 | "plugins": ["transform-react-jsx-source"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Dribble Inspired Todo Empty State UI+UX
2 |
3 | https://dribbble.com/shots/4282122-Empty-State
4 |
5 | https://expo.io/@atticoos/todoemptystate
6 |
7 |
8 |
--------------------------------------------------------------------------------
/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import App from './App';
3 |
4 | import renderer from 'react-test-renderer';
5 |
6 | it('renders without crashing', () => {
7 | const rendered = renderer.create().toJSON();
8 | expect(rendered).toBeTruthy();
9 | });
10 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "Todo Empty State Transition",
4 | "slug": "todoemptystate",
5 | "privacy": "public",
6 | "description": "An inspired UI+UX from https://dribbble.com/shots/4282122-Empty-State",
7 | "sdkVersion": "25.0.0"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # expo
4 | .expo/
5 |
6 | # dependencies
7 | /node_modules
8 |
9 | # misc
10 | .env.local
11 | .env.development.local
12 | .env.test.local
13 | .env.production.local
14 |
15 | npm-debug.log*
16 | yarn-debug.log*
17 | yarn-error.log*
18 |
--------------------------------------------------------------------------------
/src/button.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import glamorous from 'glamorous-native'
3 |
4 | export default function Button ({children, ...props}) {
5 | return (
6 |
7 | {children}
8 |
9 | )
10 | }
11 |
12 | const Touchable = glamorous.touchableHighlight({
13 | backgroundColor: 'rgb(253, 183, 58)',
14 | paddingVertical: 10,
15 | paddingHorizontal: 20,
16 | borderRadius: 6
17 | });
18 | Touchable.propsAreStyleOverrides = true;
19 |
20 | const Text = glamorous.text({
21 | color: '#fff',
22 | fontSize: 16
23 | });
24 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "TodoEmptyState",
3 | "version": "0.1.0",
4 | "private": true,
5 | "devDependencies": {
6 | "jest-expo": "25.0.0",
7 | "react-native-scripts": "1.11.1",
8 | "react-test-renderer": "16.2.0"
9 | },
10 | "main": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
11 | "scripts": {
12 | "start": "react-native-scripts start",
13 | "eject": "react-native-scripts eject",
14 | "android": "react-native-scripts android",
15 | "ios": "react-native-scripts ios",
16 | "test": "node node_modules/jest/bin/jest.js"
17 | },
18 | "jest": {
19 | "preset": "jest-expo"
20 | },
21 | "dependencies": {
22 | "expo": "^25.0.0",
23 | "glamorous-native": "^1.3.0",
24 | "react": "16.2.0",
25 | "react-native": "0.52.0",
26 | "recompose": "^0.26.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/graphic.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import glamorous from 'glamorous-native'
3 | import {Animated} from 'react-native'
4 | import {withProps} from 'recompose';
5 |
6 | export default function Graphic (props) {
7 | return (
8 |
9 |
10 |
11 | )
12 | }
13 |
14 | const Transition = glamorous(Animated.View)(
15 | {
16 | position: 'absolute',
17 | top: 120,
18 | },
19 | ({transition}) => ({
20 | transform: [
21 | // {perspective: 1000},
22 | {
23 | translateY: transition.interpolate({
24 | inputRange: [0, 1],
25 | outputRange: [0, -20]
26 | })
27 | },
28 | {
29 | scale: transition.interpolate({
30 | inputRange: [0, 1],
31 | outputRange: [1, 6]
32 | })
33 | },
34 | {rotate: transition.interpolate({
35 | inputRange: [0, 1],
36 | outputRange: ['0deg', '-20deg']
37 | })
38 | }
39 | ]
40 | })
41 | );
42 |
43 | const TableTop = withProps({
44 | source: require('./tableTop.png'),
45 | resizeMode: 'contain'
46 | })(glamorous.image({
47 | width: 215,
48 | height: 200
49 | }));
50 |
--------------------------------------------------------------------------------
/src/introContent.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import glamorous from 'glamorous-native'
3 | import {Animated} from 'react-native'
4 | import Button from './button';
5 |
6 | export default function IntroContent ({onAddNote, ...props}) {
7 | return (
8 |
9 | Add your first note
10 |
11 |
12 | Relax and write something{`\n`}beautiful
13 |
14 |
15 |
18 |
19 |
20 | UI by @nimasha_perera{`\n`}UX by Edoardo Mercati
21 |
22 |
23 | )
24 | }
25 |
26 | const Transition = glamorous(Animated.View)(
27 | {
28 | position: 'absolute',
29 | alignItems: 'center',
30 | top: 350
31 | },
32 | ({transition}) => ({
33 | transform: [
34 | {
35 | translateY: transition.interpolate({
36 | inputRange: [0, 1],
37 | outputRange: [0, 600]
38 | })
39 | }
40 | ]
41 | })
42 | )
43 |
44 | const PrimaryText = glamorous.text({
45 | fontSize: 16,
46 | color: '#0f0f0f',
47 | fontWeight: 'bold',
48 | marginVertical: 6
49 | });
50 |
51 | const SecondaryText = glamorous.text({
52 | fontSize: 16,
53 | lineHeight: 24,
54 | textAlign: 'center',
55 | color: 'lightgray'
56 | })
57 | SecondaryText.propsAreStyleOverrides = true;
58 |
--------------------------------------------------------------------------------
/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Animated, Easing, StyleSheet, Text, TextInput, View } from 'react-native';
3 | import InputOverlay from './src/inputOverlay';
4 | import Graphic from './src/graphic';
5 | import IntroContent from './src/introContent';
6 |
7 | export default class App extends React.Component {
8 | state = {
9 | transition: new Animated.Value(0),
10 | inputActive: false
11 | }
12 |
13 | transitionToInput() {
14 | this.setState({inputActive: true})
15 | Animated.timing(
16 | this.state.transition,
17 | {
18 | toValue: 1,
19 | easing: Easing.in(Easing.exp),
20 | userNativeDriver: true
21 | }
22 | ).start();
23 | }
24 |
25 | transitionToSplash() {
26 | Animated.timing(
27 | this.state.transition,
28 | {toValue: 0, userNativeDriver: true}
29 | ).start(() => this.setState({inputActive: false}));
30 | }
31 |
32 | render() {
33 | return (
34 |
35 |
38 |
39 | this.transitionToInput()}
42 | />
43 |
44 | {this.state.inputActive && (
45 | this.transitionToSplash()}
49 | />
50 | )}
51 |
52 | );
53 | }
54 | }
55 |
56 | const styles = StyleSheet.create({
57 | container: {
58 | flex: 1,
59 | paddingTop: 40,
60 | backgroundColor: '#fff',
61 | alignItems: 'center'
62 | },
63 | });
64 |
--------------------------------------------------------------------------------
/src/inputOverlay.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import glamorous from 'glamorous-native'
3 | import {Animated, Keyboard} from 'react-native'
4 |
5 | export default function InputOverlay ({onDismiss, ...props}) {
6 | return (
7 |
8 |
9 | H1
10 |
11 | {
12 | Keyboard.dismiss();
13 | onDismiss();
14 | }} />
15 |
16 |
17 | )
18 | }
19 |
20 | function Dismiss (props) {
21 | return (
22 |
34 |
35 | X
36 |
37 |
38 | )
39 | }
40 |
41 | const TextInput = glamorous.textInput({
42 | color: 'gray',
43 | flex: 1,
44 | marginHorizontal: 5,
45 | fontSize: 22
46 | })
47 |
48 | const TransitionContainer = glamorous(Animated.View)(
49 | {
50 | position: 'absolute',
51 | backgroundColor: '#fff',
52 | paddingTop: 60,
53 | top: 0,
54 | left: 0,
55 | right: 0,
56 | bottom: 0,
57 | paddingHorizontal: 30
58 | },
59 | ({transition, enter}) => ({
60 | opacity: transition.interpolate({
61 | inputRange: [0, enter, 1],
62 | outputRange: [0, 0, 1]
63 | }),
64 | transform: [
65 | {
66 | translateY: transition.interpolate({
67 | inputRange: [0, enter, 1],
68 | outputRange: [35, 35, 0]
69 | })
70 | },
71 | {
72 | rotate: transition.interpolate({
73 | inputRange: [0, 1],
74 | outputRange: ['25deg', '0deg']
75 | })
76 | }
77 | ]
78 | })
79 | );
80 |
81 | const Row = glamorous.view({
82 | flexDirection: 'row'
83 | })
84 |
85 | const InputType = glamorous.text({
86 | fontSize: 16,
87 | marginTop: 2,
88 | color: 'lightgray'
89 | })
90 |
--------------------------------------------------------------------------------
/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 | ; We fork some components by platform
3 | .*/*[.]android.js
4 |
5 | ; Ignore templates for 'react-native init'
6 | /node_modules/react-native/local-cli/templates/.*
7 |
8 | ; Ignore RN jest
9 | /node_modules/react-native/jest/.*
10 |
11 | ; Ignore RNTester
12 | /node_modules/react-native/RNTester/.*
13 |
14 | ; Ignore the website subdir
15 | /node_modules/react-native/website/.*
16 |
17 | ; Ignore the Dangerfile
18 | /node_modules/react-native/danger/dangerfile.js
19 |
20 | ; Ignore Fbemitter
21 | /node_modules/fbemitter/.*
22 |
23 | ; Ignore "BUCK" generated dirs
24 | /node_modules/react-native/\.buckd/
25 |
26 | ; Ignore unexpected extra "@providesModule"
27 | .*/node_modules/.*/node_modules/fbjs/.*
28 |
29 | ; Ignore polyfills
30 | /node_modules/react-native/Libraries/polyfills/.*
31 |
32 | ; Ignore various node_modules
33 | /node_modules/react-native-gesture-handler/.*
34 | /node_modules/expo/.*
35 | /node_modules/react-navigation/.*
36 | /node_modules/xdl/.*
37 | /node_modules/reqwest/.*
38 | /node_modules/metro-bundler/.*
39 |
40 | [include]
41 |
42 | [libs]
43 | node_modules/react-native/Libraries/react-native/react-native-interface.js
44 | node_modules/react-native/flow/
45 | node_modules/expo/flow/
46 |
47 | [options]
48 | emoji=true
49 |
50 | module.system=haste
51 |
52 | module.file_ext=.js
53 | module.file_ext=.jsx
54 | module.file_ext=.json
55 | module.file_ext=.ios.js
56 |
57 | munge_underscores=true
58 |
59 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
60 |
61 | suppress_type=$FlowIssue
62 | suppress_type=$FlowFixMe
63 | suppress_type=$FlowFixMeProps
64 | suppress_type=$FlowFixMeState
65 | suppress_type=$FixMe
66 |
67 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)
68 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)?:? #[0-9]+
69 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
70 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
71 |
72 | unsafe.enable_getters_and_setters=true
73 |
74 | [version]
75 | ^0.56.0
76 |
--------------------------------------------------------------------------------