├── .watchmanconfig ├── App.js ├── .gitignore ├── src ├── assets │ └── icon.png ├── util │ └── index.js ├── components │ ├── Stories │ │ ├── Footer.vue │ │ ├── Story.vue │ │ ├── index.vue │ │ ├── Footer.js │ │ ├── index.js │ │ └── Story.js │ ├── Header │ │ ├── Left.vue │ │ └── Left.js │ └── TabView │ │ ├── Scroller.vue │ │ ├── index.vue │ │ ├── Scroller.js │ │ └── index.js ├── store │ ├── mutations.js │ ├── fetch.js │ ├── actions.js │ └── index.js ├── views │ ├── WebSite.vue │ ├── Home.vue │ ├── WebSite.js │ └── Home.js └── index.js ├── .babelrc ├── jsconfig.json ├── App.test.js ├── app.json ├── .eslintrc ├── package.json ├── .flowconfig └── README.md /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | export { default } from './src'; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | npm-debug.* 4 | -------------------------------------------------------------------------------- /src/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmallComfort/HackerNews/HEAD/src/assets/icon.png -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["babel-preset-expo"], 3 | "env": { 4 | "development": { 5 | "plugins": ["transform-react-jsx-source"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true 5 | }, 6 | "exclude": [ 7 | "node_modules" 8 | ] 9 | } -------------------------------------------------------------------------------- /src/util/index.js: -------------------------------------------------------------------------------- 1 | import { Dimensions } from 'react-native'; 2 | 3 | export const SCREEN_WIDTH = Dimensions.get('window').width; 4 | export const SCREEN_HEIGHT = Dimensions.get('window').height; 5 | 6 | export function emptyFn() {} -------------------------------------------------------------------------------- /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": "HackerNews", 4 | "description": "A mobile app for Hacker News, powered by Vue and React Native", 5 | "sdkVersion": "17.0.0", 6 | "icon": "./src/assets/icon.png", 7 | "loading": { 8 | "icon": "./src/assets/icon.png", 9 | "hideExponentText": true 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/components/Stories/Footer.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 20 | -------------------------------------------------------------------------------- /src/store/mutations.js: -------------------------------------------------------------------------------- 1 | 2 | export function SET_ACTIVE_TYPE (state, { type }) { 3 | state.activeType = type; 4 | } 5 | 6 | export function SET_LIST (state, { type, ids }) { 7 | state.lists[type] = ids; 8 | } 9 | 10 | export function SET_ITEMS (state, { items, type }) { 11 | state.items[type] = state.items[type] 12 | .concat(items) 13 | .reduce((acc, val) => { 14 | for (let i = 0, j = acc.length; i < j; i++) { 15 | if (acc[i].id === val.id) { 16 | return acc.splice(i, 1, val); 17 | } 18 | } 19 | acc.push(val); 20 | return acc; 21 | }, []); 22 | } 23 | -------------------------------------------------------------------------------- /src/store/fetch.js: -------------------------------------------------------------------------------- 1 | const baseURL = 'https://hacker-news.firebaseio.com/v0'; 2 | 3 | export function _fetch (path) { 4 | return fetch({ 5 | method: 'GET', 6 | url: `${baseURL}/${path}.json` 7 | }) 8 | .then((response) => response.json()); 9 | } 10 | 11 | export function fetchIdsByType (type) { 12 | return _fetch(`${type}stories`); 13 | } 14 | 15 | export function fetchItem (id) { 16 | return _fetch(`item/${id}`); 17 | } 18 | 19 | export function fetchItems (ids) { 20 | return Promise.all(ids.map(id => fetchItem(id))); 21 | } 22 | 23 | export function fetchUser (id) { 24 | return _fetch(`user/${id}`); 25 | } 26 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["eslint:recommended"], 3 | "parserOptions": { 4 | "ecmaVersion": 6, 5 | "sourceType": "module", 6 | "ecmaFeatures": { 7 | "experimentalObjectRestSpread": true, 8 | "jsx": true 9 | } 10 | }, 11 | "parser": "babel-eslint", 12 | "env": { 13 | "browser": true, 14 | "commonjs": true, 15 | "node": true, 16 | "es6": true 17 | }, 18 | "rules": { 19 | "no-console": 0, 20 | "quotes": [2, "single"], 21 | "strict": [2, "never"], 22 | "semi": [2, "always"], 23 | "react/jsx-uses-react": 2, 24 | "react/jsx-uses-vars": 2, 25 | "react/react-in-jsx-scope": 2 26 | }, 27 | "plugins": [ 28 | "react", 29 | "html" 30 | ] 31 | } -------------------------------------------------------------------------------- /src/views/WebSite.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 32 | 33 | 38 | -------------------------------------------------------------------------------- /src/components/Header/Left.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 21 | 22 | 40 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StatusBar, Platform } from 'react-native'; 3 | import { StackNavigator } from 'react-navigation'; 4 | import Home from './views/Home'; 5 | import WebSite from './views/WebSite'; 6 | import Left from './components/Header/Left'; 7 | 8 | Home.navigationOptions = { 9 | title: 'Hacker News', 10 | headerTitleStyle: { 11 | color: '#ff6600' 12 | } 13 | }; 14 | 15 | WebSite.navigationOptions = ({ navigation }) => { 16 | return { 17 | title: navigation.state.params.title, 18 | headerTitleStyle: { 19 | color: '#ff6600' 20 | }, 21 | headerLeft: 22 | }; 23 | }; 24 | 25 | export default StackNavigator({ 26 | Home: { screen: Home }, 27 | WebSite: { screen: WebSite } 28 | }, { 29 | cardStyle: { 30 | paddingTop: Platform.OS === 'ios' ? 0 : StatusBar.currentHeight 31 | } 32 | }); 33 | 34 | -------------------------------------------------------------------------------- /src/store/actions.js: -------------------------------------------------------------------------------- 1 | import { fetchItems, fetchIdsByType } from './fetch'; 2 | 3 | const LOAD_MORE_STEP = 10; 4 | 5 | // ensure data for rendering given list type 6 | export function FETCH_LIST_DATA ({ commit, dispatch }, { type }) { 7 | commit('SET_ACTIVE_TYPE', { type }); 8 | return fetchIdsByType(type) 9 | .then(ids => commit('SET_LIST', { type, ids })) 10 | .then(() => dispatch('ENSURE_ACTIVE_ITEMS')); 11 | } 12 | 13 | // load more items 14 | export function LOAD_MORE_ITEMS ({ dispatch, state }) { 15 | state.counts[state.activeType] += LOAD_MORE_STEP; 16 | return dispatch('ENSURE_ACTIVE_ITEMS'); 17 | } 18 | 19 | // ensure all active items are fetched 20 | export function ENSURE_ACTIVE_ITEMS ({ dispatch, getters }) { 21 | return dispatch('FETCH_ITEMS', { 22 | ids: getters.activeIds 23 | }); 24 | } 25 | 26 | export function FETCH_ITEMS ({ commit, state }, { ids }) { 27 | const type = state.activeType; 28 | return ids.length 29 | ? fetchItems(ids).then(items => commit('SET_ITEMS', { items, type })) 30 | : Promise.resolve(); 31 | } 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "HackerNews", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "babel-eslint": "^7.2.3", 7 | "eslint": "^4.0.0", 8 | "eslint-plugin-html": "^3.0.0", 9 | "eslint-plugin-react": "^7.1.0", 10 | "jest-expo": "~1.0.1", 11 | "react-native-scripts": "0.0.31", 12 | "react-vue-native-scripts": "0.0.2-rc1", 13 | "react-test-renderer": "16.0.0-alpha.6" 14 | }, 15 | "main": "./node_modules/react-native-scripts/build/bin/crna-entry.js", 16 | "scripts": { 17 | "start": "react-native-scripts start", 18 | "compiler": "react-vue-native-scripts compiler", 19 | "eject": "react-native-scripts eject", 20 | "android": "react-native-scripts android", 21 | "ios": "react-native-scripts ios", 22 | "test": "node node_modules/jest/bin/jest.js --watch" 23 | }, 24 | "jest": { 25 | "preset": "jest-expo" 26 | }, 27 | "dependencies": { 28 | "expo": "^17.0.0", 29 | "react": "16.0.0-alpha.6", 30 | "react-native": "^0.44.0", 31 | "react-navigation": "^1.0.0-beta.11", 32 | "vuex": "^2.3.1", 33 | "react-vue": "0.0.3-rc1", 34 | "react-vue-helper": "0.0.3-rc1" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * reference 3 | * https://github.com/weexteam/weex-hackernews/tree/master/src/store 4 | */ 5 | 6 | import Vue from 'react-vue'; 7 | import Vuex from 'vuex'; 8 | import * as actions from './actions'; 9 | import * as mutations from './mutations'; 10 | 11 | Vue.use(Vuex); 12 | 13 | const store = new Vuex.Store({ 14 | actions, 15 | mutations, 16 | 17 | state: { 18 | activeType: 'top', 19 | items: { 20 | top: [], 21 | new: [], 22 | show: [], 23 | ask: [], 24 | job: [] 25 | }, 26 | counts: { 27 | top: 0, 28 | new: 0, 29 | show: 0, 30 | ask: 0, 31 | job: 0 32 | }, 33 | lists: { 34 | top: [], 35 | new: [], 36 | show: [], 37 | ask: [], 38 | job: [] 39 | } 40 | }, 41 | 42 | getters: { 43 | // ids of the items that should be currently displayed based on 44 | // current list type and current pagination 45 | activeIds (state) { 46 | const { activeType, lists, counts } = state; 47 | const result = activeType ? lists[activeType].slice(counts[activeType], counts[activeType] + 10) : []; 48 | return result; 49 | }, 50 | 51 | activeIndex (state) { 52 | const { activeType, lists } = state; 53 | const index = Object.keys(lists).indexOf(activeType); 54 | return index === -1 ? 0 : index; 55 | } 56 | } 57 | }); 58 | 59 | export default store; 60 | -------------------------------------------------------------------------------- /src/components/Stories/Story.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 35 | 36 | 68 | -------------------------------------------------------------------------------- /src/components/Stories/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 74 | 75 | 80 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore unexpected extra "@providesModule" 9 | .*/node_modules/.*/node_modules/fbjs/.* 10 | 11 | ; Ignore duplicate module providers 12 | ; For RN Apps installed via npm, "Libraries" folder is inside 13 | ; "node_modules/react-native" but in the source repo it is in the root 14 | .*/Libraries/react-native/React.js 15 | .*/Libraries/react-native/ReactNative.js 16 | 17 | ; Additional create-react-native-app ignores 18 | 19 | ; Ignore duplicate module providers 20 | .*/node_modules/fbemitter/lib/* 21 | 22 | ; Ignore misbehaving dev-dependencies 23 | .*/node_modules/xdl/build/* 24 | .*/node_modules/reqwest/tests/* 25 | 26 | ; Ignore missing expo-sdk dependencies (temporarily) 27 | ; https://github.com/expo/expo/issues/162 28 | .*/node_modules/expo/src/* 29 | 30 | ; Ignore react-native-fbads dependency of the expo sdk 31 | .*/node_modules/react-native-fbads/* 32 | 33 | [include] 34 | 35 | [libs] 36 | node_modules/react-native/Libraries/react-native/react-native-interface.js 37 | node_modules/react-native/flow 38 | flow/ 39 | 40 | [options] 41 | module.system=haste 42 | 43 | emoji=true 44 | 45 | experimental.strict_type_args=true 46 | 47 | munge_underscores=true 48 | 49 | 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' 50 | 51 | suppress_type=$FlowIssue 52 | suppress_type=$FlowFixMe 53 | suppress_type=$FixMe 54 | 55 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(4[0-2]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 56 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(4[0-2]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 57 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 58 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 59 | 60 | unsafe.enable_getters_and_setters=true 61 | 62 | [version] 63 | ^0.42.0 64 | -------------------------------------------------------------------------------- /src/components/Stories/Footer.js: -------------------------------------------------------------------------------- 1 | import __react__vue__Vue, { 2 | observer as __react__vue__observer 3 | } from 'react-vue' 4 | import __react__vue__ReactNative from 'react-native' 5 | import __react__vue__PropType from 'prop-types' 6 | import { 7 | buildNativeComponent as __react__vue__buildNativeComponent 8 | } from 'react-vue-helper' 9 | import { 10 | bindNativeClass as __react__vue__bindClass, 11 | bindNativeStyle as __react__vue__bindStyle, 12 | mergeProps as __react__vue__mergeProps 13 | } from 'react-vue-helper' 14 | import { 15 | createElement as __react__vue__createElement, 16 | Component as __react__vue__Component 17 | } from 'react' 18 | 19 | const __react__vue__options = {} 20 | 21 | const __react__vue__render = function render(vm) { 22 | return __react__vue__createElement(vm.$options.components['View'], __react__vue__mergeProps.call(this, this.props.__react__vue__nativeEvents, { 23 | ref: ref => { 24 | this.setRootRef(ref); 25 | this.props['__react__vue__setRef'] && this.props['__react__vue__setRef'](ref); 26 | }, 27 | style: [__react__vue__bindClass.call(this, { 28 | staticClass: "footer", 29 | parentClass: this.props.style 30 | }), __react__vue__bindStyle(undefined, undefined, undefined)] 31 | }), __react__vue__createElement(vm.$options.components['Text'], { 32 | style: [__react__vue__bindClass.call(this, { 33 | staticClass: "text" 34 | })] 35 | }, "loading...")); 36 | }; 37 | 38 | const __react__vue__css = { 39 | "footer": { 40 | "flex": 1, 41 | "paddingTop": 24, 42 | "paddingBottom": 44, 43 | "justifyContent": "center", 44 | "alignItems": "center" 45 | }, 46 | "text": { 47 | "color": "#999" 48 | } 49 | } 50 | 51 | const __react__vue__ComponentBuilded = __react__vue__buildNativeComponent(__react__vue__render, __react__vue__options, { 52 | Component: __react__vue__Component, 53 | PropTypes: __react__vue__PropType, 54 | Vue: __react__vue__Vue, 55 | ReactNative: __react__vue__ReactNative, 56 | css: __react__vue__css 57 | }) 58 | 59 | export default __react__vue__observer(__react__vue__ComponentBuilded) -------------------------------------------------------------------------------- /src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 81 | 82 | 87 | 88 | -------------------------------------------------------------------------------- /src/components/TabView/Scroller.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 85 | 86 | 91 | -------------------------------------------------------------------------------- /src/views/WebSite.js: -------------------------------------------------------------------------------- 1 | import __react__vue__Vue, { 2 | observer as __react__vue__observer 3 | } from 'react-vue' 4 | import __react__vue__ReactNative from 'react-native' 5 | import __react__vue__PropType from 'prop-types' 6 | import { 7 | buildNativeComponent as __react__vue__buildNativeComponent 8 | } from 'react-vue-helper' 9 | import { 10 | bindNativeClass as __react__vue__bindClass, 11 | bindNativeStyle as __react__vue__bindStyle, 12 | mergeProps as __react__vue__mergeProps 13 | } from 'react-vue-helper' 14 | import { 15 | createElement as __react__vue__createElement, 16 | Component as __react__vue__Component 17 | } from 'react' 18 | 19 | 20 | const __react__vue__options = { 21 | props: { 22 | navigation: { 23 | type: Object 24 | } 25 | }, 26 | computed: { 27 | uri() { 28 | const { 29 | params 30 | } = this.navigation.state; 31 | return params.url; 32 | } 33 | }, 34 | methods: { 35 | onLoadEnd() { 36 | this.navigation.setParams({ 37 | title: '' 38 | }); 39 | }, 40 | onLoadStart() { 41 | this.navigation.setParams({ 42 | title: 'loading...' 43 | }); 44 | } 45 | } 46 | }; 47 | 48 | const __react__vue__render = function render(vm) { 49 | return __react__vue__createElement(vm.$options.components['WebView'], __react__vue__mergeProps.call(this, this.props.__react__vue__nativeEvents, { 50 | ref: ref => { 51 | this.setRootRef(ref); 52 | this.props['__react__vue__setRef'] && this.props['__react__vue__setRef'](ref); 53 | }, 54 | source: { 55 | uri: vm['uri'] 56 | }, 57 | onLoadEnd: vm['onLoadEnd'], 58 | onLoadStart: vm['onLoadStart'], 59 | style: [__react__vue__bindClass.call(this, { 60 | staticClass: "web", 61 | parentClass: this.props.style 62 | }), __react__vue__bindStyle(undefined, undefined, undefined)] 63 | })); 64 | }; 65 | 66 | const __react__vue__css = { 67 | "web": { 68 | "flex": 1 69 | } 70 | } 71 | 72 | const __react__vue__ComponentBuilded = __react__vue__buildNativeComponent(__react__vue__render, __react__vue__options, { 73 | Component: __react__vue__Component, 74 | PropTypes: __react__vue__PropType, 75 | Vue: __react__vue__Vue, 76 | ReactNative: __react__vue__ReactNative, 77 | css: __react__vue__css 78 | }) 79 | 80 | export default __react__vue__observer(__react__vue__ComponentBuilded) -------------------------------------------------------------------------------- /src/components/Header/Left.js: -------------------------------------------------------------------------------- 1 | import __react__vue__Vue, { 2 | observer as __react__vue__observer 3 | } from 'react-vue' 4 | import __react__vue__ReactNative from 'react-native' 5 | import __react__vue__PropType from 'prop-types' 6 | import { 7 | buildNativeComponent as __react__vue__buildNativeComponent 8 | } from 'react-vue-helper' 9 | import { 10 | bindNativeClass as __react__vue__bindClass, 11 | bindNativeStyle as __react__vue__bindStyle, 12 | mergeProps as __react__vue__mergeProps 13 | } from 'react-vue-helper' 14 | import { 15 | createElement as __react__vue__createElement, 16 | Component as __react__vue__Component 17 | } from 'react' 18 | 19 | 20 | const __react__vue__options = { 21 | props: { 22 | navigation: { 23 | type: Object 24 | } 25 | }, 26 | methods: { 27 | onPress() { 28 | this.navigation.goBack(); 29 | } 30 | } 31 | }; 32 | 33 | const __react__vue__render = function render(vm) { 34 | return __react__vue__createElement(vm.$options.components['TouchableOpacity'], __react__vue__mergeProps.call(this, this.props.__react__vue__nativeEvents, { 35 | ref: ref => { 36 | this.setRootRef(ref); 37 | this.props['__react__vue__setRef'] && this.props['__react__vue__setRef'](ref); 38 | }, 39 | onPress: vm['onPress'], 40 | activeOpacity: 1, 41 | style: [__react__vue__bindClass.call(this, { 42 | staticClass: "container", 43 | parentClass: this.props.style 44 | }), __react__vue__bindStyle(undefined, undefined, undefined)] 45 | }), __react__vue__createElement(vm.$options.components['View'], { 46 | style: [__react__vue__bindClass.call(this, { 47 | staticClass: "back" 48 | })] 49 | })); 50 | }; 51 | 52 | const __react__vue__css = { 53 | "container": { 54 | "flex": 1, 55 | "justifyContent": "center", 56 | "alignItems": "center" 57 | }, 58 | "back": { 59 | "width": 30, 60 | "height": 30, 61 | "borderTopWidth": 4, 62 | "borderLeftWidth": 4, 63 | "borderTopColor": "#ff6600", 64 | "borderLeftColor": "#ff6600", 65 | "marginLeft": 8, 66 | "alignSelf": "center", 67 | "transform": [{ 68 | "rotate": "-45deg" 69 | }, { 70 | "scale": 0.5 71 | }] 72 | } 73 | } 74 | 75 | const __react__vue__ComponentBuilded = __react__vue__buildNativeComponent(__react__vue__render, __react__vue__options, { 76 | Component: __react__vue__Component, 77 | PropTypes: __react__vue__PropType, 78 | Vue: __react__vue__Vue, 79 | ReactNative: __react__vue__ReactNative, 80 | css: __react__vue__css 81 | }) 82 | 83 | export default __react__vue__observer(__react__vue__ComponentBuilded) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hacker News 2 | 3 | A mobile app for Hacker News, powered by Vue and React Native 4 | 5 | 6 | 7 | ## Preview 8 | Use [Expo app](https://expo.io) to scan the QR code below. 9 | 10 | 11 | 12 | QR Code content: [https://expo.io/@chuangchuang/hackernews](https://expo.io/@chuangchuang/hackernews) 13 | 14 | ## Available Scripts 15 | * [npm compiler](#npm-compiler) 16 | * [npm start](#npm-start) 17 | * [npm test](#npm-test) 18 | * [npm run ios](#npm-run-ios) 19 | * [npm run android](#npm-run-android) 20 | * [npm run eject](#npm-run-eject) 21 | 22 | If Yarn was installed when the project was initialized, then dependencies will have been installed via Yarn, and you should probably use it to run these commands as well. Unlike dependency installation, command running syntax is identical for Yarn and NPM at the time of this writing. 23 | 24 | ### `npm compiler` 25 | 26 | Start a server to compile vue components into react components 27 | 28 | ### `npm start` 29 | 30 | Runs your app in development mode. 31 | 32 | Open it in the [Expo app](https://expo.io) on your phone to view it. It will reload if you save edits to your files, and you will see build errors and logs in the terminal. 33 | 34 | Sometimes you may need to reset or clear the React Native packager's cache. To do so, you can pass the `--reset-cache` flag to the start script: 35 | 36 | ``` 37 | npm start -- --reset-cache 38 | # or 39 | yarn start -- --reset-cache 40 | ``` 41 | 42 | #### `npm test` 43 | 44 | Runs the [jest](https://github.com/facebook/jest) test runner on your tests. 45 | 46 | #### `npm run ios` 47 | 48 | Like `npm start`, but also attempts to open your app in the iOS Simulator if you're on a Mac and have it installed. 49 | 50 | #### `npm run android` 51 | 52 | Like `npm start`, but also attempts to open your app on a connected Android device or emulator. Requires an installation of Android build tools (see [React Native docs](https://facebook.github.io/react-native/docs/getting-started.html) for detailed setup). We also recommend installing Genymotion as your Android emulator. Once you've finished setting up the native build environment, there are two options for making the right copy of `adb` available to Create React Native App: 53 | 54 | ##### Using Android Studio's `adb` 55 | 56 | 1. Make sure that you can run adb from your terminal. 57 | 2. Open Genymotion and navigate to `Settings -> ADB`. Select “Use custom Android SDK tools” and update with your [Android SDK directory](https://stackoverflow.com/questions/25176594/android-sdk-location). 58 | 59 | ##### Using Genymotion's `adb` 60 | 61 | 1. Find Genymotion’s copy of adb. On macOS for example, this is normally `/Applications/Genymotion.app/Contents/MacOS/tools/`. 62 | 2. Add the Genymotion tools directory to your path (instructions for [Mac](http://osxdaily.com/2014/08/14/add-new-path-to-path-command-line/), [Linux](http://www.computerhope.com/issues/ch001647.htm), and [Windows](https://www.howtogeek.com/118594/how-to-edit-your-system-path-for-easy-command-line-access/)). 63 | 3. Make sure that you can run adb from your terminal. 64 | 65 | #### `npm run eject` 66 | 67 | This will start the process of "ejecting" from Create React Native App's build scripts. You'll be asked a couple of questions about how you'd like to build your project. 68 | 69 | **Warning:** Running eject is a permanent action (aside from whatever version control system you use). An ejected app will require you to have an [Xcode and/or Android Studio environment](https://facebook.github.io/react-native/docs/getting-started.html) set up. 70 | -------------------------------------------------------------------------------- /src/components/TabView/index.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 112 | 113 | 140 | -------------------------------------------------------------------------------- /src/components/Stories/index.js: -------------------------------------------------------------------------------- 1 | import __react__vue__Vue, { 2 | observer as __react__vue__observer 3 | } from 'react-vue' 4 | import __react__vue__ReactNative from 'react-native' 5 | import __react__vue__PropType from 'prop-types' 6 | import { 7 | buildNativeComponent as __react__vue__buildNativeComponent 8 | } from 'react-vue-helper' 9 | import { 10 | bindNativeClass as __react__vue__bindClass, 11 | bindNativeStyle as __react__vue__bindStyle, 12 | mergeProps as __react__vue__mergeProps 13 | } from 'react-vue-helper' 14 | import { 15 | createElement as __react__vue__createElement, 16 | Component as __react__vue__Component 17 | } from 'react' 18 | 19 | import React from 'react'; 20 | import { 21 | RefreshControl 22 | } from 'react-native'; 23 | import { 24 | SCREEN_WIDTH, 25 | emptyFn 26 | } from '../../util'; 27 | import Story from './Story'; 28 | import Footer from './Footer' 29 | const __react__vue__options = { 30 | props: { 31 | stories: { 32 | type: Array, 33 | default: [] 34 | }, 35 | loading: { 36 | type: Boolean, 37 | default: true 38 | }, 39 | onRefresh: { 40 | type: Function, 41 | default: function() { 42 | return emptyFn; 43 | } 44 | }, 45 | onEndReached: { 46 | type: Function, 47 | default: function() { 48 | return emptyFn; 49 | } 50 | }, 51 | navigation: { 52 | type: Object 53 | } 54 | }, 55 | computed: { 56 | data() { 57 | return this.stories.map(story => { 58 | story.key = story.id; 59 | return story; 60 | }); 61 | }, 62 | refreshControl() { 63 | return ; 73 | }, 74 | listFooterComponent() { 75 | return Footer; 76 | }, 77 | flatListStyle() { 78 | return { 79 | width: SCREEN_WIDTH 80 | }; 81 | } 82 | }, 83 | methods: { 84 | renderItem(props) { 85 | return ; 91 | }, 92 | onPress(item) { 93 | const { 94 | navigate 95 | } = this.navigation; 96 | navigate('WebSite', { 97 | url: item.url 98 | }); 99 | } 100 | } 101 | }; 102 | 103 | const __react__vue__render = function render(vm) { 104 | return __react__vue__createElement(vm.$options.components['FlatList'], __react__vue__mergeProps.call(this, this.props.__react__vue__nativeEvents, { 105 | ref: ref => { 106 | this.setRootRef(ref); 107 | this.props['__react__vue__setRef'] && this.props['__react__vue__setRef'](ref); 108 | }, 109 | refreshControl: vm['refreshControl'], 110 | data: vm['data'], 111 | renderItem: vm['renderItem'], 112 | onEndReached: vm['onEndReached'], 113 | ListFooterComponent: vm['listFooterComponent'], 114 | style: [__react__vue__bindClass.call(this, { 115 | parentClass: this.props.style 116 | }), __react__vue__bindStyle(vm['flatListStyle'], undefined, undefined)] 117 | })); 118 | }; 119 | 120 | const __react__vue__css = { 121 | "list": { 122 | "flex": 1 123 | } 124 | } 125 | 126 | const __react__vue__ComponentBuilded = __react__vue__buildNativeComponent(__react__vue__render, __react__vue__options, { 127 | Component: __react__vue__Component, 128 | PropTypes: __react__vue__PropType, 129 | Vue: __react__vue__Vue, 130 | ReactNative: __react__vue__ReactNative, 131 | css: __react__vue__css 132 | }) 133 | 134 | export default __react__vue__observer(__react__vue__ComponentBuilded) -------------------------------------------------------------------------------- /src/components/Stories/Story.js: -------------------------------------------------------------------------------- 1 | import __react__vue__Vue, { 2 | observer as __react__vue__observer 3 | } from 'react-vue' 4 | import __react__vue__ReactNative from 'react-native' 5 | import __react__vue__PropType from 'prop-types' 6 | import { 7 | buildNativeComponent as __react__vue__buildNativeComponent 8 | } from 'react-vue-helper' 9 | import { 10 | bindNativeClass as __react__vue__bindClass, 11 | bindNativeStyle as __react__vue__bindStyle, 12 | mergeProps as __react__vue__mergeProps, 13 | _toString as __react__vue__toString 14 | } from 'react-vue-helper' 15 | import { 16 | createElement as __react__vue__createElement, 17 | Component as __react__vue__Component 18 | } from 'react' 19 | 20 | import { 21 | emptyFn 22 | } from '../../util' 23 | const __react__vue__options = { 24 | props: { 25 | item: { 26 | type: Object 27 | }, 28 | onPress: { 29 | type: Function, 30 | default () { 31 | return emptyFn; 32 | } 33 | } 34 | }, 35 | methods: { 36 | _onPress() { 37 | this.onPress(this.item); 38 | } 39 | } 40 | }; 41 | 42 | const __react__vue__render = function render(vm) { 43 | return __react__vue__createElement(vm.$options.components['TouchableOpacity'], __react__vue__mergeProps.call(this, this.props.__react__vue__nativeEvents, { 44 | ref: ref => { 45 | this.setRootRef(ref); 46 | this.props['__react__vue__setRef'] && this.props['__react__vue__setRef'](ref); 47 | }, 48 | onPress: vm['_onPress'], 49 | activeOpacity: 0.5, 50 | style: [__react__vue__bindClass.call(this, { 51 | staticClass: "container", 52 | parentClass: this.props.style 53 | }), __react__vue__bindStyle(undefined, undefined, undefined)] 54 | }), __react__vue__createElement(vm.$options.components['View'], { 55 | style: [__react__vue__bindClass.call(this, { 56 | staticClass: "score" 57 | })] 58 | }, __react__vue__createElement(vm.$options.components['Text'], { 59 | style: [__react__vue__bindClass.call(this, { 60 | staticClass: "score-text" 61 | })] 62 | }, __react__vue__toString(vm['item'].score))), __react__vue__createElement(vm.$options.components['View'], { 63 | style: [__react__vue__bindClass.call(this, { 64 | staticClass: "detail" 65 | })] 66 | }, __react__vue__createElement(vm.$options.components['Text'], { 67 | style: [__react__vue__bindClass.call(this, { 68 | staticClass: "name" 69 | })] 70 | }, __react__vue__toString(vm['item'].by)), __react__vue__createElement(vm.$options.components['Text'], { 71 | style: [__react__vue__bindClass.call(this, { 72 | staticClass: "title" 73 | })] 74 | }, __react__vue__toString(vm['item'].title)))); 75 | }; 76 | 77 | const __react__vue__css = { 78 | "container": { 79 | "flex": 1, 80 | "flexDirection": "row", 81 | "padding": 16 82 | }, 83 | "score": { 84 | "width": 40, 85 | "height": 40, 86 | "backgroundColor": "#fff", 87 | "borderRadius": 20, 88 | "justifyContent": "center", 89 | "alignItems": "center", 90 | "marginRight": 16 91 | }, 92 | "score-text": { 93 | "color": "#ff6600", 94 | "fontWeight": "bold" 95 | }, 96 | "detail": { 97 | "flex": 1 98 | }, 99 | "name": { 100 | "color": "#666", 101 | "fontSize": 12, 102 | "marginBottom": 6 103 | }, 104 | "title": { 105 | "color": "#333", 106 | "fontSize": 14 107 | } 108 | } 109 | 110 | const __react__vue__ComponentBuilded = __react__vue__buildNativeComponent(__react__vue__render, __react__vue__options, { 111 | Component: __react__vue__Component, 112 | PropTypes: __react__vue__PropType, 113 | Vue: __react__vue__Vue, 114 | ReactNative: __react__vue__ReactNative, 115 | css: __react__vue__css 116 | }) 117 | 118 | export default __react__vue__observer(__react__vue__ComponentBuilded) -------------------------------------------------------------------------------- /src/views/Home.js: -------------------------------------------------------------------------------- 1 | import __react__vue__Vue, { 2 | observer as __react__vue__observer 3 | } from 'react-vue' 4 | import __react__vue__ReactNative from 'react-native' 5 | import __react__vue__PropType from 'prop-types' 6 | import { 7 | buildNativeComponent as __react__vue__buildNativeComponent 8 | } from 'react-vue-helper' 9 | import { 10 | bindNativeClass as __react__vue__bindClass, 11 | bindNativeStyle as __react__vue__bindStyle, 12 | mergeProps as __react__vue__mergeProps, 13 | renderList as __react__vue__renderList 14 | } from 'react-vue-helper' 15 | import { 16 | createElement as __react__vue__createElement, 17 | Component as __react__vue__Component 18 | } from 'react' 19 | 20 | import TabView from '../components/TabView'; 21 | import Stories from '../components/Stories'; 22 | import store from '../store' 23 | const __react__vue__options = { 24 | components: { 25 | TabView, 26 | Stories 27 | }, 28 | props: { 29 | navigation: { 30 | type: Object 31 | } 32 | }, 33 | data() { 34 | return { 35 | loading: {} 36 | }; 37 | }, 38 | computed: { 39 | items() { 40 | return store.state.items; 41 | }, 42 | titles() { 43 | return Object.keys(this.items); 44 | }, 45 | renderHeader() { 46 | return this.titles.map(v => { 47 | return { 48 | value: v[0].toUpperCase() + v.slice(1), 49 | type: v 50 | }; 51 | }); 52 | } 53 | }, 54 | methods: { 55 | select(i) { 56 | const type = this.renderHeader[i].type; 57 | this.fetchList(type); 58 | }, 59 | onRefresh() { 60 | this.pullFetchList(store.state.activeType); 61 | }, 62 | onEndReached() { 63 | const type = store.state.activeType; 64 | if (this.items[type].length) { 65 | store.dispatch('LOAD_MORE_ITEMS'); 66 | } 67 | }, 68 | pullFetchList(type) { 69 | this.$set(this.loading, type, true); 70 | this.fetchList(type).then(() => { 71 | this.$set(this.loading, type, false); 72 | }); 73 | }, 74 | fetchList(type) { 75 | return store.dispatch('FETCH_LIST_DATA', { 76 | type: type 77 | }); 78 | } 79 | }, 80 | created() { 81 | this.fetchList(store.state.activeType); 82 | } 83 | }; 84 | 85 | const __react__vue__render = function render(vm) { 86 | return __react__vue__createElement(vm.$options.components['TabView'], __react__vue__mergeProps.call(this, this.props.__react__vue__nativeEvents, { 87 | ref: ref => { 88 | this.setRootRef(ref); 89 | this.props['__react__vue__setRef'] && this.props['__react__vue__setRef'](ref); 90 | }, 91 | renderHeader: vm['renderHeader'], 92 | onPress: vm['select'], 93 | onPageSelected: vm['select'], 94 | style: [__react__vue__bindClass.call(this, { 95 | staticClass: "container", 96 | parentClass: this.props.style 97 | }), __react__vue__bindStyle(undefined, undefined, undefined)] 98 | }), __react__vue__renderList(vm['items'], function(item, type) { 99 | return __react__vue__createElement(vm.$options.components['Stories'], { 100 | stories: item, 101 | navigation: vm['navigation'], 102 | loading: !!vm['loading'][type], 103 | onRefresh: vm['onRefresh'], 104 | onEndReached: vm['onEndReached'], 105 | key: arguments[1], 106 | style: [] 107 | }); 108 | }.bind(this))); 109 | }; 110 | 111 | const __react__vue__css = { 112 | "container": { 113 | "backgroundColor": "#f6f6ef" 114 | } 115 | } 116 | 117 | const __react__vue__ComponentBuilded = __react__vue__buildNativeComponent(__react__vue__render, __react__vue__options, { 118 | Component: __react__vue__Component, 119 | PropTypes: __react__vue__PropType, 120 | Vue: __react__vue__Vue, 121 | ReactNative: __react__vue__ReactNative, 122 | css: __react__vue__css 123 | }) 124 | 125 | export default __react__vue__observer(__react__vue__ComponentBuilded) -------------------------------------------------------------------------------- /src/components/TabView/Scroller.js: -------------------------------------------------------------------------------- 1 | import __react__vue__Vue, { 2 | observer as __react__vue__observer 3 | } from 'react-vue' 4 | import __react__vue__ReactNative from 'react-native' 5 | import __react__vue__PropType from 'prop-types' 6 | import { 7 | buildNativeComponent as __react__vue__buildNativeComponent 8 | } from 'react-vue-helper' 9 | import { 10 | bindNativeClass as __react__vue__bindClass, 11 | bindNativeStyle as __react__vue__bindStyle, 12 | mergeProps as __react__vue__mergeProps, 13 | renderSlot as __react__vue__renderSlot 14 | } from 'react-vue-helper' 15 | import { 16 | createElement as __react__vue__createElement, 17 | Component as __react__vue__Component 18 | } from 'react' 19 | 20 | import { 21 | Platform 22 | } from 'react-native'; 23 | import { 24 | emptyFn, 25 | SCREEN_WIDTH 26 | } from '../../util' 27 | const __react__vue__options = { 28 | props: { 29 | defaultIndex: { 30 | type: Number, 31 | default: 0 32 | }, 33 | onScroll: { 34 | type: Function, 35 | default () { 36 | return emptyFn; 37 | } 38 | }, 39 | onPageSelected: { 40 | type: Function, 41 | default () { 42 | return emptyFn; 43 | } 44 | }, 45 | setRef: { 46 | type: Function, 47 | default () { 48 | return emptyFn; 49 | } 50 | } 51 | }, 52 | computed: { 53 | ios() { 54 | return Platform.OS === 'ios'; 55 | }, 56 | android() { 57 | return Platform.OS === 'android'; 58 | }, 59 | contentOffset() { 60 | return { 61 | x: this.defaultIndex * SCREEN_WIDTH, 62 | y: 0 63 | }; 64 | }, 65 | SCREEN_WIDTH() { 66 | return SCREEN_WIDTH; 67 | } 68 | }, 69 | methods: { 70 | _onScroll(v) { 71 | this.onScroll(v); 72 | }, 73 | _onPageSelected(i) { 74 | this.onPageSelected(i); 75 | } 76 | }, 77 | mounted() { 78 | this.setRef(this.$refs.scroller); 79 | } 80 | }; 81 | 82 | const __react__vue__render = function render(vm) { 83 | const __react__vue__slotSet = __react__vue__renderSlot.call(this, [], this.props.children); 84 | return __react__vue__createElement(vm.$options.components['View'], __react__vue__mergeProps.call(this, this.props.__react__vue__nativeEvents, { 85 | ref: ref => { 86 | this.setRootRef(ref); 87 | this.props['__react__vue__setRef'] && this.props['__react__vue__setRef'](ref); 88 | }, 89 | style: [__react__vue__bindClass.call(this, { 90 | staticClass: "container", 91 | parentClass: this.props.style 92 | }), __react__vue__bindStyle(undefined, undefined, undefined)] 93 | }), vm['ios'] ? __react__vue__createElement(vm.$options.components['ScrollView'], { 94 | ref: ref => { 95 | this.setRef(ref, "scroller", false); 96 | }, 97 | horizontal: true, 98 | pagingEnabled: true, 99 | scrollEventThrottle: 10, 100 | showsHorizontalScrollIndicator: false, 101 | onScroll: e => vm['_onScroll'](e.nativeEvent.contentOffset.x / vm['SCREEN_WIDTH']), 102 | onMomentumScrollEnd: e => vm['_onPageSelected'](e.nativeEvent.contentOffset.x / vm['SCREEN_WIDTH']), 103 | contentOffset: vm['contentOffset'], 104 | style: [] 105 | }, __react__vue__slotSet(undefined, {})) : vm['android'] ? __react__vue__createElement(vm.$options.components['ViewPagerAndroid'], { 106 | ref: ref => { 107 | this.setRef(ref, "scroller", false); 108 | }, 109 | initialPage: vm['defaultIndex'], 110 | keyBoardDismissMode: "on-drag", 111 | onPageScroll: e => vm['_onScroll'](e.nativeEvent.position + e.nativeEvent.offset), 112 | onPageSelected: e => vm['_onPageSelected'](e.nativeEvent.position), 113 | style: [__react__vue__bindClass.call(this, { 114 | staticClass: "container" 115 | })] 116 | }, __react__vue__slotSet(undefined, {})) : null); 117 | }; 118 | 119 | const __react__vue__css = { 120 | "container": { 121 | "flex": 1 122 | } 123 | } 124 | 125 | const __react__vue__ComponentBuilded = __react__vue__buildNativeComponent(__react__vue__render, __react__vue__options, { 126 | Component: __react__vue__Component, 127 | PropTypes: __react__vue__PropType, 128 | Vue: __react__vue__Vue, 129 | ReactNative: __react__vue__ReactNative, 130 | css: __react__vue__css 131 | }) 132 | 133 | export default __react__vue__observer(__react__vue__ComponentBuilded) -------------------------------------------------------------------------------- /src/components/TabView/index.js: -------------------------------------------------------------------------------- 1 | import __react__vue__Vue, { 2 | observer as __react__vue__observer 3 | } from 'react-vue' 4 | import __react__vue__ReactNative from 'react-native' 5 | import __react__vue__PropType from 'prop-types' 6 | import { 7 | buildNativeComponent as __react__vue__buildNativeComponent 8 | } from 'react-vue-helper' 9 | import { 10 | bindNativeClass as __react__vue__bindClass, 11 | bindNativeStyle as __react__vue__bindStyle, 12 | mergeProps as __react__vue__mergeProps, 13 | renderList as __react__vue__renderList, 14 | _toString as __react__vue__toString, 15 | renderSlot as __react__vue__renderSlot 16 | } from 'react-vue-helper' 17 | import { 18 | createElement as __react__vue__createElement, 19 | Component as __react__vue__Component 20 | } from 'react' 21 | 22 | import { 23 | Animated, 24 | Platform 25 | } from 'react-native'; 26 | import Scroller from './Scroller'; 27 | import { 28 | emptyFn, 29 | SCREEN_WIDTH 30 | } from '../../util' 31 | const __react__vue__options = { 32 | components: { 33 | Scroller 34 | }, 35 | props: { 36 | renderHeader: { 37 | type: Array 38 | }, 39 | renderScene: { 40 | type: Array 41 | }, 42 | defaultIndex: { 43 | type: Number, 44 | default: 0 45 | }, 46 | onPress: { 47 | type: Function, 48 | default: function() { 49 | return emptyFn; 50 | } 51 | }, 52 | onPageSelected: { 53 | type: Function, 54 | default: function() { 55 | return emptyFn; 56 | } 57 | } 58 | }, 59 | computed: { 60 | scenes() { 61 | return this.renderScene || this.$slots.default.map(v => () => v); 62 | }, 63 | underlineStyle() { 64 | const width = SCREEN_WIDTH / (this.renderHeader.length || 1); 65 | return { 66 | width: width, 67 | transform: [{ 68 | translateX: this.animateValue.interpolate({ 69 | inputRange: [0, 1], 70 | outputRange: [0, width] 71 | }) 72 | }] 73 | }; 74 | }, 75 | scrollerWidth() { 76 | return SCREEN_WIDTH * this.renderHeader.length; 77 | } 78 | }, 79 | methods: { 80 | _onPress(v, i) { 81 | if (Platform.OS === 'ios') { 82 | this.scrollerRef.scrollTo({ 83 | x: i * SCREEN_WIDTH, 84 | y: 0, 85 | animated: true 86 | }); 87 | } else { 88 | this.scrollerRef.setPage(i); 89 | } 90 | this.onPress(i, v); 91 | }, 92 | _onScroll(v) { 93 | this.animateValue.setValue(v); 94 | }, 95 | _onPageSelected(i) { 96 | this.onPageSelected(i); 97 | }, 98 | setRef(ref) { 99 | this.scrollerRef = ref; 100 | } 101 | }, 102 | created() { 103 | this.animateValue = new Animated.Value(this.defaultIndex); 104 | } 105 | }; 106 | 107 | const __react__vue__render = function render(vm) { 108 | const __react__vue__slotSet = __react__vue__renderSlot.call(this, [], this.props.children); 109 | return __react__vue__createElement(vm.$options.components['View'], __react__vue__mergeProps.call(this, this.props.__react__vue__nativeEvents, { 110 | ref: ref => { 111 | this.setRootRef(ref); 112 | this.props['__react__vue__setRef'] && this.props['__react__vue__setRef'](ref); 113 | }, 114 | style: [__react__vue__bindClass.call(this, { 115 | staticClass: "container", 116 | parentClass: this.props.style 117 | }), __react__vue__bindStyle(undefined, undefined, undefined)] 118 | }), __react__vue__createElement(vm.$options.components['View'], { 119 | style: [__react__vue__bindClass.call(this, { 120 | staticClass: "bar" 121 | })] 122 | }, __react__vue__renderList(vm['renderHeader'], function(item, index) { 123 | return __react__vue__createElement(vm.$options.components['TouchableOpacity'], { 124 | activeOpacity: 1, 125 | onPress: () => vm['_onPress'](item, index), 126 | key: arguments[1], 127 | style: [__react__vue__bindClass.call(this, { 128 | staticClass: "item" 129 | })] 130 | }, __react__vue__createElement(vm.$options.components['Animated']['Text'], { 131 | style: [__react__vue__bindClass.call(this, { 132 | staticClass: "item-text" 133 | }), __react__vue__bindStyle({ 134 | color: vm['animateValue'].interpolate({ 135 | inputRange: [index - 1, index, index + 1], 136 | outputRange: ['#000', '#ff6600', '#000'], 137 | extrapolate: 'clamp' 138 | }) 139 | }, undefined, undefined)] 140 | }, "" + __react__vue__toString(item.value || item) + "")); 141 | }.bind(this)), __react__vue__createElement(vm.$options.components['Animated']['View'], { 142 | style: [__react__vue__bindClass.call(this, { 143 | staticClass: "underline" 144 | }), __react__vue__bindStyle(vm['underlineStyle'], undefined, undefined)] 145 | })), __react__vue__createElement(vm.$options.components['Scroller'], { 146 | defaultIndex: vm['defaultIndex'], 147 | setRef: vm['setRef'], 148 | onScroll: vm['_onScroll'], 149 | onPageSelected: vm['_onPageSelected'], 150 | style: [] 151 | }, __react__vue__slotSet(undefined, {}))); 152 | }; 153 | 154 | const __react__vue__css = { 155 | "container": { 156 | "flex": 1 157 | }, 158 | "bar": { 159 | "position": "relative", 160 | "flexDirection": "row", 161 | "backgroundColor": "#fff", 162 | "borderBottomColor": "#d9d9d9", 163 | "height": 44 164 | }, 165 | "underline": { 166 | "position": "absolute", 167 | "left": 0, 168 | "bottom": 0, 169 | "height": 2, 170 | "backgroundColor": "#ff6600" 171 | }, 172 | "item": { 173 | "flex": 1, 174 | "justifyContent": "center", 175 | "alignItems": "center" 176 | }, 177 | "item-text": { 178 | "fontSize": 14 179 | } 180 | } 181 | 182 | const __react__vue__ComponentBuilded = __react__vue__buildNativeComponent(__react__vue__render, __react__vue__options, { 183 | Component: __react__vue__Component, 184 | PropTypes: __react__vue__PropType, 185 | Vue: __react__vue__Vue, 186 | ReactNative: __react__vue__ReactNative, 187 | css: __react__vue__css 188 | }) 189 | 190 | export default __react__vue__observer(__react__vue__ComponentBuilded) --------------------------------------------------------------------------------